1 // -*- c++ -*-
2
3 // This file is part of the Collective Variables module (Colvars).
4 // The original version of Colvars and its updates are located at:
5 // https://github.com/Colvars/colvars
6 // Please update all Colvars source files before making any changes.
7 // If you wish to distribute your changes, please submit them to the
8 // Colvars repository at GitHub.
9
10
11 #include "colvarmodule.h"
12 #include "colvarproxy.h"
13 #include "colvardeps.h"
14
15
colvardeps()16 colvardeps::colvardeps()
17 {
18 time_step_factor = 1;
19 }
20
21
~colvardeps()22 colvardeps::~colvardeps() {
23 size_t i;
24
25 // Protest if we are deleting an object while a parent object may still depend on it
26 if (parents.size()) {
27 cvm::log("Warning: destroying \"" + description + "\" before its parents objects:");
28 for (i=0; i<parents.size(); i++) {
29 cvm::log(parents[i]->description);
30 }
31 }
32
33 // Do not delete features if it's a static object
34 // may change in the future though
35 // for (i=0; i<features.size(); i++) {
36 // if (features[i] != NULL) delete features[i];
37 // }
38
39 remove_all_children();
40 }
41
42
free_children_deps()43 void colvardeps::free_children_deps() {
44 // Dereference children requirements of all enabled features
45 // Useful when object is destroyed or set inactive
46 // CAUTION: when setting the parent object inactive, disable "active" first
47 // then call this, to avoid double-dereferencing the deps of "active"
48
49 // Cannot be in the base class destructor because it needs the derived class features()
50 size_t i,j,fid;
51
52 if (cvm::debug()) cvm::log("DEPS: freeing children deps for " + description + "\n");
53
54 cvm::increase_depth();
55 for (fid = 0; fid < feature_states.size(); fid++) {
56 if (is_enabled(fid)) {
57 for (i=0; i<features()[fid]->requires_children.size(); i++) {
58 int g = features()[fid]->requires_children[i];
59 for (j=0; j<children.size(); j++) {
60 if (cvm::debug()) cvm::log("DEPS: dereferencing children's "
61 + children[j]->features()[g]->description + "\n");
62 children[j]->decr_ref_count(g);
63 }
64 }
65 }
66 }
67 cvm::decrease_depth();
68 }
69
70
71 // re-enable children features (and increase ref count accordingly)
72 // So free_children_deps() can be called whenever an object becomes inactive
restore_children_deps()73 void colvardeps::restore_children_deps() {
74 size_t i,j,fid;
75
76 cvm::increase_depth();
77 for (fid = 0; fid < feature_states.size(); fid++) {
78 if (is_enabled(fid)) {
79 for (i=0; i<features()[fid]->requires_children.size(); i++) {
80 int g = features()[fid]->requires_children[i];
81 for (j=0; j<children.size(); j++) {
82 if (cvm::debug()) cvm::log("DEPS: re-enabling children's "
83 + children[j]->features()[g]->description + "\n");
84 children[j]->enable(g, false, false);
85 }
86 }
87 }
88 }
89 cvm::decrease_depth();
90 }
91
92
provide(int feature_id,bool truefalse)93 void colvardeps::provide(int feature_id, bool truefalse) {
94 feature_states[feature_id].available = truefalse;
95 }
96
97
set_enabled(int feature_id,bool truefalse)98 void colvardeps::set_enabled(int feature_id, bool truefalse) {
99 if (truefalse) {
100 enable(feature_id);
101 } else {
102 disable(feature_id);
103 }
104 }
105
106
get_keyval_feature(colvarparse * cvp,std::string const & conf,char const * key,int feature_id,bool const & def_value,colvarparse::Parse_Mode const parse_mode)107 bool colvardeps::get_keyval_feature(colvarparse *cvp,
108 std::string const &conf, char const *key,
109 int feature_id, bool const &def_value,
110 colvarparse::Parse_Mode const parse_mode)
111 {
112 if (!is_user(feature_id)) {
113 cvm::error("Cannot set feature \"" + features()[feature_id]->description + "\" from user input in \"" + description + "\".\n");
114 return false;
115 }
116 bool value;
117 bool const found = cvp->get_keyval(conf, key, value, def_value, parse_mode);
118 // If the default value is on, this function should be able to disable the feature!
119 set_enabled(feature_id, value);
120
121 return found;
122 }
123
124
enable(int feature_id,bool dry_run,bool toplevel)125 int colvardeps::enable(int feature_id,
126 bool dry_run /* default: false */,
127 bool toplevel /* default: true */)
128 {
129 int res;
130 size_t i, j;
131 bool ok;
132 feature *f = features()[feature_id];
133 feature_state *fs = &feature_states[feature_id];
134
135 if (cvm::debug()) {
136 cvm::log("DEPS: " + description +
137 (dry_run ? " testing " : " enabling ") +
138 "\"" + f->description +"\"\n");
139 }
140
141 if (fs->enabled) {
142 if (!(dry_run || toplevel)) {
143 // This is a dependency: prevent disabling this feature as long
144 // as requirement is enabled
145 fs->ref_count++;
146 if (cvm::debug())
147 cvm::log("DEPS: bumping ref_count to " + cvm::to_str(fs->ref_count) + "\n");
148 }
149 // Do not try to further resolve deps
150 return COLVARS_OK;
151 }
152
153 std::string feature_type_descr = is_static(feature_id) ? "Static" :
154 (is_dynamic(feature_id) ? "Dynamic" : "User-controlled");
155
156 if (!fs->available) {
157 if (!dry_run) {
158 if (toplevel) {
159 cvm::error("Error: " + feature_type_descr + " feature unavailable: \""
160 + f->description + "\" in " + description + ".");
161 } else {
162 cvm::log(feature_type_descr + " feature unavailable: \""
163 + f->description + "\" in " + description + ".");
164 }
165 }
166 return COLVARS_ERROR;
167 }
168
169 if (!toplevel && !is_dynamic(feature_id)) {
170 if (!dry_run) {
171 cvm::log(feature_type_descr + " feature \"" + f->description
172 + "\" cannot be enabled automatically in " + description + ".");
173 if (is_user(feature_id)) {
174 cvm::log("Try setting it manually.\n");
175 }
176 }
177 return COLVARS_ERROR;
178 }
179
180 // 1) enforce exclusions
181 // reminder: exclusions must be mutual for this to work
182 for (i=0; i<f->requires_exclude.size(); i++) {
183 feature *g = features()[f->requires_exclude[i]];
184 if (cvm::debug())
185 cvm::log(f->description + " requires exclude " + g->description);
186 if (is_enabled(f->requires_exclude[i])) {
187 if (!dry_run) {
188 cvm::log("Feature \"" + f->description + "\" is incompatible with \""
189 + g->description + "\" in " + description + ".");
190 if (toplevel) {
191 cvm::error("Error: Failed dependency in " + description + ".");
192 }
193 }
194 return COLVARS_ERROR;
195 }
196 }
197
198 // 2) solve internal deps (self)
199 for (i=0; i<f->requires_self.size(); i++) {
200 if (cvm::debug())
201 cvm::log(f->description + " requires self " + features()[f->requires_self[i]]->description);
202 res = enable(f->requires_self[i], dry_run, false);
203 if (res != COLVARS_OK) {
204 if (!dry_run) {
205 cvm::log("...required by \"" + f->description + "\" in " + description);
206 if (toplevel) {
207 cvm::error("Error: Failed dependency in " + description + ".");
208 }
209 }
210 return res;
211 }
212 }
213
214 // 3) solve internal alternate deps
215 for (i=0; i<f->requires_alt.size(); i++) {
216
217 // test if one is available; if yes, enable and exit w/ success
218 ok = false;
219 for (j=0; j<f->requires_alt[i].size(); j++) {
220 int g = f->requires_alt[i][j];
221 if (cvm::debug())
222 cvm::log(f->description + " requires alt " + features()[g]->description);
223 res = enable(g, true, false); // see if available
224 if (res == COLVARS_OK) {
225 ok = true;
226 if (!dry_run) {
227 enable(g, false, false); // Require again, for real
228 fs->alternate_refs.push_back(g); // We remember we enabled this
229 // so we can free it if this feature gets disabled
230 }
231 break;
232 }
233 }
234 if (!ok) {
235 if (!dry_run) {
236 cvm::log("\"" + f->description + "\" in " + description
237 + " requires one of the following features, none of which can be enabled:\n");
238 cvm::log("-----------------------------------------\n");
239 cvm::increase_depth();
240 for (j=0; j<f->requires_alt[i].size(); j++) {
241 int g = f->requires_alt[i][j];
242 cvm::log(cvm::to_str(j+1) + ". " + features()[g]->description);
243 enable(g, false, false); // Just for printing error output
244 }
245 cvm::decrease_depth();
246 cvm::log("-----------------------------------------\n");
247 if (toplevel) {
248 cvm::error("Error: Failed dependency in " + description + ".");
249 }
250 }
251 return COLVARS_ERROR;
252 }
253 }
254
255 // 4) solve deps in children
256 // if the object is inactive, we solve but do not enable: will be enabled
257 // when the object becomes active
258 cvm::increase_depth();
259 for (i=0; i<f->requires_children.size(); i++) {
260 int g = f->requires_children[i];
261 for (j=0; j<children.size(); j++) {
262 res = children[j]->enable(g, dry_run || !is_enabled(), false);
263 if (res != COLVARS_OK) {
264 if (!dry_run) {
265 cvm::log("...required by \"" + f->description + "\" in " + description);
266 if (toplevel) {
267 cvm::error("Error: Failed dependency in " + description + ".");
268 }
269 }
270 return res;
271 }
272 }
273 }
274 cvm::decrease_depth();
275
276 // Actually enable feature only once everything checks out
277 if (!dry_run) {
278 fs->enabled = true;
279 // This should be the only reference
280 if (!toplevel) fs->ref_count = 1;
281 if (feature_id == 0) {
282 // Waking up this object, enable all deps in children
283 restore_children_deps();
284 }
285 do_feature_side_effects(feature_id);
286 if (cvm::debug())
287 cvm::log("DEPS: feature \"" + f->description + "\" in "
288 + description + " enabled, ref_count = 1." + "\n");
289 }
290 return COLVARS_OK;
291 }
292
293
disable(int feature_id)294 int colvardeps::disable(int feature_id) {
295 size_t i, j;
296 feature *f = features()[feature_id];
297 feature_state *fs = &feature_states[feature_id];
298
299 if (cvm::debug()) cvm::log("DEPS: disabling feature \""
300 + f->description + "\" in " + description + "\n");
301
302 if (fs->enabled == false) {
303 return COLVARS_OK;
304 }
305
306 if (fs->ref_count > 1) {
307 cvm::error("Error: cannot disable feature \"" + f->description
308 + "\" in " + description + " because of " + cvm::to_str(fs->ref_count-1)
309 + " remaining references.\n" );
310 return COLVARS_ERROR;
311 }
312
313 // internal deps (self)
314 for (i=0; i<f->requires_self.size(); i++) {
315 if (cvm::debug()) cvm::log("DEPS: dereferencing self "
316 + features()[f->requires_self[i]]->description + "\n");
317 decr_ref_count(f->requires_self[i]);
318 }
319
320 // alternates
321 for (i=0; i<fs->alternate_refs.size(); i++) {
322 if (cvm::debug()) cvm::log("DEPS: dereferencing alt "
323 + features()[fs->alternate_refs[i]]->description + "\n");
324 decr_ref_count(fs->alternate_refs[i]);
325 }
326 // Forget these, now that they are dereferenced
327 fs->alternate_refs.clear();
328
329 // deps in children
330 // except if the object is inactive, then children dependencies
331 // have already been dereferenced by this function
332 // (or never referenced if feature was enabled while the object
333 // was inactive)
334 if (is_enabled()) {
335 cvm::increase_depth();
336 for (i=0; i<f->requires_children.size(); i++) {
337 int g = f->requires_children[i];
338 for (j=0; j<children.size(); j++) {
339 if (cvm::debug()) cvm::log("DEPS: dereferencing children's "
340 + children[j]->features()[g]->description + "\n");
341 children[j]->decr_ref_count(g);
342 }
343 }
344 cvm::decrease_depth();
345 }
346
347 fs->enabled = false;
348 fs->ref_count = 0;
349 if (feature_id == 0) {
350 // Putting this object to sleep
351 free_children_deps();
352 }
353 return COLVARS_OK;
354 }
355
356
decr_ref_count(int feature_id)357 int colvardeps::decr_ref_count(int feature_id) {
358 int &rc = feature_states[feature_id].ref_count;
359 feature *f = features()[feature_id];
360
361 if (cvm::debug())
362 cvm::log("DEPS: decreasing reference count of \"" + f->description
363 + "\" in " + description + ".\n");
364
365 if (rc <= 0) {
366 cvm::error("Error: cannot decrease reference count of feature \"" + f->description
367 + "\" in " + description + ", which is " + cvm::to_str(rc) + ".\n");
368 return COLVARS_ERROR;
369 }
370
371 rc--;
372 if (rc == 0 && f->is_dynamic()) {
373 // we can auto-disable this feature
374 if (cvm::debug())
375 cvm::log("DEPS will now auto-disable dynamic feature \"" + f->description
376 + "\" in " + description + ".\n");
377 disable(feature_id);
378 }
379 return COLVARS_OK;
380 }
381
382
init_feature(int feature_id,const char * description_in,feature_type type)383 void colvardeps::init_feature(int feature_id, const char *description_in, feature_type type) {
384 modify_features()[feature_id]->description = description_in;
385 modify_features()[feature_id]->type = type;
386 }
387
388
389 // Shorthand functions for describing dependencies
require_feature_self(int f,int g)390 void colvardeps::require_feature_self(int f, int g) {
391 features()[f]->requires_self.push_back(g);
392 }
393
394
395 // Ensure that exclusions are symmetric
exclude_feature_self(int f,int g)396 void colvardeps::exclude_feature_self(int f, int g) {
397 features()[f]->requires_exclude.push_back(g);
398 features()[g]->requires_exclude.push_back(f);
399 }
400
401
require_feature_children(int f,int g)402 void colvardeps::require_feature_children(int f, int g) {
403 features()[f]->requires_children.push_back(g);
404 }
405
406
require_feature_alt(int f,int g,int h)407 void colvardeps::require_feature_alt(int f, int g, int h) {
408 features()[f]->requires_alt.push_back(std::vector<int>(2));
409 features()[f]->requires_alt.back()[0] = g;
410 features()[f]->requires_alt.back()[1] = h;
411 }
412
413
require_feature_alt(int f,int g,int h,int i)414 void colvardeps::require_feature_alt(int f, int g, int h, int i) {
415 features()[f]->requires_alt.push_back(std::vector<int>(3));
416 features()[f]->requires_alt.back()[0] = g;
417 features()[f]->requires_alt.back()[1] = h;
418 features()[f]->requires_alt.back()[2] = i;
419 }
420
421
require_feature_alt(int f,int g,int h,int i,int j)422 void colvardeps::require_feature_alt(int f, int g, int h, int i, int j) {
423 features()[f]->requires_alt.push_back(std::vector<int>(4));
424 features()[f]->requires_alt.back()[0] = g;
425 features()[f]->requires_alt.back()[1] = h;
426 features()[f]->requires_alt.back()[2] = i;
427 features()[f]->requires_alt.back()[3] = j;
428 }
429
430
print_state()431 void colvardeps::print_state() {
432 size_t i;
433 cvm::log("Features of \"" + description + "\" (refcount)\n");
434 for (i = 0; i < feature_states.size(); i++) {
435 std::string onoff = is_enabled(i) ? "ON " : " ";
436 // Only display refcount if non-zero for less clutter
437 std::string refcount = feature_states[i].ref_count != 0 ?
438 " (" + cvm::to_str(feature_states[i].ref_count) + ") " : "";
439 cvm::log("- " + onoff + features()[i]->description + refcount + "\n");
440 }
441 cvm::increase_depth();
442 for (i=0; i<children.size(); i++) {
443 cvm::log("* child " + cvm::to_str(i+1));
444 children[i]->print_state();
445 }
446 cvm::decrease_depth();
447 }
448
449
add_child(colvardeps * child)450 void colvardeps::add_child(colvardeps *child) {
451
452 children.push_back(child);
453 child->parents.push_back(this);
454
455 // Solve dependencies of already enabled parent features
456 // in the new child
457
458 size_t i, fid;
459 cvm::increase_depth();
460 for (fid = 0; fid < feature_states.size(); fid++) {
461 if (is_enabled(fid)) {
462 for (i=0; i<features()[fid]->requires_children.size(); i++) {
463 int g = features()[fid]->requires_children[i];
464 if (cvm::debug()) cvm::log("DEPS: re-enabling children's "
465 + child->features()[g]->description + "\n");
466 child->enable(g, false, false);
467 }
468 }
469 }
470 cvm::decrease_depth();
471 }
472
473
remove_child(colvardeps * child)474 void colvardeps::remove_child(colvardeps *child) {
475 int i;
476 bool found = false;
477
478 for (i = children.size()-1; i>=0; --i) {
479 if (children[i] == child) {
480 children.erase(children.begin() + i);
481 found = true;
482 break;
483 }
484 }
485 if (!found) {
486 cvm::error("Trying to remove missing child reference from " + description + "\n");
487 }
488 found = false;
489 for (i = child->parents.size()-1; i>=0; --i) {
490 if (child->parents[i] == this) {
491 child->parents.erase(child->parents.begin() + i);
492 found = true;
493 break;
494 }
495 }
496 if (!found) {
497 cvm::error("Trying to remove missing parent reference from " + child->description + "\n");
498 }
499 }
500
501
remove_all_children()502 void colvardeps::remove_all_children() {
503 size_t i;
504 int j;
505 bool found;
506
507 for (i = 0; i < children.size(); ++i) {
508 found = false;
509 for (j = children[i]->parents.size()-1; j>=0; --j) {
510 if (children[i]->parents[j] == this) {
511 children[i]->parents.erase(children[i]->parents.begin() + j);
512 found = true;
513 break;
514 }
515 }
516 if (!found) {
517 cvm::error("Trying to remove missing parent reference from " + children[i]->description + "\n");
518 }
519 }
520 children.clear();
521 }
522