00001 // -*- c++ -*- 00002 00003 // This file is part of the Collective Variables module (Colvars). 00004 // The original version of Colvars and its updates are located at: 00005 // https://github.com/Colvars/colvars 00006 // Please update all Colvars source files before making any changes. 00007 // If you wish to distribute your changes, please submit them to the 00008 // Colvars repository at GitHub. 00009 00010 00011 #include "colvarmodule.h" 00012 #include "colvarproxy.h" 00013 #include "colvardeps.h" 00014 00015 00016 colvardeps::colvardeps() 00017 { 00018 time_step_factor = 1; 00019 } 00020 00021 00022 colvardeps::~colvardeps() { 00023 size_t i; 00024 00025 // Protest if we are deleting an object while a parent object may still depend on it 00026 if (parents.size()) { 00027 cvm::log("Warning: destroying \"" + description + "\" before its parents objects:"); 00028 for (i=0; i<parents.size(); i++) { 00029 cvm::log(parents[i]->description + "\n"); 00030 } 00031 } 00032 00033 // Do not delete features if it's a static object 00034 // may change in the future though 00035 // for (i=0; i<features.size(); i++) { 00036 // if (features[i] != NULL) delete features[i]; 00037 // } 00038 00039 remove_all_children(); 00040 } 00041 00042 00043 void colvardeps::free_children_deps() { 00044 // Dereference children requirements of all enabled features 00045 // Useful when object is destroyed or set inactive 00046 // CAUTION: when setting the parent object inactive, disable "active" first 00047 // then call this, to avoid double-dereferencing the deps of "active" 00048 00049 // Cannot be in the base class destructor because it needs the derived class features() 00050 size_t i,j,fid; 00051 00052 if (cvm::debug()) cvm::log("DEPS: freeing children deps for " + description + "\n"); 00053 00054 cvm::increase_depth(); 00055 for (fid = 0; fid < feature_states.size(); fid++) { 00056 if (is_enabled(fid)) { 00057 for (i=0; i<features()[fid]->requires_children.size(); i++) { 00058 int g = features()[fid]->requires_children[i]; 00059 for (j=0; j<children.size(); j++) { 00060 if (cvm::debug()) cvm::log("DEPS: dereferencing children's " 00061 + children[j]->features()[g]->description + "\n"); 00062 children[j]->decr_ref_count(g); 00063 } 00064 } 00065 } 00066 } 00067 cvm::decrease_depth(); 00068 } 00069 00070 00071 // re-enable children features (and increase ref count accordingly) 00072 // So free_children_deps() can be called whenever an object becomes inactive 00073 void colvardeps::restore_children_deps() { 00074 size_t i,j,fid; 00075 00076 cvm::increase_depth(); 00077 for (fid = 0; fid < feature_states.size(); fid++) { 00078 if (is_enabled(fid)) { 00079 for (i=0; i<features()[fid]->requires_children.size(); i++) { 00080 int g = features()[fid]->requires_children[i]; 00081 for (j=0; j<children.size(); j++) { 00082 if (cvm::debug()) cvm::log("DEPS: re-enabling children's " 00083 + children[j]->features()[g]->description + "\n"); 00084 children[j]->enable(g, false, false); 00085 } 00086 } 00087 } 00088 } 00089 cvm::decrease_depth(); 00090 } 00091 00092 00093 void colvardeps::provide(int feature_id, bool truefalse) { 00094 feature_states[feature_id].available = truefalse; 00095 } 00096 00097 00098 void colvardeps::set_enabled(int feature_id, bool truefalse) { 00099 if (truefalse) { 00100 enable(feature_id); 00101 } else { 00102 disable(feature_id); 00103 } 00104 } 00105 00106 00107 bool colvardeps::get_keyval_feature(colvarparse *cvp, 00108 std::string const &conf, char const *key, 00109 int feature_id, bool const &def_value, 00110 colvarparse::Parse_Mode const parse_mode) 00111 { 00112 if (!is_user(feature_id)) { 00113 cvm::error("Cannot set feature \"" + features()[feature_id]->description + "\" from user input in \"" + description + "\".\n"); 00114 return false; 00115 } 00116 bool value; 00117 bool const found = cvp->get_keyval(conf, key, value, def_value, parse_mode); 00118 // If the default value is on, this function should be able to disable the feature! 00119 set_enabled(feature_id, value); 00120 00121 return found; 00122 } 00123 00124 00125 int colvardeps::enable(int feature_id, 00126 bool dry_run /* default: false */, 00127 bool toplevel /* default: true */) 00128 { 00129 int res; 00130 size_t i, j; 00131 bool ok; 00132 feature *f = features()[feature_id]; 00133 feature_state *fs = &feature_states[feature_id]; 00134 00135 if (cvm::debug()) { 00136 cvm::log("DEPS: " + description + 00137 (dry_run ? " testing " : " enabling ") + 00138 "\"" + f->description +"\"\n"); 00139 } 00140 00141 if (fs->enabled) { 00142 if (!(dry_run || toplevel)) { 00143 // This is a dependency: prevent disabling this feature as long 00144 // as requirement is enabled 00145 fs->ref_count++; 00146 if (cvm::debug()) 00147 cvm::log("DEPS: bumping ref_count to " + cvm::to_str(fs->ref_count) + "\n"); 00148 } 00149 // Do not try to further resolve deps 00150 return COLVARS_OK; 00151 } 00152 00153 std::string feature_type_descr = is_static(feature_id) ? "Static" : 00154 (is_dynamic(feature_id) ? "Dynamic" : "User-controlled"); 00155 00156 if (!fs->available) { 00157 if (!dry_run) { 00158 if (toplevel) { 00159 cvm::error("Error: " + feature_type_descr + " feature unavailable: \"" 00160 + f->description + "\" in " + description + ".\n"); 00161 } else { 00162 cvm::log(feature_type_descr + " feature unavailable: \"" 00163 + f->description + "\" in " + description + ".\n"); 00164 } 00165 } 00166 return COLVARS_ERROR; 00167 } 00168 00169 if (!toplevel && !is_dynamic(feature_id)) { 00170 if (!dry_run) { 00171 cvm::log(feature_type_descr + " feature \"" + f->description 00172 + "\" cannot be enabled automatically in " + description + ".\n"); 00173 if (is_user(feature_id)) { 00174 cvm::log("Try setting it manually.\n"); 00175 } 00176 } 00177 return COLVARS_ERROR; 00178 } 00179 00180 // 1) enforce exclusions 00181 // reminder: exclusions must be mutual for this to work 00182 for (i=0; i<f->requires_exclude.size(); i++) { 00183 feature *g = features()[f->requires_exclude[i]]; 00184 if (cvm::debug()) 00185 cvm::log(f->description + " requires exclude " + g->description + "\n"); 00186 if (is_enabled(f->requires_exclude[i])) { 00187 if (!dry_run) { 00188 cvm::log("Feature \"" + f->description + "\" is incompatible with \"" 00189 + g->description + "\" in " + description + ".\n"); 00190 if (toplevel) { 00191 cvm::error("Error: Failed dependency in " + description + ".\n"); 00192 } 00193 } 00194 return COLVARS_ERROR; 00195 } 00196 } 00197 00198 // 2) solve internal deps (self) 00199 for (i=0; i<f->requires_self.size(); i++) { 00200 if (cvm::debug()) 00201 cvm::log(f->description + " requires self " + features()[f->requires_self[i]]->description + "\n"); 00202 res = enable(f->requires_self[i], dry_run, false); 00203 if (res != COLVARS_OK) { 00204 if (!dry_run) { 00205 cvm::log("...required by \"" + f->description + "\" in " + description + "\n"); 00206 if (toplevel) { 00207 cvm::error("Error: Failed dependency in " + description + ".\n"); 00208 } 00209 } 00210 return res; 00211 } 00212 } 00213 00214 // 3) solve internal alternate deps 00215 for (i=0; i<f->requires_alt.size(); i++) { 00216 00217 // test if one is available; if yes, enable and exit w/ success 00218 ok = false; 00219 for (j=0; j<f->requires_alt[i].size(); j++) { 00220 int g = f->requires_alt[i][j]; 00221 if (cvm::debug()) 00222 cvm::log(f->description + " requires alt " + features()[g]->description + "\n"); 00223 res = enable(g, true, false); // see if available 00224 if (res == COLVARS_OK) { 00225 ok = true; 00226 if (!dry_run) { 00227 enable(g, false, false); // Require again, for real 00228 fs->alternate_refs.push_back(g); // We remember we enabled this 00229 // so we can free it if this feature gets disabled 00230 } 00231 break; 00232 } 00233 } 00234 if (!ok) { 00235 if (!dry_run) { 00236 cvm::log("\"" + f->description + "\" in " + description 00237 + " requires one of the following features, none of which can be enabled:\n"); 00238 cvm::log("-----------------------------------------\n"); 00239 cvm::increase_depth(); 00240 for (j=0; j<f->requires_alt[i].size(); j++) { 00241 int g = f->requires_alt[i][j]; 00242 cvm::log(cvm::to_str(j+1) + ". " + features()[g]->description + "\n"); 00243 enable(g, false, false); // Just for printing error output 00244 } 00245 cvm::decrease_depth(); 00246 cvm::log("-----------------------------------------\n"); 00247 if (toplevel) { 00248 cvm::error("Error: Failed dependency in " + description + ".\n"); 00249 } 00250 } 00251 return COLVARS_ERROR; 00252 } 00253 } 00254 00255 // 4) solve deps in children 00256 // if the object is inactive, we solve but do not enable: will be enabled 00257 // when the object becomes active 00258 cvm::increase_depth(); 00259 for (i=0; i<f->requires_children.size(); i++) { 00260 int g = f->requires_children[i]; 00261 for (j=0; j<children.size(); j++) { 00262 res = children[j]->enable(g, dry_run || !is_enabled(), false); 00263 if (res != COLVARS_OK) { 00264 if (!dry_run) { 00265 cvm::log("...required by \"" + f->description + "\" in " + description + "\n"); 00266 if (toplevel) { 00267 cvm::error("Error: Failed dependency in " + description + ".\n"); 00268 } 00269 } 00270 return res; 00271 } 00272 } 00273 } 00274 cvm::decrease_depth(); 00275 00276 // Actually enable feature only once everything checks out 00277 if (!dry_run) { 00278 fs->enabled = true; 00279 // This should be the only reference 00280 if (!toplevel) fs->ref_count = 1; 00281 if (feature_id == 0) { 00282 // Waking up this object, enable all deps in children 00283 restore_children_deps(); 00284 } 00285 do_feature_side_effects(feature_id); 00286 if (cvm::debug()) 00287 cvm::log("DEPS: feature \"" + f->description + "\" in " 00288 + description + " enabled, ref_count = 1." + "\n"); 00289 } 00290 return COLVARS_OK; 00291 } 00292 00293 00294 int colvardeps::disable(int feature_id) { 00295 size_t i, j; 00296 feature *f = features()[feature_id]; 00297 feature_state *fs = &feature_states[feature_id]; 00298 00299 if (cvm::debug()) cvm::log("DEPS: disabling feature \"" 00300 + f->description + "\" in " + description + "\n"); 00301 00302 if (fs->enabled == false) { 00303 return COLVARS_OK; 00304 } 00305 00306 if (fs->ref_count > 1) { 00307 cvm::error("Error: cannot disable feature \"" + f->description 00308 + "\" in " + description + " because of " + cvm::to_str(fs->ref_count-1) 00309 + " remaining references.\n" ); 00310 return COLVARS_ERROR; 00311 } 00312 00313 // internal deps (self) 00314 for (i=0; i<f->requires_self.size(); i++) { 00315 if (cvm::debug()) cvm::log("DEPS: dereferencing self " 00316 + features()[f->requires_self[i]]->description + "\n"); 00317 decr_ref_count(f->requires_self[i]); 00318 } 00319 00320 // alternates 00321 for (i=0; i<fs->alternate_refs.size(); i++) { 00322 if (cvm::debug()) cvm::log("DEPS: dereferencing alt " 00323 + features()[fs->alternate_refs[i]]->description + "\n"); 00324 decr_ref_count(fs->alternate_refs[i]); 00325 } 00326 // Forget these, now that they are dereferenced 00327 fs->alternate_refs.clear(); 00328 00329 // deps in children 00330 // except if the object is inactive, then children dependencies 00331 // have already been dereferenced by this function 00332 // (or never referenced if feature was enabled while the object 00333 // was inactive) 00334 if (is_enabled()) { 00335 cvm::increase_depth(); 00336 for (i=0; i<f->requires_children.size(); i++) { 00337 int g = f->requires_children[i]; 00338 for (j=0; j<children.size(); j++) { 00339 if (cvm::debug()) cvm::log("DEPS: dereferencing children's " 00340 + children[j]->features()[g]->description + "\n"); 00341 children[j]->decr_ref_count(g); 00342 } 00343 } 00344 cvm::decrease_depth(); 00345 } 00346 00347 fs->enabled = false; 00348 fs->ref_count = 0; 00349 if (feature_id == 0) { 00350 // Putting this object to sleep 00351 free_children_deps(); 00352 } 00353 return COLVARS_OK; 00354 } 00355 00356 00357 int colvardeps::decr_ref_count(int feature_id) { 00358 int &rc = feature_states[feature_id].ref_count; 00359 feature *f = features()[feature_id]; 00360 00361 if (cvm::debug()) 00362 cvm::log("DEPS: decreasing reference count of \"" + f->description 00363 + "\" in " + description + ".\n"); 00364 00365 if (rc <= 0) { 00366 cvm::error("Error: cannot decrease reference count of feature \"" + f->description 00367 + "\" in " + description + ", which is " + cvm::to_str(rc) + ".\n"); 00368 return COLVARS_ERROR; 00369 } 00370 00371 rc--; 00372 if (rc == 0 && f->is_dynamic()) { 00373 // we can auto-disable this feature 00374 if (cvm::debug()) 00375 cvm::log("DEPS will now auto-disable dynamic feature \"" + f->description 00376 + "\" in " + description + ".\n"); 00377 disable(feature_id); 00378 } 00379 return COLVARS_OK; 00380 } 00381 00382 00383 void colvardeps::init_feature(int feature_id, const char *description_in, feature_type type) { 00384 modify_features()[feature_id]->description = description_in; 00385 modify_features()[feature_id]->type = type; 00386 } 00387 00388 00389 // Shorthand functions for describing dependencies 00390 void colvardeps::require_feature_self(int f, int g) { 00391 features()[f]->requires_self.push_back(g); 00392 } 00393 00394 00395 // Ensure that exclusions are symmetric 00396 void colvardeps::exclude_feature_self(int f, int g) { 00397 features()[f]->requires_exclude.push_back(g); 00398 features()[g]->requires_exclude.push_back(f); 00399 } 00400 00401 00402 void colvardeps::require_feature_children(int f, int g) { 00403 features()[f]->requires_children.push_back(g); 00404 } 00405 00406 00407 void colvardeps::require_feature_alt(int f, int g, int h) { 00408 features()[f]->requires_alt.push_back(std::vector<int>(2)); 00409 features()[f]->requires_alt.back()[0] = g; 00410 features()[f]->requires_alt.back()[1] = h; 00411 } 00412 00413 00414 void colvardeps::require_feature_alt(int f, int g, int h, int i) { 00415 features()[f]->requires_alt.push_back(std::vector<int>(3)); 00416 features()[f]->requires_alt.back()[0] = g; 00417 features()[f]->requires_alt.back()[1] = h; 00418 features()[f]->requires_alt.back()[2] = i; 00419 } 00420 00421 00422 void colvardeps::require_feature_alt(int f, int g, int h, int i, int j) { 00423 features()[f]->requires_alt.push_back(std::vector<int>(4)); 00424 features()[f]->requires_alt.back()[0] = g; 00425 features()[f]->requires_alt.back()[1] = h; 00426 features()[f]->requires_alt.back()[2] = i; 00427 features()[f]->requires_alt.back()[3] = j; 00428 } 00429 00430 00431 void colvardeps::print_state() { 00432 size_t i; 00433 cvm::log("Features of \"" + description + "\" (refcount)\n"); 00434 for (i = 0; i < feature_states.size(); i++) { 00435 std::string onoff = is_enabled(i) ? "ON " : " "; 00436 // Only display refcount if non-zero for less clutter 00437 std::string refcount = feature_states[i].ref_count != 0 ? 00438 " (" + cvm::to_str(feature_states[i].ref_count) + ") " : ""; 00439 cvm::log("- " + onoff + features()[i]->description + refcount + "\n"); 00440 } 00441 cvm::increase_depth(); 00442 for (i=0; i<children.size(); i++) { 00443 cvm::log("* child " + cvm::to_str(i+1)); 00444 children[i]->print_state(); 00445 } 00446 cvm::decrease_depth(); 00447 } 00448 00449 00450 void colvardeps::add_child(colvardeps *child) { 00451 00452 children.push_back(child); 00453 child->parents.push_back(this); 00454 00455 // Solve dependencies of already enabled parent features 00456 // in the new child 00457 00458 size_t i, fid; 00459 cvm::increase_depth(); 00460 for (fid = 0; fid < feature_states.size(); fid++) { 00461 if (is_enabled(fid)) { 00462 for (i=0; i<features()[fid]->requires_children.size(); i++) { 00463 int g = features()[fid]->requires_children[i]; 00464 if (cvm::debug()) cvm::log("DEPS: re-enabling children's " 00465 + child->features()[g]->description + "\n"); 00466 child->enable(g, false, false); 00467 } 00468 } 00469 } 00470 cvm::decrease_depth(); 00471 } 00472 00473 00474 void colvardeps::remove_child(colvardeps *child) { 00475 int i; 00476 bool found = false; 00477 00478 for (i = children.size()-1; i>=0; --i) { 00479 if (children[i] == child) { 00480 children.erase(children.begin() + i); 00481 found = true; 00482 break; 00483 } 00484 } 00485 if (!found) { 00486 cvm::error("Trying to remove missing child reference from " + description + "\n"); 00487 } 00488 found = false; 00489 for (i = child->parents.size()-1; i>=0; --i) { 00490 if (child->parents[i] == this) { 00491 child->parents.erase(child->parents.begin() + i); 00492 found = true; 00493 break; 00494 } 00495 } 00496 if (!found) { 00497 cvm::error("Trying to remove missing parent reference from " + child->description + "\n"); 00498 } 00499 } 00500 00501 00502 void colvardeps::remove_all_children() { 00503 size_t i; 00504 int j; 00505 bool found; 00506 00507 for (i = 0; i < children.size(); ++i) { 00508 found = false; 00509 for (j = children[i]->parents.size()-1; j>=0; --j) { 00510 if (children[i]->parents[j] == this) { 00511 children[i]->parents.erase(children[i]->parents.begin() + j); 00512 found = true; 00513 break; 00514 } 00515 } 00516 if (!found) { 00517 cvm::error("Trying to remove missing parent reference from " + children[i]->description + "\n"); 00518 } 00519 } 00520 children.clear(); 00521 }