diff --git a/lib/colvars/colvar.cpp b/lib/colvars/colvar.cpp index 5e90636e1..fcd7d57f0 100644 --- a/lib/colvars/colvar.cpp +++ b/lib/colvars/colvar.cpp @@ -1,1934 +1,1943 @@ /// -*- c++ -*- #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" #include "colvar.h" #include "colvarcomp.h" #include "colvarscript.h" #include <algorithm> colvar::colvar(std::string const &conf) : colvarparse(conf) { size_t i, j; cvm::log("Initializing a new collective variable.\n"); get_keyval(conf, "name", this->name, (std::string("colvar")+cvm::to_str(cvm::colvars.size()+1))); if (cvm::colvar_by_name(this->name) != NULL) { cvm::error("Error: this colvar cannot have the same name, \""+this->name+ "\", as another colvar.\n", INPUT_ERROR); return; } // all tasks disabled by default for (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); \ if (cvm::get_error()) { \ cvm::error("Error: in setting up component \"" \ def_config_key"\".\n"); \ return; \ } \ cvm::decrease_depth(); \ } else { \ cvm::error("Error: in allocating component \"" \ def_config_key"\".\n", \ MEMORY_ERROR); \ return; \ } \ 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::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), \ INPUT_ERROR); \ return; \ } \ } \ 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("Cartesian coordinates", "cartesian", cartesian); 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("N1xN2-long vector of pairwise distances", "distancePairs", distance_pairs); 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("orientation " "projection", "orientationProj",orientation_proj); 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::error("Error: no valid components were provided " "for this collective variable.\n", INPUT_ERROR); return; } cvm::log("All components initialized.\n"); // Setup colvar as scripted function of components if (get_keyval(conf, "scriptedFunction", scripted_function, "", colvarparse::parse_silent)) { enable(task_scripted); cvm::log("This colvar uses scripted function \"" + scripted_function + "\"."); std::string type_str; get_keyval(conf, "scriptedFunctionType", type_str, "scalar"); x.type(colvarvalue::type_notset); int t; for (t = 0; t < colvarvalue::type_all; t++) { if (type_str == colvarvalue::type_keyword(colvarvalue::Type(t))) { x.type(colvarvalue::Type(t)); break; } } if (x.type() == colvarvalue::type_notset) { cvm::error("Could not parse scripted colvar type."); return; } x_reported.type(x.type()); cvm::log(std::string("Expecting colvar value of type ") + colvarvalue::type_desc(x.type())); if (x.type() == colvarvalue::type_vector) { int size; if (!get_keyval(conf, "scriptedFunctionVectorSize", size)) { cvm::error("Error: no size specified for vector scripted function."); return; } x.vector1d_value.resize(size); } // Sort array of cvcs based on values of componentExp std::vector<cvc *> temp_vec; for (i = 1; i <= cvcs.size(); i++) { for (j = 0; j < cvcs.size(); j++) { if (cvcs[j]->sup_np == int(i)) { temp_vec.push_back(cvcs[j]); break; } } } if (temp_vec.size() != cvcs.size()) { cvm::error("Could not find order numbers for all components " "in componentExp values."); return; } cvcs = temp_vec; // Build ordered list of component values that will be // passed to the script for (j = 0; j < cvcs.size(); j++) { sorted_cvc_values.push_back(&(cvcs[j]->value())); } b_homogeneous = false; // Scripted functions are deemed non-periodic b_periodic = false; period = 0.0; b_inverse_gradients = false; b_Jacobian_force = false; } if (!tasks[task_scripted]) { colvarvalue const &cvc_value = (cvcs[0])->value(); if (cvm::debug()) cvm::log ("This collective variable is a "+ colvarvalue::type_desc(cvc_value.type())+ ((cvc_value.size() > 1) ? " with "+ cvm::to_str(cvc_value.size())+" individual components.\n" : ".\n")); x.type(cvc_value); x_reported.type(cvc_value); } // If using scripted biases, any colvar may receive bias forces // and will need its gradient if (cvm::scripted_forces()) { enable(task_gradients); } // check for linear combinations b_linear = !tasks[task_scripted]; for (i = 0; i < cvcs.size(); i++) { if ((cvcs[i])->b_debug_gradients) enable(task_gradients); 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"); } } } // Colvar is homogeneous iff: // - it is not scripted // - it is linear // - all cvcs have coefficient 1 or -1 // i.e. sum or difference of cvcs b_homogeneous = !tasks[task_scripted] && b_linear; for (i = 0; i < cvcs.size(); i++) { if ((std::fabs(cvcs[i]->sup_coeff) - 1.0) > 1.0e-10) { b_homogeneous = false; } } // Colvar is deemed periodic iff: // - it is homogeneous // - all cvcs are periodic // - all cvcs have the same period b_periodic = cvcs[0]->b_periodic && b_homogeneous; period = cvcs[0]->period; for (i = 1; i < cvcs.size(); i++) { if (!cvcs[i]->b_periodic || cvcs[i]->period != period) { b_periodic = false; period = 0.0; } } // these will be set to false if any of the cvcs has them false b_inverse_gradients = !tasks[task_scripted]; b_Jacobian_force = !tasks[task_scripted]; // check the available features of each cvc for (i = 0; i < cvcs.size(); i++) { 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; // components may have different types only for scripted functions if (!tasks[task_scripted] && (colvarvalue::check_types(cvcs[i]->value(), cvcs[0]->value())) ) { cvm::error("ERROR: you are definining this collective variable " "by using components of different types. " "You must use the same type in order to " " sum them together.\n", INPUT_ERROR); return; } } // at this point, the colvar's type is defined f.type(value()); fb.type(value()); get_keyval(conf, "width", width, 1.0); if (width <= 0.0) { cvm::error("Error: \"width\" must be positive.\n", INPUT_ERROR); } lower_boundary.type(value()); lower_wall.type(value()); upper_boundary.type(value()); upper_wall.type(value()); if (value().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::error("Error: the upper boundary, "+ cvm::to_str(upper_boundary)+ ", is not higher than the lower boundary, "+ cvm::to_str(lower_boundary)+".\n", INPUT_ERROR); } } if (tasks[task_lower_wall] && tasks[task_upper_wall]) { if (lower_wall >= upper_wall) { cvm::error("Error: the upper wall, "+ cvm::to_str(upper_wall)+ ", is not higher than the lower wall, "+ cvm::to_str(lower_wall)+".\n", INPUT_ERROR); } 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::error("Error: trying to expand boundaries that already " "cover a whole period of a periodic colvar.\n", INPUT_ERROR); } if (expand_boundaries && hard_lower_boundary && hard_upper_boundary) { cvm::error("Error: inconsistent configuration " "(trying to expand boundaries with both " "hardLowerBoundary and hardUpperBoundary enabled).\n", INPUT_ERROR); } { 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(value()); vr.type(value()); fr.type(value()); const bool found = get_keyval(conf, "extendedTemp", temp, cvm::temperature()); if (temp <= 0.0) { if (found) cvm::error("Error: \"extendedTemp\" must be positive.\n", INPUT_ERROR); else cvm::error("Error: a positive temperature must be provided, either " "by enabling a thermostat, or through \"extendedTemp\".\n", INPUT_ERROR); } get_keyval(conf, "extendedFluctuation", tolerance); if (tolerance <= 0.0) { cvm::error("Error: \"extendedFluctuation\" must be positive.\n", INPUT_ERROR); } 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::error("Error: \"extendedTimeConstant\" must be positive.\n", INPUT_ERROR); } 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::error("Error: \"extendedLangevinDamping\" may not be negative.\n", INPUT_ERROR); } 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()); unsigned int id_i = 0; std::list<int>::iterator li; for (li = temp_id_list.begin(); li != temp_id_list.end(); ++li) { atom_ids[id_i] = *li; id_i++; } 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"); } } int 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::error("Error: runAveStride must be commensurate with the restart frequency.\n", INPUT_ERROR); } 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_by_name(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::log("Unknown type of correlation function, \""+ acf_type_str+"\".\n"); cvm::set_error_bits(INPUT_ERROR); } 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::error("Error: corrFuncStride must be commensurate with the restart frequency.\n", INPUT_ERROR); } get_keyval(conf, "corrFuncNormalize", acf_normalize, true); get_keyval(conf, "corrFuncOutputFile", acf_outfile, std::string(cvm::output_prefix+"."+this->name+ ".corrfunc.dat")); } return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK); } int 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) { cvm::error("Error: colvar \""+this->name+ "\" does not have Jacobian forces implemented.\n", INPUT_ERROR); } if (!b_linear) { cvm::error("Error: colvar \""+this->name+ "\" must be defined as a linear combination " "to calculate the Jacobian force.\n", INPUT_ERROR); } if (cvm::debug()) cvm::log("Enabling calculation of the Jacobian force " "on this colvar.\n"); } fj.type(value()); break; case task_system_force: if (!tasks[task_extended_lagrangian]) { if (!b_inverse_gradients) { cvm::error("Error: one or more of the components of " "colvar \""+this->name+ "\" does not support system force calculation.\n", INPUT_ERROR); } cvm::request_system_force(); } ft.type(value()); ft_reported.type(value()); 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(value()); v_fdiff.type(value()); v_reported.type(value()); break; case task_output_velocity: enable(task_fdiff_velocity); break; case task_grid: if (value().type() != colvarvalue::type_scalar) { cvm::error("Cannot calculate a grid for collective variable, \""+ this->name+"\", because its value is not a scalar number.\n", INPUT_ERROR); } break; case task_extended_lagrangian: enable(task_gradients); v_reported.type(value()); break; case task_lower_boundary: case task_upper_boundary: if (value().type() != colvarvalue::type_scalar) { cvm::error("Error: this colvar is not a scalar value " "and cannot produce a grid.\n", INPUT_ERROR); } break; case task_output_value: case task_runave: case task_corrfunc: case task_ntot: case task_langevin: case task_output_energy: case task_scripted: case task_gradients: break; case task_collect_gradients: if (value().type() != colvarvalue::type_scalar) { cvm::error("Collecting atomic gradients for non-scalar collective variable \""+ this->name+"\" is not yet implemented.\n", INPUT_ERROR); } enable(task_gradients); if (atom_ids.size() == 0) { build_atom_list(); } break; } tasks[t] = true; return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK); } 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: case task_langevin: case task_output_energy: case task_collect_gradients: case task_scripted: break; } tasks[t] = false; } void colvar::setup() { // loop over all components to reset masses of all groups 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.read_positions(); atoms.reset_mass(name,i,ig); } } } colvar::~colvar() { for (size_t i = 0; i < cvcs.size(); i++) { delete cvcs[i]; } // remove reference to this colvar from the CVM for (std::vector<colvar *>::iterator cvi = cvm::colvars.begin(); cvi != cvm::colvars.end(); ++cvi) { if ( *cvi == this) { cvm::colvars.erase(cvi); break; } } } // ******************** CALC FUNCTIONS ******************** void colvar::calc() { size_t i, ig; 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"); + // Update the enabled/disabled status of cvcs if necessary + if (cvc_flags.size()) { + bool any = false; + for (i = 0; i < cvcs.size(); i++) { + cvcs[i]->b_enabled = cvc_flags[i]; + any = any || cvc_flags[i]; + } + if (!any) { + cvm::error("ERROR: All CVCs are disabled for colvar " + this->name +"\n"); + return; + } + cvc_flags.resize(0); + } + for (i = 0; i < cvcs.size(); i++) { + if (!cvcs[i]->b_enabled) continue; for (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 } } //// Don't try to get atom velocities, as no back-end currently implements it // if (tasks[task_output_velocity] && !tasks[task_fdiff_velocity]) { // for (i = 0; i < cvcs.size(); i++) { // for (ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { // cvcs[i]->atom_groups[ig]->read_velocities(); // } // } // } if (tasks[task_system_force]) { for (i = 0; i < cvcs.size(); i++) { for (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(); // First, update component values for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; 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"); } // Then combine them appropriately if (tasks[task_scripted]) { // cvcs combined by user script int res = cvm::proxy->run_colvar_callback(scripted_function, sorted_cvc_values, x); if (res == COLVARS_NOT_IMPLEMENTED) { cvm::error("Scripted colvars are not implemented."); return; } if (res != COLVARS_OK) { cvm::error("Error running scripted colvar"); return; } } else if (x.type() == colvarvalue::type_scalar) { // polynomial combination allowed for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; 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 (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; 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 (i = 0; i < cvcs.size(); i++) { // calculate the gradients of each component if (!cvcs[i]->b_enabled) continue; 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 (ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { 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]) { if (tasks[task_scripted]) { cvm::error("Collecting atomic gradients is not implemented for " "scripted colvars."); return; } // Collect the atomic gradients inside colvar object for (unsigned int a = 0; a < atomic_gradients.size(); a++) { atomic_gradients[a].reset(); } for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; // 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; } } } } } } if (tasks[task_system_force] && !tasks[task_extended_lagrangian]) { // If extended Lagrangian is enabled, system force calculation is trivial // and done together with integration of the extended coordinate. if (tasks[task_scripted]) { // TODO see if this could reasonably be done in a generic way // from generic inverse gradients cvm::error("System force is not implemented for " "scripted colvars."); return; } if (cvm::debug()) cvm::log("Calculating system force of colvar \""+this->name+"\".\n"); ft.reset(); // if (!tasks[task_extended_lagrangian] && (cvm::step_relative() > 0)) { // Disabled check to allow for explicit system force calculation // even with extended Lagrangian if (cvm::step_relative() > 0) { // get from the cvcs the system forces from the PREVIOUS step for (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.type(value()); 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(value()); fw.reset(); if (cvm::debug()) cvm::log("Calculating wall forces for colvar \""+this->name+"\".\n"); // 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]) { size_t i; for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; cvm::increase_depth(); (cvcs[i])->calc_Jacobian_derivative(); cvm::decrease_depth(); } size_t ncvc = 0; fj.reset(); for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; // linear combination is assumed fj += 1.0 / cvm::real((cvcs[i])->sup_coeff) * (cvcs[i])->Jacobian_derivative(); ncvc++; } fj *= (1.0/cvm::real(ncvc)) * 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]) f -= fj; } if (tasks[task_extended_lagrangian]) { cvm::real dt = cvm::dt(); cvm::real f_ext; // the total force is applied to the fictitious mass, while the // atoms only feel the harmonic force // fr: extended coordinate force (without harmonic spring), for output in trajectory // f_ext: total force on extended coordinate (including harmonic spring) // f: - initially, external biasing force // - after this code block, colvar force to be applied to atomic coordinates, ie. spring force fr = f; f_ext = f + (-0.5 * ext_force_k) * this->dist2_lgrad(xr, x); f = (-0.5 * ext_force_k) * this->dist2_rgrad(xr, x); // leapfrog: starting from x_i, f_i, v_(i-1/2) vr += (0.5 * dt) * f_ext / 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) * f_ext / 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() { size_t i; if (cvm::debug()) cvm::log("Communicating forces from colvar \""+this->name+"\".\n"); if (tasks[task_scripted]) { std::vector<cvm::matrix2d<cvm::real> > func_grads; for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; func_grads.push_back(cvm::matrix2d<cvm::real> (x.size(), cvcs[i]->value().size())); } int res = cvm::proxy->run_colvar_gradient_callback(scripted_function, sorted_cvc_values, func_grads); if (res != COLVARS_OK) { if (res == COLVARS_NOT_IMPLEMENTED) { cvm::error("Colvar gradient scripts are not implemented."); } else { cvm::error("Error running colvar gradient script"); } return; } + int grad_index = 0; // index in the scripted gradients, to account for some components being disabled for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; cvm::increase_depth(); // cvc force is colvar force times colvar/cvc Jacobian // (vector-matrix product) - (cvcs[i])->apply_force(colvarvalue(f.as_vector() * func_grads[i], + (cvcs[i])->apply_force(colvarvalue(f.as_vector() * func_grads[grad_index++], cvcs[i]->value().type())); cvm::decrease_depth(); } } else if (x.type() == colvarvalue::type_scalar) { for (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; cvm::increase_depth(); (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 (i = 0; i < cvcs.size(); i++) { if (!cvcs[i]->b_enabled) continue; 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"); } int colvar::set_cvc_flags(std::vector<bool> const &flags) { - size_t i; if (flags.size() != cvcs.size()) { cvm::error("ERROR: Wrong number of CVC flags provided."); return COLVARS_ERROR; } - bool e = false; - for (i = 0; i < cvcs.size(); i++) { - cvcs[i]->b_enabled = flags[i]; - e = e || flags[i]; - } - if (!e) { - cvm::error("ERROR: All CVCs are disabled for this colvar."); - return COLVARS_ERROR; - } + // We cannot enable or disable cvcs in the middle of a timestep or colvar evaluation sequence + // so we store the flags that will be enforced at the next call to calc() + cvc_flags = flags; return COLVARS_OK; } // ******************** 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::log("Error: requesting to histogram the " "collective variable \""+this->name+"\", but a " "pair of lower and upper boundaries must be " "defined.\n"); cvm::set_error_bits(INPUT_ERROR); } 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::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 { if (b_homogeneous) { return (cvcs[0])->dist2(x1, x2); } else { return x1.dist2(x2); } } colvarvalue colvar::dist2_lgrad(colvarvalue const &x1, colvarvalue const &x2) const { if (b_homogeneous) { return (cvcs[0])->dist2_lgrad(x1, x2); } else { return x1.dist2_grad(x2); } } colvarvalue colvar::dist2_rgrad(colvarvalue const &x1, colvarvalue const &x2) const { if (b_homogeneous) { return (cvcs[0])->dist2_rgrad(x1, x2); } else { return x2.dist2_grad(x1); } } void colvar::wrap(colvarvalue &x) const { if (b_homogeneous) { (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::error("Error: the state file does not match the " "configuration file, at colvar \""+name+"\".\n"); } if (check_name.size() == 0) { cvm::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; 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]) { // extended DOF 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]) { // extended DOF 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_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]) { os << " " << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width) << ft_reported; } if (tasks[task_output_applied_force]) { if (tasks[task_extended_lagrangian]) { os << " " << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width) << fr; } else { os << " " << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width) << f; } } return os; } int colvar::write_output_files() { if (cvm::b_analysis) { if (acf.size()) { cvm::log("Writing acf to file \""+acf_outfile+"\".\n"); cvm::ofstream acf_os(acf_outfile.c_str()); if (! acf_os.good()) { cvm::error("Cannot open file \""+acf_outfile+"\".\n", FILE_ERROR); } write_acf(acf_os); acf_os.close(); } if (runave_os.good()) { runave_os.close(); } } return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK); } // ******************** ANALYSIS FUNCTIONS ******************** void colvar::analyze() { 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()) history_p = history.begin(); } int 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.empty() && acf_v_history.empty()) { // first-step operations colvar *cfcv = (acf_colvar_name.size() ? cvm::colvar_by_name(acf_colvar_name) : this); if (colvarvalue::check_types(cfcv->value(), value())) { cvm::error("Error: correlation function between \""+cfcv->name+ "\" and \""+this->name+"\" cannot be calculated, " "because their value types are different.\n", INPUT_ERROR); } 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); size_t i; switch (acf_type) { case acf_vel: // allocate space for the velocities history for (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 (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() ? cvm::colvar_by_name(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; } return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK); } int 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(); ++acf_i; // 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); acf_nframes++; } return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK); } 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); 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); 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.empty()) { runave.type(value().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; std::list<colvarvalue>::iterator xs_i; for (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 (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 79b00c026..8a3e40518 100644 --- a/lib/colvars/colvar.h +++ b/lib/colvars/colvar.h @@ -1,580 +1,583 @@ /// -*- 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::value() /// 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; /// \brief Current value (previously set by calc() or by read_traj()) colvarvalue const & value() const; /// \brief Current actual value (not extended DOF) colvarvalue const & actual_value() const; /// \brief Current velocity (previously set by calc() or by 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 this \link colvar \endlink is a linear /// combination of cvcs with coefficients 1 or -1 bool b_homogeneous; /// \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 Value and gradients computed by user script task_scripted, /// \brief Number of possible tasks task_ntot }; /// Tasks performed by this colvar bool tasks[task_ntot]; /// List of biases that depend on this colvar std::vector<colvarbias *> biases; 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; // TODO: implement functionality to treat these // /// Vector of individual values from CVCs // colvarvalue x_cvc; // /// Jacobian matrix of individual values from CVCs // colvarvalue dx_cvc; /// 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 int enable(colvar::task const &t); /// Disable the specified task void disable(colvar::task const &t); /// Get ready for a run and possibly re-initialize internal data void setup(); /// 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 Enables and disables individual CVCs based on flags int set_cvc_flags(std::vector<bool> const &flags); /// \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 wrap a value into a standard interval /// /// Handles correctly symmetries and periodic boundary conditions void wrap(colvarvalue &x) const; /// Read the analysis tasks int parse_analysis(std::string const &conf); /// Perform analysis tasks void analyze(); /// 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) int write_output_files(); protected: /// Previous value (to calculate velocities during analysis) colvarvalue x_old; /// Time series of values and velocities used in correlation /// 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) int 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) int 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 cvm::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 distance_pairs; class angle; class dihedral; class coordnum; class selfcoordnum; class h_bond; class rmsd; class orientation_angle; class orientation_proj; 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 cartesian; class orientation; protected: /// \brief Array of \link cvc \endlink objects std::vector<cvc *> cvcs; + /// \brief Flags to enable or disable cvcs at next colvar evaluation + std::vector<bool> cvc_flags; + /// \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); private: /// Name of scripted function to be used std::string scripted_function; /// Current cvc values in the order requested by script /// when using scriptedFunction std::vector<const colvarvalue *> sorted_cvc_values; 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 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.type(value()); fb.reset(); } #endif diff --git a/lib/colvars/colvargrid.cpp b/lib/colvars/colvargrid.cpp index 6cc667b51..e2e660b98 100644 --- a/lib/colvars/colvargrid.cpp +++ b/lib/colvars/colvargrid.cpp @@ -1,244 +1,172 @@ /// -*- 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>() { mult = 1; } 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, 1) {} colvar_grid_count::colvar_grid_count(std::vector<colvar *> &colvars, size_t const &def_count) : colvar_grid<size_t>(colvars, def_count, 1) {} -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; -} - - cvm::real colvar_grid_scalar::maximum_value() const { cvm::real max = data[0]; for (size_t i = 0; i < nt; i++) { if (data[i] > max) max = data[i]; } return max; } cvm::real colvar_grid_scalar::minimum_value() const { cvm::real min = data[0]; for (size_t i = 0; i < nt; i++) { if (data[i] < min) min = data[i]; } return min; } cvm::real colvar_grid_scalar::integral() const { cvm::real sum = 0.0; for (size_t i = 0; i < nt; i++) { sum += data[i]; } cvm::real bin_volume = 1.0; for (size_t id = 0; id < widths.size(); id++) { bin_volume *= widths[id]; } return bin_volume * sum; } cvm::real colvar_grid_scalar::entropy() const { cvm::real sum = 0.0; for (size_t i = 0; i < nt; i++) { sum += -1.0 * data[i] * std::log(data[i]); } cvm::real bin_volume = 1.0; for (size_t id = 0; id < widths.size(); id++) { bin_volume *= widths[id]; } return bin_volume * sum; } 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; } diff --git a/lib/colvars/colvargrid.h b/lib/colvars/colvargrid.h index 29d21feb3..ac192ed0e 100644 --- a/lib/colvars/colvargrid.h +++ b/lib/colvars/colvargrid.h @@ -1,1384 +1,1392 @@ /// -*- 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: vector colvars are treated as arrays 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 such as atomic gradients) 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::error("Error: exceeding bounds in colvar_grid.\n", BUG_ERROR); return 0; } } } 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 int setup(std::vector<int> const &nx_i, T const &t = T(), size_t const &mult_i = 1) { if (cvm::debug()) { cvm::log("Allocating grid: multiplicity = "+cvm::to_str(mult_i)+ ", dimensions = "+cvm::to_str(nx_i)+".\n"); } mult = mult_i; data.clear(); nx = nx_i; nd = nx.size(); nxc.resize(nd); // setup dimensions nt = mult; for (int i = nd-1; i >= 0; i--) { if (nx[i] <= 0) { cvm::error("Error: providing an invalid number of grid points, "+ cvm::to_str(nx[i])+".\n", BUG_ERROR); return COLVARS_ERROR; } nxc[i] = nt; nt *= nx[i]; } if (cvm::debug()) { cvm::log("Total number of grid elements = "+cvm::to_str(nt)+".\n"); } data.reserve(nt); data.assign(nt, t); return COLVARS_OK; } /// \brief Allocate data (allow initialization also after construction) int setup() { return setup(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; mult = 1; this->setup(); } /// Destructor virtual ~colvar_grid() {} /// \brief "Almost copy-constructor": only copies configuration /// parameters from another grid, but doesn't reallocate stuff; /// setup() must be called after that; colvar_grid(colvar_grid<T> const &g) : nd(g.nd), nx(g.nx), mult(g.mult), data(), cv(g.cv), actual_value(g.actual_value), lower_boundaries(g.lower_boundaries), upper_boundaries(g.upper_boundaries), periodic(g.periodic), hard_lower_boundaries(g.hard_lower_boundaries), hard_upper_boundaries(g.hard_upper_boundaries), widths(g.widths), has_data(false) { 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 mult_i = 1) : has_data(false) { save_delimiters = false; this->setup(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 mult_i = 1, bool margin = false) : has_data(false) { save_delimiters = false; this->init_from_colvars(colvars, t, mult_i, margin); } int init_from_colvars(std::vector<colvar *> const &colvars, T const &t = T(), size_t mult_i = 1, bool margin = false) { if (cvm::debug()) { cvm::log("Reading grid configuration from collective variables.\n"); } cv = colvars; nd = colvars.size(); mult = mult_i; size_t i; for (i = 0; i < cv.size(); i++) { if (!cv[i]->tasks[colvar::task_lower_boundary] || !cv[i]->tasks[colvar::task_upper_boundary]) { cvm::error("Tried to initialize a grid on a " "variable with undefined boundaries.\n", INPUT_ERROR); return COLVARS_ERROR; } } if (cvm::debug()) { cvm::log("Allocating a grid for "+cvm::to_str(colvars.size())+ " collective variables, multiplicity = "+cvm::to_str(mult_i)+".\n"); } for (i = 0; i < cv.size(); i++) { if (cv[i]->value().type() != colvarvalue::type_scalar) { cvm::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", INPUT_ERROR); return COLVARS_ERROR; } if (cv[i]->width <= 0.0) { cvm::error("Tried to initialize a grid on a " "variable with negative or zero width.\n", INPUT_ERROR); return COLVARS_ERROR; } 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); } } this->init_from_boundaries(); return this->setup(); } int init_from_boundaries(T const &t = T(), size_t const &mult_i = 1) { if (cvm::debug()) { cvm::log("Configuring grid dimensions from colvars boundaries.\n"); } // these will have to be updated nx.clear(); nxc.clear(); nt = 0; for (size_t i = 0; i < lower_boundaries.size(); i++) { 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.push_back(nbins_round); } return COLVARS_OK; } /// 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::error("Trying to wrap illegal index vector (non-PBC) for a grid point: " + cvm::to_str(ix), BUG_ERROR); return; } } } } /// \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 change from this to other_grid /// and store the result in this. /// this_grid := other_grid - this_grid /// Grids must have the same dimensions. void delta_grid(colvar_grid<T> const &other_grid) { if (other_grid.multiplicity() != this->multiplicity()) { cvm::error("Error: trying to subtract two grids with " "different multiplicity.\n"); return; } if (other_grid.data.size() != this->data.size()) { cvm::error("Error: trying to subtract two grids with " "different size.\n"); return; } for (size_t i = 0; i < data.size(); i++) { data[i] = other_grid.data[i] - data[i]; } has_data = true; } /// \brief Copy data from another grid of the same type, AND /// identical definition (boundaries, widths) /// Added for shared ABF. void copy_grid(colvar_grid<T> const &other_grid) { if (other_grid.multiplicity() != this->multiplicity()) { cvm::error("Error: trying to copy two grids with " "different multiplicity.\n"); return; } if (other_grid.data.size() != this->data.size()) { cvm::error("Error: trying to copy two grids with " "different size.\n"); return; } for (size_t i = 0; i < data.size(); i++) { data[i] = other_grid.data[i]; } has_data = true; } /// \brief Extract the grid data as they are represented in memory. /// Put the results in "out_data". void raw_data_out(T* out_data) const { for (size_t i = 0; i < data.size(); i++) out_data[i] = data[i]; } /// \brief Input the data as they are represented in memory. void raw_data_in(const T* in_data) { for (size_t i = 0; i < data.size(); i++) data[i] = in_data[i]; has_data = true; } /// \brief Size of the data as they are represented in memory. size_t raw_data_num() const { return data.size(); } /// \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++) 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++) 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::error("Error: trying to merge two grids with values of " "different multiplicity.\n"); return; } 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::error("Error: trying to sum togetehr two grids with values of " "different multiplicity.\n"); return; } if (scale_factor != 1.0) for (size_t i = 0; i < data.size(); i++) { data[i] += scale_factor * other_grid.data[i]; } 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) { size_t i; os << "grid_parameters {\n n_colvars " << nd << "\n"; os << " lower_boundaries "; for (i = 0; i < nd; i++) os << " " << lower_boundaries[i]; os << "\n"; os << " upper_boundaries "; for (i = 0; i < nd; i++) os << " " << upper_boundaries[i]; os << "\n"; os << " widths "; for (i = 0; i < nd; i++) os << " " << widths[i]; os << "\n"; os << " sizes "; for (i = 0; i < nd; i++) os << " " << nx[i]; os << "\n"; os << "}\n"; return os; } /// Read a grid definition from a config string int parse_params(std::string const &conf) { if (cvm::debug()) cvm::log("Reading grid configuration from string.\n"); 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::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"); return COLVARS_ERROR; } } 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); // support also camel case colvarparse::get_keyval(conf, "lowerBoundaries", lower_boundaries, lower_boundaries, colvarparse::parse_silent); colvarparse::get_keyval(conf, "upperBoundaries", 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); if (nd < lower_boundaries.size()) nd = lower_boundaries.size(); if (! actual_value.size()) actual_value.assign(nd, false); if (! periodic.size()) periodic.assign(nd, false); if (! widths.size()) widths.assign(nd, 1.0); bool new_params = false; if (old_nx.size()) { 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; } } } else { new_params = true; } // reallocate the array in case the grid params have just changed if (new_params) { init_from_boundaries(); // data.resize(0); // no longer needed: setup calls clear() return this->setup(nx, T(), mult); } return COLVARS_OK; } /// \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) || (std::sqrt(cv[i]->dist2(cv[i]->upper_boundary, upper_boundaries[i])) > 1.0E-10) || (std::sqrt(cv[i]->dist2(cv[i]->width, widths[i])) > 1.0E-10) ) { cvm::error("Error: restart information for a grid is " "inconsistent with that of its colvars.\n"); return; } } } /// \brief Check that the grid information inside (boundaries, /// widths, ...) is consistent with that 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) || (std::fabs(other_grid.upper_boundaries[i] - upper_boundaries[i]) > 1.0E-10) || (std::fabs(other_grid.widths[i] - widths[i]) > 1.0E-10) || (data.size() != other_grid.data.size()) ) { cvm::error("Error: inconsistency between " "two grids that are supposed to be equal, " "aside from the data stored.\n"); return; } } } + /// \brief Read grid entry in restart file + std::istream & 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; + } + + /// \brief Write grid entry in restart file + std::ostream & write_restart(std::ostream &os) + { + write_params(os); + write_raw(os); + return os; + } + + /// \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); cvm::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"); return is; } } } has_data = true; return is; } /// \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::error("Cannot read grid file: missing reference to colvars."); return is; } if ( !(is >> hash) || (hash != "#") ) { cvm::error("Error reading grid at position "+ cvm::to_str(is.tellg())+" in stream(read \"" + hash + "\")\n"); return is; } is >> n; if ( n != nd ) { cvm::error("Error reading grid: wrong number of collective variables.\n"); return is; } 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::error("Error reading grid at position "+ cvm::to_str(is.tellg())+" in stream(read \"" + hash + "\")\n"); return is; } 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; 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 Write the grid data without labels, as they are /// represented in memory /// \param buf_size Number of values per line std::ostream & write_opendx(std::ostream &os) { // write the header os << "object 1 class gridpositions counts"; int icv; for (icv = 0; icv < number_of_colvars(); icv++) { os << " " << number_of_points(icv); } os << "\n"; os << "origin"; for (icv = 0; icv < number_of_colvars(); icv++) { os << " " << (lower_boundaries[icv].real_value + 0.5 * widths[icv]); } os << "\n"; for (icv = 0; icv < number_of_colvars(); icv++) { os << "delta"; for (size_t icv2 = 0; icv2 < number_of_colvars(); icv2++) { if (icv == icv2) os << " " << widths[icv]; else os << " " << 0.0; } os << "\n"; } os << "object 2 class gridconnections counts"; for (icv = 0; icv < number_of_colvars(); icv++) { os << " " << number_of_points(icv); } os << "\n"; os << "object 3 class array type double rank 0 items " << number_of_points() << " data follows\n"; write_raw(os); os << "object \"collective variables scalar field\" class field\n"; return os; } }; /// \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 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::error("Finite differences available in dimension 2 only."); return grad; } for (unsigned 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; } /// \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::error("Error: trying to access a component " "larger than 1 in a scalar data grid.\n"); return 0.; } 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::error("Error: trying to access a component " "larger than 1 in a scalar data grid.\n"); return; } 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 cvm::real maximum_value() const; /// \brief Return the lowest value cvm::real minimum_value() const; /// \brief Calculates the integral of the map (uses widths if they are defined) cvm::real integral() const; /// \brief Assuming that the map is a normalized probability density, /// calculates the entropy (uses widths if they are defined) cvm::real entropy() const; 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.h b/lib/colvars/colvarmodule.h index e1d25b1d9..8fe92cea1 100644 --- a/lib/colvars/colvarmodule.h +++ b/lib/colvars/colvarmodule.h @@ -1,625 +1,625 @@ /// -*- c++ -*- #ifndef COLVARMODULE_H #define COLVARMODULE_H #ifndef COLVARS_VERSION -#define COLVARS_VERSION "2015-07-21" +#define COLVARS_VERSION "2015-08-12" #endif #ifndef COLVARS_DEBUG #define COLVARS_DEBUG false #endif /// \file colvarmodule.h /// \brief Collective variables main module /// /// This file declares the main class for defining and manipulating /// collective variables: there should be only one instance of this /// class, because several variables are made static (i.e. they are /// shared between all object instances) to be accessed from other /// objects. // Internal method return codes #define COLVARS_NOT_IMPLEMENTED -2 #define COLVARS_ERROR -1 #define COLVARS_OK 0 // On error, values of the colvars module error register #define GENERAL_ERROR 1 #define FILE_ERROR (1<<1) #define MEMORY_ERROR (1<<2) #define BUG_ERROR (1<<3) // Inconsistent state indicating bug #define INPUT_ERROR (1<<4) // out of bounds or inconsistent input #define DELETE_COLVARS (1<<5) // Instruct the caller to delete cvm #define FATAL_ERROR (1<<6) // Should be set, or not, together with other bits #include <iostream> #include <iomanip> #include <string> #include <cstring> #include <sstream> #include <fstream> #include <cmath> #include <vector> #include <list> #ifdef NAMD_VERSION // use Lustre-friendly wrapper to POSIX write() #include "fstream_namd.h" #endif class colvarparse; class colvar; class colvarbias; class colvarproxy; class colvarscript; /// \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; // TODO colvarscript should be unaware of colvarmodule's internals friend class colvarscript; /// Defining an abstract real number allows to switch precision typedef double real; /// Residue identifier typedef int residue_id; class rvector; template <class T> class vector1d; template <class T> 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; /// Module-wide error state /// see constants at the top of this file static int errorCode; static inline void set_error_bits(int code) { errorCode |= code; } static inline int get_error() { return errorCode; } static inline void clear_error() { errorCode = 0; } /// Current step number static long it; /// Starting step number for this run static long it_restart; /// Return the current step number from the beginning of this run static inline long step_relative() { return it - it_restart; } /// Return the current step number from the beginning of the whole /// calculation static inline long 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; /// 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 restraint biases initialized (no limit on the /// number) static size_t n_rest_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(colvarproxy *proxy); /// Destructor ~colvarmodule(); /// Actual function called by the destructor int reset(); /// Open a config file, load its contents, and pass it to config_string() int read_config_file(char const *config_file_name); /// \brief Parse a config string assuming it is a complete configuration /// (i.e. calling all parse functions) int read_config_string(std::string const &conf); /// \brief Parse a "clean" config string (no comments) int parse_config(std::string &conf); // Parse functions (setup internal data based on a string) /// Parse the few module's global parameters int parse_global_params(std::string const &conf); /// Parse and initialize collective variables int parse_colvars(std::string const &conf); /// Parse and initialize collective variable biases int parse_biases(std::string const &conf); /// Parse and initialize collective variable biases of a specific type template <class bias_type> int parse_biases_type(std::string const &conf, char const *keyword, size_t &bias_count); /// Test error condition and keyword parsing /// on error, delete new bias bool check_new_bias(std::string &conf, char const *key); // "Setup" functions (change internal data based on related data // from the proxy that may change during program execution) // No additional parsing is done within these functions /// (Re)initialize internal data (currently used by LAMMPS) /// Also calls setup() member functions of colvars and biases int setup(); /// (Re)initialize and (re)read the input state file calling read_restart() int setup_input(); /// (Re)initialize the output trajectory and state file (does not write it yet) int setup_output(); #ifdef NAMD_VERSION typedef ofstream_namd ofstream; #else typedef std::ofstream ofstream; #endif /// Read the input restart file std::istream & read_restart(std::istream &is); /// Write the output restart file std::ostream & write_restart(std::ostream &os); /// Open a trajectory file if requested (and leave it open) int open_traj_file(std::string const &file_name); /// Close it int close_traj_file(); /// Write in the trajectory file std::ostream & write_traj(std::ostream &os); /// Write explanatory labels in the trajectory file std::ostream & write_traj_label(std::ostream &os); /// Write all FINAL output files int write_output_files(); /// Backup a file before writing it static int backup_file(char const *filename); /// Look up a bias by name; returns NULL if not found static colvarbias * bias_by_name(std::string const &name); /// Look up a colvar by name; returns NULL if not found static colvar * colvar_by_name(std::string const &name); /// Load new configuration for the given bias - /// currently works for harmonic (force constant and/or centers) int change_configuration(std::string const &bias_name, std::string const &conf); /// Read a colvar value std::string read_colvar(std::string const &name); /// 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); /// Give the total number of bins for a given bias. int bias_bin_num(std::string const &bias_name); /// Calculate the bin index for a given bias. int bias_current_bin(std::string const &bias_name); //// Give the count at a given bin index. int bias_bin_count(std::string const &bias_name, size_t bin_index); //// Share among replicas. int bias_share(std::string const &bias_name); /// Calculate collective variables and biases int calc(); /// Perform analysis int analyze(); /// \brief Read a collective variable trajectory (post-processing /// only, not called at runtime) int read_traj(char const *traj_filename, long traj_read_begin, long traj_read_end); /// Quick conversion of an object to a string template<typename T> static std::string to_str(T const &x, size_t const &width = 0, size_t const &prec = 0); /// Quick conversion of a vector of objects to a string template<typename T> static std::string to_str(std::vector<T> const &x, size_t const &width = 0, size_t const &prec = 0); /// Reduce the number of characters in a string static inline std::string wrap_string(std::string const &s, size_t const &nchars) { if (!s.size()) return std::string(nchars, ' '); else return ( (s.size() <= size_t(nchars)) ? (s+std::string(nchars-s.size(), ' ')) : (std::string(s, 0, nchars)) ); } /// Number of characters to represent a time step static size_t const it_width; /// Number of digits to represent a collective variables value(s) static size_t const cv_prec; /// Number of characters to represent a collective variables value(s) static size_t const cv_width; /// Number of digits to represent the collective variables energy static size_t const en_prec; /// Number of characters to represent the collective variables energy static size_t const en_width; /// Line separator in the log output static std::string const line_marker; // proxy functions /// \brief Value of the unit for atomic coordinates with respect to /// angstroms (used by some variables for hard-coded default values) static real unit_angstrom(); /// \brief Boltmann constant static real boltzmann(); /// \brief Temperature of the simulation (K) static real temperature(); /// \brief Time step of MD integrator (fs) static real dt(); /// Request calculation of system force from MD engine static void request_system_force(); /// Print a message to the main log static void log(std::string const &message); /// Print a message to the main log and exit with error code static void fatal_error(std::string const &message); /// Print a message to the main log and set global error code static void error(std::string const &message, int code = GENERAL_ERROR); /// Print a message to the main log and exit normally static void exit(std::string const &message); // Replica exchange commands. static bool replica_enabled(); static int replica_index(); static int replica_num(); static void replica_comm_barrier(); static int replica_comm_recv(char* msg_data, int buf_len, int src_rep); static int replica_comm_send(char* msg_data, int msg_len, int dest_rep); /// \brief Get the distance between two atomic positions with pbcs handled /// correctly static rvector position_distance(atom_pos const &pos1, atom_pos const &pos2); /// \brief Get the square distance between two positions (with /// periodic boundary conditions handled transparently) /// /// Note: in the case of periodic boundary conditions, this provides /// an analytical square distance (while taking the square of /// position_distance() would produce leads to a cusp) static real position_dist2(atom_pos const &pos1, atom_pos const &pos2); /// \brief Get the closest periodic image to a reference position /// \param pos The position to look for the closest periodic image /// \param ref_pos (optional) The reference position static void select_closest_image(atom_pos &pos, atom_pos const &ref_pos); /// \brief Perform select_closest_image() on a set of atomic positions /// /// After that, distance vectors can then be calculated directly, /// without using position_distance() static void select_closest_images(std::vector<atom_pos> &pos, atom_pos const &ref_pos); /// \brief 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 int 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 int 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 /// (PDB or XYZ) static int 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); /// \brief Load the coordinates for a group of atoms from an /// XYZ file static int load_coords_xyz(char const *filename, std::vector<atom_pos> &pos, const std::vector<int> &indices); /// 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 colvarmodule::ofstream cv_traj_os; /// Appending to the existing trajectory file? bool cv_traj_append; /// Output restart file colvarmodule::ofstream restart_out_os; /// \brief Counter for the current depth in the object hierarchy (useg e.g. in output static size_t depth; /// Use scripted colvars forces? static bool use_scripted_forces; public: /// \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; /// 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(); static inline bool scripted_forces() { return use_scripted_forces; } }; /// Shorthand for the frequently used type prefix typedef colvarmodule cvm; #include "colvartypes.h" std::ostream & operator << (std::ostream &os, cvm::rvector const &v); std::istream & operator >> (std::istream &is, cvm::rvector &v); template<typename T> std::string cvm::to_str(T const &x, size_t const &width, size_t const &prec) { std::ostringstream os; if (width) os.width(width); if (prec) { os.setf(std::ios::scientific, std::ios::floatfield); os.precision(prec); } os << x; return os.str(); } template<typename T> std::string cvm::to_str(std::vector<T> const &x, size_t const &width, size_t const &prec) { if (!x.size()) return std::string(""); std::ostringstream os; if (prec) { os.setf(std::ios::scientific, std::ios::floatfield); } os << "{ "; if (width) os.width(width); if (prec) os.precision(prec); os << x[0]; for (size_t i = 1; i < x.size(); i++) { os << ", "; if (width) os.width(width); if (prec) os.precision(prec); os << x[i]; } os << " }"; return os.str(); } #include "colvarproxy.h" inline cvm::real cvm::unit_angstrom() { return proxy->unit_angstrom(); } inline cvm::real cvm::boltzmann() { return proxy->boltzmann(); } inline cvm::real cvm::temperature() { return proxy->temperature(); } inline cvm::real cvm::dt() { return proxy->dt(); } // Replica exchange commands inline bool cvm::replica_enabled() { return proxy->replica_enabled(); } inline int cvm::replica_index() { return proxy->replica_index(); } inline int cvm::replica_num() { return proxy->replica_num(); } inline void cvm::replica_comm_barrier() { return proxy->replica_comm_barrier(); } inline int cvm::replica_comm_recv(char* msg_data, int buf_len, int src_rep) { return proxy->replica_comm_recv(msg_data,buf_len,src_rep); } inline int cvm::replica_comm_send(char* msg_data, int msg_len, int dest_rep) { return proxy->replica_comm_send(msg_data,msg_len,dest_rep); } 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 cvm::real cvm::rand_gaussian(void) { return proxy->rand_gaussian(); } #endif