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