Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-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 "Keywords.h"
23 : #include "Log.h"
24 : #include "Tools.h"
25 : #include <iostream>
26 : #include <iomanip>
27 : #include <algorithm>
28 :
29 : template <typename T>
30 34716 : void erase_remove(std::vector<T>& vec, const T& value) {
31 34716 : vec.erase(std::remove(vec.begin(), vec.end(), value), vec.end());
32 34716 : }
33 :
34 0 : void erase_remove(std::string& vec, const char value) {
35 0 : vec.erase(std::remove(vec.begin(), vec.end(), value), vec.end());
36 0 : }
37 :
38 : //few definition to avoid rewriting the too many times the same docstring
39 : #define NUMBERED_DOCSTRING(key) ". You can use multiple instances of this keyword i.e. " \
40 : + std::string(key) +"1, " + std::string(key) + "2, " + std::string(key) + "3..."
41 :
42 :
43 : namespace PLMD {
44 :
45 17307 : std::string toString(Keywords::argType at) {
46 : //the simple cases
47 17307 : switch (at) {
48 : case Keywords::argType::scalar:
49 5700 : return "scalar";
50 :
51 : case Keywords::argType::vector:
52 3027 : return "vector";
53 :
54 : case Keywords::argType::matrix:
55 985 : return "matrix";
56 :
57 : case Keywords::argType::grid:
58 177 : return "grid";
59 : }
60 : //the not simple cases
61 : {
62 7418 : std::string ret="";
63 7418 : std::string next="";
64 7418 : if(valid(at & Keywords::argType::scalar)) {
65 : ret+="scalar";
66 : next="/";
67 : }
68 7418 : if(valid(at & Keywords::argType::vector)) {
69 6646 : ret+=next+"vector";
70 : next="/";
71 : }
72 7418 : if(valid(at & Keywords::argType::matrix)) {
73 4543 : ret+=next+"matrix";
74 : next="/";
75 : }
76 7418 : if(valid(at & Keywords::argType::grid)) {
77 1361 : ret+=next+"grid";
78 : }
79 7418 : return ret;
80 : }
81 : //the return is outsids so the compile should block the compilation
82 : //when expanding the enum without updating the toString
83 : return "";
84 12550 : }
85 :
86 137769 : Keywords::argType stoat(std::string_view str) {
87 137769 : if(auto pos = str.find("/"); pos!=str.npos) {
88 : //here we can express that we do not want certain combinations
89 45858 : auto val=stoat(str.substr(0,pos));
90 45858 : return val | stoat(str.substr(pos+1));
91 : }
92 91911 : if (str == "scalar") {
93 : return Keywords::argType::scalar;
94 : }
95 63261 : if (str == "vector") {
96 : return Keywords::argType::vector;
97 : }
98 30978 : if (str == "matrix") {
99 : return Keywords::argType::matrix;
100 : }
101 7208 : if (str == "grid") {
102 : return Keywords::argType::grid;
103 : }
104 : // Handle the case where the string does not match any enum value.
105 0 : plumed_massert(false,"invalid argType specifier " + std::string(str));
106 : }
107 :
108 4011 : std::string toString(Keywords::componentType at) {
109 4011 : switch (at) {
110 : case Keywords::componentType::scalar:
111 2952 : return "scalar";
112 :
113 : case Keywords::componentType::vector:
114 309 : return "vector";
115 :
116 : case Keywords::componentType::matrix:
117 270 : return "matrix";
118 :
119 : case Keywords::componentType::grid:
120 69 : return "grid";
121 :
122 : case Keywords::componentType::atom:
123 3 : return "atom";
124 :
125 : case Keywords::componentType::atoms:
126 3 : return "atoms";
127 : }
128 : //the not simple cases
129 : {
130 405 : std::string ret="";
131 405 : std::string next="";
132 405 : if(valid(at & Keywords::componentType::scalar)) {
133 : ret+="scalar";
134 : next="/";
135 : }
136 405 : if(valid(at & Keywords::componentType::vector)) {
137 798 : ret+=next+"vector";
138 : next="/";
139 : }
140 405 : if(valid(at & Keywords::componentType::matrix)) {
141 156 : ret+=next+"matrix";
142 : next="/";
143 : }
144 405 : if(valid(at & Keywords::componentType::grid)) {
145 48 : ret+=next+"grid";
146 : next="/";
147 : }
148 : //I do not think these two are necessary
149 405 : if(valid(at & Keywords::componentType::atom)) {
150 0 : ret+=next+"atom";
151 : next="/";
152 : }
153 405 : if(valid(at & Keywords::componentType::atoms)) {
154 0 : ret+=next+"atoms";
155 : }
156 405 : return ret;
157 : }
158 : //the return is outsids so the compile should block the compilation
159 : //when expanding the enum without updating the toString
160 : return "";
161 : }
162 :
163 389513 : inline Keywords::componentType stoct(std::string_view str) {
164 389513 : if(auto pos = str.find("/"); pos!=str.npos) {
165 : //here we can express that we do not want certain combinations
166 96593 : auto val=stoct(str.substr(0,pos));
167 96593 : return val | stoct(str.substr(pos+1));
168 : }
169 292920 : if (str == "scalar") {
170 : return Keywords::componentType::scalar;
171 : }
172 127121 : if (str == "grid") {
173 : return Keywords::componentType::grid;
174 : }
175 101187 : if (str == "vector") {
176 : return Keywords::componentType::vector;
177 : }
178 50276 : if (str == "matrix") {
179 : return Keywords::componentType::matrix;
180 : }
181 8038 : if (str == "atom") {
182 : return Keywords::componentType::atom;
183 : }
184 4 : if (str == "atoms") {
185 : return Keywords::componentType::atoms;
186 : }
187 :
188 0 : plumed_massert(false,"invalid componentType specifier " + std::string(str));
189 : }
190 :
191 3113127 : Keywords::KeyType::keyStyle Keywords::KeyType::keyStyleFromString(std::string_view type ) {
192 3113127 : if( type=="compulsory" ) {
193 : return keyStyle::compulsory;
194 2297311 : } else if( type=="flag" ) {
195 : return keyStyle::flag;
196 1626297 : } else if( type=="optional" ) {
197 : return keyStyle::optional;
198 : //this is special: some atoms keywords have extra characters usually a "-" followed by a number
199 724531 : } else if( type.find("atoms")!=type.npos || type.find("residues")!=type.npos) {
200 : return keyStyle::atoms;
201 339464 : } else if( type=="hidden" ) {
202 : return keyStyle::hidden;
203 18208 : } else if( type=="deprecated" ) {
204 : return keyStyle::deprecated;
205 : } else {
206 0 : plumed_massert(false,"invalid keyword specifier " + std::string(type));
207 : }
208 : }
209 :
210 3090575 : Keywords::KeyType::KeyType( std::string_view type )
211 3090575 : : style(keyStyleFromString(type)) {}
212 :
213 6185425 : Keywords::KeyType::KeyType( Keywords::KeyType::keyStyle type )
214 6185425 : : style(type) {}
215 :
216 22552 : void Keywords::KeyType::setStyle( std::string_view type ) {
217 22552 : style=keyStyleFromString(type);
218 22552 : }
219 :
220 6184000 : Keywords::keyInfo::keyInfo()
221 6184000 : : type(Keywords::KeyType::keyStyle::unknown),
222 6184000 : docstring(""),
223 : defaultValue(std::monostate()),
224 6184000 : allowmultiple(false)
225 6184000 : {}
226 :
227 3092000 : Keywords::keyInfo& Keywords::keyInfo::setType(Keywords::KeyType t) {
228 3092000 : type=t;
229 3092000 : return *this;
230 : }
231 3092000 : Keywords::keyInfo& Keywords::keyInfo::setDocString(std::string_view d) {
232 3092000 : docstring=d;
233 3092000 : return *this;
234 : }
235 610671 : Keywords::keyInfo& Keywords::keyInfo::setDefaultValue(std::string_view d) {
236 610671 : defaultValue=std::string(d);
237 610671 : return *this;
238 : }
239 673280 : Keywords::keyInfo& Keywords::keyInfo::setDefaultFlag(bool a) {
240 673280 : defaultValue=a;
241 673280 : return *this;
242 : }
243 46053 : Keywords::keyInfo& Keywords::keyInfo::setArgumentType(argType a) {
244 46053 : argument_type=a;
245 46053 : return *this;
246 : }
247 3092000 : Keywords::keyInfo& Keywords::keyInfo::setAllowMultiple(bool a) {
248 3092000 : allowmultiple=a;
249 3092000 : return *this;
250 : }
251 3096902 : Keywords::keyInfo& Keywords::keyInfo::setLinkedAction(std::string_view a) {
252 3096902 : linkaction=a;
253 3096902 : return *this;
254 : }
255 30095 : Keywords::keyInfo& Keywords::keyInfo::setLinkedPage(std::string_view a) {
256 30095 : linkpage=a;
257 30095 : return *this;
258 : }
259 1322628 : bool Keywords::keyInfo::isArgument() const {
260 1322628 : return std::holds_alternative<argType>(argument_type);
261 : }
262 :
263 456397 : Keywords::component::component():
264 : //the 0 ensures something that always fails unles explicitly set
265 456397 : type(static_cast<componentType>(0)) {}
266 228195 : Keywords::component& Keywords::component::setKey(std::string_view k) {
267 228195 : key=k;
268 228195 : return *this;
269 : }
270 228195 : Keywords::component& Keywords::component::setDocstring(std::string_view d) {
271 228195 : docstring=d;
272 228195 : return *this;
273 : }
274 228195 : Keywords::component& Keywords::component::setType(componentType t) {
275 228195 : type=t;
276 228195 : return *this;
277 : }
278 :
279 3004702 : std::string Keywords::getStyle( const std::string& k ) const {
280 3004702 : plumed_massert( exists(k)||reserved(k), "Did not find keyword " + k );
281 3004702 : return (keywords.at(k).type).toString();
282 : }
283 :
284 0 : void Keywords::add( const Keywords& newkeys ) {
285 : //copies data from
286 : //loop on the declared keys
287 0 : for(const auto& thiskey:newkeys.keys) {
288 0 : plumed_massert( !exists(thiskey), "keyword " + thiskey + " is in twice" );
289 0 : plumed_massert( !reserved(thiskey), "keyword " + thiskey + " is in twice" );
290 0 : keywords[thiskey] = newkeys.keywords.at(thiskey);
291 0 : keys.emplace_back( thiskey );
292 : }
293 : //loop on the reserved keys
294 0 : for (const auto&thiskey : newkeys.reserved_keys) {
295 0 : plumed_massert( !exists(thiskey), "keyword " + thiskey + " is in twice" );
296 0 : plumed_massert( !reserved(thiskey), "keyword " + thiskey + " is in twice" );
297 :
298 0 : keywords[thiskey] = newkeys.keywords.at(thiskey);
299 0 : reserved_keys.emplace_back( thiskey );
300 : }
301 0 : for (const auto& thisnam : newkeys.cnames) {
302 0 : plumed_massert( components.find(thisnam)!=components.end(), "keyword for component" + thisnam + " is in twice" );
303 0 : cnames.push_back( thisnam );
304 0 : components[thisnam]=newkeys.components.at(thisnam);
305 : }
306 0 : }
307 :
308 1808890 : void Keywords::addOrReserve( std::string_view keytype,
309 : std::string_view key,
310 : std::string_view docstring,
311 : const bool reserve ) {
312 1808890 : plumed_massert(!exists(key), "keyword " + std::string(key) + " has already been registered");
313 1808890 : plumed_massert(!reserved(key),"keyword " + std::string(key) + " has already been reserved");
314 1808890 : std::string t_type{keytype};
315 1808890 : bool isNumbered = keytype=="numbered";
316 1808890 : if( isNumbered ) {
317 : t_type="optional";
318 : }
319 : //let's fail asap in case of typo
320 1808890 : auto type = KeyType(t_type);
321 1808890 : if (!reserve) {
322 1512604 : plumed_massert( !type.isFlag(), "use addFlag() to register a flag keyword (" + std::string(key) + ")");
323 : }
324 :
325 0 : std::string fd{docstring};
326 : bool allowMultiple= false;
327 1808890 : if( isNumbered ) {
328 76248 : fd += NUMBERED_DOCSTRING(key);
329 : allowMultiple = true;
330 : }
331 :
332 7235560 : keywords[std::string(key)] = keyInfo()
333 1808890 : .setType(type)
334 1808890 : .setDocString(fd)
335 1808890 : .setAllowMultiple(allowMultiple)
336 5426670 : .setLinkedAction("none");
337 1808890 : if( type.isAtomList() ) {
338 : //keytype may be "residues" or something like "atoms-3"
339 384334 : keywords.find(key)->second.atomtag=keytype;
340 384334 : if (isaction && keytype == "atoms") { //this narrow the doctrstring ONLY to atoms
341 160287 : keywords.find(key)->second.docstring+= ". For more information on how to specify lists of atoms see the section of the manual on specifying atoms";
342 : }
343 : }
344 1808890 : if (reserve) {
345 296286 : reserved_keys.emplace_back(key);
346 : } else {
347 1512604 : keys.emplace_back(key);
348 : }
349 1808890 : }
350 :
351 296286 : void Keywords::reserve( std::string_view keytype,
352 : std::string_view key,
353 : std::string_view docstring ) {
354 : //If you modify this function, please update also the add with three arguments
355 296286 : addOrReserve(keytype,key,docstring,true);
356 296286 : }
357 :
358 1425 : void Keywords::reserveFlag(const std::string & key, const bool defaultValue, const std::string & docstring ) {
359 1425 : plumed_assert( !exists(key) && !reserved(key) );
360 : std::string defstr;
361 1425 : if( defaultValue ) {
362 : defstr="( default=on ) ";
363 : } else {
364 : defstr="( default=off ) ";
365 : }
366 :
367 2850 : keywords[key] = keyInfo()
368 1425 : .setType(KeyType{KeyType::keyStyle::flag})
369 2850 : .setDocString(defstr + docstring)
370 1425 : .setAllowMultiple(false)
371 1425 : .setDefaultFlag(defaultValue)
372 2850 : .setLinkedAction("none");
373 1425 : reserved_keys.emplace_back(key);
374 1425 : }
375 :
376 : ///this "copies" a reserved key into the keylist so it can be used
377 25896 : void Keywords::use(std::string_view k ) {
378 : // Keywords that were reserved in older versions (e.g. ARG of Bias and Function) are now added directly
379 : // by the base classes: code written for those versions (e.g. LOADed actions) calls use() on them.
380 25896 : if( exists(k) ) {
381 : return;
382 : }
383 25895 : plumed_massert( reserved(k), "the " + std::string(k) + " keyword is not reserved");
384 25895 : keys.emplace_back(k);
385 : }
386 :
387 29511 : void Keywords::reset_style( const std::string & k, const std::string & style ) {
388 29511 : plumed_massert( exists(k) || reserved(k), "no " + k + " keyword" );
389 : //Adding this two feels correct, but breaks some actions where a numbered keyword is changed to compulsory
390 : //So also the atomtag is removed
391 : //keywords.at(k).atomtag="";
392 : //keywords.at(k).allowmultiple=false;
393 29511 : if( style=="numbered" ) {
394 6959 : keywords.at(k).allowmultiple=true;
395 6959 : return;
396 : }
397 22552 : keywords.at(k).type.setStyle(style);
398 22552 : if( (keywords.at(k).type).isAtomList() ) {
399 733 : keywords.at(k).atomtag=style;
400 : }
401 : }
402 :
403 1512604 : void Keywords::add(std::string_view keytype,
404 : std::string_view key,
405 : std::string_view docstring ) {
406 : //the 'false' deactivates the "reserve mode"
407 1512604 : addOrReserve(keytype,key,docstring,false);
408 1512604 : }
409 :
410 841 : void Keywords::addDeprecatedFlag( const std::string& key,
411 : const std::string& replacement ) {
412 841 : addDeprecatedKeyword(key, replacement);
413 841 : keywords.at(key).setDefaultFlag(false);
414 841 : }
415 :
416 8360 : void Keywords::addDeprecatedKeyword( std::string_view key,
417 : const std::string& replacement ) {
418 8360 : if( exists(replacement) ) {
419 14158 : std::string docs = "You should use " + replacement + " instead of this keyword which was used in older versions of PLUMED and is provided for back compatibility only.";
420 7079 : add("deprecated", key, docs);
421 : // Action reads the value given with key as if it was given with replacement
422 21237 : keywords.at(std::string(key)).replacement=replacement;
423 : } else {
424 1281 : add("deprecated", key, "Including this keyword in the input to this action makes no difference to the calculation performed it was used in older versions of PLUMED and is provided here for back compatibility only.");
425 : }
426 8360 : }
427 :
428 46040 : void Keywords::addInputKeyword( const std::string & keyType,
429 : const std::string & key,
430 : const std::string & datatype,
431 : const std::string & docstring ) {
432 46040 : addInputKeyword(keyType,key,stoat(datatype),docstring);
433 46040 : }
434 :
435 46040 : void Keywords::addInputKeyword( const std::string & keyType,
436 : const std::string & key,
437 : argType datatype,
438 : const std::string & docstring ) {
439 46040 : if( exists(key) ) {
440 1120 : remove(key);
441 : }
442 : //insert({k,datatype}) Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.[cit.]
443 : //operator[] inserts if the key doesn't exist, or overwrites if it does
444 46040 : add( keyType, key, docstring );
445 46040 : keywords.at(key).setArgumentType(datatype);
446 46040 : }
447 :
448 13 : void Keywords::addInputKeyword( const std::string & keyType,
449 : const std::string & key,
450 : const std::string & datatype,
451 : const std::string & defaultV,
452 : const std::string & docstring ) {
453 13 : addInputKeyword(keyType,key,stoat(datatype),defaultV,docstring);
454 13 : }
455 :
456 13 : void Keywords::addInputKeyword( const std::string & keyType,
457 : const std::string & key,
458 : argType datatype,
459 : const std::string & defaultV,
460 : const std::string & docstring ) {
461 13 : if( exists(key) ) {
462 0 : remove(key);
463 : }
464 13 : add( keyType, key, defaultV, docstring );
465 13 : keywords[key].setArgumentType(datatype);
466 13 : }
467 :
468 610671 : void Keywords::add( std::string_view keytype,
469 : std::string_view key,
470 : std::string_view defaultValue,
471 : std::string_view docstring ) {
472 : //let's fail asap in case of typo
473 610671 : auto type = KeyType(keytype);
474 :
475 610671 : plumed_massert( !exists(key) && !reserved(key), "failing on keyword " + std::string(key) );
476 : // An optional keyword can't have a default
477 610671 : plumed_massert(type.isCompulsory() || type.isHidden(), "You can't set a default value for an optional keyword, failing on " + std::string(key));
478 1832013 : keywords[std::string(key)] = keyInfo()
479 610671 : .setType(type)
480 610671 : .setDefaultValue(defaultValue)
481 1832013 : .setDocString("( default=" + std::string(defaultValue) + " ) " + std::string(docstring) )
482 610671 : .setAllowMultiple(false)
483 1832013 : .setLinkedAction("none");
484 :
485 610671 : keys.emplace_back(key);
486 610671 : }
487 :
488 671014 : void Keywords::addFlag(std::string_view key, bool defaultValue, std::string_view docstring) {
489 671014 : plumed_massert( !exists(key) && !reserved(key), "keyword " + std::string(key) + " has already been registered");
490 671014 : plumed_massert( !defaultValue, "the second argument to addFlag must be false " + std::string(key) );
491 671014 : std::string defstr="( default=off ) ";
492 2013042 : keywords[std::string(key)] = keyInfo()
493 671014 : .setType(KeyType("flag"))
494 671014 : .setDefaultFlag(false)
495 1342028 : .setDocString(std::string(defstr) + std::string(docstring))
496 671014 : .setAllowMultiple(false)
497 2013042 : .setLinkedAction("none");
498 :
499 671014 : keys.emplace_back(key);
500 1342028 : }
501 :
502 34491 : void Keywords::remove( const std::string & k ) {
503 : bool found=false;
504 34491 : if(exists(k)) {
505 34491 : erase_remove(keys,k);
506 : found=true;
507 0 : } else if(reserved(k)) {
508 0 : erase_remove(reserved_keys,k);
509 : found=true;
510 : }
511 0 : plumed_massert(found,"You are trying to forbid " + k + " a keyword that isn't there");
512 : // Delete documentation, type and so on from the description
513 : keywords.erase(k);
514 :
515 : // Remove any output components that this keyword creates
516 : //we need the double loop because we should not remove and iterate on the map at the same time
517 34491 : std::vector<std::string> markForRemoval{};
518 61853 : for(const auto& dkey : components ) {
519 27362 : if( dkey.second.key==k ) {
520 225 : markForRemoval.push_back(dkey.first);
521 : }
522 : }
523 34716 : for(const auto& toremove : markForRemoval ) {
524 225 : removeOutputComponent( toremove );
525 : }
526 34491 : }
527 :
528 608617 : bool Keywords::numbered( const std::string & k ) const {
529 1217234 : if( style( k,"atoms") ) {
530 : return true;
531 : }
532 : //should I add also the "reserved(k)" to the test?
533 362399 : plumed_massert( exists(k), "Did not find keyword " + k );
534 362399 : return keywords.at(k).allowmultiple;
535 : }
536 :
537 2987146 : bool Keywords::style( const std::string & k, const std::string & t ) const {
538 2987146 : if( getStyle(k)==t ) {
539 : return true;
540 : }
541 : return false;
542 : }
543 :
544 1584424 : unsigned Keywords::size() const {
545 1584424 : return keys.size();
546 : }
547 :
548 727477 : std::string Keywords::getKeyword( const unsigned i ) const {
549 727477 : plumed_assert( i<size() );
550 727477 : return keys[i];
551 : }
552 :
553 656184 : std::string Keywords::getDeprecatedReplacement( std::string_view k ) const {
554 : auto kw=keywords.find(k);
555 656184 : if( kw==keywords.end() || !kw->second.type.isDeprecated() ) {
556 647820 : return "";
557 : }
558 8364 : return kw->second.replacement;
559 : }
560 :
561 8494417 : bool Keywords::exists( std::string_view k ) const {
562 8494417 : return std::find(keys.begin(), keys.end(), k) != keys.end();
563 : }
564 :
565 3117895 : bool Keywords::reserved( std::string_view k ) const {
566 3117895 : return std::find(reserved_keys.begin(), reserved_keys.end(), k) != reserved_keys.end();
567 : }
568 :
569 2 : void Keywords::print_template(const std::string& actionname, bool include_optional) const {
570 : std::printf("%s",actionname.c_str());
571 : {
572 2 : std::string prevtag="start";
573 20 : for(const auto& key : keys) {
574 18 : if( keywords.at(key).type.isAtomList() ) {
575 2 : plumed_massert( keywords.at(key).atomtag!="", "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello");
576 2 : const auto & currentTag=keywords.at(key).atomtag;
577 2 : if( prevtag!="start" && prevtag!=currentTag ) {
578 : break;
579 : }
580 2 : if( currentTag.find("residues")!=std::string::npos) {
581 : std::printf(" %s=<residue selection>", key.c_str() );
582 : } else {
583 : std::printf(" %s=<atom selection>", key.c_str() );
584 : }
585 : prevtag=currentTag;
586 : }
587 : }
588 : }
589 :
590 20 : for(const auto& key : keys) {
591 18 : if ( keywords.at(key).type.isCompulsory() ) {
592 : std::string def;
593 0 : if( getDefaultValue( key, def) ) {
594 : std::printf(" %s=%s ", key.c_str(), def.c_str() );
595 : } else {
596 : std::printf(" %s= ", key.c_str() );
597 : }
598 18 : } else if (include_optional) {
599 : // TG no defaults for optional keywords?
600 : std::printf(" [%s]", key.c_str() );
601 :
602 : }
603 : }
604 : std::printf("\n");
605 : std::flush(std::cout);
606 2 : }
607 :
608 472 : void Keywords::print_vim() const {
609 6076 : for(const auto& key : keys) {
610 5604 : if( keywords.at(key).type.isFlag() ) {
611 : std::printf( ",flag:%s", key.c_str() );
612 : } else {
613 4634 : if( keywords.at(key).allowmultiple ) {
614 : std::printf(",numbered:%s",key.c_str() );
615 : } else {
616 : std::printf(",option:%s",key.c_str() );
617 : }
618 : }
619 : }
620 472 : std::fprintf(stdout, "\n%s", getHelpString().c_str() );
621 472 : }
622 :
623 2 : void Keywords::print_html() const {
624 : // This is the part that outputs the details of the components
625 2 : if( cnames.size()>0 ) {
626 : unsigned ndef=0;
627 : //running on the order of insertion
628 8 : for(const auto& cname : cnames) {
629 7 : if(components.at(cname).key=="default") {
630 1 : ndef++;
631 : }
632 : }
633 :
634 1 : if( ndef>0 ) {
635 1 : std::cout<<"\\par Description of components\n\n";
636 1 : std::cout<<cstring<<"\n\n";
637 1 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
638 : std::printf("<tr> <td width=5%%> <b> Quantity </b> </td> <td> <b> Description </b> </td> </tr>\n");
639 : unsigned nndef=0;
640 8 : for(const auto& cname : cnames) {
641 7 : if( components.at(cname).key!="default" ) {
642 6 : nndef++;
643 6 : continue;
644 : }
645 : std::printf("<tr>\n");
646 : std::printf("<td width=15%%> <b> %s </b></td>\n",cname.c_str() );
647 1 : std::printf("<td> %s </td>\n",(components.at(cname).docstring).c_str() );
648 : std::printf("</tr>\n");
649 : }
650 1 : std::cout<<"</table>\n\n";
651 1 : if( nndef>0 ) {
652 1 : std::cout<<"In addition the following quantities can be calculated by employing the keywords listed below"<<std::endl;
653 1 : std::cout<<"\n\n";
654 1 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
655 : std::printf("<tr> <td width=5%%> <b> Quantity </b> </td> <td> <b> Keyword </b> </td> <td> <b> Description </b> </td> </tr>\n");
656 8 : for(const auto& cname : cnames) {
657 7 : if( components.at(cname).key!="default") {
658 : std::printf("<tr>\n");
659 : std::printf("<td width=5%%> <b> %s </b></td> <td width=10%%> <b> %s </b> </td> \n",
660 6 : cname.c_str(),(components.at(cname).key).c_str() );
661 6 : std::printf("<td> %s </td>\n",(components.at(cname).docstring).c_str() );
662 : std::printf("</tr>\n");
663 : }
664 : }
665 1 : std::cout<<"</table>\n\n";
666 : }
667 : } else {
668 : unsigned nregs=0;
669 0 : for(const auto& cname : cnames) {
670 0 : if( exists(components.at(cname).key) ) {
671 0 : nregs++;
672 : }
673 : }
674 0 : if( nregs>0 ) {
675 0 : std::cout<<"\\par Description of components\n\n";
676 0 : std::cout<<cstring<<"\n\n";
677 0 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
678 : std::printf("<tr> <td width=5%%> <b> Quantity </b> </td> <td> <b> Keyword </b> </td> <td> <b> Description </b> </td> </tr>\n");
679 0 : for(const auto& cname : cnames) {
680 0 : if( exists(components.at(cname).key) ) {
681 : std::printf("<tr>\n");
682 : std::printf("<td width=5%%> <b> %s </b></td> <td width=10%%> <b> %s </b> </td> \n",
683 0 : cname.c_str(),(components.at(cname).key).c_str() );
684 0 : std::printf("<td> %s </td>\n",(components.at(cname).docstring).c_str() );
685 : std::printf("</tr>\n");
686 : }
687 : }
688 0 : std::cout<<"</table>\n\n";
689 : }
690 : }
691 : }
692 :
693 : unsigned nkeys=0;
694 54 : for(const auto& key : keys) {
695 52 : if ( keywords.at(key).type.isAtomList() ) {
696 15 : nkeys++;
697 : }
698 : }
699 2 : if( nkeys>0 ) {
700 2 : if(isaction && isatoms) {
701 1 : std::cout<<"\\par The atoms involved can be specified using\n\n";
702 1 : } else if(isaction) {
703 0 : std::cout<<"\\par The data to analyze can be the output from another analysis algorithm\n\n";
704 : } else {
705 1 : std::cout<<"\\par The input trajectory is specified using one of the following\n\n";
706 : }
707 2 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
708 2 : std::string prevtag="start";
709 : unsigned counter=0;
710 54 : for(const auto& key : keys) {
711 52 : if ( keywords.at(key).type.isAtomList() ) {
712 15 : const auto& currentTag = keywords.at(key).atomtag;
713 15 : plumed_massert( currentTag!="", "keyword " + key + " allegedly specifies atoms but no tag has been specified. Please email Gareth Tribello");
714 15 : if( prevtag!="start" && prevtag!=currentTag && isaction ) {
715 0 : std::cout<<"</table>\n\n";
716 0 : if( isatoms ) {
717 0 : std::cout<<"\\par Or alternatively by using\n\n";
718 0 : } else if( counter==0 ) {
719 0 : std::cout<<"\\par Alternatively data can be collected from the trajectory using \n\n";
720 0 : counter++;
721 : } else {
722 0 : std::cout<<"\\par Lastly data collected in a previous analysis action can be reanalyzed by using the keyword \n\n";
723 : }
724 0 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
725 : }
726 15 : print_html_item( key );
727 : prevtag=currentTag;
728 : }
729 : }
730 2 : std::cout<<"</table>\n\n";
731 : }
732 : nkeys=0;
733 54 : for(const auto& key : keys) {
734 52 : if ( keywords.at(key).type.isCompulsory() ) {
735 4 : nkeys++;
736 : }
737 : }
738 2 : if( nkeys>0 ) {
739 1 : if(isaction) {
740 0 : std::cout<< "\\par Compulsory keywords\n\n";
741 : } else {
742 1 : std::cout<<"\\par The following must be present\n\n";
743 : }
744 1 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
745 44 : for(const auto& key : keys) {
746 43 : if ( keywords.at(key).type.isCompulsory() ) {
747 4 : print_html_item( key );
748 : }
749 : }
750 1 : std::cout<<"</table>\n\n";
751 : }
752 : nkeys=0;
753 54 : for(const auto& key : keys) {
754 52 : if ( keywords.at(key).type.isFlag() || keywords.at(key).type.isOptional() ) {
755 25 : nkeys++;
756 : }
757 : }
758 2 : if( nkeys>0 ) {
759 2 : if(isaction) {
760 1 : std::cout<<"\\par Options\n\n";
761 : } else {
762 1 : std::cout<<"\\par The following options are available\n\n";
763 : }
764 2 : std::cout<<" <table align=center frame=void width=95%% cellpadding=5%%> \n";
765 54 : for(const auto& key : keys) {
766 52 : if ( keywords.at(key).type.isFlag() ) {
767 11 : print_html_item( key );
768 : }
769 : }
770 2 : std::cout<<"\n";
771 : }
772 : nkeys=0;
773 54 : for(const auto& key : keys) {
774 52 : if ( keywords.at(key).type.isOptional() ) {
775 14 : nkeys++;
776 : }
777 : }
778 2 : if( nkeys>0 ) {
779 44 : for(const auto& key : keys) {
780 43 : if ( keywords.at(key).type.isOptional() ) {
781 14 : print_html_item( key );
782 : }
783 : }
784 : }
785 2 : std::cout<<"</table>\n\n";
786 2 : }
787 :
788 1 : void Keywords::print_spelling() const {
789 10 : for(const auto& key : keys) {
790 : std::printf("%s\n", key.c_str() );
791 : }
792 8 : for(const auto& cname : cnames) {
793 : std::printf("%s\n",cname.c_str() );
794 : }
795 1 : }
796 :
797 15110 : std::string Keywords::getKeywordDocs( const std::string& key ) const {
798 15110 : bool killdot=( keywords.at(key).docstring.find("\\f$")!=std::string::npos ); // Check for latex
799 15110 : std::vector<std::string> w=Tools::getWords( keywords.at(key).docstring );
800 15110 : std::stringstream sstr;
801 15110 : sstr<<std::setw(23)<<key<<" - ";
802 : unsigned nl=0;
803 15110 : std::string blank=" ";
804 254056 : for(unsigned i=0; i<w.size(); ++i) {
805 238990 : nl+=w[i].length() + 1;
806 238990 : if( nl>60 ) {
807 44991 : sstr<<"\n"<<std::setw(23)<<blank<<" "<<w[i]<<" ";
808 : nl=0;
809 : } else {
810 223993 : sstr<<w[i]<<" ";
811 : }
812 238990 : if( killdot && w[i].find(".")!=std::string::npos ) {
813 : break; // If there is latex only write up to first dot
814 : }
815 : }
816 15110 : sstr<<"\n";
817 15110 : return sstr.str();
818 15110 : }
819 :
820 1887 : std::string Keywords::getHelpString() const {
821 : std::string helpstr;
822 : unsigned nkeys=0;
823 24300 : for(const auto& key : keys) {
824 22413 : if ( keywords.at(key).type.isAtomList() ) {
825 1317 : nkeys++;
826 : }
827 : }
828 1887 : if( nkeys>0 ) {
829 : helpstr += "The input trajectory can be in any of the following formats: \n\n";
830 11110 : for(const auto& key : keys) {
831 10461 : if ( keywords.at(key).type.isAtomList() ) {
832 2634 : helpstr += getKeywordDocs( key );
833 : }
834 : }
835 : }
836 : unsigned ncompulsory=0;
837 24300 : for(const auto& key : keys) {
838 22413 : if ( keywords.at(key).type.isCompulsory() ) {
839 5324 : ncompulsory++;
840 : }
841 : }
842 1887 : if( ncompulsory>0 ) {
843 : helpstr += "\nThe following arguments are compulsory: \n\n";
844 20750 : for(const auto& key : keys) {
845 19180 : if ( keywords.at(key).type.isCompulsory() ) {
846 10648 : helpstr += getKeywordDocs( key );
847 : }
848 : }
849 : }
850 : nkeys=0;
851 24300 : for(const auto& key : keys) {
852 22413 : if ( keywords.at(key).type.isFlag() ) {
853 3869 : nkeys++;
854 : }
855 : }
856 1887 : if( nkeys>0 ) {
857 1391 : if(ncompulsory>0) {
858 : helpstr += "\nIn addition you may use the following options: \n\n";
859 : } else {
860 : helpstr += "\nThe following options are available\n\n";
861 : }
862 19676 : for(const auto& key : keys) {
863 18285 : if ( keywords.at(key).type.isFlag() ) {
864 7738 : helpstr += getKeywordDocs( key ).c_str();
865 : }
866 : }
867 : }
868 : nkeys=0;
869 24300 : for(const auto& key : keys) {
870 22413 : if ( keywords.at(key).type.isOptional() ) {
871 4600 : nkeys++;
872 : }
873 : }
874 1887 : if( nkeys>0 ) {
875 18006 : for(const auto& key : keys) {
876 16828 : if ( keywords.at(key).type.isOptional() ) {
877 9200 : helpstr += getKeywordDocs( key );
878 : }
879 : }
880 : helpstr += "\n";
881 : }
882 1887 : return helpstr;
883 : }
884 :
885 0 : void Keywords::print( Log& log ) const {
886 0 : log.printf("%s", getHelpString().c_str() );
887 0 : }
888 :
889 2 : void Keywords::print( FILE* out ) const {
890 2 : fprintf( out,"%s", getHelpString().c_str() );
891 2 : }
892 :
893 30 : std::string Keywords::getTooltip( const std::string& name ) const {
894 30 : std::size_t dd=name.find_first_of("0123456789");
895 30 : std::string kname=name.substr(0,dd);
896 30 : if( !exists(kname) ) {
897 0 : return "<b> could not find this keyword </b>";
898 : }
899 : std::string mystring;
900 30 : std::string docstr = keywords.at(kname).docstring;
901 30 : if( keywords.at(kname).type.isCompulsory() ) {
902 : mystring += "<b>compulsory keyword ";
903 9 : if( docstr.find("default")!=std::string::npos ) {
904 3 : std::size_t bra = docstr.find_first_of(")");
905 3 : mystring += docstr.substr(0,bra+1);
906 6 : docstr = docstr.substr(bra+1);
907 : }
908 : mystring += "</b>\n";
909 : }
910 30 : std::vector<std::string> w=Tools::getWords( docstr );
911 : unsigned nl=0;
912 375 : for(unsigned i=0; i<w.size(); ++i) {
913 363 : nl+=w[i].length() + 1;
914 363 : if( nl>80 ) {
915 12 : mystring += w[i] + "\n";
916 : nl=0;
917 : } else {
918 714 : mystring += w[i] + " ";
919 : }
920 363 : if( w[i].find(".")!=std::string::npos ) {
921 : break; // Only write up the first dot
922 : }
923 : }
924 30 : return mystring;
925 30 : }
926 :
927 44 : void Keywords::print_html_item( const std::string& key ) const {
928 : std::printf("<tr>\n");
929 : std::printf("<td width=15%%> <b> %s </b></td>\n",key.c_str() );
930 44 : std::printf("<td> %s </td>\n",(keywords.at(key).docstring).c_str() );
931 : std::printf("</tr>\n");
932 44 : }
933 :
934 137868 : bool Keywords::getLogicalDefault(const std::string & key, bool& def ) const {
935 137868 : if (std::holds_alternative<bool>(keywords.at(key).defaultValue)) {
936 136004 : def = std::get<bool>(keywords.at(key).defaultValue);
937 136004 : return true;
938 : } else {
939 : return false;
940 : }
941 : }
942 :
943 41204 : bool Keywords::getDefaultValue(const std::string & key, std::string& def ) const {
944 57456 : plumed_massert( style(key,"compulsory") || style(key,"hidden"),"You can't ask for the default value of a keyword that doesn't have one ("+key+")" );
945 41204 : if (std::holds_alternative<std::string>(keywords.at(key).defaultValue)) {
946 33080 : def = std::get<std::string>(keywords.at(key).defaultValue);
947 33080 : return true;
948 : } else {
949 : return false;
950 : }
951 : }
952 :
953 0 : void Keywords::destroyData() {
954 0 : keys.clear();
955 0 : reserved_keys.clear();
956 : keywords.clear();
957 : components.clear();
958 : //cname was missing before, is it wanted or not?
959 0 : cnames.clear();
960 0 : }
961 :
962 50875 : void Keywords::setComponentsIntroduction( const std::string& instr ) {
963 50875 : cstring = instr;
964 50875 : }
965 :
966 8 : void Keywords::addOutputComponent( const std::string& name, const std::string& key, const std::string& descr ) {
967 8 : addOutputComponent( name, key, "scalar", descr );
968 8 : }
969 :
970 133226 : void Keywords::addOutputComponent( const std::string& name, const std::string& key, const std::string& type, const std::string& descr ) {
971 133226 : addOutputComponent(name,key,stoct(type),descr);
972 133226 : }
973 :
974 165002 : void Keywords::addOutputComponent( const std::string& name, const std::string& key, componentType type, const std::string& descr ) {
975 165002 : plumed_assert( !outputComponentExists(name) );
976 165002 : plumed_massert( name!=".#!value", name + " is reserved for storing description of value" );
977 165002 : plumed_massert( name.find("-")==std::string::npos,"dash is reseved character in component names" );
978 :
979 165002 : std::size_t num2=name.find_first_of("_");
980 165002 : if( num2!=std::string::npos ) {
981 : char uu = '_';
982 1245 : plumed_massert( std::count(name.begin(),name.end(), uu)==1, "underscore is reserved character in component names and there should only be one");
983 1245 : plumed_massert( num2==0, "underscore is reserved character in component names that has special meaning");
984 : }
985 165002 : if( key=="default" ) {
986 : cstring = "By default this Action calculates the following quantities. These quantities can "
987 : "be referenced elsewhere in the input by using this Action's label followed by a "
988 106945 : "dot and the name of the quantity required from the list below.";
989 : }
990 330004 : components[name] = component()
991 165002 : .setKey(key)
992 165002 : .setDocstring(descr)
993 330004 : .setType(type);
994 165002 : cnames.emplace_back(name);
995 165002 : }
996 :
997 63101 : void Keywords::setValueDescription( const std::string& type, const std::string& descr ) {
998 63101 : setValueDescription(stoct (type),descr);
999 63101 : }
1000 :
1001 68397 : void Keywords::setValueDescription( componentType type, const std::string& descr ) {
1002 136794 : if( !outputComponentExists(".#!value") ) {
1003 126386 : components[".#!value"] =component()
1004 126386 : .setKey("default")
1005 63193 : .setDocstring(descr)
1006 126386 : .setType(type);
1007 63193 : cnames.emplace_back(".#!value");
1008 : } else {
1009 5204 : components[".#!value"].docstring = descr;
1010 5204 : components[".#!value"].type = type;
1011 : }
1012 68397 : }
1013 :
1014 371239 : bool Keywords::outputComponentExists( const std::string& name ) const {
1015 371239 : if( cstring.find("customize")!=std::string::npos ) {
1016 : return true;
1017 : }
1018 :
1019 : std::string sname;
1020 365215 : std::size_t num=name.find_first_of("-");
1021 365215 : std::size_t num2=name.find_last_of("_");
1022 :
1023 365215 : if( num2!=std::string::npos ) {
1024 7262 : sname=name.substr(num2);
1025 361584 : } else if( num!=std::string::npos ) {
1026 61228 : sname=name.substr(0,num);
1027 : } else {
1028 : sname=name;
1029 : }
1030 :
1031 365215 : return components.find(sname)!=components.end();
1032 : }
1033 :
1034 124409 : bool Keywords::componentHasCorrectType( const std::string& name, const std::size_t& rank, const bool& hasderiv ) const {
1035 124409 : if( cstring.find("customize")!=std::string::npos ) {
1036 : return true;
1037 : }
1038 :
1039 : auto comp=components.find(name);
1040 119244 : if( comp==components.end() ) {
1041 : std::string sname;
1042 33000 : std::size_t num=name.find_first_of("-");
1043 33000 : std::size_t num2=name.find_last_of("_");
1044 33000 : if( num2!=std::string::npos ) {
1045 4772 : sname=name.substr(num2);
1046 30614 : } else if( num!=std::string::npos ) {
1047 61228 : sname=name.substr(0,num);
1048 : } else {
1049 : sname=name;
1050 : }
1051 : comp=components.find(sname);
1052 : }
1053 : // an output that is not documented does not have the correct type
1054 : // (components.at() threw std::out_of_range here, and users only saw "map::at")
1055 119244 : if( comp==components.end() ) {
1056 : return false;
1057 : }
1058 119244 : const componentType type=comp->second.type;
1059 :
1060 : // using valid(type & (componentType::atom | componentType::atoms) will have a sligthly different flavour
1061 : // == means "is exactly", the valid(&) construct instead measn "can be different, but must contain the asked flag"
1062 119244 : if( thisactname=="CENTER" && (type == componentType::atom || type == componentType::atoms)) {
1063 : return true;
1064 : }
1065 :
1066 : // The types are bits of a mask: they must be tested with &, a test with | was always true.
1067 119244 : if( rank==0 ) {
1068 98833 : return valid(type & componentType::scalar);
1069 20411 : } else if( hasderiv ) {
1070 448 : return valid(type & componentType::grid);
1071 19963 : } else if( rank==1 ) {
1072 15175 : return valid(type & componentType::vector);
1073 4788 : } else if( rank==2 ) {
1074 4788 : return valid(type & componentType::matrix);
1075 : }
1076 : return false;
1077 : }
1078 :
1079 95555 : std::vector<std::string> Keywords::getArgumentKeys() const {
1080 95555 : std::vector<std::string> arguments;
1081 95555 : std::copy_if(keys.begin(), keys.end(),std::back_inserter(arguments),
1082 1322628 : [this](auto const& kw) {
1083 1322628 : return keywords.at(kw).isArgument();
1084 : });
1085 95555 : return arguments;
1086 0 : }
1087 :
1088 61612 : bool Keywords::checkArgumentType( const std::size_t& rank, const bool& hasderiv ) const {
1089 : // The argument is accepted if at least one of the keywords that read arguments accepts its type
1090 : // (the argument list does not record which keyword it was read from).
1091 : // The types are bits of a mask: they must be tested with &, a test with | was always true.
1092 61612 : const auto argument_keys=getArgumentKeys();
1093 61612 : if( argument_keys.empty() ) {
1094 : return true;
1095 : }
1096 61708 : for(auto const& kw : argument_keys ) {
1097 61707 : const auto & at = std::get<argType>(keywords.at(kw).argument_type);
1098 61707 : if( rank==0 && valid(at & argType::scalar)) {
1099 : return true;
1100 : }
1101 17124 : if( hasderiv && valid(at & argType::grid)) {
1102 : return true;
1103 : }
1104 16445 : if( rank==1 && valid(at & argType::vector)) {
1105 : return true;
1106 : }
1107 5200 : if( rank==2 && valid(at & argType::matrix)) {
1108 : return true;
1109 : }
1110 : }
1111 : return false;
1112 61612 : }
1113 :
1114 33942 : std::string Keywords::getArgumentType( const std::string& name ) const {
1115 33942 : auto argument_keys = getArgumentKeys();
1116 33942 : if( find(argument_keys.begin(),argument_keys.end(),name)==argument_keys.end() ) {
1117 16635 : return "";
1118 : }
1119 34614 : return toString(std::get<argType>(keywords.at(name).argument_type));
1120 33942 : }
1121 :
1122 62548 : std::string Keywords::getOutputComponentFlag( const std::string& name ) const {
1123 62548 : return components.at(name).key;
1124 : }
1125 :
1126 4011 : std::string Keywords::getOutputComponentType( const std::string& name ) const {
1127 : //return toString( components.find(name)->second.type); brings to segfault in case name is ot present
1128 : //at at least throws
1129 4011 : return toString( components.at(name).type);
1130 : }
1131 :
1132 8520 : std::string Keywords::getOutputComponentDescription( const std::string& name ) const {
1133 8520 : std::string checkname = name;
1134 8520 : std::size_t hyp=name.find_first_of("-");
1135 8520 : if( hyp!=std::string::npos ) {
1136 4 : checkname = name.substr(0,hyp);
1137 : }
1138 :
1139 : bool found=components.find(checkname)!=components.end();
1140 :
1141 8520 : if( !found ) {
1142 0 : if( name==".#!value" ) {
1143 0 : return "the value calculated by this action";
1144 : }
1145 0 : if( outputComponentExists( name ) ) {
1146 0 : plumed_merror("cannot find description for component " + name + " that allegedly exists. Gareth Tribello might know what the fuck that is about.");
1147 : }
1148 0 : plumed_merror("could not find output component named " + name );
1149 : }
1150 8520 : return components.at(checkname).docstring;
1151 : }
1152 :
1153 : ///////////DUPLICATED??????????///////
1154 225 : void Keywords::removeOutputComponent( const std::string& name ) {
1155 : components.erase(name);
1156 225 : erase_remove(cnames,name);
1157 225 : }
1158 :
1159 17556 : std::string Keywords::getKeywordDescription( const std::string& key ) const {
1160 17556 : plumed_assert( exists( key ) );
1161 17556 : return keywords.at(key).docstring;
1162 : }
1163 :
1164 81577 : void Keywords::needsAction( const std::string& name ) {
1165 81577 : if( std::find(neededActions.begin(), neededActions.end(), name )!=neededActions.end() ) {
1166 : return;
1167 : }
1168 80833 : neededActions.push_back( name );
1169 : }
1170 :
1171 23286 : bool Keywords::isActionNeeded( std::string_view name ) const {
1172 23286 : return std::find(neededActions.begin(), neededActions.end(), name )!=neededActions.end();
1173 : }
1174 :
1175 1806 : const std::vector<std::string>& Keywords::getNeededKeywords() const {
1176 1806 : return neededActions;
1177 : }
1178 :
1179 73061 : void Keywords::addActionNameSuffix( const std::string& suffix ) {
1180 73061 : if( std::find(actionNameSuffixes.begin(), actionNameSuffixes.end(), suffix )!=actionNameSuffixes.end() ) {
1181 : return;
1182 : }
1183 73061 : actionNameSuffixes.push_back( suffix );
1184 : }
1185 :
1186 16677 : bool Keywords::isActionSuffixed( std::string_view name, std::string_view basename) const {
1187 16677 : std::string bname{basename};
1188 16677 : return std::any_of(actionNameSuffixes.begin(),
1189 : actionNameSuffixes.end(),
1190 23530 : [name,&bname](const std::string& suffix)->bool{
1191 23530 : return (bname + suffix)==name ;
1192 : }
1193 16677 : );
1194 : }
1195 :
1196 130377 : void Keywords::setDisplayName( const std::string& name ) {
1197 130377 : thisactname = name;
1198 130377 : }
1199 :
1200 126130 : std::string Keywords::getDisplayName() const {
1201 126130 : return thisactname;
1202 : }
1203 :
1204 1684 : void Keywords::setDeprecated( const std::string& name ) {
1205 1684 : replaceaction = name;
1206 1684 : }
1207 :
1208 1413 : std::string Keywords::getReplacementAction() const {
1209 1413 : return replaceaction;
1210 : }
1211 :
1212 26662 : void Keywords::addDOI( const std::string& doi ) {
1213 26662 : doilist.push_back( doi );
1214 26662 : }
1215 :
1216 2217 : const std::vector<std::string>& Keywords::getDOIList() const {
1217 2217 : return doilist;
1218 : }
1219 :
1220 4902 : void Keywords::linkActionInDocs( const std::string& k, const std::string& action ) {
1221 4902 : plumed_massert( exists(k), "no " + k + " keyword" );
1222 4902 : keywords.at(k).setLinkedAction(action);
1223 4902 : }
1224 :
1225 30095 : void Keywords::addLinkInDocForFlag( const std::string& k, const std::string& page ) {
1226 30095 : plumed_massert( exists(k), "no " + k + " keyword" );
1227 60190 : plumed_massert( style(k,"flag"), k + " is not a flag" );
1228 30095 : keywords.at(k).setLinkedPage(page);
1229 30095 : }
1230 :
1231 17556 : std::string Keywords::getLinkedActions( const std::string& key ) const {
1232 17556 : plumed_assert( exists( key ) );
1233 17556 : return keywords.at(key).linkaction;
1234 : }
1235 :
1236 17556 : std::string Keywords::getLinkedPages( const std::string& key ) const {
1237 17556 : plumed_assert( exists( key ) );
1238 17556 : return keywords.at(key).linkpage;
1239 : }
1240 :
1241 : }// namespace PLMD
|