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 18194 : void ActionShortcut::registerKeywords( Keywords& keys ) {
31 18194 : Action::registerKeywords( keys );
32 18194 : keys.add("hidden","IS_SHORTCUT","hidden keyword to tell if actions are shortcuts so that example generator can provide expansions of shortcuts");
33 18194 : keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
34 18194 : }
35 :
36 233 : void ActionShortcut::readShortcutKeywords( const Keywords& keys, std::map<std::string,std::string>& keymap ) {
37 2786 : for (auto& keyname:keys.getKeys()) {
38 : std::string t;
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 15836 : ActionShortcut::ActionShortcut(const ActionOptions&ao):
66 : Action(ao),
67 15836 : shortcutlabel(label) {
68 : std::string s;
69 15836 : Tools::convert(plumed.getActionSet().size(),s);
70 15836 : if( shortcutlabel==("@" + s) ) {
71 : std::string t;
72 0 : Tools::convert(plumed.getActionSet().size(),t);
73 0 : shortcutlabel="@" + t;
74 : } else {
75 31672 : label = ("@s" + s);
76 : }
77 15836 : }
78 :
79 21141 : void ActionShortcut::readInputLine( const std::string& input, bool saveline ) {
80 21141 : std::vector<std::string> words=Tools::getWords(input);
81 21141 : Tools::interpretLabel(words);
82 : // Check if this action name has been registered
83 : bool founds=false;
84 21141 : bool found = keywords.isActionNeeded(words[0]);
85 : // Check if we are just calling something like SUM_VECTOR using just SUM.
86 21141 : if( !found && words[0].find(getName())!=std::string::npos ) {
87 14575 : found =keywords.isActionSuffixed(words[0],getName());
88 : founds=true;
89 : }
90 21141 : if( found ) {
91 21141 : if( !founds && saveline ) {
92 6566 : addToSavedInputLines( input );
93 : }
94 21141 : if( keywords.exists("RESTART") ) {
95 0 : if( restart ) {
96 0 : words.push_back("RESTART=YES");
97 : }
98 0 : if( !restart ) {
99 0 : words.push_back("RESTART=NO");
100 : }
101 : }
102 21141 : plumed.readInputWords( words, false);
103 21126 : if( !founds ) {
104 : ActionWithValue* av=NULL;
105 11191 : for(auto pp=plumed.getActionSet().rbegin(); pp!=plumed.getActionSet().rend(); ++pp) {
106 11191 : av = pp->get()->castToActionWithValue();
107 11191 : if( !av ) {
108 : continue ;
109 : }
110 6566 : if( std::find(savedOutputs.begin(), savedOutputs.end(), av->getLabel() )!=savedOutputs.end() ) {
111 : av=NULL;
112 : }
113 : break;
114 : }
115 6560 : if( av ) {
116 6560 : std::string av_label = av->getLabel();
117 6560 : if( av_label == getShortcutLabel() && av->getNumberOfComponents()==1 ) {
118 1169 : savedOutputs.push_back( av_label );
119 2338 : plumed_massert( keywords.componentHasCorrectType(".#!value",
120 : (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ),
121 : "documentation for type of value is incorrect");
122 : } else {
123 34502 : for(const auto& cname: keywords.getOutputComponents()) {
124 29111 : if( av_label == getShortcutLabel() + "_" + cname ) {
125 303 : savedOutputs.push_back( av_label );
126 303 : plumed_massert( keywords.componentHasCorrectType(cname,
127 : (av->copyOutput(0))->getRank(),
128 : (av->copyOutput(0))->hasDerivatives() ),
129 : "documentation for type of component " + cname + " is incorrect");
130 57616 : } else if( keywords.getOutputComponentFlag(cname)!="default" ) {
131 23139 : std::string thisflag = keywords.getOutputComponentFlag(cname);
132 46973 : if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + cname)!=std::string::npos ) {
133 30 : savedOutputs.push_back( av_label );
134 30 : plumed_massert( keywords.componentHasCorrectType(cname,
135 : (av->copyOutput(0))->getRank(),
136 : (av->copyOutput(0))->hasDerivatives() ),
137 : "documentation for type of component " + cname + " is incorrect");
138 : }
139 : }
140 : }
141 : }
142 : }
143 : } else {
144 14560 : ActionWithValue* av = plumed.getActionSet()[plumed.getActionSet().size()-1]->castToActionWithValue();
145 14560 : if( !av ) {
146 0 : error("shortcut is using suffix but action created is not ActionWithValue");
147 : }
148 14560 : Keywords thiskeys;
149 14560 : actionRegister().getKeywords( av->getName(), thiskeys );
150 29120 : if( thiskeys.getDisplayName()!=getName() ) {
151 0 : error("mismatch between display name of hidden action " + thiskeys.getDisplayName() + " and shortcut that creates it " + getName() );
152 : }
153 14560 : }
154 : } else {
155 0 : error("requirement for action " + words[0] + " should be registered in registerKeywords function for shortcut action using keys.useAction");
156 : }
157 21141 : }
158 :
159 0 : void ActionShortcut::addCommentToShortcutOutput( const std::string& input ) {
160 0 : savedInputLines.push_back( input );
161 0 : }
162 :
163 37 : std::string ActionShortcut::getUpdateLimits() const {
164 37 : std::string f_input="";
165 37 : if( update_from!=std::numeric_limits<double>::max() ) {
166 : std::string ufrom;
167 2 : Tools::convert( update_from, ufrom );
168 4 : f_input += " UPDATE_FROM=" + ufrom;
169 : }
170 37 : if( update_until!=std::numeric_limits<double>::max() ) {
171 : std::string util;
172 2 : Tools::convert( update_until, util );
173 4 : f_input += " UPDATE_UNTIL=" + util;
174 : }
175 37 : return f_input;
176 : }
177 :
178 6566 : void ActionShortcut::addToSavedInputLines( const std::string& line ) {
179 6566 : std::vector<std::string> words = Tools::getWords(line);
180 : std::string actname;
181 6566 : if( words[0].find_first_of(":")!=std::string::npos) {
182 : actname = words[1];
183 : } else {
184 : actname = words[0];
185 : }
186 6566 : if( !actionRegister().check(actname) ) {
187 0 : error("found no action with name " + actname + " to create shortcut");
188 : }
189 6566 : Keywords thiskeys;
190 6566 : actionRegister().getKeywords( actname, thiskeys );
191 : std::vector<std::string> numberedkeys;
192 55354 : for (auto& keyname: thiskeys.getKeys()) {
193 48788 : if( thiskeys.numbered( keyname ) ) {
194 3050 : numberedkeys.push_back( keyname );
195 : }
196 : }
197 7883 : if( numberedkeys.size()>0 && actname!="CONCATENATE" ) {
198 : std::string reducedline;
199 195238 : for(unsigned i=0; i<words.size(); ++i) {
200 : bool notnumbered=true;
201 527144 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
202 905416 : if( words[i].find(numberedkeys[j])!=std::string::npos && words[i].substr(0,numberedkeys[j].length()+1)!=numberedkeys[j]+"=" ) {
203 : notnumbered=false;
204 : break;
205 : }
206 : }
207 194095 : if( notnumbered || words[i]==actname ) {
208 4259 : if( words[i].find(" ")!=std::string::npos) {
209 104 : std::size_t eq=words[i].find_first_of("=");
210 208 : reducedline += words[i].substr(0,eq) + "={" + words[i].substr(eq+1) + "} ";
211 : } else {
212 8310 : reducedline += words[i] + " ";
213 : }
214 : }
215 : }
216 1143 : std::vector<unsigned> ninstances( numberedkeys.size(), 0 );
217 4019 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
218 2876 : for(unsigned i=1;; ++i) {
219 : std::string num, val;
220 192098 : Tools::convert(i, num);
221 192098 : bool found = Tools::parse(words, numberedkeys[j] + num, val );
222 192098 : if( !found) {
223 : break ;
224 : }
225 189222 : if( i<6 ) {
226 1236 : reducedline += numberedkeys[j] + num + "=" + val + " ";
227 : } else {
228 188604 : ninstances[j]++;
229 : }
230 189222 : }
231 : }
232 : bool outputcomment=false;
233 3885 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
234 2822 : if( ninstances[j]>0 ) {
235 : outputcomment=true;
236 : break;
237 : }
238 : }
239 1143 : if( outputcomment ) {
240 : reducedline += " # Action input conctinues with ";
241 288 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
242 : std::string num;
243 208 : Tools::convert( ninstances[j], num );
244 208 : if( ninstances[j]>0 ) {
245 168 : reducedline += num + " further " + numberedkeys[j] + "n keywords, ";
246 : }
247 : }
248 80 : savedInputLines.push_back( reducedline );
249 : } else {
250 1063 : savedInputLines.push_back( line );
251 : }
252 : } else {
253 5423 : savedInputLines.push_back( line );
254 : }
255 13132 : }
256 :
257 68545 : const std::string & ActionShortcut::getShortcutLabel() const {
258 68545 : return shortcutlabel;
259 : }
260 :
261 18 : std::vector<std::string> ActionShortcut::getSavedInputLines() const {
262 18 : return savedInputLines;
263 : }
264 :
265 41 : std::vector<std::string> ActionShortcut::getSavedOutputs() const {
266 41 : return savedOutputs;
267 : }
268 :
269 14549 : std::string ActionShortcut::convertInputLineToString() {
270 : std::string output;
271 65607 : for(auto p=line.begin(); p!=line.end(); ++p) {
272 51058 : if( (*p).find(" " )!=std::string::npos ) {
273 411 : std::size_t eq = (*p).find_first_of("=");
274 822 : output += " " + (*p).substr(0,eq) + "={" + (*p).substr(eq+1) + "}";
275 : } else {
276 101294 : output += " " + (*p);
277 : }
278 : }
279 14549 : line.resize(0);
280 14549 : return output;
281 : }
282 :
283 641 : void ActionShortcut::interpretDataLabel( const std::string& mystr, Action* myuser, std::vector<Value*>& arg ) const {
284 : std::size_t dot=mystr.find_first_of('.');
285 641 : std::string a=mystr.substr(0,dot);
286 641 : std::string name=mystr.substr(dot+1);
287 : // Retrieve the keywords for the shortcut
288 641 : Keywords skeys;
289 641 : actionRegister().getKeywords( getName(), skeys );
290 641 : std::vector<std::string> out_comps( skeys.getOutputComponents() );
291 : // Now get the output components
292 641 : if( name=="*" ) {
293 2908 : for(unsigned k=0; k<out_comps.size(); ++k) {
294 2499 : if( out_comps[k]=="" ) {
295 0 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
296 0 : if( action ) {
297 0 : if( action->getNumberOfComponents()!=1 ) {
298 0 : myuser->error("action named " + a + " has more than one component");
299 : }
300 0 : arg.push_back(action->copyOutput(0));
301 : }
302 : } else {
303 4998 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] );
304 2499 : if( action ) {
305 258 : if( action->getNumberOfComponents()!=1 ) {
306 0 : myuser->error("action named " + a + "_" + out_comps[k] + " has more than one component");
307 : }
308 258 : arg.push_back(action->copyOutput(0));
309 : } else {
310 2241 : for(unsigned j=1;; ++j) {
311 : std::string numstr;
312 4525 : Tools::convert( j, numstr );
313 9050 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] + "-" + numstr );
314 4525 : if( act ) {
315 140 : for(unsigned n=0; n<act->getNumberOfComponents(); ++n ) {
316 70 : arg.push_back(act->copyOutput(n));
317 : }
318 4455 : } else if( j>1 ) {
319 : break; // This ensures that * syntax works with moments, which normally start from 2
320 : }
321 2284 : }
322 : }
323 : }
324 : }
325 : } else {
326 : // Check for an action that has action.component
327 232 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
328 232 : if( act && act->exists(mystr) ) {
329 : return;
330 : }
331 : // Get components that are actually actions
332 235 : for(unsigned k=0; k<out_comps.size(); ++k) {
333 235 : if(name.find_first_of(out_comps[k])!=std::string::npos ) {
334 400 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + name );
335 200 : if( action ) {
336 88 : arg.push_back(action->copyOutput(a+"_"+name));
337 : }
338 : break;
339 : }
340 : }
341 : }
342 641 : }
343 :
344 : }
|