| version 1.6 | version 1.7 |
|---|
| |
| | |
| #include "colvardeps.h" | #include "colvardeps.h" |
| | |
| | colvardeps::colvardeps() |
| | : time_step_factor (1) {} |
| | |
| colvardeps::~colvardeps() { | colvardeps::~colvardeps() { |
| size_t i; | size_t i; |
| | |
| | // Protest if we are deleting an object while a parent object may still depend on it |
| | // Another possible strategy is to have the child unlist itself from the parent's children |
| | if (parents.size()) { |
| | cvm::log("Warning: destroying \"" + description + "\" before its parents objects:"); |
| | for (i=0; i<parents.size(); i++) { |
| | cvm::log(parents[i]->description); |
| | } |
| | } |
| | |
| // Do not delete features if it's static | // Do not delete features if it's static |
| // for (i=0; i<features.size(); i++) { | // for (i=0; i<features.size(); i++) { |
| // if (features[i] != NULL) delete features[i]; | // if (features[i] != NULL) delete features[i]; |
| // } | // } |
| | |
| remove_all_children(); | remove_all_children(); |
| | } |
| | |
| // Protest if we are deleting an object while a parent object may still depend on it | |
| // Another possible strategy is to have the child unlist itself from the parent's children | void colvardeps::free_children_deps() { |
| if (parents.size()) { | // Dereference children requirements of all enabled features |
| cvm::log("Warning: destroying " + description + " before its parents objects:"); | // Useful when object is destroyed or set inactive |
| for (i=0; i<parents.size(); i++) { | // CAUTION: when setting the parent object inactive, disable "active" first |
| cvm::log(parents[i]->description); | // then call this, to avoid double-dereferencing the deps of "active" |
| | |
| | // Cannot be in the base class destructor because it needs the derived class features() |
| | size_t i,j,fid; |
| | |
| | if (cvm::debug()) cvm::log("DEPS: freeing children deps for " + description); |
| | |
| | for (fid = 0; fid < feature_states.size(); fid++) { |
| | if (is_enabled(fid)) { |
| | for (i=0; i<features()[fid]->requires_children.size(); i++) { |
| | int g = features()[fid]->requires_children[i]; |
| | for (j=0; j<children.size(); j++) { |
| | cvm::increase_depth(); |
| | if (cvm::debug()) cvm::log("DEPS: dereferencing children's " |
| | + children[j]->features()[g]->description); |
| | children[j]->decr_ref_count(g); |
| | cvm::decrease_depth(); |
| | } |
| | } |
| | } |
| | } |
| | } |
| | |
| | |
| | // re-enable children features (and increase ref count accordingly) |
| | // So free_children_deps() can be called whenever an object becomes inactive |
| | void colvardeps::restore_children_deps() { |
| | size_t i,j,fid; |
| | for (fid = 0; fid < feature_states.size(); fid++) { |
| | if (is_enabled(fid)) { |
| | for (i=0; i<features()[fid]->requires_children.size(); i++) { |
| | int g = features()[fid]->requires_children[i]; |
| | for (j=0; j<children.size(); j++) { |
| | cvm::increase_depth(); |
| | if (cvm::debug()) cvm::log("DEPS: re-enabling children's " |
| | + children[j]->features()[g]->description); |
| | children[j]->enable(g, false, false); |
| | cvm::decrease_depth(); |
| | } |
| | } |
| } | } |
| } | } |
| } | } |
| |
| | |
| | |
| void colvardeps::set_enabled(int feature_id, bool truefalse) { | void colvardeps::set_enabled(int feature_id, bool truefalse) { |
| // if (!is_static(feature_id)) { | |
| // cvm::error("Cannot set feature " + features()[feature_id]->description + " statically in " + description + ".\n"); | |
| // return; | |
| // } | |
| if (truefalse) { | if (truefalse) { |
| // Resolve dependencies too | |
| enable(feature_id); | enable(feature_id); |
| } else { | } else { |
| feature_states[feature_id].enabled = false; | disable(feature_id); |
| } | } |
| } | } |
| | |
| |
| colvarparse::Parse_Mode const parse_mode) | colvarparse::Parse_Mode const parse_mode) |
| { | { |
| if (!is_user(feature_id)) { | if (!is_user(feature_id)) { |
| cvm::error("Cannot set feature " + features()[feature_id]->description + " from user input in " + description + ".\n"); | cvm::error("Cannot set feature \"" + features()[feature_id]->description + "\" from user input in \"" + description + "\".\n"); |
| return false; | return false; |
| } | } |
| bool value; | bool value; |
| |
| | |
| if (cvm::debug()) { | if (cvm::debug()) { |
| cvm::log("DEPS: " + description + | cvm::log("DEPS: " + description + |
| (dry_run ? " testing " : " requiring ") + | (dry_run ? " testing " : " enabling ") + |
| "\"" + f->description +"\""); | "\"" + f->description +"\""); |
| } | } |
| | |
| if (fs->enabled) { | if (fs->enabled) { |
| // Do not try to solve deps if already enabled | if (!(dry_run || toplevel)) { |
| | // This is a dependency |
| | // Prevent disabling this feature as long |
| | // as requirement is enabled |
| | fs->ref_count++; |
| | if (cvm::debug()) |
| | cvm::log("DEPS: bumping ref_count to " + cvm::to_str(fs->ref_count)); |
| | } |
| | // Do not try to further resolve deps |
| return COLVARS_OK; | return COLVARS_OK; |
| } | } |
| | |
| | std::string feature_type_descr = is_static(feature_id) ? "Static" : |
| | (is_dynamic(feature_id) ? "Dynamic" : "User-controlled"); |
| | |
| if (!fs->available) { | if (!fs->available) { |
| if (!dry_run) { | if (!dry_run) { |
| if (toplevel) { | if (toplevel) { |
| cvm::error("Error: Feature unavailable: \"" + f->description + "\" in " + description + "."); | cvm::error("Error: " + feature_type_descr + " feature unavailable: \"" |
| | + f->description + "\" in " + description + "."); |
| } else { | } else { |
| cvm::log("Feature unavailable: \"" + f->description + "\" in " + description); | cvm::log(feature_type_descr + " feature unavailable: \"" |
| | + f->description + "\" in " + description + "."); |
| } | } |
| } | } |
| return COLVARS_ERROR; | return COLVARS_ERROR; |
| |
| | |
| if (!toplevel && !is_dynamic(feature_id)) { | if (!toplevel && !is_dynamic(feature_id)) { |
| if (!dry_run) { | if (!dry_run) { |
| cvm::log("Non-dynamic feature : \"" + f->description | cvm::log(feature_type_descr + " feature \"" + f->description |
| + "\" in " + description + " may not be enabled as a dependency.\n"); | + "\" may not be enabled as a dependency in " + description + ".\n"); |
| } | } |
| return COLVARS_ERROR; | return COLVARS_ERROR; |
| } | } |
| | |
| // 1) enforce exclusions | // 1) enforce exclusions |
| | // reminder: exclusions must be mutual for this to work |
| for (i=0; i<f->requires_exclude.size(); i++) { | for (i=0; i<f->requires_exclude.size(); i++) { |
| feature *g = features()[f->requires_exclude[i]]; | feature *g = features()[f->requires_exclude[i]]; |
| if (cvm::debug()) | if (cvm::debug()) |
| cvm::log(f->description + " requires exclude " + g->description); | cvm::log(f->description + " requires exclude " + g->description); |
| if (is_enabled(f->requires_exclude[i])) { | if (is_enabled(f->requires_exclude[i])) { |
| if (!dry_run) { | if (!dry_run) { |
| cvm::log("Features \"" + f->description + "\" is incompatible with \"" | cvm::log("Feature \"" + f->description + "\" is incompatible with \"" |
| + g->description + "\" in " + description); | + g->description + "\" in " + description + "."); |
| if (toplevel) { | if (toplevel) { |
| cvm::error("Error: Failed dependency in " + description + "."); | cvm::error("Error: Failed dependency in " + description + "."); |
| } | } |
| |
| res = enable(g, true, false); // see if available | res = enable(g, true, false); // see if available |
| if (res == COLVARS_OK) { | if (res == COLVARS_OK) { |
| ok = true; | ok = true; |
| if (!dry_run) enable(g, false, false); // Require again, for real | if (!dry_run) { |
| | enable(g, false, false); // Require again, for real |
| | fs->alternate_refs.push_back(g); // We remember we enabled this |
| | // so we can free it if this feature gets disabled |
| | } |
| break; | break; |
| } | } |
| } | } |
| |
| } | } |
| | |
| // 4) solve deps in children | // 4) solve deps in children |
| | // if the object is inactive, we solve but do not enable: will be enabled |
| | // when the object becomes active |
| for (i=0; i<f->requires_children.size(); i++) { | for (i=0; i<f->requires_children.size(); i++) { |
| int g = f->requires_children[i]; | int g = f->requires_children[i]; |
| for (j=0; j<children.size(); j++) { | for (j=0; j<children.size(); j++) { |
| cvm::increase_depth(); | cvm::increase_depth(); |
| res = children[j]->enable(g, dry_run, false); | res = children[j]->enable(g, dry_run || !is_enabled(), false); |
| cvm::decrease_depth(); | cvm::decrease_depth(); |
| if (res != COLVARS_OK) { | if (res != COLVARS_OK) { |
| if (!dry_run) { | if (!dry_run) { |
| |
| return res; | return res; |
| } | } |
| } | } |
| // If we've just touched the features of child objects, refresh them | } |
| if (!dry_run && f->requires_children.size() != 0) { | |
| | // Actually enable feature only once everything checks out |
| | if (!dry_run) { |
| | fs->enabled = true; |
| | // This should be the only reference |
| | if (!toplevel) fs->ref_count = 1; |
| | if (feature_id == 0) { |
| | // Waking up this object, enable all deps in children |
| | restore_children_deps(); |
| | } |
| | do_feature_side_effects(feature_id); |
| | if (cvm::debug()) |
| | cvm::log("DEPS: feature \"" + f->description + "\" in " |
| | + description + " enabled, ref_count = 1."); |
| | } |
| | return COLVARS_OK; |
| | } |
| | |
| | |
| | int colvardeps::disable(int feature_id) { |
| | size_t i, j; |
| | feature *f = features()[feature_id]; |
| | feature_state *fs = &feature_states[feature_id]; |
| | |
| | if (cvm::debug()) cvm::log("DEPS: disabling feature \"" |
| | + f->description + "\" in " + description); |
| | |
| | if (fs->enabled == false) { |
| | return COLVARS_OK; |
| | } |
| | |
| | if (fs->ref_count > 1) { |
| | cvm::error("Error: cannot disable feature \"" + f->description |
| | + "\" in " + description + " because of " + cvm::to_str(fs->ref_count-1) |
| | + " remaining references.\n" ); |
| | return COLVARS_ERROR; |
| | } |
| | |
| | // internal deps (self) |
| | for (i=0; i<f->requires_self.size(); i++) { |
| | if (cvm::debug()) cvm::log("DEPS: dereferencing self " |
| | + features()[f->requires_self[i]]->description); |
| | decr_ref_count(f->requires_self[i]); |
| | } |
| | |
| | // alternates |
| | for (i=0; i<fs->alternate_refs.size(); i++) { |
| | if (cvm::debug()) cvm::log("DEPS: dereferencing alt " |
| | + features()[fs->alternate_refs[i]]->description); |
| | decr_ref_count(fs->alternate_refs[i]); |
| | } |
| | // Forget these, now that they are dereferenced |
| | fs->alternate_refs.clear(); |
| | |
| | // deps in children |
| | // except if the object is inactive, then children dependencies |
| | // have already been dereferenced by this function |
| | // (or never referenced if feature was enabled while the object |
| | // was inactive) |
| | if (is_enabled()) { |
| | for (i=0; i<f->requires_children.size(); i++) { |
| | int g = f->requires_children[i]; |
| for (j=0; j<children.size(); j++) { | for (j=0; j<children.size(); j++) { |
| children[j]->refresh_deps(); | cvm::increase_depth(); |
| | if (cvm::debug()) cvm::log("DEPS: dereferencing children's " |
| | + children[j]->features()[g]->description); |
| | children[j]->decr_ref_count(g); |
| | cvm::decrease_depth(); |
| } | } |
| } | } |
| } | } |
| | |
| // Actually enable feature only once everything checks out | fs->enabled = false; |
| if (!dry_run) fs->enabled = true; | fs->ref_count = 0; |
| | if (feature_id == 0) { |
| | // Putting this object to sleep |
| | free_children_deps(); |
| | } |
| return COLVARS_OK; | return COLVARS_OK; |
| } | } |
| | |
| | int colvardeps::decr_ref_count(int feature_id) { |
| | int &rc = feature_states[feature_id].ref_count; |
| | feature *f = features()[feature_id]; |
| | |
| | if (cvm::debug()) |
| | cvm::log("DEPS: decreasing reference count of \"" + f->description |
| | + "\" in " + description + ".\n"); |
| | |
| | if (rc <= 0) { |
| | cvm::error("Error: cannot decrease reference count of feature \"" + f->description + "\" in " + description + ", which is " + cvm::to_str(rc) + ".\n"); |
| | return COLVARS_ERROR; |
| | } |
| | |
| | rc--; |
| | if (rc == 0 && f->is_dynamic()) { |
| | // we can auto-disable this feature |
| | if (cvm::debug()) |
| | cvm::log("DEPS will now auto-disable dynamic feature \"" + f->description |
| | + "\" in " + description + ".\n"); |
| | disable(feature_id); |
| | } |
| | return COLVARS_OK; |
| | } |
| | |
| // disable() { | |
| // | |
| // // we need refs to parents to walk up the deps tree! | |
| // // or refresh | |
| // } | |
| void colvardeps::init_feature(int feature_id, const char *description, feature_type type) { | void colvardeps::init_feature(int feature_id, const char *description, feature_type type) { |
| features()[feature_id]->description = description; | features()[feature_id]->description = description; |
| features()[feature_id]->type = type; | features()[feature_id]->type = type; |
| |
| init_feature(f_cvb_active, "active", f_type_dynamic); | init_feature(f_cvb_active, "active", f_type_dynamic); |
| f_req_children(f_cvb_active, f_cv_active); | f_req_children(f_cvb_active, f_cv_active); |
| | |
| | init_feature(f_cvb_awake, "awake", f_type_static); |
| | f_req_self(f_cvb_awake, f_cvb_active); |
| | |
| init_feature(f_cvb_apply_force, "apply force", f_type_user); | init_feature(f_cvb_apply_force, "apply force", f_type_user); |
| f_req_children(f_cvb_apply_force, f_cv_gradient); | f_req_children(f_cvb_apply_force, f_cv_gradient); |
| | |
| |
| // Colvars must be either a linear combination, or scalar (and polynomial) or scripted | // Colvars must be either a linear combination, or scalar (and polynomial) or scripted |
| f_req_alt3(f_cv_active, f_cv_scalar, f_cv_linear, f_cv_scripted); | f_req_alt3(f_cv_active, f_cv_scalar, f_cv_linear, f_cv_scripted); |
| | |
| | init_feature(f_cv_awake, "awake", f_type_static); |
| | f_req_self(f_cv_awake, f_cv_active); |
| | |
| init_feature(f_cv_gradient, "gradient", f_type_dynamic); | init_feature(f_cv_gradient, "gradient", f_type_dynamic); |
| f_req_children(f_cv_gradient, f_cvc_gradient); | f_req_children(f_cv_gradient, f_cvc_gradient); |
| | |
| init_feature(f_cv_collect_gradient, "collect gradient", f_type_dynamic); | init_feature(f_cv_collect_gradient, "collect gradient", f_type_dynamic); |
| f_req_self(f_cv_collect_gradient, f_cv_gradient); | f_req_self(f_cv_collect_gradient, f_cv_gradient); |
| f_req_self(f_cv_collect_gradient, f_cv_scalar); | f_req_self(f_cv_collect_gradient, f_cv_scalar); |
| | // The following exlusion could be lifted by implementing the feature |
| | f_req_exclude(f_cv_collect_gradient, f_cv_scripted); |
| | |
| init_feature(f_cv_fdiff_velocity, "fdiff_velocity", f_type_dynamic); | init_feature(f_cv_fdiff_velocity, "fdiff_velocity", f_type_dynamic); |
| | |
| |
| init_feature(f_cv_scalar, "scalar", f_type_static); | init_feature(f_cv_scalar, "scalar", f_type_static); |
| init_feature(f_cv_linear, "linear", f_type_static); | init_feature(f_cv_linear, "linear", f_type_static); |
| init_feature(f_cv_homogeneous, "homogeneous", f_type_static); | init_feature(f_cv_homogeneous, "homogeneous", f_type_static); |
| | |
| | // because total forces are obtained from the previous time step, |
| | // we cannot (currently) have colvar values and total forces for the same timestep |
| | init_feature(f_cv_multiple_ts, "multiple timestep colvar"); |
| | f_req_exclude(f_cv_multiple_ts, f_cv_total_force_calc); |
| } | } |
| | |
| // Initialize feature_states for each instance | // Initialize feature_states for each instance |
| |
| init_feature(f_ag_center, "translational fit", f_type_static); | init_feature(f_ag_center, "translational fit", f_type_static); |
| init_feature(f_ag_rotate, "rotational fit", f_type_static); | init_feature(f_ag_rotate, "rotational fit", f_type_static); |
| init_feature(f_ag_fitting_group, "reference positions group", f_type_static); | init_feature(f_ag_fitting_group, "reference positions group", f_type_static); |
| init_feature(f_ag_fit_gradient_group, "fit gradient for main group", f_type_static); | init_feature(f_ag_fit_gradients, "fit gradients", f_type_user); |
| init_feature(f_ag_fit_gradient_ref, "fit gradient for reference group", f_type_static); | |
| init_feature(f_ag_atom_forces, "atomic forces", f_type_dynamic); | init_feature(f_ag_atom_forces, "atomic forces", f_type_dynamic); |
| | |
| // parallel calculation implies that we have at least a scalable center of mass, | // parallel calculation implies that we have at least a scalable center of mass, |
| |
| feature_states[f_ag_scalable_com].available = false; | feature_states[f_ag_scalable_com].available = false; |
| // TODO make f_ag_scalable depend on f_ag_scalable_com (or something else) | // TODO make f_ag_scalable depend on f_ag_scalable_com (or something else) |
| feature_states[f_ag_scalable].available = true; | feature_states[f_ag_scalable].available = true; |
| | feature_states[f_ag_fit_gradients].available = true; |
| } | } |
| | |
| | |
| void colvardeps::print_state() { | void colvardeps::print_state() { |
| size_t i; | size_t i; |
| cvm::log("Enabled features of " + description); | cvm::log("Enabled features of \"" + description + "\" (with reference count)"); |
| for (i = 0; i < feature_states.size(); i++) { | for (i = 0; i < feature_states.size(); i++) { |
| if (feature_states[i].enabled) | if (is_enabled(i)) |
| cvm::log("- " + features()[i]->description); | cvm::log("- " + features()[i]->description + " (" |
| | + cvm::to_str(feature_states[i].ref_count) + ")"); |
| } | } |
| for (i=0; i<children.size(); i++) { | for (i=0; i<children.size(); i++) { |
| cvm::log("* child " + cvm::to_str(i+1)); | cvm::log("* child " + cvm::to_str(i+1)); |