LCOV - code coverage report
Current view: top level - core - ActionShortcut.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 162 182 89.0 %
Date: 2026-11-05 10:19:04 Functions: 12 14 85.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2018-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 "ActionShortcut.h"
      23             : #include "PlumedMain.h"
      24             : #include "ActionWithValue.h"
      25             : #include "ActionRegister.h"
      26             : #include "ActionSet.h"
      27             : #include <cctype>
      28             : #include <unordered_map>
      29             : 
      30             : namespace PLMD {
      31             : 
      32       18940 : void ActionShortcut::registerKeywords( Keywords& keys ) {
      33       18940 :   Action::registerKeywords( keys );
      34       18940 :   keys.add("hidden","IS_SHORTCUT","hidden keyword to tell if actions are shortcuts so that example generator can provide expansions of shortcuts");
      35       18940 :   keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
      36       18940 : }
      37             : 
      38         271 : void ActionShortcut::readShortcutKeywords( const Keywords& keys, std::map<std::string,std::string>& keymap ) {
      39        3197 :   for (auto& keyname:keys.getKeys()) {
      40             :     std::string t;
      41             :     bool def;
      42        2926 :     if( keys.getLogicalDefault(keyname,def) ) {
      43        1062 :       bool found=false;
      44        1062 :       parseFlag(keyname,found);
      45        1062 :       if( found ) {
      46         234 :         keymap.insert(std::pair<std::string,std::string>(keyname,""));
      47             :       }
      48        5592 :     } else if( keys.style( keyname, "optional") || keys.style( keyname, "compulsory") || keys.style( keyname, "deprecated") ) {
      49        1864 :       parse(keyname,t);
      50        1864 :       if( t.length()>0 ) {
      51         130 :         keymap.insert(std::pair<std::string,std::string>(keyname,t));
      52        1799 :       } else if( keys.numbered( keyname ) ) {
      53         764 :         for(unsigned i=1;; ++i) {
      54             :           std::string istr;
      55         788 :           Tools::convert( i, istr );
      56         788 :           if( !parseNumbered(keyname,i,t) ) {
      57             :             break ;
      58             :           }
      59          48 :           keymap.insert(std::pair<std::string,std::string>(keyname + istr,t));
      60          24 :         }
      61             :       }
      62             :     } else {
      63           0 :       plumed_merror("shortcut keywords should be optional, compulsory or flags");
      64             :     }
      65             :   }
      66         271 : }
      67             : 
      68       18026 : ActionShortcut::ActionShortcut(const ActionOptions&ao):
      69             :   Action(ao),
      70       18026 :   shortcutlabel(actionLabel) {
      71             :   std::string s;
      72       18026 :   Tools::convert(plumed.getActionSet().size(),s);
      73       18026 :   if( shortcutlabel==("@" + s) ) {
      74             :     std::string t;
      75           0 :     Tools::convert(plumed.getActionSet().size(),t);
      76           0 :     shortcutlabel="@" + t;
      77             :   } else {
      78       36052 :     actionLabel = ("@s" + s);
      79             :   }
      80       18026 : }
      81             : 
      82       23286 : void ActionShortcut::readInputLine( const std::string& input, bool saveline ) {
      83       23286 :   std::vector<std::string> words=Tools::getWords(input);
      84       23286 :   Tools::interpretLabel(words);
      85             :   // Check if this action name has been registered
      86             :   bool founds=false;
      87       23286 :   bool found = keywords.isActionNeeded(words[0]);
      88             :   // Check if we are just calling something like SUM_VECTOR using just SUM.
      89       23286 :   if( !found && words[0].find(getName())!=std::string::npos ) {
      90       16677 :     found =keywords.isActionSuffixed(words[0],getName());
      91             :     founds=true;
      92             :   }
      93       23286 :   if( found ) {
      94       23286 :     if( !founds && saveline ) {
      95        6609 :       addToSavedInputLines( input );
      96             :     }
      97       23286 :     if( keywords.exists("RESTART") ) {
      98           0 :       if( restart ) {
      99           0 :         words.push_back("RESTART=YES");
     100             :       }
     101           0 :       if( !restart ) {
     102           0 :         words.push_back("RESTART=NO");
     103             :       }
     104             :     }
     105       23286 :     plumed.readInputWords( words, false);
     106       23256 :     if( !founds ) {
     107             :       ActionWithValue* av=NULL;
     108       11081 :       for(auto pp=plumed.getActionSet().rbegin(); pp!=plumed.getActionSet().rend(); ++pp) {
     109       11081 :         av = pp->get()->castToActionWithValue();
     110       11081 :         if( !av ) {
     111             :           continue ;
     112             :         }
     113        6603 :         if( std::find(savedOutputs.begin(), savedOutputs.end(), av->getLabel() )!=savedOutputs.end() ) {
     114             :           av=NULL;
     115             :         }
     116             :         break;
     117             :       }
     118           0 :       if( av ) {
     119        6597 :         std::string av_label = av->getLabel();
     120        6597 :         if( av_label == getShortcutLabel() && av->getNumberOfComponents()==1 ) {
     121        1169 :           savedOutputs.push_back( av_label );
     122        2338 :           plumed_massert( keywords.componentHasCorrectType(".#!value",
     123             :                           (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ),
     124             :                           "documentation for type of value of " + getName() + " is missing or incorrect");
     125             :         } else {
     126       36798 :           for(const auto& cname: keywords.getOutputComponents()) {
     127       62740 :             if( av_label == getShortcutLabel() + "_" + cname ) {
     128         380 :               savedOutputs.push_back( av_label );
     129         380 :               plumed_massert( keywords.componentHasCorrectType(cname,
     130             :                               (av->copyOutput(0))->getRank(),
     131             :                               (av->copyOutput(0))->hasDerivatives() ),
     132             :                               "documentation for type of component " + cname + " of " + getName() + " is missing or incorrect");
     133       30990 :             } else if( keywords.getOutputComponentFlag(cname)!="default" ) {
     134       25096 :               std::string thisflag = keywords.getOutputComponentFlag(cname);
     135       52593 :               if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + cname)!=std::string::npos ) {
     136          39 :                 savedOutputs.push_back( av_label );
     137          39 :                 plumed_massert( keywords.componentHasCorrectType(cname,
     138             :                                 (av->copyOutput(0))->getRank(),
     139             :                                 (av->copyOutput(0))->hasDerivatives() ),
     140             :                                 "documentation for type of component " + cname + " of " + getName() + " is missing or incorrect");
     141             :               }
     142             :             }
     143             :           }
     144             :         }
     145             :       }
     146             :     } else {
     147       16653 :       ActionWithValue* av = plumed.getActionSet()[plumed.getActionSet().size()-1]->castToActionWithValue();
     148       16653 :       if( !av ) {
     149           0 :         error("shortcut is using suffix but action created is not ActionWithValue");
     150             :       }
     151       16653 :       Keywords thiskeys;
     152       16653 :       actionRegister().getKeywords( av->getName(), thiskeys );
     153       16653 :       if( thiskeys.getDisplayName()!=getName() ) {
     154             :         //NOTE: this was an error
     155             :         // I deactivated it because it clashes with the gpu-openacc-by-LOAD does not load
     156             :         // at moment of writing this I do not know if there is a way to the check by passing the
     157             :         // dlloader object to this function.
     158             :         // bypassing this check makes my contraption work
     159          72 :         warning("mismatch between display name of hidden action " + thiskeys.getDisplayName() + " and shortcut that creates it " + getName() );
     160             :       }
     161       16653 :     }
     162             :   } else {
     163           0 :     error("requirement for action " + words[0] + " should be registered in registerKeywords function for shortcut action using keys.useAction");
     164             :   }
     165       23286 : }
     166             : 
     167           0 : void ActionShortcut::addCommentToShortcutOutput( const std::string& input ) {
     168           0 :   savedInputLines.push_back( input );
     169           0 : }
     170             : 
     171          55 : std::string ActionShortcut::getUpdateLimits() const {
     172          55 :   std::string f_input="";
     173          55 :   if( update_from!=std::numeric_limits<double>::max() ) {
     174             :     std::string ufrom;
     175           2 :     Tools::convert( update_from, ufrom );
     176           2 :     f_input += " UPDATE_FROM=" + ufrom;
     177             :   }
     178          55 :   if( update_until!=std::numeric_limits<double>::max() ) {
     179             :     std::string util;
     180           2 :     Tools::convert( update_until, util );
     181           2 :     f_input += " UPDATE_UNTIL=" + util;
     182             :   }
     183          55 :   return f_input;
     184           4 : }
     185             : 
     186        6609 : void ActionShortcut::addToSavedInputLines( const std::string& line ) {
     187        6609 :   std::vector<std::string> words = Tools::getWords(line);
     188             :   std::string actname;
     189        6609 :   if( words[0].find_first_of(":")!=std::string::npos) {
     190             :     actname = words[1];
     191             :   } else {
     192             :     actname = words[0];
     193             :   }
     194        6609 :   if( !actionRegister().check(actname) ) {
     195           0 :     error("found no action with name " + actname + " to create shortcut");
     196             :   }
     197        6609 :   Keywords thiskeys;
     198        6609 :   actionRegister().getKeywords( actname, thiskeys );
     199        6609 :   std::vector<std::string> numberedkeys;
     200       64053 :   for (auto& keyname: thiskeys.getKeys()) {
     201       57444 :     if( thiskeys.numbered( keyname ) ) {
     202        2398 :       numberedkeys.push_back( keyname );
     203             :     }
     204             :   }
     205        6609 :   if( numberedkeys.size()>0 && actname!="CONCATENATE" ) {
     206             :     std::string reducedline;
     207      433271 :     for(unsigned i=0; i<words.size(); ++i) {
     208             :       bool notnumbered=true;
     209      640728 :       for(unsigned j=0; j<numberedkeys.size(); ++j) {
     210     1496470 :         if( words[i].find(numberedkeys[j])!=std::string::npos && words[i].substr(0,numberedkeys[j].length()+1)!=numberedkeys[j]+"=" ) {
     211             :           notnumbered=false;
     212             :           break;
     213             :         }
     214             :       }
     215      432083 :       if( notnumbered || words[i]==actname ) {
     216        3981 :         if( words[i].find(" ")!=std::string::npos) {
     217         142 :           std::size_t eq=words[i].find_first_of("=");
     218         426 :           reducedline += words[i].substr(0,eq) + "={" + words[i].substr(eq+1) + "} ";
     219             :         } else {
     220        7678 :           reducedline += words[i] + " ";
     221             :         }
     222             :       }
     223             :     }
     224             :     // Index the KEY=value words once.  Looking up each numbered keyword with Tools::parse
     225             :     // (a linear search followed by an erase in the middle of the vector) made this function
     226             :     // quadratic in the number of numbered keywords, which dominated the set up of shortcuts
     227             :     // such as DISTANCES with GROUPA/GROUPB (hundreds of thousands of ATOMSn keywords).
     228             :     // As in Tools::parse, the first occurrence wins, the comparison is case insensitive and
     229             :     // a keyword with an empty value is treated as not present.
     230      431812 :     auto upper = []( std::string s ) {
     231     4608261 :       for(auto& c : s) {
     232     4176449 :         c = static_cast<char>( std::toupper( static_cast<unsigned char>(c) ) );
     233             :       }
     234      431812 :       return s;
     235             :     };
     236             :     std::unordered_map<std::string,std::string> wordvalues;
     237             :     wordvalues.reserve( words.size() );
     238      433271 :     for(const auto& w : words ) {
     239      432083 :       std::size_t eq=w.find_first_of("=");
     240      432083 :       if( eq!=std::string::npos ) {
     241      858962 :         wordvalues.emplace( upper( w.substr(0,eq) ), w.substr(eq+1) );
     242             :       }
     243             :     }
     244        1188 :     std::vector<unsigned> ninstances( numberedkeys.size(), 0 );
     245        3519 :     for(unsigned j=0; j<numberedkeys.size(); ++j) {
     246        2331 :       const std::string ukey = upper( numberedkeys[j] );
     247        2331 :       for(unsigned i=1;; ++i) {
     248             :         std::string num;
     249      430193 :         Tools::convert(i, num);
     250      430193 :         auto it = wordvalues.find( ukey + num );
     251      858055 :         if( it==wordvalues.end() || it->second.empty() ) {
     252             :           break ;
     253             :         }
     254      427862 :         if( i<6 ) {
     255        3687 :           reducedline += numberedkeys[j] + num + "=" + it->second + " ";
     256             :         } else {
     257      426633 :           ninstances[j]++;
     258             :         }
     259      427862 :       }
     260             :     }
     261             :     bool outputcomment=false;
     262        3281 :     for(unsigned j=0; j<numberedkeys.size(); ++j) {
     263        2263 :       if( ninstances[j]>0 ) {
     264             :         outputcomment=true;
     265             :         break;
     266             :       }
     267             :     }
     268        1188 :     if( outputcomment ) {
     269             :       reducedline += "    # Action input conctinues with ";
     270         467 :       for(unsigned  j=0; j<numberedkeys.size(); ++j) {
     271             :         std::string num;
     272         297 :         Tools::convert( ninstances[j], num );
     273         297 :         if( ninstances[j]>0 ) {
     274         352 :           reducedline += num + " further " + numberedkeys[j] + "n keywords, ";
     275             :         }
     276             :       }
     277         170 :       savedInputLines.push_back( reducedline );
     278             :     } else {
     279        1018 :       savedInputLines.push_back( line );
     280             :     }
     281             :   } else {
     282        5421 :     savedInputLines.push_back( line );
     283             :   }
     284       13218 : }
     285             : 
     286       72741 : const std::string & ActionShortcut::getShortcutLabel() const {
     287       72741 :   return shortcutlabel;
     288             : }
     289             : 
     290          22 : const std::vector<std::string>& ActionShortcut::getSavedInputLines() const {
     291          22 :   return savedInputLines;
     292             : }
     293             : 
     294          41 : const std::vector<std::string>& ActionShortcut::getSavedOutputs() const {
     295          41 :   return savedOutputs;
     296             : }
     297             : 
     298       16686 : std::string ActionShortcut::convertInputLineToString() {
     299       16686 :   return linemap.convertToString(true);
     300             : }
     301             : 
     302         744 : void ActionShortcut::interpretDataLabel( const std::string& mystr, Action* myuser, std::vector<Value*>& arg ) const {
     303             :   std::size_t dot=mystr.find_first_of('.');
     304         744 :   std::string a=mystr.substr(0,dot);
     305         744 :   std::string dataName=mystr.substr(dot+1);
     306             :   // Retrieve the keywords for the shortcut
     307         744 :   Keywords skeys;
     308         744 :   actionRegister().getKeywords( getName(), skeys );
     309         744 :   std::vector<std::string> out_comps( skeys.getOutputComponents() );
     310             :   // Now get the output components
     311         744 :   if( dataName=="*" ) {
     312        2911 :     for(unsigned k=0; k<out_comps.size(); ++k) {
     313        2435 :       if( out_comps[k]=="" ) {
     314           0 :         ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
     315           0 :         if( action ) {
     316           0 :           if( action->getNumberOfComponents()!=1 ) {
     317           0 :             myuser->error("action named " + a + " has more than one component");
     318             :           }
     319           0 :           arg.push_back(action->copyOutput(0));
     320             :         }
     321             :       } else {
     322        2435 :         ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] );
     323        2435 :         if( action ) {
     324         278 :           if( action->getNumberOfComponents()!=1 ) {
     325           0 :             myuser->error("action named " + a + "_" + out_comps[k] + " has more than one component");
     326             :           }
     327         278 :           arg.push_back(action->copyOutput(0));
     328             :         } else {
     329        2157 :           for(unsigned j=1;; ++j) {
     330             :             std::string numstr;
     331        4342 :             Tools::convert( j, numstr );
     332        8684 :             ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] + "-" + numstr );
     333        4342 :             if( act ) {
     334          90 :               for(unsigned n=0; n<act->getNumberOfComponents(); ++n ) {
     335          45 :                 arg.push_back(act->copyOutput(n));
     336             :               }
     337        4297 :             } else if( j>1 ) {
     338             :               break;  // This ensures that * syntax works with moments, which normally start from 2
     339             :             }
     340        2185 :           }
     341             :         }
     342             :       }
     343             :     }
     344             :   } else {
     345             :     // Check for an action that has action.component
     346         268 :     ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
     347         268 :     if( act && act->exists(mystr) ) {
     348             :       return;
     349             :     }
     350             :     // Get components that are actually actions
     351         319 :     for(unsigned k=0; k<out_comps.size(); ++k) {
     352         319 :       if(dataName.find_first_of(out_comps[k])!=std::string::npos ) {
     353         236 :         ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + dataName );
     354         236 :         if( action ) {
     355         228 :           arg.push_back(action->copyOutput(a+"_"+dataName));
     356             :         }
     357             :         break;
     358             :       }
     359             :     }
     360             :   }
     361         744 : }
     362             : 
     363             : }

Generated by: LCOV version 1.16