LCOV - code coverage report
Current view: top level - tools - Keywords.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 578 645 89.6 %
Date: 2026-11-05 10:19:04 Functions: 90 94 95.7 %

          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

Generated by: LCOV version 1.16