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 : 28 : namespace PLMD { 29 : 30 18476 : void ActionShortcut::registerKeywords( Keywords& keys ) { 31 18476 : Action::registerKeywords( keys ); 32 36952 : keys.add("hidden","IS_SHORTCUT","hidden keyword to tell if actions are shortcuts so that example generator can provide expansions of shortcuts"); 33 36952 : keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values"); 34 18476 : } 35 : 36 233 : void ActionShortcut::readShortcutKeywords( const Keywords& keys, std::map<std::string,std::string>& keymap ) { 37 2786 : for(unsigned i=0; i<keys.size(); ++i) { 38 2553 : std::string t, keyname = keys.get(i); 39 3481 : if( keys.style( keyname, "optional") || keys.style( keyname, "compulsory") ) { 40 1625 : parse(keyname,t); 41 1625 : if( t.length()>0 ) { 42 102 : keymap.insert(std::pair<std::string,std::string>(keyname,t)); 43 1574 : } else if( keys.numbered( keyname ) ) { 44 659 : for(unsigned i=1;; ++i) { 45 674 : std::string istr; Tools::convert( i, istr ); 46 674 : if( !parseNumbered(keyname,i,t) ) break ; 47 30 : keymap.insert(std::pair<std::string,std::string>(keyname + istr,t)); 48 15 : } 49 : } 50 1856 : } else if( keys.style( keyname, "flag") ) { 51 928 : bool found=false; parseFlag(keyname,found); 52 1032 : if( found ) keymap.insert(std::pair<std::string,std::string>(keyname,"")); 53 0 : } else plumed_merror("shortcut keywords should be optional, compulsory or flags"); 54 : } 55 233 : } 56 : 57 16112 : ActionShortcut::ActionShortcut(const ActionOptions&ao): 58 : Action(ao), 59 16112 : shortcutlabel(label) 60 : { 61 16112 : std::string s; Tools::convert(plumed.getActionSet().size(),s); 62 16112 : if( shortcutlabel==("@" + s) ) { 63 0 : std::string t; Tools::convert(plumed.getActionSet().size(),t); 64 0 : shortcutlabel="@" + t; 65 32224 : } else label = ("@s" + s); 66 16112 : } 67 : 68 21703 : void ActionShortcut::readInputLine( const std::string& input, bool saveline ) { 69 21703 : std::vector<std::string> words=Tools::getWords(input); Tools::interpretLabel(words); 70 : // Check if this action name has been registered 71 21703 : bool founds=false, found = std::find(keywords.neededActions.begin(), keywords.neededActions.end(), words[0] )!=keywords.neededActions.end(); 72 : // Check if we are just calling something like SUM_VECTOR using just SUM. 73 36560 : if( !found && words[0].find(getName())!=std::string::npos ) { 74 19631 : for(unsigned j=0 ; j<keywords.actionNameSuffixes.size(); ++j) { 75 19631 : if( (getName() + keywords.actionNameSuffixes[j])==words[0] ) { found=true; break; } 76 : } 77 : founds=true; 78 : } 79 21703 : if( found ) { 80 21703 : std::string f_input = input; if( !founds && saveline ) addToSavedInputLines( input ); 81 43406 : if( keywords.exists("RESTART") ) { 82 0 : if( restart ) f_input += " RESTART=YES"; 83 0 : if( !restart ) f_input += " RESTART=NO"; 84 : } 85 21703 : plumed.readInputLine( f_input ); 86 21688 : if( !founds ) { 87 : ActionWithValue* av=NULL; 88 11758 : for(auto pp=plumed.getActionSet().rbegin(); pp!=plumed.getActionSet().rend(); ++pp) { 89 11758 : av = pp->get()->castToActionWithValue(); 90 11758 : if( !av ) continue ; 91 6846 : if( std::find(savedOutputs.begin(), savedOutputs.end(), av->getLabel() )!=savedOutputs.end() ) av=NULL; 92 : break; 93 : } 94 6840 : if( av ) { 95 6840 : std::string av_label = av->getLabel(); 96 6840 : if( av_label == getShortcutLabel() && av->getNumberOfComponents()==1 ) { 97 1163 : savedOutputs.push_back( av_label ); 98 2326 : plumed_massert( keywords.componentHasCorrectType(".#!value", (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), "documentation for type of value is incorrect"); 99 : } else { 100 38840 : for(unsigned i=0; i<keywords.cnames.size(); ++i) { 101 33163 : if( av_label == getShortcutLabel() + "_" + keywords.cnames[i] ) { 102 297 : savedOutputs.push_back( av_label ); 103 297 : plumed_massert( keywords.componentHasCorrectType(keywords.cnames[i], (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), "documentation for type of component " + keywords.cnames[i] + " is incorrect"); 104 65732 : } else if( keywords.getOutputComponentFlag(keywords.cnames[i])!="default" ) { 105 26935 : std::string thisflag = keywords.getOutputComponentFlag(keywords.cnames[i]); 106 54273 : if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + keywords.cnames[i])!=std::string::npos ) { 107 30 : savedOutputs.push_back( av_label ); 108 30 : plumed_massert( keywords.componentHasCorrectType(keywords.cnames[i], (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ), "documentation for type of component " + keywords.cnames[i] + " is incorrect"); 109 : } 110 : } 111 : } 112 : } 113 : } 114 : } else { 115 14842 : ActionWithValue* av = plumed.getActionSet()[plumed.getActionSet().size()-1]->castToActionWithValue(); 116 14857 : if( !av ) error("shortcut is using suffix but action created is not ActionWithValue"); 117 14842 : Keywords thiskeys; actionRegister().getKeywords( av->getName(), thiskeys ); 118 29684 : if( thiskeys.getDisplayName()!=getName() ) error("mismatch between display name of hidden action " + thiskeys.getDisplayName() + " and shortcut that creates it " + getName() ); 119 14842 : } 120 0 : } else error("requirement for action " + words[0] + " should be registered in registerKeywords function for shortcut action using keys.useAction"); 121 21703 : } 122 : 123 1 : void ActionShortcut::addCommentToShortcutOutput( const std::string& input ) { 124 1 : savedInputLines.push_back( input ); 125 1 : } 126 : 127 37 : std::string ActionShortcut::getUpdateLimits() const { 128 37 : std::string f_input=""; 129 37 : if( update_from!=std::numeric_limits<double>::max() ) { 130 4 : std::string ufrom; Tools::convert( update_from, ufrom ); f_input += " UPDATE_FROM=" + ufrom; 131 : } 132 37 : if( update_until!=std::numeric_limits<double>::max() ) { 133 4 : std::string util; Tools::convert( update_until, util ); f_input += " UPDATE_UNTIL=" + util; 134 : } 135 37 : return f_input; 136 : } 137 : 138 6561 : void ActionShortcut::addToSavedInputLines( const std::string& line ) { 139 6561 : std::vector<std::string> words = Tools::getWords(line); std::string actname; 140 6561 : if( words[0].find_first_of(":")!=std::string::npos) actname = words[1]; else actname = words[0]; 141 6561 : if( !actionRegister().check(actname) ) error("found no action with name " + actname + " to create shortcut"); 142 6561 : Keywords thiskeys; actionRegister().getKeywords( actname, thiskeys ); std::vector<std::string> numberedkeys; 143 55070 : for(unsigned i=0; i<thiskeys.size(); ++i ) { 144 99923 : if( thiskeys.numbered( thiskeys.getKeyword(i) ) ) numberedkeys.push_back( thiskeys.getKeyword(i) ); 145 : } 146 7879 : if( numberedkeys.size()>0 && actname!="CONCATENATE" ) { 147 : std::string reducedline; 148 167615 : for(unsigned i=0; i<words.size(); ++i) { 149 : bool notnumbered=true; 150 443346 : for(unsigned j=0; j<numberedkeys.size(); ++j) { 151 766400 : if( words[i].find(numberedkeys[j])!=std::string::npos && words[i].substr(0,numberedkeys[j].length()+1)!=numberedkeys[j]+"=" ) { notnumbered=false; break; } 152 : } 153 166471 : if( notnumbered || words[i]==actname ) { 154 4244 : if( words[i].find(" ")!=std::string::npos) { 155 104 : std::size_t eq=words[i].find_first_of("="); 156 208 : reducedline += words[i].substr(0,eq) + "={" + words[i].substr(eq+1) + "} "; 157 8280 : } else reducedline += words[i] + " "; 158 : } 159 : } 160 1144 : std::vector<unsigned> ninstances( numberedkeys.size(), 0 ); 161 3875 : for(unsigned j=0; j<numberedkeys.size(); ++j) { 162 2731 : for(unsigned i=1;; ++i) { 163 164350 : std::string num, val; Tools::convert(i, num); 164 164350 : bool found = Tools::parse(words, numberedkeys[j] + num, val ); 165 164350 : if( !found) break ; 166 162198 : if( i<6 ) reducedline += numberedkeys[j] + num + "=" + val + " "; 167 161040 : else ninstances[j]++; 168 161619 : } 169 : } 170 : bool outputcomment=false; 171 3753 : for(unsigned j=0; j<numberedkeys.size(); ++j) { 172 2683 : if( ninstances[j]>0 ) { outputcomment=true; break; } 173 : } 174 1144 : if( outputcomment ) { 175 : reducedline += " # Action input conctinues with "; 176 258 : for(unsigned j=0; j<numberedkeys.size(); ++j) { 177 184 : std::string num; Tools::convert( ninstances[j], num ); 178 262 : if( ninstances[j]>0 ) reducedline += num + " further " + numberedkeys[j] + "n keywords, "; 179 : } 180 74 : savedInputLines.push_back( reducedline ); 181 1070 : } else savedInputLines.push_back( line ); 182 5417 : } else savedInputLines.push_back( line ); 183 13122 : } 184 : 185 74602 : const std::string & ActionShortcut::getShortcutLabel() const { 186 74602 : return shortcutlabel; 187 : } 188 : 189 18 : std::vector<std::string> ActionShortcut::getSavedInputLines() const { 190 18 : return savedInputLines; 191 : } 192 : 193 41 : std::vector<std::string> ActionShortcut::getSavedOutputs() const { 194 41 : return savedOutputs; 195 : } 196 : 197 14832 : std::string ActionShortcut::convertInputLineToString() { 198 : std::string output; 199 66184 : for(auto p=line.begin(); p!=line.end(); ++p) { 200 51352 : if( (*p).find(" " )!=std::string::npos ) { 201 405 : std::size_t eq = (*p).find_first_of("="); 202 810 : output += " " + (*p).substr(0,eq) + "={" + (*p).substr(eq+1) + "}"; 203 101894 : } else output += " " + (*p); 204 : } 205 14832 : line.resize(0); return output; 206 : } 207 : 208 613 : void ActionShortcut::interpretDataLabel( const std::string& mystr, Action* myuser, std::vector<Value*>& arg ) const { 209 613 : std::size_t dot=mystr.find_first_of('.'); std::string a=mystr.substr(0,dot); std::string name=mystr.substr(dot+1); 210 : // Retrieve the keywords for the shortcut 211 613 : Keywords skeys; actionRegister().getKeywords( getName(), skeys ); 212 613 : std::vector<std::string> out_comps( skeys.getOutputComponents() ); 213 : // Now get the output components 214 613 : if( name=="*" ) { 215 2812 : for(unsigned k=0; k<out_comps.size(); ++k) { 216 2427 : if( out_comps[k]=="" ) { 217 0 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a ); 218 0 : if( action ) { 219 0 : if( action->getNumberOfComponents()!=1 ) myuser->error("action named " + a + " has more than one component"); 220 0 : arg.push_back(action->copyOutput(0)); 221 : } 222 : } else { 223 4854 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] ); 224 2427 : if( action ) { 225 234 : if( action->getNumberOfComponents()!=1 ) myuser->error("action named " + a + "_" + out_comps[k] + " has more than one component"); 226 234 : arg.push_back(action->copyOutput(0)); 227 : } else { 228 2193 : for(unsigned j=1;; ++j) { 229 4429 : std::string numstr; Tools::convert( j, numstr ); 230 8858 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] + "-" + numstr ); 231 4429 : if( act ) { 232 140 : for(unsigned n=0; n<act->getNumberOfComponents(); ++n ) arg.push_back(act->copyOutput(n)); 233 4359 : } else if( j>1 ) break; // This ensures that * syntax works with moments, which normally start from 2 234 2236 : } 235 : } 236 : } 237 : } 238 : } else { 239 : // Check for an action that has action.component 240 228 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a ); 241 228 : if( act && act->exists(mystr) ) return; 242 : // Get components that are actually actions 243 231 : for(unsigned k=0; k<out_comps.size(); ++k) { 244 231 : if(name.find_first_of(out_comps[k])!=std::string::npos ) { 245 392 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + name ); 246 196 : if( action ) arg.push_back(action->copyOutput(a+"_"+name)); 247 : break; 248 : } 249 : } 250 : } 251 613 : } 252 : 253 : }