Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2011-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "PlumedMain.h"
23 : #include "ActionAtomistic.h"
24 : #include "ActionPilot.h"
25 : #include "ActionForInterface.h"
26 : #include "ActionRegister.h"
27 : #include "ActionSet.h"
28 : #include "ActionWithValue.h"
29 : #include "ActionWithVirtualAtom.h"
30 : #include "ActionToGetData.h"
31 : #include "ActionToPutData.h"
32 : #include "CLToolMain.h"
33 : #include "ExchangePatterns.h"
34 : #include "GREX.h"
35 : #include "DomainDecomposition.h"
36 : #include "config/Config.h"
37 : #include "tools/Citations.h"
38 : #include "tools/Communicator.h"
39 : #include "tools/DLLoader.h"
40 : #include "tools/Exception.h"
41 : #include "tools/IFile.h"
42 : #include "tools/Log.h"
43 : #include "tools/OpenMP.h"
44 : #include "tools/Tools.h"
45 : #include "tools/Stopwatch.h"
46 : #include "tools/TypesafePtr.h"
47 : #include "lepton/Exception.h"
48 : #include "DataPassingTools.h"
49 : #include "small_vector/small_vector.h"
50 : #include <cstdlib>
51 : #include <cstdio>
52 : #include <cstring>
53 : #include <set>
54 : #include <exception>
55 : #include <stdexcept>
56 : #include <ios>
57 : #include <new>
58 : #include <typeinfo>
59 : #include <iostream>
60 : #include <algorithm>
61 : #include <system_error>
62 : #include <future>
63 : #include <memory>
64 : #include <functional>
65 : #include <regex>
66 : #include <any>
67 : #include <optional>
68 : #include <variant>
69 : #include <filesystem>
70 :
71 : namespace PLMD {
72 :
73 : /// Small utility just used in this file to throw arbitrary exceptions
74 61 : [[noreturn]] static void testThrow(const char* what) {
75 61 : auto words=Tools::getWords(what);
76 61 : plumed_assert(words.size()>0);
77 : #define __PLUMED_THROW_NOMSG(type) if(words[0]==#type) throw type()
78 : #define __PLUMED_THROW_MSG(type) if(words[0]==#type) throw type(what)
79 62 : __PLUMED_THROW_MSG(PLMD::ExceptionError);
80 61 : __PLUMED_THROW_MSG(PLMD::ExceptionDebug);
81 60 : __PLUMED_THROW_MSG(PLMD::Exception);
82 59 : __PLUMED_THROW_MSG(PLMD::lepton::Exception);
83 57 : __PLUMED_THROW_NOMSG(std::bad_exception);
84 56 : __PLUMED_THROW_NOMSG(std::bad_array_new_length);
85 55 : __PLUMED_THROW_NOMSG(std::bad_alloc);
86 54 : __PLUMED_THROW_NOMSG(std::bad_function_call);
87 53 : __PLUMED_THROW_NOMSG(std::bad_weak_ptr);
88 52 : __PLUMED_THROW_NOMSG(std::bad_cast);
89 51 : __PLUMED_THROW_NOMSG(std::bad_typeid);
90 50 : __PLUMED_THROW_NOMSG(std::bad_variant_access);
91 49 : __PLUMED_THROW_NOMSG(std::bad_optional_access);
92 48 : __PLUMED_THROW_NOMSG(std::bad_any_cast);
93 47 : __PLUMED_THROW_MSG(std::underflow_error);
94 46 : __PLUMED_THROW_MSG(std::overflow_error);
95 45 : __PLUMED_THROW_MSG(std::range_error);
96 44 : __PLUMED_THROW_MSG(std::runtime_error);
97 43 : __PLUMED_THROW_MSG(std::out_of_range);
98 42 : __PLUMED_THROW_MSG(std::length_error);
99 41 : __PLUMED_THROW_MSG(std::domain_error);
100 40 : __PLUMED_THROW_MSG(std::invalid_argument);
101 39 : __PLUMED_THROW_MSG(std::logic_error);
102 :
103 :
104 :
105 38 : if(words[0]=="std::system_error") {
106 4 : plumed_assert(words.size()>2);
107 : int error_code;
108 4 : Tools::convert(words[2],error_code);
109 4 : if(words[1]=="std::generic_category") {
110 1 : throw std::system_error(error_code,std::generic_category(),what);
111 : }
112 3 : if(words[1]=="std::system_category") {
113 1 : throw std::system_error(error_code,std::system_category(),what);
114 : }
115 2 : if(words[1]=="std::iostream_category") {
116 1 : throw std::system_error(error_code,std::iostream_category(),what);
117 : }
118 1 : if(words[1]=="std::future_category") {
119 1 : throw std::system_error(error_code,std::future_category(),what);
120 : }
121 : }
122 :
123 34 : if(words[0]=="std::filesystem::filesystem_error") {
124 : int error_code;
125 3 : plumed_assert(words.size()>2);
126 3 : Tools::convert(words[2],error_code);
127 : std::error_code x_error_code;
128 3 : if(words[1]=="std::generic_category") {
129 3 : x_error_code=::std::error_code(error_code,::std::generic_category());
130 : }
131 3 : if(words[1]=="std::system_category") {
132 0 : x_error_code=::std::error_code(error_code,::std::system_category());
133 : }
134 3 : if(words[1]=="std::iostream_category") {
135 0 : x_error_code=::std::error_code(error_code,::std::iostream_category());
136 : }
137 3 : if(words[1]=="std::future_category") {
138 0 : x_error_code=::std::error_code(error_code,::std::future_category());
139 : }
140 :
141 3 : if(words.size()<4) {
142 2 : throw std::filesystem::filesystem_error(what,x_error_code);
143 : }
144 2 : if(words.size()<5) {
145 3 : throw std::filesystem::filesystem_error(what,std::filesystem::path(words[3]),x_error_code);
146 : }
147 4 : throw std::filesystem::filesystem_error(what,std::filesystem::path(words[3]),std::filesystem::path(words[4]),x_error_code);
148 : }
149 :
150 : #define __PLUMED_THROW_REGEX(name) if(words[1]=="std::regex_constants::error_" #name) throw std::regex_error(std::regex_constants::error_ ##name)
151 31 : if(words[0]=="std::regex_error") {
152 13 : plumed_assert(words.size()>1);
153 13 : __PLUMED_THROW_REGEX(collate);
154 12 : __PLUMED_THROW_REGEX(ctype);
155 11 : __PLUMED_THROW_REGEX(escape);
156 10 : __PLUMED_THROW_REGEX(backref);
157 9 : __PLUMED_THROW_REGEX(brack);
158 8 : __PLUMED_THROW_REGEX(paren);
159 7 : __PLUMED_THROW_REGEX(brace);
160 6 : __PLUMED_THROW_REGEX(badbrace);
161 5 : __PLUMED_THROW_REGEX(range);
162 4 : __PLUMED_THROW_REGEX(space);
163 3 : __PLUMED_THROW_REGEX(badrepeat);
164 2 : __PLUMED_THROW_REGEX(complexity);
165 1 : __PLUMED_THROW_REGEX(stack);
166 : }
167 :
168 : #define __PLUMED_THROW_FUTURE(name) if(words[1]=="std::future_errc::" #name) throw std::future_error(::std::future_errc::name)
169 18 : if(words[0]=="std::future_error") {
170 4 : plumed_assert(words.size()>1);
171 4 : __PLUMED_THROW_FUTURE(broken_promise);
172 3 : __PLUMED_THROW_FUTURE(future_already_retrieved);
173 2 : __PLUMED_THROW_FUTURE(promise_already_satisfied);
174 1 : __PLUMED_THROW_FUTURE(no_state);
175 : }
176 :
177 14 : if(words[0]=="std::ios_base::failure") {
178 1 : int error_code=0;
179 1 : if(words.size()>2) {
180 0 : Tools::convert(words[2],error_code);
181 : }
182 1 : if(words.size()>1 && words[1]=="std::generic_category") {
183 0 : throw std::ios_base::failure(what,std::error_code(error_code,std::generic_category()));
184 : }
185 1 : if(words.size()>1 && words[1]=="std::system_category") {
186 0 : throw std::ios_base::failure(what,std::error_code(error_code,std::system_category()));
187 : }
188 1 : if(words.size()>1 && words[1]=="std::iostream_category") {
189 0 : throw std::ios_base::failure(what,std::error_code(error_code,std::iostream_category()));
190 : }
191 1 : if(words.size()>1 && words[1]=="std::future_category") {
192 0 : throw std::ios_base::failure(what,std::error_code(error_code,std::future_category()));
193 : }
194 2 : throw std::ios_base::failure(what);
195 : }
196 :
197 13 : if(words[0]=="int") {
198 1 : int value=0;
199 1 : if(words.size()>1) {
200 0 : Tools::convert(words[1],value);
201 : }
202 1 : throw value;
203 : }
204 :
205 12 : if(words[0]=="test_nested1") {
206 : try {
207 12 : throw Exception(std::string("inner ")+what);
208 6 : } catch(...) {
209 : try {
210 18 : std::throw_with_nested(Exception(std::string("middle ")+what));
211 6 : } catch(...) {
212 18 : std::throw_with_nested(Exception(std::string("outer ")+what));
213 6 : }
214 6 : }
215 : }
216 :
217 6 : if(words[0]=="test_nested2") {
218 : try {
219 3 : throw std::bad_alloc();
220 3 : } catch(...) {
221 : try {
222 9 : std::throw_with_nested(Exception(std::string("middle ")+what));
223 3 : } catch(...) {
224 9 : std::throw_with_nested(Exception(std::string("outer ")+what));
225 3 : }
226 3 : }
227 : }
228 :
229 3 : if(words[0]=="test_nested3") {
230 : try {
231 2 : throw "inner";
232 2 : } catch(...) {
233 : try {
234 6 : std::throw_with_nested(Exception(std::string("middle ")+what));
235 2 : } catch(...) {
236 6 : std::throw_with_nested(Exception(std::string("outer ")+what));
237 2 : }
238 2 : }
239 : }
240 :
241 2 : plumed_error() << "unknown exception " << what;
242 61 : }
243 :
244 : namespace {
245 : /// This is an internal tool used to count how many PlumedMain objects have been created
246 : /// and if they were correctly destroyed.
247 : /// When using debug options, it leads to a crash
248 : /// Otherwise, it just prints a message
249 : class CountInstances {
250 : std::atomic<int> counter{};
251 : // private constructor to avoid direct usage
252 5394 : CountInstances() noexcept {}
253 5394 : ~CountInstances() {
254 5394 : if(counter!=0) {
255 0 : std::cerr<<"WARNING: internal inconsistency in allocated PlumedMain instances (" <<counter<< ")\n";
256 0 : std::cerr<<"Might be a consequence of incorrectly paired plumed_create/plumed_finalize in the C interface\n";
257 0 : std::cerr<<"Or it could be due to incorrect calls to std::exit, without properly destroying all PlumedMain objects\n";
258 : #ifndef NDEBUG
259 : std::cerr<<"This is a debug build, so the warning will make PLUMED abort\n";
260 : std::abort();
261 : #endif
262 : }
263 5394 : }
264 1613640 : static CountInstances & instance() {
265 1613640 : static CountInstances counter;
266 1613640 : return counter;
267 : }
268 : public:
269 : /// Only access through these static functions
270 : /// The first call to increase() ensures the instance is constructed
271 : /// This should provide the correct construction and destruction order
272 : /// also in cases where the PlumedMain object is constructed in the
273 : /// constructor of a static object
274 806820 : static void increase() noexcept {
275 806820 : ++instance().counter;
276 806820 : }
277 : /// See increase()
278 806820 : static void decrease() noexcept {
279 806820 : --instance().counter;
280 806820 : }
281 : };
282 :
283 : }
284 :
285 :
286 806820 : PlumedMain::PlumedMain():
287 806820 : datoms_fwd(*this),
288 : // automatically write on log in destructor
289 806820 : stopwatch_fwd(log),
290 806820 : actionSet_fwd(*this),
291 806820 : passtools(DataPassingTools::create(sizeof(double))) {
292 806820 : passtools->usingNaturalUnits=false;
293 806820 : increaseReferenceCounter();
294 806820 : log.link(comm);
295 806820 : log.setLinePrefix("PLUMED: ");
296 : // this is at last so as to avoid inconsistencies if an exception is thrown
297 806820 : CountInstances::increase(); // noexcept
298 806820 : }
299 :
300 : // destructor needed to delete forward declarated objects
301 1612642 : PlumedMain::~PlumedMain() {
302 806820 : CountInstances::decrease();
303 4839922 : }
304 :
305 : /////////////////////////////////////////////////////////////
306 : // MAIN INTERPRETER
307 :
308 : #define CHECK_INIT(ini,word) plumed_assert(ini)<<"cmd(\"" << word << "\") should be only used after plumed initialization"
309 : #define CHECK_NOTINIT(ini,word) plumed_assert(!(ini))<<"cmd(\"" << word << "\") should be only used before plumed initialization"
310 : #define CHECK_NOTNULL(val,word) plumed_assert(val)<<"NULL pointer received in cmd(\"" << word << "\")"
311 :
312 :
313 1350634 : void PlumedMain::cmd(std::string_view word,const TypesafePtr & val) {
314 :
315 : // Enumerate all possible commands:
316 : enum {
317 : #include "PlumedMainEnum.inc"
318 : };
319 :
320 : // Static object (initialized once) containing the map of commands:
321 : const static Tools::FastStringUnorderedMap<int> word_map = {
322 : #include "PlumedMainMap.inc"
323 1350634 : };
324 :
325 : try {
326 :
327 1350634 : auto ss=stopwatch.startPause();
328 :
329 : gch::small_vector<std::string_view> words;
330 1350634 : Tools::getWordsSimple(words,word);
331 :
332 1350634 : unsigned nw=words.size();
333 1350634 : if(nw==0) {
334 : // do nothing
335 : } else {
336 : int iword=-1;
337 : const auto it=word_map.find(words[0]);
338 1350633 : if(it!=word_map.end()) {
339 1350626 : iword=it->second;
340 : }
341 :
342 1350626 : switch(iword) {
343 70588 : case cmd_setBox:
344 70588 : CHECK_INIT(initialized,word);
345 70588 : CHECK_NOTNULL(val,word);
346 70588 : setInputValue( "Box", 0, 1, val );
347 70587 : break;
348 74765 : case cmd_setPositions:
349 74765 : CHECK_INIT(initialized,word);
350 74765 : setInputValue("posx", 0, 3, val );
351 74765 : setInputValue("posy", 1, 3, val );
352 74765 : setInputValue("posz", 2, 3, val );
353 74765 : break;
354 74728 : case cmd_setMasses:
355 74728 : CHECK_INIT(initialized,word);
356 74728 : setInputValue("Masses", 0, 1, val );
357 74725 : break;
358 66653 : case cmd_setCharges:
359 66653 : CHECK_INIT(initialized,word);
360 66653 : setInputValue("Charges", 0, 1, val);
361 66653 : break;
362 1 : case cmd_setPositionsX:
363 1 : CHECK_INIT(initialized,word);
364 1 : setInputValue("posx", 0, 1, val);
365 1 : break;
366 1 : case cmd_setPositionsY:
367 1 : CHECK_INIT(initialized,word);
368 1 : setInputValue("posy", 0, 1, val);
369 1 : break;
370 1 : case cmd_setPositionsZ:
371 1 : CHECK_INIT(initialized,word);
372 1 : setInputValue("posz", 0, 1, val);
373 1 : break;
374 66902 : case cmd_setVirial:
375 66902 : CHECK_INIT(initialized,word);
376 66902 : CHECK_NOTNULL(val,word);
377 66902 : setInputForce("Box",val);
378 66884 : break;
379 7842 : case cmd_setEnergy:
380 7842 : CHECK_INIT(initialized,word);
381 7842 : CHECK_NOTNULL(val,word);
382 7842 : if( name_of_energy!="" ) {
383 3989 : setInputValue( name_of_energy, 0, 1, val );
384 : }
385 : break;
386 74732 : case cmd_setForces:
387 74732 : CHECK_INIT(initialized,word);
388 74732 : setInputForce("posx",val);
389 74732 : setInputForce("posy",val);
390 74732 : setInputForce("posz",val);
391 74732 : break;
392 1 : case cmd_setForcesX:
393 1 : CHECK_INIT(initialized,word);
394 1 : setInputForce("posx",val);
395 1 : break;
396 1 : case cmd_setForcesY:
397 1 : CHECK_INIT(initialized,word);
398 1 : setInputForce("posy",val);
399 1 : break;
400 1 : case cmd_setForcesZ:
401 1 : CHECK_INIT(initialized,word);
402 1 : setInputForce("posz",val);
403 1 : break;
404 280115 : case cmd_calc:
405 280115 : CHECK_INIT(initialized,word);
406 280115 : calc();
407 : break;
408 99 : case cmd_prepareDependencies:
409 99 : CHECK_INIT(initialized,word);
410 99 : prepareDependencies();
411 : break;
412 84 : case cmd_shareData:
413 84 : CHECK_INIT(initialized,word);
414 84 : shareData();
415 : break;
416 6951 : case cmd_prepareCalc:
417 6951 : CHECK_INIT(initialized,word);
418 6951 : prepareCalc();
419 : break;
420 10 : case cmd_performCalc:
421 10 : CHECK_INIT(initialized,word);
422 10 : performCalc();
423 : break;
424 7011 : case cmd_performCalcNoUpdate:
425 7011 : CHECK_INIT(initialized,word);
426 7011 : performCalcNoUpdate();
427 : break;
428 10 : case cmd_performCalcNoForces:
429 10 : CHECK_INIT(initialized,word);
430 10 : performCalcNoForces();
431 : break;
432 79 : case cmd_update:
433 79 : CHECK_INIT(initialized,word);
434 79 : update();
435 : break;
436 16009 : case cmd_setStep:
437 16009 : CHECK_INIT(initialized,word);
438 16012 : CHECK_NOTNULL(val,word);
439 16006 : step=val.get<int>();
440 16005 : startStep();
441 : break;
442 0 : case cmd_setStepLong:
443 0 : CHECK_INIT(initialized,word);
444 0 : CHECK_NOTNULL(val,word);
445 0 : step=val.get<long int>();
446 0 : startStep();
447 : break;
448 271058 : case cmd_setStepLongLong:
449 271058 : CHECK_INIT(initialized,word);
450 271058 : CHECK_NOTNULL(val,word);
451 271058 : step=val.get<long long int>();
452 271058 : startStep();
453 : break;
454 23456 : case cmd_setValue: {
455 23456 : CHECK_INIT(initialized,words[0]);
456 23456 : plumed_assert(nw==2);
457 23456 : setInputValue( std::string(words[1]), 0, 1, val );
458 : }
459 23456 : break;
460 : /* ADDED WITH API=7 */
461 0 : case cmd_setValueForces: {
462 0 : CHECK_INIT(initialized,words[0]);
463 0 : plumed_assert(nw==2);
464 0 : setInputForce( std::string(words[1]), val );
465 : }
466 0 : break;
467 : // words used less frequently:
468 1140 : case cmd_setAtomsNlocal:
469 1140 : CHECK_INIT(initialized,word);
470 1140 : CHECK_NOTNULL(val,word);
471 4582 : for(const auto & pp : inputs ) {
472 3442 : plumed_assert(pp);
473 3442 : DomainDecomposition* dd=pp->castToDomainDecomposition();
474 3442 : if( dd ) {
475 1140 : dd->setAtomsNlocal(val.get<int>());
476 : }
477 : }
478 : break;
479 988 : case cmd_setAtomsGatindex:
480 988 : CHECK_INIT(initialized,word);
481 3974 : for(const auto & pp : inputs ) {
482 2986 : plumed_assert(pp);
483 2986 : DomainDecomposition* dd=pp->castToDomainDecomposition();
484 2986 : if( dd ) {
485 988 : dd->setAtomsGatindex(val,false);
486 : }
487 : }
488 : break;
489 0 : case cmd_setAtomsFGatindex:
490 0 : CHECK_INIT(initialized,word);
491 0 : for(const auto & pp : inputs ) {
492 0 : plumed_assert(pp);
493 0 : DomainDecomposition* dd=pp->castToDomainDecomposition();
494 0 : if( dd ) {
495 0 : dd->setAtomsGatindex(val,false);
496 : }
497 : }
498 : break;
499 152 : case cmd_setAtomsContiguous:
500 152 : CHECK_INIT(initialized,word);
501 152 : CHECK_NOTNULL(val,word);
502 608 : for(const auto & pp : inputs ) {
503 456 : plumed_assert(pp);
504 456 : DomainDecomposition* dd=pp->castToDomainDecomposition();
505 456 : if( dd ) {
506 152 : dd->setAtomsContiguous(val.get<int>());
507 : }
508 : }
509 : break;
510 116 : case cmd_createFullList:
511 116 : CHECK_INIT(initialized,word);
512 116 : CHECK_NOTNULL(val,word);
513 553 : for(const auto & pp : inputs ) {
514 437 : plumed_assert(pp);
515 437 : DomainDecomposition* dd=pp->castToDomainDecomposition();
516 437 : if( dd ) {
517 116 : dd->createFullList(val);
518 : }
519 : }
520 : break;
521 116 : case cmd_getFullList: {
522 116 : CHECK_INIT(initialized,word);
523 116 : CHECK_NOTNULL(val,word);
524 : unsigned nlists=0;
525 553 : for(const auto & pp : inputs ) {
526 437 : plumed_assert(pp);
527 437 : DomainDecomposition* dd=pp->castToDomainDecomposition();
528 437 : if( dd ) {
529 116 : dd->getFullList(val);
530 116 : nlists++;
531 : }
532 : }
533 116 : plumed_assert( nlists==1 );
534 : }
535 : break;
536 116 : case cmd_clearFullList:
537 116 : CHECK_INIT(initialized,word);
538 553 : for(const auto & pp : inputs ) {
539 437 : plumed_assert(pp);
540 437 : DomainDecomposition* dd=pp->castToDomainDecomposition();
541 437 : if( dd ) {
542 116 : dd->clearFullList();
543 : }
544 : }
545 : break;
546 : /* ADDED WITH API==6 */
547 115 : case cmd_getDataRank: {
548 115 : CHECK_INIT(initialized,words[0]);
549 115 : plumed_assert(nw==2 || nw==3);
550 115 : std::string vtype="";
551 115 : if( nw==3 ) {
552 0 : vtype=" TYPE="+std::string(words[2]);
553 : }
554 345 : readInputLine( "grab_" + std::string(words[1]) + ": GET ARG=" + std::string(words[1]) + vtype );
555 230 : ActionToGetData* as=actionSet.selectWithLabel<ActionToGetData*>("grab_"+std::string(words[1]));
556 115 : plumed_assert( as );
557 115 : as->get_rank( val );
558 : }
559 115 : break;
560 : /* ADDED WITH API==6 */
561 51 : case cmd_getDataShape: {
562 51 : CHECK_INIT(initialized,std::string(words[0]));
563 102 : ActionToGetData* as1=actionSet.selectWithLabel<ActionToGetData*>("grab_"+std::string(words[1]));
564 51 : plumed_assert( as1 );
565 51 : as1->get_shape( val );
566 : }
567 : break;
568 : /* ADDED WITH API==6 */
569 115 : case cmd_setMemoryForData: {
570 115 : CHECK_INIT(initialized,words[0]);
571 115 : plumed_assert(nw==2 || nw==3);
572 230 : ActionToGetData* as2=actionSet.selectWithLabel<ActionToGetData*>("grab_"+std::string(words[1]));
573 115 : plumed_assert( as2 );
574 115 : as2->set_memory( val );
575 : }
576 : break;
577 : /* ADDED WITH API==6 */
578 : case cmd_setErrorHandler: {
579 0 : if(val) {
580 0 : error_handler=*static_cast<const plumed_error_handler*>(val.get<const void*>());
581 : } else {
582 0 : error_handler.handler=NULL;
583 : }
584 : }
585 : break;
586 0 : case cmd_read:
587 0 : CHECK_INIT(initialized,word);
588 0 : if(val) {
589 0 : readInputFile(val.getCString());
590 : } else {
591 0 : readInputFile("plumed.dat");
592 : }
593 : break;
594 819 : case cmd_readInputLine:
595 819 : CHECK_INIT(initialized,word);
596 819 : CHECK_NOTNULL(val,word);
597 819 : readInputLine(val.getCString());
598 777 : break;
599 2 : case cmd_readInputLines:
600 2 : CHECK_INIT(initialized,word);
601 2 : CHECK_NOTNULL(val,word);
602 2 : readInputLines(val.getCString());
603 2 : break;
604 3 : case cmd_clear: {
605 3 : CHECK_INIT(initialized,word);
606 : std::vector<int> natoms;
607 7 : for(const auto & pp : inputs ) {
608 4 : plumed_assert(pp);
609 4 : DomainDecomposition* dd=pp->castToDomainDecomposition();
610 4 : if ( dd ) {
611 1 : natoms.push_back( dd->getNumberOfAtoms() );
612 : }
613 : }
614 3 : actionSet.clearDelete();
615 : inputs.clear();
616 4 : for(unsigned i=0; i<natoms.size(); ++i) {
617 : std::string str_natoms;
618 1 : Tools::convert( natoms[i], str_natoms );
619 3 : readInputLine( MDEngine + ": DOMAIN_DECOMPOSITION NATOMS=" + str_natoms +
620 2 : " VALUE1=posx UNIT1=length PERIODIC1=NO CONSTANT1=False ROLE1=x" +
621 2 : " VALUE2=posy UNIT2=length PERIODIC2=NO CONSTANT2=False ROLE2=y" +
622 2 : " VALUE3=posz UNIT3=length PERIODIC3=NO CONSTANT3=False ROLE3=z" +
623 2 : " VALUE4=Masses UNIT4=mass PERIODIC4=NO CONSTANT4=True ROLE4=m" +
624 : " VALUE5=Charges UNIT5=charge PERIODIC5=NO CONSTANT5=True ROLE5=q");
625 :
626 : }
627 3 : setUnits( passtools->usingNaturalUnits, passtools->units );
628 : }
629 3 : break;
630 : case cmd_getApiVersion:
631 44 : CHECK_NOTNULL(val,word);
632 44 : val.set(int(10));
633 : break;
634 : // commands which can be used only before initialization:
635 1310 : case cmd_init:
636 1310 : CHECK_NOTINIT(initialized,word);
637 1310 : init();
638 : break;
639 1065 : case cmd_setRealPrecision:
640 1065 : CHECK_NOTINIT(initialized,word);
641 1065 : CHECK_NOTNULL(val,word);
642 2129 : passtools=DataPassingTools::create(val.get<int>());
643 1064 : passtools->usingNaturalUnits=false;
644 1064 : break;
645 977 : case cmd_setMDLengthUnits:
646 977 : CHECK_NOTINIT(initialized,word);
647 977 : CHECK_NOTNULL(val,word);
648 977 : passtools->MDUnits.setLength(passtools->MD2double(val));
649 : break;
650 977 : case cmd_setMDChargeUnits:
651 977 : CHECK_NOTINIT(initialized,word);
652 977 : CHECK_NOTNULL(val,word);
653 977 : passtools->MDUnits.setCharge(passtools->MD2double(val));
654 : break;
655 977 : case cmd_setMDMassUnits:
656 977 : CHECK_NOTINIT(initialized,word);
657 977 : CHECK_NOTNULL(val,word);
658 977 : passtools->MDUnits.setMass(passtools->MD2double(val));
659 : break;
660 45 : case cmd_setMDEnergyUnits:
661 45 : CHECK_NOTINIT(initialized,word);
662 45 : CHECK_NOTNULL(val,word);
663 45 : passtools->MDUnits.setEnergy(passtools->MD2double(val));
664 : break;
665 6 : case cmd_setMDTimeUnits:
666 6 : CHECK_NOTINIT(initialized,word);
667 6 : CHECK_NOTNULL(val,word);
668 6 : passtools->MDUnits.setTime(passtools->MD2double(val));
669 : break;
670 0 : case cmd_setNaturalUnits:
671 : // set the boltzman constant for MD in natural units (kb=1)
672 : // only needed in LJ codes if the MD is passing temperatures to plumed (so, not yet...)
673 : // use as cmd("setNaturalUnits")
674 0 : CHECK_NOTINIT(initialized,word);
675 0 : passtools->usingNaturalUnits=true;
676 0 : break;
677 62 : case cmd_setNoVirial: {
678 62 : CHECK_NOTINIT(initialized,word);
679 62 : ActionToPutData* ap=actionSet.selectWithLabel<ActionToPutData*>("Box");
680 62 : if( ap ) {
681 52 : ap->noforce=true;
682 : } else {
683 10 : ActionForInterface* af = actionSet.selectWithLabel<ActionForInterface*>(MDEngine);
684 10 : if( af ) {
685 0 : plumed_merror("setNoVirial should be called after number of atoms have been set");
686 : }
687 : }
688 : }
689 : break;
690 1027 : case cmd_setPlumedDat:
691 1027 : CHECK_NOTINIT(initialized,word);
692 1027 : CHECK_NOTNULL(val,word);
693 1027 : plumedDat=val.getCString();
694 : break;
695 362 : case cmd_setMPIComm:
696 362 : CHECK_NOTINIT(initialized,word);
697 362 : comm.Set_comm(val);
698 364 : for(const auto & pp : inputs ) {
699 2 : pp->Set_comm(comm);
700 : }
701 : break;
702 0 : case cmd_setMPIFComm:
703 0 : CHECK_NOTINIT(initialized,word);
704 0 : comm.Set_fcomm(val);
705 0 : for(const auto & pp : inputs ) {
706 0 : pp->Set_comm(comm);
707 : }
708 : break;
709 0 : case cmd_setMPImultiSimComm:
710 0 : CHECK_NOTINIT(initialized,word);
711 0 : multi_sim_comm.Set_comm(val);
712 : break;
713 1294 : case cmd_setNatoms: {
714 1294 : CHECK_NOTINIT(initialized,word);
715 1294 : CHECK_NOTNULL(val,word);
716 1294 : int natoms = val.get<int>();
717 : std::string str_natoms;
718 1284 : Tools::convert( natoms, str_natoms );
719 1284 : ActionForInterface* dd=actionSet.selectWithLabel<ActionForInterface*>(MDEngine);
720 1284 : if( !dd && natoms>0 )
721 3645 : readInputLine( MDEngine + ": DOMAIN_DECOMPOSITION NATOMS=" + str_natoms + +
722 2430 : " VALUE1=posx UNIT1=length PERIODIC1=NO CONSTANT1=False ROLE1=x" +
723 2430 : " VALUE2=posy UNIT2=length PERIODIC2=NO CONSTANT2=False ROLE2=y" +
724 2430 : " VALUE3=posz UNIT3=length PERIODIC3=NO CONSTANT3=False ROLE3=z" +
725 2430 : " VALUE4=Masses UNIT4=mass PERIODIC4=NO CONSTANT4=True ROLE4=m" +
726 2430 : " VALUE5=Charges UNIT5=charge PERIODIC5=NO CONSTANT5=True ROLE5=q", true );
727 : }
728 1284 : break;
729 1045 : case cmd_setTimestep: {
730 1045 : CHECK_NOTINIT(initialized,word);
731 1045 : CHECK_NOTNULL(val,word);
732 1045 : ActionToPutData* ts = actionSet.selectWithLabel<ActionToPutData*>("timestep");
733 1045 : if( !ts ) {
734 1043 : readInputLine("timestep: PUT UNIT=time PERIODIC=NO CONSTANT", true);
735 2086 : ts = actionSet.selectWithLabel<ActionToPutData*>("timestep");
736 : }
737 2090 : if( !ts->setValuePointer("timestep", val ) ) {
738 0 : plumed_error();
739 : }
740 : // The following is to avoid extra digits in case the MD code uses floats
741 : // e.g.: float f=0.002 when converted to double becomes 0.002000000094995
742 : // To avoid this, we keep only up to 6 significant digits after first one
743 1045 : if( getRealPrecision()<=4 ) {
744 166 : Value* tstepv = ts->copyOutput(0);
745 0 : double magnitude=std::pow(10,std::floor(std::log10(tstepv->get())));
746 0 : tstepv->set( std::round(tstepv->get()/magnitude*1e6)/1e6*magnitude );
747 : }
748 1045 : ts->updateUnits( passtools.get() );
749 : }
750 : break;
751 : /* ADDED WITH API==2 */
752 61 : case cmd_setKbT: {
753 61 : CHECK_NOTINIT(initialized,word);
754 61 : CHECK_NOTNULL(val,word);
755 61 : readInputLine("kBT: PUT CONSTANT PERIODIC=NO UNIT=energy", true);
756 61 : ActionToPutData* kb = actionSet.selectWithLabel<ActionToPutData*>("kBT");
757 122 : if( !kb->setValuePointer("kBT", val ) ) {
758 0 : plumed_error();
759 : }
760 61 : kb->updateUnits( passtools.get() );
761 : }
762 : break;
763 : /* ADDED WITH API==3 */
764 8 : case cmd_setRestart:
765 8 : CHECK_NOTINIT(initialized,word);
766 8 : CHECK_NOTNULL(val,word);
767 8 : if(val.get<int>()!=0) {
768 2 : restart=true;
769 : }
770 : break;
771 : /* ADDED WITH API==4 */
772 0 : case cmd_doCheckPoint:
773 0 : CHECK_INIT(initialized,word);
774 0 : CHECK_NOTNULL(val,word);
775 0 : doCheckPoint = false;
776 0 : if(val.get<int>()!=0) {
777 0 : doCheckPoint = true;
778 : }
779 : break;
780 : /* ADDED WITH API==6 */
781 : case cmd_setNumOMPthreads:
782 0 : CHECK_NOTNULL(val,word);
783 : {
784 0 : auto nt=val.get<unsigned>();
785 : if(nt==0) {
786 : nt=1;
787 : }
788 0 : OpenMP::setNumThreads(nt);
789 : }
790 : break;
791 : /* ADDED WITH API==10 */
792 : case cmd_setGpuDeviceId:
793 0 : CHECK_NOTNULL(val,word);
794 : {
795 0 : auto id=val.get<int>();
796 0 : if(id>=0) {
797 0 : gpuDeviceId=id;
798 : }
799 : }
800 : break;
801 : /* ADDED WITH API==6 */
802 : /* only used for testing */
803 : case cmd_throw:
804 61 : CHECK_NOTNULL(val,word);
805 61 : testThrow(val.getCString());
806 : /* ADDED WITH API==10 */
807 : case cmd_setNestedExceptions:
808 27 : CHECK_NOTNULL(val,word);
809 27 : if(val.get<int>()!=0) {
810 26 : nestedExceptions=true;
811 : } else {
812 1 : nestedExceptions=false;
813 : }
814 : break;
815 : /* STOP API */
816 1042 : case cmd_setMDEngine:
817 1042 : CHECK_NOTINIT(initialized,word);
818 1042 : CHECK_NOTNULL(val,word);
819 1042 : MDEngine=val.getCString();
820 : break;
821 1017 : case cmd_setLog:
822 1017 : CHECK_NOTINIT(initialized,word);
823 1017 : log.link(val.get<FILE*>());
824 : break;
825 169 : case cmd_setLogFile:
826 169 : CHECK_NOTINIT(initialized,word);
827 169 : CHECK_NOTNULL(val,word);
828 169 : log.open(val.getCString());
829 169 : break;
830 : // other commands that should be used after initialization:
831 268201 : case cmd_setStopFlag:
832 268201 : CHECK_INIT(initialized,word);
833 268201 : CHECK_NOTNULL(val,word);
834 268201 : val.get<int*>(); // just check type and discard pointer
835 268200 : stopFlag=val.copy();
836 268200 : break;
837 0 : case cmd_getExchangesFlag:
838 0 : CHECK_INIT(initialized,word);
839 0 : CHECK_NOTNULL(val,word);
840 0 : exchangePatterns.getFlag(*val.get<int*>()); // note: getFlag changes the value of the reference!
841 : break;
842 0 : case cmd_setExchangesSeed:
843 0 : CHECK_INIT(initialized,word);
844 0 : CHECK_NOTNULL(val,word);
845 0 : exchangePatterns.setSeed(val.get<int>());
846 : break;
847 0 : case cmd_setNumberOfReplicas:
848 0 : CHECK_INIT(initialized,word);
849 0 : CHECK_NOTNULL(val,word);
850 0 : exchangePatterns.setNofR(val.get<int>());
851 : break;
852 0 : case cmd_getExchangesList:
853 0 : CHECK_INIT(initialized,word);
854 0 : CHECK_NOTNULL(val,word);
855 0 : exchangePatterns.getList(val);
856 : break;
857 967 : case cmd_runFinalJobs:
858 967 : CHECK_INIT(initialized,word);
859 967 : runJobsAtEndOfCalculation();
860 : break;
861 96 : case cmd_isEnergyNeeded: {
862 96 : CHECK_INIT(initialized,word);
863 96 : CHECK_NOTNULL(val,word);
864 96 : if( name_of_energy =="" ) {
865 96 : val.set(int(0));
866 : } else {
867 0 : ActionToPutData* ap=actionSet.selectWithLabel<ActionToPutData*>(name_of_energy);
868 0 : if(ap->isActive()) {
869 0 : val.set(int(1));
870 : } else {
871 0 : val.set(int(0));
872 : }
873 : }
874 : }
875 : break;
876 7051 : case cmd_getBias:
877 7051 : CHECK_INIT(initialized,word);
878 7051 : CHECK_NOTNULL(val,word);
879 7051 : plumedQuantityToMD( "energy", getBias(), val );
880 7051 : break;
881 : case cmd_checkAction:
882 2 : CHECK_NOTNULL(val,word);
883 2 : plumed_assert(nw==2);
884 3 : val.set(int(actionRegister().check(dlloader.getHandles(), std::string(words[1])) ? 1:0));
885 2 : break;
886 : case cmd_setExtraCV: {
887 30 : CHECK_NOTNULL(val,word);
888 30 : plumed_assert(nw==2);
889 60 : if( valueExists(std::string(words[1])) ) {
890 60 : setInputValue( std::string(words[1]), 0, 1, val );
891 : }
892 : }
893 : break;
894 : case cmd_setExtraCVForce: {
895 30 : CHECK_NOTNULL(val,word);
896 30 : plumed_assert(nw==2);
897 60 : if( valueExists(std::string(words[1])) ) {
898 60 : setInputForce( std::string(words[1]), val );
899 : }
900 : }
901 : break;
902 : /* ADDED WITH API==10 */
903 : case cmd_isExtraCVNeeded:
904 10 : CHECK_NOTNULL(val,word);
905 10 : plumed_assert(nw==2);
906 10 : val.set(int(0));
907 56 : for(const auto & p : inputs) {
908 50 : if( p->getLabel()==words[1] && p->isActive() ) {
909 4 : val.set(int(1));
910 : break;
911 : }
912 : }
913 : break;
914 1089 : case cmd_GREX:
915 1089 : if(!grex) {
916 208 : grex=Tools::make_unique<GREX>(*this);
917 : }
918 1089 : plumed_massert(grex,"error allocating grex");
919 : {
920 1089 : std::string kk=std::string(words[1]);
921 1260 : for(unsigned i=2; i<words.size(); i++) {
922 342 : kk+=" "+std::string(words[i]);
923 : }
924 1089 : grex->cmd(kk.c_str(),val);
925 : }
926 1089 : break;
927 16686 : case cmd_CLTool:
928 16686 : CHECK_NOTINIT(initialized,word);
929 16686 : if(!cltool) {
930 5464 : cltool=Tools::make_unique<CLToolMain>();
931 : }
932 : {
933 16686 : std::string kk(words[1]);
934 16686 : for(unsigned i=2; i<words.size(); i++) {
935 0 : kk+=" "+std::string(words[i]);
936 : }
937 16686 : cltool->cmd(kk.c_str(),val);
938 : }
939 16686 : break;
940 : break;
941 : /* ADDED WITH API==7 */
942 : case cmd_convert: {
943 : double v;
944 57 : plumed_assert(words.size()==2);
945 280 : if(Tools::convertNoexcept(std::string(words[1]),v)) {
946 47 : passtools->double2MD(v,val);
947 : }
948 : }
949 57 : break;
950 7 : default:
951 14 : plumed_error() << "cannot interpret cmd(\"" << word << "\"). check plumed developers manual to see the available commands.";
952 : break;
953 : }
954 : }
955 :
956 1350800 : } catch (...) {
957 166 : if(log.isOpen()) {
958 : try {
959 92 : log<<"\n################################################################################\n";
960 92 : log<<Tools::concatenateExceptionMessages();
961 92 : log<<"\n################################################################################\n";
962 92 : log.flush();
963 0 : } catch(...) {
964 : // ignore errors here.
965 : // in any case, we are rethrowing this below
966 0 : }
967 : }
968 166 : throw;
969 166 : }
970 1350468 : }
971 :
972 : ////////////////////////////////////////////////////////////////////////
973 :
974 1310 : void PlumedMain::init() {
975 : // check that initialization just happens once
976 1310 : initialized=true;
977 1310 : if(!log.isOpen()) {
978 124 : log.link(stdout);
979 : }
980 1310 : log<<"PLUMED is starting\n";
981 3930 : log<<"Version: "<<config::getVersionLong()<<" (git: "<<config::getVersionGit()<<") "
982 5240 : <<"compiled on " <<config::getCompilationDate() << " at " << config::getCompilationTime() << "\n";
983 1310 : log<<"Please cite these papers when using PLUMED ";
984 2620 : log<<cite("The PLUMED consortium, Nat. Methods 16, 670 (2019)");
985 2620 : log<<cite("Tribello, Bonomi, Branduardi, Camilloni, and Bussi, Comput. Phys. Commun. 185, 604 (2014)");
986 1310 : log<<"\n";
987 1310 : log<<"For further information see the PLUMED web page at http://www.plumed.org\n";
988 1310 : log<<"Root: "<<config::getPlumedRoot()<<"\n";
989 1310 : log<<"LibraryPath: "<<config::getLibraryPath()<<"\n";
990 2620 : log<<"For installed feature, see "<<config::getPlumedRoot() + "/src/config/config.txt\n";
991 1310 : log.printf("Molecular dynamics engine: %s\n",MDEngine.c_str());
992 1310 : log.printf("Precision of reals: %d\n",passtools->getRealPrecision());
993 2374 : log.printf("Running over %d %s\n",comm.Get_size(),(comm.Get_size()>1?"nodes":"node"));
994 1310 : log<<"Number of threads: "<<OpenMP::getNumThreads()<<"\n";
995 1310 : log<<"Cache line size: "<<OpenMP::getCachelineSize()<<"\n";
996 4862 : for(const auto & pp : inputs ) {
997 3552 : plumed_assert(pp);
998 3552 : DomainDecomposition* dd=pp->castToDomainDecomposition();
999 3552 : if ( dd ) {
1000 1214 : log.printf("Number of atoms: %d\n",dd->getNumberOfAtoms());
1001 : }
1002 : }
1003 1310 : if(grex) {
1004 208 : log.printf("GROMACS-like replica exchange is on\n");
1005 : }
1006 1310 : log.printf("File suffix: %s\n",getSuffix().c_str());
1007 1310 : if(plumedDat.length()>0) {
1008 1027 : readInputFile(plumedDat);
1009 : plumedDat="";
1010 : }
1011 1310 : setUnits( passtools->usingNaturalUnits, passtools->units );
1012 1310 : ActionToPutData* ts = actionSet.selectWithLabel<ActionToPutData*>("timestep");
1013 1310 : if(ts) {
1014 1043 : log.printf("Timestep: %f\n",(ts->copyOutput(0))->get());
1015 : }
1016 1310 : ActionToPutData* kb = actionSet.selectWithLabel<ActionToPutData*>("kBT");
1017 1310 : if(kb) {
1018 61 : log.printf("KbT: %f\n",(kb->copyOutput(0))->get());
1019 : } else {
1020 1249 : log.printf("KbT has not been set by the MD engine\n");
1021 1249 : log.printf("It should be set by hand where needed\n");
1022 : }
1023 1310 : log<<"Relevant bibliography:\n";
1024 1310 : log<<citations;
1025 1310 : log<<"Please read and cite where appropriate!\n";
1026 1310 : log<<"Finished setup\n";
1027 1310 : }
1028 :
1029 51979 : void PlumedMain::setupInterfaceActions() {
1030 51979 : inputs.clear();
1031 51979 : std::vector<ActionForInterface*> ap=actionSet.select<ActionForInterface*>();
1032 420645 : for(unsigned i=0; i<ap.size(); ++i) {
1033 368666 : if( ap[i]->getName()=="ENERGY" || ap[i]->getDependencies().size()==0 ) {
1034 140901 : inputs.push_back( ap[i] );
1035 : }
1036 : }
1037 51979 : }
1038 :
1039 1050 : void PlumedMain::readInputFile(const std::string & str) {
1040 1050 : plumed_assert(initialized);
1041 1050 : log<<"FILE: "<<str<<"\n";
1042 1050 : IFile ifile;
1043 1050 : ifile.link(*this);
1044 1050 : ifile.open(str);
1045 1050 : ifile.allowNoEOL();
1046 1050 : readInputFile(ifile);
1047 1050 : log<<"END FILE: "<<str<<"\n";
1048 1050 : log.flush();
1049 :
1050 1050 : }
1051 :
1052 1052 : void PlumedMain::readInputFile(IFile & ifile) {
1053 : std::vector<std::string> words;
1054 19689 : while(Tools::getParsedLine(ifile,words) && !endPlumed) {
1055 18637 : readInputWords(words,false);
1056 : }
1057 1052 : endPlumed=false;
1058 1052 : pilots=actionSet.select<ActionPilot*>();
1059 1052 : setupInterfaceActions();
1060 1052 : }
1061 :
1062 10618 : void PlumedMain::readInputLine(const std::string & str, const bool& before_init) {
1063 10618 : if( !before_init ) {
1064 988 : plumed_assert(initialized);
1065 : }
1066 10618 : if(str.empty()) {
1067 0 : return;
1068 : }
1069 10618 : std::vector<std::string> words=Tools::getWords(str);
1070 10618 : if( before_init ) {
1071 9630 : plumed_assert( citations.empty() );
1072 : }
1073 10618 : citations.clear();
1074 10618 : readInputWords(words,before_init);
1075 10578 : if(!citations.empty()) {
1076 3 : log<<"Relevant bibliography:\n";
1077 3 : log<<citations;
1078 3 : log<<"Please read and cite where appropriate!\n";
1079 : }
1080 10618 : }
1081 :
1082 2 : void PlumedMain::readInputLines(const std::string & str) {
1083 2 : plumed_assert(initialized);
1084 2 : if(str.empty()) {
1085 0 : return;
1086 : }
1087 :
1088 2 : log<<"FILE: (temporary)\n";
1089 :
1090 : // Open a temporary file
1091 2 : auto fp=std::tmpfile();
1092 2 : plumed_assert(fp);
1093 :
1094 : // make sure file is closed (and thus deleted) also if an exception occurs
1095 : auto deleter=[](auto fp) {
1096 2 : std::fclose(fp);
1097 : };
1098 : std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
1099 :
1100 2 : auto ret=std::fputs(str.c_str(),fp);
1101 2 : plumed_assert(ret!=EOF);
1102 :
1103 2 : std::rewind(fp);
1104 :
1105 2 : IFile ifile;
1106 2 : ifile.link(*this);
1107 2 : ifile.link(fp);
1108 2 : ifile.allowNoEOL();
1109 :
1110 2 : readInputFile(ifile);
1111 2 : log<<"END FILE: (temporary)\n";
1112 2 : }
1113 :
1114 51154 : void PlumedMain::readInputWords(const std::vector<std::string> & words, const bool& before_init) {
1115 51154 : if( !before_init ) {
1116 41524 : plumed_assert(initialized);
1117 : }
1118 51154 : if(words.empty()) {
1119 : return;
1120 50982 : } else if(words[0]=="_SET_SUFFIX") {
1121 3 : plumed_assert(words.size()==2);
1122 : setSuffix(words[1]);
1123 : } else {
1124 50979 : std::vector<std::string> interpreted(words);
1125 50979 : Tools::interpretLabel(interpreted);
1126 101903 : auto action=actionRegister().create(dlloader.getHandles(),ActionOptions(*this,interpreted));
1127 50924 : if(!action) {
1128 : std::string msg;
1129 : msg ="ERROR\nI cannot understand line:";
1130 0 : for(unsigned i=0; i<interpreted.size(); ++i) {
1131 0 : msg+=" "+interpreted[i];
1132 : }
1133 : msg+="\nMaybe a missing space or a typo?";
1134 0 : log << msg;
1135 0 : log.flush();
1136 0 : plumed_merror(msg);
1137 : }
1138 50924 : action->checkRead();
1139 50924 : actionSet.emplace_back(std::move(action));
1140 50979 : };
1141 :
1142 50927 : pilots=actionSet.select<ActionPilot*>();
1143 50927 : setupInterfaceActions();
1144 : }
1145 :
1146 : ////////////////////////////////////////////////////////////////////////
1147 :
1148 0 : void PlumedMain::exit(int c) {
1149 0 : comm.Abort(c);
1150 0 : }
1151 :
1152 50977 : Log& PlumedMain::getLog() {
1153 50977 : return log;
1154 : }
1155 :
1156 280115 : void PlumedMain::calc() {
1157 280115 : prepareCalc();
1158 280099 : performCalc();
1159 280097 : }
1160 :
1161 287066 : void PlumedMain::prepareCalc() {
1162 287066 : prepareDependencies();
1163 287066 : shareData();
1164 287050 : }
1165 :
1166 : //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1167 : // here we have the main steps in "calc()"
1168 : // they can be called individually, but the standard thing is to
1169 : // traverse them in this order:
1170 287393 : void PlumedMain::prepareDependencies() {
1171 :
1172 : // Stopwatch is stopped when sw goes out of scope
1173 287393 : auto sw=stopwatch.startStop("1 Prepare dependencies");
1174 :
1175 : // activate all the actions which are on step
1176 : // activation is recursive and enables also the dependencies
1177 : // before doing that, the prepare() method is called to see if there is some
1178 : // new/changed dependency (up to now, only useful for dependences on virtual atoms,
1179 : // which can be dynamically changed).
1180 :
1181 : // First switch off all actions
1182 4674443 : for(const auto & p : actionSet) {
1183 4387050 : p->deactivate();
1184 : }
1185 :
1186 : // for optimization, an "active" flag remains false if no action at all is active
1187 287393 : active=false;
1188 1730927 : for(unsigned i=0; i<pilots.size(); ++i) {
1189 1443534 : if(pilots[i]->onStep()) {
1190 1350283 : pilots[i]->activate();
1191 1350283 : active=true;
1192 : }
1193 : };
1194 :
1195 : // This stops the driver calculation if there is not a read action
1196 287393 : if( !active && !inputsAreActive() ) {
1197 2 : stopFlag.set(int(1));
1198 : }
1199 :
1200 : // also, if one of them is the total energy, tell to atoms that energy should be collected
1201 4674443 : for(const auto & p : actionSet) {
1202 4387050 : if(p->isActive()) {
1203 2648452 : if(p->checkNeedsGradients()) {
1204 172 : p->setOption("GRADIENTS");
1205 : }
1206 : }
1207 : }
1208 :
1209 287393 : }
1210 :
1211 1763 : bool PlumedMain::inputsAreActive() const {
1212 5178 : for(const auto & ip : inputs) {
1213 5122 : if( ip->onStep() ) {
1214 : return true;
1215 : }
1216 : }
1217 : return false;
1218 : }
1219 :
1220 114 : void PlumedMain::shareAll() {
1221 456 : for(const auto & ip : inputs) {
1222 342 : ip->shareAll();
1223 : }
1224 114 : }
1225 :
1226 287150 : void PlumedMain::shareData() {
1227 : // atom positions are shared (but only if there is something to do)
1228 287150 : if(!active) {
1229 1703 : return;
1230 : }
1231 : // Stopwatch is stopped when sw goes out of scope
1232 285447 : auto sw=stopwatch.startStop("2 Sharing data");
1233 752210 : for(const auto & ip : inputs) {
1234 466779 : ip->share();
1235 : }
1236 285447 : }
1237 :
1238 7011 : void PlumedMain::performCalcNoUpdate() {
1239 7011 : waitData();
1240 7011 : justCalculate();
1241 7011 : backwardPropagate();
1242 7011 : resetInputs();
1243 7011 : }
1244 :
1245 10 : void PlumedMain::performCalcNoForces() {
1246 10 : waitData();
1247 10 : justCalculate();
1248 10 : }
1249 :
1250 280109 : void PlumedMain::performCalc() {
1251 280109 : waitData();
1252 280109 : justCalculate();
1253 280109 : backwardPropagate();
1254 280107 : update();
1255 280107 : resetInputs();
1256 280107 : }
1257 :
1258 287244 : void PlumedMain::waitData() {
1259 287244 : if(!active) {
1260 1703 : return;
1261 : }
1262 : // Stopwatch is stopped when sw goes out of scope
1263 285541 : auto sw=stopwatch.startStop("3 Waiting for data");
1264 752622 : for(const auto & ip : inputs) {
1265 467081 : if( ip->isActive() && ip->hasBeenSet() ) {
1266 168631 : ip->wait();
1267 298450 : } else if( ip->isActive() ) {
1268 8084 : ip->warning("input requested but this quantity has not been set");
1269 : }
1270 : }
1271 285541 : }
1272 :
1273 287244 : void PlumedMain::justCalculate() {
1274 287244 : if(!active) {
1275 1703 : return;
1276 : }
1277 : // Stopwatch is stopped when sw goes out of scope
1278 285541 : auto sw=stopwatch.startStop("4 Calculating (forward loop)");
1279 285541 : bias=0.0;
1280 285541 : work=0.0;
1281 :
1282 : // Check the input actions to determine if we need to calculate constants that
1283 : // depend on masses and charges
1284 : bool firststep=false;
1285 752622 : for(const auto & ip : inputs) {
1286 467081 : if( ip->firststep ) {
1287 : firststep=true;
1288 : }
1289 : }
1290 285541 : if( firststep ) {
1291 4548 : for(const auto & ip : inputs) {
1292 3370 : ip->firststep=false;
1293 : }
1294 : }
1295 :
1296 : int iaction=0;
1297 : // calculate the active actions in order (assuming *backward* dependence)
1298 4641593 : for(const auto & pp : actionSet) {
1299 : Action* p(pp.get());
1300 4356052 : plumed_assert(p);
1301 : try {
1302 4356052 : if(p->isActive()) {
1303 : // Stopwatch is stopped when sw goes out of scope.
1304 : // We explicitly declare a Stopwatch::Handler here to allow for conditional initialization.
1305 2646457 : Stopwatch::Handler sw;
1306 2646457 : if(detailedTimers) {
1307 2481 : auto actionNumberLabel=std::to_string(iaction);
1308 2481 : const unsigned m=actionSet.size();
1309 : unsigned k=0;
1310 : unsigned n=1;
1311 7443 : while(n<m) {
1312 4962 : n*=10;
1313 4962 : k++;
1314 : }
1315 2481 : auto spaces=std::string(k-actionNumberLabel.length(),' ');
1316 2481 : sw=stopwatch.startStop("4A " + spaces + actionNumberLabel+" "+p->getLabel());
1317 : }
1318 2646457 : ActionWithValue*av=p->castToActionWithValue();
1319 2646457 : ActionAtomistic*aa=p->castToActionAtomistic();
1320 : {
1321 2646457 : if(av) {
1322 2362757 : av->clearInputForces();
1323 : }
1324 2646457 : if(av) {
1325 2362757 : av->clearDerivatives();
1326 : }
1327 2646457 : if( av && av->calculateOnUpdate() ) {
1328 : continue ;
1329 : }
1330 : }
1331 : {
1332 2642442 : if(aa)
1333 483323 : if(aa->isActive()) {
1334 483323 : aa->retrieveAtoms();
1335 : }
1336 : }
1337 2642442 : if(p->checkNumericalDerivatives()) {
1338 251 : p->calculateNumericalDerivatives();
1339 : } else {
1340 2642191 : p->calculate();
1341 : }
1342 : // This retrieves components called bias
1343 2642442 : if(av) {
1344 2358742 : bias+=av->getOutputQuantity("bias");
1345 2358742 : work+=av->getOutputQuantity("work");
1346 2358742 : av->setGradientsIfNeeded();
1347 : }
1348 : // This makes all values that depend on the (fixed) masses and charges constant
1349 2642442 : if( firststep ) {
1350 28251 : p->setupConstantValues( true );
1351 : }
1352 2642442 : ActionWithVirtualAtom*avv=p->castToActionWithVirtualAtom();
1353 2642442 : if(avv) {
1354 35094 : avv->setGradientsIfNeeded();
1355 : }
1356 2646457 : }
1357 0 : } catch(...) {
1358 0 : plumed_error_nested() << "An error happened while calculating " << p->getLabel();
1359 0 : }
1360 4352037 : iaction++;
1361 : }
1362 285541 : }
1363 :
1364 0 : void PlumedMain::justApply() {
1365 0 : backwardPropagate();
1366 0 : update();
1367 0 : resetInputs();
1368 0 : }
1369 :
1370 287120 : void PlumedMain::backwardPropagate() {
1371 287120 : if(!active) {
1372 1703 : return;
1373 : }
1374 : int iaction=0;
1375 : // Stopwatch is stopped when sw goes out of scope
1376 285417 : auto sw=stopwatch.startStop("5 Applying (backward loop)");
1377 : // apply them in reverse order
1378 4639127 : for(auto pp=actionSet.rbegin(); pp!=actionSet.rend(); ++pp) {
1379 : const auto & p(pp->get());
1380 4353712 : if(p->isActive()) {
1381 :
1382 : // Stopwatch is stopped when sw goes out of scope.
1383 : // We explicitly declare a Stopwatch::Handler here to allow for conditional initialization.
1384 2644781 : Stopwatch::Handler sw;
1385 2644781 : if(detailedTimers) {
1386 2481 : auto actionNumberLabel=std::to_string(iaction);
1387 2481 : const unsigned m=actionSet.size();
1388 : unsigned k=0;
1389 : unsigned n=1;
1390 7443 : while(n<m) {
1391 4962 : n*=10;
1392 4962 : k++;
1393 : }
1394 2481 : auto spaces=std::string(k-actionNumberLabel.length(),' ');
1395 2481 : sw=stopwatch.startStop("5A " + spaces + actionNumberLabel+" "+p->getLabel());
1396 : }
1397 :
1398 2644781 : p->apply();
1399 2644781 : }
1400 4353710 : iaction++;
1401 : }
1402 :
1403 : // Stopwatch is stopped when sw goes out of scope.
1404 : // We explicitly declare a Stopwatch::Handler here to allow for conditional initialization.
1405 285415 : Stopwatch::Handler sw1;
1406 285415 : if(detailedTimers) {
1407 113 : sw1=stopwatch.startStop("5B Update forces");
1408 : }
1409 285417 : }
1410 :
1411 280186 : void PlumedMain::update() {
1412 280186 : if(!active) {
1413 1703 : return;
1414 : }
1415 :
1416 : // Stopwatch is stopped when sw goes out of scope
1417 278483 : auto sw=stopwatch.startStop("6 Update");
1418 :
1419 : // update step (for statistics, etc)
1420 278483 : updateFlags.push(true);
1421 4414908 : for(const auto & p : actionSet) {
1422 4136425 : p->beforeUpdate();
1423 6686197 : if(p->isActive() && p->checkUpdate() && updateFlagsTop()) {
1424 2549752 : ActionWithValue* av=dynamic_cast<ActionWithValue*>(p.get());
1425 2549752 : if( av && av->calculateOnUpdate() ) {
1426 4015 : p->prepare();
1427 4015 : p->calculate();
1428 : } else {
1429 2545737 : p->update();
1430 : }
1431 : }
1432 : }
1433 556970 : while(!updateFlags.empty()) {
1434 : updateFlags.pop();
1435 : }
1436 : if(!updateFlags.empty()) {
1437 : plumed_merror("non matching changes in the update flags");
1438 : }
1439 : // Check that no action has told the calculation to stop
1440 278483 : if(stopNow) {
1441 65 : if(stopFlag) {
1442 65 : stopFlag.set(int(1));
1443 : } else {
1444 0 : plumed_merror("your md code cannot handle plumed stop events - add a call to plumed.comm(stopFlag,stopCondition)");
1445 : }
1446 : }
1447 :
1448 : // flush by default every 10000 steps
1449 : // hopefully will not affect performance
1450 : // also if receive checkpointing signal
1451 278483 : if(step%10000==0||doCheckPoint) {
1452 1225 : fflush();
1453 1225 : log.flush();
1454 53126 : for(const auto & p : actionSet) {
1455 51901 : p->fflush();
1456 : }
1457 : }
1458 278483 : }
1459 :
1460 42 : void PlumedMain::load(const std::string& fileName) {
1461 42 : if(DLLoader::installed()) {
1462 42 : std::string libName=fileName;
1463 42 : size_t n=libName.find_last_of(".");
1464 43 : std::string extension="";
1465 42 : std::string base=libName;
1466 42 : if(n!=std::string::npos && n<libName.length()-1) {
1467 84 : extension=libName.substr(n+1);
1468 : }
1469 42 : if(n!=std::string::npos && n<libName.length()) {
1470 84 : base=libName.substr(0,n);
1471 : }
1472 :
1473 42 : if(extension=="cpp") {
1474 46 : libName="./"+base+"."+config::getVersionLong()+"."+config::getSoExt();
1475 : // full path command, including environment setup
1476 : // this will work even if plumed is not in the execution path or if it has been
1477 : // installed with a name different from "plumed"
1478 46 : std::string cmd=config::getEnvCommand()+" \""+config::getPlumedRoot()+"\"/scripts/mklib.sh -n -o "+libName+" "+fileName;
1479 :
1480 23 : if(std::getenv("PLUMED_LOAD_ACTION_DEBUG")) {
1481 0 : log<<"Executing: "<<cmd;
1482 : } else {
1483 23 : log<<"Compiling: "<<fileName<<" to "<<libName;
1484 : }
1485 :
1486 23 : if(comm.Get_size()>0) {
1487 23 : log<<" (only on master node)";
1488 : }
1489 23 : log<<"\n";
1490 :
1491 : // On MPI process (intracomm), we use Get_rank to make sure a single process does the compilation
1492 : // Processes from multiple replicas might simultaneously do the compilation.
1493 23 : if(comm.Get_rank()==0) {
1494 23 : static Tools::CriticalSectionWithKey<std::string> section;
1495 : // This is only locking commands that are running with identical arguments.
1496 : // It is not necessary for correctness (a second mklib would just result in a no op since
1497 : // the library is already there, even if running simultaneously).
1498 : // It however decreases the system load if many threads are used.
1499 : auto s=section.startStop(cmd);
1500 23 : int ret=std::system(cmd.c_str());
1501 23 : if(ret!=0) {
1502 2 : plumed_error() <<"An error happened while executing command "<<cmd<<"\n";
1503 : }
1504 23 : }
1505 22 : comm.Barrier();
1506 : } else {
1507 38 : libName=base+"."+config::getSoExt();
1508 : }
1509 :
1510 : // If we have multiple threads (each holding a Plumed object), each of them
1511 : // will load the library, but each of them will only see actions registered
1512 : // from the owned library
1513 41 : auto *p=dlloader.load(libName);
1514 42 : log<<"Loading shared library "<<libName.c_str()<<" at "<<p<<"\n";
1515 41 : log<<"Here is the list of new actions\n";
1516 41 : log<<"\n";
1517 82 : for(const auto & a : actionRegister().getKeysWithDLHandle(p)) {
1518 41 : log<<a<<"\n";
1519 41 : }
1520 41 : log<<"\n";
1521 : } else {
1522 0 : plumed_error()<<"While loading library "<< fileName << " loading was not enabled, please check if dlopen was found at configure time";
1523 : }
1524 41 : }
1525 :
1526 287118 : void PlumedMain::resetInputs() {
1527 758958 : for(const auto & ip : inputs) {
1528 471840 : if( ip->isActive() && ip->hasBeenSet() ) {
1529 168355 : ip->reset();
1530 : }
1531 : }
1532 287118 : }
1533 :
1534 7684 : double PlumedMain::getBias() const {
1535 7684 : return bias;
1536 : }
1537 :
1538 450 : double PlumedMain::getWork() const {
1539 450 : return work;
1540 : }
1541 :
1542 82 : FILE* PlumedMain::fopen(const char *path, const char *mode) {
1543 82 : std::string mmode(mode);
1544 83 : std::string ppath(path);
1545 82 : std::string suffix(getSuffix());
1546 82 : std::string ppathsuf=ppath+suffix;
1547 82 : FILE*fp=std::fopen(const_cast<char*>(ppathsuf.c_str()),const_cast<char*>(mmode.c_str()));
1548 82 : if(!fp) {
1549 1 : fp=std::fopen(const_cast<char*>(ppath.c_str()),const_cast<char*>(mmode.c_str()));
1550 : }
1551 84 : plumed_massert(fp,"file " + ppath + " cannot be found");
1552 81 : return fp;
1553 : }
1554 :
1555 99 : int PlumedMain::fclose(FILE*fp) {
1556 99 : return std::fclose(fp);
1557 : }
1558 :
1559 4886 : std::string PlumedMain::cite(const std::string&item) {
1560 4886 : return citations.cite(item);
1561 : }
1562 :
1563 1786 : void PlumedMain::fflush() {
1564 5382 : for(const auto & p : files) {
1565 3596 : p->flush();
1566 : }
1567 1786 : }
1568 :
1569 5199 : void PlumedMain::insertFile(FileBase&f) {
1570 5199 : files.insert(&f);
1571 5199 : }
1572 :
1573 5482 : void PlumedMain::eraseFile(FileBase&f) {
1574 5482 : files.erase(&f);
1575 5482 : }
1576 :
1577 65 : void PlumedMain::stop() {
1578 65 : stopNow=true;
1579 65 : }
1580 :
1581 967 : void PlumedMain::runJobsAtEndOfCalculation() {
1582 47461 : for(const auto & p : actionSet) {
1583 46494 : ActionWithValue* av=dynamic_cast<ActionWithValue*>(p.get());
1584 46494 : if( av && av->calculateOnUpdate() ) {
1585 405 : p->activate();
1586 : }
1587 : }
1588 47461 : for(const auto & p : actionSet) {
1589 46494 : ActionPilot* ap=dynamic_cast<ActionPilot*>(p.get());
1590 46494 : ActionWithValue* av=dynamic_cast<ActionWithValue*>(p.get());
1591 46494 : if( av && av->calculateOnUpdate() ) {
1592 405 : p->calculate();
1593 46089 : } else if( ap && !av && ap->getStride()==0 ) {
1594 70 : p->update();
1595 : } else {
1596 46019 : p->runFinalJobs();
1597 : }
1598 : }
1599 967 : }
1600 :
1601 8807491 : unsigned PlumedMain::increaseReferenceCounter() noexcept {
1602 8807491 : return ++referenceCounter;
1603 : }
1604 :
1605 8806391 : unsigned PlumedMain::decreaseReferenceCounter() noexcept {
1606 8806391 : return --referenceCounter;
1607 : }
1608 :
1609 42 : unsigned PlumedMain::useCountReferenceCounter() const noexcept {
1610 42 : return referenceCounter;
1611 : }
1612 :
1613 60 : bool PlumedMain::valueExists( const std::string& name ) const {
1614 240 : for(const auto & p : inputs) {
1615 240 : if( p->getLabel()==name ) {
1616 : return true;
1617 : }
1618 : }
1619 : return false;
1620 : }
1621 :
1622 463742 : void PlumedMain::setInputValue( const std::string& name, const unsigned& start, const unsigned& stride, const TypesafePtr & val ) {
1623 : bool found=false;
1624 1257419 : for(const auto & pp : inputs) {
1625 1257419 : if( pp->setValuePointer( name, val ) ) {
1626 463738 : pp->setStart(name, start);
1627 463738 : pp->setStride(name, stride);
1628 : found=true;
1629 : break;
1630 : }
1631 : }
1632 0 : plumed_massert( found, "found no action to set named " + name );
1633 463738 : }
1634 :
1635 291131 : void PlumedMain::setInputForce( const std::string& name, const TypesafePtr & val ) {
1636 : bool found=false;
1637 782236 : for(const auto & pp : inputs) {
1638 782236 : if( pp->setForcePointer( name, val ) ) {
1639 : found=true;
1640 : break;
1641 : }
1642 : }
1643 291113 : plumed_massert( found, "found no action to set named " + name );
1644 291113 : }
1645 :
1646 1347 : void PlumedMain::setUnits( const bool& natural, const Units& u ) {
1647 1347 : passtools->usingNaturalUnits = natural;
1648 1347 : passtools->units=u;
1649 1347 : std::vector<ActionToPutData*> idata = actionSet.select<ActionToPutData*>();
1650 10041 : for(const auto & ip : idata) {
1651 8694 : ip->updateUnits( passtools.get() );
1652 : }
1653 50806 : for(const auto & p : actionSet ) {
1654 49459 : p->resetStoredTimestep();
1655 : }
1656 1347 : }
1657 :
1658 287063 : void PlumedMain::startStep() {
1659 758695 : for(const auto & ip : inputs) {
1660 471632 : ip->resetForStepStart();
1661 : }
1662 287063 : }
1663 :
1664 114 : void PlumedMain::writeBinary(std::ostream&o)const {
1665 456 : for(const auto & ip : inputs) {
1666 342 : ip->writeBinary(o);
1667 : }
1668 114 : }
1669 :
1670 114 : void PlumedMain::readBinary(std::istream&i) {
1671 456 : for(const auto & ip : inputs) {
1672 342 : ip->readBinary(i);
1673 : }
1674 114 : }
1675 :
1676 40 : void PlumedMain::setEnergyValue( const std::string& name ) {
1677 40 : name_of_energy = name;
1678 40 : }
1679 :
1680 9625 : int PlumedMain::getRealPrecision() const {
1681 9625 : return passtools->getRealPrecision();
1682 : }
1683 :
1684 5091 : bool PlumedMain::usingNaturalUnits() const {
1685 5091 : return passtools->usingNaturalUnits;
1686 : }
1687 :
1688 16252429 : const Units& PlumedMain::getUnits() {
1689 16252429 : return passtools->units;
1690 : }
1691 :
1692 4 : PlumedMain::DeprecatedAtoms& PlumedMain::getAtoms() {
1693 4 : return datoms;
1694 : }
1695 :
1696 7222 : void PlumedMain::plumedQuantityToMD( const std::string& unit, const double& eng, const TypesafePtr & m) const {
1697 7222 : passtools->double2MD( eng/passtools->getUnitConversion(unit),m );
1698 7222 : }
1699 :
1700 0 : double PlumedMain::MDQuantityToPLUMED( const std::string& unit, const TypesafePtr & m) const {
1701 0 : double x=passtools->MD2double(m);
1702 0 : return x*passtools->getUnitConversion(unit);
1703 : }
1704 :
1705 1 : double PlumedMain::DeprecatedAtoms::getKBoltzmann() const {
1706 1 : if( plumed.usingNaturalUnits() ) {
1707 : return 1.0;
1708 : }
1709 1 : return kBoltzmann/plumed.getUnits().getEnergy();
1710 : }
1711 :
1712 1 : double PlumedMain::DeprecatedAtoms::getKbT() const {
1713 1 : ActionForInterface* kb=plumed.getActionSet().selectWithLabel<ActionForInterface*>("kBT");
1714 1 : if( kb ) {
1715 1 : return (kb->copyOutput(0))->get();
1716 : }
1717 : return 0.0;
1718 : }
1719 :
1720 1 : int PlumedMain::DeprecatedAtoms::getNatoms() const {
1721 1 : std::vector<ActionToPutData*> inputs=plumed.getActionSet().select<ActionToPutData*>();
1722 1 : for(const auto & pp : inputs ) {
1723 2 : if( pp->getRole()=="x" ) {
1724 1 : return (pp->copyOutput(0))->getShape()[0];
1725 : }
1726 : }
1727 : return 0;
1728 : }
1729 :
1730 1 : bool PlumedMain::DeprecatedAtoms::usingNaturalUnits() const {
1731 1 : return plumed.usingNaturalUnits();
1732 : }
1733 :
1734 0 : void PlumedMain::DeprecatedAtoms::setCollectEnergy(bool b) const {
1735 0 : plumed.readInputWords( Tools::getWords(plumed.MDEngine + "_energy: ENERGY"), false );
1736 0 : plumed.setEnergyValue( plumed.MDEngine + "_energy" );
1737 0 : }
1738 :
1739 0 : double PlumedMain::DeprecatedAtoms::getEnergy() const {
1740 0 : ActionToPutData* av = plumed.getActionSet().selectWithLabel<ActionToPutData*>( plumed.MDEngine + "_energy" );
1741 0 : return (av->copyOutput(0))->get();
1742 : }
1743 :
1744 5 : void PlumedMain::activateParseOnlyMode() {
1745 5 : doParseOnly=true;
1746 5 : }
1747 :
1748 2246 : bool PlumedMain::parseOnlyMode() const {
1749 2246 : return doParseOnly;
1750 : }
1751 :
1752 96 : void PlumedMain::getKeywordsForAction( const std::string& action, Keywords& keys ) const {
1753 96 : actionRegister().getKeywords( dlloader.getHandles(), action, keys );
1754 96 : }
1755 :
1756 : #ifdef __PLUMED_HAS_PYTHON
1757 : // This is here to stop cppcheck throwing an error
1758 : #endif
1759 :
1760 : #ifdef __PLUMED_HAS_DLADDR
1761 : // This is here to stop cppcheck throwing an error
1762 : #endif
1763 :
1764 : }
1765 :
1766 : //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|