Update Colvars to version 2018-12-14
[namd.git] / colvars / src / colvarscript.cpp
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 #include <cstdlib>
11 #include <cstring>
12
13 #define COLVARSCRIPT_CPP
14 #include "colvarscript.h"
15 #undef COLVARSCRIPT_CPP
16
17 #include "colvarproxy.h"
18 #include "colvardeps.h"
19
20
21 colvarscript::colvarscript(colvarproxy *p)
22  : proxy(p),
23    colvars(p->colvars),
24    proxy_error(0)
25 {
26   comm_help.resize(colvarscript::cv_n_commands);
27   comm_fns.resize(colvarscript::cv_n_commands);
28 #define COLVARSCRIPT_INIT_FN
29 #include "colvarscript.h"
30 #undef COLVARSCRIPT_INIT_FN
31 }
32
33
34 extern "C" {
35
36   // Generic hooks; NAMD and VMD have Tcl-specific versions in the respective proxies
37
38   int run_colvarscript_command(int objc, unsigned char *const objv[])
39   {
40     colvarproxy *cvp = cvm::proxy;
41     if (!cvp) {
42       return -1;
43     }
44     if (!cvp->script) {
45       cvm::error("Called run_colvarscript_command without a script object initialized.\n");
46       return -1;
47     }
48     return cvp->script->run(objc, objv);
49   }
50
51   const char * get_colvarscript_result()
52   {
53     colvarproxy *cvp = cvm::proxy;
54     if (!cvp->script) {
55       cvm::error("Called run_colvarscript_command without a script object initialized.\n");
56       return "";
57     }
58     return cvp->script->result.c_str();
59   }
60 }
61
62
63 /// Run method based on given arguments
64 int colvarscript::run(int objc, unsigned char *const objv[])
65 {
66   result.clear();
67
68   if (cvm::debug()) {
69     cvm::log("Called script run with " + cvm::to_str(objc) + " args:");
70     for (int i = 0; i < objc; i++) {
71       cvm::log(obj_to_str(objv[i]));
72     }
73   }
74
75   if (objc < 2) {
76     set_str_result("No commands given: use \"cv help\" "
77                    "for a list of commands.");
78     return COLVARSCRIPT_ERROR;
79   }
80
81   std::string const cmd(obj_to_str(objv[1]));
82
83   int error_code = COLVARS_OK;
84
85   // If command is found in map, execute it
86   std::string const cmd_key("cv_"+cmd);
87   if (comm_str_map.count(cmd_key) > 0) {
88     error_code |= (*(comm_fns[comm_str_map[cmd_key]]))(
89                       reinterpret_cast<void *>(this), objc, objv);
90     return error_code;
91   }
92
93   if (cmd == "colvar") {
94     if (objc < 3) {
95       result = "Missing parameters\n" + help_string();
96       return COLVARSCRIPT_ERROR;
97     }
98     std::string const name(obj_to_str(objv[2]));
99     colvar *cv = cvm::colvar_by_name(name);
100     if (cv == NULL) {
101       result = "Colvar not found: " + name;
102       return COLVARSCRIPT_ERROR;
103     }
104     return proc_colvar(cv, objc-1, &(objv[1]));
105   }
106
107   if (cmd == "bias") {
108     if (objc < 3) {
109       result = "Missing parameters\n" + help_string();
110       return COLVARSCRIPT_ERROR;
111     }
112     std::string const name(obj_to_str(objv[2]));
113     colvarbias *b = cvm::bias_by_name(name);
114     if (b == NULL) {
115       result = "Bias not found: " + name;
116       return COLVARSCRIPT_ERROR;
117     }
118     return proc_bias(b, objc-1, &(objv[1]));
119   }
120
121   if (cmd == "version") {
122     result = COLVARS_VERSION;
123     return COLVARS_OK;
124   }
125
126   if (cmd == "reset") {
127     /// Delete every child object
128     colvars->reset();
129     return COLVARS_OK;
130   }
131
132   if (cmd == "delete") {
133     // Note: the delete bit may be ignored by some backends
134     // it is mostly useful in VMD
135     return proxy->request_deletion();
136   }
137
138   if (cmd == "update") {
139     error_code |= proxy->update_input();
140     if (error_code) {
141       result += "Error updating the Colvars module.\n";
142       return error_code;
143     }
144     error_code |= colvars->calc();
145     error_code |= proxy->update_output();
146     if (error_code) {
147       result += "Error updating the Colvars module.\n";
148     }
149     return error_code;
150   }
151
152   if (cmd == "list") {
153     if (objc == 2) {
154       for (std::vector<colvar *>::iterator cvi = colvars->colvars.begin();
155            cvi != colvars->colvars.end();
156            ++cvi) {
157         result += (cvi == colvars->colvars.begin() ? "" : " ") + (*cvi)->name;
158       }
159       return COLVARS_OK;
160     } else if (objc == 3 && !strcmp(obj_to_str(objv[2]), "biases")) {
161       for (std::vector<colvarbias *>::iterator bi = colvars->biases.begin();
162            bi != colvars->biases.end();
163            ++bi) {
164         result += (bi == colvars->biases.begin() ? "" : " ") + (*bi)->name;
165       }
166       return COLVARS_OK;
167     } else {
168       result = "Wrong arguments to command \"list\"\n" + help_string();
169       return COLVARSCRIPT_ERROR;
170     }
171   }
172
173   /// Parse config from file
174   if (cmd == "configfile") {
175     if (objc < 3) {
176       result = "Missing arguments\n" + help_string();
177       return COLVARSCRIPT_ERROR;
178     }
179     if (colvars->read_config_file(obj_to_str(objv[2])) == COLVARS_OK) {
180       return COLVARS_OK;
181     } else {
182       result = "Error parsing configuration file";
183       return COLVARSCRIPT_ERROR;
184     }
185   }
186
187   /// Parse config from string
188   if (cmd == "config") {
189     return exec_command(cv_config, NULL, objc, objv);
190   }
191
192   /// Load an input state file
193   if (cmd == "load") {
194     if (objc < 3) {
195       result = "Missing arguments\n" + help_string();
196       return COLVARSCRIPT_ERROR;
197     }
198     proxy->input_prefix() = obj_to_str(objv[2]);
199     if (colvars->setup_input() == COLVARS_OK) {
200       return COLVARS_OK;
201     } else {
202       result = "Error loading state file";
203       return COLVARSCRIPT_ERROR;
204     }
205   }
206
207   /// Save to an output state file
208   if (cmd == "save") {
209     if (objc < 3) {
210       result = "Missing arguments";
211       return COLVARSCRIPT_ERROR;
212     }
213     proxy->output_prefix() = obj_to_str(objv[2]);
214     int error = 0;
215     error |= colvars->setup_output();
216     error |= colvars->write_restart_file(colvars->output_prefix()+
217                                          ".colvars.state");
218     error |= colvars->write_output_files();
219     return error ? COLVARSCRIPT_ERROR : COLVARS_OK;
220   }
221
222   /// Print the values that would go on colvars.traj
223   if (cmd == "printframelabels") {
224     std::ostringstream os;
225     colvars->write_traj_label(os);
226     result = os.str();
227     return COLVARS_OK;
228   }
229   if (cmd == "printframe") {
230     std::ostringstream os;
231     colvars->write_traj(os);
232     result = os.str();
233     return COLVARS_OK;
234   }
235
236   if (cmd == "frame") {
237     if (objc == 2) {
238       long int f;
239       int error = proxy->get_frame(f);
240       if (error == COLVARS_OK) {
241         result = cvm::to_str(f);
242         return COLVARS_OK;
243       } else {
244         result = "Frame number is not available";
245         return COLVARSCRIPT_ERROR;
246       }
247     } else if (objc == 3) {
248       // Failure of this function does not trigger an error, but
249       // returns nonzero, to let scripts detect available frames
250       int error = proxy->set_frame(strtol(obj_to_str(objv[2]), NULL, 10));
251       result = cvm::to_str(error == COLVARS_OK ? 0 : -1);
252       return COLVARS_OK;
253     } else {
254       result = "Wrong arguments to command \"frame\"\n" + help_string();
255       return COLVARSCRIPT_ERROR;
256     }
257   }
258
259   if (cmd == "addenergy") {
260     if (objc == 3) {
261       colvars->total_bias_energy += strtod(obj_to_str(objv[2]), NULL);
262       return COLVARS_OK;
263     } else {
264       result = "Wrong arguments to command \"addenergy\"\n" + help_string();
265       return COLVARSCRIPT_ERROR;
266     }
267   }
268
269   if (cmd == "help") {
270     return exec_command(cv_help, NULL, objc, objv);
271   }
272
273   result = "Syntax error\n" + help_string();
274   return COLVARSCRIPT_ERROR;
275 }
276
277
278 int colvarscript::proc_colvar(colvar *cv, int objc, unsigned char *const objv[]) {
279
280   std::string const subcmd(obj_to_str(objv[2]));
281
282   if (subcmd == "value") {
283     result = (cv->value()).to_simple_string();
284     return COLVARS_OK;
285   }
286
287   if (subcmd == "run_ave") {
288     result = (cv->run_ave()).to_simple_string();
289     return COLVARS_OK;
290   }
291
292   if (subcmd == "width") {
293     result = cvm::to_str(cv->width, 0, cvm::cv_prec);
294     return COLVARS_OK;
295   }
296
297   if (subcmd == "type") {
298     result = cv->value().type_desc(cv->value().value_type);
299     return COLVARS_OK;
300   }
301
302   if (subcmd == "update") {
303     cv->calc();
304     cv->update_forces_energy();
305     result = (cv->value()).to_simple_string();
306     return COLVARS_OK;
307   }
308
309   if (subcmd == "delete") {
310     while (cv->biases.size() > 0) {
311       size_t i = cv->biases.size()-1;
312       cvm::log("Warning: before deleting colvar " + cv->name
313         + ", deleting parent bias " + cv->biases[i]->name);
314       delete cv->biases[i];
315     }
316     // colvar destructor is tasked with the cleanup
317     delete cv;
318     // TODO this could be done by the destructors
319     if (colvars->cv_traj_os != NULL) {
320       colvars->write_traj_label(*(colvars->cv_traj_os));
321     }
322     return COLVARS_OK;
323   }
324
325   if (subcmd == "getconfig") {
326     result = cv->get_config();
327     return COLVARS_OK;
328   }
329
330   if (subcmd == "getappliedforce") {
331     result = (cv->applied_force()).to_simple_string();
332     return COLVARS_OK;
333   }
334
335   if (subcmd == "getsystemforce") {
336     // TODO warning here
337     result = (cv->total_force()).to_simple_string();
338     return COLVARS_OK;
339   }
340
341   if (subcmd == "gettotalforce") {
342     result = (cv->total_force()).to_simple_string();
343     return COLVARS_OK;
344   }
345
346   if (subcmd == "addforce") {
347     if (objc < 4) {
348       result = "addforce: missing parameter: force value\n" + help_string();
349       return COLVARSCRIPT_ERROR;
350     }
351     std::string const f_str(obj_to_str(objv[3]));
352     std::istringstream is(f_str);
353     is.width(cvm::cv_width);
354     is.precision(cvm::cv_prec);
355     colvarvalue force(cv->value());
356     force.is_derivative();
357     if (force.from_simple_string(is.str()) != COLVARS_OK) {
358       result = "addforce : error parsing force value";
359       return COLVARSCRIPT_ERROR;
360     }
361     cv->add_bias_force(force);
362     result = force.to_simple_string();
363     return COLVARS_OK;
364   }
365
366   if (subcmd == "cvcflags") {
367     if (objc < 4) {
368       result = "cvcflags: missing parameter: vector of flags";
369       return COLVARSCRIPT_ERROR;
370     }
371     std::string const flags_str(obj_to_str(objv[3]));
372     std::istringstream is(flags_str);
373     std::vector<bool> flags;
374
375     int flag;
376     while (is >> flag) {
377       flags.push_back(flag != 0);
378     }
379
380     int res = cv->set_cvc_flags(flags);
381     if (res != COLVARS_OK) {
382       result = "Error setting CVC flags";
383       return COLVARSCRIPT_ERROR;
384     }
385     result = "0";
386     return COLVARS_OK;
387   }
388
389   if (subcmd == "modifycvcs") {
390     if (objc < 4) {
391       result = "cvcflags: missing parameter: vector of strings";
392       return COLVARSCRIPT_ERROR;
393     }
394     std::vector<std::string> const confs(proxy->script_obj_to_str_vector(objv[3]));
395     cvm::increase_depth();
396     int res = cv->update_cvc_config(confs);
397     cvm::decrease_depth();
398     if (res != COLVARS_OK) {
399       result = "Error setting CVC flags";
400       return COLVARSCRIPT_ERROR;
401     }
402     result = "0";
403     return COLVARS_OK;
404   }
405
406   if ((subcmd == "get") || (subcmd == "set") || (subcmd == "state")) {
407     return proc_features(cv, objc, objv);
408   }
409
410   result = "Syntax error\n" + help_string();
411   return COLVARSCRIPT_ERROR;
412 }
413
414
415 int colvarscript::proc_bias(colvarbias *b, int objc, unsigned char *const objv[]) {
416
417   std::string const subcmd(obj_to_str(objv[2]));
418
419   if (subcmd == "energy") {
420     result = cvm::to_str(b->get_energy());
421     return COLVARS_OK;
422   }
423
424   if (subcmd == "update") {
425     b->update();
426     result = cvm::to_str(b->get_energy());
427     return COLVARS_OK;
428   }
429
430   if (subcmd == "getconfig") {
431     result = b->get_config();
432     return COLVARS_OK;
433   }
434
435   // Subcommands for MW ABF
436   if (subcmd == "bin") {
437     int r = b->current_bin();
438     result = cvm::to_str(r);
439     return COLVARS_OK;
440   }
441
442   if (subcmd == "binnum") {
443     int r = b->bin_num();
444     if (r < 0) {
445       result = "Error: calling bin_num() for bias " + b->name;
446       return COLVARSCRIPT_ERROR;
447     }
448     result = cvm::to_str(r);
449     return COLVARS_OK;
450   }
451
452   if (subcmd == "share") {
453     int r = b->replica_share();
454     if (r < 0) {
455       result = "Error: calling replica_share() for bias " + b->name;
456       return COLVARSCRIPT_ERROR;
457     }
458     result = cvm::to_str(r);
459     return COLVARS_OK;
460   }
461   // End commands for MW ABF
462
463   if (subcmd == "delete") {
464     // the bias destructor takes care of the cleanup at cvm level
465     delete b;
466     // TODO this could be done by the destructors
467     if (colvars->cv_traj_os != NULL) {
468       colvars->write_traj_label(*(colvars->cv_traj_os));
469     }
470     return COLVARS_OK;
471   }
472
473   if ((subcmd == "get") || (subcmd == "set") || (subcmd == "state")) {
474     return proc_features(b, objc, objv);
475   }
476
477   if (objc >= 4) {
478     std::string const param(obj_to_str(objv[3]));
479     if (subcmd == "count") {
480       int index;
481       if (!(std::istringstream(param) >> index)) {
482         result = "bin_count: error parsing bin index";
483         return COLVARSCRIPT_ERROR;
484       }
485       result = cvm::to_str(b->bin_count(index));
486       return COLVARS_OK;
487     }
488
489     result = "Syntax error\n" + help_string();
490     return COLVARSCRIPT_ERROR;
491   }
492
493   result = "Syntax error\n" + help_string();
494   return COLVARSCRIPT_ERROR;
495 }
496
497
498 int colvarscript::proc_features(colvardeps *obj,
499                                 int objc, unsigned char *const objv[]) {
500   // size was already checked before calling
501   std::string const subcmd(obj_to_str(objv[2]));
502
503   if (objc == 3) {
504     if (subcmd == "state") {
505       // TODO make this returned as result?
506       obj->print_state();
507       return COLVARS_OK;
508     }
509
510     // get and set commands require more arguments
511     result = "Syntax error\n" + help_string();
512     return COLVARSCRIPT_ERROR;
513   }
514
515   if ((subcmd == "get") || (subcmd == "set")) {
516     std::vector<colvardeps::feature *> const &features = obj->features();
517     std::string const req_feature(obj_to_str(objv[3]));
518     colvardeps::feature *f = NULL;
519     int fid = 0;
520     for (fid = 0; fid < int(features.size()); fid++) {
521       if (features[fid]->description ==
522           colvarparse::to_lower_cppstr(req_feature)) {
523         f = features[fid];
524         break;
525       }
526     }
527
528     if (f == NULL) {
529
530       result = "Error: feature \""+req_feature+"\" does not exist.\n";
531       return COLVARSCRIPT_ERROR;
532
533     } else {
534
535       if (! obj->is_available(fid)) {
536         result = "Error: feature \""+req_feature+"\" is unavailable.\n";
537         return COLVARSCRIPT_ERROR;
538       }
539
540       if (subcmd == "get") {
541         result = cvm::to_str(obj->is_enabled(fid) ? 1 : 0);
542         return COLVARS_OK;
543       }
544
545       if (subcmd == "set") {
546         if (objc == 5) {
547           std::string const yesno =
548             colvarparse::to_lower_cppstr(std::string(obj_to_str(objv[4])));
549           if ((yesno == std::string("yes")) ||
550               (yesno == std::string("on")) ||
551               (yesno == std::string("1"))) {
552             obj->enable(fid);
553             return COLVARS_OK;
554           } else if ((yesno == std::string("no")) ||
555               (yesno == std::string("off")) ||
556               (yesno == std::string("0"))) {
557             obj->disable(fid);
558             return COLVARS_OK;
559           }
560         }
561         result = "Syntax error\n" + help_string();
562         return COLVARSCRIPT_ERROR;
563       }
564     }
565   }
566
567   result = "Syntax error\n" + help_string();
568   return COLVARSCRIPT_ERROR;
569 }
570
571
572 std::string colvarscript::help_string() const
573 {
574   std::string buf;
575   buf = "Usage: cv <subcommand> [args...]\n\
576 \n\
577 Managing the Colvars module:\n\
578   configfile <file name>      -- read configuration from a file\n\
579   config <string>             -- read configuration from the given string\n\
580   getconfig                   -- get the module's configuration string\n\
581   resetindexgroups            -- clear the index groups loaded so far\n\
582   reset                       -- delete all internal configuration\n\
583   delete                      -- delete this Colvars module instance\n\
584   version                     -- return version of Colvars code\n\
585   \n\
586 Input and output:\n\
587   list                        -- return a list of all variables\n\
588   list biases                 -- return a list of all biases\n\
589   load <file name>            -- load a state file (requires configuration)\n\
590   save <file name>            -- save a state file (requires configuration)\n\
591   update                      -- recalculate colvars and biases\n\
592   addenergy <E>               -- add <E> to the total bias energy\n\
593   printframe                  -- return a summary of the current frame\n\
594   printframelabels            -- return labels to annotate printframe's output\n";
595
596   long int tmp;
597   if (proxy->get_frame(tmp) != COLVARS_NOT_IMPLEMENTED) {
598       buf += "\
599   frame                       -- return current frame number\n\
600   frame <new_frame>           -- set frame number\n";
601   }
602
603   buf += "\n\
604 Accessing collective variables:\n\
605   colvar <name> value         -- return the current value of colvar <name>\n\
606   colvar <name> update        -- recalculate colvar <name>\n\
607   colvar <name> type          -- return the type of colvar <name>\n\
608   colvar <name> delete        -- delete colvar <name>\n\
609   colvar <name> addforce <F>  -- apply given force on colvar <name>\n\
610   colvar <name> getappliedforce -- return applied force of colvar <name>\n\
611   colvar <name> gettotalforce -- return total force of colvar <name>\n\
612   colvar <name> getconfig     -- return config string of colvar <name>\n\
613   colvar <name> cvcflags <fl> -- enable or disable cvcs according to 0/1 flags\n\
614   colvar <name> modifycvcs <str> -- pass new config strings to each CVC\n\
615   colvar <name> get <f>       -- get the value of the colvar feature <f>\n\
616   colvar <name> set <f> <val> -- set the value of the colvar feature <f>\n\
617 \n\
618 Accessing biases:\n\
619   bias <name> energy          -- return the current energy of bias <name>\n\
620   bias <name> update          -- recalculate bias <name>\n\
621   bias <name> delete          -- delete bias <name>\n\
622   bias <name> getconfig       -- return config string of bias <name>\n\
623   bias <name> get <f>         -- get the value of the bias feature <f>\n\
624   bias <name> set <f> <val>   -- set the value of the bias feature <f>\n\
625 ";
626
627   return buf;
628 }