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 18469 : void ActionShortcut::registerKeywords( Keywords& keys ) {
31 18469 : Action::registerKeywords( keys );
32 36938 : keys.add("hidden","IS_SHORTCUT","hidden keyword to tell if actions are shortcuts so that example generator can provide expansions of shortcuts");
33 36938 : keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
34 18469 : }
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 : std::string istr;
46 674 : Tools::convert( i, istr );
47 674 : if( !parseNumbered(keyname,i,t) ) {
48 : break ;
49 : }
50 30 : keymap.insert(std::pair<std::string,std::string>(keyname + istr,t));
51 15 : }
52 : }
53 1856 : } else if( keys.style( keyname, "flag") ) {
54 928 : bool found=false;
55 928 : parseFlag(keyname,found);
56 928 : if( found ) {
57 208 : keymap.insert(std::pair<std::string,std::string>(keyname,""));
58 : }
59 : } else {
60 0 : plumed_merror("shortcut keywords should be optional, compulsory or flags");
61 : }
62 : }
63 233 : }
64 :
65 15851 : ActionShortcut::ActionShortcut(const ActionOptions&ao):
66 : Action(ao),
67 15851 : shortcutlabel(label) {
68 : std::string s;
69 15851 : Tools::convert(plumed.getActionSet().size(),s);
70 15851 : if( shortcutlabel==("@" + s) ) {
71 : std::string t;
72 0 : Tools::convert(plumed.getActionSet().size(),t);
73 0 : shortcutlabel="@" + t;
74 : } else {
75 31702 : label = ("@s" + s);
76 : }
77 15851 : }
78 :
79 21165 : void ActionShortcut::readInputLine( const std::string& input, bool saveline ) {
80 21165 : std::vector<std::string> words=Tools::getWords(input);
81 21165 : Tools::interpretLabel(words);
82 : // Check if this action name has been registered
83 21165 : bool founds=false, found = std::find(keywords.neededActions.begin(), keywords.neededActions.end(), words[0] )!=keywords.neededActions.end();
84 : // Check if we are just calling something like SUM_VECTOR using just SUM.
85 35755 : if( !found && words[0].find(getName())!=std::string::npos ) {
86 19385 : for(unsigned j=0 ; j<keywords.actionNameSuffixes.size(); ++j) {
87 19385 : if( (getName() + keywords.actionNameSuffixes[j])==words[0] ) {
88 : found=true;
89 : break;
90 : }
91 : }
92 : founds=true;
93 : }
94 21165 : if( found ) {
95 21165 : if( !founds && saveline ) {
96 6575 : addToSavedInputLines( input );
97 : }
98 42330 : if( keywords.exists("RESTART") ) {
99 0 : if( restart ) {
100 0 : words.push_back("RESTART=YES");
101 : }
102 0 : if( !restart ) {
103 0 : words.push_back("RESTART=NO");
104 : }
105 : }
106 21165 : plumed.readInputWords( words, false);
107 21150 : if( !founds ) {
108 : ActionWithValue* av=NULL;
109 11209 : for(auto pp=plumed.getActionSet().rbegin(); pp!=plumed.getActionSet().rend(); ++pp) {
110 11209 : av = pp->get()->castToActionWithValue();
111 11209 : if( !av ) {
112 : continue ;
113 : }
114 6575 : if( std::find(savedOutputs.begin(), savedOutputs.end(), av->getLabel() )!=savedOutputs.end() ) {
115 : av=NULL;
116 : }
117 : break;
118 : }
119 6569 : if( av ) {
120 6569 : std::string av_label = av->getLabel();
121 6569 : if( av_label == getShortcutLabel() ) {
122 1213 : savedOutputs.push_back( av_label );
123 : } else {
124 31982 : for(unsigned i=0; i<keywords.cnames.size(); ++i) {
125 26626 : if( av_label == getShortcutLabel() + "_" + keywords.cnames[i] ) {
126 303 : savedOutputs.push_back( av_label );
127 52646 : } else if( keywords.getOutputComponentFlag(keywords.cnames[i])!="default" ) {
128 22592 : std::string thisflag = keywords.getOutputComponentFlag(keywords.cnames[i]);
129 46078 : if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + keywords.cnames[i])!=std::string::npos ) {
130 30 : savedOutputs.push_back( av_label );
131 : }
132 : }
133 : }
134 : }
135 : }
136 : } else {
137 14575 : ActionWithValue* av = plumed.getActionSet()[plumed.getActionSet().size()-1]->castToActionWithValue();
138 14575 : if( !av ) {
139 0 : error("shortcut is using suffix but action created is not ActionWithValue");
140 : }
141 14575 : Keywords thiskeys;
142 14575 : actionRegister().getKeywords( av->getName(), thiskeys );
143 29150 : if( thiskeys.getDisplayName()!=getName() ) {
144 0 : error("mismatch between display name of hidden action " + thiskeys.getDisplayName() + " and shortcut that creates it " + getName() );
145 : }
146 14575 : }
147 : } else {
148 0 : error("requirement for action " + words[0] + " should be registered in registerKeywords function for shortcut action using keys.useAction");
149 : }
150 21165 : }
151 :
152 0 : void ActionShortcut::addCommentToShortcutOutput( const std::string& input ) {
153 0 : savedInputLines.push_back( input );
154 0 : }
155 :
156 37 : std::string ActionShortcut::getUpdateLimits() const {
157 37 : std::string f_input="";
158 37 : if( update_from!=std::numeric_limits<double>::max() ) {
159 : std::string ufrom;
160 2 : Tools::convert( update_from, ufrom );
161 4 : f_input += " UPDATE_FROM=" + ufrom;
162 : }
163 37 : if( update_until!=std::numeric_limits<double>::max() ) {
164 : std::string util;
165 2 : Tools::convert( update_until, util );
166 4 : f_input += " UPDATE_UNTIL=" + util;
167 : }
168 37 : return f_input;
169 : }
170 :
171 6575 : void ActionShortcut::addToSavedInputLines( const std::string& line ) {
172 6575 : std::vector<std::string> words = Tools::getWords(line);
173 : std::string actname;
174 6575 : if( words[0].find_first_of(":")!=std::string::npos) {
175 : actname = words[1];
176 : } else {
177 : actname = words[0];
178 : }
179 6575 : if( !actionRegister().check(actname) ) {
180 0 : error("found no action with name " + actname + " to create shortcut");
181 : }
182 6575 : Keywords thiskeys;
183 6575 : actionRegister().getKeywords( actname, thiskeys );
184 : std::vector<std::string> numberedkeys;
185 55723 : for(unsigned i=0; i<thiskeys.size(); ++i ) {
186 98296 : if( thiskeys.numbered( thiskeys.getKeyword(i) ) ) {
187 15570 : numberedkeys.push_back( thiskeys.getKeyword(i) );
188 : }
189 : }
190 12074 : if( numberedkeys.size()>0 && actname!="CONCATENATE" ) {
191 : std::string reducedline;
192 217948 : for(unsigned i=0; i<words.size(); ++i) {
193 : bool notnumbered=true;
194 564966 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
195 938777 : if( words[i].find(numberedkeys[j])!=std::string::npos && words[i].substr(0,numberedkeys[j].length()+1)!=numberedkeys[j]+"=" ) {
196 : notnumbered=false;
197 : break;
198 : }
199 : }
200 212623 : if( notnumbered || words[i]==actname ) {
201 22786 : if( words[i].find(" ")!=std::string::npos) {
202 250 : std::size_t eq=words[i].find_first_of("=");
203 500 : reducedline += words[i].substr(0,eq) + "={" + words[i].substr(eq+1) + "} ";
204 : } else {
205 45072 : reducedline += words[i] + " ";
206 : }
207 : }
208 : }
209 5325 : std::vector<unsigned> ninstances( numberedkeys.size(), 0 );
210 12762 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
211 7437 : for(unsigned i=1;; ++i) {
212 : std::string num, val;
213 196659 : Tools::convert(i, num);
214 196659 : bool found = Tools::parse(words, numberedkeys[j] + num, val );
215 196659 : if( !found) {
216 : break ;
217 : }
218 189222 : if( i<6 ) {
219 1236 : reducedline += numberedkeys[j] + num + "=" + val + " ";
220 : } else {
221 188604 : ninstances[j]++;
222 : }
223 189222 : }
224 : }
225 : bool outputcomment=false;
226 12628 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
227 7383 : if( ninstances[j]>0 ) {
228 : outputcomment=true;
229 : break;
230 : }
231 : }
232 5325 : if( outputcomment ) {
233 : reducedline += " # Action input conctinues with ";
234 288 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
235 : std::string num;
236 208 : Tools::convert( ninstances[j], num );
237 208 : if( ninstances[j]>0 ) {
238 168 : reducedline += num + " further " + numberedkeys[j] + "n keywords, ";
239 : }
240 : }
241 : }
242 5325 : savedInputLines.push_back( reducedline );
243 : } else {
244 1250 : savedInputLines.push_back( line );
245 : }
246 13150 : }
247 :
248 66009 : const std::string & ActionShortcut::getShortcutLabel() const {
249 66009 : return shortcutlabel;
250 : }
251 :
252 18 : std::vector<std::string> ActionShortcut::getSavedInputLines() const {
253 18 : return savedInputLines;
254 : }
255 :
256 41 : std::vector<std::string> ActionShortcut::getSavedOutputs() const {
257 41 : return savedOutputs;
258 : }
259 :
260 14565 : std::string ActionShortcut::convertInputLineToString() {
261 : std::string output;
262 65651 : for(auto p=line.begin(); p!=line.end(); ++p) {
263 51086 : if( (*p).find(" " )!=std::string::npos ) {
264 411 : std::size_t eq = (*p).find_first_of("=");
265 822 : output += " " + (*p).substr(0,eq) + "={" + (*p).substr(eq+1) + "}";
266 : } else {
267 101350 : output += " " + (*p);
268 : }
269 : }
270 14565 : line.resize(0);
271 14565 : return output;
272 : }
273 :
274 643 : void ActionShortcut::interpretDataLabel( const std::string& mystr, Action* myuser, std::vector<Value*>& arg ) const {
275 : std::size_t dot=mystr.find_first_of('.');
276 643 : std::string a=mystr.substr(0,dot);
277 643 : std::string name=mystr.substr(dot+1);
278 : // Retrieve the keywords for the shortcut
279 643 : Keywords skeys;
280 643 : actionRegister().getKeywords( getName(), skeys );
281 643 : std::vector<std::string> out_comps( skeys.getOutputComponents() );
282 : // Now get the output components
283 643 : if( name=="*" ) {
284 2693 : for(unsigned k=0; k<out_comps.size(); ++k) {
285 2282 : if( out_comps[k]=="" ) {
286 0 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
287 0 : if( action ) {
288 0 : if( action->getNumberOfComponents()!=1 ) {
289 0 : myuser->error("action named " + a + " has more than one component");
290 : }
291 0 : arg.push_back(action->copyOutput(0));
292 : }
293 : } else {
294 4564 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] );
295 2282 : if( action ) {
296 258 : if( action->getNumberOfComponents()!=1 ) {
297 0 : myuser->error("action named " + a + "_" + out_comps[k] + " has more than one component");
298 : }
299 258 : arg.push_back(action->copyOutput(0));
300 : } else {
301 2024 : for(unsigned j=1;; ++j) {
302 : std::string numstr;
303 4091 : Tools::convert( j, numstr );
304 8182 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] + "-" + numstr );
305 4091 : if( act ) {
306 140 : for(unsigned n=0; n<act->getNumberOfComponents(); ++n ) {
307 70 : arg.push_back(act->copyOutput(n));
308 : }
309 4021 : } else if( j>1 ) {
310 : break; // This ensures that * syntax works with moments, which normally start from 2
311 : }
312 2067 : }
313 : }
314 : }
315 : }
316 : } else {
317 : // Check for an action that has action.component
318 232 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
319 232 : if( act && act->exists(mystr) ) {
320 : return;
321 : }
322 : // Get components that are actually actions
323 235 : for(unsigned k=0; k<out_comps.size(); ++k) {
324 235 : if(name.find_first_of(out_comps[k])!=std::string::npos ) {
325 400 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + name );
326 200 : if( action ) {
327 88 : arg.push_back(action->copyOutput(a+"_"+name));
328 : }
329 : break;
330 : }
331 : }
332 : }
333 643 : }
334 :
335 : }
|