diff --git a/lib/colvars/colvar.cpp b/lib/colvars/colvar.cpp index cb10ac4f6..25ffec898 100644 --- a/lib/colvars/colvar.cpp +++ b/lib/colvars/colvar.cpp @@ -1,1676 +1,1676 @@ // -*- c++ -*- #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" #include <algorithm> // XX TODO make the acf code handle forces as well as values and velocities colvar::colvar (std::string const &conf) { cvm::log ("Initializing a new collective variable.\n"); - + get_keyval (conf, "name", this->name, (std::string ("colvar")+cvm::to_str (cvm::colvars.size()+1))); for (std::vector<colvar *>::iterator cvi = cvm::colvars.begin(); cvi < cvm::colvars.end(); cvi++) { if ((*cvi)->name == this->name) cvm::fatal_error ("Error: this colvar cannot have the same name, \""+this->name+ "\", as another colvar.\n"); } - // all tasks disabled by default + // all tasks disabled by default for (size_t i = 0; i < task_ntot; i++) { tasks[i] = false; } kinetic_energy = 0.0; potential_energy = 0.0; // read the configuration and set up corresponding instances, for // each type of component implemented #define initialize_components(def_desc,def_config_key,def_class_name) \ { \ size_t def_count = 0; \ std::string def_conf = ""; \ size_t pos = 0; \ while ( this->key_lookup (conf, \ def_config_key, \ def_conf, \ pos) ) { \ if (!def_conf.size()) continue; \ cvm::log ("Initializing " \ "a new \""+std::string (def_config_key)+"\" component"+ \ (cvm::debug() ? ", with configuration:\n"+def_conf \ : ".\n")); \ cvm::increase_depth(); \ cvc *cvcp = new colvar::def_class_name (def_conf); \ if (cvcp != NULL) { \ cvcs.push_back (cvcp); \ cvcp->check_keywords (def_conf, def_config_key); \ cvm::decrease_depth(); \ } else { \ cvm::fatal_error ("Error: in allocating component \"" \ def_config_key"\".\n"); \ } \ if ( (cvcp->period != 0.0) || (cvcp->wrap_center != 0.0) ) { \ if ( (cvcp->function_type != std::string ("distance_z")) && \ (cvcp->function_type != std::string ("dihedral")) && \ (cvcp->function_type != std::string ("spin_angle")) ) { \ cvm::fatal_error ("Error: invalid use of period and/or " \ "wrapAround in a \""+ \ std::string (def_config_key)+ \ "\" component.\n"+ \ "Period: "+cvm::to_str(cvcp->period) + \ " wrapAround: "+cvm::to_str(cvcp->wrap_center));\ } \ } \ if ( ! cvcs.back()->name.size()) \ cvcs.back()->name = std::string (def_config_key)+ \ (cvm::to_str (++def_count)); \ if (cvm::debug()) \ cvm::log ("Done initializing a \""+ \ std::string (def_config_key)+ \ "\" component"+ \ (cvm::debug() ? \ ", named \""+cvcs.back()->name+"\"" \ : "")+".\n"); \ def_conf = ""; \ if (cvm::debug()) \ cvm::log ("Parsed "+cvm::to_str (cvcs.size())+ \ " components at this time.\n"); \ } \ } initialize_components ("distance", "distance", distance); initialize_components ("distance vector", "distanceVec", distance_vec); initialize_components ("distance vector " "direction", "distanceDir", distance_dir); initialize_components ("distance projection " "on an axis", "distanceZ", distance_z); initialize_components ("distance projection " "on a plane", "distanceXY", distance_xy); initialize_components ("average distance weighted by inverse power", "distanceInv", distance_inv); initialize_components ("coordination " "number", "coordNum", coordnum); initialize_components ("self-coordination " "number", "selfCoordNum", selfcoordnum); initialize_components ("angle", "angle", angle); initialize_components ("dihedral", "dihedral", dihedral); initialize_components ("hydrogen bond", "hBond", h_bond); // initialize_components ("alpha helix", "alphaDihedrals", alpha_dihedrals); initialize_components ("alpha helix", "alpha", alpha_angles); initialize_components ("dihedral principal " "component", "dihedralPC", dihedPC); initialize_components ("orientation", "orientation", orientation); initialize_components ("orientation " "angle", "orientationAngle",orientation_angle); initialize_components ("tilt", "tilt", tilt); initialize_components ("spin angle", "spinAngle", spin_angle); initialize_components ("RMSD", "rmsd", rmsd); // initialize_components ("logarithm of MSD", "logmsd", logmsd); initialize_components ("radius of " "gyration", "gyration", gyration); initialize_components ("moment of " "inertia", "inertia", inertia); initialize_components ("moment of inertia around an axis", "inertiaZ", inertia_z); initialize_components ("eigenvector", "eigenvector", eigenvector); if (!cvcs.size()) cvm::fatal_error ("Error: no valid components were provided " "for this collective variable.\n"); cvm::log ("All components initialized.\n"); // this is set false if any of the components has an exponent // different from 1 in the polynomial b_linear = true; // these will be set to false if any of the cvcs has them false b_inverse_gradients = true; b_Jacobian_force = true; // Decide whether the colvar is periodic // Used to wrap extended DOF if extendedLagrangian is on - if (cvcs.size() == 1 && (cvcs[0])->b_periodic && (cvcs[0])->sup_np == 1 + if (cvcs.size() == 1 && (cvcs[0])->b_periodic && (cvcs[0])->sup_np == 1 && (cvcs[0])->sup_coeff == 1.0 ) { this->b_periodic = true; this->period = (cvcs[0])->period; // TODO write explicit wrap() function for colvars to allow for // sup_coeff different from 1 // this->period = (cvcs[0])->period * (cvcs[0])->sup_coeff; } else { this->b_periodic = false; this->period = 0.0; } // check the available features of each cvc for (size_t i = 0; i < cvcs.size(); i++) { if ((cvcs[i])->sup_np != 1) { if (cvm::debug() && b_linear) cvm::log ("Warning: You are using a non-linear polynomial " "combination to define this collective variable, " "some biasing methods may be unavailable.\n"); b_linear = false; if ((cvcs[i])->sup_np < 0) { cvm::log ("Warning: you chose a negative exponent in the combination; " "if you apply forces, the simulation may become unstable " "when the component \""+ (cvcs[i])->function_type+"\" approaches zero.\n"); } } if ((cvcs[i])->b_periodic && !b_periodic) { cvm::log ("Warning: although this component is periodic, the colvar will " "not be treated as periodic, either because the exponent is not " "1, or because multiple components are present. Make sure that " "you know what you are doing!"); } if (! (cvcs[i])->b_inverse_gradients) b_inverse_gradients = false; if (! (cvcs[i])->b_Jacobian_derivative) b_Jacobian_force = false; for (size_t j = i; j < cvcs.size(); j++) { if ( (cvcs[i])->type() != (cvcs[j])->type() ) { cvm::fatal_error ("ERROR: you are definining this collective variable " "by using components of different types, \""+ colvarvalue::type_desc[(cvcs[i])->type()]+ "\" and \""+ colvarvalue::type_desc[(cvcs[j])->type()]+ "\". " "You must use the same type in order to " " sum them together.\n"); } } } { colvarvalue::Type const value_type = (cvcs[0])->type(); if (cvm::debug()) cvm::log ("This collective variable is a "+ colvarvalue::type_desc[value_type]+", corresponding to "+ cvm::to_str (colvarvalue::dof_num[value_type])+ " internal degrees of freedom.\n"); x.type (value_type); x_reported.type (value_type); } get_keyval (conf, "width", width, 1.0); if (width <= 0.0) cvm::fatal_error ("Error: \"width\" must be positive.\n"); lower_boundary.type (this->type()); lower_wall.type (this->type()); upper_boundary.type (this->type()); upper_wall.type (this->type()); if (this->type() == colvarvalue::type_scalar) { if (get_keyval (conf, "lowerBoundary", lower_boundary, lower_boundary)) { enable (task_lower_boundary); } get_keyval (conf, "lowerWallConstant", lower_wall_k, 0.0); if (lower_wall_k > 0.0) { get_keyval (conf, "lowerWall", lower_wall, lower_boundary); enable (task_lower_wall); } if (get_keyval (conf, "upperBoundary", upper_boundary, upper_boundary)) { enable (task_upper_boundary); } get_keyval (conf, "upperWallConstant", upper_wall_k, 0.0); if (upper_wall_k > 0.0) { get_keyval (conf, "upperWall", upper_wall, upper_boundary); enable (task_upper_wall); } } if (tasks[task_lower_boundary]) { get_keyval (conf, "hardLowerBoundary", hard_lower_boundary, false); } if (tasks[task_upper_boundary]) { get_keyval (conf, "hardUpperBoundary", hard_upper_boundary, false); } // consistency checks for boundaries and walls if (tasks[task_lower_boundary] && tasks[task_upper_boundary]) { if (lower_boundary >= upper_boundary) { cvm::fatal_error ("Error: the upper boundary, "+ cvm::to_str (upper_boundary)+ ", is not higher than the lower boundary, "+ cvm::to_str (lower_boundary)+".\n"); } } if (tasks[task_lower_wall] && tasks[task_upper_wall]) { if (lower_wall >= upper_wall) { cvm::fatal_error ("Error: the upper wall, "+ cvm::to_str (upper_wall)+ ", is not higher than the lower wall, "+ cvm::to_str (lower_wall)+".\n"); } if (dist2 (lower_wall, upper_wall) < 1.0E-12) { cvm::log ("Lower wall and upper wall are equal " "in the periodic domain of the colvar: disabling walls.\n"); disable (task_lower_wall); disable (task_upper_wall); } } get_keyval (conf, "expandBoundaries", expand_boundaries, false); if (expand_boundaries && periodic_boundaries()) { cvm::fatal_error ("Error: trying to expand boundaries that already " "cover a whole period of a periodic colvar.\n"); } if (expand_boundaries && hard_lower_boundary && hard_upper_boundary) { cvm::fatal_error ("Error: inconsistent configuration " "(trying to expand boundaries with both " "hardLowerBoundary and hardUpperBoundary enabled).\n"); } { bool b_extended_lagrangian; get_keyval (conf, "extendedLagrangian", b_extended_lagrangian, false); if (b_extended_lagrangian) { cvm::real temp, tolerance, period; cvm::log ("Enabling the extended Lagrangian term for colvar \""+ this->name+"\".\n"); - + enable (task_extended_lagrangian); xr.type (this->type()); vr.type (this->type()); fr.type (this->type()); const bool found = get_keyval (conf, "extendedTemp", temp, cvm::temperature()); if (temp <= 0.0) { if (found) cvm::fatal_error ("Error: \"extendedTemp\" must be positive.\n"); else cvm::fatal_error ("Error: a positive temperature must be provided, either " "by enabling a thermostat, or through \"extendedTemp\".\n"); } get_keyval (conf, "extendedFluctuation", tolerance, width); if (tolerance <= 0.0) cvm::fatal_error ("Error: \"extendedFluctuation\" must be positive.\n"); ext_force_k = cvm::boltzmann() * temp / (tolerance * tolerance); cvm::log ("Computed extended system force constant: " + cvm::to_str(ext_force_k) + " kcal/mol/U^2"); get_keyval (conf, "extendedTimeConstant", period, 200.0); if (period <= 0.0) cvm::fatal_error ("Error: \"extendedTimeConstant\" must be positive.\n"); ext_mass = (cvm::boltzmann() * temp * period * period) / (4.0 * PI * PI * tolerance * tolerance); cvm::log ("Computed fictitious mass: " + cvm::to_str(ext_mass) + " kcal/mol/(U/fs)^2 (U: colvar unit)"); { bool b_output_energy; get_keyval (conf, "outputEnergy", b_output_energy, false); if (b_output_energy) { enable (task_output_energy); } } get_keyval (conf, "extendedLangevinDamping", ext_gamma, 1.0); if (ext_gamma < 0.0) cvm::fatal_error ("Error: \"extendedLangevinDamping\" may not be negative.\n"); if (ext_gamma != 0.0) { enable (task_langevin); ext_gamma *= 1.0e-3; // convert from ps-1 to fs-1 ext_sigma = std::sqrt(2.0 * cvm::boltzmann() * temp * ext_gamma * ext_mass / cvm::dt()); } } } { bool b_output_value; get_keyval (conf, "outputValue", b_output_value, true); if (b_output_value) { enable (task_output_value); } } { bool b_output_velocity; get_keyval (conf, "outputVelocity", b_output_velocity, false); if (b_output_velocity) { enable (task_output_velocity); } } { bool b_output_system_force; get_keyval (conf, "outputSystemForce", b_output_system_force, false); if (b_output_system_force) { enable (task_output_system_force); } } { bool b_output_applied_force; get_keyval (conf, "outputAppliedForce", b_output_applied_force, false); if (b_output_applied_force) { enable (task_output_applied_force); } } if (cvm::b_analysis) parse_analysis (conf); if (cvm::debug()) cvm::log ("Done initializing collective variable \""+this->name+"\".\n"); } void colvar::build_atom_list (void) { // If atomic gradients are requested, build full list of atom ids from all cvcs std::list<int> temp_id_list; for (size_t i = 0; i < cvcs.size(); i++) { for (size_t j = 0; j < cvcs[i]->atom_groups.size(); j++) { for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) { temp_id_list.push_back (cvcs[i]->atom_groups[j]->at(k).id); } } } temp_id_list.sort(); temp_id_list.unique(); atom_ids = std::vector<int> (temp_id_list.begin(), temp_id_list.end()); temp_id_list.clear(); atomic_gradients.resize (atom_ids.size()); if (atom_ids.size()) { if (cvm::debug()) cvm::log ("Colvar: created atom list with " + cvm::to_str(atom_ids.size()) + " atoms.\n"); } else { cvm::log ("Warning: colvar components communicated no atom IDs.\n"); } } void colvar::parse_analysis (std::string const &conf) { // if (cvm::debug()) // cvm::log ("Parsing analysis flags for collective variable \""+ // this->name+"\".\n"); runave_length = 0; bool b_runave = false; if (get_keyval (conf, "runAve", b_runave) && b_runave) { enable (task_runave); get_keyval (conf, "runAveLength", runave_length, 1000); get_keyval (conf, "runAveStride", runave_stride, 1); if ((cvm::restart_out_freq % runave_stride) != 0) cvm::fatal_error ("Error: runAveStride must be commensurate with the restart frequency.\n"); std::string runave_outfile; get_keyval (conf, "runAveOutputFile", runave_outfile, std::string (cvm::output_prefix+"."+ this->name+".runave.traj")); size_t const this_cv_width = x.output_width (cvm::cv_width); runave_os.open (runave_outfile.c_str()); runave_os << "# " << cvm::wrap_string ("step", cvm::it_width-2) << " " << cvm::wrap_string ("running average", this_cv_width) << " " << cvm::wrap_string ("running stddev", this_cv_width) << "\n"; } acf_length = 0; bool b_acf = false; if (get_keyval (conf, "corrFunc", b_acf) && b_acf) { enable (task_corrfunc); std::string acf_colvar_name; get_keyval (conf, "corrFuncWithColvar", acf_colvar_name, this->name); if (acf_colvar_name == this->name) { cvm::log ("Calculating auto-correlation function.\n"); } else { cvm::log ("Calculating correlation function with \""+ this->name+"\".\n"); } std::string acf_type_str; get_keyval (conf, "corrFuncType", acf_type_str, to_lower_cppstr (std::string ("velocity"))); if (acf_type_str == to_lower_cppstr (std::string ("coordinate"))) { acf_type = acf_coor; } else if (acf_type_str == to_lower_cppstr (std::string ("velocity"))) { acf_type = acf_vel; enable (task_fdiff_velocity); if (acf_colvar_name.size()) (cvm::colvar_p (acf_colvar_name))->enable (task_fdiff_velocity); } else if (acf_type_str == to_lower_cppstr (std::string ("coordinate_p2"))) { acf_type = acf_p2coor; } else { cvm::fatal_error ("Unknown type of correlation function, \""+ acf_type_str+"\".\n"); } get_keyval (conf, "corrFuncOffset", acf_offset, 0); get_keyval (conf, "corrFuncLength", acf_length, 1000); get_keyval (conf, "corrFuncStride", acf_stride, 1); if ((cvm::restart_out_freq % acf_stride) != 0) cvm::fatal_error ("Error: corrFuncStride must be commensurate with the restart frequency.\n"); get_keyval (conf, "corrFuncNormalize", acf_normalize, true); get_keyval (conf, "corrFuncOutputFile", acf_outfile, std::string (cvm::output_prefix+"."+this->name+ ".corrfunc.dat")); } } void colvar::enable (colvar::task const &t) { switch (t) { case task_output_system_force: enable (task_system_force); break; case task_report_Jacobian_force: enable (task_Jacobian_force); enable (task_system_force); if (cvm::debug()) cvm::log ("Adding the Jacobian force to the system force, " "rather than applying its opposite silently.\n"); break; case task_Jacobian_force: // checks below do not apply to extended-system colvars if ( !tasks[task_extended_lagrangian] ) { enable (task_gradients); - if (!b_Jacobian_force) + if (!b_Jacobian_force) cvm::fatal_error ("Error: colvar \""+this->name+ "\" does not have Jacobian forces implemented.\n"); - if (!b_linear) + if (!b_linear) cvm::fatal_error ("Error: colvar \""+this->name+ "\" must be defined as a linear combination " "to calculate the Jacobian force.\n"); if (cvm::debug()) cvm::log ("Enabling calculation of the Jacobian force " "on this colvar.\n"); } fj.type (this->type()); break; case task_system_force: if (!tasks[task_extended_lagrangian]) { if (!b_inverse_gradients) { cvm::fatal_error ("Error: one or more of the components of " "colvar \""+this->name+ "\" does not support system force calculation.\n"); } cvm::request_system_force(); } ft.type (this->type()); ft_reported.type (this->type()); break; case task_output_applied_force: case task_lower_wall: case task_upper_wall: // all of the above require gradients enable (task_gradients); break; case task_fdiff_velocity: x_old.type (this->type()); v_fdiff.type (this->type()); v_reported.type (this->type()); break; case task_output_velocity: enable (task_fdiff_velocity); break; case task_grid: if (this->type() != colvarvalue::type_scalar) cvm::fatal_error ("Cannot calculate a grid for collective variable, \""+ this->name+"\", because its value is not a scalar number.\n"); break; case task_extended_lagrangian: enable (task_gradients); v_reported.type (this->type()); break; case task_lower_boundary: case task_upper_boundary: if (this->type() != colvarvalue::type_scalar) { cvm::fatal_error ("Error: this colvar is not a scalar value " "and cannot produce a grid.\n"); } break; case task_output_value: case task_runave: case task_corrfunc: case task_ntot: break; case task_gradients: f.type (this->type()); fb.type (this->type()); break; case task_collect_gradients: if (this->type() != colvarvalue::type_scalar) cvm::fatal_error ("Collecting atomic gradients for non-scalar collective variable \""+ this->name+"\" is not yet implemented.\n"); enable (task_gradients); if (atom_ids.size() == 0) { build_atom_list(); } break; } tasks[t] = true; } void colvar::disable (colvar::task const &t) { // check dependencies switch (t) { case task_gradients: disable (task_upper_wall); disable (task_lower_wall); disable (task_output_applied_force); disable (task_system_force); disable (task_Jacobian_force); break; case task_system_force: disable (task_output_system_force); break; case task_Jacobian_force: disable (task_report_Jacobian_force); break; case task_fdiff_velocity: disable (task_output_velocity); break; case task_lower_boundary: case task_upper_boundary: disable (task_grid); break; case task_extended_lagrangian: case task_report_Jacobian_force: case task_output_value: case task_output_velocity: case task_output_applied_force: case task_output_system_force: case task_runave: case task_corrfunc: case task_grid: case task_lower_wall: case task_upper_wall: case task_ntot: break; } tasks[t] = false; } colvar::~colvar() { for (size_t i = 0; i < cvcs.size(); i++) { delete cvcs[i]; } -} +} // ******************** CALC FUNCTIONS ******************** void colvar::calc() { if (cvm::debug()) cvm::log ("Calculating colvar \""+this->name+"\".\n"); // prepare atom groups for calculation if (cvm::debug()) cvm::log ("Collecting data from atom groups.\n"); for (size_t i = 0; i < cvcs.size(); i++) { for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { cvm::atom_group &atoms = *(cvcs[i]->atom_groups[ig]); atoms.reset_atoms_data(); atoms.read_positions(); if (atoms.b_center || atoms.b_rotate) { atoms.calc_apply_roto_translation(); } // each atom group will take care of its own ref_pos_group, if defined } } if (tasks[task_output_velocity]) { for (size_t i = 0; i < cvcs.size(); i++) { for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { cvcs[i]->atom_groups[ig]->read_velocities(); - } + } } } if (tasks[task_system_force]) { for (size_t i = 0; i < cvcs.size(); i++) { for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { cvcs[i]->atom_groups[ig]->read_system_forces(); - } + } } } // calculate the value of the colvar if (cvm::debug()) cvm::log ("Calculating colvar components.\n"); x.reset(); if (x.type() == colvarvalue::type_scalar) { // polynomial combination allowed for (size_t i = 0; i < cvcs.size(); i++) { cvm::increase_depth(); (cvcs[i])->calc_value(); cvm::decrease_depth(); if (cvm::debug()) cvm::log ("Colvar component no. "+cvm::to_str (i+1)+ " within colvar \""+this->name+"\" has value "+ cvm::to_str ((cvcs[i])->value(), cvm::cv_width, cvm::cv_prec)+".\n"); x += (cvcs[i])->sup_coeff * ( ((cvcs[i])->sup_np != 1) ? std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np) : (cvcs[i])->value().real_value ); - } + } } else { // only linear combination allowed for (size_t i = 0; i < cvcs.size(); i++) { cvm::increase_depth(); (cvcs[i])->calc_value(); cvm::decrease_depth(); if (cvm::debug()) cvm::log ("Colvar component no. "+cvm::to_str (i+1)+ " within colvar \""+this->name+"\" has value "+ cvm::to_str ((cvcs[i])->value(), cvm::cv_width, cvm::cv_prec)+".\n"); x += (cvcs[i])->sup_coeff * (cvcs[i])->value(); - } + } } if (cvm::debug()) cvm::log ("Colvar \""+this->name+"\" has value "+ cvm::to_str (x, cvm::cv_width, cvm::cv_prec)+".\n"); if (tasks[task_gradients]) { if (cvm::debug()) cvm::log ("Calculating gradients of colvar \""+this->name+"\".\n"); for (size_t i = 0; i < cvcs.size(); i++) { // calculate the gradients of each component cvm::increase_depth(); (cvcs[i])->calc_gradients(); // if requested, propagate (via chain rule) the gradients above // to the atoms used to define the roto-translation for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { - if (cvcs[i]->atom_groups[ig]->b_fit_gradients) + if (cvcs[i]->atom_groups[ig]->b_fit_gradients) cvcs[i]->atom_groups[ig]->calc_fit_gradients(); - } + } cvm::decrease_depth(); } if (cvm::debug()) cvm::log ("Done calculating gradients of colvar \""+this->name+"\".\n"); if (tasks[task_collect_gradients]) { // Collect the atomic gradients inside colvar object for (int a = 0; a < atomic_gradients.size(); a++) { atomic_gradients[a].reset(); } for (size_t i = 0; i < cvcs.size(); i++) { // Coefficient: d(a * x^n) = a * n * x^(n-1) * dx cvm::real coeff = (cvcs[i])->sup_coeff * cvm::real ((cvcs[i])->sup_np) * std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np-1); for (size_t j = 0; j < cvcs[i]->atom_groups.size(); j++) { // If necessary, apply inverse rotation to get atomic // gradient in the laboratory frame if (cvcs[i]->atom_groups[j]->b_rotate) { cvm::rotation const rot_inv = cvcs[i]->atom_groups[j]->rot.inverse(); for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) { int a = std::lower_bound (atom_ids.begin(), atom_ids.end(), cvcs[i]->atom_groups[j]->at(k).id) - atom_ids.begin(); atomic_gradients[a] += coeff * rot_inv.rotate (cvcs[i]->atom_groups[j]->at(k).grad); } } else { for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) { int a = std::lower_bound (atom_ids.begin(), atom_ids.end(), cvcs[i]->atom_groups[j]->at(k).id) - atom_ids.begin(); - atomic_gradients[a] += coeff * cvcs[i]->atom_groups[j]->at(k).grad; + atomic_gradients[a] += coeff * cvcs[i]->atom_groups[j]->at(k).grad; } } } } } } if (tasks[task_system_force]) { if (cvm::debug()) cvm::log ("Calculating system force of colvar \""+this->name+"\".\n"); ft.reset(); if(!tasks[task_extended_lagrangian] && (cvm::step_relative() > 0)) { // get from the cvcs the system forces from the PREVIOUS step for (size_t i = 0; i < cvcs.size(); i++) { (cvcs[i])->calc_force_invgrads(); // linear combination is assumed cvm::increase_depth(); ft += (cvcs[i])->system_force() / ((cvcs[i])->sup_coeff * cvm::real (cvcs.size())); cvm::decrease_depth(); } } if (tasks[task_report_Jacobian_force]) { // add the Jacobian force to the system force, and don't apply any silent // correction internally: biases such as colvarbias_abf will handle it ft += fj; } if (cvm::debug()) cvm::log ("Done calculating system force of colvar \""+this->name+"\".\n"); } if (tasks[task_fdiff_velocity]) { // calculate the velocity by finite differences if (cvm::step_relative() == 0) x_old = x; else { v_fdiff = fdiff_velocity (x_old, x); v_reported = v_fdiff; } } if (tasks[task_extended_lagrangian]) { // initialize the restraint center in the first step to the value // just calculated from the cvcs // TODO: put it in the restart information if (cvm::step_relative() == 0) { xr = x; vr = 0.0; // (already 0; added for clarity) } // report the restraint center as "value" x_reported = xr; v_reported = vr; // the "system force" with the extended Lagrangian is just the // harmonic term acting on the extended coordinate // Note: this is the force for current timestep ft_reported = (-0.5 * ext_force_k) * this->dist2_lgrad (xr, x); } else { x_reported = x; ft_reported = ft; } if (cvm::debug()) cvm::log ("Done calculating colvar \""+this->name+"\".\n"); } cvm::real colvar::update() { if (cvm::debug()) cvm::log ("Updating colvar \""+this->name+"\".\n"); // set to zero the applied force f.reset(); // add the biases' force, which at this point should already have // been summed over each bias using this colvar f += fb; if (tasks[task_lower_wall] || tasks[task_upper_wall]) { // wall force colvarvalue fw (this->type()); // if the two walls are applied concurrently, decide which is the // closer one (on a periodic colvar, both walls may be applicable // at the same time) if ( (!tasks[task_upper_wall]) || (this->dist2 (x, lower_wall) < this->dist2 (x, upper_wall)) ) { cvm::real const grad = this->dist2_lgrad (x, lower_wall); if (grad < 0.0) { fw = -0.5 * lower_wall_k * grad; if (cvm::debug()) cvm::log ("Applying a lower wall force ("+ cvm::to_str (fw)+") to \""+this->name+"\".\n"); f += fw; } } else { cvm::real const grad = this->dist2_lgrad (x, upper_wall); if (grad > 0.0) { fw = -0.5 * upper_wall_k * grad; if (cvm::debug()) cvm::log ("Applying an upper wall force ("+ cvm::to_str (fw)+") to \""+this->name+"\".\n"); f += fw; } } } if (tasks[task_Jacobian_force]) { - + cvm::increase_depth(); for (size_t i = 0; i < cvcs.size(); i++) { (cvcs[i])->calc_Jacobian_derivative(); } cvm::decrease_depth(); fj.reset(); for (size_t i = 0; i < cvcs.size(); i++) { // linear combination is assumed fj += 1.0 / ( cvm::real (cvcs.size()) * cvm::real ((cvcs[i])->sup_coeff) ) * (cvcs[i])->Jacobian_derivative(); } fj *= cvm::boltzmann() * cvm::temperature(); // the instantaneous Jacobian force was not included in the reported system force; - // instead, it is subtracted from the applied force (silent Jacobian correction) - if (! tasks[task_report_Jacobian_force]) + // instead, it is subtracted from the applied force (silent Jacobian correction) + if (! tasks[task_report_Jacobian_force]) f -= fj; } if (tasks[task_extended_lagrangian]) { cvm::real dt = cvm::dt(); // the total force is applied to the fictitious mass, while the // atoms only feel the harmonic force // fr: extended coordinate force; f: colvar force applied to atomic coordinates fr = f; fr += (-0.5 * ext_force_k) * this->dist2_lgrad (xr, x); f = (-0.5 * ext_force_k) * this->dist2_rgrad (xr, x); // leap frog: starting from x_i, f_i, v_(i-1/2) vr += (0.5 * dt) * fr / ext_mass; // Because of leapfrog, kinetic energy at time i is approximate kinetic_energy = 0.5 * ext_mass * vr * vr; potential_energy = 0.5 * ext_force_k * this->dist2(xr, x); // leap to v_(i+1/2) if (tasks[task_langevin]) { vr -= dt * ext_gamma * vr.real_value; - vr += dt * ext_sigma * cvm::rand_gaussian() / ext_mass; - } - vr += (0.5 * dt) * fr / ext_mass; + vr += dt * ext_sigma * cvm::rand_gaussian() / ext_mass; + } + vr += (0.5 * dt) * fr / ext_mass; xr += dt * vr; xr.apply_constraints(); if (this->b_periodic) this->wrap (xr); } if (tasks[task_fdiff_velocity]) { // set it for the next step x_old = x; } if (cvm::debug()) cvm::log ("Done updating colvar \""+this->name+"\".\n"); return (potential_energy + kinetic_energy); } void colvar::communicate_forces() { if (cvm::debug()) - cvm::log ("Communicating forces from colvar \""+this->name+"\".\n"); + cvm::log ("Communicating forces from colvar \""+this->name+"\".\n"); if (x.type() == colvarvalue::type_scalar) { for (size_t i = 0; i < cvcs.size(); i++) { cvm::increase_depth(); - (cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff * + (cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff * cvm::real ((cvcs[i])->sup_np) * (std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np-1)) ); cvm::decrease_depth(); } } else { for (size_t i = 0; i < cvcs.size(); i++) { cvm::increase_depth(); (cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff); cvm::decrease_depth(); } } if (cvm::debug()) - cvm::log ("Done communicating forces from colvar \""+this->name+"\".\n"); + cvm::log ("Done communicating forces from colvar \""+this->name+"\".\n"); } // ******************** METRIC FUNCTIONS ******************** // Use the metrics defined by \link cvc \endlink objects bool colvar::periodic_boundaries (colvarvalue const &lb, colvarvalue const &ub) const { if ( (!tasks[task_lower_boundary]) || (!tasks[task_upper_boundary]) ) { cvm::fatal_error ("Error: requesting to histogram the " "collective variable \""+this->name+"\", but a " "pair of lower and upper boundaries must be " "defined.\n"); } if (period > 0.0) { if ( ((std::sqrt (this->dist2 (lb, ub))) / this->width) < 1.0E-10 ) { return true; } } return false; } bool colvar::periodic_boundaries() const { if ( (!tasks[task_lower_boundary]) || (!tasks[task_upper_boundary]) ) { cvm::fatal_error ("Error: requesting to histogram the " "collective variable \""+this->name+"\", but a " "pair of lower and upper boundaries must be " "defined.\n"); } return periodic_boundaries (lower_boundary, upper_boundary); } cvm::real colvar::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { return (cvcs[0])->dist2 (x1, x2); } colvarvalue colvar::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { return (cvcs[0])->dist2_lgrad (x1, x2); } colvarvalue colvar::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { return (cvcs[0])->dist2_rgrad (x1, x2); } cvm::real colvar::compare (colvarvalue const &x1, colvarvalue const &x2) const { return (cvcs[0])->compare (x1, x2); } void colvar::wrap (colvarvalue &x) const { (cvcs[0])->wrap (x); return; } // ******************** INPUT FUNCTIONS ******************** std::istream & colvar::read_restart (std::istream &is) { size_t const start_pos = is.tellg(); std::string conf; if ( !(is >> colvarparse::read_block ("colvar", conf)) ) { // this is not a colvar block is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } { std::string check_name = ""; if ( (get_keyval (conf, "name", check_name, std::string (""), colvarparse::parse_silent)) && (check_name != name) ) { cvm::fatal_error ("Error: the state file does not match the " "configuration file, at colvar \""+name+"\".\n"); } if (check_name.size() == 0) { cvm::fatal_error ("Error: Collective variable in the " "restart file without any identifier.\n"); } } if ( !(get_keyval (conf, "x", x, colvarvalue (x.type()), colvarparse::parse_silent)) ) { cvm::log ("Error: restart file does not contain " "the value of the colvar \""+ name+"\" .\n"); } else { cvm::log ("Restarting collective variable \""+name+"\" from value: "+ cvm::to_str (x)+"\n"); } if (tasks[colvar::task_extended_lagrangian]) { if ( !(get_keyval (conf, "extended_x", xr, colvarvalue (x.type()), colvarparse::parse_silent)) && !(get_keyval (conf, "extended_v", vr, colvarvalue (x.type()), colvarparse::parse_silent)) ) { cvm::log ("Error: restart file does not contain " "\"extended_x\" or \"extended_v\" for the colvar \""+ name+"\", but you requested \"extendedLagrangian\".\n"); } } if (tasks[task_extended_lagrangian]) { x_reported = xr; } else { x_reported = x; } if (tasks[task_output_velocity]) { if ( !(get_keyval (conf, "v", v_fdiff, colvarvalue (x.type()), colvarparse::parse_silent)) ) { cvm::log ("Error: restart file does not contain " "the velocity for the colvar \""+ name+"\", but you requested \"outputVelocity\".\n"); } if (tasks[task_extended_lagrangian]) { v_reported = vr; } else { v_reported = v_fdiff; } } return is; } std::istream & colvar::read_traj (std::istream &is) { size_t const start_pos = is.tellg(); if (tasks[task_output_value]) { if (!(is >> x)) { cvm::log ("Error: in reading the value of colvar \""+ this->name+"\" from trajectory.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } if (tasks[task_extended_lagrangian]) { is >> xr; x_reported = xr; } else { x_reported = x; } } if (tasks[task_output_velocity]) { is >> v_fdiff; if (tasks[task_extended_lagrangian]) { is >> vr; v_reported = vr; } else { v_reported = v_fdiff; } } if (tasks[task_output_system_force]) { is >> ft; - if (tasks[task_extended_lagrangian]) { + if (tasks[task_extended_lagrangian]) { is >> fr; ft_reported = fr; } else { ft_reported = ft; } } if (tasks[task_output_applied_force]) { is >> f; } return is; } // ******************** OUTPUT FUNCTIONS ******************** std::ostream & colvar::write_restart (std::ostream &os) { os << "colvar {\n" << " name " << name << "\n" << " x " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << x << "\n"; if (tasks[task_output_velocity]) { os << " v " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << v_reported << "\n"; } if (tasks[task_extended_lagrangian]) { os << " extended_x " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << xr << "\n" << " extended_v " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << vr << "\n"; } os << "}\n\n"; return os; -} +} std::ostream & colvar::write_traj_label (std::ostream & os) { size_t const this_cv_width = x.output_width (cvm::cv_width); os << " "; if (tasks[task_output_value]) { os << " " << cvm::wrap_string (this->name, this_cv_width); if (tasks[task_extended_lagrangian]) { // restraint center os << " r_" << cvm::wrap_string (this->name, this_cv_width-2); } } if (tasks[task_output_velocity]) { os << " v_" << cvm::wrap_string (this->name, this_cv_width-2); if (tasks[task_extended_lagrangian]) { // restraint center os << " vr_" << cvm::wrap_string (this->name, this_cv_width-3); } } if (tasks[task_output_energy]) { os << " Ep_" << cvm::wrap_string (this->name, this_cv_width-3) << " Ek_" << cvm::wrap_string (this->name, this_cv_width-3); } if (tasks[task_output_system_force]) { os << " fs_" << cvm::wrap_string (this->name, this_cv_width-3); if (tasks[task_extended_lagrangian]) { // restraint center os << " fr_" << cvm::wrap_string (this->name, this_cv_width-3); } } if (tasks[task_output_applied_force]) { os << " fa_" << cvm::wrap_string (this->name, this_cv_width-3); } return os; } std::ostream & colvar::write_traj (std::ostream &os) { os << " "; if (tasks[task_output_value]) { if (tasks[task_extended_lagrangian]) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << x; } os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << x_reported; } if (tasks[task_output_velocity]) { if (tasks[task_extended_lagrangian]) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << v_fdiff; } os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << v_reported; } if (tasks[task_output_energy]) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << potential_energy << " " << kinetic_energy; } if (tasks[task_output_system_force]) { if (tasks[task_extended_lagrangian]) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << ft; } os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << ft_reported; } if (tasks[task_output_applied_force]) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << f; } return os; } void colvar::write_output_files() { if (cvm::b_analysis) { if (acf.size()) { cvm::log ("Writing acf to file \""+acf_outfile+"\".\n"); std::ofstream acf_os (acf_outfile.c_str()); if (! acf_os.good()) cvm::fatal_error ("Cannot open file \""+acf_outfile+"\".\n"); write_acf (acf_os); acf_os.close(); } if (runave_os.good()) { runave_os.close(); } } } // ******************** ANALYSIS FUNCTIONS ******************** void colvar::analyse() { if (tasks[task_runave]) { calc_runave(); } if (tasks[task_corrfunc]) { calc_acf(); } } inline void history_add_value (size_t const &history_length, std::list<colvarvalue> &history, colvarvalue const &new_value) { history.push_front (new_value); if (history.size() > history_length) history.pop_back(); } inline void history_incr (std::list< std::list<colvarvalue> > &history, std::list< std::list<colvarvalue> >::iterator &history_p) { - if ((++history_p) == history.end()) + if ((++history_p) == history.end()) history_p = history.begin(); } void colvar::calc_acf() { // using here an acf_stride-long list of vectors for either // coordinates (acf_x_history) or velocities (acf_v_history); each vector can // contain up to acf_length values, which are contiguous in memory // representation but separated by acf_stride in the time series; // the pointer to each vector is changed at every step if (! (acf_x_history.size() || acf_v_history.size()) ) { // first-step operations - colvar *cfcv = (acf_colvar_name.size() ? + colvar *cfcv = (acf_colvar_name.size() ? cvm::colvar_p (acf_colvar_name) : this); if (cfcv->type() != this->type()) cvm::fatal_error ("Error: correlation function between \""+cfcv->name+ "\" and \""+this->name+"\" cannot be calculated, " "because their value types are different.\n"); acf_nframes = 0; cvm::log ("Colvar \""+this->name+"\": initializing ACF calculation.\n"); if (acf.size() < acf_length+1) acf.resize (acf_length+1, 0.0); switch (acf_type) { case acf_vel: // allocate space for the velocities history for (size_t i = 0; i < acf_stride; i++) { acf_v_history.push_back (std::list<colvarvalue>()); } acf_v_history_p = acf_v_history.begin(); break; case acf_coor: case acf_p2coor: // allocate space for the coordinates history for (size_t i = 0; i < acf_stride; i++) { acf_x_history.push_back (std::list<colvarvalue>()); } acf_x_history_p = acf_x_history.begin(); break; default: break; } } else { - colvar *cfcv = (acf_colvar_name.size() ? + colvar *cfcv = (acf_colvar_name.size() ? cvm::colvar_p (acf_colvar_name) : this); switch (acf_type) { case acf_vel: if (tasks[task_fdiff_velocity]) { // calc() should do this already, but this only happens in a // simulation; better do it again in case a trajectory is // being read v_reported = v_fdiff = fdiff_velocity (x_old, cfcv->value()); } calc_vel_acf ((*acf_v_history_p), cfcv->velocity()); // store this value in the history history_add_value (acf_length+acf_offset, *acf_v_history_p, cfcv->velocity()); // if stride is larger than one, cycle among different histories history_incr (acf_v_history, acf_v_history_p); break; case acf_coor: calc_coor_acf ((*acf_x_history_p), cfcv->value()); history_add_value (acf_length+acf_offset, *acf_x_history_p, cfcv->value()); history_incr (acf_x_history, acf_x_history_p); break; case acf_p2coor: calc_p2coor_acf ((*acf_x_history_p), cfcv->value()); history_add_value (acf_length+acf_offset, *acf_x_history_p, cfcv->value()); history_incr (acf_x_history, acf_x_history_p); break; default: break; } } if (tasks[task_fdiff_velocity]) { // set it for the next step x_old = x; } } void colvar::calc_vel_acf (std::list<colvarvalue> &v_list, colvarvalue const &v) { // loop over stored velocities and add to the ACF, but only the // length is sufficient to hold an entire row of ACF values if (v_list.size() >= acf_length+acf_offset) { std::list<colvarvalue>::iterator vs_i = v_list.begin(); std::vector<cvm::real>::iterator acf_i = acf.begin(); for (size_t i = 0; i < acf_offset; i++) vs_i++; // current vel with itself *(acf_i++) += v.norm2(); // inner products of previous velocities with current (acf_i and // vs_i are updated) - colvarvalue::inner_opt (v, vs_i, v_list.end(), acf_i); + colvarvalue::inner_opt (v, vs_i, v_list.end(), acf_i); acf_nframes++; } } void colvar::calc_coor_acf (std::list<colvarvalue> &x_list, colvarvalue const &x) { // same as above but for coordinates if (x_list.size() >= acf_length+acf_offset) { std::list<colvarvalue>::iterator xs_i = x_list.begin(); std::vector<cvm::real>::iterator acf_i = acf.begin(); for (size_t i = 0; i < acf_offset; i++) xs_i++; *(acf_i++) += x.norm2(); - colvarvalue::inner_opt (x, xs_i, x_list.end(), acf_i); + colvarvalue::inner_opt (x, xs_i, x_list.end(), acf_i); acf_nframes++; } } void colvar::calc_p2coor_acf (std::list<colvarvalue> &x_list, colvarvalue const &x) { // same as above but with second order Legendre polynomial instead // of just the scalar product if (x_list.size() >= acf_length+acf_offset) { std::list<colvarvalue>::iterator xs_i = x_list.begin(); std::vector<cvm::real>::iterator acf_i = acf.begin(); for (size_t i = 0; i < acf_offset; i++) xs_i++; // value of P2(0) = 1 *(acf_i++) += 1.0; - colvarvalue::p2leg_opt (x, xs_i, x_list.end(), acf_i); + colvarvalue::p2leg_opt (x, xs_i, x_list.end(), acf_i); acf_nframes++; } } void colvar::write_acf (std::ostream &os) { if (!acf_nframes) cvm::log ("Warning: ACF was not calculated (insufficient frames).\n"); os.setf (std::ios::scientific, std::ios::floatfield); os << "# Autocorrelation function for collective variable \"" << this->name << "\"\n"; // one frame is used for normalization, the statistical sample is // hence decreased os << "# nframes = " << (acf_normalize ? acf_nframes - 1 : acf_nframes) << "\n"; cvm::real const acf_norm = acf.front() / cvm::real (acf_nframes); std::vector<cvm::real>::iterator acf_i; size_t it = acf_offset; for (acf_i = acf.begin(); acf_i != acf.end(); acf_i++) { os << std::setw (cvm::it_width) << acf_stride * (it++) << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << ( acf_normalize ? (*acf_i)/(acf_norm * cvm::real (acf_nframes)) : (*acf_i)/(cvm::real (acf_nframes)) ) << "\n"; } } void colvar::calc_runave() { if (!x_history.size()) { runave.type (x.type()); runave.reset(); // first-step operations if (cvm::debug()) cvm::log ("Colvar \""+this->name+ "\": initializing running average calculation.\n"); acf_nframes = 0; x_history.push_back (std::list<colvarvalue>()); x_history_p = x_history.begin(); } else { if ( (cvm::step_relative() % runave_stride) == 0) { if ((*x_history_p).size() >= runave_length-1) { runave = x; for (std::list<colvarvalue>::iterator xs_i = (*x_history_p).begin(); xs_i != (*x_history_p).end(); xs_i++) { runave += (*xs_i); } runave *= 1.0 / cvm::real (runave_length); runave.apply_constraints(); runave_variance = 0.0; runave_variance += this->dist2 (x, runave); for (std::list<colvarvalue>::iterator xs_i = (*x_history_p).begin(); xs_i != (*x_history_p).end(); xs_i++) { runave_variance += this->dist2 (x, (*xs_i)); } runave_variance *= 1.0 / cvm::real (runave_length-1); runave_os << std::setw (cvm::it_width) << cvm::step_relative() << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << runave << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << std::sqrt (runave_variance) << "\n"; } history_add_value (runave_length, *x_history_p, x); } } } diff --git a/lib/colvars/colvar.h b/lib/colvars/colvar.h index c16355dd1..6466ac1bf 100644 --- a/lib/colvars/colvar.h +++ b/lib/colvars/colvar.h @@ -1,578 +1,578 @@ // -*- c++ -*- #ifndef COLVAR_H #define COLVAR_H #include <iostream> #include <iomanip> #include <cmath> #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" /// \brief A collective variable (main class); to be defined, it needs /// at least one object of a derived class of colvar::cvc; it /// calculates and returns a \link colvarvalue \endlink object /// /// This class parses the configuration, defines the behaviour and /// stores the value (\link colvar::x \endlink) and all related data /// of a collective variable. How the value is calculated is defined /// in \link colvar::cvc \endlink and its derived classes. The /// \link colvar \endlink object contains pointers to multiple \link /// colvar::cvc \endlink derived objects, which can be combined /// together into one collective variable. This makes possible to /// implement new collective variables at runtime based on the /// existing ones. Currently, this possibility is limited to a /// polynomial, using the coefficients cvc::sup_coeff and the /// exponents cvc::sup_np. In case of non-scalar variables, /// only exponents equal to 1 are accepted. /// /// Please note that most of its members are \link colvarvalue /// \endlink objects, i.e. they can handle different data types /// together, and must all be set to the same type of colvar::x by /// using the colvarvalue::type() member function before using them /// together in assignments or other operations; this is usually done /// automatically in the constructor. If you add a new member of /// \link colvarvalue \endlink type, you should also add its /// initialization line in the \link colvar \endlink constructor. class colvar : public colvarparse { public: /// Name std::string name; /// Type of value colvarvalue::Type type() const; /// \brief Current value (previously obtained from calc() or read_traj()) colvarvalue const & value() const; /// \brief Current actual value (not extended DOF) colvarvalue const & actual_value() const; /// \brief Current velocity (previously obtained from calc() or read_traj()) colvarvalue const & velocity() const; /// \brief Current system force (previously obtained from calc() or /// read_traj()). Note: this is calculated using the atomic forces /// from the last simulation step. /// /// Total atom forces are read from the MD program, the total force /// acting on the collective variable is calculated summing those /// from all colvar components, the bias and walls forces are /// subtracted. colvarvalue const & system_force() const; /// \brief /// \brief Typical fluctuation amplitude for this collective /// variable (e.g. local width of a free energy basin) /// /// In metadynamics calculations, \link colvarbias_meta \endlink, /// this value is used to calculate the width of a gaussian. In ABF /// calculations, \link colvarbias_abf \endlink, it is used to /// calculate the grid spacing in the direction of this collective /// variable. cvm::real width; /// \brief True if this \link colvar \endlink is a linear /// combination of \link cvc \endlink elements bool b_linear; /// \brief True if all \link cvc \endlink objects are capable /// of calculating inverse gradients bool b_inverse_gradients; /// \brief True if all \link cvc \endlink objects are capable /// of calculating Jacobian forces bool b_Jacobian_force; /// \brief Options controlling the behaviour of the colvar during /// the simulation, which are set from outside the cvcs enum task { /// \brief Gradients are calculated and temporarily stored, so /// that external forces can be applied task_gradients, /// \brief Collect atomic gradient data from all cvcs into vector /// atomic_gradients task_collect_gradients, /// \brief Calculate the velocity with finite differences task_fdiff_velocity, /// \brief The system force is calculated, projecting the atomic /// forces on the inverse gradients task_system_force, /// \brief The variable has a harmonic restraint around a moving /// center with fictitious mass; bias forces will be applied to /// the center task_extended_lagrangian, /// \brief The extended system coordinate undergoes Langevin /// dynamics task_langevin, /// \brief Output the potential and kinetic energies /// (for extended Lagrangian colvars only) task_output_energy, /// \brief Compute analytically the "force" arising from the /// geometric entropy component (for example, that from the angular /// states orthogonal to a distance vector) task_Jacobian_force, /// \brief Report the Jacobian force as part of the system force /// if disabled, apply a correction internally to cancel it task_report_Jacobian_force, /// \brief Output the value to the trajectory file (on by default) task_output_value, /// \brief Output the velocity to the trajectory file task_output_velocity, /// \brief Output the applied force to the trajectory file task_output_applied_force, /// \brief Output the system force to the trajectory file task_output_system_force, /// \brief A lower boundary is defined task_lower_boundary, /// \brief An upper boundary is defined task_upper_boundary, /// \brief Provide a discretization of the values of the colvar to /// be used by the biases or in analysis (needs lower and upper /// boundary) task_grid, /// \brief Apply a restraining potential (|x-xb|^2) to the colvar /// when it goes below the lower wall task_lower_wall, /// \brief Apply a restraining potential (|x-xb|^2) to the colvar /// when it goes above the upper wall task_upper_wall, /// \brief Compute running average task_runave, /// \brief Compute time correlation function task_corrfunc, /// \brief Number of possible tasks task_ntot }; /// Tasks performed by this colvar bool tasks[task_ntot]; protected: /* extended: H = H_{0} + \sum_{i} 1/2*\lambda*(S_i(x(t))-s_i(t))^2 \\ + \sum_{i} 1/2*m_i*(ds_i(t)/dt)^2 \\ + \sum_{t'<t} W * exp (-1/2*\sum_{i} (s_i(t')-s_i(t))^2/(\delta{}s_i)^2) \\ + \sum_{w} (\sum_{i}c_{w,i}s_i(t) - D_w)^M/(\Sigma_w)^M normal: H = H_{0} + \sum_{t'<t} W * exp (-1/2*\sum_{i} (S_i(x(t'))-S_i(x(t)))^2/(\delta{}S_i)^2) \\ + \sum_{w} (\sum_{i}c_{w,i}S_i(t) - D_w)^M/(\Sigma_w)^M output: H = H_{0} (only output S(x), no forces) Here: S(x(t)) = x s(t) = xr DS = Ds = delta */ /// Value of the colvar colvarvalue x; /// Cached reported value (x may be manipulated) colvarvalue x_reported; /// Finite-difference velocity colvarvalue v_fdiff; inline colvarvalue fdiff_velocity (colvarvalue const &xold, colvarvalue const &xnew) { // using the gradient of the square distance to calculate the // velocity (non-scalar variables automatically taken into // account) cvm::real dt = cvm::dt(); return ( ( (dt > 0.0) ? (1.0/dt) : 1.0 ) * 0.5 * dist2_lgrad (xnew, xold) ); } /// Cached reported velocity colvarvalue v_reported; // Options for task_extended_lagrangian /// Restraint center colvarvalue xr; /// Velocity of the restraint center colvarvalue vr; /// Mass of the restraint center cvm::real ext_mass; /// Restraint force constant cvm::real ext_force_k; /// Friction coefficient for Langevin extended dynamics cvm::real ext_gamma; /// Amplitude of Gaussian white noise for Langevin extended dynamics cvm::real ext_sigma; - + /// \brief Harmonic restraint force colvarvalue fr; /// \brief Jacobian force, when task_Jacobian_force is enabled colvarvalue fj; /// Cached reported system force colvarvalue ft_reported; public: /// \brief Bias force; reset_bias_force() should be called before /// the biases are updated colvarvalue fb; /// \brief Total \em applied force; fr (if task_extended_lagrangian /// is defined), fb (if biases are applied) and the walls' forces /// (if defined) contribute to it colvarvalue f; /// \brief Total force, as derived from the atomic trajectory; /// should equal the total system force plus \link f \endlink colvarvalue ft; /// Period, if it is a constant cvm::real period; /// \brief Same as above, but also takes into account components /// with a variable period, such as distanceZ bool b_periodic; /// \brief Expand the boundaries of multiples of width, to keep the /// value always within range bool expand_boundaries; /// \brief Location of the lower boundary colvarvalue lower_boundary; /// \brief Location of the lower wall colvarvalue lower_wall; /// \brief Force constant for the lower boundary potential (|x-xb|^2) cvm::real lower_wall_k; /// \brief Whether this colvar has a hard lower boundary bool hard_lower_boundary; /// \brief Location of the upper boundary colvarvalue upper_boundary; /// \brief Location of the upper wall colvarvalue upper_wall; /// \brief Force constant for the upper boundary potential (|x-xb|^2) cvm::real upper_wall_k; /// \brief Whether this colvar has a hard upper boundary bool hard_upper_boundary; /// \brief Is the interval defined by the two boundaries periodic? bool periodic_boundaries() const; /// \brief Is the interval defined by the two boundaries periodic? bool periodic_boundaries (colvarvalue const &lb, colvarvalue const &ub) const; /// Constructor colvar (std::string const &conf); /// Enable the specified task void enable (colvar::task const &t); /// Disable the specified task void disable (colvar::task const &t); /// Destructor ~colvar(); /// \brief Calculate the colvar value and all the other requested /// quantities void calc(); /// \brief Calculate just the value, and store it in the argument void calc_value (colvarvalue &xn); /// Set the total biasing force to zero void reset_bias_force(); /// Add to the total force from biases void add_bias_force (colvarvalue const &force); /// \brief Collect all forces on this colvar, integrate internal /// equations of motion of internal degrees of freedom; see also /// colvar::communicate_forces() /// return colvar energy if extended Lagrandian active cvm::real update(); /// \brief Communicate forces (previously calculated in /// colvar::update()) to the external degrees of freedom void communicate_forces(); /// \brief Use the internal metrics (as from \link cvc /// \endlink objects) to calculate square distances and gradients /// /// Handles correctly symmetries and periodic boundary conditions cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Use the internal metrics (as from \link cvc /// \endlink objects) to calculate square distances and gradients /// /// Handles correctly symmetries and periodic boundary conditions colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Use the internal metrics (as from \link cvc /// \endlink objects) to calculate square distances and gradients /// /// Handles correctly symmetries and periodic boundary conditions colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Use the internal metrics (as from \link cvc /// \endlink objects) to compare colvar values /// /// Handles correctly symmetries and periodic boundary conditions cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Use the internal metrics (as from \link cvc /// \endlink objects) to wrap a value into a standard interval /// /// Handles correctly symmetries and periodic boundary conditions void wrap (colvarvalue &x) const; /// Read the analysis tasks void parse_analysis (std::string const &conf); /// Perform analysis tasks void analyse(); /// Read the value from a collective variable trajectory file std::istream & read_traj (std::istream &is); /// Output formatted values to the trajectory file std::ostream & write_traj (std::ostream &os); /// Write a label to the trajectory file (comment line) std::ostream & write_traj_label (std::ostream &os); /// Read the collective variable from a restart file std::istream & read_restart (std::istream &is); /// Write the collective variable to a restart file std::ostream & write_restart (std::ostream &os); /// Write output files (if defined, e.g. in analysis mode) void write_output_files(); protected: /// Previous value (to calculate velocities during analysis) colvarvalue x_old; /// Time series of values and velocities used in correlation - /// functions + /// functions std::list< std::list<colvarvalue> > acf_x_history, acf_v_history; /// Time series of values and velocities used in correlation /// functions (pointers)x std::list< std::list<colvarvalue> >::iterator acf_x_history_p, acf_v_history_p; /// Time series of values and velocities used in running averages std::list< std::list<colvarvalue> > x_history; /// Time series of values and velocities used in correlation /// functions (pointers)x std::list< std::list<colvarvalue> >::iterator x_history_p; /// \brief Collective variable with which the correlation is /// calculated (default: itself) std::string acf_colvar_name; /// Length of autocorrelation function (ACF) size_t acf_length; /// After how many steps the ACF starts size_t acf_offset; /// How many timesteps separate two ACF values size_t acf_stride; /// Number of frames for each ACF point size_t acf_nframes; /// Normalize the ACF to a maximum value of 1? bool acf_normalize; /// ACF values std::vector<cvm::real> acf; /// Name of the file to write the ACF std::string acf_outfile; /// Type of autocorrelation function (ACF) enum acf_type_e { /// Unset type acf_notset, /// Velocity ACF, scalar product between v(0) and v(t) acf_vel, /// Coordinate ACF, scalar product between x(0) and x(t) acf_coor, /// \brief Coordinate ACF, second order Legendre polynomial /// between x(0) and x(t) (does not work with scalar numbers) acf_p2coor }; /// Type of autocorrelation function (ACF) acf_type_e acf_type; /// \brief Velocity ACF, scalar product between v(0) and v(t) void calc_vel_acf (std::list<colvarvalue> &v_history, colvarvalue const &v); /// \brief Coordinate ACF, scalar product between x(0) and x(t) /// (does not work with scalar numbers) void calc_coor_acf (std::list<colvarvalue> &x_history, colvarvalue const &x); /// \brief Coordinate ACF, second order Legendre polynomial between /// x(0) and x(t) (does not work with scalar numbers) void calc_p2coor_acf (std::list<colvarvalue> &x_history, colvarvalue const &x); /// Calculate the auto-correlation function (ACF) void calc_acf(); /// Save the ACF to a file void write_acf (std::ostream &os); /// Length of running average series size_t runave_length; /// Timesteps to skip between two values in the running average series size_t runave_stride; /// Name of the file to write the running average std::ofstream runave_os; /// Current value of the running average colvarvalue runave; /// Current value of the square deviation from the running average cvm::real runave_variance; /// Calculate the running average and its standard deviation void calc_runave(); /// If extended Lagrangian active: colvar energies (kinetic and harmonic potential) cvm::real kinetic_energy; cvm::real potential_energy; public: // collective variable component base class class cvc; // currently available collective variable components // scalar colvar components class distance; class distance_z; class distance_xy; class distance_inv; class angle; class dihedral; class coordnum; class selfcoordnum; class h_bond; class rmsd; class orientation_angle; class tilt; class spin_angle; class gyration; class inertia; class inertia_z; class eigenvector; class alpha_dihedrals; class alpha_angles; class dihedPC; // non-scalar components class distance_vec; class distance_dir; class orientation; protected: /// \brief Array of \link cvc \endlink objects std::vector<cvc *> cvcs; /// \brief Initialize the sorted list of atom IDs for atoms involved /// in all cvcs (called when enabling task_collect_gradients) void build_atom_list (void); public: /// \brief Sorted array of (zero-based) IDs for all atoms involved std::vector<int> atom_ids; /// \brief Array of atomic gradients collected from all cvcs /// with appropriate components, rotations etc. /// For scalar variables only! std::vector<cvm::rvector> atomic_gradients; inline size_t n_components () const { return cvcs.size(); } }; inline colvar * cvm::colvar_p (std::string const &name) { for (std::vector<colvar *>::iterator cvi = cvm::colvars.begin(); cvi != cvm::colvars.end(); cvi++) { if ((*cvi)->name == name) { return (*cvi); } } return NULL; } inline colvarvalue::Type colvar::type() const { return x.type(); } inline colvarvalue const & colvar::value() const { return x_reported; } inline colvarvalue const & colvar::actual_value() const { return x; } inline colvarvalue const & colvar::velocity() const { return v_reported; } inline colvarvalue const & colvar::system_force() const { return ft_reported; } inline void colvar::add_bias_force (colvarvalue const &force) { fb += force; } inline void colvar::reset_bias_force() { fb.reset(); } #endif diff --git a/lib/colvars/colvaratoms.cpp b/lib/colvars/colvaratoms.cpp index 34b171e08..fdfb7c1e1 100644 --- a/lib/colvars/colvaratoms.cpp +++ b/lib/colvars/colvaratoms.cpp @@ -1,844 +1,844 @@ #include "colvarmodule.h" #include "colvarparse.h" #include "colvaratoms.h" // member functions of the "atom" class depend tightly on the MD interface, and are // thus defined in colvarproxy_xxx.cpp // in this file only atom_group functions are defined // Note: "conf" is the configuration of the cvc who is using this atom group; // "key" is the name of the atom group (e.g. "atoms", "group1", "group2", ...) cvm::atom_group::atom_group (std::string const &conf, char const *key) : b_center (false), b_rotate (false), b_user_defined_fit (false), ref_pos_group (NULL), b_fit_gradients (false), noforce (false) { cvm::log ("Defining atom group \""+ std::string (key)+"\".\n"); // real work is done by parse parse (conf, key); } cvm::atom_group::atom_group (std::vector<cvm::atom> const &atoms) - : b_dummy (false), b_center (false), b_rotate (false), - ref_pos_group (NULL), b_fit_gradients (false), + : b_dummy (false), b_center (false), b_rotate (false), + ref_pos_group (NULL), b_fit_gradients (false), 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), b_fit_gradients (false), + : b_dummy (false), b_center (false), b_rotate (false), + ref_pos_group (NULL), b_fit_gradients (false), 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::parse (std::string const &conf, char const *key) { 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; { // std::vector<int> atom_indexes; std::string numbers_conf = ""; size_t pos = 0; std::vector<int> atom_indexes; while (key_lookup (group_conf, "atomNumbers", numbers_conf, pos)) { if (numbers_conf.size()) { std::istringstream is (numbers_conf); int ia; while (is >> ia) { atom_indexes.push_back (ia); } } 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"); atom_indexes.clear(); } std::string index_group_name; if (get_keyval (group_conf, "indexGroup", index_group_name)) { // use an index group from the index file read globally std::list<std::string>::iterator names_i = cvm::index_group_names.begin(); std::list<std::vector<int> >::iterator index_groups_i = cvm::index_groups.begin(); for ( ; names_i != cvm::index_group_names.end() ; names_i++, index_groups_i++) { if (*names_i == index_group_name) break; } if (names_i == cvm::index_group_names.end()) { cvm::fatal_error ("Error: could not find index group "+ index_group_name+" among those provided by the index file.\n"); } this->reserve (index_groups_i->size()); for (size_t i = 0; i < index_groups_i->size(); i++) { this->push_back (cvm::atom ((*index_groups_i)[i])); } } } { 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(); + 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 + } else b_dummy = false; - if (b_dummy && (this->size())) + 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) { bool enable_forces = true; // disableForces is deprecated if (get_keyval (group_conf, "enableForces", enable_forces, true, mode)) { noforce = !enable_forces; } else { get_keyval (group_conf, "disableForces", noforce, false, mode); } } // FITTING OPTIONS 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); // is the user setting explicit options? b_user_defined_fit = b_defined_center || b_defined_rotate; get_keyval (group_conf, "enableFitGradients", b_fit_gradients, true, mode); if (b_center || b_rotate) { if (b_dummy) cvm::fatal_error ("Error: centerReference or rotateReference " "cannot be defined for a dummy atom.\n"); if (key_lookup (group_conf, "refPositionsGroup")) { // instead of this group, define another group to compute the fit 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 *group_for_fit = ref_pos_group ? ref_pos_group : this; get_keyval (group_conf, "refPositions", ref_pos, ref_pos, mode); 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(); ref_pos.resize (group_for_fit->size()); } 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 (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 subtract it center_ref_pos(); } else { #if (! defined (COLVARS_STANDALONE)) cvm::fatal_error ("Error: no reference positions provided.\n"); #endif } if (b_fit_gradients) { group_for_fit->fit_gradients.assign (group_for_fit->size(), cvm::atom_pos (0.0, 0.0, 0.0)); rot.request_group1_gradients (group_for_fit->size()); } if (b_rotate && !noforce) { cvm::log ("Warning: atom group \""+std::string (key)+ "\" will be aligned to a fixed orientation given by the reference positions provided. " "If the internal structure of the group changes too much (i.e. its RMSD is comparable " "to its radius of gyration), the optimal rotation and its gradients may become discontinuous. " "If that happens, use refPositionsGroup (or a different definition for it if already defined) " "to align the coordinates.\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())+" atoms initialized: total mass = "+ cvm::to_str (this->total_mass)+".\n"); cvm::decrease_depth(); } 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; for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->read_position(); } if (ref_pos_group) ref_pos_group->read_positions(); } void cvm::atom_group::calc_apply_roto_translation() { atom_group *fit_group = ref_pos_group ? ref_pos_group : this; if (b_center) { // center on the origin first 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) 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) { // align with 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) +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) +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(); } } } 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; } 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; } } void cvm::atom_group::calc_fit_gradients() { if (b_dummy) return; if ((!b_center) && (!b_rotate)) return; // no fit if (cvm::debug()) cvm::log ("Calculating fit gradients.\n"); atom_group *group_for_fit = ref_pos_group ? ref_pos_group : this; group_for_fit->fit_gradients.assign (group_for_fit->size(), cvm::rvector (0.0, 0.0, 0.0)); if (b_center) { // add the center of geometry contribution to the gradients for (size_t i = 0; i < this->size(); i++) { // need to bring the gradients in original frame first cvm::rvector const atom_grad = b_rotate ? (rot.inverse()).rotate ((*this)[i].grad) : (*this)[i].grad; for (size_t j = 0; j < group_for_fit->size(); j++) { group_for_fit->fit_gradients[j] += (-1.0)/(cvm::real (group_for_fit->size())) * atom_grad; } } } if (b_rotate) { - + // add the rotation matrix contribution to the gradients cvm::rotation const rot_inv = rot.inverse(); cvm::atom_pos const cog = this->center_of_geometry(); - + for (size_t i = 0; i < this->size(); i++) { cvm::atom_pos const pos_orig = rot_inv.rotate ((b_center ? ((*this)[i].pos - cog) : ((*this)[i].pos))); for (size_t j = 0; j < group_for_fit->size(); j++) { // calculate \partial(R(q) \vec{x}_i)/\partial q) \cdot \partial\xi/\partial\vec{x}_i cvm::quaternion const dxdq = rot.q.position_derivative_inner (pos_orig, (*this)[i].grad); // multiply by \cdot {\partial q}/\partial\vec{x}_j and add it to the fit gradients for (size_t iq = 0; iq < 4; iq++) { group_for_fit->fit_gradients[j] += dxdq[iq] * rot.dQ0_1[j][iq]; } } } } if (cvm::debug()) cvm::log ("Done calculating fit gradients.\n"); } 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 " "\"enableForces\" set to off.\n"); if (b_rotate) { // rotate forces back to the original 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 { for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->apply_force (force * ai->grad); } } if ((b_center || b_rotate) && b_fit_gradients) { atom_group *group_for_fit = ref_pos_group ? ref_pos_group : this; // add the contribution from the roto-translational fit to the gradients if (b_rotate) { // rotate forces back to the original frame cvm::rotation const rot_inv = rot.inverse(); for (size_t j = 0; j < group_for_fit->size(); j++) { (*group_for_fit)[j].apply_force (rot_inv.rotate (force * group_for_fit->fit_gradients[j])); } } else { for (size_t j = 0; j < group_for_fit->size(); j++) { (*group_for_fit)[j].apply_force (force * group_for_fit->fit_gradients[j]); } } } } 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 3cca61baa..96e383a91 100644 --- a/lib/colvars/colvaratoms.h +++ b/lib/colvars/colvaratoms.h @@ -1,327 +1,327 @@ // -*- 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!) int 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 and index to invalid numbers atom() : id (-1), index (-1) { reset_data(); } /// \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 When updating atomic coordinates, translate them to align with the /// center of mass of the reference coordinates bool b_center; /// \brief When updating atom coordinates (and after /// centering them if b_center is set), rotate the group to /// align with the reference coordinates. /// /// 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; /// The rotation calculated automatically if b_rotate is defined cvm::rotation rot; /// \brief Indicates that the user has explicitly set centerReference or /// rotateReference, and the corresponding reference: /// cvc's (eg rmsd, eigenvector) will not override the user's choice bool b_user_defined_fit; /// \brief Whether or not the derivatives of the roto-translation /// should be included when calculating the colvar's gradients (default: no) bool b_fit_gradients; /// \brief use reference coordinates for b_center or b_rotate 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 If b_center or b_rotate is true, use this group to /// define the transformation (default: this 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); /// \brief Initialize the group by looking up its configuration /// string in conf and parsing it void parse (std::string const &conf, char const *key); /// \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 + /// \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, calc_apply_roto_translation() will be called too void read_positions(); /// \brief (Re)calculate the optimal roto-translation void calc_apply_roto_translation(); /// \brief Save center of geometry fo ref positions, - /// then subtract it + /// 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(); if (ref_pos_group) ref_pos_group->reset_atoms_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, assuming /// that coordinates are already pbc-wrapped cvm::atom_pos center_of_geometry() const; /// \brief Return the center of mass of the positions, assuming that /// coordinates are already pbc-wrapped cvm::atom_pos center_of_mass() const; /// \brief Atom positions at 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 Calculate the derivatives of the fitting transformation void calc_fit_gradients(); /// \brief Derivatives of the fitting transformation std::vector<cvm::atom_pos> fit_gradients; /// \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 rotated 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/colvarbias.cpp b/lib/colvars/colvarbias.cpp index 80877c97f..58ec049b3 100644 --- a/lib/colvars/colvarbias.cpp +++ b/lib/colvars/colvarbias.cpp @@ -1,590 +1,590 @@ #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarbias.h" colvarbias::colvarbias (std::string const &conf, char const *key) : colvarparse(), has_data (false) { cvm::log ("Initializing a new \""+std::string (key)+"\" instance.\n"); size_t rank = 1; std::string const key_str (key); if (to_lower_cppstr (key_str) == std::string ("abf")) { rank = cvm::n_abf_biases+1; } if (to_lower_cppstr (key_str) == std::string ("harmonic")) { rank = cvm::n_harm_biases+1; } if (to_lower_cppstr (key_str) == std::string ("histogram")) { rank = cvm::n_histo_biases+1; } if (to_lower_cppstr (key_str) == std::string ("metadynamics")) { rank = cvm::n_meta_biases+1; } get_keyval (conf, "name", name, key_str+cvm::to_str (rank)); for (std::vector<colvarbias *>::iterator bi = cvm::biases.begin(); bi != cvm::biases.end(); bi++) { if ((*bi)->name == this->name) cvm::fatal_error ("Error: this bias cannot have the same name, \""+this->name+ "\", of another bias.\n"); } // lookup the associated colvars std::vector<std::string> colvars_str; if (get_keyval (conf, "colvars", colvars_str)) { for (size_t i = 0; i < colvars_str.size(); i++) { add_colvar (colvars_str[i]); } } if (!colvars.size()) { cvm::fatal_error ("Error: no collective variables specified.\n"); } get_keyval (conf, "outputEnergy", b_output_energy, false); } colvarbias::colvarbias() : colvarparse(), has_data (false) {} void colvarbias::add_colvar (std::string const &cv_name) { if (colvar *cvp = cvm::colvar_p (cv_name)) { cvp->enable (colvar::task_gradients); if (cvm::debug()) cvm::log ("Applying this bias to collective variable \""+ - cvp->name+"\".\n"); + cvp->name+"\".\n"); colvars.push_back (cvp); colvar_forces.push_back (colvarvalue (cvp->type())); } else { cvm::fatal_error ("Error: cannot find a colvar named \""+ cv_name+"\".\n"); } } void colvarbias::communicate_forces() { for (size_t i = 0; i < colvars.size(); i++) { if (cvm::debug()) { cvm::log ("Communicating a force to colvar \""+ colvars[i]->name+"\", of type \""+ colvarvalue::type_desc[colvars[i]->type()]+"\".\n"); } colvars[i]->add_bias_force (colvar_forces[i]); } -} +} void colvarbias::change_configuration(std::string const &conf) { cvm::fatal_error ("Error: change_configuration() not implemented.\n"); } cvm::real colvarbias::energy_difference(std::string const &conf) { cvm::fatal_error ("Error: energy_difference() not implemented.\n"); return 0.; } std::ostream & colvarbias::write_traj_label (std::ostream &os) { os << " "; - if (b_output_energy) + if (b_output_energy) os << " E_" << cvm::wrap_string (this->name, cvm::en_width-2); return os; } std::ostream & colvarbias::write_traj (std::ostream &os) { os << " "; - if (b_output_energy) + if (b_output_energy) os << " " << bias_energy; return os; } colvarbias_harmonic::colvarbias_harmonic (std::string const &conf, char const *key) - : colvarbias (conf, key), + : colvarbias (conf, key), target_nsteps (0), target_nstages (0) { get_keyval (conf, "forceConstant", force_k, 1.0); for (size_t i = 0; i < colvars.size(); i++) { if (colvars[i]->width != 1.0) cvm::log ("The force constant for colvar \""+colvars[i]->name+ "\" will be rescaled to "+ cvm::to_str (force_k/(colvars[i]->width*colvars[i]->width))+ " according to the specified width.\n"); } // get the initial restraint centers colvar_centers.resize (colvars.size()); colvar_centers_raw.resize (colvars.size()); for (size_t i = 0; i < colvars.size(); i++) { colvar_centers[i].type (colvars[i]->type()); colvar_centers_raw[i].type (colvars[i]->type()); } if (get_keyval (conf, "centers", colvar_centers, colvar_centers)) { for (size_t i = 0; i < colvars.size(); i++) { colvar_centers[i].apply_constraints(); colvar_centers_raw[i] = colvar_centers[i]; } } else { colvar_centers.clear(); cvm::fatal_error ("Error: must define the initial centers of the restraints.\n"); } if (colvar_centers.size() != colvars.size()) cvm::fatal_error ("Error: number of harmonic centers does not match " "that of collective variables.\n"); if (get_keyval (conf, "targetCenters", target_centers, colvar_centers)) { b_chg_centers = true; for (size_t i = 0; i < target_centers.size(); i++) { target_centers[i].apply_constraints(); } } else { b_chg_centers = false; target_centers.clear(); } if (get_keyval (conf, "targetForceConstant", target_force_k, 0.0)) { if (b_chg_centers) cvm::fatal_error ("Error: cannot specify both targetCenters and targetForceConstant.\n"); starting_force_k = force_k; b_chg_force_k = true; get_keyval (conf, "targetEquilSteps", target_equil_steps, 0); get_keyval (conf, "lambdaSchedule", lambda_schedule, lambda_schedule); if (lambda_schedule.size()) { // There is one more lambda-point than stages target_nstages = lambda_schedule.size() - 1; } } else { b_chg_force_k = false; } if (b_chg_centers || b_chg_force_k) { get_keyval (conf, "targetNumSteps", target_nsteps, 0); if (!target_nsteps) cvm::fatal_error ("Error: targetNumSteps must be non-zero.\n"); if (get_keyval (conf, "targetNumStages", target_nstages, target_nstages) && lambda_schedule.size()) { cvm::fatal_error ("Error: targetNumStages and lambdaSchedule are incompatible.\n"); } if (target_nstages) { // This means that either numStages of lambdaSchedule has been provided stage = 0; restraint_FE = 0.0; } if (get_keyval (conf, "targetForceExponent", force_k_exp, 1.0)) { if (! b_chg_force_k) cvm::log ("Warning: not changing force constant: targetForceExponent will be ignored\n"); if (force_k_exp < 1.0) cvm::log ("Warning: for all practical purposes, targetForceExponent should be 1.0 or greater.\n"); } } get_keyval (conf, "outputCenters", b_output_centers, false); get_keyval (conf, "outputAccumulatedWork", b_output_acc_work, false); acc_work = 0.0; if (cvm::debug()) cvm::log ("Done initializing a new harmonic restraint bias.\n"); } colvarbias_harmonic::~colvarbias_harmonic () { if (cvm::n_harm_biases > 0) cvm::n_harm_biases -= 1; } void colvarbias_harmonic::change_configuration (std::string const &conf) { get_keyval (conf, "forceConstant", force_k, force_k); if (get_keyval (conf, "centers", colvar_centers, colvar_centers)) { for (size_t i = 0; i < colvars.size(); i++) { colvar_centers[i].apply_constraints(); colvar_centers_raw[i] = colvar_centers[i]; } } } cvm::real colvarbias_harmonic::energy_difference (std::string const &conf) { std::vector<colvarvalue> alt_colvar_centers; cvm::real alt_force_k; cvm::real alt_bias_energy = 0.0; get_keyval (conf, "forceConstant", alt_force_k, force_k); alt_colvar_centers.resize (colvars.size()); for (size_t i = 0; i < colvars.size(); i++) { alt_colvar_centers[i].type (colvars[i]->type()); } if (get_keyval (conf, "centers", alt_colvar_centers, colvar_centers)) { for (size_t i = 0; i < colvars.size(); i++) { colvar_centers[i].apply_constraints(); } } for (size_t i = 0; i < colvars.size(); i++) { alt_bias_energy += 0.5 * alt_force_k / (colvars[i]->width * colvars[i]->width) * colvars[i]->dist2 (colvars[i]->value(), alt_colvar_centers[i]); } return alt_bias_energy - bias_energy; } cvm::real colvarbias_harmonic::update() { bias_energy = 0.0; if (cvm::debug()) cvm::log ("Updating the harmonic bias \""+this->name+"\".\n"); // Setup first stage of staged variable force constant calculation if (b_chg_force_k && target_nstages && cvm::step_absolute() == 0) { cvm::real lambda; if (lambda_schedule.size()) { lambda = lambda_schedule[0]; } else { lambda = 0.0; } force_k = starting_force_k + (target_force_k - starting_force_k) * std::pow (lambda, force_k_exp); cvm::log ("Harmonic restraint " + this->name + ", stage " + cvm::to_str(stage) + " : lambda = " + cvm::to_str(lambda)); cvm::log ("Setting force constant to " + cvm::to_str (force_k)); } if (b_chg_centers) { if (!centers_incr.size()) { // if this is the first calculation, calculate the advancement // at each simulation step (or stage, if applicable) // (take current stage into account: it can be non-zero // if we are restarting a staged calculation) centers_incr.resize (colvars.size()); for (size_t i = 0; i < colvars.size(); i++) { centers_incr[i].type (colvars[i]->type()); centers_incr[i] = (target_centers[i] - colvar_centers_raw[i]) / cvm::real ( target_nstages ? (target_nstages - stage) : (target_nsteps - cvm::step_absolute())); } if (cvm::debug()) cvm::log ("Center increment for the harmonic bias \""+ this->name+"\": "+cvm::to_str (centers_incr)+" at stage "+cvm::to_str (stage)+ ".\n"); } if (target_nstages) { if ((cvm::step_relative() > 0) && (cvm::step_absolute() % target_nsteps) == 0 && stage < target_nstages) { for (size_t i = 0; i < colvars.size(); i++) { colvar_centers_raw[i] += centers_incr[i]; colvar_centers[i] = colvar_centers_raw[i]; colvars[i]->wrap(colvar_centers[i]); colvar_centers[i].apply_constraints(); } stage++; cvm::log ("Moving restraint stage " + cvm::to_str(stage) + " : setting centers to " + cvm::to_str (colvar_centers) + " at step " + cvm::to_str (cvm::step_absolute())); } } else if ((cvm::step_relative() > 0) && (cvm::step_absolute() <= target_nsteps)) { // move the restraint centers in the direction of the targets // (slow growth) for (size_t i = 0; i < colvars.size(); i++) { colvar_centers_raw[i] += centers_incr[i]; colvar_centers[i] = colvar_centers_raw[i]; colvars[i]->wrap(colvar_centers[i]); colvar_centers[i].apply_constraints(); } } if (cvm::debug()) cvm::log ("Current centers for the harmonic bias \""+ this->name+"\": "+cvm::to_str (colvar_centers)+".\n"); } if (b_chg_force_k) { // Coupling parameter, between 0 and 1 cvm::real lambda; if (target_nstages) { // TI calculation: estimate free energy derivative // need current lambda if (lambda_schedule.size()) { lambda = lambda_schedule[stage]; } else { lambda = cvm::real(stage) / cvm::real(target_nstages); } if (target_equil_steps == 0 || cvm::step_absolute() % target_nsteps >= target_equil_steps) { // Start averaging after equilibration period, if requested - + // Square distance normalized by square colvar width cvm::real dist_sq = 0.0; for (size_t i = 0; i < colvars.size(); i++) { dist_sq += colvars[i]->dist2 (colvars[i]->value(), colvar_centers[i]) / (colvars[i]->width * colvars[i]->width); } restraint_FE += 0.5 * force_k_exp * std::pow(lambda, force_k_exp - 1.0) * (target_force_k - starting_force_k) * dist_sq; } // Finish current stage... if (cvm::step_absolute() % target_nsteps == 0 && cvm::step_absolute() > 0) { cvm::log ("Lambda= " + cvm::to_str (lambda) + " dA/dLambda= " + cvm::to_str (restraint_FE / cvm::real(target_nsteps - target_equil_steps))); - + // ...and move on to the next one if (stage < target_nstages) { restraint_FE = 0.0; stage++; if (lambda_schedule.size()) { lambda = lambda_schedule[stage]; } else { lambda = cvm::real(stage) / cvm::real(target_nstages); } force_k = starting_force_k + (target_force_k - starting_force_k) * std::pow (lambda, force_k_exp); cvm::log ("Harmonic restraint " + this->name + ", stage " + cvm::to_str(stage) + " : lambda = " + cvm::to_str(lambda)); cvm::log ("Setting force constant to " + cvm::to_str (force_k)); } } } else if (cvm::step_absolute() <= target_nsteps) { // update force constant (slow growth) lambda = cvm::real(cvm::step_absolute()) / cvm::real(target_nsteps); force_k = starting_force_k + (target_force_k - starting_force_k) * std::pow (lambda, force_k_exp); } } if (cvm::debug()) cvm::log ("Done updating the harmonic bias \""+this->name+"\".\n"); - + // Force and energy calculation for (size_t i = 0; i < colvars.size(); i++) { colvar_forces[i] = (-0.5) * force_k / (colvars[i]->width * colvars[i]->width) * colvars[i]->dist2_lgrad (colvars[i]->value(), colvar_centers[i]); bias_energy += 0.5 * force_k / (colvars[i]->width * colvars[i]->width) * colvars[i]->dist2(colvars[i]->value(), colvar_centers[i]); if (cvm::debug()) cvm::log ("dist_grad["+cvm::to_str (i)+ "] = "+cvm::to_str (colvars[i]->dist2_lgrad (colvars[i]->value(), colvar_centers[i]))+"\n"); } if (b_output_acc_work) { if ((cvm::step_relative() > 0) || (cvm::step_absolute() == 0)) { for (size_t i = 0; i < colvars.size(); i++) { // project forces on the calculated increments at this step acc_work += colvar_forces[i] * centers_incr[i]; } } } if (cvm::debug()) cvm::log ("Current forces for the harmonic bias \""+ this->name+"\": "+cvm::to_str (colvar_forces)+".\n"); return bias_energy; } std::istream & colvarbias_harmonic::read_restart (std::istream &is) { size_t const start_pos = is.tellg(); cvm::log ("Restarting harmonic bias \""+ this->name+"\".\n"); std::string key, brace, conf; if ( !(is >> key) || !(key == "harmonic") || !(is >> brace) || !(brace == "{") || !(is >> colvarparse::read_block ("configuration", conf)) ) { cvm::log ("Error: in reading restart configuration for harmonic bias \""+ this->name+"\" at position "+ cvm::to_str (is.tellg())+" in stream.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } -// int id = -1; +// int id = -1; std::string name = ""; // if ( ( (colvarparse::get_keyval (conf, "id", id, -1, colvarparse::parse_silent)) && // (id != this->id) ) || if ( (colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent)) && (name != this->name) ) cvm::fatal_error ("Error: in the restart file, the " "\"harmonic\" block has a wrong name\n"); // if ( (id == -1) && (name == "") ) { if (name.size() == 0) { cvm::fatal_error ("Error: \"harmonic\" block in the restart file " "has no identifiers.\n"); } if (b_chg_centers) { // cvm::log ("Reading the updated restraint centers from the restart.\n"); if (!get_keyval (conf, "centers", colvar_centers)) cvm::fatal_error ("Error: restraint centers are missing from the restart.\n"); if (!get_keyval (conf, "centers_raw", colvar_centers_raw)) cvm::fatal_error ("Error: \"raw\" restraint centers are missing from the restart.\n"); } if (b_chg_force_k) { // cvm::log ("Reading the updated force constant from the restart.\n"); if (!get_keyval (conf, "forceConstant", force_k)) cvm::fatal_error ("Error: force constant is missing from the restart.\n"); } if (target_nstages) { // cvm::log ("Reading current stage from the restart.\n"); if (!get_keyval (conf, "stage", stage)) cvm::fatal_error ("Error: current stage is missing from the restart.\n"); } if (b_output_acc_work) { if (!get_keyval (conf, "accumulatedWork", acc_work)) cvm::fatal_error ("Error: accumulatedWork is missing from the restart.\n"); } is >> brace; if (brace != "}") { cvm::fatal_error ("Error: corrupt restart information for harmonic bias \""+ this->name+"\": no matching brace at position "+ cvm::to_str (is.tellg())+" in the restart file.\n"); is.setstate (std::ios::failbit); } return is; } std::ostream & colvarbias_harmonic::write_restart (std::ostream &os) { os << "harmonic {\n" << " configuration {\n" // << " id " << this->id << "\n" << " name " << this->name << "\n"; if (b_chg_centers) { os << " centers "; for (size_t i = 0; i < colvars.size(); i++) { os << " " << colvar_centers[i]; } os << "\n"; os << " centers_raw "; for (size_t i = 0; i < colvars.size(); i++) { os << " " << colvar_centers_raw[i]; } os << "\n"; } if (b_chg_force_k) { os << " forceConstant " << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) << force_k << "\n"; } if (target_nstages) { os << " stage " << std::setw (cvm::it_width) << stage << "\n"; } if (b_output_acc_work) { os << " accumulatedWork " << acc_work << "\n"; } os << " }\n" << "}\n\n"; return os; } std::ostream & colvarbias_harmonic::write_traj_label (std::ostream &os) { os << " "; - if (b_output_energy) + if (b_output_energy) os << " E_" << cvm::wrap_string (this->name, cvm::en_width-2); - if (b_output_centers) + if (b_output_centers) for (size_t i = 0; i < colvars.size(); i++) { size_t const this_cv_width = (colvars[i]->value()).output_width (cvm::cv_width); os << " x0_" << cvm::wrap_string (colvars[i]->name, this_cv_width-3); } - if (b_output_acc_work) + if (b_output_acc_work) os << " W_" << cvm::wrap_string (this->name, cvm::en_width-2); return os; } std::ostream & colvarbias_harmonic::write_traj (std::ostream &os) { os << " "; - if (b_output_energy) + if (b_output_energy) os << " " << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) << bias_energy; - if (b_output_centers) + if (b_output_centers) for (size_t i = 0; i < colvars.size(); i++) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << colvar_centers[i]; } - if (b_output_acc_work) + if (b_output_acc_work) os << " " << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) << acc_work; return os; } diff --git a/lib/colvars/colvarbias.h b/lib/colvars/colvarbias.h index c2063329b..5a2c2d8c5 100644 --- a/lib/colvars/colvarbias.h +++ b/lib/colvars/colvarbias.h @@ -1,193 +1,193 @@ #ifndef COLVARBIAS_H #define COLVARBIAS_H #include "colvar.h" #include "colvarparse.h" /// \brief Collective variable bias, base class class colvarbias : public colvarparse { public: /// Numeric id of this bias int id; /// Name of this bias std::string name; - + /// Add a new collective variable to this bias void add_colvar (std::string const &cv_name); /// Retrieve colvar values and calculate their biasing forces /// Return bias energy virtual cvm::real update() = 0; /// Load new configuration - force constant and/or centers only virtual void change_configuration(std::string const &conf); /// Calculate change in energy from using alternate configuration virtual cvm::real energy_difference(std::string const &conf); /// Perform analysis tasks virtual inline void analyse() {} /// Send forces to the collective variables void communicate_forces(); /// \brief Constructor - /// + /// /// The constructor of the colvarbias base class is protected, so /// that it can only be called from inherited classes colvarbias (std::string const &conf, char const *key); /// Default constructor colvarbias(); /// Destructor virtual inline ~colvarbias() {} /// Read the bias configuration from a restart file virtual std::istream & read_restart (std::istream &is) = 0; /// Write the bias configuration to a restart file virtual std::ostream & write_restart (std::ostream &os) = 0; /// Write a label to the trajectory file (comment line) virtual std::ostream & write_traj_label (std::ostream &os); /// Output quantities such as the bias energy to the trajectory file virtual std::ostream & write_traj (std::ostream &os); protected: /// \brief Pointers to collective variables to which the bias is /// applied; current values and metric functions will be obtained /// through each colvar object std::vector<colvar *> colvars; /// \brief Current forces from this bias to the colvars std::vector<colvarvalue> colvar_forces; /// \brief Current energy of this bias (colvar_forces should be obtained by deriving this) cvm::real bias_energy; /// Whether to write the current bias energy from this bias to the trajectory file bool b_output_energy; /// \brief Whether this bias has already accumulated information /// (when relevant) bool has_data; }; /// \brief Harmonic restraint, optionally moving towards a target /// (implementation of \link colvarbias \endlink) class colvarbias_harmonic : public colvarbias { public: /// Retrieve colvar values and calculate their biasing forces virtual cvm::real update(); /// Load new configuration - force constant and/or centers only virtual void change_configuration(std::string const &conf); /// Calculate change in energy from using alternate configuration virtual cvm::real energy_difference(std::string const &conf); /// Read the bias configuration from a restart file virtual std::istream & read_restart (std::istream &is); /// Write the bias configuration to a restart file virtual std::ostream & write_restart (std::ostream &os); /// Write a label to the trajectory file (comment line) virtual std::ostream & write_traj_label (std::ostream &os); /// Output quantities such as the bias energy to the trajectory file virtual std::ostream & write_traj (std::ostream &os); /// \brief Constructor colvarbias_harmonic (std::string const &conf, char const *key); /// Destructor virtual ~colvarbias_harmonic(); protected: /// \brief Restraint centers std::vector<colvarvalue> colvar_centers; /// \brief Restraint centers without wrapping or constraints applied std::vector<colvarvalue> colvar_centers_raw; /// \brief Moving target? bool b_chg_centers; /// \brief New restraint centers std::vector<colvarvalue> target_centers; /// \brief Amplitude of the restraint centers' increment at each step /// (or stage) towards the new values (calculated from target_nsteps) std::vector<colvarvalue> centers_incr; /// Whether to write the current restraint centers to the trajectory file bool b_output_centers; /// Whether to write the current accumulated work to the trajectory file bool b_output_acc_work; /// \brief Accumulated work cvm::real acc_work; /// \brief Restraint force constant cvm::real force_k; /// \brief Changing force constant? bool b_chg_force_k; /// \brief Restraint force constant (target value) cvm::real target_force_k; /// \brief Restraint force constant (starting value) cvm::real starting_force_k; /// \brief Lambda-schedule for custom varying force constant std::vector<cvm::real> lambda_schedule; /// \brief Exponent for varying the force constant cvm::real force_k_exp; - + /// \brief Intermediate quantity to compute the restraint free energy /// (in TI, would be the accumulating FE derivative) cvm::real restraint_FE; /// \brief Equilibration steps for restraint FE calculation through TI cvm::real target_equil_steps; /// \brief Number of stages over which to perform the change /// If zero, perform a continuous change int target_nstages; /// \brief Number of current stage of the perturbation int stage; /// \brief Number of steps required to reach the target force constant /// or restraint centers size_t target_nsteps; }; #endif // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvarbias_abf.cpp b/lib/colvars/colvarbias_abf.cpp index b441f3bd6..5de562f29 100644 --- a/lib/colvars/colvarbias_abf.cpp +++ b/lib/colvars/colvarbias_abf.cpp @@ -1,498 +1,498 @@ /******************************************************************************** * Implementation of the ABF and histogram biases * ********************************************************************************/ #include "colvarmodule.h" #include "colvar.h" #include "colvarbias_abf.h" /// ABF bias constructor; parses the config file colvarbias_abf::colvarbias_abf (std::string const &conf, char const *key) : colvarbias (conf, key), gradients (NULL), samples (NULL) { if (cvm::temperature() == 0.0) cvm::log ("WARNING: ABF should not be run without a thermostat or at 0 Kelvin!\n"); - + // ************* parsing general ABF options *********************** get_keyval (conf, "applyBias", apply_bias, true); if (!apply_bias) cvm::log ("WARNING: ABF biases will *not* be applied!\n"); get_keyval (conf, "updateBias", update_bias, true); if (!update_bias) cvm::log ("WARNING: ABF biases will *not* be updated!\n"); get_keyval (conf, "hideJacobian", hide_Jacobian, false); if (hide_Jacobian) { cvm::log ("Jacobian (geometric) forces will be handled internally.\n"); } else { cvm::log ("Jacobian (geometric) forces will be included in reported free energy gradients.\n"); } get_keyval (conf, "fullSamples", full_samples, 200); - if ( full_samples <= 1 ) full_samples = 1; + if ( full_samples <= 1 ) full_samples = 1; min_samples = full_samples / 2; // full_samples - min_samples >= 1 is guaranteed get_keyval (conf, "inputPrefix", input_prefix, std::vector<std::string> ()); get_keyval (conf, "outputFreq", output_freq, cvm::restart_out_freq); get_keyval (conf, "historyFreq", history_freq, 0); b_history_files = (history_freq > 0); // ************* checking the associated colvars ******************* if (colvars.size() == 0) { cvm::fatal_error ("Error: no collective variables specified for the ABF bias.\n"); } for (size_t i = 0; i < colvars.size(); i++) { if (colvars[i]->type() != colvarvalue::type_scalar) { cvm::fatal_error ("Error: ABF bias can only use scalar-type variables.\n"); } colvars[i]->enable (colvar::task_gradients); if (update_bias) { // Request calculation of system force (which also checks for availability) colvars[i]->enable (colvar::task_system_force); if (!colvars[i]->tasks[colvar::task_extended_lagrangian]) { // request computation of Jacobian force colvars[i]->enable (colvar::task_Jacobian_force); // request Jacobian force as part as system force // except if the user explicitly requires the "silent" Jacobian // correction AND the colvar has a single component if (hide_Jacobian) { if (colvars[i]->n_components() > 1) { cvm::log ("WARNING: colvar \"" + colvars[i]->name + "\" has multiple components; reporting its Jacobian forces\n"); colvars[i]->enable (colvar::task_report_Jacobian_force); } } else { colvars[i]->enable (colvar::task_report_Jacobian_force); } } } // Here we could check for orthogonality of the Cartesian coordinates - // and make it just a warning if some parameter is set? + // and make it just a warning if some parameter is set? } bin.assign (colvars.size(), 0); force_bin.assign (colvars.size(), 0); force = new cvm::real [colvars.size()]; // Construct empty grids based on the colvars samples = new colvar_grid_count (colvars); gradients = new colvar_grid_gradient (colvars); gradients->samples = samples; samples->has_parent_data = true; // If custom grids are provided, read them if ( input_prefix.size() > 0 ) { read_gradients_samples (); } - + cvm::log ("Finished ABF setup.\n"); } /// Destructor colvarbias_abf::~colvarbias_abf() { if (samples) { delete samples; samples = NULL; } if (gradients) { delete gradients; gradients = NULL; } delete [] force; if (cvm::n_abf_biases > 0) cvm::n_abf_biases -= 1; } /// Update the FE gradient, compute and apply biasing force /// also output data to disk if needed cvm::real colvarbias_abf::update() { if (cvm::debug()) cvm::log ("Updating ABF bias " + this->name); if (cvm::step_relative() == 0) { - + // At first timestep, do only: // initialization stuff (file operations relying on n_abf_biases // compute current value of colvars if ( cvm::n_abf_biases == 1 && cvm::n_meta_biases == 0 ) { // This is the only ABF bias output_prefix = cvm::output_prefix; } else { output_prefix = cvm::output_prefix + "." + this->name; } for (size_t i=0; i<colvars.size(); i++) { bin[i] = samples->current_bin_scalar(i); } } else { for (size_t i=0; i<colvars.size(); i++) { bin[i] = samples->current_bin_scalar(i); } if ( update_bias && samples->index_ok (force_bin) ) { // Only if requested and within bounds of the grid... for (size_t i=0; i<colvars.size(); i++) { // get forces (lagging by 1 timestep) from colvars force[i] = colvars[i]->system_force(); } gradients->acc_force (force_bin, force); } } // save bin for next timestep - force_bin = bin; + force_bin = bin; // Reset biasing forces from previous timestep for (size_t i=0; i<colvars.size(); i++) { colvar_forces[i].reset(); } // Compute and apply the new bias, if applicable if ( apply_bias && samples->index_ok (bin) ) { size_t count = samples->value (bin); cvm::real fact = 1.0; // Factor that ensures smooth introduction of the force if ( count < full_samples ) { fact = ( count < min_samples) ? 0.0 : (cvm::real (count - min_samples)) / (cvm::real (full_samples - min_samples)); } - + const cvm::real * grad = &(gradients->value (bin)); if ( fact != 0.0 ) { if ( (colvars.size() == 1) && colvars[0]->periodic_boundaries() ) { // Enforce a zero-mean bias on periodic, 1D coordinates colvar_forces[0].real_value += fact * (grad[0] / cvm::real (count) - gradients->average ()); } else { for (size_t i=0; i<colvars.size(); i++) { // subtracting the mean force (opposite of the FE gradient) means adding the gradient colvar_forces[i].real_value += fact * grad[i] / cvm::real (count); // without .real_value, the above would do (cheap) runtime type checking } } } } if (output_freq && (cvm::step_absolute() % output_freq) == 0) { if (cvm::debug()) cvm::log ("ABF bias trying to write gradients and samples to disk"); write_gradients_samples (output_prefix); } if (b_history_files && (cvm::step_absolute() % history_freq) == 0) { // append to existing file only if cvm::step_absolute() > 0 // otherwise, backup and replace write_gradients_samples (output_prefix + ".hist", (cvm::step_absolute() > 0)); } return 0.0; // TODO compute bias energy whenever possible (i.e. 1D with updateBias off) } void colvarbias_abf::write_gradients_samples (const std::string &prefix, bool append) { std::string samples_out_name = prefix + ".count"; std::string gradients_out_name = prefix + ".grad"; std::ios::openmode mode = (append ? std::ios::app : std::ios::out); std::ofstream samples_os; std::ofstream gradients_os; if (!append) cvm::backup_file (samples_out_name.c_str()); samples_os.open (samples_out_name.c_str(), mode); if (!samples_os.good()) cvm::fatal_error ("Error opening ABF samples file " + samples_out_name + " for writing"); samples->write_multicol (samples_os); samples_os.close (); if (!append) cvm::backup_file (gradients_out_name.c_str()); gradients_os.open (gradients_out_name.c_str(), mode); if (!gradients_os.good()) cvm::fatal_error ("Error opening ABF gradient file " + gradients_out_name + " for writing"); gradients->write_multicol (gradients_os); gradients_os.close (); if (colvars.size () == 1) { std::string pmf_out_name = prefix + ".pmf"; if (!append) cvm::backup_file (pmf_out_name.c_str()); std::ofstream pmf_os; // Do numerical integration and output a PMF pmf_os.open (pmf_out_name.c_str(), mode); if (!pmf_os.good()) cvm::fatal_error ("Error opening pmf file " + pmf_out_name + " for writing"); gradients->write_1D_integral (pmf_os); pmf_os << std::endl; pmf_os.close (); } return; } void colvarbias_abf::read_gradients_samples () { std::string samples_in_name, gradients_in_name; for ( size_t i = 0; i < input_prefix.size(); i++ ) { samples_in_name = input_prefix[i] + ".count"; gradients_in_name = input_prefix[i] + ".grad"; // For user-provided files, the per-bias naming scheme may not apply - + std::ifstream is; cvm::log ("Reading sample count from " + samples_in_name + " and gradients from " + gradients_in_name); is.open (samples_in_name.c_str()); if (!is.good()) cvm::fatal_error ("Error opening ABF samples file " + samples_in_name + " for reading"); samples->read_multicol (is, true); is.close (); is.clear(); is.open (gradients_in_name.c_str()); if (!is.good()) cvm::fatal_error ("Error opening ABF gradient file " + gradients_in_name + " for reading"); gradients->read_multicol (is, true); is.close (); } return; } std::ostream & colvarbias_abf::write_restart (std::ostream& os) { - std::ios::fmtflags flags (os.flags ()); + std::ios::fmtflags flags (os.flags ()); os.setf(std::ios::fmtflags (0), std::ios::floatfield); // default floating-point format os << "abf {\n" << " configuration {\n" << " name " << this->name << "\n"; os << " }\n"; os << "samples\n"; samples->write_raw (os, 8); os << "\ngradient\n"; gradients->write_raw (os); os << "}\n\n"; os.flags (flags); return os; } std::istream & colvarbias_abf::read_restart (std::istream& is) { if ( input_prefix.size() > 0 ) { cvm::fatal_error ("ERROR: cannot provide both inputPrefix and restart information (colvarsInput)"); } size_t const start_pos = is.tellg(); cvm::log ("Restarting ABF bias \""+ this->name+"\".\n"); std::string key, brace, conf; if ( !(is >> key) || !(key == "abf") || !(is >> brace) || !(brace == "{") || !(is >> colvarparse::read_block ("configuration", conf)) ) { cvm::log ("Error: in reading restart configuration for ABF bias \""+ this->name+"\" at position "+ cvm::to_str (is.tellg())+" in stream.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } std::string name = ""; if ( (colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent)) && (name != this->name) ) cvm::fatal_error ("Error: in the restart file, the " "\"abf\" block has wrong name (" + name + ")\n"); if ( name == "" ) { cvm::fatal_error ("Error: \"abf\" block in the restart file has no name.\n"); } - + if ( !(is >> key) || !(key == "samples")) { cvm::log ("Error: in reading restart configuration for ABF bias \""+ this->name+"\" at position "+ cvm::to_str (is.tellg())+" in stream.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } if (! samples->read_raw (is)) { samples->read_raw_error(); } if ( !(is >> key) || !(key == "gradient")) { cvm::log ("Error: in reading restart configuration for ABF bias \""+ this->name+"\" at position "+ cvm::to_str (is.tellg())+" in stream.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } if (! gradients->read_raw (is)) { gradients->read_raw_error(); } is >> brace; if (brace != "}") { cvm::fatal_error ("Error: corrupt restart information for ABF bias \""+ this->name+"\": no matching brace at position "+ cvm::to_str (is.tellg())+" in the restart file.\n"); is.setstate (std::ios::failbit); } return is; } /// Histogram "bias" constructor colvarbias_histogram::colvarbias_histogram (std::string const &conf, char const *key) : colvarbias (conf, key), grid (NULL) { get_keyval (conf, "outputfreq", output_freq, cvm::restart_out_freq); if ( output_freq == 0 ) { cvm::fatal_error ("User required histogram with zero output frequency"); } grid = new colvar_grid_count (colvars); bin.assign (colvars.size(), 0); out_name = cvm::output_prefix + "." + this->name + ".dat"; - cvm::log ("Histogram will be written to file " + out_name); + cvm::log ("Histogram will be written to file " + out_name); cvm::log ("Finished histogram setup.\n"); } /// Destructor colvarbias_histogram::~colvarbias_histogram() { if (grid_os.good()) grid_os.close(); if (grid) { delete grid; grid = NULL; } if (cvm::n_histo_biases > 0) cvm::n_histo_biases -= 1; } /// Update the grid cvm::real colvarbias_histogram::update() { if (cvm::debug()) cvm::log ("Updating Grid bias " + this->name); for (size_t i=0; i<colvars.size(); i++) { bin[i] = grid->current_bin_scalar(i); } if ( grid->index_ok (bin) ) { // Only within bounds of the grid... grid->incr_count (bin); } if (output_freq && (cvm::step_absolute() % output_freq) == 0) { if (cvm::debug()) cvm::log ("Histogram bias trying to write grid to disk"); grid_os.open (out_name.c_str()); if (!grid_os.good()) cvm::fatal_error ("Error opening histogram file " + out_name + " for writing"); grid->write_multicol (grid_os); grid_os.close (); } return 0.0; // no bias energy for histogram } std::istream & colvarbias_histogram::read_restart (std::istream& is) { size_t const start_pos = is.tellg(); cvm::log ("Restarting collective variable histogram \""+ this->name+"\".\n"); std::string key, brace, conf; if ( !(is >> key) || !(key == "histogram") || !(is >> brace) || !(brace == "{") || !(is >> colvarparse::read_block ("configuration", conf)) ) { cvm::log ("Error: in reading restart configuration for histogram \""+ this->name+"\" at position "+ cvm::to_str (is.tellg())+" in stream.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } int id = -1; std::string name = ""; if ( (colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent)) && (name != this->name) ) cvm::fatal_error ("Error: in the restart file, the " "\"histogram\" block has a wrong name: different system?\n"); if ( (id == -1) && (name == "") ) { cvm::fatal_error ("Error: \"histogram\" block in the restart file " "has no name.\n"); } - + if ( !(is >> key) || !(key == "grid")) { cvm::log ("Error: in reading restart configuration for histogram \""+ this->name+"\" at position "+ cvm::to_str (is.tellg())+" in stream.\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } if (! grid->read_raw (is)) { grid->read_raw_error(); } is >> brace; if (brace != "}") { cvm::fatal_error ("Error: corrupt restart information for ABF bias \""+ this->name+"\": no matching brace at position "+ cvm::to_str (is.tellg())+" in the restart file.\n"); is.setstate (std::ios::failbit); } return is; } std::ostream & colvarbias_histogram::write_restart (std::ostream& os) { os << "histogram {\n" << " configuration {\n" << " name " << this->name << "\n"; os << " }\n"; os << "grid\n"; grid->write_raw (os, 8); os << "}\n\n"; return os; } diff --git a/lib/colvars/colvarbias_abf.h b/lib/colvars/colvarbias_abf.h index f107b7824..dfb6aec8d 100644 --- a/lib/colvars/colvarbias_abf.h +++ b/lib/colvars/colvarbias_abf.h @@ -1,95 +1,95 @@ /************************************************************************ * Headers for the ABF and histogram biases * ************************************************************************/ #ifndef COLVARBIAS_ABF_H #define COLVARBIAS_ABF_H #include <vector> #include <list> #include <sstream> #include <iomanip> //#include <cmath> #include "colvarbias.h" #include "colvargrid.h" typedef cvm::real* gradient_t; /// ABF bias class colvarbias_abf : public colvarbias { public: colvarbias_abf (std::string const &conf, char const *key); ~colvarbias_abf (); cvm::real update (); private: /// Filename prefix for human-readable gradient/sample count output std::string output_prefix; /// Base filename(s) for reading previous gradient data (replaces data from restart file) std::vector<std::string> input_prefix; bool apply_bias; bool update_bias; bool hide_Jacobian; size_t full_samples; size_t min_samples; /// frequency for updating output files (default: same as restartFreq?) int output_freq; /// Write combined files with a history of all output data? bool b_history_files; size_t history_freq; // Internal data and methods std::vector<int> bin, force_bin; gradient_t force; /// n-dim grid of free energy gradients colvar_grid_gradient *gradients; /// n-dim grid of number of samples colvar_grid_count *samples; /// Write human-readable FE gradients and sample count void write_gradients_samples (const std::string &prefix, bool append = false); /// Read human-readable FE gradients and sample count (if not using restart) void read_gradients_samples (); std::istream& read_restart (std::istream&); std::ostream& write_restart (std::ostream&); }; /// Histogram "bias" (does as the name says) class colvarbias_histogram : public colvarbias { public: colvarbias_histogram (std::string const &conf, char const *key); ~colvarbias_histogram (); cvm::real update (); private: /// n-dim histogram colvar_grid_count *grid; std::vector<int> bin; std::string out_name; - int output_freq; + int output_freq; void write_grid (); std::ofstream grid_os; /// Stream for writing grid to disk std::istream& read_restart (std::istream&); std::ostream& write_restart (std::ostream&); }; #endif diff --git a/lib/colvars/colvarbias_meta.cpp b/lib/colvars/colvarbias_meta.cpp index 5ad6d8f24..3ddc6af6c 100644 --- a/lib/colvars/colvarbias_meta.cpp +++ b/lib/colvars/colvarbias_meta.cpp @@ -1,1717 +1,1717 @@ #include <iostream> #include <sstream> #include <fstream> #include <iomanip> #include <cmath> #include <algorithm> // used to set the absolute path of a replica file #if defined (WIN32) && !defined(__CYGWIN__) #include <direct.h> #define CHDIR ::_chdir #define GETCWD ::_getcwd #define PATHSEP "\\" #else #include <unistd.h> #define CHDIR ::chdir #define GETCWD ::getcwd #define PATHSEP "/" #endif #include "colvar.h" #include "colvarbias_meta.h" colvarbias_meta::colvarbias_meta() : colvarbias(), state_file_step (0), new_hills_begin (hills.end()) { } colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) : colvarbias (conf, key), state_file_step (0), new_hills_begin (hills.end()) { if (cvm::n_abf_biases > 0) cvm::log ("Warning: ABF and metadynamics running at the " "same time can lead to inconsistent results.\n"); get_keyval (conf, "hillWeight", hill_weight, 0.01); if (hill_weight == 0.0) cvm::log ("Warning: hillWeight has been set to zero, " "this bias will have no effect.\n"); get_keyval (conf, "newHillFrequency", new_hill_freq, 1000); get_keyval (conf, "hillWidth", hill_width, std::sqrt (2.0 * PI) / 2.0); { bool b_replicas = false; get_keyval (conf, "multipleReplicas", b_replicas, false); if (b_replicas) comm = multiple_replicas; - else + else comm = single_replica; } get_keyval (conf, "useGrids", use_grids, true); if (use_grids) { get_keyval (conf, "gridsUpdateFrequency", grids_freq, new_hill_freq); get_keyval (conf, "rebinGrids", rebin_grids, false); expand_grids = false; for (size_t i = 0; i < colvars.size(); i++) { if (colvars[i]->expand_boundaries) { expand_grids = true; cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": Will expand grids when the colvar \""+ colvars[i]->name+"\" approaches its boundaries.\n"); } } get_keyval (conf, "keepHills", keep_hills, false); if (! get_keyval (conf, "writeFreeEnergyFile", dump_fes, true)) get_keyval (conf, "dumpFreeEnergyFile", dump_fes, true, colvarparse::parse_silent); get_keyval (conf, "saveFreeEnergyFile", dump_fes_save, false); for (size_t i = 0; i < colvars.size(); i++) { colvars[i]->enable (colvar::task_grid); } hills_energy = new colvar_grid_scalar (colvars); hills_energy_gradients = new colvar_grid_gradient (colvars); } else { rebin_grids = false; keep_hills = false; dump_fes = false; dump_fes_save = false; dump_replica_fes = false; } if (comm != single_replica) { if (expand_grids) cvm::fatal_error ("Error: expandBoundaries is not supported when " "using more than one replicas; please allocate " "wide enough boundaries for each colvar" "ahead of time.\n"); if (get_keyval (conf, "dumpPartialFreeEnergyFile", dump_replica_fes, false)) { if (dump_replica_fes && (! dump_fes)) { cvm::log ("Enabling \"dumpFreeEnergyFile\".\n"); } } get_keyval (conf, "replicaID", replica_id, std::string ("")); if (!replica_id.size()) cvm::fatal_error ("Error: replicaID must be defined " "when using more than one replica.\n"); get_keyval (conf, "replicasRegistry", - replicas_registry_file, + replicas_registry_file, (this->name+".replicas.registry.txt")); get_keyval (conf, "replicaUpdateFrequency", replica_update_freq, new_hill_freq); if (keep_hills) cvm::log ("Warning: in metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": keepHills with more than one replica can lead to a very " "large amount input/output and slow down your calculations. " "Please consider disabling it.\n"); { // TODO: one may want to specify the path manually for intricated filesystems? char *pwd = new char[3001]; if (GETCWD (pwd, 3000) == NULL) cvm::fatal_error ("Error: cannot get the path of the current working directory.\n"); replica_list_file = (std::string (pwd)+std::string (PATHSEP)+ this->name+"."+replica_id+".files.txt"); // replica_hills_file and replica_state_file are those written // by the current replica; within the mirror biases, they are // those by another replica replica_hills_file = (std::string (pwd)+std::string (PATHSEP)+ cvm::output_prefix+".colvars."+this->name+"."+replica_id+".hills"); replica_state_file = (std::string (pwd)+std::string (PATHSEP)+ cvm::output_prefix+".colvars."+this->name+"."+replica_id+".state"); delete pwd; } // now register this replica // first check that it isn't already there bool registered_replica = false; std::ifstream reg_is (replicas_registry_file.c_str()); if (reg_is.good()) { // the file may not be there yet std::string existing_replica (""); std::string existing_replica_file (""); while ((reg_is >> existing_replica) && existing_replica.size() && (reg_is >> existing_replica_file) && existing_replica_file.size()) { if (existing_replica == replica_id) { // this replica was already registered replica_list_file = existing_replica_file; reg_is.close(); registered_replica = true; break; } } reg_is.close(); } // if this replica was not included yet, we should generate a // new record for it: but first, we write this replica's files, // for the others to read - + // open the "hills" buffer file replica_hills_os.open (replica_hills_file.c_str()); if (!replica_hills_os.good()) cvm::fatal_error ("Error: in opening file \""+ replica_hills_file+"\" for writing.\n"); replica_hills_os.setf (std::ios::scientific, std::ios::floatfield); // write the state file (so that there is always one available) write_replica_state_file(); // schedule to read the state files of the other replicas for (size_t ir = 0; ir < replicas.size(); ir++) { (replicas[ir])->replica_state_file_in_sync = false; } // if we're running without grids, use a growing list of "hills" files // otherwise, just one state file and one "hills" file as buffer - std::ofstream list_os (replica_list_file.c_str(), + std::ofstream list_os (replica_list_file.c_str(), (use_grids ? std::ios::trunc : std::ios::app)); if (! list_os.good()) cvm::fatal_error ("Error: in opening file \""+ replica_list_file+"\" for writing.\n"); list_os << "stateFile " << replica_state_file << "\n"; list_os << "hillsFile " << replica_hills_file << "\n"; list_os.close(); // finally, if add a new record for this replica to the registry if (! registered_replica) { std::ofstream reg_os (replicas_registry_file.c_str(), std::ios::app); if (! reg_os.good()) cvm::fatal_error ("Error: in opening file \""+ replicas_registry_file+"\" for writing.\n"); reg_os << replica_id << " " << replica_list_file << "\n"; reg_os.close(); } } get_keyval (conf, "writeHillsTrajectory", b_hills_traj, false); if (b_hills_traj) { std::string const traj_file_name (cvm::output_prefix+ ".colvars."+this->name+ ( (comm != single_replica) ? ("."+replica_id) : ("") )+ ".hills.traj"); hills_traj_os.open (traj_file_name.c_str()); if (!hills_traj_os.good()) cvm::fatal_error ("Error: in opening hills output file \"" + traj_file_name + "\".\n"); } // for well-tempered metadynamics get_keyval (conf, "wellTempered", well_tempered, false); get_keyval (conf, "biasTemperature", bias_temperature, -1.0); if ((bias_temperature == -1.0) && well_tempered) { cvm::fatal_error ("Error: biasTemperature is not set.\n"); } if (well_tempered) { cvm::log("Well-tempered metadynamics is used.\n"); cvm::log("The bias temperature is "+cvm::to_str(bias_temperature)+".\n"); } - + if (cvm::debug()) cvm::log ("Done initializing the metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n"); save_delimiters = false; } colvarbias_meta::~colvarbias_meta() { if (hills_energy) { delete hills_energy; hills_energy = NULL; } if (hills_energy_gradients) { delete hills_energy_gradients; hills_energy_gradients = NULL; } if (replica_hills_os.good()) replica_hills_os.close(); if (hills_traj_os.good()) hills_traj_os.close(); if (cvm::n_meta_biases > 0) cvm::n_meta_biases -= 1; } // ********************************************************************** // Hill management member functions // ********************************************************************** -std::list<colvarbias_meta::hill>::const_iterator +std::list<colvarbias_meta::hill>::const_iterator colvarbias_meta::create_hill (colvarbias_meta::hill const &h) { hill_iter const hills_end = hills.end(); hills.push_back (h); if (new_hills_begin == hills_end) { // if new_hills_begin is unset, set it for the first time new_hills_begin = hills.end(); new_hills_begin--; } if (use_grids) { // also add it to the list of hills that are off-grid, which may // need to be computed analytically when the colvar returns // off-grid cvm::real const min_dist = hills_energy->bin_distance_from_boundaries (h.centers, true); if (min_dist < (3.0 * std::floor (hill_width)) + 1.0) { hills_off_grid.push_back (h); } } // output to trajectory (if specified) if (hills_traj_os.good()) { hills_traj_os << (hills.back()).output_traj(); if (cvm::debug()) { hills_traj_os.flush(); } } has_data = true; return hills.end(); } std::list<colvarbias_meta::hill>::const_iterator colvarbias_meta::delete_hill (hill_iter &h) { if (cvm::debug()) { cvm::log ("Deleting hill from the metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ", with step number "+ cvm::to_str (h->it)+(h->replica.size() ? ", replica id \""+h->replica : "")+".\n"); } if (use_grids && hills_off_grid.size()) { for (hill_iter hoff = hills_off_grid.begin(); hoff != hills_off_grid.end(); hoff++) { if (*h == *hoff) { hills_off_grid.erase (hoff); break; } } } if (hills_traj_os.good()) { - // output to the trajectory + // output to the trajectory hills_traj_os << "# DELETED this hill: " << (hills.back()).output_traj() << "\n"; if (cvm::debug()) hills_traj_os.flush(); } return hills.erase (h); } cvm::real colvarbias_meta::update() { if (cvm::debug()) cvm::log ("Updating the metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n"); if (use_grids) { std::vector<int> curr_bin = hills_energy->get_colvars_index(); if (expand_grids) { // first of all, expand the grids, if specified if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": current coordinates on the grid: "+ cvm::to_str (curr_bin)+".\n"); bool changed_grids = false; int const min_buffer = (3 * (size_t) std::floor (hill_width)) + 1; std::vector<int> new_sizes (hills_energy->sizes()); std::vector<colvarvalue> new_lower_boundaries (hills_energy->lower_boundaries); std::vector<colvarvalue> new_upper_boundaries (hills_energy->upper_boundaries); for (size_t i = 0; i < colvars.size(); i++) { if (! colvars[i]->expand_boundaries) continue; cvm::real &new_lb = new_lower_boundaries[i].real_value; cvm::real &new_ub = new_upper_boundaries[i].real_value; int &new_size = new_sizes[i]; bool changed_lb = false, changed_ub = false; if (!colvars[i]->hard_lower_boundary) if (curr_bin[i] < min_buffer) { int const extra_points = (min_buffer - curr_bin[i]); new_lb -= extra_points * colvars[i]->width; new_size += extra_points; // changed offset in this direction => the pointer needs to // be changed, too curr_bin[i] += extra_points; changed_lb = true; cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": new lower boundary for colvar \""+ colvars[i]->name+"\", at "+ cvm::to_str (new_lower_boundaries[i])+".\n"); } if (!colvars[i]->hard_upper_boundary) if (curr_bin[i] > new_size - min_buffer - 1) { int const extra_points = (curr_bin[i] - (new_size - 1) + min_buffer); new_ub += extra_points * colvars[i]->width; new_size += extra_points; changed_ub = true; cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": new upper boundary for colvar \""+ colvars[i]->name+"\", at "+ cvm::to_str (new_upper_boundaries[i])+".\n"); } if (changed_lb || changed_ub) changed_grids = true; } if (changed_grids) { // map everything into new grids colvar_grid_scalar *new_hills_energy = new colvar_grid_scalar (*hills_energy); colvar_grid_gradient *new_hills_energy_gradients = new colvar_grid_gradient (*hills_energy_gradients); // supply new boundaries to the new grids new_hills_energy->lower_boundaries = new_lower_boundaries; new_hills_energy->upper_boundaries = new_upper_boundaries; new_hills_energy->create (new_sizes, 0.0, 1); new_hills_energy_gradients->lower_boundaries = new_lower_boundaries; new_hills_energy_gradients->upper_boundaries = new_upper_boundaries; new_hills_energy_gradients->create (new_sizes, 0.0, colvars.size()); new_hills_energy->map_grid (*hills_energy); new_hills_energy_gradients->map_grid (*hills_energy_gradients); delete hills_energy; delete hills_energy_gradients; hills_energy = new_hills_energy; hills_energy_gradients = new_hills_energy_gradients; curr_bin = hills_energy->get_colvars_index(); if (cvm::debug()) cvm::log ("Coordinates on the new grid: "+ cvm::to_str (curr_bin)+".\n"); } } } // add a new hill if the required time interval has passed if ((cvm::step_absolute() % new_hill_freq) == 0) { if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": adding a new hill at step "+cvm::to_str (cvm::step_absolute())+".\n"); switch (comm) { case single_replica: if (well_tempered) { std::vector<int> curr_bin = hills_energy->get_colvars_index(); cvm::real const hills_energy_sum_here = hills_energy->value(curr_bin); cvm::real const exp_weight = std::exp(-hills_energy_sum_here/(bias_temperature*cvm::boltzmann())); create_hill (hill ((hill_weight*exp_weight), colvars, hill_width)); - } else { + } else { create_hill (hill (hill_weight, colvars, hill_width)); - } + } break; case multiple_replicas: if (well_tempered) { std::vector<int> curr_bin = hills_energy->get_colvars_index(); cvm::real const hills_energy_sum_here = hills_energy->value(curr_bin); cvm::real const exp_weight = std::exp(-hills_energy_sum_here/(bias_temperature*cvm::boltzmann())); create_hill (hill ((hill_weight*exp_weight), colvars, hill_width, replica_id)); - } else { + } else { create_hill (hill (hill_weight, colvars, hill_width, replica_id)); - } + } if (replica_hills_os.good()) { replica_hills_os << hills.back(); } else { cvm::fatal_error ("Error: in metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ " while writing hills for the other replicas.\n"); } break; } } // sync with the other replicas (if needed) if (comm != single_replica) { // reread the replicas registry if ((cvm::step_absolute() % replica_update_freq) == 0) { update_replicas_registry(); // empty the output buffer replica_hills_os.flush(); read_replica_files(); } } // calculate the biasing energy and forces bias_energy = 0.0; for (size_t ir = 0; ir < colvars.size(); ir++) { colvar_forces[ir].reset(); } if (comm == multiple_replicas) for (size_t ir = 0; ir < replicas.size(); ir++) { replicas[ir]->bias_energy = 0.0; for (size_t ic = 0; ic < colvars.size(); ic++) { replicas[ir]->colvar_forces[ic].reset(); } } if (use_grids) { // get the forces from the grid if ((cvm::step_absolute() % grids_freq) == 0) { // map the most recent gaussians to the grids project_hills (new_hills_begin, hills.end(), hills_energy, hills_energy_gradients); new_hills_begin = hills.end(); // TODO: we may want to condense all into one replicas array, // including "this" as the first element if (comm == multiple_replicas) { for (size_t ir = 0; ir < replicas.size(); ir++) { replicas[ir]->project_hills (replicas[ir]->new_hills_begin, replicas[ir]->hills.end(), replicas[ir]->hills_energy, replicas[ir]->hills_energy_gradients); replicas[ir]->new_hills_begin = replicas[ir]->hills.end(); } } } std::vector<int> curr_bin = hills_energy->get_colvars_index(); if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": current coordinates on the grid: "+ cvm::to_str (curr_bin)+".\n"); if (hills_energy->index_ok (curr_bin)) { // within the grid: add the energy and the forces from there bias_energy += hills_energy->value (curr_bin); for (size_t ic = 0; ic < colvars.size(); ic++) { cvm::real const *f = &(hills_energy_gradients->value (curr_bin)); colvar_forces[ic].real_value += -1.0 * f[ic]; // the gradients are stored, not the forces } if (comm == multiple_replicas) for (size_t ir = 0; ir < replicas.size(); ir++) { bias_energy += replicas[ir]->hills_energy->value (curr_bin); cvm::real const *f = &(replicas[ir]->hills_energy_gradients->value (curr_bin)); for (size_t ic = 0; ic < colvars.size(); ic++) { colvar_forces[ic].real_value += -1.0 * f[ic]; } } } else { // off the grid: compute analytically only the hills at the grid's edges calc_hills (hills_off_grid.begin(), hills_off_grid.end(), bias_energy); for (size_t ic = 0; ic < colvars.size(); ic++) { calc_hills_force (ic, hills_off_grid.begin(), hills_off_grid.end(), colvar_forces); } if (comm == multiple_replicas) for (size_t ir = 0; ir < replicas.size(); ir++) { calc_hills (replicas[ir]->hills_off_grid.begin(), replicas[ir]->hills_off_grid.end(), bias_energy); for (size_t ic = 0; ic < colvars.size(); ic++) { calc_hills_force (ic, replicas[ir]->hills_off_grid.begin(), replicas[ir]->hills_off_grid.end(), colvar_forces); } } } } // now include the hills that have not been binned yet (starting // from new_hills_begin) calc_hills (new_hills_begin, hills.end(), bias_energy); for (size_t ic = 0; ic < colvars.size(); ic++) { calc_hills_force (ic, new_hills_begin, hills.end(), colvar_forces); } - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Hills energy = "+cvm::to_str (bias_energy)+ ", hills forces = "+cvm::to_str (colvar_forces)+".\n"); - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": adding the forces from the other replicas.\n"); if (comm == multiple_replicas) for (size_t ir = 0; ir < replicas.size(); ir++) { calc_hills (replicas[ir]->new_hills_begin, replicas[ir]->hills.end(), bias_energy); for (size_t ic = 0; ic < colvars.size(); ic++) { calc_hills_force (ic, replicas[ir]->new_hills_begin, replicas[ir]->hills.end(), colvar_forces); } - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Hills energy = "+cvm::to_str (bias_energy)+ ", hills forces = "+cvm::to_str (colvar_forces)+".\n"); } return bias_energy; } void colvarbias_meta::calc_hills (colvarbias_meta::hill_iter h_first, colvarbias_meta::hill_iter h_last, cvm::real &energy, std::vector<colvarvalue> const &colvar_values) { std::vector<colvarvalue> curr_values (colvars.size()); for (size_t i = 0; i < colvars.size(); i++) { curr_values[i].type (colvars[i]->type()); } if (colvar_values.size()) { for (size_t i = 0; i < colvars.size(); i++) { curr_values[i] = colvar_values[i]; } } else { for (size_t i = 0; i < colvars.size(); i++) { curr_values[i] = colvars[i]->value(); } } for (hill_iter h = h_first; h != h_last; h++) { - + // compute the gaussian exponent cvm::real cv_sqdev = 0.0; for (size_t i = 0; i < colvars.size(); i++) { colvarvalue const &x = curr_values[i]; colvarvalue const ¢er = h->centers[i]; cvm::real const half_width = 0.5 * h->widths[i]; cv_sqdev += (colvars[i]->dist2 (x, center)) / (half_width*half_width); } // compute the gaussian if (cv_sqdev > 23.0) { // set it to zero if the exponent is more negative than log(1.0E-05) h->value (0.0); } else { h->value (std::exp (-0.5*cv_sqdev)); } energy += h->energy(); } } void colvarbias_meta::calc_hills_force (size_t const &i, colvarbias_meta::hill_iter h_first, colvarbias_meta::hill_iter h_last, std::vector<colvarvalue> &forces, std::vector<colvarvalue> const &values) { // Retrieve the value of the colvar colvarvalue x (values.size() ? values[i].type() : colvars[i]->type()); x = (values.size() ? values[i] : colvars[i]->value()); // do the type check only once (all colvarvalues in the hills series // were already saved with their types matching those in the // colvars) switch (colvars[i]->type()) { case colvarvalue::type_scalar: for (hill_iter h = h_first; h != h_last; h++) { if (h->value() == 0.0) continue; colvarvalue const ¢er = h->centers[i]; cvm::real const half_width = 0.5 * h->widths[i]; - forces[i].real_value += - ( h->weight() * h->value() * (0.5 / (half_width*half_width)) * + forces[i].real_value += + ( h->weight() * h->value() * (0.5 / (half_width*half_width)) * (colvars[i]->dist2_lgrad (x, center)).real_value ); } break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: for (hill_iter h = h_first; h != h_last; h++) { if (h->value() == 0.0) continue; colvarvalue const ¢er = h->centers[i]; cvm::real const half_width = 0.5 * h->widths[i]; forces[i].rvector_value += ( h->weight() * h->value() * (0.5 / (half_width*half_width)) * (colvars[i]->dist2_lgrad (x, center)).rvector_value ); } break; case colvarvalue::type_quaternion: for (hill_iter h = h_first; h != h_last; h++) { if (h->value() == 0.0) continue; colvarvalue const ¢er = h->centers[i]; cvm::real const half_width = 0.5 * h->widths[i]; forces[i].quaternion_value += ( h->weight() * h->value() * (0.5 / (half_width*half_width)) * (colvars[i]->dist2_lgrad (x, center)).quaternion_value ); } break; case colvarvalue::type_notset: break; } } // ********************************************************************** // grid management functions // ********************************************************************** void colvarbias_meta::project_hills (colvarbias_meta::hill_iter h_first, colvarbias_meta::hill_iter h_last, colvar_grid_scalar *he, colvar_grid_gradient *hg, bool print_progress) { if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": projecting hills.\n"); // TODO: improve it by looping over a small subgrid instead of the whole grid std::vector<colvarvalue> colvar_values (colvars.size()); std::vector<cvm::real> colvar_forces_scalar (colvars.size()); std::vector<int> he_ix = he->new_index(); std::vector<int> hg_ix = (hg != NULL) ? hg->new_index() : std::vector<int> (0); cvm::real hills_energy_here = 0.0; std::vector<colvarvalue> hills_forces_here (colvars.size(), 0.0); size_t count = 0; size_t const print_frequency = ((hills.size() >= 1000000) ? 1 : (1000000/(hills.size()+1))); if (hg != NULL) { // loop over the points of the grid for ( ; (he->index_ok (he_ix)) && (hg->index_ok (hg_ix)); count++) { for (size_t i = 0; i < colvars.size(); i++) { colvar_values[i] = hills_energy->bin_to_value_scalar (he_ix[i], i); } // loop over the hills and increment the energy grid locally hills_energy_here = 0.0; calc_hills (h_first, h_last, hills_energy_here, colvar_values); he->acc_value (he_ix, hills_energy_here); for (size_t i = 0; i < colvars.size(); i++) { hills_forces_here[i].reset(); calc_hills_force (i, h_first, h_last, hills_forces_here, colvar_values); colvar_forces_scalar[i] = hills_forces_here[i].real_value; } hg->acc_force (hg_ix, &(colvar_forces_scalar.front())); he->incr (he_ix); hg->incr (hg_ix); if ((count % print_frequency) == 0) { if (print_progress) { cvm::real const progress = cvm::real (count) / cvm::real (hg->number_of_points()); std::ostringstream os; os.setf (std::ios::fixed, std::ios::floatfield); os << std::setw (6) << std::setprecision (2) << 100.0 * progress << "% done."; cvm::log (os.str()); } } } } else { // simpler version, with just the energy for ( ; (he->index_ok (he_ix)); ) { for (size_t i = 0; i < colvars.size(); i++) { colvar_values[i] = hills_energy->bin_to_value_scalar (he_ix[i], i); } hills_energy_here = 0.0; calc_hills (h_first, h_last, hills_energy_here, colvar_values); he->acc_value (he_ix, hills_energy_here); he->incr (he_ix); count++; if ((count % print_frequency) == 0) { if (print_progress) { cvm::real const progress = cvm::real (count) / cvm::real (he->number_of_points()); std::ostringstream os; os.setf (std::ios::fixed, std::ios::floatfield); os << std::setw (6) << std::setprecision (2) << 100.0 * progress << "% done."; cvm::log (os.str()); } } } } if (print_progress) { cvm::log ("100.00% done."); } if (! keep_hills) { hills.erase (hills.begin(), hills.end()); } } void colvarbias_meta::recount_hills_off_grid (colvarbias_meta::hill_iter h_first, colvarbias_meta::hill_iter h_last, colvar_grid_scalar *he) { hills_off_grid.clear(); for (hill_iter h = h_first; h != h_last; h++) { cvm::real const min_dist = hills_energy->bin_distance_from_boundaries (h->centers, true); if (min_dist < (3.0 * std::floor (hill_width)) + 1.0) { hills_off_grid.push_back (*h); } } } // ********************************************************************** // multiple replicas functions // ********************************************************************** void colvarbias_meta::update_replicas_registry() { if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ": updating the list of replicas, currently containing "+ cvm::to_str (replicas.size())+" elements.\n"); { // copy the whole file into a string for convenience std::string line (""); std::ifstream reg_file (replicas_registry_file.c_str()); if (reg_file.good()) { replicas_registry.clear(); while (colvarparse::getline_nocomments (reg_file, line)) replicas_registry.append (line+"\n"); } else { cvm::fatal_error ("Error: failed to open file \""+replicas_registry_file+ "\" for reading.\n"); } } // now parse it std::istringstream reg_is (replicas_registry); if (reg_is.good()) { std::string new_replica (""); std::string new_replica_file (""); while ((reg_is >> new_replica) && new_replica.size() && (reg_is >> new_replica_file) && new_replica_file.size()) { if (new_replica == this->replica_id) { // this is the record for this same replica, skip it if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": skipping this replica's own record: \""+ new_replica+"\", \""+new_replica_file+"\"\n"); new_replica_file.clear(); new_replica.clear(); continue; } bool already_loaded = false; for (size_t ir = 0; ir < replicas.size(); ir++) { if (new_replica == (replicas[ir])->replica_id) { // this replica was already added if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": skipping a replica already loaded, \""+ (replicas[ir])->replica_id+"\".\n"); already_loaded = true; break; } } if (!already_loaded) { // add this replica to the registry cvm::log ("Metadynamics bias \""+this->name+"\""+ ": accessing replica \""+new_replica+"\".\n"); replicas.push_back (new colvarbias_meta()); (replicas.back())->replica_id = new_replica; (replicas.back())->replica_list_file = new_replica_file; (replicas.back())->replica_state_file = ""; (replicas.back())->replica_state_file_in_sync = false; // Note: the following could become a copy constructor? (replicas.back())->name = this->name; (replicas.back())->colvars = colvars; (replicas.back())->use_grids = use_grids; (replicas.back())->dump_fes = false; (replicas.back())->expand_grids = false; (replicas.back())->rebin_grids = false; (replicas.back())->keep_hills = false; (replicas.back())->colvar_forces = colvar_forces; (replicas.back())->comm = multiple_replicas; if (use_grids) { (replicas.back())->hills_energy = new colvar_grid_scalar (colvars); (replicas.back())->hills_energy_gradients = new colvar_grid_gradient (colvars); } } } // continue the cycle new_replica_file = ""; new_replica = ""; } else { cvm::fatal_error ("Error: cannot read the replicas registry file \""+ replicas_registry+"\".\n"); } // now (re)read the list file of each replica for (size_t ir = 0; ir < replicas.size(); ir++) { if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ": reading the list file for replica \""+ (replicas[ir])->replica_id+"\".\n"); std::ifstream list_is ((replicas[ir])->replica_list_file.c_str()); std::string key; std::string new_state_file, new_hills_file; if (!(list_is >> key) || !(key == std::string ("stateFile")) || !(list_is >> new_state_file) || !(list_is >> key) || !(key == std::string ("hillsFile")) || !(list_is >> new_hills_file)) { cvm::log ("Metadynamics bias \""+this->name+"\""+ ": failed to read the file \""+ (replicas[ir])->replica_list_file+"\": will try again after "+ cvm::to_str (replica_update_freq)+" steps.\n"); (replicas[ir])->update_status++; } else { (replicas[ir])->update_status = 0; if (new_state_file != (replicas[ir])->replica_state_file) { cvm::log ("Metadynamics bias \""+this->name+"\""+ ": replica \""+(replicas[ir])->replica_id+ "\" has supplied a new state file, \""+new_state_file+ "\".\n"); (replicas[ir])->replica_state_file_in_sync = false; (replicas[ir])->replica_state_file = new_state_file; (replicas[ir])->replica_hills_file = new_hills_file; } } } if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\": the list of replicas contains "+ cvm::to_str (replicas.size())+" elements.\n"); } void colvarbias_meta::read_replica_files() { for (size_t ir = 0; ir < replicas.size(); ir++) { if (! (replicas[ir])->replica_state_file_in_sync) { // if a new state file is being read, the hills file is also new (replicas[ir])->replica_hills_file_pos = 0; } // (re)read the state file if necessary - if ( (! (replicas[ir])->has_data) || + if ( (! (replicas[ir])->has_data) || (! (replicas[ir])->replica_state_file_in_sync) ) { cvm::log ("Metadynamics bias \""+this->name+"\""+ ": reading the state of replica \""+ (replicas[ir])->replica_id+"\" from file \""+ (replicas[ir])->replica_state_file+"\".\n"); std::ifstream is ((replicas[ir])->replica_state_file.c_str()); if (! (replicas[ir])->read_restart (is)) { cvm::log ("Reading from file \""+(replicas[ir])->replica_state_file+ "\" failed or incomplete: will try again in "+ cvm::to_str (replica_update_freq)+" steps.\n"); } else { // state file has been read successfully (replicas[ir])->replica_state_file_in_sync = true; (replicas[ir])->update_status = 0; } is.close(); } - + // now read the hills added after writing the state file if ((replicas[ir])->replica_hills_file.size()) { - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ": checking for new hills from replica \""+ (replicas[ir])->replica_id+"\" in the file \""+ (replicas[ir])->replica_hills_file+"\".\n"); // read hills from the other replicas' files; for each file, resume // the position recorded previously std::ifstream is ((replicas[ir])->replica_hills_file.c_str()); if (is.good()) { // try to resume the previous position is.seekg ((replicas[ir])->replica_hills_file_pos, std::ios::beg); if (!is.good()){ // if fail (the file may have been overwritten), reset this // position is.clear(); is.seekg (0, std::ios::beg); // reset the counter (replicas[ir])->replica_hills_file_pos = 0; // schedule to reread the state file (replicas[ir])->replica_state_file_in_sync = false; // and record the failure (replicas[ir])->update_status++; cvm::log ("Failed to read the file \""+(replicas[ir])->replica_hills_file+ "\" at the previous position: will try again in "+ cvm::to_str (replica_update_freq)+" steps.\n"); } else { while ((replicas[ir])->read_hill (is)) { // if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ": received a hill from replica \""+ (replicas[ir])->replica_id+ "\" at step "+ cvm::to_str (((replicas[ir])->hills.back()).it)+".\n"); } is.clear(); // store the position for the next read (replicas[ir])->replica_hills_file_pos = is.tellg(); if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ": stopped reading file \""+(replicas[ir])->replica_hills_file+ "\" at position "+ cvm::to_str ((replicas[ir])->replica_hills_file_pos)+".\n"); - // test whether this is the end of the file + // test whether this is the end of the file is.seekg (0, std::ios::end); if (is.tellg() > (replicas[ir])->replica_hills_file_pos+1) { (replicas[ir])->update_status++; } else { (replicas[ir])->update_status = 0; } } } else { cvm::log ("Failed to read the file \""+(replicas[ir])->replica_hills_file+ "\": will try again in "+ cvm::to_str (replica_update_freq)+" steps.\n"); (replicas[ir])->update_status++; // cvm::fatal_error ("Error: cannot read from file \""+ // (replicas[ir])->replica_hills_file+"\".\n"); } is.close(); } size_t const n_flush = (replica_update_freq/new_hill_freq + 1); if ((replicas[ir])->update_status > 3*n_flush) { // TODO: suspend the calculation? cvm::log ("WARNING: in metadynamics bias \""+this->name+"\""+ " failed to read completely the output of replica \""+ (replicas[ir])->replica_id+ "\" after more than "+ cvm::to_str ((replicas[ir])->update_status * replica_update_freq)+ " steps. Ensure that it is still running.\n"); } } } // ********************************************************************** // input functions // ********************************************************************** std::istream & colvarbias_meta::read_restart (std::istream& is) { size_t const start_pos = is.tellg(); if (comm == single_replica) { - // if using a multiple replicas scheme, output messages + // if using a multiple replicas scheme, output messages // are printed before and after calling this function cvm::log ("Restarting metadynamics bias \""+this->name+"\""+ ".\n"); } std::string key, brace, conf; if ( !(is >> key) || !(key == "metadynamics") || !(is >> brace) || !(brace == "{") || !(is >> colvarparse::read_block ("configuration", conf)) ) { - if (comm == single_replica) + if (comm == single_replica) cvm::log ("Error: in reading restart configuration for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ (replica_state_file_in_sync ? ("at position "+ cvm::to_str (start_pos)+ " in the state file") : "")+".\n"); is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } std::string name = ""; if ( colvarparse::get_keyval (conf, "name", name, std::string (""), colvarparse::parse_silent) && (name != this->name) ) cvm::fatal_error ("Error: in the restart file, the " "\"metadynamics\" block has a different name: different system?\n"); if (name.size() == 0) { cvm::fatal_error ("Error: \"metadynamics\" block within the restart file " "has no identifiers.\n"); } if (comm != single_replica) { std::string replica = ""; if (colvarparse::get_keyval (conf, "replicaID", replica, std::string (""), colvarparse::parse_silent) && (replica != this->replica_id)) cvm::fatal_error ("Error: in the restart file, the " "\"metadynamics\" block has a different replicaID: different system?\n"); colvarparse::get_keyval (conf, "step", state_file_step, cvm::step_absolute(), colvarparse::parse_silent); } bool grids_from_restart_file = use_grids; if (use_grids) { if (expand_grids) { // the boundaries of the colvars may have been changed; TODO: // this reallocation is only for backward-compatibility, and may // be deleted when grid_parameters (i.e. colvargrid's own // internal reallocation) has kicked in delete hills_energy; delete hills_energy_gradients; hills_energy = new colvar_grid_scalar (colvars); hills_energy_gradients = new colvar_grid_gradient (colvars); } colvar_grid_scalar *hills_energy_backup = NULL; colvar_grid_gradient *hills_energy_gradients_backup = NULL; if (has_data) { if (cvm::debug()) cvm::log ("Backupping grids for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n"); hills_energy_backup = hills_energy; hills_energy_gradients_backup = hills_energy_gradients; hills_energy = new colvar_grid_scalar (colvars); hills_energy_gradients = new colvar_grid_gradient (colvars); } size_t const hills_energy_pos = is.tellg(); if (!(is >> key)) { if (hills_energy_backup != NULL) { delete hills_energy; delete hills_energy_gradients; hills_energy = hills_energy_backup; hills_energy_gradients = hills_energy_gradients_backup; } is.clear(); is.seekg (hills_energy_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } else if (!(key == std::string ("hills_energy")) || !(hills_energy->read_restart (is))) { is.clear(); is.seekg (hills_energy_pos, std::ios::beg); grids_from_restart_file = false; if (!rebin_grids) { if (hills_energy_backup == NULL) cvm::fatal_error ("Error: couldn't read the free energy grid for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ "; if useGrids was off when the state file was written, " "enable rebinGrids now to regenerate the grids.\n"); else { if (comm == single_replica) cvm::log ("Error: couldn't read the free energy grid for metadynamics bias \""+ this->name+"\".\n"); delete hills_energy; delete hills_energy_gradients; hills_energy = hills_energy_backup; hills_energy_gradients = hills_energy_gradients_backup; is.setstate (std::ios::failbit); return is; } } } size_t const hills_energy_gradients_pos = is.tellg(); if (!(is >> key)) { if (hills_energy_backup != NULL) { delete hills_energy; delete hills_energy_gradients; hills_energy = hills_energy_backup; hills_energy_gradients = hills_energy_gradients_backup; } is.clear(); is.seekg (hills_energy_gradients_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } else if (!(key == std::string ("hills_energy_gradients")) || !(hills_energy_gradients->read_restart (is))) { is.clear(); is.seekg (hills_energy_gradients_pos, std::ios::beg); grids_from_restart_file = false; - if (!rebin_grids) { + if (!rebin_grids) { if (hills_energy_backup == NULL) cvm::fatal_error ("Error: couldn't read the free energy gradients grid for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ "; if useGrids was off when the state file was written, " "enable rebinGrids now to regenerate the grids.\n"); else { if (comm == single_replica) cvm::log ("Error: couldn't read the free energy gradients grid for metadynamics bias \""+ this->name+"\".\n"); delete hills_energy; delete hills_energy_gradients; hills_energy = hills_energy_backup; hills_energy_gradients = hills_energy_gradients_backup; is.setstate (std::ios::failbit); return is; } } } if (cvm::debug()) cvm::log ("Successfully read new grids for bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+"\n"); if (hills_energy_backup != NULL) { // now that we have successfully updated the grids, delete the // backup copies if (cvm::debug()) cvm::log ("Deallocating the older grids.\n"); delete hills_energy_backup; delete hills_energy_gradients_backup; } } bool const existing_hills = (hills.size() > 0); size_t const old_hills_size = hills.size(); hill_iter old_hills_end = hills.end(); hill_iter old_hills_off_grid_end = hills_off_grid.end(); // read the hills explicitly written (if there are any) while (read_hill (is)) { - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Read a previously saved hill under the " "metadynamics bias \""+ this->name+"\", created at step "+ cvm::to_str ((hills.back()).it)+".\n"); } is.clear(); new_hills_begin = hills.end(); if (grids_from_restart_file) { if (hills.size() > old_hills_size) cvm::log ("Read "+cvm::to_str (hills.size())+ " hills in addition to the grids.\n"); } else { if (hills.size()) cvm::log ("Read "+cvm::to_str (hills.size())+" hills.\n"); } if (rebin_grids) { // allocate new grids (based on the new boundaries and widths just // read from the configuration file), and project onto them the // grids just read from the restart file colvar_grid_scalar *new_hills_energy = new colvar_grid_scalar (colvars); colvar_grid_gradient *new_hills_energy_gradients = new colvar_grid_gradient (colvars); if (!grids_from_restart_file || (keep_hills && hills.size())) { // if there are hills, recompute the new grids from them cvm::log ("Rebinning the energy and forces grids from "+ cvm::to_str (hills.size())+" hills (this may take a while)...\n"); project_hills (hills.begin(), hills.end(), new_hills_energy, new_hills_energy_gradients, true); cvm::log ("rebinning done.\n"); } else { // otherwise, use the grids in the restart file cvm::log ("Rebinning the energy and forces grids " "from the grids in the restart file.\n"); new_hills_energy->map_grid (*hills_energy); new_hills_energy_gradients->map_grid (*hills_energy_gradients); } delete hills_energy; delete hills_energy_gradients; hills_energy = new_hills_energy; hills_energy_gradients = new_hills_energy_gradients; // assuming that some boundaries have expanded, eliminate those // off-grid hills that aren't necessary any more if (hills.size()) recount_hills_off_grid (hills.begin(), hills.end(), hills_energy); } if (use_grids) { if (hills_off_grid.size()) { cvm::log (cvm::to_str (hills_off_grid.size())+" hills are near the " "grid boundaries: they will be computed analytically " "and saved to the state files.\n"); } } is >> brace; if (brace != "}") { cvm::log ("Incomplete restart information for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": no closing brace at position "+ cvm::to_str (is.tellg())+" in the file.\n"); is.setstate (std::ios::failbit); return is; } if (cvm::debug()) cvm::log ("colvarbias_meta::read_restart() done\n"); if (existing_hills) { hills.erase (hills.begin(), old_hills_end); hills_off_grid.erase (hills_off_grid.begin(), old_hills_off_grid_end); } - + has_data = true; if (comm != single_replica) { read_replica_files(); } return is; -} +} std::istream & colvarbias_meta::read_hill (std::istream &is) { if (!is) return is; // do nothing if failbit is set size_t const start_pos = is.tellg(); std::string data; if ( !(is >> read_block ("hill", data)) ) { is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } size_t h_it; get_keyval (data, "step", h_it, 0, parse_silent); if (h_it <= state_file_step) { if (cvm::debug()) cvm::log ("Skipping a hill older than the state file for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+"\n"); return is; } cvm::real h_weight; get_keyval (data, "weight", h_weight, hill_weight, parse_silent); std::vector<colvarvalue> h_centers (colvars.size()); for (size_t i = 0; i < colvars.size(); i++) { - h_centers[i].type ((colvars[i]->value()).type()); + h_centers[i].type ((colvars[i]->value()).type()); } { // it is safer to read colvarvalue objects one at a time; // TODO: change this it later std::string centers_input; key_lookup (data, "centers", centers_input); std::istringstream centers_is (centers_input); for (size_t i = 0; i < colvars.size(); i++) { centers_is >> h_centers[i]; } } std::vector<cvm::real> h_widths (colvars.size()); get_keyval (data, "widths", h_widths, std::vector<cvm::real> (colvars.size(), (std::sqrt (2.0 * PI) / 2.0)), parse_silent); - + std::string h_replica = ""; if (comm != single_replica) { get_keyval (data, "replicaID", h_replica, replica_id, parse_silent); if (h_replica != replica_id) cvm::fatal_error ("Error: trying to read a hill created by replica \""+h_replica+ "\" for replica \""+replica_id+ "\"; did you swap output files?\n"); } hill_iter const hills_end = hills.end(); hills.push_back (hill (h_it, h_weight, h_centers, h_widths, h_replica)); if (new_hills_begin == hills_end) { // if new_hills_begin is unset, set it for the first time new_hills_begin = hills.end(); new_hills_begin--; } if (use_grids) { // add this also to the list of hills that are off-grid, which will // be computed analytically cvm::real const min_dist = hills_energy->bin_distance_from_boundaries ((hills.back()).centers, true); if (min_dist < (3.0 * std::floor (hill_width)) + 1.0) { hills_off_grid.push_back (hills.back()); } } has_data = true; return is; } // ********************************************************************** // output functions // ********************************************************************** std::ostream & colvarbias_meta::write_restart (std::ostream& os) { os << "metadynamics {\n" << " configuration {\n" << " step " << cvm::step_absolute() << "\n" << " name " << this->name << "\n"; if (this->comm != single_replica) os << " replicaID " << this->replica_id << "\n"; os << " }\n\n"; if (use_grids) { // this is a very good time to project hills, if you haven't done // it already! project_hills (new_hills_begin, hills.end(), hills_energy, hills_energy_gradients); new_hills_begin = hills.end(); // write down the grids to the restart file os << " hills_energy\n"; hills_energy->write_restart (os); os << " hills_energy_gradients\n"; hills_energy_gradients->write_restart (os); } if ( (!use_grids) || keep_hills ) { // write all hills currently in memory for (std::list<hill>::const_iterator h = this->hills.begin(); h != this->hills.end(); h++) { os << *h; } } else { // write just those that are near the grid boundaries for (std::list<hill>::const_iterator h = this->hills_off_grid.begin(); h != this->hills_off_grid.end(); h++) { os << *h; } } os << "}\n\n"; if (comm != single_replica) { write_replica_state_file(); // schedule to reread the state files of the other replicas (they // have also rewritten them) for (size_t ir = 0; ir < replicas.size(); ir++) { (replicas[ir])->replica_state_file_in_sync = false; } } if (dump_fes) { write_pmf(); } return os; -} +} void colvarbias_meta::write_pmf() { // allocate a new grid to store the pmf colvar_grid_scalar *pmf = new colvar_grid_scalar (*hills_energy); pmf->create(); std::string fes_file_name_prefix (cvm::output_prefix); - + if ((cvm::n_meta_biases > 1) || (cvm::n_abf_biases > 0)) { // if this is not the only free energy integrator, append // this bias's name, to distinguish it from the output of the other // biases producing a .pmf file // TODO: fix for ABF with updateBias == no fes_file_name_prefix += ("."+this->name); } if ((comm == single_replica) || (dump_replica_fes)) { // output the PMF from this instance or replica pmf->reset(); pmf->add_grid (*hills_energy); cvm::real const max = pmf->maximum_value(); pmf->add_constant (-1.0 * max); pmf->multiply_constant (-1.0); if (well_tempered) { cvm::real const well_temper_scale = (bias_temperature + cvm::temperature()) / bias_temperature; pmf->multiply_constant (well_temper_scale); } { std::string const fes_file_name (fes_file_name_prefix + ((comm != single_replica) ? ".partial" : "") + (dump_fes_save ? "."+cvm::to_str (cvm::step_absolute()) : "") + ".pmf"); cvm::backup_file (fes_file_name.c_str()); std::ofstream fes_os (fes_file_name.c_str()); pmf->write_multicol (fes_os); fes_os.close(); } } if (comm != single_replica) { // output the combined PMF from all replicas pmf->reset(); pmf->add_grid (*hills_energy); for (size_t ir = 0; ir < replicas.size(); ir++) { pmf->add_grid (*(replicas[ir]->hills_energy)); } cvm::real const max = pmf->maximum_value(); pmf->add_constant (-1.0 * max); pmf->multiply_constant (-1.0); if (well_tempered) { cvm::real const well_temper_scale = (bias_temperature + cvm::temperature()) / bias_temperature; pmf->multiply_constant (well_temper_scale); } std::string const fes_file_name (fes_file_name_prefix + (dump_fes_save ? "."+cvm::to_str (cvm::step_absolute()) : "") + ".pmf"); cvm::backup_file (fes_file_name.c_str()); std::ofstream fes_os (fes_file_name.c_str()); pmf->write_multicol (fes_os); fes_os.close(); } delete pmf; } void colvarbias_meta::write_replica_state_file() { // write down also the restart for the other replicas: TODO: this // is duplicated code, that could be cleaned up later cvm::backup_file (replica_state_file.c_str()); std::ofstream rep_state_os (replica_state_file.c_str()); if (!rep_state_os.good()) cvm::fatal_error ("Error: in opening file \""+ replica_state_file+"\" for writing.\n"); - rep_state_os.setf (std::ios::scientific, std::ios::floatfield); + rep_state_os.setf (std::ios::scientific, std::ios::floatfield); rep_state_os << "\n" << "metadynamics {\n" << " configuration {\n" << " name " << this->name << "\n" << " step " << cvm::step_absolute() << "\n"; if (this->comm != single_replica) { rep_state_os << " replicaID " << this->replica_id << "\n"; } rep_state_os << " }\n\n"; rep_state_os << " hills_energy\n"; rep_state_os << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width); hills_energy->write_restart (rep_state_os); rep_state_os << " hills_energy_gradients\n"; rep_state_os << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width); hills_energy_gradients->write_restart (rep_state_os); if ( (!use_grids) || keep_hills ) { // write all hills currently in memory for (std::list<hill>::const_iterator h = this->hills.begin(); h != this->hills.end(); h++) { rep_state_os << *h; } } else { // write just those that are near the grid boundaries for (std::list<hill>::const_iterator h = this->hills_off_grid.begin(); h != this->hills_off_grid.end(); h++) { rep_state_os << *h; } } rep_state_os << "}\n\n"; rep_state_os.close(); // reopen the hills file replica_hills_os.close(); replica_hills_os.open (replica_hills_file.c_str()); if (!replica_hills_os.good()) cvm::fatal_error ("Error: in opening file \""+ replica_hills_file+"\" for writing.\n"); replica_hills_os.setf (std::ios::scientific, std::ios::floatfield); } std::string colvarbias_meta::hill::output_traj() { std::ostringstream os; os.setf (std::ios::fixed, std::ios::floatfield); os << std::setw (cvm::it_width) << it << " "; os.setf (std::ios::scientific, std::ios::floatfield); os << " "; for (size_t i = 0; i < centers.size(); i++) { os << " "; os << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << centers[i]; } os << " "; for (size_t i = 0; i < widths.size(); i++) { os << " "; os << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << widths[i]; } os << " "; os << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) << W << "\n"; return os.str(); -} +} std::ostream & operator << (std::ostream &os, colvarbias_meta::hill const &h) { os.setf (std::ios::scientific, std::ios::floatfield); os << "hill {\n"; os << " step " << std::setw (cvm::it_width) << h.it << "\n"; os << " weight " << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) << h.W << "\n"; if (h.replica.size()) os << " replicaID " << h.replica << "\n"; os << " centers "; for (size_t i = 0; i < (h.centers).size(); i++) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << h.centers[i]; } os << "\n"; os << " widths "; for (size_t i = 0; i < (h.widths).size(); i++) { os << " " << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) << h.widths[i]; } os << "\n"; os << "}\n"; return os; } diff --git a/lib/colvars/colvarbias_meta.h b/lib/colvars/colvarbias_meta.h index 1f51e5469..8c3eb9100 100644 --- a/lib/colvars/colvarbias_meta.h +++ b/lib/colvars/colvarbias_meta.h @@ -1,425 +1,425 @@ #ifndef COLVARBIAS_META_H #define COLVARBIAS_META_H #include <vector> #include <list> #include <sstream> #include <fstream> #include "colvarbias.h" #include "colvargrid.h" /// Metadynamics bias (implementation of \link colvarbias \endlink) class colvarbias_meta : public colvarbias { public: /// Communication between different replicas enum Communication { /// One replica (default) single_replica, /// Hills added concurrently by several replicas multiple_replicas }; /// Communication between different replicas Communication comm; /// Constructor colvarbias_meta (std::string const &conf, char const *key); /// Default constructor colvarbias_meta(); /// Destructor virtual ~colvarbias_meta(); - + virtual cvm::real update(); virtual std::istream & read_restart (std::istream &is); virtual std::ostream & write_restart (std::ostream &os); virtual void write_pmf(); class hill; typedef std::list<hill>::iterator hill_iter; protected: /// \brief width of a hill /// /// The local width of each collective variable, multiplied by this /// number, provides the hill width along that direction cvm::real hill_width; /// \brief Number of simulation steps between two hills size_t new_hill_freq; /// Write the hill logfile bool b_hills_traj; /// Logfile of hill management (creation and deletion) std::ofstream hills_traj_os; /// \brief List of hills used on this bias (total); if a grid is /// employed, these don't need to be updated at every time step std::list<hill> hills; /// \brief Iterator to the first of the "newest" hills (when using /// grids, those who haven't been mapped yet) hill_iter new_hills_begin; /// \brief List of hills used on this bias that are on the boundary /// edges; these are updated regardless of whether hills are used std::list<hill> hills_off_grid; /// \brief Same as new_hills_begin, but for the off-grid ones hill_iter new_hills_off_grid_begin; /// Regenerate the hills_off_grid list void recount_hills_off_grid (hill_iter h_first, hill_iter h_last, colvar_grid_scalar *ge); /// Read a hill from a file std::istream & read_hill (std::istream &is); /// \brief step present in a state file - /// + /// /// When using grids and reading state files containing them /// (multiple replicas), this is used to check whether a hill is /// newer or older than the grids size_t state_file_step; /// \brief Add a new hill; if a .hills trajectory is written, /// write it there; if there is more than one replica, communicate /// it to the others virtual std::list<hill>::const_iterator create_hill (hill const &h); /// \brief Remove a previously saved hill (returns an iterator for /// the next hill in the list) virtual std::list<hill>::const_iterator delete_hill (hill_iter &h); /// \brief Calculate the values of the hills, incrementing /// bias_energy virtual void calc_hills (hill_iter h_first, hill_iter h_last, cvm::real &energy, std::vector<colvarvalue> const &values = std::vector<colvarvalue> (0)); /// \brief Calculate the forces acting on the i-th colvar, /// incrementing colvar_forces[i]; must be called after calc_hills /// each time the values of the colvars are changed virtual void calc_hills_force (size_t const &i, hill_iter h_first, hill_iter h_last, std::vector<colvarvalue> &forces, std::vector<colvarvalue> const &values = std::vector<colvarvalue> (0)); /// Height of new hills cvm::real hill_weight; /// \brief Bin the hills on grids of energy and forces, and use them /// to force the colvars (as opposed to deriving the hills analytically) bool use_grids; /// \brief Rebin the hills upon restarting bool rebin_grids; /// \brief Should the grids be expanded if necessary? bool expand_grids; /// \brief How often the hills should be projected onto the grids size_t grids_freq; /// \brief Whether to keep the hills in the restart file (e.g. to do /// meaningful accurate rebinning afterwards) bool keep_hills; /// \brief Dump the free energy surface (.pmf file) every restartFrequency bool dump_fes; /// \brief Dump the free energy surface (.pmf file) every restartFrequency /// using only the hills from this replica (only applicable to more than one replica) bool dump_replica_fes; /// \brief Dump the free energy surface files at different /// time steps, appending the step number to each file bool dump_fes_save; - /// \brief Whether to use well-tempered metadynamics - bool well_tempered; + /// \brief Whether to use well-tempered metadynamics + bool well_tempered; - /// \brief Biasing temperature in well-tempered metadynamics + /// \brief Biasing temperature in well-tempered metadynamics cvm::real bias_temperature; /// \brief Try to read the restart information by allocating new /// grids before replacing the current ones (used e.g. in /// multiple_replicas) bool safely_read_restart; /// Hill energy, cached on a grid colvar_grid_scalar *hills_energy; /// Hill forces, cached on a grid colvar_grid_gradient *hills_energy_gradients; /// \brief Project the selected hills onto grids void project_hills (hill_iter h_first, hill_iter h_last, colvar_grid_scalar *ge, colvar_grid_gradient *gf, bool print_progress = false); // Multiple Replicas variables and functions /// \brief Identifier for this replica std::string replica_id; /// \brief File containing the paths to the output files from this replica std::string replica_file_name; /// \brief Read the existing replicas on registry virtual void update_replicas_registry(); /// \brief Read new data from replicas' files virtual void read_replica_files(); /// \brief Write data to other replicas virtual void write_replica_state_file(); /// \brief Additional, "mirror" metadynamics biases, to collect info /// from the other replicas /// /// These are supposed to be synchronized by reading data from the /// other replicas, and not be modified by the "local" replica std::vector<colvarbias_meta *> replicas; /// \brief Frequency at which data the "mirror" biases are updated size_t replica_update_freq; /// List of replicas (and their output list files): contents are /// copied into replicas_registry for convenience std::string replicas_registry_file; /// List of replicas (and their output list files) std::string replicas_registry; /// List of files written by this replica std::string replica_list_file; /// Hills energy and gradients written specifically for other /// replica (in addition to its own restart file) std::string replica_state_file; /// Whether a mirror bias has read the latest version of its state file bool replica_state_file_in_sync; /// If there was a failure reading one of the files (because they /// are not complete), this counter is incremented size_t update_status; /// Explicit hills communicated between replicas /// /// This file becomes empty after replica_state_file is rewritten std::string replica_hills_file; /// \brief Output stream corresponding to replica_hills_file std::ofstream replica_hills_os; /// Position within replica_hills_file (when reading it) size_t replica_hills_file_pos; }; /// \brief A hill for the metadynamics bias class colvarbias_meta::hill { protected: /// Value of the hill function (ranges between 0 and 1) cvm::real hill_value; /// Scale factor, which could be modified at runtime (default: 1) cvm::real sW; /// Maximum height in energy of the hill cvm::real W; /// Center of the hill in the collective variable space std::vector<colvarvalue> centers; /// Widths of the hill in the collective variable space std::vector<cvm::real> widths; public: friend class colvarbias_meta; /// Time step at which this hill was added size_t it; /// Identity of the replica who added this hill (only in multiple replica simulations) std::string replica; /// \brief Runtime constructor: data are read directly from /// collective variables \param weight Weight of the hill \param /// cv Pointer to the array of collective variables involved \param /// replica (optional) Identity of the replica which creates the /// hill inline hill (cvm::real const &W_in, std::vector<colvar *> &cv, cvm::real const &hill_width, std::string const &replica_in = "") : sW (1.0), W (W_in), centers (cv.size()), widths (cv.size()), it (cvm::it), replica (replica_in) { for (size_t i = 0; i < cv.size(); i++) { centers[i].type (cv[i]->type()); centers[i] = cv[i]->value(); widths[i] = cv[i]->width * hill_width; } - if (cvm::debug()) + if (cvm::debug()) cvm::log ("New hill, applied to "+cvm::to_str (cv.size())+ " collective variables, with centers "+ cvm::to_str (centers)+", widths "+ cvm::to_str (widths)+" and weight "+ cvm::to_str (W)+".\n"); } /// \brief General constructor: all data are explicitly passed as /// arguments (used for instance when reading hills saved on a /// file) \param it Time step of creation of the hill \param /// weight Weight of the hill \param centers Center of the hill /// \param widths Width of the hill around centers \param replica /// (optional) Identity of the replica which creates the hill inline hill (size_t const &it_in, cvm::real const &W_in, std::vector<colvarvalue> const ¢ers_in, std::vector<cvm::real> const &widths_in, std::string const &replica_in = "") : sW (1.0), W (W_in), centers (centers_in), widths (widths_in), it (it_in), replica (replica_in) {} /// Copy constructor inline hill (colvarbias_meta::hill const &h) : sW (1.0), W (h.W), centers (h.centers), widths (h.widths), it (h.it), replica (h.replica) {} /// Destructor inline ~hill() {} - + /// Get the energy inline cvm::real energy() { return W * sW * hill_value; } /// Get the energy using another hill weight inline cvm::real energy (cvm::real const &new_weight) { return new_weight * sW * hill_value; } /// Get the current hill value inline cvm::real const &value() { return hill_value; } /// Set the hill value as specified inline void value (cvm::real const &new_value) { hill_value = new_value; } /// Get the weight inline cvm::real weight() { return W * sW; } /// Scale the weight with this factor (by default 1.0 is used) inline void scale (cvm::real const &new_scale_fac) { sW = new_scale_fac; } /// Get the center of the hill inline std::vector<colvarvalue> & center() { return centers; } /// Get the i-th component of the center inline colvarvalue & center (size_t const &i) { return centers[i]; } /// Comparison operator inline friend bool operator < (hill const &h1, hill const &h2) { if (h1.it < h2.it) return true; else return false; } /// Comparison operator inline friend bool operator <= (hill const &h1, hill const &h2) { if (h1.it <= h2.it) return true; else return false; } /// Comparison operator inline friend bool operator > (hill const &h1, hill const &h2) { if (h1.it > h2.it) return true; else return false; } /// Comparison operator inline friend bool operator >= (hill const &h1, hill const &h2) { if (h1.it >= h2.it) return true; else return false; } /// Comparison operator inline friend bool operator == (hill const &h1, hill const &h2) { if ( (h1.it >= h2.it) && (h1.replica == h2.replica) ) return true; else return false; } /// Represent the hill ina string suitable for a trajectory file std::string output_traj(); /// Write the hill to an output stream inline friend std::ostream & operator << (std::ostream &os, hill const &h); }; #endif // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvarcomp.cpp b/lib/colvars/colvarcomp.cpp index 25da537da..316454e8e 100644 --- a/lib/colvars/colvarcomp.cpp +++ b/lib/colvars/colvarcomp.cpp @@ -1,147 +1,147 @@ #include "colvarmodule.h" #include "colvarvalue.h" #include "colvar.h" #include "colvarcomp.h" colvar::cvc::cvc() : sup_coeff (1.0), sup_np (1), b_periodic (false), b_debug_gradients (false), b_inverse_gradients (false), b_Jacobian_derivative (false) {} colvar::cvc::cvc (std::string const &conf) : sup_coeff (1.0), sup_np (1), b_periodic (false), b_debug_gradients (false), b_inverse_gradients (false), b_Jacobian_derivative (false) { if (cvm::debug()) cvm::log ("Initializing cvc base object.\n"); get_keyval (conf, "name", this->name, std::string (""), parse_silent); get_keyval (conf, "componentCoeff", sup_coeff, 1.0); get_keyval (conf, "componentExp", sup_np, 1); get_keyval (conf, "period", period, 0.0); get_keyval (conf, "wrapAround", wrap_center, 0.0); get_keyval (conf, "debugGradients", b_debug_gradients, false, parse_silent); if (cvm::debug()) cvm::log ("Done initializing cvc base object.\n"); } -void colvar::cvc::parse_group (std::string const &conf, +void colvar::cvc::parse_group (std::string const &conf, char const *group_key, cvm::atom_group &group, bool optional) { if (key_lookup (conf, group_key)) { group.parse (conf, group_key); } else { if (! optional) { cvm::fatal_error ("Error: definition for atom group \""+ std::string (group_key)+"\" not found.\n"); } } } colvar::cvc::~cvc() {} void colvar::cvc::calc_force_invgrads() { cvm::fatal_error ("Error: calculation of inverse gradients is not implemented " "for colvar components of type \""+function_type+"\".\n"); } void colvar::cvc::calc_Jacobian_derivative() { cvm::fatal_error ("Error: calculation of inverse gradients is not implemented " "for colvar components of type \""+function_type+"\".\n"); } void colvar::cvc::debug_gradients (cvm::atom_group &group) { // this function should work for any scalar variable: // the only difference will be the name of the atom group (here, "group") // collect into a vector for convenience std::vector<cvm::rvector> gradients (group.size()); for (size_t i = 0; i < group.size(); i++) { gradients[i] = group[i].grad; } cvm::rotation const rot_0 = group.rot; cvm::rotation const rot_inv = group.rot.inverse(); cvm::real const x_0 = x.real_value; cvm::log ("gradients = "+cvm::to_str (gradients)+"\n"); // it only makes sense to debug the fit gradients // when the fitting group is the same as this group if (group.b_rotate || group.b_center) if (group.b_fit_gradients && (group.ref_pos_group == NULL)) { group.calc_fit_gradients(); if (group.b_rotate) { // fit_gradients are in the original frame, we should print them in the rotated frame for (size_t j = 0; j < group.fit_gradients.size(); j++) { group.fit_gradients[j] = rot_0.rotate (group.fit_gradients[j]); } } cvm::log ("fit_gradients = "+cvm::to_str (group.fit_gradients)+"\n"); if (group.b_rotate) { for (size_t j = 0; j < group.fit_gradients.size(); j++) { group.fit_gradients[j] = rot_inv.rotate (group.fit_gradients[j]); } } } for (size_t ia = 0; ia < group.size(); ia++) { // tests are best conducted in the unrotated (simulation) frame cvm::rvector const atom_grad = group.b_rotate ? rot_inv.rotate (group[ia].grad) : group[ia].grad; for (size_t id = 0; id < 3; id++) { // (re)read original positions group.read_positions(); - // change one coordinate + // change one coordinate group[ia].pos[id] += cvm::debug_gradients_step_size; // (re)do the fit (if defined) if (group.b_center || group.b_rotate) { group.calc_apply_roto_translation(); } calc_value(); cvm::real const x_1 = x.real_value; cvm::log ("Atom "+cvm::to_str (ia)+", component "+cvm::to_str (id)+":\n"); cvm::log ("dx(actual) = "+cvm::to_str (x_1 - x_0, 21, 14)+"\n"); cvm::real const dx_pred = (group.fit_gradients.size() && (group.ref_pos_group == NULL)) ? (cvm::debug_gradients_step_size * (atom_grad[id] + group.fit_gradients[ia][id])) : (cvm::debug_gradients_step_size * atom_grad[id]); cvm::log ("dx(interp) = "+cvm::to_str (dx_pred, 21, 14)+"\n"); cvm::log ("|dx(actual) - dx(interp)|/|dx(actual)| = "+ cvm::to_str (std::fabs (x_1 - x_0 - dx_pred) / std::fabs (x_1 - x_0), 12, 5)+ ".\n"); } } } diff --git a/lib/colvars/colvarcomp.h b/lib/colvars/colvarcomp.h index 3924ba24f..078c9b4cd 100644 --- a/lib/colvars/colvarcomp.h +++ b/lib/colvars/colvarcomp.h @@ -1,1445 +1,1445 @@ #ifndef COLVARCOMP_H #define COLVARCOMP_H // Declaration of colvar::cvc base class and derived ones. // // Future cvc's could be declared on additional header files. -// After the declaration of a new derived class, its metric +// After the declaration of a new derived class, its metric // functions must be reimplemented as well. // If the new cvc has no symmetry or periodicity, // this can be done straightforwardly by using the macro: // simple_scalar_dist_functions (derived_class) #include <fstream> #include <cmath> #include "colvarmodule.h" #include "colvar.h" #include "colvaratoms.h" /// \brief Colvar component (base class); most implementations of /// \link cvc \endlink utilize one or more \link /// colvarmodule::atom \endlink or \link colvarmodule::atom_group /// \endlink objects to access atoms. /// /// A \link cvc \endlink object (or an object of a /// cvc-derived class) specifies how to calculate a collective /// variable, its gradients and other related physical quantities /// which do not depend only on the numeric value (the \link colvar /// \endlink class already serves this purpose). /// /// No restriction is set to what kind of calculation a \link /// cvc \endlink object performs (usually calculate an /// analytical function of atomic coordinates). The only constraint /// is that the value calculated is implemented as a \link colvarvalue /// \endlink object. This serves to provide a unique way to calculate /// scalar and non-scalar collective variables, and specify if and how /// they can be combined together by the parent \link colvar \endlink /// object. /// /// <b> If you wish to implement a new collective variable component, you /// should write your own class by inheriting directly from \link /// cvc \endlink, or one of its derived classes (for instance, /// \link distance \endlink is frequently used, because it provides /// useful data and function members for any colvar based on two /// atom groups). The steps are: \par /// 1. add the name of this class under colvar.h \par /// 2. add a call to the parser in colvar.C, within the function colvar::colvar() \par /// 3. declare the class in colvarcomp.h \par /// 4. implement the class in one of the files colvarcomp_*.C -/// +/// /// </b> /// The cvm::atom and cvm::atom_group classes are available to /// transparently communicate with the simulation program. However, /// they are not strictly needed, as long as all the degrees of /// freedom associated to the cvc are properly evolved by a simple /// call to e.g. apply_force(). class colvar::cvc : public colvarparse { public: /// \brief The name of the object (helps to identify this /// cvc instance when debugging) std::string name; /// \brief Description of the type of collective variable - /// + /// /// Normally this string is set by the parent \link colvar \endlink /// object within its constructor, when all \link cvc \endlink /// objects are initialized; therefore the main "config string" /// constructor does not need to define it. If a \link cvc /// \endlink is initialized and/or a different constructor is used, /// this variable definition should be set within the constructor. std::string function_type; /// \brief Type of \link colvarvalue \endlink that this cvc /// provides colvarvalue::Type type() const; /// \brief Coefficient in the polynomial combination (default: 1.0) cvm::real sup_coeff; /// \brief Exponent in the polynomial combination (default: 1) int sup_np; /// \brief Period of this cvc value, (default: 0.0, non periodic) cvm::real period; /// \brief If the component is periodic, wrap around this value (default: 0.0) cvm::real wrap_center; bool b_periodic; /// \brief Constructor /// /// At least one constructor which reads a string should be defined /// for every class inheriting from cvc \param conf Contents /// of the configuration file pertaining to this \link cvc /// \endlink cvc (std::string const &conf); /// \brief Within the constructor, make a group parse its own /// options from the provided configuration string - void parse_group (std::string const &conf, + void parse_group (std::string const &conf, char const *group_key, cvm::atom_group &group, bool optional = false); /// \brief Default constructor (used when \link cvc \endlink /// objects are declared within other ones) cvc(); /// Destructor virtual ~cvc(); /// \brief If this flag is false (default), inverse gradients /// (derivatives of atom coordinates with respect to x) are /// unavailable; it should be set to true by the constructor of each /// derived object capable of calculating them bool b_inverse_gradients; /// \brief If this flag is false (default), the Jacobian derivative /// (divergence of the inverse gradients) is unavailable; it should /// be set to true by the constructor of each derived object capable /// of calculating it bool b_Jacobian_derivative; /// \brief Calculate the variable virtual void calc_value() = 0; /// \brief Calculate the atomic gradients, to be reused later in /// order to apply forces virtual void calc_gradients() = 0; /// \brief If true, calc_gradients() will call debug_gradients() for every group needed bool b_debug_gradients; /// \brief Calculate finite-difference gradients alongside the analytical ones, for each Cartesian component virtual void debug_gradients (cvm::atom_group &group); /// \brief Calculate the total force from the system using the /// inverse atomic gradients virtual void calc_force_invgrads(); /// \brief Calculate the divergence of the inverse atomic gradients virtual void calc_Jacobian_derivative(); /// \brief Return the previously calculated value virtual colvarvalue value() const; /// \brief Return the previously calculated system force virtual colvarvalue system_force() const; /// \brief Return the previously calculated divergence of the /// inverse atomic gradients virtual colvarvalue Jacobian_derivative() const; /// \brief Apply the collective variable force, by communicating the /// atomic forces to the simulation program (\b Note: the \link ft /// \endlink member is not altered by this function) /// /// Note: multiple calls to this function within the same simulation /// step will add the forces altogether \param cvforce The /// collective variable force, usually coming from the biases and /// eventually manipulated by the parent \link colvar \endlink /// object virtual void apply_force (colvarvalue const &cvforce) = 0; /// \brief Square distance between x1 and x2 (can be redefined to /// transparently implement constraints, symmetries and /// periodicities) - /// + /// /// colvar::cvc::dist2() and the related functions are /// declared as "const" functions, but not "static", because /// additional parameters defining the metrics (e.g. the /// periodicity) may be specific to each colvar::cvc object. /// /// If symmetries or periodicities are present, the /// colvar::cvc::dist2() should be redefined to return the /// "closest distance" value and colvar::cvc::dist2_lgrad(), /// colvar::cvc::dist2_rgrad() to return its gradients. /// /// If constraints are present (and not already implemented by any /// of the \link colvarvalue \endlink types), the /// colvar::cvc::dist2_lgrad() and /// colvar::cvc::dist2_rgrad() functions should be redefined /// to provide a gradient which is compatible with the constraint, /// i.e. already deprived of its component normal to the constraint /// hypersurface. - /// + /// /// Finally, another useful application, if you are performing very /// many operations with these functions, could be to override the /// \link colvarvalue \endlink member functions and access directly /// its member data. For instance: to define dist2(x1,x2) as /// (x2.real_value-x1.real_value)*(x2.real_value-x1.real_value) in /// case of a scalar \link colvarvalue \endlink type. virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Gradient (with respect to x1) of the square distance (can /// be redefined to transparently implement constraints, symmetries /// and periodicities) virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Gradient (with respect to x2) of the square distance (can /// be redefined to transparently implement constraints, symmetries /// and periodicities) virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Return a positive number if x2>x1, zero if x2==x1, /// negative otherwise (can be redefined to transparently implement /// constraints, symmetries and periodicities) \b Note: \b it \b /// only \b works \b with \b scalar \b variables, otherwise raises /// an error virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Wrapp value (for periodic/symmetric cvcs) virtual void wrap (colvarvalue &x) const; /// \brief Pointers to all atom groups, to let colvars collect info /// e.g. atomic gradients std::vector<cvm::atom_group *> atom_groups; protected: /// \brief Cached value colvarvalue x; /// \brief Value at the previous step colvarvalue x_old; /// \brief Calculated system force (\b Note: this is calculated from /// the total atomic forces read from the program, subtracting fromt /// the "internal" forces of the system the "external" forces from /// the colvar biases) colvarvalue ft; /// \brief Calculated Jacobian derivative (divergence of the inverse /// gradients): serves to calculate the phase space correction colvarvalue jd; }; inline colvarvalue::Type colvar::cvc::type() const { return x.type(); } inline colvarvalue colvar::cvc::value() const { return x; } inline colvarvalue colvar::cvc::system_force() const { return ft; } inline colvarvalue colvar::cvc::Jacobian_derivative() const { return jd; } inline cvm::real colvar::cvc::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { return x1.dist2 (x2); } inline colvarvalue colvar::cvc::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { return x1.dist2_grad (x2); } inline colvarvalue colvar::cvc::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { return x2.dist2_grad (x1); } inline cvm::real colvar::cvc::compare (colvarvalue const &x1, colvarvalue const &x2) const { if (this->type() == colvarvalue::type_scalar) { return cvm::real (x1 - x2); } else { cvm::fatal_error ("Error: you requested an operation which requires " "comparison between two non-scalar values.\n"); return 0.0; } } inline void colvar::cvc::wrap (colvarvalue &x) const { return; } /// \brief Colvar component: distance between the centers of mass of /// two groups (colvarvalue::type_scalar type, range [0:*)) /// /// This class also serves as the template for many collective /// variables with two atom groups: in this case, the /// distance::distance() constructor should be called on the same /// configuration string, to make the same member data and functions /// available to the derived object class colvar::distance : public colvar::cvc { protected: /// First atom group cvm::atom_group group1; /// Second atom group cvm::atom_group group2; /// Vector distance, cached to be recycled cvm::rvector dist_v; /// Use absolute positions, ignoring PBCs when present bool b_no_PBC; /// Compute system force on first site only to avoid unwanted /// coupling to other colvars (see e.g. Ciccotti et al., 2005) bool b_1site_force; public: distance (std::string const &conf, bool twogroups = true); distance(); virtual inline ~distance() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; // \brief Colvar component: distance vector between centers of mass // of two groups (\link colvarvalue::type_vector \endlink type, // range (-*:*)x(-*:*)x(-*:*)) class colvar::distance_vec : public colvar::distance { public: distance_vec (std::string const &conf); distance_vec(); virtual inline ~distance_vec() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); /// Redefined to handle the box periodicity virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the box periodicity virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the box periodicity virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the box periodicity virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: distance unit vector (direction) between /// centers of mass of two groups (colvarvalue::type_unitvector type, /// range [-1:1]x[-1:1]x[-1:1]) class colvar::distance_dir : public colvar::distance { public: distance_dir (std::string const &conf); distance_dir(); virtual inline ~distance_dir() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: projection of the distance vector along /// an axis (colvarvalue::type_scalar type, range (-*:*)) class colvar::distance_z : public colvar::cvc { protected: /// Main atom group cvm::atom_group main; /// Reference atom group cvm::atom_group ref1; /// Optional, second ref atom group cvm::atom_group ref2; /// Use absolute positions, ignoring PBCs when present bool b_no_PBC; /// Compute system force on one site only to avoid unwanted /// coupling to other colvars (see e.g. Ciccotti et al., 2005) bool b_1site_force; /// Vector on which the distance vector is projected cvm::rvector axis; /// Norm of the axis cvm::real axis_norm; /// Vector distance, cached to be recycled cvm::rvector dist_v; /// Flag: using a fixed axis vector? bool fixed_axis; public: distance_z (std::string const &conf); distance_z(); virtual inline ~distance_z() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; /// \brief Redefined to make use of the user-provided period virtual void wrap (colvarvalue &x) const; }; /// \brief Colvar component: projection of the distance vector on a /// plane (colvarvalue::type_scalar type, range [0:*)) class colvar::distance_xy : public colvar::distance_z { protected: /// Components of the distance vector orthogonal to the axis cvm::rvector dist_v_ortho; /// Vector distances cvm::rvector v12, v13; public: distance_xy (std::string const &conf); distance_xy(); virtual inline ~distance_xy() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: average distance between two groups of atoms, weighted as the sixth power, /// as in NMR refinements (colvarvalue::type_scalar type, range (0:*)) class colvar::distance_inv : public colvar::distance { protected: /// Components of the distance vector orthogonal to the axis int exponent; public: distance_inv (std::string const &conf); distance_inv(); virtual inline ~distance_inv() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: Radius of gyration of an atom group /// (colvarvalue::type_scalar type, range [0:*)) class colvar::gyration : public colvar::cvc { protected: /// Atoms involved cvm::atom_group atoms; public: /// Constructor gyration (std::string const &conf); gyration(); virtual inline ~gyration() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: moment of inertia of an atom group /// (colvarvalue::type_scalar type, range [0:*)) class colvar::inertia : public colvar::gyration { public: /// Constructor inertia (std::string const &conf); inertia(); virtual inline ~inertia() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: moment of inertia of an atom group /// around a user-defined axis (colvarvalue::type_scalar type, range [0:*)) class colvar::inertia_z : public colvar::inertia { protected: /// Vector on which the inertia tensor is projected cvm::rvector axis; public: /// Constructor inertia_z (std::string const &conf); inertia_z(); virtual inline ~inertia_z() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: projection of 3N coordinates onto an /// eigenvector (colvarvalue::type_scalar type, range (-*:*)) class colvar::eigenvector : public colvar::cvc { protected: /// Atom group cvm::atom_group atoms; /// Reference coordinates std::vector<cvm::atom_pos> ref_pos; /// Geometric center of the reference coordinates cvm::rvector ref_pos_center; /// Eigenvector (of a normal or essential mode): will always have zero center std::vector<cvm::rvector> eigenvec; /// Inverse square norm of the eigenvector cvm::real eigenvec_invnorm2; public: /// Constructor eigenvector (std::string const &conf); virtual inline ~eigenvector() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: angle between the centers of mass of /// three groups (colvarvalue::type_scalar type, range [0:PI]) class colvar::angle : public colvar::cvc { protected: /// Atom group cvm::atom_group group1; /// Atom group cvm::atom_group group2; /// Atom group cvm::atom_group group3; /// Inter site vectors cvm::rvector r21, r23; /// Inter site vector norms cvm::real r21l, r23l; /// Derivatives wrt group centers of mass cvm::rvector dxdr1, dxdr3; /// Compute system force on first site only to avoid unwanted /// coupling to other colvars (see e.g. Ciccotti et al., 2005) /// (or to allow dummy atoms) bool b_1site_force; public: /// Initialize by parsing the configuration angle (std::string const &conf); /// \brief Initialize the three groups after three atoms angle (cvm::atom const &a1, cvm::atom const &a2, cvm::atom const &a3); angle(); virtual inline ~angle() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: dihedral between the centers of mass of /// four groups (colvarvalue::type_scalar type, range [-PI:PI]) class colvar::dihedral : public colvar::cvc { protected: /// Atom group cvm::atom_group group1; /// Atom group cvm::atom_group group2; /// Atom group cvm::atom_group group3; /// Atom group cvm::atom_group group4; /// Inter site vectors cvm::rvector r12, r23, r34; /// \brief Compute system force on first site only to avoid unwanted /// coupling to other colvars (see e.g. Ciccotti et al., 2005) bool b_1site_force; - + public: /// Initialize by parsing the configuration dihedral (std::string const &conf); /// \brief Initialize the four groups after four atoms dihedral (cvm::atom const &a1, cvm::atom const &a2, cvm::atom const &a3, cvm::atom const &a4); dihedral(); virtual inline ~dihedral() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); /// Redefined to handle the 2*PI periodicity virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual void wrap (colvarvalue &x) const; }; /// \brief Colvar component: coordination number between two groups /// (colvarvalue::type_scalar type, range [0:N1*N2]) class colvar::coordnum : public colvar::distance { protected: /// \brief "Cutoff" for isotropic calculation (default) cvm::real r0; /// \brief "Cutoff vector" for anisotropic calculation cvm::rvector r0_vec; /// \brief Wheter dist/r0 or \vec{dist}*\vec{1/r0_vec} should ne be /// used bool b_anisotropic; /// Integer exponent of the function numerator int en; /// Integer exponent of the function denominator int ed; /// \brief If true, group2 will be treated as a single atom /// (default: loop over all pairs of atoms in group1 and group2) bool b_group2_center_only; public: /// Constructor coordnum (std::string const &conf); coordnum(); virtual inline ~coordnum() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); template<bool b_gradients> /// \brief Calculate a coordination number through the function /// (1-x**n)/(1-x**m), x = |A1-A2|/r0 \param r0 "cutoff" for the /// coordination number \param exp_num \i n exponent \param exp_den /// \i m exponent \param A1 atom \param A2 atom static cvm::real switching_function (cvm::real const &r0, int const &exp_num, int const &exp_den, cvm::atom &A1, cvm::atom &A2); - + template<bool b_gradients> /// \brief Calculate a coordination number through the function /// (1-x**n)/(1-x**m), x = |(A1-A2)*(r0_vec)^-|1 \param r0_vec /// vector of different cutoffs in the three directions \param /// exp_num \i n exponent \param exp_den \i m exponent \param A1 /// atom \param A2 atom static cvm::real switching_function (cvm::rvector const &r0_vec, int const &exp_num, int const &exp_den, cvm::atom &A1, cvm::atom &A2); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: self-coordination number within a group /// (colvarvalue::type_scalar type, range [0:N*(N-1)/2]) class colvar::selfcoordnum : public colvar::distance { protected: /// \brief "Cutoff" for isotropic calculation (default) cvm::real r0; /// Integer exponent of the function numerator int en; /// Integer exponent of the function denominator int ed; public: /// Constructor selfcoordnum (std::string const &conf); selfcoordnum(); virtual inline ~selfcoordnum() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); template<bool b_gradients> /// \brief Calculate a coordination number through the function /// (1-x**n)/(1-x**m), x = |A1-A2|/r0 \param r0 "cutoff" for the /// coordination number \param exp_num \i n exponent \param exp_den /// \i m exponent \param A1 atom \param A2 atom static cvm::real switching_function (cvm::real const &r0, int const &exp_num, int const &exp_den, cvm::atom &A1, cvm::atom &A2); - + virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: hydrogen bond, defined as the product of /// a colvar::coordnum and 1/2*(1-cos((180-ang)/ang_tol)) /// (colvarvalue::type_scalar type, range [0:1]) -class colvar::h_bond +class colvar::h_bond : public colvar::cvc { protected: /// Atoms involved in the component cvm::atom acceptor, donor; /// \brief "Cutoff" distance between acceptor and donor cvm::real r0; /// Integer exponent of the function numerator int en; /// Integer exponent of the function denominator int ed; public: h_bond (std::string const &conf); /// Constructor for atoms already allocated h_bond (cvm::atom const &acceptor, cvm::atom const &donor, cvm::real r0, int en, int ed); h_bond(); virtual ~h_bond(); virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: alpha helix content of a contiguous /// segment of 5 or more residues, implemented as a sum of phi/psi /// dihedral angles and hydrogen bonds (colvarvalue::type_scalar type, /// range [0:1]) // class colvar::alpha_dihedrals // : public colvar::cvc // { // protected: // /// Alpha-helical reference phi value // cvm::real phi_ref; // /// Alpha-helical reference psi value // cvm::real psi_ref; // /// List of phi dihedral angles // std::vector<dihedral *> phi; // /// List of psi dihedral angles // std::vector<dihedral *> psi; // /// List of hydrogen bonds // std::vector<h_bond *> hb; // public: // alpha_dihedrals (std::string const &conf); // alpha_dihedrals(); // virtual inline ~alpha_dihedrals() {} // virtual void calc_value(); -// virtual void calc_gradients(); +// virtual void calc_gradients(); // virtual void apply_force (colvarvalue const &force); // virtual cvm::real dist2 (colvarvalue const &x1, // colvarvalue const &x2) const; // virtual colvarvalue dist2_lgrad (colvarvalue const &x1, // colvarvalue const &x2) const; // virtual colvarvalue dist2_rgrad (colvarvalue const &x1, // colvarvalue const &x2) const; // virtual cvm::real compare (colvarvalue const &x1, // colvarvalue const &x2) const; // }; /// \brief Colvar component: alpha helix content of a contiguous /// segment of 5 or more residues, implemented as a sum of Ca-Ca-Ca /// angles and hydrogen bonds (colvarvalue::type_scalar type, range /// [0:1]) class colvar::alpha_angles : public colvar::cvc { protected: /// Reference Calpha-Calpha angle (default: 88 degrees) cvm::real theta_ref; /// Tolerance on the Calpha-Calpha angle cvm::real theta_tol; /// List of Calpha-Calpha angles std::vector<angle *> theta; /// List of hydrogen bonds std::vector<h_bond *> hb; - /// Contribution of the hb terms + /// Contribution of the hb terms cvm::real hb_coeff; public: alpha_angles (std::string const &conf); alpha_angles(); virtual ~alpha_angles(); void calc_value(); void calc_gradients(); void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: dihedPC /// Projection of the config onto a dihedral principal component /// See e.g. Altis et al., J. Chem. Phys 126, 244111 (2007) /// Based on a set of 'dihedral' cvcs class colvar::dihedPC : public colvar::cvc { protected: std::vector<dihedral *> theta; std::vector<cvm::real> coeffs; public: dihedPC (std::string const &conf); dihedPC(); virtual ~dihedPC(); void calc_value(); void calc_gradients(); void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: orientation in space of an atom group, /// with respect to a set of reference coordinates /// (colvarvalue::type_quaternion type, range /// [-1:1]x[-1:1]x[-1:1]x[-1:1]) class colvar::orientation : public colvar::cvc { protected: /// Atom group cvm::atom_group atoms; /// Center of geometry of the group cvm::atom_pos atoms_cog; /// Reference coordinates std::vector<cvm::atom_pos> ref_pos; /// Rotation object cvm::rotation rot; /// \brief This is used to remove jumps in the sign of the /// quaternion, which may be annoying in the colvars trajectory cvm::quaternion ref_quat; public: orientation (std::string const &conf); orientation(); virtual inline ~orientation() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: angle of rotation with respect to a set /// of reference coordinates (colvarvalue::type_scalar type, range /// [0:PI)) class colvar::orientation_angle : public colvar::orientation { public: orientation_angle (std::string const &conf); orientation_angle(); virtual inline ~orientation_angle() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: projection of the orientation vector onto /// a predefined axis (colvarvalue::type_scalar type, range [-1:1]) class colvar::tilt : public colvar::orientation { protected: cvm::rvector axis; public: tilt (std::string const &conf); tilt(); virtual inline ~tilt() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; /// \brief Colvar component: angle of rotation around a predefined /// axis (colvarvalue::type_scalar type, range [-PI:PI]) class colvar::spin_angle : public colvar::orientation { protected: cvm::rvector axis; public: spin_angle (std::string const &conf); spin_angle(); virtual inline ~spin_angle() {} virtual void calc_value(); virtual void calc_gradients(); virtual void apply_force (colvarvalue const &force); /// Redefined to handle the 2*PI periodicity virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; /// Redefined to handle the 2*PI periodicity virtual void wrap (colvarvalue &x) const; }; /// \brief Colvar component: root mean square deviation (RMSD) of a /// group with respect to a set of reference coordinates; uses \link /// colvar::orientation \endlink to calculate the rotation matrix /// (colvarvalue::type_scalar type, range [0:*)) class colvar::rmsd : public colvar::cvc { protected: /// Atom group cvm::atom_group atoms; /// Reference coordinates (for RMSD calculation only) std::vector<cvm::atom_pos> ref_pos; public: /// Constructor rmsd (std::string const &conf); virtual inline ~rmsd() {} virtual void calc_value(); virtual void calc_gradients(); virtual void calc_force_invgrads(); virtual void calc_Jacobian_derivative(); virtual void apply_force (colvarvalue const &force); virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const; virtual cvm::real compare (colvarvalue const &x1, colvarvalue const &x2) const; }; -// metrics functions for cvc implementations +// metrics functions for cvc implementations // simple definitions of the distance functions; these are useful only // for optimization (the type check performed in the default // colvarcomp functions is skipped) // definitions assuming the scalar type #define simple_scalar_dist_functions(TYPE) \ \ inline cvm::real colvar::TYPE::dist2 (colvarvalue const &x1, \ colvarvalue const &x2) const \ { \ return (x1.real_value - x2.real_value)*(x1.real_value - x2.real_value); \ } \ \ inline colvarvalue colvar::TYPE::dist2_lgrad (colvarvalue const &x1, \ colvarvalue const &x2) const \ { \ return 2.0 * (x1.real_value - x2.real_value); \ } \ \ inline colvarvalue colvar::TYPE::dist2_rgrad (colvarvalue const &x1, \ colvarvalue const &x2) const \ { \ return this->dist2_lgrad (x2, x1); \ } \ \ inline cvm::real colvar::TYPE::compare (colvarvalue const &x1, \ colvarvalue const &x2) const \ { \ return this->dist2_lgrad (x1, x2); \ } \ \ simple_scalar_dist_functions (distance) - // NOTE: distance_z has explicit functions, see below + // NOTE: distance_z has explicit functions, see below simple_scalar_dist_functions (distance_xy) simple_scalar_dist_functions (distance_inv) simple_scalar_dist_functions (angle) simple_scalar_dist_functions (coordnum) simple_scalar_dist_functions (selfcoordnum) simple_scalar_dist_functions (h_bond) simple_scalar_dist_functions (gyration) simple_scalar_dist_functions (inertia) simple_scalar_dist_functions (inertia_z) simple_scalar_dist_functions (rmsd) simple_scalar_dist_functions (orientation_angle) simple_scalar_dist_functions (tilt) simple_scalar_dist_functions (eigenvector) // simple_scalar_dist_functions (alpha_dihedrals) simple_scalar_dist_functions (alpha_angles) simple_scalar_dist_functions (dihedPC) // metrics functions for cvc implementations with a periodicity inline cvm::real colvar::dihedral::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; diff = (diff < -180.0 ? diff + 360.0 : (diff > 180.0 ? diff - 360.0 : diff)); return diff * diff; } inline colvarvalue colvar::dihedral::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; diff = (diff < -180.0 ? diff + 360.0 : (diff > 180.0 ? diff - 360.0 : diff)); return 2.0 * diff; } inline colvarvalue colvar::dihedral::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; diff = (diff < -180.0 ? diff + 360.0 : (diff > 180.0 ? diff - 360.0 : diff)); return (-2.0) * diff; } inline cvm::real colvar::dihedral::compare (colvarvalue const &x1, colvarvalue const &x2) const { return dist2_lgrad (x1, x2); } inline void colvar::dihedral::wrap (colvarvalue &x) const { if ((x.real_value - wrap_center) >= 180.0) { x.real_value -= 360.0; return; - } + } if ((x.real_value - wrap_center) < -180.0) { x.real_value += 360.0; return; - } + } return; } inline cvm::real colvar::spin_angle::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; diff = (diff < -180.0 ? diff + 360.0 : (diff > 180.0 ? diff - 360.0 : diff)); return diff * diff; } inline colvarvalue colvar::spin_angle::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; diff = (diff < -180.0 ? diff + 360.0 : (diff > 180.0 ? diff - 360.0 : diff)); return 2.0 * diff; } inline colvarvalue colvar::spin_angle::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; diff = (diff < -180.0 ? diff + 360.0 : (diff > 180.0 ? diff - 360.0 : diff)); return (-2.0) * diff; } inline cvm::real colvar::spin_angle::compare (colvarvalue const &x1, colvarvalue const &x2) const { return dist2_lgrad (x1, x2); } inline void colvar::spin_angle::wrap (colvarvalue &x) const { if ((x.real_value - wrap_center) >= 180.0) { x.real_value -= 360.0; return; - } + } if ((x.real_value - wrap_center) < -180.0) { x.real_value += 360.0; return; - } + } return; } // Projected distance // Differences should always be wrapped around 0 (ignoring wrap_center) inline cvm::real colvar::distance_z::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; if (period != 0.0) { cvm::real shift = std::floor (diff/period + 0.5); diff -= shift * period; } return diff * diff; } inline colvarvalue colvar::distance_z::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; if (period != 0.0) { cvm::real shift = std::floor (diff/period + 0.5); diff -= shift * period; } return 2.0 * diff; } inline colvarvalue colvar::distance_z::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { cvm::real diff = x1.real_value - x2.real_value; if (period != 0.0) { cvm::real shift = std::floor (diff/period + 0.5); diff -= shift * period; } return (-2.0) * diff; } inline cvm::real colvar::distance_z::compare (colvarvalue const &x1, colvarvalue const &x2) const { return dist2_lgrad (x1, x2); } inline void colvar::distance_z::wrap (colvarvalue &x) const { if (! this->b_periodic) { // don't wrap if the period has not been set return; } cvm::real shift = std::floor ((x.real_value - wrap_center) / period + 0.5); x.real_value -= shift * period; return; } // distance between three dimensional vectors // // TODO apply PBC to distance_vec // Note: differences should be centered around (0, 0, 0)! inline cvm::real colvar::distance_vec::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { return cvm::position_dist2 (x1.rvector_value, x2.rvector_value); } inline colvarvalue colvar::distance_vec::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { return 2.0 * cvm::position_distance(x2.rvector_value, x1.rvector_value); } inline colvarvalue colvar::distance_vec::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { return 2.0 * cvm::position_distance(x2.rvector_value, x1.rvector_value); } inline cvm::real colvar::distance_vec::compare (colvarvalue const &x1, colvarvalue const &x2) const { cvm::fatal_error ("Error: cannot compare() two distance vectors.\n"); return 0.0; } inline cvm::real colvar::distance_dir::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { return (x1.rvector_value - x2.rvector_value).norm2(); } inline colvarvalue colvar::distance_dir::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { return colvarvalue ((x1.rvector_value - x2.rvector_value), colvarvalue::type_unitvector); } inline colvarvalue colvar::distance_dir::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { return colvarvalue ((x2.rvector_value - x1.rvector_value), colvarvalue::type_unitvector); } inline cvm::real colvar::distance_dir::compare (colvarvalue const &x1, colvarvalue const &x2) const { cvm::fatal_error ("Error: cannot compare() two distance directions.\n"); return 0.0; } // distance between quaternions inline cvm::real colvar::orientation::dist2 (colvarvalue const &x1, colvarvalue const &x2) const { return x1.quaternion_value.dist2 (x2); } inline colvarvalue colvar::orientation::dist2_lgrad (colvarvalue const &x1, colvarvalue const &x2) const { return x1.quaternion_value.dist2_grad (x2); } inline colvarvalue colvar::orientation::dist2_rgrad (colvarvalue const &x1, colvarvalue const &x2) const { return x2.quaternion_value.dist2_grad (x1); } inline cvm::real colvar::orientation::compare (colvarvalue const &x1, colvarvalue const &x2) const { cvm::fatal_error ("Error: cannot compare() two quaternions.\n"); return 0.0; } #endif // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvarcomp_angles.cpp b/lib/colvars/colvarcomp_angles.cpp index 414c01df7..bc2aee059 100644 --- a/lib/colvars/colvarcomp_angles.cpp +++ b/lib/colvars/colvarcomp_angles.cpp @@ -1,377 +1,377 @@ #include "colvarmodule.h" #include "colvar.h" #include "colvarcomp.h" #include <cmath> colvar::angle::angle (std::string const &conf) : cvc (conf) { function_type = "angle"; b_inverse_gradients = true; b_Jacobian_derivative = true; parse_group (conf, "group1", group1); parse_group (conf, "group2", group2); parse_group (conf, "group3", group3); atom_groups.push_back (&group1); atom_groups.push_back (&group2); atom_groups.push_back (&group3); if (get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) { cvm::log ("Computing system force on group 1 only"); } x.type (colvarvalue::type_scalar); } colvar::angle::angle (cvm::atom const &a1, cvm::atom const &a2, cvm::atom const &a3) : group1 (std::vector<cvm::atom> (1, a1)), group2 (std::vector<cvm::atom> (1, a2)), group3 (std::vector<cvm::atom> (1, a3)) { function_type = "angle"; b_inverse_gradients = true; b_Jacobian_derivative = true; b_1site_force = false; atom_groups.push_back (&group1); atom_groups.push_back (&group2); atom_groups.push_back (&group3); x.type (colvarvalue::type_scalar); } colvar::angle::angle() { function_type = "angle"; x.type (colvarvalue::type_scalar); } void colvar::angle::calc_value() { group1.read_positions(); group2.read_positions(); group3.read_positions(); cvm::atom_pos const g1_pos = group1.center_of_mass(); cvm::atom_pos const g2_pos = group2.center_of_mass(); cvm::atom_pos const g3_pos = group3.center_of_mass(); r21 = cvm::position_distance (g2_pos, g1_pos); r21l = r21.norm(); r23 = cvm::position_distance (g2_pos, g3_pos); r23l = r23.norm(); cvm::real const cos_theta = (r21*r23)/(r21l*r23l); x.real_value = (180.0/PI) * std::acos (cos_theta); } void colvar::angle::calc_gradients() { cvm::real const cos_theta = (r21*r23)/(r21l*r23l); cvm::real const dxdcos = -1.0 / std::sqrt (1.0 - cos_theta*cos_theta); - + dxdr1 = (180.0/PI) * dxdcos * (1.0/r21l) * ( r23/r23l + (-1.0) * cos_theta * r21/r21l ); dxdr3 = (180.0/PI) * dxdcos * (1.0/r23l) * ( r21/r21l + (-1.0) * cos_theta * r23/r23l ); for (size_t i = 0; i < group1.size(); i++) { group1[i].grad = (group1[i].mass/group1.total_mass) * (dxdr1); } for (size_t i = 0; i < group2.size(); i++) { group2[i].grad = (group2[i].mass/group2.total_mass) * (dxdr1 + dxdr3) * (-1.0); } for (size_t i = 0; i < group3.size(); i++) { group3[i].grad = (group3[i].mass/group3.total_mass) * (dxdr3); } } void colvar::angle::calc_force_invgrads() { // This uses a force measurement on groups 1 and 3 only // to keep in line with the implicit variable change used to // evaluate the Jacobian term (essentially polar coordinates // centered on group2, which means group2 is kept fixed // when propagating changes in the angle) if (b_1site_force) { group1.read_system_forces(); cvm::real norm_fact = 1.0 / dxdr1.norm2(); ft.real_value = norm_fact * dxdr1 * group1.system_force(); } else { group1.read_system_forces(); group3.read_system_forces(); cvm::real norm_fact = 1.0 / (dxdr1.norm2() + dxdr3.norm2()); ft.real_value = norm_fact * ( dxdr1 * group1.system_force() + dxdr3 * group3.system_force()); } return; } void colvar::angle::calc_Jacobian_derivative() { // det(J) = (2 pi) r^2 * sin(theta) // hence Jd = cot(theta) const cvm::real theta = x.real_value * PI / 180.0; jd = PI / 180.0 * (theta != 0.0 ? std::cos(theta) / std::sin(theta) : 0.0); } void colvar::angle::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); if (!group3.noforce) group3.apply_colvar_force (force.real_value); } colvar::dihedral::dihedral (std::string const &conf) : cvc (conf) { function_type = "dihedral"; period = 360.0; b_periodic = true; b_inverse_gradients = true; b_Jacobian_derivative = true; if (get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) { cvm::log ("Computing system force on group 1 only"); } parse_group (conf, "group1", group1); parse_group (conf, "group2", group2); parse_group (conf, "group3", group3); parse_group (conf, "group4", group4); atom_groups.push_back (&group1); atom_groups.push_back (&group2); atom_groups.push_back (&group3); atom_groups.push_back (&group4); x.type (colvarvalue::type_scalar); } colvar::dihedral::dihedral (cvm::atom const &a1, cvm::atom const &a2, cvm::atom const &a3, cvm::atom const &a4) : group1 (std::vector<cvm::atom> (1, a1)), group2 (std::vector<cvm::atom> (1, a2)), group3 (std::vector<cvm::atom> (1, a3)), group4 (std::vector<cvm::atom> (1, a4)) { if (cvm::debug()) cvm::log ("Initializing dihedral object from atom groups.\n"); function_type = "dihedral"; period = 360.0; b_periodic = true; b_inverse_gradients = true; b_Jacobian_derivative = true; b_1site_force = false; atom_groups.push_back (&group1); atom_groups.push_back (&group2); atom_groups.push_back (&group3); atom_groups.push_back (&group4); x.type (colvarvalue::type_scalar); if (cvm::debug()) cvm::log ("Done initializing dihedral object from atom groups.\n"); } colvar::dihedral::dihedral() { function_type = "dihedral"; period = 360.0; b_periodic = true; b_inverse_gradients = true; b_Jacobian_derivative = true; x.type (colvarvalue::type_scalar); } void colvar::dihedral::calc_value() { group1.read_positions(); group2.read_positions(); group3.read_positions(); group4.read_positions(); cvm::atom_pos const g1_pos = group1.center_of_mass(); cvm::atom_pos const g2_pos = group2.center_of_mass(); cvm::atom_pos const g3_pos = group3.center_of_mass(); cvm::atom_pos const g4_pos = group4.center_of_mass(); // Usual sign convention: r12 = r2 - r1 r12 = cvm::position_distance (g1_pos, g2_pos); r23 = cvm::position_distance (g2_pos, g3_pos); r34 = cvm::position_distance (g3_pos, g4_pos); cvm::rvector const n1 = cvm::rvector::outer (r12, r23); cvm::rvector const n2 = cvm::rvector::outer (r23, r34); cvm::real const cos_phi = n1 * n2; cvm::real const sin_phi = n1 * r34 * r23.norm(); x.real_value = (180.0/PI) * std::atan2 (sin_phi, cos_phi); this->wrap (x); } void colvar::dihedral::calc_gradients() { cvm::rvector A = cvm::rvector::outer (r12, r23); cvm::real rA = A.norm(); cvm::rvector B = cvm::rvector::outer (r23, r34); cvm::real rB = B.norm(); - cvm::rvector C = cvm::rvector::outer (r23, A); + cvm::rvector C = cvm::rvector::outer (r23, A); cvm::real rC = C.norm(); cvm::real const cos_phi = (A*B)/(rA*rB); cvm::real const sin_phi = (C*B)/(rC*rB); cvm::rvector f1, f2, f3; rB = 1.0/rB; B *= rB; if (std::fabs (sin_phi) > 0.1) { rA = 1.0/rA; A *= rA; cvm::rvector const dcosdA = rA*(cos_phi*A-B); cvm::rvector const dcosdB = rB*(cos_phi*B-A); rA = 1.0; cvm::real const K = (1.0/sin_phi) * (180.0/PI); f1 = K * cvm::rvector::outer (r23, dcosdA); f3 = K * cvm::rvector::outer (dcosdB, r23); f2 = K * (cvm::rvector::outer (dcosdA, r12) + cvm::rvector::outer (r34, dcosdB)); } else { rC = 1.0/rC; C *= rC; cvm::rvector const dsindC = rC*(sin_phi*C-B); cvm::rvector const dsindB = rB*(sin_phi*B-C); rC = 1.0; cvm::real const K = (-1.0/cos_phi) * (180.0/PI); f1.x = K*((r23.y*r23.y + r23.z*r23.z)*dsindC.x - r23.x*r23.y*dsindC.y - r23.x*r23.z*dsindC.z); f1.y = K*((r23.z*r23.z + r23.x*r23.x)*dsindC.y - r23.y*r23.z*dsindC.z - r23.y*r23.x*dsindC.x); f1.z = K*((r23.x*r23.x + r23.y*r23.y)*dsindC.z - r23.z*r23.x*dsindC.x - r23.z*r23.y*dsindC.y); f3 = cvm::rvector::outer (dsindB, r23); f3 *= K; f2.x = K*(-(r23.y*r12.y + r23.z*r12.z)*dsindC.x +(2.0*r23.x*r12.y - r12.x*r23.y)*dsindC.y +(2.0*r23.x*r12.z - r12.x*r23.z)*dsindC.z +dsindB.z*r34.y - dsindB.y*r34.z); f2.y = K*(-(r23.z*r12.z + r23.x*r12.x)*dsindC.y +(2.0*r23.y*r12.z - r12.y*r23.z)*dsindC.z +(2.0*r23.y*r12.x - r12.y*r23.x)*dsindC.x +dsindB.x*r34.z - dsindB.z*r34.x); f2.z = K*(-(r23.x*r12.x + r23.y*r12.y)*dsindC.z +(2.0*r23.z*r12.x - r12.z*r23.x)*dsindC.x +(2.0*r23.z*r12.y - r12.z*r23.y)*dsindC.y +dsindB.y*r34.x - dsindB.x*r34.y); } for (size_t i = 0; i < group1.size(); i++) group1[i].grad = (group1[i].mass/group1.total_mass) * (-f1); for (size_t i = 0; i < group2.size(); i++) group2[i].grad = (group2[i].mass/group2.total_mass) * (-f2 + f1); for (size_t i = 0; i < group3.size(); i++) group3[i].grad = (group3[i].mass/group3.total_mass) * (-f3 + f2); for (size_t i = 0; i < group4.size(); i++) group4[i].grad = (group4[i].mass/group4.total_mass) * (f3); } void colvar::dihedral::calc_force_invgrads() { cvm::rvector const u12 = r12.unit(); cvm::rvector const u23 = r23.unit(); cvm::rvector const u34 = r34.unit(); cvm::real const d12 = r12.norm(); cvm::real const d34 = r34.norm(); cvm::rvector const cross1 = (cvm::rvector::outer (u23, u12)).unit(); cvm::rvector const cross4 = (cvm::rvector::outer (u23, u34)).unit(); cvm::real const dot1 = u23 * u12; cvm::real const dot4 = u23 * u34; cvm::real const fact1 = d12 * std::sqrt (1.0 - dot1 * dot1); cvm::real const fact4 = d34 * std::sqrt (1.0 - dot4 * dot4); group1.read_system_forces(); if ( b_1site_force ) { // This is only measuring the force on group 1 ft.real_value = PI/180.0 * fact1 * (cross1 * group1.system_force()); } else { // Default case: use groups 1 and 4 group4.read_system_forces(); ft.real_value = PI/180.0 * 0.5 * (fact1 * (cross1 * group1.system_force()) + fact4 * (cross4 * group4.system_force())); } } void colvar::dihedral::calc_Jacobian_derivative() { // With this choice of inverse gradient ("internal coordinates"), Jacobian correction is 0 jd = 0.0; } void colvar::dihedral::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); if (!group3.noforce) group3.apply_colvar_force (force.real_value); if (!group4.noforce) group4.apply_colvar_force (force.real_value); } diff --git a/lib/colvars/colvarcomp_coordnums.cpp b/lib/colvars/colvarcomp_coordnums.cpp index 6afb118c0..d08a5e505 100644 --- a/lib/colvars/colvarcomp_coordnums.cpp +++ b/lib/colvars/colvarcomp_coordnums.cpp @@ -1,359 +1,359 @@ #include <cmath> #include "colvarmodule.h" #include "colvarparse.h" #include "colvaratoms.h" #include "colvarvalue.h" #include "colvar.h" #include "colvarcomp.h" template<bool calculate_gradients> cvm::real colvar::coordnum::switching_function (cvm::real const &r0, int const &en, int const &ed, cvm::atom &A1, cvm::atom &A2) { cvm::rvector const diff = cvm::position_distance (A1.pos, A2.pos); cvm::real const l2 = diff.norm2()/(r0*r0); // Assume en and ed are even integers, and avoid sqrt in the following int const en2 = en/2; int const ed2 = ed/2; cvm::real const xn = std::pow (l2, en2); cvm::real const xd = std::pow (l2, ed2); cvm::real const func = (1.0-xn)/(1.0-xd); if (calculate_gradients) { cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) - func*ed2*(xd/l2))*(-1.0); cvm::rvector const dl2dx = (2.0/(r0*r0))*diff; A1.grad += (-1.0)*dFdl2*dl2dx; A2.grad += dFdl2*dl2dx; } return func; } template<bool calculate_gradients> cvm::real colvar::coordnum::switching_function (cvm::rvector const &r0_vec, int const &en, int const &ed, cvm::atom &A1, cvm::atom &A2) { cvm::rvector const diff = cvm::position_distance (A1.pos, A2.pos); cvm::rvector const scal_diff (diff.x/r0_vec.x, diff.y/r0_vec.y, diff.z/r0_vec.z); cvm::real const l2 = scal_diff.norm2(); // Assume en and ed are even integers, and avoid sqrt in the following int const en2 = en/2; int const ed2 = ed/2; cvm::real const xn = std::pow (l2, en2); cvm::real const xd = std::pow (l2, ed2); cvm::real const func = (1.0-xn)/(1.0-xd); if (calculate_gradients) { cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) - func*ed2*(xd/l2))*(-1.0); cvm::rvector const dl2dx ((2.0/(r0_vec.x*r0_vec.x))*diff.x, (2.0/(r0_vec.y*r0_vec.y))*diff.y, (2.0/(r0_vec.z*r0_vec.z))*diff.z); A1.grad += (-1.0)*dFdl2*dl2dx; A2.grad += dFdl2*dl2dx; } return func; } colvar::coordnum::coordnum (std::string const &conf) : distance (conf), b_anisotropic (false), b_group2_center_only (false) -{ +{ function_type = "coordnum"; x.type (colvarvalue::type_scalar); // group1 and group2 are already initialized by distance() if (group1.b_dummy) cvm::fatal_error ("Error: only group2 is allowed to be a dummy atom\n"); - + // need to specify this explicitly because the distance() constructor // has set it to true b_inverse_gradients = false; bool const b_scale = get_keyval (conf, "cutoff", r0, cvm::real (4.0 * cvm::unit_angstrom())); if (get_keyval (conf, "cutoff3", r0_vec, cvm::rvector (4.0, 4.0, 4.0), parse_silent)) { if (b_scale) cvm::fatal_error ("Error: cannot specify \"scale\" and " "\"scale3\" at the same time.\n"); b_anisotropic = true; // remove meaningless negative signs if (r0_vec.x < 0.0) r0_vec.x *= -1.0; if (r0_vec.y < 0.0) r0_vec.y *= -1.0; if (r0_vec.z < 0.0) r0_vec.z *= -1.0; } get_keyval (conf, "expNumer", en, int (6) ); get_keyval (conf, "expDenom", ed, int (12)); if ( (en%2) || (ed%2) ) { cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n"); } get_keyval (conf, "group2CenterOnly", b_group2_center_only, group2.b_dummy); } colvar::coordnum::coordnum() : b_anisotropic (false), b_group2_center_only (false) { function_type = "coordnum"; x.type (colvarvalue::type_scalar); } void colvar::coordnum::calc_value() { x.real_value = 0.0; if (b_group2_center_only) { // create a fake atom to hold the group2 com coordinates cvm::atom group2_com_atom; group2_com_atom.pos = group2.center_of_mass(); if (b_anisotropic) { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) x.real_value += switching_function<false> (r0_vec, en, ed, *ai1, group2_com_atom); } else { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) x.real_value += switching_function<false> (r0, en, ed, *ai1, group2_com_atom); } } else { if (b_anisotropic) { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { x.real_value += switching_function<false> (r0_vec, en, ed, *ai1, *ai2); } } else { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { x.real_value += switching_function<false> (r0, en, ed, *ai1, *ai2); } } } } void colvar::coordnum::calc_gradients() { if (b_group2_center_only) { // create a fake atom to hold the group2 com coordinates cvm::atom group2_com_atom; group2_com_atom.pos = group2.center_of_mass(); if (b_anisotropic) { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) switching_function<true> (r0_vec, en, ed, *ai1, group2_com_atom); } else { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) switching_function<true> (r0, en, ed, *ai1, group2_com_atom); } group2.set_weighted_gradient (group2_com_atom.grad); } else { if (b_anisotropic) { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { switching_function<true> (r0_vec, en, ed, *ai1, *ai2); } } else { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) { switching_function<true> (r0, en, ed, *ai1, *ai2); } } } // if (cvm::debug()) { // for (size_t i = 0; i < group1.size(); i++) { // cvm::log ("atom["+cvm::to_str (group1[i].id+1)+"] gradient: "+ // cvm::to_str (group1[i].grad)+"\n"); // } // for (size_t i = 0; i < group2.size(); i++) { // cvm::log ("atom["+cvm::to_str (group2[i].id+1)+"] gradient: "+ // cvm::to_str (group2[i].grad)+"\n"); // } // } } void colvar::coordnum::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); } // h_bond member functions colvar::h_bond::h_bond (std::string const &conf) : cvc (conf) { if (cvm::debug()) cvm::log ("Initializing h_bond object.\n"); function_type = "h_bond"; x.type (colvarvalue::type_scalar); int a_num, d_num; get_keyval (conf, "acceptor", a_num, -1); get_keyval (conf, "donor", d_num, -1); if ( (a_num == -1) || (d_num == -1) ) { cvm::fatal_error ("Error: either acceptor or donor undefined.\n"); } acceptor = cvm::atom (a_num); donor = cvm::atom (d_num); atom_groups.push_back (new cvm::atom_group); atom_groups[0]->add_atom (acceptor); atom_groups[0]->add_atom (donor); get_keyval (conf, "cutoff", r0, (3.3 * cvm::unit_angstrom())); get_keyval (conf, "expNumer", en, 6); get_keyval (conf, "expDenom", ed, 8); if ( (en%2) || (ed%2) ) { cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n"); } if (cvm::debug()) cvm::log ("Done initializing h_bond object.\n"); } colvar::h_bond::h_bond (cvm::atom const &acceptor_i, cvm::atom const &donor_i, cvm::real r0_i, int en_i, int ed_i) : acceptor (acceptor_i), donor (donor_i), r0 (r0_i), en (en_i), ed (ed_i) { function_type = "h_bond"; x.type (colvarvalue::type_scalar); atom_groups.push_back (new cvm::atom_group); atom_groups[0]->add_atom (acceptor); atom_groups[0]->add_atom (donor); } colvar::h_bond::h_bond() : cvc () { function_type = "h_bond"; x.type (colvarvalue::type_scalar); } colvar::h_bond::~h_bond() { for (int i=0; i<atom_groups.size(); i++) { delete atom_groups[i]; } } void colvar::h_bond::calc_value() { x.real_value = colvar::coordnum::switching_function<false> (r0, en, ed, acceptor, donor); } void colvar::h_bond::calc_gradients() { colvar::coordnum::switching_function<true> (r0, en, ed, acceptor, donor); (*atom_groups[0])[0].grad = acceptor.grad; (*atom_groups[0])[1].grad = donor.grad; } void colvar::h_bond::apply_force (colvarvalue const &force) { acceptor.apply_force (force.real_value * acceptor.grad); donor.apply_force (force.real_value * donor.grad); } colvar::selfcoordnum::selfcoordnum (std::string const &conf) : distance (conf, false) -{ +{ function_type = "selfcoordnum"; x.type (colvarvalue::type_scalar); // group1 is already initialized by distance() // need to specify this explicitly because the distance() constructor // has set it to true b_inverse_gradients = false; get_keyval (conf, "cutoff", r0, cvm::real (4.0 * cvm::unit_angstrom())); get_keyval (conf, "expNumer", en, int (6) ); get_keyval (conf, "expDenom", ed, int (12)); if ( (en%2) || (ed%2) ) { cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n"); } } colvar::selfcoordnum::selfcoordnum() { function_type = "selfcoordnum"; x.type (colvarvalue::type_scalar); } void colvar::selfcoordnum::calc_value() { x.real_value = 0.0; for (size_t i = 0; i < group1.size() - 1; i++) for (size_t j = i + 1; j < group1.size(); j++) x.real_value += colvar::coordnum::switching_function<false> (r0, en, ed, group1[i], group1[j]); } void colvar::selfcoordnum::calc_gradients() { for (size_t i = 0; i < group1.size() - 1; i++) for (size_t j = i + 1; j < group1.size(); j++) colvar::coordnum::switching_function<true> (r0, en, ed, group1[i], group1[j]); } void colvar::selfcoordnum::apply_force (colvarvalue const &force) { if (!group1.noforce) group1.apply_colvar_force (force.real_value); } diff --git a/lib/colvars/colvarcomp_distances.cpp b/lib/colvars/colvarcomp_distances.cpp index a371cff60..f59bab8a9 100644 --- a/lib/colvars/colvarcomp_distances.cpp +++ b/lib/colvars/colvarcomp_distances.cpp @@ -1,1151 +1,1151 @@ #include <cmath> #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" // "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() { 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() { 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; + 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() { 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 { 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() { 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) { 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() { 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() { 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); if (atoms.b_user_defined_fit) { cvm::log ("WARNING: explicit fitting parameters were provided for atom group \"atoms\"."); } else { atoms.b_center = true; atoms.ref_pos.assign (1, cvm::rvector (0.0, 0.0, 0.0)); } 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() { 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; } if (b_debug_gradients) { cvm::log ("Debugging gradients:\n"); debug_gradients (atoms); } } 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) : gyration (conf) { function_type = "inertia"; b_inverse_gradients = false; b_Jacobian_derivative = false; x.type (colvarvalue::type_scalar); } colvar::inertia::inertia() { function_type = "inertia"; x.type (colvarvalue::type_scalar); } void colvar::inertia::calc_value() { x.real_value = 0.0; for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { x.real_value += (ai->pos).norm2(); } } void colvar::inertia::calc_gradients() { for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { ai->grad = 2.0 * ai->pos; } if (b_debug_gradients) { cvm::log ("Debugging gradients:\n"); debug_gradients (atoms); } } 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() { 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 += iprod * iprod; } } void colvar::inertia_z::calc_gradients() { for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) { ai->grad = 2.0 * (ai->pos * axis) * axis; } if (b_debug_gradients) { cvm::log ("Debugging gradients:\n"); debug_gradients (atoms); } } 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) + if (atoms.b_dummy) cvm::fatal_error ("Error: \"atoms\" cannot be a dummy atom."); 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; // we need this because the reference coordinates defined inside the atom group // may be used only for fitting, and even more so if ref_pos_group is used 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) { cvm::log ("WARNING: explicit fitting parameters were provided for atom group \"atoms\"."); } else { // Default: fit everything cvm::log ("Enabling \"centerReference\" and \"rotateReference\", to minimize RMSD before calculating it as a variable: " "if this is not the desired behavior, disable them explicitly within the \"atoms\" block.\n"); atoms.b_center = true; atoms.b_rotate = true; // default case: reference positions for calculating the rmsd are also those used // for fitting atoms.ref_pos = ref_pos; atoms.center_ref_pos(); // 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 reference coordinates for Jacobian: // this is only required for ABF, but we do both groups here for better caching atoms.rot.request_group2_gradients (atoms.size()); } } - + void colvar::rmsd::calc_value() { // rotational-translational fit is handled by the atom group 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 = std::sqrt (x.real_value); } 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 - ref_pos[ia])); } if (b_debug_gradients) { cvm::log ("Debugging gradients:\n"); debug_gradients (atoms); } } 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 + // 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); + 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 &y = ref_pos[ia]; for (size_t alpha = 0; alpha < 3; alpha++) { for (size_t beta = 0; beta < 3; beta++) { divergence += grad_rot_mat[beta][alpha][alpha] * y[beta]; // Note: equation was derived for inverse rotation (see colvars paper) // so here the matrix is transposed // (eq would give divergence += grad_rot_mat[alpha][beta][alpha] * y[beta];) } } } } 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); { bool const b_inline = get_keyval (conf, "refPositions", ref_pos, ref_pos); if (b_inline) { 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)) { if (b_inline) cvm::fatal_error ("Error: refPositions and refPositionsFile cannot be specified at the same time.\n"); 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); } } // save for later the geometric center of the provided positions (may not be the origin) cvm::rvector ref_pos_center (0.0, 0.0, 0.0); for (size_t i = 0; i < atoms.size(); i++) { ref_pos_center += ref_pos[i]; } ref_pos_center *= 1.0 / atoms.size(); if (atoms.b_user_defined_fit) { cvm::log ("WARNING: explicit fitting parameters were provided for atom group \"atoms\".\n"); } else { // default: fit everything cvm::log ("Enabling \"centerReference\" and \"rotateReference\", to minimize RMSD before calculating the vector projection: " "if this is not the desired behavior, disable them explicitly within the \"atoms\" block.\n"); atoms.b_center = true; atoms.b_rotate = true; atoms.ref_pos = ref_pos; atoms.center_ref_pos(); // 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 reference coordinates for Jacobian: // this is only required for ABF, but we do both groups here for better caching atoms.rot.request_group2_gradients (atoms.size()); } { bool const b_inline = get_keyval (conf, "vector", eigenvec, eigenvec); // now load the eigenvector if (b_inline) { 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)) { if (b_inline) cvm::fatal_error ("Error: vector and vectorFile cannot be specified at the same time.\n"); std::string file_col; double file_col_value; if (get_keyval (conf, "vectorCol", file_col, std::string (""))) { // use PDB flags if column is provided bool found = get_keyval (conf, "vectorColValue", file_col_value, 0.0); if (found && !file_col_value) cvm::fatal_error ("Error: vectorColValue, " "if provided, must be non-zero.\n"); } else { // if not, use atom indices atoms.create_sorted_ids(); } 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 eig_center (0.0, 0.0, 0.0); for (size_t i = 0; i < atoms.size(); i++) { eig_center += eigenvec[i]; } eig_center *= 1.0 / atoms.size(); cvm::log ("Geometric center of the provided vector: "+cvm::to_str (eig_center)+"\n"); bool b_difference_vector = false; get_keyval (conf, "differenceVector", b_difference_vector, false); if (b_difference_vector) { if (atoms.b_center) { // both sets should be centered on the origin for fitting for (size_t i = 0; i < atoms.size(); i++) { eigenvec[i] -= eig_center; ref_pos[i] -= ref_pos_center; } } if (atoms.b_rotate) { atoms.rot.calc_optimal_rotation (eigenvec, ref_pos); for (size_t i = 0; i < atoms.size(); i++) { eigenvec[i] = atoms.rot.rotate (eigenvec[i]); } } cvm::log ("\"differenceVector\" is on: subtracting the reference positions from the provided vector: v = v - x0.\n"); for (size_t i = 0; i < atoms.size(); i++) { eigenvec[i] -= ref_pos[i]; } if (atoms.b_center) { // bring back the ref positions to where they were for (size_t i = 0; i < atoms.size(); i++) { ref_pos[i] += ref_pos_center; } } } else { cvm::log ("Centering the provided vector to zero.\n"); for (size_t i = 0; i < atoms.size(); i++) { eigenvec[i] -= eig_center; } } // cvm::log ("The first three components (v1x, v1y, v1z) of the resulting vector are: "+cvm::to_str (eigenvec[0])+".\n"); // for inverse gradients eigenvec_invnorm2 = 0.0; for (size_t i = 0; i < atoms.size(); i++) { eigenvec_invnorm2 += eigenvec[i].norm2(); } eigenvec_invnorm2 = 1.0 / eigenvec_invnorm2; if (b_difference_vector) { cvm::log ("\"differenceVector\" is on: normalizing the vector.\n"); for (size_t i = 0; i < atoms.size(); i++) { eigenvec[i] *= eigenvec_invnorm2; } } else { cvm::log ("The norm of the vector is |v| = "+cvm::to_str (eigenvec_invnorm2)+".\n"); } } - + void colvar::eigenvector::calc_value() { 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() { for (size_t ia = 0; ia < atoms.size(); ia++) { atoms[ia].grad = eigenvec[ia]; } if (b_debug_gradients) { cvm::log ("Debugging gradients:\n"); debug_gradients (atoms); } } 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 + // gradients of products of 2 quaternion components cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23; - cvm::atom_pos x_relative; + 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); + 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); + jd.real_value = sum * std::sqrt (eigenvec_invnorm2); } diff --git a/lib/colvars/colvarcomp_protein.cpp b/lib/colvars/colvarcomp_protein.cpp index b30d50e1c..1750782b7 100644 --- a/lib/colvars/colvarcomp_protein.cpp +++ b/lib/colvars/colvarcomp_protein.cpp @@ -1,385 +1,385 @@ #include <cmath> #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" ////////////////////////////////////////////////////////////////////// // alpha component ////////////////////////////////////////////////////////////////////// colvar::alpha_angles::alpha_angles (std::string const &conf) : cvc (conf) { if (cvm::debug()) cvm::log ("Initializing alpha_angles object.\n"); function_type = "alpha_angles"; x.type (colvarvalue::type_scalar); std::string segment_id; get_keyval (conf, "psfSegID", segment_id, std::string ("MAIN")); std::vector<int> residues; { std::string residues_conf = ""; key_lookup (conf, "residueRange", residues_conf); if (residues_conf.size()) { std::istringstream is (residues_conf); int initial, final; char dash; if ( (is >> initial) && (initial > 0) && (is >> dash) && (dash == '-') && (is >> final) && (final > 0) ) { for (int rnum = initial; rnum <= final; rnum++) { residues.push_back (rnum); } } } else { cvm::fatal_error ("Error: no residues defined in \"residueRange\".\n"); } } if (residues.size() < 5) { cvm::fatal_error ("Error: not enough residues defined in \"residueRange\".\n"); } std::string const &sid = segment_id; std::vector<int> const &r = residues; get_keyval (conf, "hBondCoeff", hb_coeff, 0.5); if ( (hb_coeff < 0.0) || (hb_coeff > 1.0) ) { cvm::fatal_error ("Error: hBondCoeff must be defined between 0 and 1.\n"); } get_keyval (conf, "angleRef", theta_ref, 88.0); get_keyval (conf, "angleTol", theta_tol, 15.0); if (hb_coeff < 1.0) { for (size_t i = 0; i < residues.size()-2; i++) { theta.push_back (new colvar::angle (cvm::atom (r[i ], "CA", sid), cvm::atom (r[i+1], "CA", sid), cvm::atom (r[i+2], "CA", sid))); } } else { cvm::log ("The hBondCoeff specified will disable the Calpha-Calpha-Calpha angle terms.\n"); } { cvm::real r0; size_t en, ed; get_keyval (conf, "hBondCutoff", r0, (3.3 * cvm::unit_angstrom())); get_keyval (conf, "hBondExpNumer", en, 6); get_keyval (conf, "hBondExpDenom", ed, 8); if (hb_coeff > 0.0) { for (size_t i = 0; i < residues.size()-4; i++) { hb.push_back (new colvar::h_bond (cvm::atom (r[i ], "O", sid), cvm::atom (r[i+4], "N", sid), r0, en, ed)); } } else { cvm::log ("The hBondCoeff specified will disable the hydrogen bond terms.\n"); } } if (cvm::debug()) cvm::log ("Done initializing alpha_angles object.\n"); } colvar::alpha_angles::alpha_angles() : cvc () { function_type = "alpha_angles"; x.type (colvarvalue::type_scalar); } colvar::alpha_angles::~alpha_angles() { while (theta.size() != 0) { delete theta.back(); theta.pop_back(); } while (hb.size() != 0) { delete hb.back(); hb.pop_back(); } } void colvar::alpha_angles::calc_value() { x.real_value = 0.0; if (theta.size()) { - cvm::real const theta_norm = + cvm::real const theta_norm = (1.0-hb_coeff) / cvm::real (theta.size()); for (size_t i = 0; i < theta.size(); i++) { (theta[i])->calc_value(); cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol; cvm::real const f = ( (1.0 - std::pow (t, (int) 2)) / (1.0 - std::pow (t, (int) 4)) ); x.real_value += theta_norm * f; if (cvm::debug()) cvm::log ("Calpha-Calpha angle no. "+cvm::to_str (i+1)+" in \""+ this->name+"\" has a value of "+ (cvm::to_str ((theta[i])->value().real_value))+ " degrees, f = "+cvm::to_str (f)+".\n"); } } if (hb.size()) { cvm::real const hb_norm = hb_coeff / cvm::real (hb.size()); for (size_t i = 0; i < hb.size(); i++) { (hb[i])->calc_value(); x.real_value += hb_norm * (hb[i])->value().real_value; if (cvm::debug()) cvm::log ("Hydrogen bond no. "+cvm::to_str (i+1)+" in \""+ this->name+"\" has a value of "+ (cvm::to_str ((hb[i])->value().real_value))+".\n"); } } } void colvar::alpha_angles::calc_gradients() { - for (size_t i = 0; i < theta.size(); i++) + for (size_t i = 0; i < theta.size(); i++) (theta[i])->calc_gradients(); for (size_t i = 0; i < hb.size(); i++) (hb[i])->calc_gradients(); } void colvar::alpha_angles::apply_force (colvarvalue const &force) { if (theta.size()) { - cvm::real const theta_norm = + cvm::real const theta_norm = (1.0-hb_coeff) / cvm::real (theta.size()); - + for (size_t i = 0; i < theta.size(); i++) { cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol; cvm::real const f = ( (1.0 - std::pow (t, (int) 2)) / (1.0 - std::pow (t, (int) 4)) ); cvm::real const dfdt = - 1.0/(1.0 - std::pow (t, (int) 4)) * + 1.0/(1.0 - std::pow (t, (int) 4)) * ( (-2.0 * t) + (-1.0*f)*(-4.0 * std::pow (t, (int) 3)) ); - (theta[i])->apply_force (theta_norm * + (theta[i])->apply_force (theta_norm * dfdt * (1.0/theta_tol) * force.real_value ); } } if (hb.size()) { cvm::real const hb_norm = hb_coeff / cvm::real (hb.size()); for (size_t i = 0; i < hb.size(); i++) { (hb[i])->apply_force (0.5 * hb_norm * force.real_value); } } } ////////////////////////////////////////////////////////////////////// // dihedral principal component ////////////////////////////////////////////////////////////////////// // FIXME: this will not make collect_gradients work // because gradients in individual atom groups // are those of the sub-cvcs (angle, hb), not those // of this cvc (alpha) // This is true of all cvcs with sub-cvcs, and those // that do not calculate explicit gradients - // SO: we need a flag giving the availability of + // SO: we need a flag giving the availability of // atomic gradients colvar::dihedPC::dihedPC (std::string const &conf) : cvc (conf) { if (cvm::debug()) cvm::log ("Initializing dihedral PC object.\n"); function_type = "dihedPC"; x.type (colvarvalue::type_scalar); std::string segment_id; get_keyval (conf, "psfSegID", segment_id, std::string ("MAIN")); std::vector<int> residues; { std::string residues_conf = ""; key_lookup (conf, "residueRange", residues_conf); if (residues_conf.size()) { std::istringstream is (residues_conf); int initial, final; char dash; if ( (is >> initial) && (initial > 0) && (is >> dash) && (dash == '-') && (is >> final) && (final > 0) ) { for (int rnum = initial; rnum <= final; rnum++) { residues.push_back (rnum); } } } else { cvm::fatal_error ("Error: no residues defined in \"residueRange\".\n"); } } if (residues.size() < 2) { cvm::fatal_error ("Error: dihedralPC requires at least two residues.\n"); } std::string const &sid = segment_id; std::vector<int> const &r = residues; std::string vecFileName; int vecNumber; if (get_keyval (conf, "vectorFile", vecFileName, vecFileName)) { - get_keyval (conf, "vectorNumber", vecNumber, 0); + get_keyval (conf, "vectorNumber", vecNumber, 0); if (vecNumber < 1) cvm::fatal_error ("A positive value of vectorNumber is required."); std::ifstream vecFile; vecFile.open (vecFileName.c_str()); if (!vecFile.good()) cvm::fatal_error ("Error opening dihedral PCA vector file " + vecFileName + " for reading"); // TODO: adapt to different formats by setting this flag bool eigenvectors_as_columns = true; if (eigenvectors_as_columns) { // Carma-style dPCA file std::string line; cvm::real c; while (vecFile.good()) { getline(vecFile, line); if (line.length() < 2) break; std::istringstream ls (line); for (int i=0; i<vecNumber; i++) ls >> c; coeffs.push_back(c); } } /* TODO Uncomment this when different formats are recognized else { // Eigenvectors as lines // Skip to the right line for (int i = 1; i<vecNumber; i++) vecFile.ignore(999999, '\n'); if (!vecFile.good()) cvm::fatal_error ("Error reading dihedral PCA vector file " + vecFileName); std::string line; getline(vecFile, line); std::istringstream ls (line); cvm::real c; while (ls.good()) { ls >> c; coeffs.push_back(c); } } */ vecFile.close(); } else { get_keyval (conf, "vector", coeffs, coeffs); } if ( coeffs.size() != 4 * (residues.size() - 1)) { cvm::fatal_error ("Error: wrong number of coefficients: " + cvm::to_str(coeffs.size()) + ". Expected " + cvm::to_str(4 * (residues.size() - 1)) + " (4 coeffs per residue, minus one residue).\n"); } for (size_t i = 0; i < residues.size()-1; i++) { // Psi theta.push_back (new colvar::dihedral (cvm::atom (r[i ], "N", sid), cvm::atom (r[i ], "CA", sid), cvm::atom (r[i ], "C", sid), cvm::atom (r[i+1], "N", sid))); // Phi (next res) theta.push_back (new colvar::dihedral (cvm::atom (r[i ], "C", sid), cvm::atom (r[i+1], "N", sid), cvm::atom (r[i+1], "CA", sid), cvm::atom (r[i+1], "C", sid))); } if (cvm::debug()) cvm::log ("Done initializing dihedPC object.\n"); } colvar::dihedPC::dihedPC() : cvc () { function_type = "dihedPC"; x.type (colvarvalue::type_scalar); } colvar::dihedPC::~dihedPC() { while (theta.size() != 0) { delete theta.back(); theta.pop_back(); } } void colvar::dihedPC::calc_value() { x.real_value = 0.0; for (size_t i = 0; i < theta.size(); i++) { theta[i]->calc_value(); cvm::real const t = (PI / 180.) * theta[i]->value().real_value; x.real_value += coeffs[2*i ] * std::cos(t) + coeffs[2*i+1] * std::sin(t); } } void colvar::dihedPC::calc_gradients() { for (size_t i = 0; i < theta.size(); i++) { theta[i]->calc_gradients(); } } void colvar::dihedPC::apply_force (colvarvalue const &force) { for (size_t i = 0; i < theta.size(); i++) { cvm::real const t = (PI / 180.) * theta[i]->value().real_value; cvm::real const dcosdt = - (PI / 180.) * std::sin(t); cvm::real const dsindt = (PI / 180.) * std::cos(t); theta[i]->apply_force((coeffs[2*i ] * dcosdt + coeffs[2*i+1] * dsindt) * force); } } diff --git a/lib/colvars/colvarcomp_rotations.cpp b/lib/colvars/colvarcomp_rotations.cpp index 50c9e0da2..b77c56387 100644 --- a/lib/colvars/colvarcomp_rotations.cpp +++ b/lib/colvars/colvarcomp_rotations.cpp @@ -1,311 +1,311 @@ #include <cmath> #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" colvar::orientation::orientation (std::string const &conf) : cvc (conf) { function_type = "orientation"; parse_group (conf, "atoms", atoms); atom_groups.push_back (&atoms); x.type (colvarvalue::type_quaternion); ref_pos.reserve (atoms.size()); 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); } } if (!ref_pos.size()) { cvm::fatal_error ("Error: must define a set of " "reference coordinates.\n"); } cvm::log ("Centering the reference coordinates: it is " "assumed that each atom is the closest " "periodic image to the center of geometry.\n"); cvm::rvector cog (0.0, 0.0, 0.0); for (size_t i = 0; i < ref_pos.size(); i++) { cog += ref_pos[i]; } cog /= cvm::real (ref_pos.size()); for (size_t i = 0; i < ref_pos.size(); i++) { ref_pos[i] -= cog; } get_keyval (conf, "closestToQuaternion", ref_quat, cvm::quaternion (1.0, 0.0, 0.0, 0.0)); // initialize rot member data if (!atoms.noforce) { rot.request_group2_gradients (atoms.size()); } } - + colvar::orientation::orientation() : cvc () { function_type = "orientation"; x.type (colvarvalue::type_quaternion); } void colvar::orientation::calc_value() { // atoms.reset_atoms_data(); // atoms.read_positions(); atoms_cog = atoms.center_of_geometry(); rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog)); if ((rot.q).inner (ref_quat) >= 0.0) { x.quaternion_value = rot.q; } else { x.quaternion_value = -1.0 * rot.q; } } void colvar::orientation::calc_gradients() { // gradients have already been calculated and stored within the // member object "rot"; we're not using the "grad" member of each // atom object, because it only can represent the gradient of a // scalar colvar } void colvar::orientation::apply_force (colvarvalue const &force) { cvm::quaternion const &FQ = force.quaternion_value; if (!atoms.noforce) { for (size_t ia = 0; ia < atoms.size(); ia++) { for (size_t i = 0; i < 4; i++) { atoms[ia].apply_force (FQ[i] * rot.dQ0_2[ia][i]); } } } } colvar::orientation_angle::orientation_angle (std::string const &conf) : orientation (conf) { function_type = "orientation_angle"; x.type (colvarvalue::type_scalar); } colvar::orientation_angle::orientation_angle() : orientation() { function_type = "orientation_angle"; x.type (colvarvalue::type_scalar); } void colvar::orientation_angle::calc_value() { // atoms.reset_atoms_data(); // atoms.read_positions(); atoms_cog = atoms.center_of_geometry(); rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog)); if ((rot.q).q0 >= 0.0) { x.real_value = (180.0/PI) * 2.0 * std::acos ((rot.q).q0); } else { x.real_value = (180.0/PI) * 2.0 * std::acos (-1.0 * (rot.q).q0); } } void colvar::orientation_angle::calc_gradients() { cvm::real const dxdq0 = - ( ((rot.q).q0 * (rot.q).q0 < 1.0) ? + ( ((rot.q).q0 * (rot.q).q0 < 1.0) ? ((180.0 / PI) * (-2.0) / std::sqrt (1.0 - ((rot.q).q0 * (rot.q).q0))) : 0.0 ); for (size_t ia = 0; ia < atoms.size(); ia++) { atoms[ia].grad = (dxdq0 * (rot.dQ0_2[ia])[0]); } } void colvar::orientation_angle::apply_force (colvarvalue const &force) { cvm::real const &fw = force.real_value; if (!atoms.noforce) { atoms.apply_colvar_force (fw); } } colvar::tilt::tilt (std::string const &conf) : orientation (conf) { function_type = "tilt"; get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0)); if (axis.norm2() != 1.0) { axis /= axis.norm(); cvm::log ("Normalizing rotation axis to "+cvm::to_str (axis)+".\n"); } x.type (colvarvalue::type_scalar); } colvar::tilt::tilt() : orientation() { function_type = "tilt"; x.type (colvarvalue::type_scalar); } void colvar::tilt::calc_value() { // atoms.reset_atoms_data(); // atoms.read_positions(); atoms_cog = atoms.center_of_geometry(); rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog)); x.real_value = rot.cos_theta (axis); } void colvar::tilt::calc_gradients() { cvm::quaternion const dxdq = rot.dcos_theta_dq (axis); for (size_t ia = 0; ia < atoms.size(); ia++) { atoms[ia].grad = cvm::rvector (0.0, 0.0, 0.0); for (size_t iq = 0; iq < 4; iq++) { atoms[ia].grad += (dxdq[iq] * (rot.dQ0_2[ia])[iq]); } } if (b_debug_gradients) { cvm::log ("Debugging tilt component gradients:\n"); debug_gradients (atoms); } } void colvar::tilt::apply_force (colvarvalue const &force) { cvm::real const &fw = force.real_value; if (!atoms.noforce) { atoms.apply_colvar_force (fw); } } colvar::spin_angle::spin_angle (std::string const &conf) : orientation (conf) { function_type = "spin_angle"; get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0)); if (axis.norm2() != 1.0) { axis /= axis.norm(); cvm::log ("Normalizing rotation axis to "+cvm::to_str (axis)+".\n"); } period = 360.0; b_periodic = true; x.type (colvarvalue::type_scalar); } colvar::spin_angle::spin_angle() : orientation() { function_type = "spin_angle"; period = 360.0; b_periodic = true; x.type (colvarvalue::type_scalar); } void colvar::spin_angle::calc_value() { // atoms.reset_atoms_data(); // atoms.read_positions(); atoms_cog = atoms.center_of_geometry(); rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog)); x.real_value = rot.spin_angle (axis); this->wrap (x); } void colvar::spin_angle::calc_gradients() { cvm::quaternion const dxdq = rot.dspin_angle_dq (axis); for (size_t ia = 0; ia < atoms.size(); ia++) { atoms[ia].grad = cvm::rvector (0.0, 0.0, 0.0); for (size_t iq = 0; iq < 4; iq++) { atoms[ia].grad += (dxdq[iq] * (rot.dQ0_2[ia])[iq]); } } } void colvar::spin_angle::apply_force (colvarvalue const &force) { cvm::real const &fw = force.real_value; if (!atoms.noforce) { atoms.apply_colvar_force (fw); } } diff --git a/lib/colvars/colvargrid.cpp b/lib/colvars/colvargrid.cpp index c6099bcb8..a300b9134 100644 --- a/lib/colvars/colvargrid.cpp +++ b/lib/colvars/colvargrid.cpp @@ -1,217 +1,217 @@ // -*- c++ -*- #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" #include "colvargrid.h" colvar_grid_count::colvar_grid_count() : colvar_grid<size_t>() {} colvar_grid_count::colvar_grid_count (std::vector<int> const &nx_i, size_t const &def_count) : colvar_grid<size_t> (nx_i, def_count) {} colvar_grid_count::colvar_grid_count (std::vector<colvar *> &colvars, size_t const &def_count) : colvar_grid<size_t> (colvars, def_count) {} std::istream & colvar_grid_count::read_restart (std::istream &is) { size_t const start_pos = is.tellg(); std::string key, conf; if ((is >> key) && (key == std::string ("grid_parameters"))) { is.seekg (start_pos, std::ios::beg); is >> colvarparse::read_block ("grid_parameters", conf); parse_params (conf); } else { cvm::log ("Grid parameters are missing in the restart file, using those from the configuration.\n"); is.seekg (start_pos, std::ios::beg); } read_raw (is); return is; } std::ostream & colvar_grid_count::write_restart (std::ostream &os) { write_params (os); write_raw (os); return os; } colvar_grid_scalar::colvar_grid_scalar() : colvar_grid<cvm::real>(), samples (NULL), grad (NULL) {} colvar_grid_scalar::colvar_grid_scalar (colvar_grid_scalar const &g) : colvar_grid<cvm::real> (g), samples (NULL), grad (NULL) { grad = new cvm::real[nd]; } colvar_grid_scalar::colvar_grid_scalar (std::vector<int> const &nx_i) : colvar_grid<cvm::real> (nx_i, 0.0, 1), samples (NULL) { grad = new cvm::real[nd]; } colvar_grid_scalar::colvar_grid_scalar (std::vector<colvar *> &colvars, bool margin) : colvar_grid<cvm::real> (colvars, 0.0, 1, margin), samples (NULL) { grad = new cvm::real[nd]; } colvar_grid_scalar::~colvar_grid_scalar() { if (grad) { delete [] grad; grad = NULL; } } std::istream & colvar_grid_scalar::read_restart (std::istream &is) { size_t const start_pos = is.tellg(); std::string key, conf; if ((is >> key) && (key == std::string ("grid_parameters"))) { is.seekg (start_pos, std::ios::beg); is >> colvarparse::read_block ("grid_parameters", conf); parse_params (conf); } else { cvm::log ("Grid parameters are missing in the restart file, using those from the configuration.\n"); is.seekg (start_pos, std::ios::beg); } read_raw (is); return is; } std::ostream & colvar_grid_scalar::write_restart (std::ostream &os) { write_params (os); write_raw (os); return os; } colvar_grid_gradient::colvar_grid_gradient() : colvar_grid<cvm::real>(), samples (NULL) {} colvar_grid_gradient::colvar_grid_gradient (std::vector<int> const &nx_i) : colvar_grid<cvm::real> (nx_i, 0.0, nx_i.size()), samples (NULL) {} colvar_grid_gradient::colvar_grid_gradient (std::vector<colvar *> &colvars) : colvar_grid<cvm::real> (colvars, 0.0, colvars.size()), samples (NULL) {} std::istream & colvar_grid_gradient::read_restart (std::istream &is) { size_t const start_pos = is.tellg(); std::string key, conf; if ((is >> key) && (key == std::string ("grid_parameters"))) { is.seekg (start_pos, std::ios::beg); is >> colvarparse::read_block ("grid_parameters", conf); parse_params (conf); } else { cvm::log ("Grid parameters are missing in the restart file, using those from the configuration.\n"); is.seekg (start_pos, std::ios::beg); } read_raw (is); return is; } std::ostream & colvar_grid_gradient::write_restart (std::ostream &os) { write_params (os); write_raw (os); return os; } void colvar_grid_gradient::write_1D_integral (std::ostream &os) { cvm::real bin, min, integral; std::vector<cvm::real> int_vals; - + os << "# xi A(xi)\n"; if ( cv.size() != 1 ) { cvm::fatal_error ("Cannot write integral for multi-dimensional gradient grids."); } integral = 0.0; int_vals.push_back ( 0.0 ); bin = 0.0; min = 0.0; // correction for periodic colvars, so that the PMF is periodic cvm::real corr; if ( periodic[0] ) { corr = average(); } else { corr = 0.0; } for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix), bin += 1.0 ) { - + if (samples) { size_t const samples_here = samples->value (ix); if (samples_here) integral += (value (ix) / cvm::real (samples_here) - corr) * cv[0]->width; } else { integral += (value (ix) - corr) * cv[0]->width; } if ( integral < min ) min = integral; int_vals.push_back ( integral ); } bin = 0.0; for ( int i = 0; i < nx[0]; i++, bin += 1.0 ) { os << std::setw (10) << cv[0]->lower_boundary.real_value + cv[0]->width * bin << " " << std::setw (cvm::cv_width) << std::setprecision (cvm::cv_prec) << int_vals[i] - min << "\n"; } os << std::setw (10) << cv[0]->lower_boundary.real_value + cv[0]->width * bin << " " << std::setw (cvm::cv_width) << std::setprecision (cvm::cv_prec) << int_vals[nx[0]] - min << "\n"; return; } // quaternion_grid::quaternion_grid (std::vector<colvar *> const &cv_i, // std::vector<std::string> const &grid_str) // { // cv = cv_i; // std::istringstream is (grid_str[0]); // is >> grid_size; // min.assign (3, -1.0); // max.assign (3, 1.0); // np.assign (3, grid_size); // dx.assign (3, 2.0/(cvm::real (grid_size))); // // assumes a uniform grid in the three directions; change // // get_value() if you want to use different sizes // cvm::log ("Allocating quaternion grid ("+cvm::to_str (np.size())+" dimensional)..."); // data.create (np, 0.0); // cvm::log ("done.\n"); // if (cvm::debug()) cvm::log ("Grid size = "+data.size()); // } diff --git a/lib/colvars/colvargrid.h b/lib/colvars/colvargrid.h index 80a7b70b1..ccaf04603 100644 --- a/lib/colvars/colvargrid.h +++ b/lib/colvars/colvargrid.h @@ -1,1188 +1,1188 @@ // -*- c++ -*- #ifndef COLVARGRID_H #define COLVARGRID_H #include <iostream> #include <iomanip> #include <cmath> #include "colvar.h" #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" /// \brief Grid of values of a function of several collective /// variables \param T The data type /// /// Only scalar colvars supported so far template <class T> class colvar_grid : public colvarparse { protected: /// Number of dimensions size_t nd; /// Number of points along each dimension std::vector<int> nx; /// Cumulative number of points along each dimension std::vector<int> nxc; /// \brief Multiplicity of each datum (allow the binning of /// non-scalar types) size_t mult; /// Total number of grid points size_t nt; /// Low-level array of values std::vector<T> data; /// Newly read data (used for count grids, when adding several grids read from disk) std::vector<size_t> new_data; /// Colvars collected in this grid std::vector<colvar *> cv; /// Do we request actual value (for extended-system colvars)? std::vector<bool> actual_value; /// Get the low-level index corresponding to an index inline size_t address (std::vector<int> const &ix) const { size_t addr = 0; for (size_t i = 0; i < nd; i++) { addr += ix[i]*nxc[i]; if (cvm::debug()) { if (ix[i] >= nx[i]) cvm::fatal_error ("Error: exceeding bounds in colvar_grid.\n"); } } return addr; } public: /// Lower boundaries of the colvars in this grid std::vector<colvarvalue> lower_boundaries; /// Upper boundaries of the colvars in this grid std::vector<colvarvalue> upper_boundaries; /// Whether some colvars are periodic std::vector<bool> periodic; /// Whether some colvars have hard lower boundaries std::vector<bool> hard_lower_boundaries; /// Whether some colvars have hard upper boundaries std::vector<bool> hard_upper_boundaries; /// Widths of the colvars in this grid std::vector<cvm::real> widths; /// True if this is a count grid related to another grid of data bool has_parent_data; /// Whether this grid has been filled with data or is still empty bool has_data; /// Return the number of colvars inline size_t number_of_colvars() const { return nd; } /// Return the number of points in the i-th direction, if provided, or /// the total number inline size_t number_of_points (int const icv = -1) const { if (icv < 0) { return nt; } else { return nx[icv]; } } /// Get the sizes in each direction inline std::vector<int> const & sizes() const { return nx; } /// Set the sizes in each direction inline void set_sizes (std::vector<int> const &new_sizes) { nx = new_sizes; } /// Return the multiplicity of the type used inline size_t multiplicity() const { return mult; } /// \brief Allocate data (allow initialization also after construction) void create (std::vector<int> const &nx_i, T const &t = T(), size_t const &mult_i = 1) { mult = mult_i; nd = nx_i.size(); nxc.resize (nd); nx = nx_i; nt = mult; for (int i = nd-1; i >= 0; i--) { if (nx_i[i] <= 0) cvm::fatal_error ("Error: providing an invalid number of points, "+ cvm::to_str (nx_i[i])+".\n"); nxc[i] = nt; nt *= nx[i]; } data.reserve (nt); data.assign (nt, t); } /// \brief Allocate data (allow initialization also after construction) void create() { create (this->nx, T(), this->mult); } /// \brief Reset data (in case the grid is being reused) void reset (T const &t = T()) { data.assign (nt, t); } /// Default constructor colvar_grid() : has_data (false) { save_delimiters = false; nd = nt = 0; } /// Destructor virtual ~colvar_grid() {} /// \brief "Almost copy-constructor": only copies configuration /// parameters from another grid, but doesn't reallocate stuff; /// create() must be called after that; colvar_grid (colvar_grid<T> const &g) : has_data (false), nd (g.nd), nx (g.nx), mult (g.mult), cv (g.cv), lower_boundaries (g.lower_boundaries), upper_boundaries (g.upper_boundaries), hard_lower_boundaries (g.hard_lower_boundaries), hard_upper_boundaries (g.hard_upper_boundaries), periodic (g.periodic), widths (g.widths), actual_value (g.actual_value), data() { save_delimiters = false; } /// \brief Constructor from explicit grid sizes \param nx_i Number /// of grid points along each dimension \param t Initial value for /// the function at each point (optional) \param mult_i Multiplicity /// of each value colvar_grid (std::vector<int> const &nx_i, T const &t = T(), size_t const &mult_i = 1) : has_data (false) { save_delimiters = false; this->create (nx_i, t, mult_i); } /// \brief Constructor from a vector of colvars colvar_grid (std::vector<colvar *> const &colvars, T const &t = T(), size_t const &mult_i = 1, bool margin = false) : cv (colvars), has_data (false) { save_delimiters = false; std::vector<int> nx_i; if (cvm::debug()) cvm::log ("Allocating a grid for "+cvm::to_str (colvars.size())+ " collective variables.\n"); for (size_t i = 0; i < cv.size(); i++) { if (cv[i]->type() != colvarvalue::type_scalar) { cvm::fatal_error ("Colvar grids can only be automatically " "constructed for scalar variables. " "ABF and histogram can not be used; metadynamics " "can be used with useGrids disabled.\n"); } if (cv[i]->width <= 0.0) { cvm::fatal_error ("Tried to initialize a grid on a " "variable with negative or zero width.\n"); } if (!cv[i]->tasks[colvar::task_lower_boundary] || !cv[i]->tasks[colvar::task_upper_boundary]) { cvm::fatal_error ("Tried to initialize a grid on a " "variable with undefined boundaries.\n"); } widths.push_back (cv[i]->width); hard_lower_boundaries.push_back (cv[i]->hard_lower_boundary); hard_upper_boundaries.push_back (cv[i]->hard_upper_boundary); periodic.push_back (cv[i]->periodic_boundaries()); // By default, get reported colvar value (for extended Lagrangian colvars) actual_value.push_back (false); // except if a colvar is specified twice in a row // then the first instance is the actual value if (i > 0 && cv[i-1] == cv[i]) { actual_value[i-1] = true; } if (margin) { if (periodic[i]) { // Shift the grid by half the bin width (values at edges instead of center of bins) lower_boundaries.push_back (cv[i]->lower_boundary.real_value - 0.5 * widths[i]); upper_boundaries.push_back (cv[i]->upper_boundary.real_value - 0.5 * widths[i]); } else { // Make this grid larger by one bin width lower_boundaries.push_back (cv[i]->lower_boundary.real_value - 0.5 * widths[i]); upper_boundaries.push_back (cv[i]->upper_boundary.real_value + 0.5 * widths[i]); } } else { lower_boundaries.push_back (cv[i]->lower_boundary); upper_boundaries.push_back (cv[i]->upper_boundary); } - { + { cvm::real nbins = ( upper_boundaries[i].real_value - lower_boundaries[i].real_value ) / widths[i]; int nbins_round = (int)(nbins+0.5); - + if (std::fabs (nbins - cvm::real (nbins_round)) > 1.0E-10) { cvm::log ("Warning: grid interval ("+ cvm::to_str (lower_boundaries[i], cvm::cv_width, cvm::cv_prec)+" - "+ cvm::to_str (upper_boundaries[i], cvm::cv_width, cvm::cv_prec)+ ") is not commensurate to its bin width ("+ cvm::to_str (widths[i], cvm::cv_width, cvm::cv_prec)+").\n"); upper_boundaries[i].real_value = lower_boundaries[i].real_value + (nbins_round * widths[i]); } if (cvm::debug()) cvm::log ("Number of points is "+cvm::to_str ((int) nbins_round)+ " for the colvar no. "+cvm::to_str (i+1)+".\n"); - + nx_i.push_back (nbins_round); } } create (nx_i, t, mult_i); } /// Wrap an index vector around periodic boundary conditions /// also checks validity of non-periodic indices inline void wrap (std::vector<int> & ix) const { for (size_t i = 0; i < nd; i++) { if (periodic[i]) { ix[i] = (ix[i] + nx[i]) % nx[i]; //to ensure non-negative result } else { if (ix[i] < 0 || ix[i] >= nx[i]) cvm::fatal_error ("Trying to wrap illegal index vector (non-PBC): " + cvm::to_str (ix)); } } } /// \brief Report the bin corresponding to the current value of variable i inline int current_bin_scalar(int const i) const { return value_to_bin_scalar (actual_value[i] ? cv[i]->actual_value() : cv[i]->value(), i); } /// \brief Use the lower boundary and the width to report which bin /// the provided value is in inline int value_to_bin_scalar (colvarvalue const &value, const int i) const { return (int) std::floor ( (value.real_value - lower_boundaries[i].real_value) / widths[i] ); } /// \brief Same as the standard version, but uses another grid definition inline int value_to_bin_scalar (colvarvalue const &value, colvarvalue const &new_offset, cvm::real const &new_width) const { return (int) std::floor ( (value.real_value - new_offset.real_value) / new_width ); } /// \brief Use the two boundaries and the width to report the /// central value corresponding to a bin index inline colvarvalue bin_to_value_scalar (int const &i_bin, int const i) const { return lower_boundaries[i].real_value + widths[i] * (0.5 + i_bin); } - + /// \brief Same as the standard version, but uses different parameters inline colvarvalue bin_to_value_scalar (int const &i_bin, colvarvalue const &new_offset, cvm::real const &new_width) const { return new_offset.real_value + new_width * (0.5 + i_bin); } /// Set the value at the point with index ix inline void set_value (std::vector<int> const &ix, T const &t, size_t const &imult = 0) - { + { data[this->address (ix)+imult] = t; has_data = true; } /// \brief Get the binned value indexed by ix, or the first of them /// if the multiplicity is larger than 1 inline T const & value (std::vector<int> const &ix, size_t const &imult = 0) const { return data[this->address (ix) + imult]; } /// \brief Add a constant to all elements (fast loop) inline void add_constant (T const &t) { - for (size_t i = 0; i < nt; i++) + for (size_t i = 0; i < nt; i++) data[i] += t; has_data = true; } /// \brief Multiply all elements by a scalar constant (fast loop) inline void multiply_constant (cvm::real const &a) { - for (size_t i = 0; i < nt; i++) + for (size_t i = 0; i < nt; i++) data[i] *= a; } - + /// \brief Get the bin indices corresponding to the provided values of /// the colvars inline std::vector<int> const get_colvars_index (std::vector<colvarvalue> const &values) const { std::vector<int> index = new_index(); for (size_t i = 0; i < nd; i++) { index[i] = value_to_bin_scalar (values[i], i); } return index; } /// \brief Get the bin indices corresponding to the current values /// of the colvars inline std::vector<int> const get_colvars_index() const { std::vector<int> index = new_index(); for (size_t i = 0; i < nd; i++) { index[i] = current_bin_scalar (i); } return index; } /// \brief Get the minimal distance (in number of bins) from the /// boundaries; a negative number is returned if the given point is /// off-grid inline cvm::real bin_distance_from_boundaries (std::vector<colvarvalue> const &values, bool skip_hard_boundaries = false) { cvm::real minimum = 1.0E+16; for (size_t i = 0; i < nd; i++) { if (periodic[i]) continue; cvm::real dl = std::sqrt (cv[i]->dist2 (values[i], lower_boundaries[i])) / widths[i]; cvm::real du = std::sqrt (cv[i]->dist2 (values[i], upper_boundaries[i])) / widths[i]; if (values[i].real_value < lower_boundaries[i]) dl *= -1.0; if (values[i].real_value > upper_boundaries[i]) du *= -1.0; if ( ((!skip_hard_boundaries) || (!hard_lower_boundaries[i])) && (dl < minimum)) minimum = dl; if ( ((!skip_hard_boundaries) || (!hard_upper_boundaries[i])) && (du < minimum)) minimum = du; } return minimum; } /// \brief Add data from another grid of the same type - /// + /// /// Note: this function maps other_grid inside this one regardless /// of whether it fits or not. void map_grid (colvar_grid<T> const &other_grid) { if (other_grid.multiplicity() != this->multiplicity()) cvm::fatal_error ("Error: trying to merge two grids with values of " "different multiplicity.\n"); std::vector<colvarvalue> const &gb = this->lower_boundaries; std::vector<cvm::real> const &gw = this->widths; std::vector<colvarvalue> const &ogb = other_grid.lower_boundaries; std::vector<cvm::real> const &ogw = other_grid.widths; std::vector<int> ix = this->new_index(); std::vector<int> oix = other_grid.new_index(); if (cvm::debug()) cvm::log ("Remapping grid...\n"); for ( ; this->index_ok (ix); this->incr (ix)) { for (size_t i = 0; i < nd; i++) { oix[i] = value_to_bin_scalar (bin_to_value_scalar (ix[i], gb[i], gw[i]), ogb[i], ogw[i]); } if (! other_grid.index_ok (oix)) { continue; } for (size_t im = 0; im < mult; im++) { this->set_value (ix, other_grid.value (oix, im), im); } } has_data = true; if (cvm::debug()) cvm::log ("Remapping done.\n"); } /// \brief Add data from another grid of the same type, AND /// identical definition (boundaries, widths) void add_grid (colvar_grid<T> const &other_grid, cvm::real scale_factor = 1.0) { if (other_grid.multiplicity() != this->multiplicity()) cvm::fatal_error ("Error: trying to sum togetehr two grids with values of " "different multiplicity.\n"); - if (scale_factor != 1.0) + if (scale_factor != 1.0) for (size_t i = 0; i < data.size(); i++) { data[i] += scale_factor * other_grid.data[i]; } - else + else // skip multiplication if possible for (size_t i = 0; i < data.size(); i++) { data[i] += other_grid.data[i]; } has_data = true; } /// \brief Return the value suitable for output purposes (so that it /// may be rescaled or manipulated without changing it permanently) virtual inline T value_output (std::vector<int> const &ix, size_t const &imult = 0) { return value (ix, imult); } /// \brief Get the value from a formatted output and transform it /// into the internal representation (the two may be different, /// e.g. when using colvar_grid_count) virtual inline void value_input (std::vector<int> const &ix, T const &t, size_t const &imult = 0, bool add = false) { if ( add ) data[address (ix) + imult] += t; else data[address (ix) + imult] = t; has_data = true; } // /// Get the pointer to the binned value indexed by ix // inline T const *value_p (std::vector<int> const &ix) // { // return &(data[address (ix)]); // } /// \brief Get the index corresponding to the "first" bin, to be /// used as the initial value for an index in looping inline std::vector<int> const new_index() const { return std::vector<int> (nd, 0); } /// \brief Check that the index is within range in each of the /// dimensions inline bool index_ok (std::vector<int> const &ix) const { for (size_t i = 0; i < nd; i++) { if ( (ix[i] < 0) || (ix[i] >= int (nx[i])) ) return false; } return true; } /// \brief Increment the index, in a way that will make it loop over /// the whole nd-dimensional array inline void incr (std::vector<int> &ix) const { for (int i = ix.size()-1; i >= 0; i--) { ix[i]++; if (ix[i] >= nx[i]) { if (i > 0) { ix[i] = 0; continue; } else { // this is the last iteration, a non-valid index is being // set for the outer index, which will be caught by // index_ok() ix[0] = nx[0]; return; } } else { return; } } } /// \brief Write the grid parameters (number of colvars, boundaries, width and number of points) std::ostream & write_params (std::ostream &os) { os << "grid_parameters {\n n_colvars " << nd << "\n"; - + os << " lower_boundaries "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << lower_boundaries[i]; os << "\n"; os << " upper_boundaries "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << upper_boundaries[i]; os << "\n"; os << " widths "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << widths[i]; os << "\n"; os << " sizes "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << nx[i]; os << "\n"; os << "}\n"; return os; - } + } bool parse_params (std::string const &conf) { std::vector<int> old_nx = nx; std::vector<colvarvalue> old_lb = lower_boundaries; { size_t nd_in = 0; colvarparse::get_keyval (conf, "n_colvars", nd_in, nd, colvarparse::parse_silent); if (nd_in != nd) cvm::fatal_error ("Error: trying to read data for a grid " "that contains a different number of colvars ("+ cvm::to_str (nd_in)+") than the grid defined " "in the configuration file ("+cvm::to_str (nd)+ ").\n"); } colvarparse::get_keyval (conf, "lower_boundaries", lower_boundaries, lower_boundaries, colvarparse::parse_silent); colvarparse::get_keyval (conf, "upper_boundaries", upper_boundaries, upper_boundaries, colvarparse::parse_silent); colvarparse::get_keyval (conf, "widths", widths, widths, colvarparse::parse_silent); colvarparse::get_keyval (conf, "sizes", nx, nx, colvarparse::parse_silent); bool new_params = false; for (size_t i = 0; i < nd; i++) { if ( (old_nx[i] != nx[i]) || (std::sqrt (cv[i]->dist2 (old_lb[i], lower_boundaries[i])) > 1.0E-10) ) { new_params = true; } } // reallocate the array in case the grid params have just changed if (new_params) { data.resize (0); this->create (nx, T(), mult); } return true; } /// \brief Check that the grid information inside (boundaries, /// widths, ...) is consistent with the current setting of the /// colvars void check_consistency() { for (size_t i = 0; i < nd; i++) { if ( (std::sqrt (cv[i]->dist2 (cv[i]->lower_boundary, - lower_boundaries[i])) > 1.0E-10) || + lower_boundaries[i])) > 1.0E-10) || (std::sqrt (cv[i]->dist2 (cv[i]->upper_boundary, - upper_boundaries[i])) > 1.0E-10) || + upper_boundaries[i])) > 1.0E-10) || (std::sqrt (cv[i]->dist2 (cv[i]->width, widths[i])) > 1.0E-10) ) { cvm::fatal_error ("Error: restart information for a grid is " "inconsistent with that of its colvars.\n"); } } } /// \brief Check that the grid information inside (boundaries, /// widths, ...) is consistent with the one of another grid void check_consistency (colvar_grid<T> const &other_grid) { for (size_t i = 0; i < nd; i++) { // we skip dist2(), because periodicities and the like should // matter: boundaries should be EXACTLY the same (otherwise, // map_grid() should be used) if ( (std::fabs (other_grid.lower_boundaries[i] - - lower_boundaries[i]) > 1.0E-10) || + lower_boundaries[i]) > 1.0E-10) || (std::fabs (other_grid.upper_boundaries[i] - - upper_boundaries[i]) > 1.0E-10) || + upper_boundaries[i]) > 1.0E-10) || (std::fabs (other_grid.widths[i] - - widths[i]) > 1.0E-10) || + widths[i]) > 1.0E-10) || (data.size() != other_grid.data.size()) ) { cvm::fatal_error ("Error: inconsistency between " "two grids that are supposed to be equal, " "aside from the data stored.\n"); } } } - + /// \brief Write the grid data without labels, as they are /// represented in memory /// \param buf_size Number of values per line std::ostream & write_raw (std::ostream &os, size_t const buf_size = 3) { std::streamsize const w = os.width(); std::streamsize const p = os.precision(); std::vector<int> ix = new_index(); size_t count = 0; for ( ; index_ok (ix); incr (ix)) { for (size_t imult = 0; imult < mult; imult++) { os << " " << std::setw (w) << std::setprecision (p) << value_output (ix, imult); if (((++count) % buf_size) == 0) os << "\n"; } } // write a final newline only if buffer is not empty if ((count % buf_size) != 0) os << "\n"; return os; } /// \brief Read data written by colvar_grid::write_raw() std::istream & read_raw (std::istream &is) { size_t const start_pos = is.tellg(); - + for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix)) { for (size_t imult = 0; imult < mult; imult++) { T new_value; if (is >> new_value) { value_input (ix, new_value, imult); } else { is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } } } has_data = true; return is; } /// \brief To be called after colvar_grid::read_raw() returns an error void read_raw_error() { cvm::fatal_error ("Error: failed to read all of the grid points from file. Possible explanations: grid parameters in the configuration (lowerBoundary, upperBoundary, width) are different from those in the file, or the file is corrupt/incomplete.\n"); } /// \brief Write the grid in a format which is both human readable /// and suitable for visualization e.g. with gnuplot void write_multicol (std::ostream &os) { std::streamsize const w = os.width(); std::streamsize const p = os.precision(); // Data in the header: nColvars, then for each // xiMin, dXi, nPoints, periodic os << std::setw (2) << "# " << nd << "\n"; for (size_t i = 0; i < nd; i++) { os << "# " << std::setw (10) << lower_boundaries[i] << std::setw (10) << widths[i] << std::setw (10) << nx[i] << " " << periodic[i] << "\n"; } for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix) ) { if (ix.back() == 0) { // if the last index is 0, add a new line to mark the new record os << "\n"; } for (size_t i = 0; i < nd; i++) { os << " " << std::setw (w) << std::setprecision (p) << bin_to_value_scalar (ix[i], i); } os << " "; for (size_t imult = 0; imult < mult; imult++) { os << " " << std::setw (w) << std::setprecision (p) << value_output (ix, imult); } os << "\n"; } } /// \brief Read a grid written by colvar_grid::write_multicol() /// Adding data if add is true, replacing if false std::istream & read_multicol (std::istream &is, bool add = false) { // Data in the header: nColvars, then for each // xiMin, dXi, nPoints, periodic std::string hash; cvm::real lower, width, x; size_t n, periodic; bool remap; std::vector<T> new_value; std::vector<int> nx_read; std::vector<int> bin; if ( cv.size() != nd ) { cvm::fatal_error ("Cannot read grid file: missing reference to colvars."); } if ( !(is >> hash) || (hash != "#") ) { cvm::fatal_error ("Error reading grid at position "+ cvm::to_str (is.tellg())+" in stream (read \"" + hash + "\")\n"); } is >> n; if ( n != nd ) { cvm::fatal_error ("Error reading grid: wrong number of collective variables.\n"); } nx_read.resize (n); bin.resize (n); new_value.resize (mult); if (this->has_parent_data && add) { new_data.resize (data.size()); } remap = false; for (size_t i = 0; i < nd; i++ ) { if ( !(is >> hash) || (hash != "#") ) { cvm::fatal_error ("Error reading grid at position "+ cvm::to_str (is.tellg())+" in stream (read \"" + hash + "\")\n"); } is >> lower >> width >> nx_read[i] >> periodic; if ( (std::fabs (lower - lower_boundaries[i].real_value) > 1.0e-10) || (std::fabs (width - widths[i] ) > 1.0e-10) || (nx_read[i] != nx[i]) ) { cvm::log ("Warning: reading from different grid definition (colvar " + cvm::to_str (i+1) + "); remapping data on new grid.\n"); remap = true; } } if ( remap ) { // re-grid data while (is.good()) { bool end_of_file = false; for (size_t i = 0; i < nd; i++ ) { if ( !(is >> x) ) end_of_file = true; bin[i] = value_to_bin_scalar (x, i); } - if (end_of_file) break; + if (end_of_file) break; for (size_t imult = 0; imult < mult; imult++) { is >> new_value[imult]; } if ( index_ok(bin) ) { for (size_t imult = 0; imult < mult; imult++) { value_input (bin, new_value[imult], imult, add); } } } } else { // do not re-grid the data but assume the same grid is used for (std::vector<int> ix = new_index(); index_ok (ix); incr (ix) ) { for (size_t i = 0; i < nd; i++ ) { is >> x; } for (size_t imult = 0; imult < mult; imult++) { is >> new_value[imult]; value_input (ix, new_value[imult], imult, add); } } } has_data = true; return is; } }; /// \brief Colvar_grid derived class to hold counters in discrete /// n-dim colvar space class colvar_grid_count : public colvar_grid<size_t> { public: /// Default constructor colvar_grid_count(); /// Destructor virtual inline ~colvar_grid_count() {} /// Constructor colvar_grid_count (std::vector<int> const &nx_i, size_t const &def_count = 0); /// Constructor from a vector of colvars colvar_grid_count (std::vector<colvar *> &colvars, size_t const &def_count = 0); /// Increment the counter at given position inline void incr_count (std::vector<int> const &ix) { ++(data[this->address (ix)]); } - /// \brief Get the binned count indexed by ix from the newly read data + /// \brief Get the binned count indexed by ix from the newly read data inline size_t const & new_count (std::vector<int> const &ix, size_t const &imult = 0) { return new_data[address (ix) + imult]; } /// \brief Read the grid from a restart std::istream & read_restart (std::istream &is); /// \brief Write the grid to a restart std::ostream & write_restart (std::ostream &os); /// \brief Get the value from a formatted output and transform it /// into the internal representation (it may have been rescaled or /// manipulated) virtual inline void value_input (std::vector<int> const &ix, size_t const &t, size_t const &imult = 0, bool add = false) { if (add) { data[address (ix)] += t; if (this->has_parent_data) { // save newly read data for inputting parent grid new_data[address (ix)] = t; } } else { data[address (ix)] = t; } has_data = true; } }; /// Class for accumulating a scalar function on a grid class colvar_grid_scalar : public colvar_grid<cvm::real> { public: /// \brief Provide the associated sample count by which each binned value /// should be divided colvar_grid_count *samples; /// Default constructor colvar_grid_scalar(); /// Copy constructor (needed because of the grad pointer) colvar_grid_scalar (colvar_grid_scalar const &g); /// Destructor ~colvar_grid_scalar(); /// Constructor from specific sizes arrays colvar_grid_scalar (std::vector<int> const &nx_i); /// Constructor from a vector of colvars colvar_grid_scalar (std::vector<colvar *> &colvars, bool margin = 0); /// Accumulate the value inline void acc_value (std::vector<int> const &ix, cvm::real const &new_value, size_t const &imult = 0) { // only legal value of imult here is 0 data[address (ix)] += new_value; if (samples) samples->incr_count (ix); has_data = true; } /// Return the gradient of the scalar field from finite differences inline const cvm::real * gradient_finite_diff ( const std::vector<int> &ix0 ) { cvm::real A0, A1; std::vector<int> ix; if (nd != 2) cvm::fatal_error ("Finite differences available in dimension 2 only."); for (int n = 0; n < nd; n++) { ix = ix0; A0 = data[address (ix)]; ix[n]++; wrap (ix); A1 = data[address (ix)]; ix[1-n]++; wrap (ix); A1 += data[address (ix)]; ix[n]--; wrap (ix); A0 += data[address (ix)]; grad[n] = 0.5 * (A1 - A0) / widths[n]; } - return grad; + return grad; } /// \brief Return the value of the function at ix divided by its /// number of samples (if the count grid is defined) virtual cvm::real value_output (std::vector<int> const &ix, size_t const &imult = 0) { if (imult > 0) cvm::fatal_error ("Error: trying to access a component " "larger than 1 in a scalar data grid.\n"); if (samples) return (samples->value (ix) > 0) ? (data[address (ix)] / cvm::real (samples->value (ix))) : 0.0; else return data[address (ix)]; } /// \brief Get the value from a formatted output and transform it /// into the internal representation (it may have been rescaled or /// manipulated) virtual void value_input (std::vector<int> const &ix, cvm::real const &new_value, size_t const &imult = 0, bool add = false) { if (imult > 0) cvm::fatal_error ("Error: trying to access a component " "larger than 1 in a scalar data grid.\n"); if (add) { if (samples) data[address (ix)] += new_value * samples->new_count (ix); else data[address (ix)] += new_value; } else { if (samples) data[address (ix)] = new_value * samples->value (ix); else data[address (ix)] = new_value; } has_data = true; } /// \brief Read the grid from a restart std::istream & read_restart (std::istream &is); /// \brief Write the grid to a restart std::ostream & write_restart (std::ostream &os); /// \brief Return the highest value inline cvm::real maximum_value() { cvm::real max = data[0]; for (size_t i = 0; i < nt; i++) { if (data[i] > max) max = data[i]; } return max; } /// \brief Return the lowest value inline cvm::real minimum_value() { cvm::real min = data[0]; for (size_t i = 0; i < nt; i++) { if (data[i] < min) min = data[i]; } return min; } private: // gradient cvm::real * grad; }; /// Class for accumulating the gradient of a scalar function on a grid class colvar_grid_gradient : public colvar_grid<cvm::real> { public: /// \brief Provide the sample count by which each binned value /// should be divided colvar_grid_count *samples; /// Default constructor colvar_grid_gradient(); /// Destructor virtual inline ~colvar_grid_gradient() {} /// Constructor from specific sizes arrays colvar_grid_gradient (std::vector<int> const &nx_i); /// Constructor from a vector of colvars colvar_grid_gradient (std::vector<colvar *> &colvars); /// \brief Accumulate the gradient inline void acc_grad (std::vector<int> const &ix, cvm::real const *grads) { for (size_t imult = 0; imult < mult; imult++) { data[address (ix) + imult] += grads[imult]; } if (samples) samples->incr_count (ix); } /// \brief Accumulate the gradient based on the force (i.e. sums the /// opposite of the force) inline void acc_force (std::vector<int> const &ix, cvm::real const *forces) { for (size_t imult = 0; imult < mult; imult++) { data[address (ix) + imult] -= forces[imult]; } if (samples) samples->incr_count (ix); } /// \brief Return the value of the function at ix divided by its /// number of samples (if the count grid is defined) virtual inline cvm::real value_output (std::vector<int> const &ix, size_t const &imult = 0) { if (samples) return (samples->value (ix) > 0) ? (data[address (ix) + imult] / cvm::real (samples->value (ix))) : 0.0; else return data[address (ix) + imult]; } /// \brief Get the value from a formatted output and transform it /// into the internal representation (it may have been rescaled or /// manipulated) virtual inline void value_input (std::vector<int> const &ix, cvm::real const &new_value, size_t const &imult = 0, bool add = false) { if (add) { if (samples) data[address (ix) + imult] += new_value * samples->new_count (ix); else data[address (ix) + imult] += new_value; } else { if (samples) data[address (ix) + imult] = new_value * samples->value (ix); else data[address (ix) + imult] = new_value; } has_data = true; } /// \brief Read the grid from a restart std::istream & read_restart (std::istream &is); /// \brief Write the grid to a restart std::ostream & write_restart (std::ostream &os); /// Compute and return average value for a 1D gradient grid inline cvm::real average() { size_t n = 0; if (nd != 1 || nx[0] == 0) { return 0.0; } cvm::real sum = 0.0; std::vector<int> ix = new_index(); if (samples) { for ( ; index_ok (ix); incr (ix)) { if ( (n = samples->value (ix)) ) sum += value (ix) / n; } } else { for ( ; index_ok (ix); incr (ix)) { sum += value (ix); } } return (sum / cvm::real (nx[0])); } /// \brief If the grid is 1-dimensional, integrate it and write the /// integral to a file void write_1D_integral (std::ostream &os); }; #endif diff --git a/lib/colvars/colvarmodule.cpp b/lib/colvars/colvarmodule.cpp index c8a434706..948ddc1f2 100644 --- a/lib/colvars/colvarmodule.cpp +++ b/lib/colvars/colvarmodule.cpp @@ -1,1434 +1,1434 @@ #include "colvarmodule.h" #include "colvarparse.h" #include "colvarproxy.h" #include "colvar.h" #include "colvarbias.h" #include "colvarbias_meta.h" #include "colvarbias_abf.h" colvarmodule::colvarmodule (char const *config_filename, colvarproxy *proxy_in) -{ +{ // pointer to the proxy object if (proxy == NULL) { proxy = proxy_in; parse = new colvarparse(); } else { cvm::fatal_error ("Error: trying to allocate the collective " "variable module twice.\n"); } cvm::log (cvm::line_marker); cvm::log ("Initializing the collective variables module, version "+ cvm::to_str(COLVARS_VERSION)+".\n"); // "it_restart" will be set by the input state file, if any; // "it" should be updated by the proxy it = it_restart = 0; it_restart_from_state_file = true; // open the configfile config_s.open (config_filename); if (!config_s) cvm::fatal_error ("Error: in opening configuration file \""+ std::string (config_filename)+"\".\n"); // read the config file into a string std::string conf = ""; { std::string line; while (colvarparse::getline_nocomments (config_s, line)) conf.append (line+"\n"); // don't need the stream any more config_s.close(); } std::string index_file_name; if (parse->get_keyval (conf, "indexFile", index_file_name)) { read_index_file (index_file_name.c_str()); } parse->get_keyval (conf, "analysis", b_analysis, false); parse->get_keyval (conf, "debugGradientsStepSize", debug_gradients_step_size, 1.0e-07, colvarparse::parse_silent); parse->get_keyval (conf, "eigenvalueCrossingThreshold", colvarmodule::rotation::crossing_threshold, 1.0e-02, colvarparse::parse_silent); parse->get_keyval (conf, "colvarsTrajFrequency", cv_traj_freq, 100); parse->get_keyval (conf, "colvarsRestartFrequency", restart_out_freq, proxy->restart_frequency()); // by default overwrite the existing trajectory file parse->get_keyval (conf, "colvarsTrajAppend", cv_traj_append, false); // input restart file restart_in_name = proxy->input_prefix().size() ? std::string (proxy->input_prefix()+".colvars.state") : std::string ("") ; // output restart file restart_out_name = proxy->restart_output_prefix().size() ? std::string (proxy->restart_output_prefix()+".colvars.state") : std::string (""); if (restart_out_name.size()) cvm::log ("The restart output state file will be \""+restart_out_name+"\".\n"); output_prefix = proxy->output_prefix(); cvm::log ("The final output state file will be \""+ (output_prefix.size() ? std::string (output_prefix+".colvars.state") : std::string ("colvars.state"))+"\".\n"); - cv_traj_name = + cv_traj_name = (output_prefix.size() ? std::string (output_prefix+".colvars.traj") : std::string ("colvars.traj")); cvm::log ("The trajectory file will be \""+ cv_traj_name+"\".\n"); if (cv_traj_freq) { // open trajectory file if (cv_traj_append) { cvm::log ("Appending to colvar trajectory file \""+cv_traj_name+ "\".\n"); cv_traj_os.open (cv_traj_name.c_str(), std::ios::app); } else { proxy->backup_file (cv_traj_name.c_str()); cv_traj_os.open (cv_traj_name.c_str(), std::ios::out); } cv_traj_os.setf (std::ios::scientific, std::ios::floatfield); } // parse the options for collective variables init_colvars (conf); // parse the options for biases init_biases (conf); // done with the parsing, check that all keywords are valid parse->check_keywords (conf, "colvarmodule"); cvm::log (cvm::line_marker); // read the restart configuration, if available if (restart_in_name.size()) { // read the restart file std::ifstream input_is (restart_in_name.c_str()); if (!input_is.good()) fatal_error ("Error: in opening restart file \""+ std::string (restart_in_name)+"\".\n"); else { cvm::log ("Restarting from file \""+restart_in_name+"\".\n"); read_restart (input_is); cvm::log (cvm::line_marker); } } // check if it is possible to save output configuration if ((!output_prefix.size()) && (!restart_out_name.size())) { cvm::fatal_error ("Error: neither the final output state file or " "the output restart file could be defined, exiting.\n"); } cvm::log ("Collective variables module initialized.\n"); cvm::log (cvm::line_marker); } -std::istream & colvarmodule::read_restart (std::istream &is) +std::istream & colvarmodule::read_restart (std::istream &is) { { // read global restart information std::string restart_conf; if (is >> colvarparse::read_block ("configuration", restart_conf)) { if (it_restart_from_state_file) { parse->get_keyval (restart_conf, "step", it_restart, (size_t) 0, colvarparse::parse_silent); it = it_restart; } } is.clear(); } // colvars restart cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { if ( !((*cvi)->read_restart (is)) ) cvm::fatal_error ("Error: in reading restart configuration for collective variable \""+ (*cvi)->name+"\".\n"); } // biases restart for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { if (!((*bi)->read_restart (is))) fatal_error ("Error: in reading restart configuration for bias \""+ (*bi)->name+"\".\n"); } cvm::decrease_depth(); return is; } void colvarmodule::init_colvars (std::string const &conf) { if (cvm::debug()) cvm::log ("Initializing the collective variables.\n"); std::string colvar_conf = ""; size_t pos = 0; while (parse->key_lookup (conf, "colvar", colvar_conf, pos)) { if (colvar_conf.size()) { cvm::log (cvm::line_marker); cvm::increase_depth(); colvars.push_back (new colvar (colvar_conf)); (colvars.back())->check_keywords (colvar_conf, "colvar"); cvm::decrease_depth(); } else { - cvm::log ("Warning: \"colvar\" keyword found without any configuration.\n"); + cvm::log ("Warning: \"colvar\" keyword found without any configuration.\n"); } colvar_conf = ""; } if (!colvars.size()) cvm::fatal_error ("Error: no collective variables defined.\n"); if (colvars.size()) cvm::log (cvm::line_marker); cvm::log ("Collective variables initialized, "+ cvm::to_str (colvars.size())+ " in total.\n"); } void colvarmodule::init_biases (std::string const &conf) { if (cvm::debug()) cvm::log ("Initializing the collective variables biases.\n"); { /// initialize ABF instances std::string abf_conf = ""; size_t abf_pos = 0; while (parse->key_lookup (conf, "abf", abf_conf, abf_pos)) { if (abf_conf.size()) { cvm::log (cvm::line_marker); cvm::increase_depth(); biases.push_back (new colvarbias_abf (abf_conf, "abf")); (biases.back())->check_keywords (abf_conf, "abf"); cvm::decrease_depth(); n_abf_biases++; } else { cvm::log ("Warning: \"abf\" keyword found without configuration.\n"); } abf_conf = ""; } } { /// initialize harmonic restraints std::string harm_conf = ""; size_t harm_pos = 0; while (parse->key_lookup (conf, "harmonic", harm_conf, harm_pos)) { if (harm_conf.size()) { cvm::log (cvm::line_marker); cvm::increase_depth(); biases.push_back (new colvarbias_harmonic (harm_conf, "harmonic")); (biases.back())->check_keywords (harm_conf, "harmonic"); cvm::decrease_depth(); n_harm_biases++; } else { cvm::log ("Warning: \"harmonic\" keyword found without configuration.\n"); } harm_conf = ""; } } { /// initialize histograms std::string histo_conf = ""; size_t histo_pos = 0; while (parse->key_lookup (conf, "histogram", histo_conf, histo_pos)) { if (histo_conf.size()) { cvm::log (cvm::line_marker); cvm::increase_depth(); biases.push_back (new colvarbias_histogram (histo_conf, "histogram")); (biases.back())->check_keywords (histo_conf, "histogram"); cvm::decrease_depth(); n_histo_biases++; } else { cvm::log ("Warning: \"histogram\" keyword found without configuration.\n"); } histo_conf = ""; } } { /// initialize metadynamics instances std::string meta_conf = ""; size_t meta_pos = 0; while (parse->key_lookup (conf, "metadynamics", meta_conf, meta_pos)) { if (meta_conf.size()) { cvm::log (cvm::line_marker); cvm::increase_depth(); biases.push_back (new colvarbias_meta (meta_conf, "metadynamics")); (biases.back())->check_keywords (meta_conf, "metadynamics"); cvm::decrease_depth(); n_meta_biases++; } else { cvm::log ("Warning: \"metadynamics\" keyword found without configuration.\n"); } meta_conf = ""; } } if (biases.size()) cvm::log (cvm::line_marker); cvm::log ("Collective variables biases initialized, "+ cvm::to_str (biases.size())+" in total.\n"); } void colvarmodule::change_configuration (std::string const &bias_name, std::string const &conf) { cvm::increase_depth(); int found = 0; for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { if ( (*bi)->name == bias_name ) { ++found; (*bi)->change_configuration (conf); } } if (found < 1) cvm::fatal_error ("Error: bias not found.\n"); if (found > 1) cvm::fatal_error ("Error: duplicate bias name.\n"); cvm::decrease_depth(); } cvm::real colvarmodule::energy_difference (std::string const &bias_name, std::string const &conf) { cvm::increase_depth(); cvm::real energy_diff = 0.; int found = 0; for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { if ( (*bi)->name == bias_name ) { ++found; energy_diff = (*bi)->energy_difference (conf); } } if (found < 1) cvm::fatal_error ("Error: bias not found.\n"); if (found > 1) cvm::fatal_error ("Error: duplicate bias name.\n"); cvm::decrease_depth(); return energy_diff; } void colvarmodule::calc() { cvm::real total_bias_energy = 0.0; cvm::real total_colvar_energy = 0.0; if (cvm::debug()) { cvm::log (cvm::line_marker); cvm::log ("Collective variables module, step no. "+ cvm::to_str (cvm::step_absolute())+"\n"); } // calculate collective variables and their gradients - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Calculating collective variables.\n"); cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->calc(); + (*cvi)->calc(); } cvm::decrease_depth(); // update the biases and communicate their forces to the collective // variables if (cvm::debug() && biases.size()) cvm::log ("Updating collective variable biases.\n"); cvm::increase_depth(); for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { - total_bias_energy += (*bi)->update(); + total_bias_energy += (*bi)->update(); } cvm::decrease_depth(); // sum the forces from all biases for each collective variable if (cvm::debug() && biases.size()) cvm::log ("Collecting forces from all biases.\n"); cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->reset_bias_force(); + (*cvi)->reset_bias_force(); } for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { - (*bi)->communicate_forces(); + (*bi)->communicate_forces(); } cvm::decrease_depth(); if (cvm::b_analysis) { // perform runtime analysis of colvars and biases if (cvm::debug() && biases.size()) cvm::log ("Perform runtime analyses.\n"); cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->analyse(); + (*cvi)->analyse(); } for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { - (*bi)->analyse(); + (*bi)->analyse(); } cvm::decrease_depth(); } // sum up the forces for each colvar and integrate any internal // equation of motion if (cvm::debug()) cvm::log ("Updating the internal degrees of freedom " "of colvars (if they have any).\n"); cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { total_colvar_energy += (*cvi)->update(); } cvm::decrease_depth(); proxy->add_energy (total_bias_energy + total_colvar_energy); // make collective variables communicate their forces to their // coupled degrees of freedom (i.e. atoms) if (cvm::debug()) cvm::log ("Communicating forces from the colvars to the atoms.\n"); cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { if ((*cvi)->tasks[colvar::task_gradients]) - (*cvi)->communicate_forces(); + (*cvi)->communicate_forces(); } cvm::decrease_depth(); // write restart file, if needed if (restart_out_freq && restart_out_name.size() && !cvm::b_analysis) { if ( (cvm::step_relative() > 0) && ((cvm::step_absolute() % restart_out_freq) == 0) ) { cvm::log ("Writing the state file \""+ restart_out_name+"\".\n"); proxy->backup_file (restart_out_name.c_str()); restart_out_os.open (restart_out_name.c_str()); restart_out_os.setf (std::ios::scientific, std::ios::floatfield); if (!write_restart (restart_out_os)) cvm::fatal_error ("Error: in writing restart file.\n"); restart_out_os.close(); - } + } } // write trajectory file, if needed if (cv_traj_freq) { if (cvm::debug()) cvm::log ("Writing trajectory file.\n"); // (re)open trajectory file if (!cv_traj_os.good()) { if (cv_traj_append) { cvm::log ("Appending to colvar trajectory file \""+cv_traj_name+ "\".\n"); cv_traj_os.open (cv_traj_name.c_str(), std::ios::app); } else { cvm::log ("Overwriting colvar trajectory file \""+cv_traj_name+ "\".\n"); proxy->backup_file (cv_traj_name.c_str()); cv_traj_os.open (cv_traj_name.c_str(), std::ios::out); } cv_traj_os.setf (std::ios::scientific, std::ios::floatfield); } // write labels in the traj file every 1000 lines and at first ts cvm::increase_depth(); if ((cvm::step_absolute() % (cv_traj_freq * 1000)) == 0 || cvm::step_relative() == 0) { cv_traj_os << "# " << cvm::wrap_string ("step", cvm::it_width-2) << " "; - if (cvm::debug()) + if (cvm::debug()) cv_traj_os.flush(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { (*cvi)->write_traj_label (cv_traj_os); } for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { (*bi)->write_traj_label (cv_traj_os); } cv_traj_os << "\n"; - if (cvm::debug()) + if (cvm::debug()) cv_traj_os.flush(); } cvm::decrease_depth(); // write collective variable values to the traj file cvm::increase_depth(); if ((cvm::step_absolute() % cv_traj_freq) == 0) { cv_traj_os << std::setw (cvm::it_width) << it << " "; for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { (*cvi)->write_traj (cv_traj_os); } for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { (*bi)->write_traj (cv_traj_os); } cv_traj_os << "\n"; if (cvm::debug()) cv_traj_os.flush(); } cvm::decrease_depth(); - if (restart_out_freq) { + if (restart_out_freq) { // flush the trajectory file if we are at the restart frequency if ( (cvm::step_relative() > 0) && ((cvm::step_absolute() % restart_out_freq) == 0) ) { cvm::log ("Synchronizing (emptying the buffer of) trajectory file \""+ cv_traj_name+"\".\n"); cv_traj_os.flush(); } } } // end if (cv_traj_freq) } void colvarmodule::analyze() { if (cvm::debug()) { cvm::log ("colvarmodule::analyze(), step = "+cvm::to_str (it)+".\n"); } if (cvm::step_relative() == 0) cvm::log ("Performing analysis.\n"); // perform colvar-specific analysis for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { cvm::increase_depth(); - (*cvi)->analyse(); + (*cvi)->analyse(); cvm::decrease_depth(); } // perform bias-specific analysis for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { cvm::increase_depth(); - (*bi)->analyse(); + (*bi)->analyse(); cvm::decrease_depth(); } } colvarmodule::~colvarmodule() { for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { delete *cvi; } colvars.clear(); for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { delete *bi; } biases.clear(); if (cv_traj_os.good()) { cv_traj_os.close(); } delete parse; proxy = NULL; -} +} void colvarmodule::write_output_files() { // if this is a simulation run (i.e. not a postprocessing), output data // must be written to be able to restart the simulation std::string const out_name = (output_prefix.size() ? std::string (output_prefix+".colvars.state") : std::string ("colvars.state")); cvm::log ("Saving collective variables state to \""+out_name+"\".\n"); proxy->backup_file (out_name.c_str()); std::ofstream out (out_name.c_str()); out.setf (std::ios::scientific, std::ios::floatfield); this->write_restart (out); out.close(); cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { (*cvi)->write_output_files(); } cvm::decrease_depth(); - + // do not close to avoid problems with multiple NAMD runs cv_traj_os.flush(); } bool colvarmodule::read_traj (char const *traj_filename, size_t traj_read_begin, size_t traj_read_end) { cvm::log ("Opening trajectory file \""+ std::string (traj_filename)+"\".\n"); std::ifstream traj_is (traj_filename); while (true) { while (true) { std::string line (""); do { if (!colvarparse::getline_nocomments (traj_is, line)) { cvm::log ("End of file \""+std::string (traj_filename)+ "\" reached, or corrupted file.\n"); traj_is.close(); return false; } } while (line.find_first_not_of (colvarparse::white_space) == std::string::npos); std::istringstream is (line); if (!(is >> it)) return false; if ( (it < traj_read_begin) ) { if ((it % 1000) == 0) std::cerr << "Skipping trajectory step " << it << " \r"; continue; - } else { + } else { if ((it % 1000) == 0) std::cerr << "Reading from trajectory, step = " << it << " \r"; if ( (traj_read_end > traj_read_begin) && (it > traj_read_end) ) { std::cerr << "\n"; cvm::log ("Reached the end of the trajectory, " "read_end = "+cvm::to_str (traj_read_end)+"\n"); return false; } for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { if (!(*cvi)->read_traj (is)) { cvm::log ("Error: in reading colvar \""+(*cvi)->name+ "\" from trajectory file \""+ std::string (traj_filename)+"\".\n"); return false; } } break; } } } return true; } std::ostream & colvarmodule::write_restart (std::ostream &os) { os << "configuration {\n" << " step " << std::setw (it_width) << it << "\n" << " dt " << dt() << "\n" << "}\n\n"; cvm::increase_depth(); for (std::vector<colvar *>::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { (*cvi)->write_restart (os); } for (std::vector<colvarbias *>::iterator bi = biases.begin(); bi != biases.end(); bi++) { (*bi)->write_restart (os); } cvm::decrease_depth(); return os; } void cvm::log (std::string const &message) { if (depth > 0) proxy->log ((std::string (2*depth, ' '))+message); else proxy->log (message); } void cvm::increase_depth() { depth++; } void cvm::decrease_depth() { if (depth) depth--; } void cvm::fatal_error (std::string const &message) { proxy->fatal_error (message); } void cvm::exit (std::string const &message) { proxy->exit (message); } void cvm::read_index_file (char const *filename) { std::ifstream is (filename); if (!is.good()) fatal_error ("Error: in opening index file \""+ std::string (filename)+"\".\n"); // std::list<std::string>::iterator names_i = cvm::index_group_names.begin(); // std::list<std::vector<int> >::iterator lists_i = cvm::index_groups.begin(); while (is.good()) { char open, close; std::string group_name; - if ( (is >> open) && (open == '[') && + if ( (is >> open) && (open == '[') && (is >> group_name) && (is >> close) && (close == ']') ) { cvm::index_group_names.push_back (group_name); cvm::index_groups.push_back (std::vector<int> ()); } else { cvm::fatal_error ("Error: in parsing index file \""+ std::string (filename)+"\".\n"); } int atom_number = 1; size_t pos = is.tellg(); while ( (is >> atom_number) && (atom_number > 0) ) { (cvm::index_groups.back()).push_back (atom_number); pos = is.tellg(); } is.clear(); is.seekg (pos, std::ios::beg); std::string delim; if ( (is >> delim) && (delim == "[") ) { // new group is.clear(); is.seekg (pos, std::ios::beg); } else { break; } } cvm::log ("The following index groups were read from the index file \""+ std::string (filename)+"\":\n"); std::list<std::string>::iterator names_i = cvm::index_group_names.begin(); std::list<std::vector<int> >::iterator lists_i = cvm::index_groups.begin(); for ( ; names_i != cvm::index_group_names.end() ; names_i++, lists_i++) { cvm::log (" "+(*names_i)+" ("+cvm::to_str (lists_i->size())+" atoms).\n"); } } // static pointers std::vector<colvar *> colvarmodule::colvars; std::vector<colvarbias *> colvarmodule::biases; size_t colvarmodule::n_abf_biases = 0; size_t colvarmodule::n_harm_biases = 0; size_t colvarmodule::n_histo_biases = 0; size_t colvarmodule::n_meta_biases = 0; colvarproxy *colvarmodule::proxy = NULL; // static runtime data cvm::real colvarmodule::debug_gradients_step_size = 1.0e-03; size_t colvarmodule::it = 0; size_t colvarmodule::it_restart = 0; size_t colvarmodule::restart_out_freq = 0; size_t colvarmodule::cv_traj_freq = 0; size_t colvarmodule::depth = 0; bool colvarmodule::b_analysis = false; cvm::real colvarmodule::rotation::crossing_threshold = 1.0E-04; std::list<std::string> colvarmodule::index_group_names; std::list<std::vector<int> > colvarmodule::index_groups; // file name prefixes std::string colvarmodule::output_prefix = ""; std::string colvarmodule::input_prefix = ""; std::string colvarmodule::restart_in_name = ""; // i/o constants size_t const colvarmodule::it_width = 12; size_t const colvarmodule::cv_prec = 14; size_t const colvarmodule::cv_width = 21; size_t const colvarmodule::en_prec = 14; size_t const colvarmodule::en_width = 21; std::string const colvarmodule::line_marker = "----------------------------------------------------------------------\n"; std::ostream & operator << (std::ostream &os, colvarmodule::rvector const &v) { std::streamsize const w = os.width(); std::streamsize const p = os.precision(); os.width (2); os << "( "; os.width (w); os.precision (p); os << v.x << " , "; os.width (w); os.precision (p); os << v.y << " , "; os.width (w); os.precision (p); os << v.z << " )"; return os; } std::istream & operator >> (std::istream &is, colvarmodule::rvector &v) { size_t const start_pos = is.tellg(); char sep; if ( !(is >> sep) || !(sep == '(') || !(is >> v.x) || !(is >> sep) || !(sep == ',') || !(is >> v.y) || !(is >> sep) || !(sep == ',') || !(is >> v.z) || !(is >> sep) || !(sep == ')') ) { is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } return is; } std::ostream & operator << (std::ostream &os, colvarmodule::quaternion const &q) { std::streamsize const w = os.width(); std::streamsize const p = os.precision(); os.width (2); os << "( "; os.width (w); os.precision (p); os << q.q0 << " , "; os.width (w); os.precision (p); os << q.q1 << " , "; os.width (w); os.precision (p); os << q.q2 << " , "; os.width (w); os.precision (p); os << q.q3 << " )"; return os; } std::istream & operator >> (std::istream &is, colvarmodule::quaternion &q) { size_t const start_pos = is.tellg(); std::string euler (""); if ( (is >> euler) && (colvarparse::to_lower_cppstr (euler) == std::string ("euler")) ) { // parse the Euler angles - + char sep; cvm::real phi, theta, psi; if ( !(is >> sep) || !(sep == '(') || !(is >> phi) || !(is >> sep) || !(sep == ',') || !(is >> theta) || !(is >> sep) || !(sep == ',') || !(is >> psi) || !(is >> sep) || !(sep == ')') ) { is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } q = colvarmodule::quaternion (phi, theta, psi); } else { // parse the quaternion components - is.seekg (start_pos, std::ios::beg); + is.seekg (start_pos, std::ios::beg); char sep; if ( !(is >> sep) || !(sep == '(') || !(is >> q.q0) || !(is >> sep) || !(sep == ',') || !(is >> q.q1) || !(is >> sep) || !(sep == ',') || !(is >> q.q2) || !(is >> sep) || !(sep == ',') || !(is >> q.q3) || !(is >> sep) || !(sep == ')') ) { is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } } return is; } cvm::quaternion cvm::quaternion::position_derivative_inner (cvm::rvector const &pos, cvm::rvector const &vec) const { cvm::quaternion result (0.0, 0.0, 0.0, 0.0); result.q0 = 2.0 * pos.x * q0 * vec.x +2.0 * pos.y * q0 * vec.y +2.0 * pos.z * q0 * vec.z -2.0 * pos.y * q3 * vec.x +2.0 * pos.z * q2 * vec.x +2.0 * pos.x * q3 * vec.y -2.0 * pos.z * q1 * vec.y -2.0 * pos.x * q2 * vec.z +2.0 * pos.y * q1 * vec.z; result.q1 = +2.0 * pos.x * q1 * vec.x -2.0 * pos.y * q1 * vec.y -2.0 * pos.z * q1 * vec.z +2.0 * pos.y * q2 * vec.x +2.0 * pos.z * q3 * vec.x +2.0 * pos.x * q2 * vec.y -2.0 * pos.z * q0 * vec.y +2.0 * pos.x * q3 * vec.z +2.0 * pos.y * q0 * vec.z; result.q2 = -2.0 * pos.x * q2 * vec.x +2.0 * pos.y * q2 * vec.y -2.0 * pos.z * q2 * vec.z +2.0 * pos.y * q1 * vec.x +2.0 * pos.z * q0 * vec.x +2.0 * pos.x * q1 * vec.y +2.0 * pos.z * q3 * vec.y -2.0 * pos.x * q0 * vec.z +2.0 * pos.y * q3 * vec.z; result.q3 = -2.0 * pos.x * q3 * vec.x -2.0 * pos.y * q3 * vec.y +2.0 * pos.z * q3 * vec.z -2.0 * pos.y * q0 * vec.x +2.0 * pos.z * q1 * vec.x +2.0 * pos.x * q0 * vec.y +2.0 * pos.z * q2 * vec.y +2.0 * pos.x * q1 * vec.z +2.0 * pos.y * q2 * vec.z; return result; } // Calculate the optimal rotation between two groups, and implement it // as a quaternion. The method is the one documented in: Coutsias EA, // Seok C, Dill KA. Using quaternions to calculate RMSD. J Comput // Chem. 25(15):1849-57 (2004) DOI: 10.1002/jcc.20110 PubMed: 15376254 void colvarmodule::rotation::build_matrix (std::vector<cvm::atom_pos> const &pos1, std::vector<cvm::atom_pos> const &pos2, matrix2d<cvm::real, 4, 4> &S) { cvm::rmatrix C; // build the correlation matrix C.reset(); for (size_t i = 0; i < pos1.size(); i++) { C.xx() += pos1[i].x * pos2[i].x; C.xy() += pos1[i].x * pos2[i].y; C.xz() += pos1[i].x * pos2[i].z; C.yx() += pos1[i].y * pos2[i].x; C.yy() += pos1[i].y * pos2[i].y; C.yz() += pos1[i].y * pos2[i].z; C.zx() += pos1[i].z * pos2[i].x; C.zy() += pos1[i].z * pos2[i].y; C.zz() += pos1[i].z * pos2[i].z; - } + } // build the "overlap" matrix, whose eigenvectors are stationary // points of the RMSD in the space of rotations S[0][0] = C.xx() + C.yy() + C.zz(); S[1][0] = C.yz() - C.zy(); S[0][1] = S[1][0]; S[2][0] = - C.xz() + C.zx() ; S[0][2] = S[2][0]; S[3][0] = C.xy() - C.yx(); S[0][3] = S[3][0]; S[1][1] = C.xx() - C.yy() - C.zz(); S[2][1] = C.xy() + C.yx(); S[1][2] = S[2][1]; S[3][1] = C.xz() + C.zx(); S[1][3] = S[3][1]; S[2][2] = - C.xx() + C.yy() - C.zz(); S[3][2] = C.yz() + C.zy(); S[2][3] = S[3][2]; S[3][3] = - C.xx() - C.yy() + C.zz(); // if (cvm::debug()) { // for (size_t i = 0; i < 4; i++) { // std::string line (""); // for (size_t j = 0; j < 4; j++) { // line += std::string (" S["+cvm::to_str (i)+ // "]["+cvm::to_str (j)+"] ="+cvm::to_str (S[i][j])); // } // cvm::log (line+"\n"); // } // } } void colvarmodule::rotation::diagonalize_matrix (matrix2d<cvm::real, 4, 4> &S, cvm::real S_eigval[4], matrix2d<cvm::real, 4, 4> &S_eigvec) { // diagonalize int jac_nrot = 0; jacobi (S, 4, S_eigval, S_eigvec, &jac_nrot); eigsrt (S_eigval, S_eigvec, 4); // jacobi saves eigenvectors by columns transpose (S_eigvec, 4); // normalize eigenvectors for (size_t ie = 0; ie < 4; ie++) { cvm::real norm2 = 0.0; for (size_t i = 0; i < 4; i++) norm2 += std::pow (S_eigvec[ie][i], int (2)); cvm::real const norm = std::sqrt (norm2); for (size_t i = 0; i < 4; i++) S_eigvec[ie][i] /= norm; } } // Calculate the rotation, plus its derivatives void colvarmodule::rotation::calc_optimal_rotation (std::vector<cvm::atom_pos> const &pos1, std::vector<cvm::atom_pos> const &pos2) { matrix2d<cvm::real, 4, 4> S; matrix2d<cvm::real, 4, 4> S_backup; cvm::real S_eigval[4]; matrix2d<cvm::real, 4, 4> S_eigvec; // if (cvm::debug()) { // cvm::atom_pos cog1 (0.0, 0.0, 0.0); // for (size_t i = 0; i < pos1.size(); i++) { // cog1 += pos1[i]; // } // cog1 /= cvm::real (pos1.size()); // cvm::atom_pos cog2 (0.0, 0.0, 0.0); // for (size_t i = 0; i < pos2.size(); i++) { // cog2 += pos2[i]; // } // cog2 /= cvm::real (pos1.size()); // cvm::log ("calc_optimal_rotation: centers of geometry are: "+ // cvm::to_str (cog1, cvm::cv_width, cvm::cv_prec)+ // " and "+cvm::to_str (cog2, cvm::cv_width, cvm::cv_prec)+".\n"); // } build_matrix (pos1, pos2, S); S_backup = S; if (cvm::debug()) { if (b_debug_gradients) { cvm::log ("S = "+cvm::to_str (cvm::to_str (S_backup), cvm::cv_width, cvm::cv_prec)+"\n"); } } diagonalize_matrix (S, S_eigval, S_eigvec); // eigenvalues and eigenvectors cvm::real const &L0 = S_eigval[0]; cvm::real const &L1 = S_eigval[1]; cvm::real const &L2 = S_eigval[2]; cvm::real const &L3 = S_eigval[3]; cvm::real const *Q0 = S_eigvec[0]; cvm::real const *Q1 = S_eigvec[1]; cvm::real const *Q2 = S_eigvec[2]; cvm::real const *Q3 = S_eigvec[3]; lambda = L0; q = cvm::quaternion (Q0); if (q_old.norm2() > 0.0) { q.match (q_old); if (q_old.inner (q) < (1.0 - crossing_threshold)) { cvm::log ("Warning: one molecular orientation has changed by more than "+ cvm::to_str (crossing_threshold)+": discontinuous rotation ?\n"); } } q_old = q; - + if (cvm::debug()) { if (b_debug_gradients) { cvm::log ("L0 = "+cvm::to_str (L0, cvm::cv_width, cvm::cv_prec)+ ", Q0 = "+cvm::to_str (cvm::quaternion (Q0), cvm::cv_width, cvm::cv_prec)+ ", Q0*Q0 = "+cvm::to_str (cvm::quaternion (Q0).inner (cvm::quaternion (Q0)), cvm::cv_width, cvm::cv_prec)+ "\n"); cvm::log ("L1 = "+cvm::to_str (L1, cvm::cv_width, cvm::cv_prec)+ ", Q1 = "+cvm::to_str (cvm::quaternion (Q1), cvm::cv_width, cvm::cv_prec)+ ", Q0*Q1 = "+cvm::to_str (cvm::quaternion (Q0).inner (cvm::quaternion (Q1)), cvm::cv_width, cvm::cv_prec)+ "\n"); cvm::log ("L2 = "+cvm::to_str (L2, cvm::cv_width, cvm::cv_prec)+ ", Q2 = "+cvm::to_str (cvm::quaternion (Q2), cvm::cv_width, cvm::cv_prec)+ ", Q0*Q2 = "+cvm::to_str (cvm::quaternion (Q0).inner (cvm::quaternion (Q2)), cvm::cv_width, cvm::cv_prec)+ "\n"); cvm::log ("L3 = "+cvm::to_str (L3, cvm::cv_width, cvm::cv_prec)+ ", Q3 = "+cvm::to_str (cvm::quaternion (Q3), cvm::cv_width, cvm::cv_prec)+ ", Q0*Q3 = "+cvm::to_str (cvm::quaternion (Q0).inner (cvm::quaternion (Q3)), cvm::cv_width, cvm::cv_prec)+ "\n"); } } // calculate derivatives of L0 and Q0 with respect to each atom in // either group; note: if dS_1 is a null vector, nothing will be // calculated for (size_t ia = 0; ia < dS_1.size(); ia++) { cvm::real const &a2x = pos2[ia].x; cvm::real const &a2y = pos2[ia].y; cvm::real const &a2z = pos2[ia].z; matrix2d<cvm::rvector, 4, 4> &ds_1 = dS_1[ia]; // derivative of the S matrix ds_1.reset(); ds_1[0][0] = cvm::rvector ( a2x, a2y, a2z); ds_1[1][0] = cvm::rvector ( 0.0, a2z, -a2y); ds_1[0][1] = ds_1[1][0]; ds_1[2][0] = cvm::rvector (-a2z, 0.0, a2x); ds_1[0][2] = ds_1[2][0]; ds_1[3][0] = cvm::rvector ( a2y, -a2x, 0.0); ds_1[0][3] = ds_1[3][0]; ds_1[1][1] = cvm::rvector ( a2x, -a2y, -a2z); ds_1[2][1] = cvm::rvector ( a2y, a2x, 0.0); ds_1[1][2] = ds_1[2][1]; ds_1[3][1] = cvm::rvector ( a2z, 0.0, a2x); ds_1[1][3] = ds_1[3][1]; ds_1[2][2] = cvm::rvector (-a2x, a2y, -a2z); ds_1[3][2] = cvm::rvector ( 0.0, a2z, a2y); ds_1[2][3] = ds_1[3][2]; ds_1[3][3] = cvm::rvector (-a2x, -a2y, a2z); cvm::rvector &dl0_1 = dL0_1[ia]; vector1d<cvm::rvector, 4> &dq0_1 = dQ0_1[ia]; // matrix multiplications; derivatives of L_0 and Q_0 are // calculated using Hellmann-Feynman theorem (i.e. exploiting the // fact that the eigenvectors Q_i form an orthonormal basis) dl0_1.reset(); for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { dl0_1 += Q0[i] * ds_1[i][j] * Q0[j]; } } dq0_1.reset(); for (size_t p = 0; p < 4; p++) { for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { dq0_1[p] += - (Q1[i] * ds_1[i][j] * Q0[j]) / (L0-L1) * Q1[p] + - (Q2[i] * ds_1[i][j] * Q0[j]) / (L0-L2) * Q2[p] + + (Q1[i] * ds_1[i][j] * Q0[j]) / (L0-L1) * Q1[p] + + (Q2[i] * ds_1[i][j] * Q0[j]) / (L0-L2) * Q2[p] + (Q3[i] * ds_1[i][j] * Q0[j]) / (L0-L3) * Q3[p]; } } } } // do the same for the second group for (size_t ia = 0; ia < dS_2.size(); ia++) { cvm::real const &a1x = pos1[ia].x; cvm::real const &a1y = pos1[ia].y; cvm::real const &a1z = pos1[ia].z; matrix2d<cvm::rvector, 4, 4> &ds_2 = dS_2[ia]; ds_2.reset(); ds_2[0][0] = cvm::rvector ( a1x, a1y, a1z); ds_2[1][0] = cvm::rvector ( 0.0, -a1z, a1y); ds_2[0][1] = ds_2[1][0]; ds_2[2][0] = cvm::rvector ( a1z, 0.0, -a1x); ds_2[0][2] = ds_2[2][0]; ds_2[3][0] = cvm::rvector (-a1y, a1x, 0.0); ds_2[0][3] = ds_2[3][0]; ds_2[1][1] = cvm::rvector ( a1x, -a1y, -a1z); ds_2[2][1] = cvm::rvector ( a1y, a1x, 0.0); ds_2[1][2] = ds_2[2][1]; ds_2[3][1] = cvm::rvector ( a1z, 0.0, a1x); ds_2[1][3] = ds_2[3][1]; ds_2[2][2] = cvm::rvector (-a1x, a1y, -a1z); ds_2[3][2] = cvm::rvector ( 0.0, a1z, a1y); ds_2[2][3] = ds_2[3][2]; ds_2[3][3] = cvm::rvector (-a1x, -a1y, a1z); cvm::rvector &dl0_2 = dL0_2[ia]; vector1d<cvm::rvector, 4> &dq0_2 = dQ0_2[ia]; dl0_2.reset(); for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { dl0_2 += Q0[i] * ds_2[i][j] * Q0[j]; } } dq0_2.reset(); for (size_t p = 0; p < 4; p++) { for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { dq0_2[p] += - (Q1[i] * ds_2[i][j] * Q0[j]) / (L0-L1) * Q1[p] + - (Q2[i] * ds_2[i][j] * Q0[j]) / (L0-L2) * Q2[p] + + (Q1[i] * ds_2[i][j] * Q0[j]) / (L0-L1) * Q1[p] + + (Q2[i] * ds_2[i][j] * Q0[j]) / (L0-L2) * Q2[p] + (Q3[i] * ds_2[i][j] * Q0[j]) / (L0-L3) * Q3[p]; } } } if (cvm::debug()) { - + if (b_debug_gradients) { matrix2d<cvm::real, 4, 4> S_new; cvm::real S_new_eigval[4]; matrix2d<cvm::real, 4, 4> S_new_eigvec; // make an infitesimal move along each cartesian coordinate of // this atom, and solve again the eigenvector problem for (size_t comp = 0; comp < 3; comp++) { S_new = S_backup; // diagonalize the new overlap matrix for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { - S_new[i][j] += + S_new[i][j] += colvarmodule::debug_gradients_step_size * ds_2[i][j][comp]; } } // cvm::log ("S_new = "+cvm::to_str (cvm::to_str (S_new), cvm::cv_width, cvm::cv_prec)+"\n"); diagonalize_matrix (S_new, S_new_eigval, S_new_eigvec); cvm::real const &L0_new = S_new_eigval[0]; cvm::real const *Q0_new = S_new_eigvec[0]; cvm::real const DL0 = (dl0_2[comp]) * colvarmodule::debug_gradients_step_size; cvm::quaternion const q0 (Q0); cvm::quaternion const DQ0 (dq0_2[0][comp] * colvarmodule::debug_gradients_step_size, dq0_2[1][comp] * colvarmodule::debug_gradients_step_size, dq0_2[2][comp] * colvarmodule::debug_gradients_step_size, dq0_2[3][comp] * colvarmodule::debug_gradients_step_size); cvm::log ( "|(l_0+dl_0) - l_0^new|/l_0 = "+ cvm::to_str (std::fabs (L0+DL0 - L0_new)/L0, cvm::cv_width, cvm::cv_prec)+ ", |(q_0+dq_0) - q_0^new| = "+ cvm::to_str ((Q0+DQ0 - Q0_new).norm(), cvm::cv_width, cvm::cv_prec)+ "\n"); } } } } } // Numerical Recipes routine for diagonalization #define ROTATE(a,i,j,k,l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau); \ a[k][l]=h+s*(g-h*tau); void jacobi(cvm::real **a, int n, cvm::real d[], cvm::real **v, int *nrot) { int j,iq,ip,i; cvm::real tresh,theta,tau,t,sm,s,h,g,c; std::vector<cvm::real> b (n, 0.0); std::vector<cvm::real> z (n, 0.0); for (ip=0;ip<n;ip++) { for (iq=0;iq<n;iq++) v[ip][iq]=0.0; v[ip][ip]=1.0; } for (ip=0;ip<n;ip++) { b[ip]=d[ip]=a[ip][ip]; z[ip]=0.0; } *nrot=0; for (i=0;i<=50;i++) { sm=0.0; for (ip=0;ip<n-1;ip++) { for (iq=ip+1;iq<n;iq++) sm += std::fabs(a[ip][iq]); } if (sm == 0.0) { return; } if (i < 4) tresh=0.2*sm/(n*n); else tresh=0.0; for (ip=0;ip<n-1;ip++) { for (iq=ip+1;iq<n;iq++) { g=100.0*std::fabs(a[ip][iq]); if (i > 4 && (cvm::real)(std::fabs(d[ip])+g) == (cvm::real)std::fabs(d[ip]) && (cvm::real)(std::fabs(d[iq])+g) == (cvm::real)std::fabs(d[iq])) a[ip][iq]=0.0; else if (std::fabs(a[ip][iq]) > tresh) { h=d[iq]-d[ip]; if ((cvm::real)(std::fabs(h)+g) == (cvm::real)std::fabs(h)) t=(a[ip][iq])/h; else { theta=0.5*h/(a[ip][iq]); t=1.0/(std::fabs(theta)+std::sqrt(1.0+theta*theta)); if (theta < 0.0) t = -t; } c=1.0/std::sqrt(1+t*t); s=t*c; tau=s/(1.0+c); h=t*a[ip][iq]; z[ip] -= h; z[iq] += h; d[ip] -= h; d[iq] += h; a[ip][iq]=0.0; for (j=0;j<=ip-1;j++) { ROTATE(a,j,ip,j,iq) } for (j=ip+1;j<=iq-1;j++) { ROTATE(a,ip,j,j,iq) } for (j=iq+1;j<n;j++) { ROTATE(a,ip,j,iq,j) } for (j=0;j<n;j++) { ROTATE(v,j,ip,j,iq) } ++(*nrot); } } } for (ip=0;ip<n;ip++) { b[ip] += z[ip]; d[ip]=b[ip]; z[ip]=0.0; } } cvm::fatal_error ("Too many iterations in routine jacobi.\n"); } #undef ROTATE void eigsrt(cvm::real d[], cvm::real **v, int n) { int k,j,i; cvm::real p; for (i=0;i<n;i++) { p=d[k=i]; for (j=i+1;j<n;j++) if (d[j] >= p) p=d[k=j]; if (k != i) { d[k]=d[i]; d[i]=p; for (j=0;j<n;j++) { p=v[j][i]; v[j][i]=v[j][k]; v[j][k]=p; } } } } void transpose(cvm::real **v, int n) { cvm::real p; for (int i=0;i<n;i++) { for (int j=i+1;j<n;j++) { p=v[i][j]; v[i][j]=v[j][i]; v[j][i]=p; } } } diff --git a/lib/colvars/colvarmodule.h b/lib/colvars/colvarmodule.h index c506f2cb5..997b0ea1b 100644 --- a/lib/colvars/colvarmodule.h +++ b/lib/colvars/colvarmodule.h @@ -1,505 +1,505 @@ #ifndef COLVARMODULE_H #define COLVARMODULE_H #ifndef COLVARS_VERSION -#define COLVARS_VERSION "2013-06-05" +#define COLVARS_VERSION "2013-06-07" #endif #ifndef COLVARS_DEBUG #define COLVARS_DEBUG false #endif -/// \file colvarmodule.h +/// \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, + 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 for the given bias - /// currently works for harmonic (force constant and/or centers) void change_configuration (std::string const &bias_name, std::string const &conf); /// Calculate change in energy from using alt. config. for the given bias - /// currently works for harmonic (force constant and/or centers) real energy_difference (std::string const &bias_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, + 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, + 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 + /// \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 Names of groups from a Gromacs .ndx file to be read at startup static std::list<std::string> index_group_names; /// \brief Groups from a Gromacs .ndx file read at startup static std::list<std::vector<int> > index_groups; /// \brief Read a Gromacs .ndx file static void read_index_file (char const *filename); /// \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, +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, +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: diff --git a/lib/colvars/colvarparse.cpp b/lib/colvars/colvarparse.cpp index 0a40de2d2..2e62072f3 100644 --- a/lib/colvars/colvarparse.cpp +++ b/lib/colvars/colvarparse.cpp @@ -1,643 +1,643 @@ #include <sstream> #include <iostream> #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" // space & tab std::string const colvarparse::white_space = " \t"; std::string colvarparse::dummy_string = ""; size_t colvarparse::dummy_pos = 0; // definition of single-value keyword parsers #define _get_keyval_scalar_string_(TYPE) \ \ bool colvarparse::get_keyval (std::string const &conf, \ char const *key, \ TYPE &value, \ TYPE const &def_value, \ Parse_Mode const parse_mode) \ { \ std::string data; \ bool b_found = false, b_found_any = false; \ size_t save_pos = 0, found_count = 0; \ \ do { \ std::string data_this = ""; \ b_found = key_lookup (conf, key, data_this, save_pos); \ if (b_found) { \ if (!b_found_any) \ b_found_any = true; \ found_count++; \ data = data_this; \ } \ } while (b_found); \ \ if (found_count > 1) \ cvm::log ("Warning: found more than one instance of \""+ \ std::string (key)+"\".\n"); \ \ if (data.size()) { \ std::istringstream is (data); \ TYPE x (def_value); \ if (is >> x) \ value = x; \ else \ cvm::fatal_error ("Error: in parsing \""+ \ std::string (key)+"\".\n"); \ if (parse_mode != parse_silent) { \ cvm::log ("# "+std::string (key)+" = "+ \ cvm::to_str (value)+"\n"); \ } \ } else { \ \ if (b_found_any) \ cvm::fatal_error ("Error: improper or missing value " \ "for \""+std::string (key)+"\".\n"); \ value = def_value; \ if (parse_mode != parse_silent) { \ cvm::log ("# "+std::string (key)+" = \""+ \ cvm::to_str (def_value)+"\" [default]\n"); \ } \ } \ \ return b_found_any; \ } #define _get_keyval_scalar_(TYPE) \ \ bool colvarparse::get_keyval (std::string const &conf, \ char const *key, \ TYPE &value, \ TYPE const &def_value, \ Parse_Mode const parse_mode) \ { \ std::string data; \ bool b_found = false, b_found_any = false; \ size_t save_pos = 0, found_count = 0; \ \ do { \ std::string data_this = ""; \ b_found = key_lookup (conf, key, data_this, save_pos); \ if (b_found) { \ if (!b_found_any) \ b_found_any = true; \ found_count++; \ data = data_this; \ } \ } while (b_found); \ \ if (found_count > 1) \ cvm::log ("Warning: found more than one instance of \""+ \ std::string (key)+"\".\n"); \ \ if (data.size()) { \ std::istringstream is (data); \ size_t data_count = 0; \ TYPE x (def_value); \ while (is >> x) { \ value = x; \ data_count++; \ } \ if (data_count == 0) \ cvm::fatal_error ("Error: in parsing \""+ \ std::string (key)+"\".\n"); \ if (data_count > 1) \ cvm::fatal_error ("Error: multiple values " \ "are not allowed for keyword \""+ \ std::string (key)+"\".\n"); \ if (parse_mode != parse_silent) { \ cvm::log ("# "+std::string (key)+" = "+ \ cvm::to_str (value)+"\n"); \ } \ } else { \ \ if (b_found_any) \ cvm::fatal_error ("Error: improper or missing value " \ "for \""+std::string (key)+"\".\n"); \ value = def_value; \ if (parse_mode != parse_silent) { \ cvm::log ("# "+std::string (key)+" = "+ \ cvm::to_str (def_value)+" [default]\n"); \ } \ } \ \ return b_found_any; \ } // definition of multiple-value keyword parsers #define _get_keyval_vector_(TYPE) \ \ bool colvarparse::get_keyval (std::string const &conf, \ char const *key, \ std::vector<TYPE> &values, \ std::vector<TYPE> const &def_values, \ Parse_Mode const parse_mode) \ { \ std::string data; \ bool b_found = false, b_found_any = false; \ size_t save_pos = 0, found_count = 0; \ \ do { \ std::string data_this = ""; \ b_found = key_lookup (conf, key, data_this, save_pos); \ if (b_found) { \ if (!b_found_any) \ b_found_any = true; \ found_count++; \ data = data_this; \ } \ } while (b_found); \ \ if (found_count > 1) \ cvm::log ("Warning: found more than one instance of \""+ \ std::string (key)+"\".\n"); \ \ if (data.size()) { \ std::istringstream is (data); \ \ if (values.size() == 0) { \ \ std::vector<TYPE> x; \ if (def_values.size()) \ x = def_values; \ else \ x.assign (1, TYPE()); \ \ for (size_t i = 0; \ ( is >> x[ ((i<x.size()) ? i : x.size()-1) ] ); \ i++) { \ values.push_back (x[ ((i<x.size()) ? i : x.size()-1) ]); \ } \ \ } else { \ \ size_t i = 0; \ for ( ; i < values.size(); i++) { \ TYPE x (values[i]); \ if (is >> x) \ values[i] = x; \ else \ cvm::fatal_error ("Error: in parsing \""+ \ std::string (key)+"\".\n"); \ } \ } \ \ if (parse_mode != parse_silent) { \ cvm::log ("# "+std::string (key)+" = "+ \ cvm::to_str (values)+"\n"); \ } \ \ } else { \ \ if (b_found_any) \ cvm::fatal_error ("Error: improper or missing values for \""+ \ std::string (key)+"\".\n"); \ \ for (size_t i = 0; i < values.size(); i++) \ values[i] = def_values[ (i > def_values.size()) ? 0 : i ]; \ \ if (parse_mode != parse_silent) { \ cvm::log ("# "+std::string (key)+" = "+ \ cvm::to_str (def_values)+" [default]\n"); \ } \ } \ \ return b_found_any; \ } // single-value keyword parsers _get_keyval_scalar_ (int); _get_keyval_scalar_ (size_t); _get_keyval_scalar_string_ (std::string); _get_keyval_scalar_ (cvm::real); _get_keyval_scalar_ (cvm::rvector); _get_keyval_scalar_ (cvm::quaternion); _get_keyval_scalar_ (colvarvalue); // multiple-value keyword parsers _get_keyval_vector_ (int); _get_keyval_vector_ (size_t); _get_keyval_vector_ (std::string); _get_keyval_vector_ (cvm::real); _get_keyval_vector_ (cvm::rvector); _get_keyval_vector_ (cvm::quaternion); _get_keyval_vector_ (colvarvalue); -bool colvarparse::get_keyval (std::string const &conf, - char const *key, - bool &value, - bool const &def_value, - Parse_Mode const parse_mode) +bool colvarparse::get_keyval (std::string const &conf, + char const *key, + bool &value, + bool const &def_value, + Parse_Mode const parse_mode) { std::string data; bool b_found = false, b_found_any = false; size_t save_pos = 0, found_count = 0; do { std::string data_this = ""; b_found = key_lookup (conf, key, data_this, save_pos); if (b_found) { if (!b_found_any) b_found_any = true; found_count++; data = data_this; } } while (b_found); if (found_count > 1) cvm::log ("Warning: found more than one instance of \""+ std::string (key)+"\".\n"); if (data.size()) { std::istringstream is (data); if ( (data == std::string ("on")) || (data == std::string ("yes")) || (data == std::string ("true")) ) { value = true; } else if ( (data == std::string ("off")) || (data == std::string ("no")) || (data == std::string ("false")) ) { value = false; } else - cvm::fatal_error ("Error: boolean values only are allowed " + cvm::fatal_error ("Error: boolean values only are allowed " "for \""+std::string (key)+"\".\n"); if (parse_mode != parse_silent) { cvm::log ("# "+std::string (key)+" = "+ (value ? "on" : "off")+"\n"); } } else { if (b_found_any) { if (parse_mode != parse_silent) { cvm::log ("# "+std::string (key)+" = on\n"); } value = true; } else { value = def_value; if (parse_mode != parse_silent) { cvm::log ("# "+std::string (key)+" = "+ (def_value ? "on" : "off")+" [default]\n"); } } } return b_found_any; } void colvarparse::add_keyword (char const *key) { for (std::list<std::string>::iterator ki = allowed_keywords.begin(); ki != allowed_keywords.end(); ki++) { if (to_lower_cppstr (std::string (key)) == *ki) return; } // not found in the list // if (cvm::debug()) // cvm::log ("Registering a new keyword, \""+std::string (key)+"\".\n"); allowed_keywords.push_back (to_lower_cppstr (std::string (key))); } void colvarparse::strip_values (std::string &conf) { size_t offset = 0; data_begin_pos.sort(); data_end_pos.sort(); std::list<size_t>::iterator data_begin = data_begin_pos.begin(); std::list<size_t>::iterator data_end = data_end_pos.begin(); for ( ; (data_begin != data_begin_pos.end()) && (data_end != data_end_pos.end()) ; data_begin++, data_end++) { // std::cerr << "data_begin, data_end " // << *data_begin << ", " << *data_end // << "\n"; size_t const nchars = *data_end-*data_begin; // std::cerr << "conf[data_begin:data_end] = \"" // << std::string (conf, *data_begin - offset, nchars) // << "\"\n"; conf.erase (*data_begin - offset, nchars); offset += nchars; // std::cerr << ("Stripped config = \"\n"+conf+"\"\n"); } } void colvarparse::check_keywords (std::string &conf, char const *key) { if (cvm::debug()) cvm::log ("Configuration string for \""+std::string (key)+ "\": \"\n"+conf+"\".\n"); strip_values (conf); // after stripping, the config string has either empty lines, or // lines beginning with a keyword std::string line; std::istringstream is (conf); while (std::getline (is, line)) { if (line.size() == 0) continue; if (line.find_first_not_of (white_space) == std::string::npos) continue; std::string uk; std::istringstream line_is (line); line_is >> uk; // if (cvm::debug()) // cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line); uk = to_lower_cppstr (uk); bool found_keyword = false; for (std::list<std::string>::iterator ki = allowed_keywords.begin(); ki != allowed_keywords.end(); ki++) { if (uk == *ki) { found_keyword = true; break; } } if (!found_keyword) cvm::fatal_error ("Error: keyword \""+uk+"\" is not supported, " "or not recognized in this context.\n"); } } std::istream & colvarparse::getline_nocomments (std::istream &is, std::string &line, char const delim) { std::getline (is, line, delim); size_t const comment = line.find ('#'); if (comment != std::string::npos) { line.erase (comment); } return is; } bool colvarparse::key_lookup (std::string const &conf, char const *key_in, std::string &data, size_t &save_pos) { // add this keyword to the register (in its camelCase version) add_keyword (key_in); // use the lowercase version from now on std::string const key (to_lower_cppstr (key_in)); // "conf_lower" is only used to lookup the keyword, but its value // will be read from "conf", in order not to mess up file names std::string const conf_lower (to_lower_cppstr (conf)); // by default, there is no value, unless we found one data = ""; // when the function is invoked without save_pos, ensure that we // start from zero colvarparse::dummy_pos = 0; // start from the first occurrence of key - size_t pos = conf_lower.find (key, save_pos); - + size_t pos = conf_lower.find (key, save_pos); + // iterate over all instances until it finds the isolated keyword while (true) { if (pos == std::string::npos) { // no valid instance of the keyword has been found return false; } bool b_isolated_left = true, b_isolated_right = true; if (pos > 0) { if ( std::string ("\n"+white_space+ "}").find (conf[pos-1]) == std::string::npos ) { - // none of the valid delimiting characters is on the left of key + // none of the valid delimiting characters is on the left of key b_isolated_left = false; } } if (pos < conf.size()-key.size()-1) { if ( std::string ("\n"+white_space+ "{").find (conf[pos+key.size()]) == std::string::npos ) { - // none of the valid delimiting characters is on the right of key + // none of the valid delimiting characters is on the right of key b_isolated_right = false; } } // check that there are matching braces between here and the end of conf bool const b_not_within_block = brace_check (conf, pos); bool const b_isolated = (b_isolated_left && b_isolated_right && b_not_within_block); - + if (b_isolated) { // found it break; } else { // try the next occurrence of key pos = conf_lower.find (key, pos+key.size()); } } // check it is not between quotes // if ( (conf.find_last_of ("\"", // conf.find_last_of (white_space, pos)) != // std::string::npos) && // (conf.find_first_of ("\"", // conf.find_first_of (white_space, pos)) != // std::string::npos) ) // return false; // save the pointer for a future call (when iterating over multiple // valid instances of the same keyword) save_pos = pos + key.size(); // get the remainder of the line size_t pl = conf.rfind ("\n", pos); size_t line_begin = (pl == std::string::npos) ? 0 : pos; size_t nl = conf.find ("\n", pos); size_t line_end = (nl == std::string::npos) ? conf.size() : nl; - std::string line (conf, line_begin, (line_end-line_begin)); + std::string line (conf, line_begin, (line_end-line_begin)); size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size(); data_begin = line.find_first_not_of (white_space, data_begin+1); // size_t data_begin_absolute = data_begin + line_begin; // size_t data_end_absolute = data_begin; if (data_begin != std::string::npos) { size_t data_end = line.find_last_not_of (white_space) + 1; data_end = (data_end == std::string::npos) ? line.size() : data_end; // data_end_absolute = data_end + line_begin; if (line.find ('{', data_begin) != std::string::npos) { size_t brace_count = 1; size_t brace = line.find ('{', data_begin); // start from the first opening brace while (brace_count > 0) { // find the matching closing brace brace = line.find_first_of ("{}", brace+1); while (brace == std::string::npos) { // add a new line if (line_end >= conf.size()) { cvm::fatal_error ("Parse error: reached the end while " "looking for closing brace; until now " "the following was parsed: \"\n"+ line+"\".\n"); return false; } size_t const old_end = line.size(); // data_end_absolute += old_end+1; line_begin = line_end; nl = conf.find ('\n', line_begin+1); - if (nl == std::string::npos) + if (nl == std::string::npos) line_end = conf.size(); - else + else line_end = nl; line.append (conf, line_begin, (line_end-line_begin)); brace = line.find_first_of ("{}", old_end); } if (line[brace] == '{') brace_count++; if (line[brace] == '}') brace_count--; } // set data_begin after the opening brace data_begin = line.find_first_of ('{', data_begin) + 1; data_begin = line.find_first_not_of (white_space, data_begin); // set data_end before the closing brace data_end = brace; data_end = line.find_last_not_of (white_space+"}", data_end) + 1; // data_end_absolute = line_end; if (data_end > line.size()) data_end = line.size(); } data.append (line, data_begin, (data_end-data_begin)); if (data.size() && save_delimiters) { data_begin_pos.push_back (conf.find (data, pos+key.size())); data_end_pos.push_back (data_begin_pos.back()+data.size()); // std::cerr << "key = " << key << ", data = \"" // << data << "\", data_begin, data_end = " // << data_begin_pos.back() << ", " << data_end_pos.back() // << "\n"; } } - + save_pos = line_end; return true; } std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb) { size_t start_pos = is.tellg(); std::string read_key, next; if ( !(is >> read_key) || !(read_key == rb.key) || !(is >> next) ) { // the requested keyword has not been found, or it is not possible // to read data after it is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); return is; } if (next != "{") { (*rb.data) = next; return is; } size_t brace_count = 1; std::string line; while (colvarparse::getline_nocomments (is, line)) { size_t br = 0, br_old; while ( (br = line.find_first_of ("{}", br)) != std::string::npos) { if (line[br] == '{') brace_count++; if (line[br] == '}') brace_count--; br_old = br; br++; } if (brace_count) (*rb.data).append (line + "\n"); else { (*rb.data).append (line, 0, br_old); break; } } if (brace_count) { // end-of-file reached // restore initial position is.clear(); is.seekg (start_pos, std::ios::beg); is.setstate (std::ios::failbit); } return is; } bool colvarparse::brace_check (std::string const &conf, size_t const start_pos) { size_t brace_count = 0; size_t brace = start_pos; while ( (brace = conf.find_first_of ("{}", brace)) != std::string::npos) { if (conf[brace] == '{') brace_count++; if (conf[brace] == '}') brace_count--; brace++; } if (brace_count != 0) return false; else return true; } // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvarparse.h b/lib/colvars/colvarparse.h index e7e1d9627..d3b3968a7 100644 --- a/lib/colvars/colvarparse.h +++ b/lib/colvars/colvarparse.h @@ -1,201 +1,201 @@ #ifndef COLVARPARSE_H #define COLVARPARSE_H #include <cstring> #include <string> #include "colvarmodule.h" #include "colvarvalue.h" /// \file colvarparse.h Parsing functions for collective variables /// \brief Base class containing parsing functions; all objects which /// need to parse input inherit from this class colvarparse { protected: /// \brief List of legal keywords for this object: this is updated /// by each call to colvarparse::get_keyval() or /// colvarparse::key_lookup() std::list<std::string> allowed_keywords; /// \brief List of delimiters for the values of each keyword in the /// configuration string; all keywords will be stripped of their /// values before the keyword check is performed std::list<size_t> data_begin_pos; /// \brief List of delimiters for the values of each keyword in the /// configuration string; all keywords will be stripped of their /// values before the keyword check is performed std::list<size_t> data_end_pos; /// \brief Whether or not to accumulate data_begin_pos and /// data_end_pos in key_lookup(); it may be useful to disable /// this after the constructor is called, because other files may be /// read (e.g. restart) that would mess up with the registry; in any /// case, nothing serious happens until check_keywords() is invoked /// (which should happen only right after construction) bool save_delimiters; /// \brief Add a new valid keyword to the list void add_keyword (char const *key); /// \brief Remove all the values from the config string void strip_values (std::string &conf); public: inline colvarparse() : save_delimiters (true) {} /// How a keyword is parsed in a string - enum Parse_Mode { + enum Parse_Mode { /// \brief (default) Read the first instance of a keyword (if /// any), report its value, and print a warning when there is more /// than one - parse_normal, + parse_normal, /// \brief Like parse_normal, but don't send any message to the log /// (useful e.g. in restart files when such messages are very /// numerous and redundant) - parse_silent + parse_silent }; /// \fn get_keyval bool const get_keyval (std::string const &conf, /// char const *key, _type_ &value, _type_ const &def_value, /// Parse_Mode const parse_mode) \brief Helper function to parse /// keywords in the configuration and get their values /// /// In normal circumstances, you should use either version the /// get_keyval function. Both of them look for the C string "key" /// in the C++ string "conf", and assign the corresponding value (if /// available) to the variable "value" (first version), or assign as /// many values as found to the vector "values" (second version). /// /// If "key" is found but no value is associated to it, the default /// value is provided (either one copy or as many copies as the /// current length of the vector "values" specifies). A message /// will print, unless parse_mode is equal to parse_silent. The /// return value of both forms of get_keyval is true if "key" is /// found (with or without value), and false when "key" is absent in /// the string "conf". If there is more than one instance of the /// keyword, a warning will be raised; instead, to loop over /// multiple instances key_lookup() should be invoked directly. /// /// If you introduce a new data type, add two new instances of this /// functions, or insert this type in the \link colvarvalue \endlink /// wrapper class (colvarvalue.h). #define _get_keyval_scalar_proto_(_type_,_def_value_) \ bool get_keyval (std::string const &conf, \ char const *key, \ _type_ &value, \ _type_ const &def_value = _def_value_, \ Parse_Mode const parse_mode = parse_normal) _get_keyval_scalar_proto_ (int, (int)0); _get_keyval_scalar_proto_ (size_t, (size_t)0); _get_keyval_scalar_proto_ (std::string, std::string ("")); _get_keyval_scalar_proto_ (cvm::real, (cvm::real)0.0); _get_keyval_scalar_proto_ (cvm::rvector, cvm::rvector()); _get_keyval_scalar_proto_ (cvm::quaternion, cvm::quaternion()); _get_keyval_scalar_proto_ (colvarvalue, colvarvalue (colvarvalue::type_notset)); _get_keyval_scalar_proto_ (bool, false); #define _get_keyval_vector_proto_(_type_,_def_value_) \ bool get_keyval (std::string const &conf, \ char const *key, \ std::vector<_type_> &values, \ std::vector<_type_> const &def_values = \ std::vector<_type_> (0, static_cast<_type_>(_def_value_)), \ Parse_Mode const parse_mode = parse_normal) - + _get_keyval_vector_proto_ (int, 0); _get_keyval_vector_proto_ (size_t, 0); _get_keyval_vector_proto_ (std::string, std::string ("")); _get_keyval_vector_proto_ (cvm::real, 0.0); _get_keyval_vector_proto_ (cvm::rvector, cvm::rvector()); _get_keyval_vector_proto_ (cvm::quaternion, cvm::quaternion()); _get_keyval_vector_proto_ (colvarvalue, colvarvalue (colvarvalue::type_notset)); /// \brief Check that all the keywords within "conf" are in the list /// of allowed keywords; this will invoke strip_values() first and /// then loop over all words void check_keywords (std::string &conf, char const *key); /// \brief Return a lowercased copy of the string static inline std::string to_lower_cppstr (std::string const &in) { std::string out = ""; for (size_t i = 0; i < in.size(); i++) { out.append (1, (char) ::tolower (in[i]) ); } return out; } /// \brief Helper class to read a block of the type "key { ... }" /// from a stream and store it in a string /// /// Useful on restarts, where the file is too big to be loaded in a /// string by key_lookup; it can only check that the keyword is /// correct and the block is properly delimited by braces, not /// skipping other blocks class read_block { std::string const key; std::string * const data; public: inline read_block (std::string const &key_in, std::string &data_in) : key (key_in), data (&data_in) {} inline ~read_block() {} friend std::istream & operator >> (std::istream &is, read_block const &rb); }; /// Accepted white space delimiters, used in key_lookup() static std::string const white_space; /// \brief Low-level function for parsing configuration strings; /// automatically adds the requested keywords to the list of valid /// ones. \param conf the content of the configuration file or one /// of its blocks \param key the keyword to search in "conf" \param /// data (optional) holds the string provided after "key", if any /// \param save_pos (optional) stores the position of the keyword /// within "conf", useful when doing multiple calls \param - /// save_delimiters (optional) + /// save_delimiters (optional) bool key_lookup (std::string const &conf, char const *key, std::string &data = dummy_string, size_t &save_pos = dummy_pos); /// Used as a default argument by key_lookup static std::string dummy_string; /// Used as a default argument by key_lookup static size_t dummy_pos; /// \brief Works as std::getline() but also removes everything /// between a comment character and the following newline static std::istream & getline_nocomments (std::istream &is, std::string &s, char const delim = '\n'); /// Check if the content of the file has matching braces bool brace_check (std::string const &conf, size_t const start_pos = 0); }; #endif // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvarproxy.h b/lib/colvars/colvarproxy.h index 43f3233de..c1416f13b 100644 --- a/lib/colvars/colvarproxy.h +++ b/lib/colvars/colvarproxy.h @@ -1,153 +1,153 @@ #ifndef COLVARPROXY_H #define COLVARPROXY_H #include "colvarmodule.h" /// \brief Interface class between the collective variables module and /// the simulation program class colvarproxy { public: /// Pointer to the instance of colvarmodule colvarmodule *colvars; /// Default destructor virtual inline ~colvarproxy() {} // **************** SYSTEM-WIDE PHYSICAL QUANTITIES **************** /// \brief Value of the unit for atomic coordinates with respect to /// angstroms (used by some variables for hard-coded default values) virtual cvm::real unit_angstrom() = 0; /// \brief Boltzmann constant virtual cvm::real boltzmann() = 0; /// \brief Temperature of the simulation (K) virtual cvm::real temperature() = 0; /// \brief Time step of the simulation (fs) virtual cvm::real dt() = 0; /// \brief Pseudo-random number with Gaussian distribution virtual cvm::real rand_gaussian (void) = 0; // **************** SIMULATION PARAMETERS **************** /// \brief Prefix to be used for input files (restarts, not /// configuration) virtual std::string input_prefix() = 0; /// \brief Prefix to be used for output restart files virtual std::string restart_output_prefix() = 0; /// \brief Prefix to be used for output files (final system /// configuration) virtual std::string output_prefix() = 0; /// \brief Restarts will be fritten each time this number of steps has passed virtual size_t restart_frequency() = 0; // **************** ACCESS ATOMIC DATA **************** /// Pass restraint energy value for current timestep to MD engine virtual void add_energy (cvm::real energy) = 0; /// Tell the proxy whether system forces are needed (may not always be available) virtual void request_system_force (bool yesno) = 0; // **************** PERIODIC BOUNDARY CONDITIONS **************** /// \brief Get the PBC-aware distance vector between two positions virtual cvm::rvector position_distance (cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) = 0; /// \brief Get the PBC-aware square distance between two positions; /// may be implemented independently from position_distance() for optimization purposes virtual cvm::real position_dist2 (cvm::atom_pos const &pos1, cvm::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 The reference position + /// \param ref_pos The reference position virtual void select_closest_image (cvm::atom_pos &pos, cvm::atom_pos const &ref_pos) = 0; /// \brief Perform select_closest_image() on a set of atomic positions /// /// After that, distance vectors can then be calculated directly, /// without using position_distance() void select_closest_images (std::vector<cvm::atom_pos> &pos, cvm::atom_pos const &ref_pos); // **************** INPUT/OUTPUT **************** /// Print a message to the main log virtual void log (std::string const &message) = 0; /// Print a message to the main log and exit with error code virtual void fatal_error (std::string const &message) = 0; /// Print a message to the main log and exit normally virtual void exit (std::string const &message) = 0; /// \brief Read atom identifiers from a file \param filename name of /// the file (usually a PDB) \param atoms array to which atoms read /// from "filename" will be appended \param pdb_field (optiona) if /// "filename" is a PDB file, use this field to determine which are /// the atoms to be set virtual void load_atoms (char const *filename, std::vector<cvm::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); if "pos" is already allocated, the number of its /// elements must match the number of atoms in "filename" virtual void load_coords (char const *filename, std::vector<cvm::atom_pos> &pos, const std::vector<int> &indices, std::string const pdb_field, double const pdb_field_value = 0.0) = 0; /// \brief Rename the given file, before overwriting it virtual void backup_file (char const *filename) {} }; inline void colvarproxy::select_closest_images (std::vector<cvm::atom_pos> &pos, cvm::atom_pos const &ref_pos) { for (std::vector<cvm::atom_pos>::iterator pi = pos.begin(); pi != pos.end(); pi++) { select_closest_image (*pi, ref_pos); } } inline cvm::real colvarproxy::position_dist2 (cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) { return (position_distance (pos1, pos2)).norm2(); } #endif // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvartypes.h b/lib/colvars/colvartypes.h index fa18310ac..4c285631a 100644 --- a/lib/colvars/colvartypes.h +++ b/lib/colvars/colvartypes.h @@ -1,1098 +1,1098 @@ #ifndef COLVARTYPES_H #define COLVARTYPES_H #include <cmath> #ifndef PI #define PI 3.14159265358979323846 #endif // ---------------------------------------------------------------------- /// Linear algebra functions and data types used in the collective /// variables implemented so far // ---------------------------------------------------------------------- /// 1-dimensional vector of real numbers with three components class colvarmodule::rvector { public: cvm::real x, y, z; - + inline rvector() : x (0.0), y (0.0), z (0.0) {} inline rvector (cvm::real const &x_i, cvm::real const &y_i, cvm::real const &z_i) : x (x_i), y (y_i), z (z_i) {} inline rvector (cvm::real v) : x (v), y (v), z (v) {} /// \brief Set all components to a scalar value inline void set (cvm::real const value = 0.0) { x = y = z = value; } /// \brief Set all components to zero inline void reset() { x = y = z = 0.0; } /// \brief Access cartesian components by index inline cvm::real & operator [] (int const &i) { return (i == 0) ? x : (i == 1) ? y : (i == 2) ? z : x; } /// \brief Access cartesian components by index inline cvm::real const & operator [] (int const &i) const { return (i == 0) ? x : (i == 1) ? y : (i == 2) ? z : x; } - inline cvm::rvector & operator = (cvm::real const &v) + inline cvm::rvector & operator = (cvm::real const &v) { x = v; y = v; z = v; return *this; } - inline void operator += (cvm::rvector const &v) + inline void operator += (cvm::rvector const &v) { x += v.x; y += v.y; z += v.z; } - inline void operator -= (cvm::rvector const &v) + inline void operator -= (cvm::rvector const &v) { x -= v.x; y -= v.y; z -= v.z; } - inline void operator *= (cvm::real const &v) + inline void operator *= (cvm::real const &v) { x *= v; y *= v; z *= v; } - inline void operator /= (cvm::real const& v) + inline void operator /= (cvm::real const& v) { x /= v; y /= v; z /= v; } inline cvm::real norm2() const { return (x*x + y*y + z*z); } inline cvm::real norm() const { return std::sqrt (this->norm2()); } inline cvm::rvector unit() const { const cvm::real n = this->norm(); return (n > 0. ? cvm::rvector (x, y, z)/n : cvm::rvector (1., 0., 0.)); } static inline size_t output_width (size_t const &real_width) { return 3*real_width + 10; } - static inline cvm::rvector outer (cvm::rvector const &v1, cvm::rvector const &v2) + static inline cvm::rvector outer (cvm::rvector const &v1, cvm::rvector const &v2) { return cvm::rvector ( v1.y*v2.z - v2.y*v1.z, -v1.x*v2.z + v2.x*v1.z, v1.x*v2.y - v2.x*v1.y); } - friend inline cvm::rvector operator - (cvm::rvector const &v) + friend inline cvm::rvector operator - (cvm::rvector const &v) { return cvm::rvector (-v.x, -v.y, -v.z); } - friend inline int operator == (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline int operator == (cvm::rvector const &v1, cvm::rvector const &v2) { return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); } - friend inline int operator != (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline int operator != (cvm::rvector const &v1, cvm::rvector const &v2) { return (v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z); } - friend inline cvm::rvector operator + (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline cvm::rvector operator + (cvm::rvector const &v1, cvm::rvector const &v2) { return cvm::rvector (v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); } - friend inline cvm::rvector operator - (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline cvm::rvector operator - (cvm::rvector const &v1, cvm::rvector const &v2) { return cvm::rvector (v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); } - friend inline cvm::real operator * (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline cvm::real operator * (cvm::rvector const &v1, cvm::rvector const &v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } - friend inline cvm::rvector operator * (cvm::real const &a, cvm::rvector const &v) + friend inline cvm::rvector operator * (cvm::real const &a, cvm::rvector const &v) { return cvm::rvector (a*v.x, a*v.y, a*v.z); } - friend inline cvm::rvector operator * (cvm::rvector const &v, cvm::real const &a) + friend inline cvm::rvector operator * (cvm::rvector const &v, cvm::real const &a) { return cvm::rvector (a*v.x, a*v.y, a*v.z); } - friend inline cvm::rvector operator / (cvm::rvector const &v, cvm::real const &a) + friend inline cvm::rvector operator / (cvm::rvector const &v, cvm::real const &a) { return cvm::rvector (v.x/a, v.y/a, v.z/a); } - + }; /// \brief Arbitrary size array (one dimensions) suitable for linear /// algebra operations (i.e. for floating point numbers it can be used /// with library functions) template <class T, size_t const length> class colvarmodule::vector1d { protected: /// Underlying C-array T *array; public: /// Length of the array inline size_t size() { return length; } - + /// Default constructor inline vector1d (T const &t = T()) { array = new T[length]; reset(); } /// Set all elements to zero inline void reset() { for (size_t i = 0; i < length; i++) { array[i] = T (0.0); } } /// Constructor from a 1-d C array inline vector1d (T const *v) { array = new T[length]; for (size_t i = 0; i < length; i++) { array[i] = v[i]; } } /// Copy constructor inline vector1d (vector1d<T, length> const &v) { array = new T[length]; for (size_t i = 0; i < length; i++) { array[i] = v.array[i]; } } /// Assignment inline vector1d<T, length> & operator = (vector1d<T, length> const &v) { for (size_t i = 0; i < length; i++) { this->array[i] = v.array[i]; } return *this; } /// Destructor inline ~vector1d() { delete [] array; } /// Return the 1-d C array inline T *c_array() { return array; } /// Return the 1-d C array inline operator T *() { return array; } /// Inner product inline friend T operator * (vector1d<T, length> const &v1, vector1d<T, length> const &v2) { T prod (0.0); for (size_t i = 0; i < length; i++) { prod += v1.array[i] * v2.array[i]; } return prod; } - /// Formatted output + /// Formatted output friend std::ostream & operator << (std::ostream &os, vector1d<T, length> const &v) { std::streamsize const w = os.width(); std::streamsize const p = os.precision(); os << "( "; for (size_t i = 0; i < length-1; i++) { os.width (w); os.precision (p); os << v.array[i] << " , "; } os.width (w); os.precision (p); os << v.array[length-1] << " )"; return os; } }; /// \brief Arbitrary size array (two dimensions) suitable for linear /// algebra operations (i.e. for floating point numbers it can be used /// with library functions) template <class T, size_t const outer_length, size_t const inner_length> class colvarmodule::matrix2d { protected: /// Underlying C array T **array; public: /// Allocation routine, used by all constructors inline void alloc() { array = new T * [outer_length]; for (size_t i = 0; i < outer_length; i++) { array[i] = new T [inner_length]; } } /// Set all elements to zero inline void reset() { for (size_t i = 0; i < outer_length; i++) { for (size_t j = 0; j < inner_length; j++) { array[i][j] = T (0.0); } } } /// Default constructor inline matrix2d() { this->alloc(); reset(); } - + /// Constructor from a 2-d C array inline matrix2d (T const **m) { this->alloc(); for (size_t i = 0; i < outer_length; i++) { for (size_t j = 0; j < inner_length; j++) array[i][j] = m[i][j]; } } /// Copy constructor inline matrix2d (matrix2d<T, outer_length, inner_length> const &m) { this->alloc(); for (size_t i = 0; i < outer_length; i++) { for (size_t j = 0; j < inner_length; j++) this->array[i][j] = m.array[i][j]; } } /// Assignment inline matrix2d<T, outer_length, inner_length> & operator = (matrix2d<T, outer_length, inner_length> const &m) { for (size_t i = 0; i < outer_length; i++) { for (size_t j = 0; j < inner_length; j++) this->array[i][j] = m.array[i][j]; } return *this; } /// Destructor inline ~matrix2d() { for (size_t i = 0; i < outer_length; i++) { delete [] array[i]; } delete [] array; } /// Return the 2-d C array inline T **c_array() { return array; } /// Return the 2-d C array inline operator T **() { return array; } // /// Matrix addi(c)tion // inline friend matrix2d<T, outer_length, inner_length> // operator + (matrix2d<T, outer_length, inner_length> const &mat1, // matrix2d<T, outer_length, inner_length> const &mat2) { // matrix2d<T, outer_length, inner_length> sum; // for (size_t i = 0; i < outer_length; i++) { // for (size_t j = 0; j < inner_length; j++) { // sum[i][j] = mat1[i][j] + mat2[i][j]; // } // } // } // /// Matrix subtraction // inline friend matrix2d<T, outer_length, inner_length> // operator - (matrix2d<T, outer_length, inner_length> const &mat1, // matrix2d<T, outer_length, inner_length> const &mat2) { // matrix2d<T, outer_length, inner_length> sum; // for (size_t i = 0; i < outer_length; i++) { // for (size_t j = 0; j < inner_length; j++) { // sum[i][j] = mat1[i][j] - mat2[i][j]; // } // } // } - /// Formatted output + /// Formatted output friend std::ostream & operator << (std::ostream &os, matrix2d<T, outer_length, inner_length> const &m) { std::streamsize const w = os.width(); std::streamsize const p = os.precision(); os << "("; for (size_t i = 0; i < outer_length; i++) { os << " ( "; for (size_t j = 0; j < inner_length-1; j++) { os.width (w); os.precision (p); os << m.array[i][j] << " , "; } os.width (w); os.precision (p); os << m.array[i][inner_length-1] << " )"; } os << " )"; return os; } }; /// \brief 2-dimensional array of real numbers with three components /// along each dimension (works with colvarmodule::rvector) class colvarmodule::rmatrix : public colvarmodule::matrix2d<colvarmodule::real, 3, 3> { private: public: /// Return the xx element inline cvm::real & xx() { return array[0][0]; } /// Return the xy element inline cvm::real & xy() { return array[0][1]; } /// Return the xz element inline cvm::real & xz() { return array[0][2]; } /// Return the yx element inline cvm::real & yx() { return array[1][0]; } /// Return the yy element inline cvm::real & yy() { return array[1][1]; } /// Return the yz element inline cvm::real & yz() { return array[1][2]; } /// Return the zx element inline cvm::real & zx() { return array[2][0]; } /// Return the zy element inline cvm::real & zy() { return array[2][1]; } /// Return the zz element inline cvm::real & zz() { return array[2][2]; } /// Return the xx element inline cvm::real xx() const { return array[0][0]; } /// Return the xy element inline cvm::real xy() const { return array[0][1]; } /// Return the xz element inline cvm::real xz() const { return array[0][2]; } /// Return the yx element inline cvm::real yx() const { return array[1][0]; } /// Return the yy element inline cvm::real yy() const { return array[1][1]; } /// Return the yz element inline cvm::real yz() const { return array[1][2]; } /// Return the zx element inline cvm::real zx() const { return array[2][0]; } /// Return the zy element inline cvm::real zy() const { return array[2][1]; } /// Return the zz element inline cvm::real zz() const { return array[2][2]; } /// Constructor from a 2-d C array - inline rmatrix (cvm::real const **m) - : cvm::matrix2d<cvm::real, 3, 3> (m) + inline rmatrix (cvm::real const **m) + : cvm::matrix2d<cvm::real, 3, 3> (m) {} /// Default constructor - inline rmatrix() + inline rmatrix() : cvm::matrix2d<cvm::real, 3, 3>() {} - /// Constructor component by component + /// Constructor component by component inline rmatrix (cvm::real const &xxi, cvm::real const &xyi, cvm::real const &xzi, cvm::real const &yxi, cvm::real const &yyi, cvm::real const &yzi, cvm::real const &zxi, cvm::real const &zyi, - cvm::real const &zzi) + cvm::real const &zzi) : cvm::matrix2d<cvm::real, 3, 3>() { this->xx() = xxi; this->xy() = xyi; this->xz() = xzi; this->yx() = yxi; this->yy() = yyi; this->yz() = yzi; this->zx() = zxi; this->zy() = zyi; this->zz() = zzi; } /// Destructor inline ~rmatrix() - {} + {} /// Return the determinant inline cvm::real determinant() const { return ( xx() * (yy()*zz() - zy()*yz())) - (yx() * (xy()*zz() - zy()*xz())) + (zx() * (xy()*yz() - yy()*xz())); } inline cvm::rmatrix transpose() const { return cvm::rmatrix (this->xx(), this->yx(), this->zx(), this->xy(), this->yy(), this->zy(), this->xz(), this->yz(), this->zz()); } friend cvm::rvector operator * (cvm::rmatrix const &m, cvm::rvector const &r); // friend inline cvm::rmatrix const operator * (cvm::rmatrix const &m1, cvm::rmatrix const &m2) { // return cvm::rmatrix (m1.xx()*m2.xx() + m1.xy()*m2.yx() + m1.xz()*m2.yz(), // m1.xx()*m2.xy() + m1.xy()*m2.yy() + m1.xz()*m2.zy(), // m1.xx()*m2.xz() + m1.xy()*m2.yz() + m1.xz()*m2.zz(), // m1.yx()*m2.xx() + m1.yy()*m2.yx() + m1.yz()*m2.yz(), // m1.yx()*m2.xy() + m1.yy()*m2.yy() + m1.yz()*m2.yy(), // m1.yx()*m2.xz() + m1.yy()*m2.yz() + m1.yz()*m2.yz(), // m1.zx()*m2.xx() + m1.zy()*m2.yx() + m1.zz()*m2.yz(), // m1.zx()*m2.xy() + m1.zy()*m2.yy() + m1.zz()*m2.yy(), // m1.zx()*m2.xz() + m1.zy()*m2.yz() + m1.zz()*m2.yz()); // } }; - + inline cvm::rvector operator * (cvm::rmatrix const &m, cvm::rvector const &r) { return cvm::rvector (m.xx()*r.x + m.xy()*r.y + m.xz()*r.z, m.yx()*r.x + m.yy()*r.y + m.yz()*r.z, m.zx()*r.x + m.zy()*r.y + m.zz()*r.z); } /// Numerical recipes diagonalization void jacobi (cvm::real **a, int n, cvm::real d[], cvm::real **v, int *nrot); /// Eigenvector sort void eigsrt (cvm::real d[], cvm::real **v, int n); /// Transpose the matrix void transpose (cvm::real **v, int n); /// \brief 1-dimensional vector of real numbers with four components and /// a quaternion algebra class colvarmodule::quaternion { public: cvm::real q0, q1, q2, q3; /// Constructor from a 3-d vector inline quaternion (cvm::real const &x, cvm::real const &y, cvm::real const &z) : q0 (0.0), q1 (x), q2 (y), q3 (z) {} /// Constructor component by component inline quaternion (cvm::real const qv[4]) : q0 (qv[0]), q1 (qv[1]), q2 (qv[2]), q3 (qv[3]) {} /// Constructor component by component inline quaternion (cvm::real const &q0i, cvm::real const &q1i, cvm::real const &q2i, cvm::real const &q3i) : q0 (q0i), q1 (q1i), q2 (q2i), q3 (q3i) {} /// "Constructor" after Euler angles (in radians) /// /// http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles inline void set_from_euler_angles (cvm::real const &phi_in, cvm::real const &theta_in, cvm::real const &psi_in) { q0 = ( (std::cos (phi_in/2.0)) * (std::cos (theta_in/2.0)) * (std::cos (psi_in/2.0)) + (std::sin (phi_in/2.0)) * (std::sin (theta_in/2.0)) * (std::sin (psi_in/2.0)) ); q1 = ( (std::sin (phi_in/2.0)) * (std::cos (theta_in/2.0)) * (std::cos (psi_in/2.0)) - (std::cos (phi_in/2.0)) * (std::sin (theta_in/2.0)) * (std::sin (psi_in/2.0)) ); q2 = ( (std::cos (phi_in/2.0)) * (std::sin (theta_in/2.0)) * (std::cos (psi_in/2.0)) + (std::sin (phi_in/2.0)) * (std::cos (theta_in/2.0)) * (std::sin (psi_in/2.0)) ); q3 = ( (std::cos (phi_in/2.0)) * (std::cos (theta_in/2.0)) * (std::sin (psi_in/2.0)) - (std::sin (phi_in/2.0)) * (std::sin (theta_in/2.0)) * (std::cos (psi_in/2.0)) ); } /// \brief Default constructor inline quaternion() { reset(); } /// \brief Set all components to a scalar inline void set (cvm::real const value = 0.0) { q0 = q1 = q2 = q3 = value; } /// \brief Set all components to zero (null quaternion) inline void reset() { q0 = q1 = q2 = q3 = 0.0; } /// \brief Set the q0 component to 1 and the others to 0 (quaternion /// representing no rotation) inline void reset_rotation() { q0 = 1.0; q1 = q2 = q3 = 0.0; } /// Tell the number of characters required to print a quaternion, given that of a real number static inline size_t output_width (size_t const &real_width) { return 4*real_width + 13; } /// \brief Formatted output operator friend std::ostream & operator << (std::ostream &os, cvm::quaternion const &q); /// \brief Formatted input operator friend std::istream & operator >> (std::istream &is, cvm::quaternion &q); /// Access the quaternion as a 4-d array (return a reference) inline cvm::real & operator [] (int const &i) { switch (i) { case 0: return this->q0; case 1: return this->q1; case 2: return this->q2; case 3: return this->q3; default: cvm::fatal_error ("Error: incorrect quaternion component.\n"); return q0; } } /// Access the quaternion as a 4-d array (return a value) inline cvm::real operator [] (int const &i) const { switch (i) { case 0: return this->q0; case 1: return this->q1; case 2: return this->q2; case 3: return this->q3; default: cvm::fatal_error ("Error: trying to access a quaternion " "component which is not between 0 and 3.\n"); return this->q0; } } /// Square norm of the quaternion inline cvm::real norm2() const { return q0*q0 + q1*q1 + q2*q2 + q3*q3; } /// Norm of the quaternion inline cvm::real norm() const { return std::sqrt (this->norm2()); } - /// Return the conjugate quaternion + /// Return the conjugate quaternion inline cvm::quaternion conjugate() const { return cvm::quaternion (q0, -q1, -q2, -q3); } inline void operator *= (cvm::real const &a) { q0 *= a; q1 *= a; q2 *= a; q3 *= a; } inline void operator /= (cvm::real const &a) { q0 /= a; q1 /= a; q2 /= a; q3 /= a; } inline void set_positive() { if (q0 > 0.0) return; q0 = -q0; q1 = -q1; q2 = -q2; q3 = -q3; } inline void operator += (cvm::quaternion const &h) { q0+=h.q0; q1+=h.q1; q2+=h.q2; q3+=h.q3; } inline void operator -= (cvm::quaternion const &h) { q0-=h.q0; q1-=h.q1; q2-=h.q2; q3-=h.q3; } /// Promote a 3-vector to a quaternion static inline cvm::quaternion promote (cvm::rvector const &v) { return cvm::quaternion (0.0, v.x, v.y, v.z); } /// Return the vector component - inline cvm::rvector get_vector() const + inline cvm::rvector get_vector() const { return cvm::rvector (q1, q2, q3); } friend inline cvm::quaternion operator + (cvm::quaternion const &h, cvm::quaternion const &q) { return cvm::quaternion (h.q0+q.q0, h.q1+q.q1, h.q2+q.q2, h.q3+q.q3); } friend inline cvm::quaternion operator - (cvm::quaternion const &h, cvm::quaternion const &q) { return cvm::quaternion (h.q0-q.q0, h.q1-q.q1, h.q2-q.q2, h.q3-q.q3); } /// \brief Provides the quaternion product. \b NOTE: for the inner /// product use: \code h.inner (q); \endcode friend inline cvm::quaternion operator * (cvm::quaternion const &h, cvm::quaternion const &q) { return cvm::quaternion (h.q0*q.q0 - h.q1*q.q1 - h.q2*q.q2 - h.q3*q.q3, h.q0*q.q1 + h.q1*q.q0 + h.q2*q.q3 - h.q3*q.q2, h.q0*q.q2 + h.q2*q.q0 + h.q3*q.q1 - h.q1*q.q3, h.q0*q.q3 + h.q3*q.q0 + h.q1*q.q2 - h.q2*q.q1); } friend inline cvm::quaternion operator * (cvm::real const &c, cvm::quaternion const &q) { return cvm::quaternion (c*q.q0, c*q.q1, c*q.q2, c*q.q3); } friend inline cvm::quaternion operator * (cvm::quaternion const &q, cvm::real const &c) { return cvm::quaternion (q.q0*c, q.q1*c, q.q2*c, q.q3*c); } friend inline cvm::quaternion operator / (cvm::quaternion const &q, cvm::real const &c) { return cvm::quaternion (q.q0/c, q.q1/c, q.q2/c, q.q3/c); } /// \brief Rotate v through this quaternion (put it in the rotated /// reference frame) inline cvm::rvector rotate (cvm::rvector const &v) const { return ((*this) * promote (v) * ((*this).conjugate())).get_vector(); } /// \brief Rotate Q2 through this quaternion (put it in the rotated /// reference frame) inline cvm::quaternion rotate (cvm::quaternion const &Q2) const { cvm::rvector const vq_rot = this->rotate (Q2.get_vector()); return cvm::quaternion (Q2.q0, vq_rot.x, vq_rot.y, vq_rot.z); } /// Return the 3x3 matrix associated to this quaternion inline cvm::rmatrix rotation_matrix() const { cvm::rmatrix R; R.xx() = q0*q0 + q1*q1 - q2*q2 - q3*q3; R.yy() = q0*q0 - q1*q1 + q2*q2 - q3*q3; R.zz() = q0*q0 - q1*q1 - q2*q2 + q3*q3; R.xy() = 2.0 * (q1*q2 - q0*q3); R.xz() = 2.0 * (q0*q2 + q1*q3); R.yx() = 2.0 * (q0*q3 + q1*q2); R.yz() = 2.0 * (q2*q3 - q0*q1); R.zx() = 2.0 * (q1*q3 - q0*q2); R.zy() = 2.0 * (q0*q1 + q2*q3); return R; } /// \brief Multiply the given vector by the derivative of the given /// (rotated) position with respect to the quaternion cvm::quaternion position_derivative_inner (cvm::rvector const &pos, cvm::rvector const &vec) const; /// \brief Return the cosine between the orientation frame /// associated to this quaternion and another inline cvm::real cosine (cvm::quaternion const &q) const { cvm::real const iprod = this->inner (q); return 2.0*iprod*iprod - 1.0; } /// \brief Square distance from another quaternion on the /// 4-dimensional unit sphere: returns the square of the angle along /// the shorter of the two geodesics inline cvm::real dist2 (cvm::quaternion const &Q2) const { cvm::real const cos_omega = this->q0*Q2.q0 + this->q1*Q2.q1 + this->q2*Q2.q2 + this->q3*Q2.q3; cvm::real const omega = std::acos ( (cos_omega > 1.0) ? 1.0 : ( (cos_omega < -1.0) ? -1.0 : cos_omega) ); // get the minimum distance: x and -x are the same quaternion if (cos_omega > 0.0) return omega * omega; else return (PI-omega) * (PI-omega); } /// Gradient of the square distance: returns a 4-vector equivalent /// to the one provided by slerp inline cvm::quaternion dist2_grad (cvm::quaternion const &Q2) const { cvm::real const cos_omega = this->q0*Q2.q0 + this->q1*Q2.q1 + this->q2*Q2.q2 + this->q3*Q2.q3; cvm::real const omega = std::acos ( (cos_omega > 1.0) ? 1.0 : ( (cos_omega < -1.0) ? -1.0 : cos_omega) ); cvm::real const sin_omega = std::sin (omega); if (std::fabs (sin_omega) < 1.0E-14) { // return a null 4d vector return cvm::quaternion (0.0, 0.0, 0.0, 0.0); } cvm::quaternion const grad1 ((-1.0)*sin_omega*Q2.q0 + cos_omega*(this->q0-cos_omega*Q2.q0)/sin_omega, (-1.0)*sin_omega*Q2.q1 + cos_omega*(this->q1-cos_omega*Q2.q1)/sin_omega, (-1.0)*sin_omega*Q2.q2 + cos_omega*(this->q2-cos_omega*Q2.q2)/sin_omega, (-1.0)*sin_omega*Q2.q3 + cos_omega*(this->q3-cos_omega*Q2.q3)/sin_omega); if (cos_omega > 0.0) { return 2.0*omega*grad1; } else { return -2.0*(PI-omega)*grad1; } } /// \brief Choose the closest between Q2 and -Q2 and save it back. /// Not required for dist2() and dist2_grad() inline void match (cvm::quaternion &Q2) const { cvm::real const cos_omega = this->q0*Q2.q0 + this->q1*Q2.q1 + this->q2*Q2.q2 + this->q3*Q2.q3; if (cos_omega < 0.0) Q2 *= -1.0; } /// \brief Inner product (as a 4-d vector) with Q2; requires match() /// if the largest overlap is looked for inline cvm::real inner (cvm::quaternion const &Q2) const { cvm::real const prod = this->q0*Q2.q0 + this->q1*Q2.q1 + this->q2*Q2.q2 + this->q3*Q2.q3; return prod; } }; /// \brief A rotation between two sets of coordinates (for the moment /// a wrapper for colvarmodule::quaternion) class colvarmodule::rotation { public: /// \brief The rotation itself (implemented as a quaternion) cvm::quaternion q; /// \brief Eigenvalue corresponding to the optimal rotation cvm::real lambda; /// \brief Perform gradient tests bool b_debug_gradients; /// \brief Positions to superimpose: the rotation should brings pos1 /// into pos2 std::vector< cvm::atom_pos > pos1, pos2; /// Derivatives of S std::vector< cvm::matrix2d<cvm::rvector, 4, 4> > dS_1, dS_2; /// Derivatives of leading eigenvalue std::vector< cvm::rvector > dL0_1, dL0_2; /// Derivatives of leading eigenvector std::vector< cvm::vector1d<cvm::rvector, 4> > dQ0_1, dQ0_2; /// Allocate space for the derivatives of the rotation inline void request_group1_gradients (size_t const &n) { dS_1.resize (n, cvm::matrix2d<cvm::rvector, 4, 4>()); dL0_1.resize (n, cvm::rvector (0.0, 0.0, 0.0)); dQ0_1.resize (n, cvm::vector1d<cvm::rvector, 4>()); } inline void request_group2_gradients (size_t const &n) { dS_2.resize (n, cvm::matrix2d<cvm::rvector, 4, 4>()); dL0_2.resize (n, cvm::rvector (0.0, 0.0, 0.0)); dQ0_2.resize (n, cvm::vector1d<cvm::rvector, 4>()); } /// \brief Calculate the optimal rotation and store the /// corresponding eigenvalue and eigenvector in the arguments l0 and /// q0; if the gradients have been previously requested, calculate /// them as well /// /// The method to derive the optimal rotation is defined in: /// Coutsias EA, Seok C, Dill KA. /// Using quaternions to calculate RMSD. /// J Comput Chem. 25(15):1849-57 (2004) /// DOI: 10.1002/jcc.20110 PubMed: 15376254 void calc_optimal_rotation (std::vector<atom_pos> const &pos1, std::vector<atom_pos> const &pos2); /// Default constructor inline rotation() : b_debug_gradients (false) {} /// Constructor after a quaternion inline rotation (cvm::quaternion const &qi) : b_debug_gradients (false) { q = qi; } /// Constructor after an axis of rotation and an angle (in radians) inline rotation (cvm::real const &angle, cvm::rvector const &axis) : b_debug_gradients (false) { cvm::rvector const axis_n = axis.unit(); cvm::real const sina = std::sin (angle/2.0); q = cvm::quaternion (std::cos (angle/2.0), sina * axis_n.x, sina * axis_n.y, sina * axis_n.z); } /// Destructor inline ~rotation() {} /// Return the rotated vector inline cvm::rvector rotate (cvm::rvector const &v) const { return q.rotate (v); } /// Return the inverse of this rotation inline cvm::rotation inverse() const { return cvm::rotation (this->q.conjugate()); } /// Return the associated 3x3 matrix inline cvm::rmatrix matrix() const { return q.rotation_matrix(); } /// \brief Return the spin angle (in degrees) with respect to the /// provided axis (which MUST be normalized) inline cvm::real spin_angle (cvm::rvector const &axis) const { cvm::rvector const q_vec = q.get_vector(); cvm::real alpha = (180.0/PI) * 2.0 * std::atan2 (axis * q_vec, q.q0); while (alpha > 180.0) alpha -= 360; while (alpha < -180.0) alpha += 360; return alpha; } /// \brief Return the derivative of the spin angle with respect to /// the quaternion inline cvm::quaternion dspin_angle_dq (cvm::rvector const &axis) const { cvm::rvector const q_vec = q.get_vector(); cvm::real const iprod = axis * q_vec; if (q.q0 != 0.0) { // cvm::real const x = iprod/q.q0; - + cvm::real const dspindx = (180.0/PI) * 2.0 * (1.0 / (1.0 + (iprod*iprod)/(q.q0*q.q0))); - return - cvm::quaternion ( dspindx * (iprod * (-1.0) / (q.q0*q.q0)), + return + cvm::quaternion ( dspindx * (iprod * (-1.0) / (q.q0*q.q0)), dspindx * ((1.0/q.q0) * axis.x), dspindx * ((1.0/q.q0) * axis.y), dspindx * ((1.0/q.q0) * axis.z)); } else { // (1/(1+x^2)) ~ (1/x)^2 return cvm::quaternion ((180.0/PI) * 2.0 * ((-1.0)/iprod), 0.0, 0.0, 0.0); // XX TODO: What if iprod == 0? XX } } /// \brief Return the projection of the orientation vector onto a /// predefined axis inline cvm::real cos_theta (cvm::rvector const &axis) const { cvm::rvector const q_vec = q.get_vector(); - cvm::real const alpha = + cvm::real const alpha = (180.0/PI) * 2.0 * std::atan2 (axis * q_vec, q.q0); cvm::real const cos_spin_2 = std::cos (alpha * (PI/180.0) * 0.5); - cvm::real const cos_theta_2 = ( (cos_spin_2 != 0.0) ? + cvm::real const cos_theta_2 = ( (cos_spin_2 != 0.0) ? (q.q0 / cos_spin_2) : (0.0) ); // cos(2t) = 2*cos(t)^2 - 1 return 2.0 * (cos_theta_2*cos_theta_2) - 1.0; } /// Return the derivative of the tilt wrt the quaternion inline cvm::quaternion dcos_theta_dq (cvm::rvector const &axis) const { cvm::rvector const q_vec = q.get_vector(); cvm::real const iprod = axis * q_vec; cvm::real const cos_spin_2 = std::cos (std::atan2 (iprod, q.q0)); if (q.q0 != 0.0) { - cvm::real const d_cos_theta_dq0 = + cvm::real const d_cos_theta_dq0 = (4.0 * q.q0 / (cos_spin_2*cos_spin_2)) * (1.0 - (iprod*iprod)/(q.q0*q.q0) / (1.0 + (iprod*iprod)/(q.q0*q.q0))); cvm::real const d_cos_theta_dqn = (4.0 * q.q0 / (cos_spin_2*cos_spin_2) * (iprod/q.q0) / (1.0 + (iprod*iprod)/(q.q0*q.q0))); - return cvm::quaternion (d_cos_theta_dq0, + return cvm::quaternion (d_cos_theta_dq0, d_cos_theta_dqn * axis.x, d_cos_theta_dqn * axis.y, d_cos_theta_dqn * axis.z); } else { cvm::real const d_cos_theta_dqn = (4.0 / (cos_spin_2*cos_spin_2 * iprod)); return cvm::quaternion (0.0, d_cos_theta_dqn * axis.x, d_cos_theta_dqn * axis.y, d_cos_theta_dqn * axis.z); } } /// \brief Threshold for the eigenvalue crossing test static cvm::real crossing_threshold; protected: /// \brief Previous value of the rotation (used to warn the user /// when the structure changes too much, and there may be an /// eigenvalue crossing) cvm::quaternion q_old; /// Build the overlap matrix S (used by calc_optimal_rotation()) void build_matrix (std::vector<cvm::atom_pos> const &pos1, std::vector<cvm::atom_pos> const &pos2, cvm::matrix2d<real, 4, 4> &S); /// Diagonalize the overlap matrix S (used by calc_optimal_rotation()) void diagonalize_matrix (cvm::matrix2d<cvm::real, 4, 4> &S, cvm::real S_eigval[4], cvm::matrix2d<cvm::real, 4, 4> &S_eigvec); }; #endif // Emacs // Local Variables: // mode: C++ // End: diff --git a/lib/colvars/colvarvalue.cpp b/lib/colvars/colvarvalue.cpp index 25f888c00..18a3bd00c 100644 --- a/lib/colvars/colvarvalue.cpp +++ b/lib/colvars/colvarvalue.cpp @@ -1,261 +1,261 @@ #include <vector> #include "colvarmodule.h" #include "colvarvalue.h" std::string const colvarvalue::type_desc[colvarvalue::type_quaternion+1] = { "not_set", "scalar number", "3-dimensional vector", "3-dimensional unit vector", "4-dimensional unit vector" }; size_t const colvarvalue::dof_num[ colvarvalue::type_quaternion+1] = { 0, 1, 3, 2, 3 }; void colvarvalue::undef_op() const { cvm::fatal_error ("Error: Undefined operation on a colvar of type \""+ colvarvalue::type_desc[this->value_type]+"\".\n"); } void colvarvalue::error_rside (colvarvalue::Type const &vt) const { cvm::fatal_error ("Trying to assign a colvar value with type \""+ type_desc[this->value_type]+"\" to one with type \""+ type_desc[vt]+"\".\n"); -} +} void colvarvalue::error_lside (colvarvalue::Type const &vt) const { cvm::fatal_error ("Trying to use a colvar value with type \""+ type_desc[vt]+"\" as one of type \""+ type_desc[this->value_type]+"\".\n"); -} +} void colvarvalue::inner_opt (colvarvalue const &x, std::vector<colvarvalue>::iterator &xv, std::vector<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner) { // doing type check only once, here colvarvalue::check_types (x, *xv); std::vector<colvarvalue>::iterator &xvi = xv; std::vector<cvm::real>::iterator &ii = inner; switch (x.value_type) { case colvarvalue::type_scalar: while (xvi != xv_end) { *(ii++) += (xvi++)->real_value * x.real_value; } break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: while (xvi != xv_end) { *(ii++) += (xvi++)->rvector_value * x.rvector_value; } break; case colvarvalue::type_quaternion: while (xvi != xv_end) { *(ii++) += ((xvi++)->quaternion_value).cosine (x.quaternion_value); } break; default: x.undef_op(); }; } void colvarvalue::inner_opt (colvarvalue const &x, std::list<colvarvalue>::iterator &xv, std::list<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner) { // doing type check only once, here colvarvalue::check_types (x, *xv); std::list<colvarvalue>::iterator &xvi = xv; std::vector<cvm::real>::iterator &ii = inner; switch (x.value_type) { case colvarvalue::type_scalar: while (xvi != xv_end) { *(ii++) += (xvi++)->real_value * x.real_value; } break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: while (xvi != xv_end) { *(ii++) += (xvi++)->rvector_value * x.rvector_value; } break; case colvarvalue::type_quaternion: while (xvi != xv_end) { *(ii++) += ((xvi++)->quaternion_value).cosine (x.quaternion_value); } break; default: x.undef_op(); }; } void colvarvalue::p2leg_opt (colvarvalue const &x, std::vector<colvarvalue>::iterator &xv, std::vector<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner) { // doing type check only once, here colvarvalue::check_types (x, *xv); std::vector<colvarvalue>::iterator &xvi = xv; std::vector<cvm::real>::iterator &ii = inner; switch (x.value_type) { case colvarvalue::type_scalar: cvm::fatal_error ("Error: cannot calculate Legendre polynomials " "for scalar variables.\n"); break; case colvarvalue::type_vector: while (xvi != xv_end) { - cvm::real const cosine = + cvm::real const cosine = ((xvi)->rvector_value * x.rvector_value) / ((xvi)->rvector_value.norm() * x.rvector_value.norm()); xvi++; *(ii++) += 1.5*cosine*cosine - 0.5; } break; case colvarvalue::type_unitvector: while (xvi != xv_end) { cvm::real const cosine = (xvi++)->rvector_value * x.rvector_value; *(ii++) += 1.5*cosine*cosine - 0.5; } break; case colvarvalue::type_quaternion: while (xvi != xv_end) { cvm::real const cosine = (xvi++)->quaternion_value.cosine (x.quaternion_value); *(ii++) += 1.5*cosine*cosine - 0.5; } break; default: x.undef_op(); }; } void colvarvalue::p2leg_opt (colvarvalue const &x, std::list<colvarvalue>::iterator &xv, std::list<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner) { // doing type check only once, here colvarvalue::check_types (x, *xv); std::list<colvarvalue>::iterator &xvi = xv; std::vector<cvm::real>::iterator &ii = inner; switch (x.value_type) { case colvarvalue::type_scalar: cvm::fatal_error ("Error: cannot calculate Legendre polynomials " "for scalar variables.\n"); break; case colvarvalue::type_vector: while (xvi != xv_end) { - cvm::real const cosine = + cvm::real const cosine = ((xvi)->rvector_value * x.rvector_value) / ((xvi)->rvector_value.norm() * x.rvector_value.norm()); xvi++; *(ii++) += 1.5*cosine*cosine - 0.5; } break; case colvarvalue::type_unitvector: while (xvi != xv_end) { cvm::real const cosine = (xvi++)->rvector_value * x.rvector_value; *(ii++) += 1.5*cosine*cosine - 0.5; } break; case colvarvalue::type_quaternion: while (xvi != xv_end) { cvm::real const cosine = (xvi++)->quaternion_value.cosine (x.quaternion_value); *(ii++) += 1.5*cosine*cosine - 0.5; } break; default: x.undef_op(); }; } std::ostream & operator << (std::ostream &os, colvarvalue const &x) { switch (x.type()) { case colvarvalue::type_scalar: os << x.real_value; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: os << x.rvector_value; break; case colvarvalue::type_quaternion: os << x.quaternion_value; break; case colvarvalue::type_notset: os << "not set"; break; } return os; } std::ostream & operator << (std::ostream &os, std::vector<colvarvalue> const &v) { for (size_t i = 0; i < v.size(); i++) os << v[i]; return os; } std::istream & operator >> (std::istream &is, colvarvalue &x) { if (x.type() == colvarvalue::type_notset) { cvm::fatal_error ("Trying to read from a stream a colvarvalue, " "which has not yet been assigned a data type.\n"); } switch (x.type()) { case colvarvalue::type_scalar: - is >> x.real_value; + is >> x.real_value; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: - is >> x.rvector_value; + is >> x.rvector_value; break; case colvarvalue::type_quaternion: is >> x.quaternion_value; break; default: x.undef_op(); } x.apply_constraints(); return is; } size_t colvarvalue::output_width (size_t const &real_width) const { switch (this->value_type) { case colvarvalue::type_scalar: return real_width; case colvarvalue::type_vector: case colvarvalue::type_unitvector: return cvm::rvector::output_width (real_width); case colvarvalue::type_quaternion: return cvm::quaternion::output_width (real_width); case colvarvalue::type_notset: default: return 0; } } diff --git a/lib/colvars/colvarvalue.h b/lib/colvars/colvarvalue.h index eb4459345..e262e7feb 100644 --- a/lib/colvars/colvarvalue.h +++ b/lib/colvars/colvarvalue.h @@ -1,727 +1,727 @@ #ifndef COLVARVALUE_H #define COLVARVALUE_H #include "colvarmodule.h" /// \brief Value of a collective variable: this is a metatype which /// can be set at runtime. By default it is set to be a scalar /// number, and can be treated like that in all operations (this is /// done by most \link cvc \endlink implementations). /// /// \link colvarvalue \endlink allows \link colvar \endlink to be /// treat different data types. By default, a \link colvarvalue /// \endlink variable is a scalar number. If you want to use it as /// another type, you should declare and initialize a variable as /// \code colvarvalue x (colvarvalue::type_xxx); \endcode where /// type_xxx is a value within the \link Type \endlink enum. /// Alternatively, initialize x with \link x.type /// (colvarvalue::type_xxx) \endlink at a later stage. /// /// Given a colvarvalue variable x which is not yet assigned (and /// thus has not yet a type) it is also possible to correctly assign /// the type with \code x = y; \endcode if y is correctly set. /// Otherwise, an error will be raised if the \link Type \endlink of x /// is different from the \link Type \endlink of y. /// /// Also, every operator (either unary or binary) on a \link /// colvarvalue \endlink object performs one or more checks on the /// \link Type \endlink to avoid errors such as initializing a /// three-dimensional vector with a scalar number (legal otherwise). /// /// \b Special \b case: when reading from a stream, there is no way to /// detect the \link Type \endlink and safely handle errors at the /// same time. Hence, when using \code is >> x; \endcode x \b MUST /// already have a type correcly set up for properly parsing the /// stream. An error will be raised otherwise. Usually this is not /// the problem, because \link colvarvalue \endlink objects are first /// initialized in the configuration, and the stream operation will be /// performed only when reading restart files. -/// +/// /// No problem of course with the output streams: \code os << x; /// \endcode will print a different output according to the value of /// colvarvalue::value_type, and the width of such output is returned /// by colvarvalue::output_width() /// /// \em Note \em on \em performance: at every operation between two /// \link colvarvalue \endlink objects, their two \link Type \endlink /// flags will be checked for a match. In a long array of \link /// colvarvalue \endlink objects this is time consuming: a few static /// functions are defined ("xxx_opt" functions) within the \link /// colvarvalue \endlink class, which only check the matching once for /// a large array, and execute different loops according to the type. /// You should do the same for every time consuming loop involving /// operations on \link colvarvalue \endlink objects if you want /// e.g. to optimize your colvar bias. class colvarvalue { public: /// \brief Possible types of value /// /// These three cover most possibilities of data type one can /// devise. If you need to implement a new colvar with a very /// complex data type, it's better to put an allocatable class here enum Type { /// Undefined type type_notset, /// Scalar number, implemented as \link colvarmodule::real \endlink (default) type_scalar, /// 3-dimensional vector, implemented as \link colvarmodule::rvector \endlink type_vector, /// 3-dimensional unit vector, implemented as \link colvarmodule::rvector \endlink type_unitvector, /// 4-dimensional unit vector representing a rotation, implemented as \link colvarmodule::quaternion \endlink type_quaternion }; /// Runtime description of value types std::string static const type_desc[colvarvalue::type_quaternion+1]; /// Number of degrees of freedom for each type size_t static const dof_num[ colvarvalue::type_quaternion+1]; /// \brief Real data member cvm::real real_value; /// \brief Vector data member cvm::rvector rvector_value; /// \brief Quaternion data member cvm::quaternion quaternion_value; /// Current type of this colvarvalue object - Type value_type; + Type value_type; static inline bool type_checking() { return true; } /// \brief Default constructor: this class defaults to a scalar /// number and always behaves like it unless you change its type inline colvarvalue() : real_value (0.0), value_type (type_scalar) {} /// Constructor from a type specification inline colvarvalue (Type const &vti) : value_type (vti) { reset(); } /// Copy constructor from real base type inline colvarvalue (cvm::real const &x) : real_value (x), value_type (type_scalar) {} /// \brief Copy constructor from rvector base type (Note: this sets /// automatically a type \link type_vector \endlink , if you want a /// \link type_unitvector \endlink you must set it explicitly) inline colvarvalue (cvm::rvector const &v) : rvector_value (v), value_type (type_vector) {} /// \brief Copy constructor from rvector base type (additional /// argument to make possible to choose a \link type_unitvector /// \endlink inline colvarvalue (cvm::rvector const &v, Type const &vti) : rvector_value (v), value_type (vti) {} /// \brief Copy constructor from quaternion base type inline colvarvalue (cvm::quaternion const &q) : quaternion_value (q), value_type (type_quaternion) {} /// Copy constructor from another \link colvarvalue \endlink inline colvarvalue (colvarvalue const &x) : value_type (x.value_type) { reset(); switch (x.value_type) { case type_scalar: real_value = x.real_value; break; - case type_vector: + case type_vector: case type_unitvector: rvector_value = x.rvector_value; break; case type_quaternion: quaternion_value = x.quaternion_value; break; case type_notset: default: break; } } /// Set to the null value for the data type currently defined void reset(); /// \brief If the variable has constraints (e.g. unitvector or /// quaternion), transform it to satisfy them; use it when the \link /// colvarvalue \endlink is not calculated from \link cvc /// \endlink objects, but manipulated by you void apply_constraints(); /// Get the current type inline Type type() const { return value_type; } /// Set the type explicitly inline void type (Type const &vti) { reset(); value_type = vti; reset(); } /// Set the type after another \link colvarvalue \endlink inline void type (colvarvalue const &x) { reset(); value_type = x.value_type; reset(); } /// Square norm of this colvarvalue cvm::real norm2() const; /// Norm of this colvarvalue inline cvm::real norm() const { return std::sqrt (this->norm2()); } /// \brief Return the value whose scalar product with this value is /// 1 inline colvarvalue inverse() const; /// Square distance between this \link colvarvalue \endlink and another cvm::real dist2 (colvarvalue const &x2) const; /// Derivative with respect to this \link colvarvalue \endlink of the square distance colvarvalue dist2_grad (colvarvalue const &x2) const; /// Assignment operator (type of x is checked) colvarvalue & operator = (colvarvalue const &x); void operator += (colvarvalue const &x); void operator -= (colvarvalue const &x); void operator *= (cvm::real const &a); void operator /= (cvm::real const &a); // Casting operator inline operator cvm::real() const { if (value_type != type_scalar) error_rside (type_scalar); return real_value; } // Casting operator inline operator cvm::rvector() const { if ( (value_type != type_vector) && (value_type != type_unitvector)) error_rside (type_vector); return rvector_value; } // Casting operator inline operator cvm::quaternion() const { if (value_type != type_quaternion) error_rside (type_quaternion); return quaternion_value; } /// Special case when the variable is a real number, and all operations are defined inline bool is_scalar() { return (value_type == type_scalar); } /// Ensure that the two types are the same within a binary operator void static check_types (colvarvalue const &x1, colvarvalue const &x2); - /// Undefined operation + /// Undefined operation void undef_op() const; /// Trying to assign this \link colvarvalue \endlink object to /// another object set with a different type void error_lside (Type const &vt) const; /// Trying to assign another \link colvarvalue \endlink object set /// with a different type to this object void error_rside (Type const &vt) const; /// Give the number of characters required to output this /// colvarvalue, given the current type assigned and the number of /// characters for a real number size_t output_width (size_t const &real_width) const; // optimized routines for operations with an array; xv and inner as // vectors are assumed to have the same number of elements (i.e. the // end for inner comes together with xv_end in the loop) /// \brief Optimized routine for the inner product of one collective /// variable with an array void static inner_opt (colvarvalue const &x, std::vector<colvarvalue>::iterator &xv, std::vector<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner); /// \brief Optimized routine for the inner product of one collective /// variable with an array void static inner_opt (colvarvalue const &x, std::list<colvarvalue>::iterator &xv, std::list<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner); /// \brief Optimized routine for the second order Legendre /// polynomial, (3cos^2(w)-1)/2, of one collective variable with an /// array void static p2leg_opt (colvarvalue const &x, std::vector<colvarvalue>::iterator &xv, std::vector<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner); /// \brief Optimized routine for the second order Legendre /// polynomial of one collective variable with an array void static p2leg_opt (colvarvalue const &x, std::list<colvarvalue>::iterator &xv, std::list<colvarvalue>::iterator const &xv_end, std::vector<cvm::real>::iterator &inner); }; inline void colvarvalue::reset() { switch (value_type) { case colvarvalue::type_scalar: real_value = cvm::real (0.0); break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: rvector_value = cvm::rvector (0.0, 0.0, 0.0); break; case colvarvalue::type_quaternion: quaternion_value = cvm::quaternion (0.0, 0.0, 0.0, 0.0); break; case colvarvalue::type_notset: default: break; - } + } } inline void colvarvalue::apply_constraints() { switch (value_type) { case colvarvalue::type_scalar: break; case colvarvalue::type_vector: break; case colvarvalue::type_unitvector: rvector_value /= std::sqrt (rvector_value.norm2()); break; case colvarvalue::type_quaternion: quaternion_value /= std::sqrt (quaternion_value.norm2()); break; case colvarvalue::type_notset: default: break; - } + } } inline cvm::real colvarvalue::norm2() const { switch (value_type) { case colvarvalue::type_scalar: return (this->real_value)*(this->real_value); case colvarvalue::type_vector: case colvarvalue::type_unitvector: return (this->rvector_value).norm2(); case colvarvalue::type_quaternion: return (this->quaternion_value).norm2(); case colvarvalue::type_notset: default: return 0.0; - } + } } inline colvarvalue colvarvalue::inverse() const { switch (value_type) { case colvarvalue::type_scalar: return colvarvalue (1.0/real_value); case colvarvalue::type_vector: return colvarvalue (cvm::rvector (1.0/rvector_value.x, 1.0/rvector_value.y, 1.0/rvector_value.z)); case colvarvalue::type_quaternion: return colvarvalue (cvm::quaternion (1.0/quaternion_value.q0, 1.0/quaternion_value.q1, 1.0/quaternion_value.q2, 1.0/quaternion_value.q3)); case colvarvalue::type_notset: default: undef_op(); - } + } return colvarvalue(); } inline colvarvalue & colvarvalue::operator = (colvarvalue const &x) { if (this->value_type != type_notset) - if (this->value_type != x.value_type) + if (this->value_type != x.value_type) error_lside (x.value_type); this->value_type = x.value_type; - + switch (this->value_type) { case colvarvalue::type_scalar: this->real_value = x.real_value; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: this->rvector_value = x.rvector_value; break; case colvarvalue::type_quaternion: this->quaternion_value = x.quaternion_value; break; case colvarvalue::type_notset: default: undef_op(); break; } return *this; } inline void colvarvalue::operator += (colvarvalue const &x) { if (colvarvalue::type_checking()) colvarvalue::check_types (*this, x); switch (this->value_type) { case colvarvalue::type_scalar: this->real_value += x.real_value; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: this->rvector_value += x.rvector_value; break; case colvarvalue::type_quaternion: this->quaternion_value += x.quaternion_value; break; case colvarvalue::type_notset: default: undef_op(); } } inline void colvarvalue::operator -= (colvarvalue const &x) { if (colvarvalue::type_checking()) colvarvalue::check_types (*this, x); switch (value_type) { case colvarvalue::type_scalar: real_value -= x.real_value; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: rvector_value -= x.rvector_value; break; case colvarvalue::type_quaternion: quaternion_value -= x.quaternion_value; break; case colvarvalue::type_notset: default: undef_op(); } } inline void colvarvalue::operator *= (cvm::real const &a) { switch (value_type) { case colvarvalue::type_scalar: real_value *= a; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: rvector_value *= a; break; case colvarvalue::type_quaternion: quaternion_value *= a; break; case colvarvalue::type_notset: default: undef_op(); } } inline void colvarvalue::operator /= (cvm::real const &a) { switch (value_type) { case colvarvalue::type_scalar: real_value /= a; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: rvector_value /= a; break; case colvarvalue::type_quaternion: quaternion_value /= a; break; case colvarvalue::type_notset: default: undef_op(); } } // binary operations between two colvarvalues inline colvarvalue operator + (colvarvalue const &x1, colvarvalue const &x2) { if (colvarvalue::type_checking()) colvarvalue::check_types (x1, x2); switch (x1.value_type) { case colvarvalue::type_scalar: return colvarvalue (x1.real_value + x2.real_value); case colvarvalue::type_vector: return colvarvalue (x1.rvector_value + x2.rvector_value); case colvarvalue::type_unitvector: return colvarvalue (x1.rvector_value + x2.rvector_value, colvarvalue::type_unitvector); case colvarvalue::type_quaternion: return colvarvalue (x1.quaternion_value + x2.quaternion_value); case colvarvalue::type_notset: default: x1.undef_op(); return colvarvalue (colvarvalue::type_notset); }; } inline colvarvalue operator - (colvarvalue const &x1, colvarvalue const &x2) { if (colvarvalue::type_checking()) colvarvalue::check_types (x1, x2); switch (x1.value_type) { case colvarvalue::type_scalar: return colvarvalue (x1.real_value - x2.real_value); case colvarvalue::type_vector: return colvarvalue (x1.rvector_value - x2.rvector_value); case colvarvalue::type_unitvector: return colvarvalue (x1.rvector_value - x2.rvector_value, colvarvalue::type_unitvector); case colvarvalue::type_quaternion: return colvarvalue (x1.quaternion_value - x2.quaternion_value); default: x1.undef_op(); return colvarvalue (colvarvalue::type_notset); }; } // binary operations with real numbers inline colvarvalue operator * (cvm::real const &a, colvarvalue const &x) { switch (x.value_type) { case colvarvalue::type_scalar: return colvarvalue (a * x.real_value); case colvarvalue::type_vector: return colvarvalue (a * x.rvector_value); case colvarvalue::type_unitvector: return colvarvalue (a * x.rvector_value, colvarvalue::type_unitvector); case colvarvalue::type_quaternion: return colvarvalue (a * x.quaternion_value); case colvarvalue::type_notset: default: x.undef_op(); return colvarvalue (colvarvalue::type_notset); } } inline colvarvalue operator * (colvarvalue const &x, cvm::real const &a) { switch (x.value_type) { case colvarvalue::type_scalar: return colvarvalue (x.real_value * a); case colvarvalue::type_vector: return colvarvalue (x.rvector_value * a); case colvarvalue::type_unitvector: return colvarvalue (x.rvector_value * a, colvarvalue::type_unitvector); case colvarvalue::type_quaternion: return colvarvalue (x.quaternion_value * a); case colvarvalue::type_notset: default: x.undef_op(); return colvarvalue (colvarvalue::type_notset); } } inline colvarvalue operator / (colvarvalue const &x, cvm::real const &a) { switch (x.value_type) { case colvarvalue::type_scalar: return colvarvalue (x.real_value / a); case colvarvalue::type_vector: return colvarvalue (x.rvector_value / a); case colvarvalue::type_unitvector: return colvarvalue (x.rvector_value / a, colvarvalue::type_unitvector); case colvarvalue::type_quaternion: return colvarvalue (x.quaternion_value / a); case colvarvalue::type_notset: default: x.undef_op(); return colvarvalue (colvarvalue::type_notset); } } // inner product between two colvarvalues inline cvm::real operator * (colvarvalue const &x1, colvarvalue const &x2) { if (colvarvalue::type_checking()) colvarvalue::check_types (x1, x2); switch (x1.value_type) { case colvarvalue::type_scalar: return (x1.real_value * x2.real_value); case colvarvalue::type_vector: case colvarvalue::type_unitvector: return (x1.rvector_value * x2.rvector_value); case colvarvalue::type_quaternion: // the "*" product is the quaternion product, here the inner // member function is used instead return (x1.quaternion_value.inner (x2.quaternion_value)); case colvarvalue::type_notset: default: x1.undef_op(); return 0.0; }; } inline cvm::real colvarvalue::dist2 (colvarvalue const &x2) const { if (colvarvalue::type_checking()) colvarvalue::check_types (*this, x2); switch (this->value_type) { case colvarvalue::type_scalar: return (this->real_value - x2.real_value)*(this->real_value - x2.real_value); case colvarvalue::type_vector: return (this->rvector_value - x2.rvector_value).norm2(); case colvarvalue::type_unitvector: // angle between (*this) and x2 is the distance return std::acos (this->rvector_value * x2.rvector_value) * std::acos (this->rvector_value * x2.rvector_value); case colvarvalue::type_quaternion: // angle between (*this) and x2 is the distance, the quaternion // object has it implemented internally return this->quaternion_value.dist2 (x2.quaternion_value); case colvarvalue::type_notset: default: this->undef_op(); return 0.0; }; } inline colvarvalue colvarvalue::dist2_grad (colvarvalue const &x2) const { if (colvarvalue::type_checking()) colvarvalue::check_types (*this, x2); switch (this->value_type) { case colvarvalue::type_scalar: return 2.0 * (this->real_value - x2.real_value); case colvarvalue::type_vector: return 2.0 * (this->rvector_value - x2.rvector_value); case colvarvalue::type_unitvector: { cvm::rvector const &v1 = this->rvector_value; cvm::rvector const &v2 = x2.rvector_value; cvm::real const cos_t = v1 * v2; cvm::real const sin_t = std::sqrt (1.0 - cos_t*cos_t); return colvarvalue ( 2.0 * sin_t * cvm::rvector ( (-1.0) * sin_t * v2.x + cos_t/sin_t * (v1.x - cos_t*v2.x), (-1.0) * sin_t * v2.y + cos_t/sin_t * (v1.y - cos_t*v2.y), (-1.0) * sin_t * v2.z + cos_t/sin_t * (v1.z - cos_t*v2.z) ), colvarvalue::type_unitvector ); } case colvarvalue::type_quaternion: return this->quaternion_value.dist2_grad (x2.quaternion_value); case colvarvalue::type_notset: default: this->undef_op(); return colvarvalue (colvarvalue::type_notset); }; } inline void colvarvalue::check_types (colvarvalue const &x1, colvarvalue const &x2) { if (x1.value_type != x2.value_type) { cvm::log ("x1 type = "+cvm::to_str (x1.value_type)+ ", x2 type = "+cvm::to_str (x2.value_type)+"\n"); cvm::fatal_error ("Performing an operation between two colvar " "values with different types, \""+ colvarvalue::type_desc[x1.value_type]+ "\" and \""+ colvarvalue::type_desc[x2.value_type]+ "\".\n"); } } std::ostream & operator << (std::ostream &os, colvarvalue const &x); std::ostream & operator << (std::ostream &os, std::vector<colvarvalue> const &v); std::istream & operator >> (std::istream &is, colvarvalue &x); #endif // Emacs // Local Variables: // mode: C++ // End: