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 16084 : void ActionShortcut::registerKeywords( Keywords& keys ) {
31 16084 : Action::registerKeywords( keys );
32 16084 : keys.add("hidden","IS_SHORTCUT","hidden keyword to tell if actions are shortcuts so that example generator can provide expansions of shortcuts");
33 16084 : keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
34 16084 : }
35 :
36 236 : void ActionShortcut::readShortcutKeywords( const Keywords& keys, std::map<std::string,std::string>& keymap ) {
37 2822 : for (auto& keyname:keys.getKeys()) {
38 : std::string t;
39 : bool def;
40 2586 : if( keys.getLogicalDefault(keyname,def) ) {
41 940 : bool found=false;
42 940 : parseFlag(keyname,found);
43 940 : if( found ) {
44 208 : keymap.insert(std::pair<std::string,std::string>(keyname,""));
45 : }
46 6584 : } else if( keys.style( keyname, "optional") || keys.style( keyname, "compulsory") || keys.style( keyname, "deprecated") ) {
47 1646 : parse(keyname,t);
48 1646 : if( t.length()>0 ) {
49 102 : keymap.insert(std::pair<std::string,std::string>(keyname,t));
50 1595 : } else if( keys.numbered( keyname ) ) {
51 668 : for(unsigned i=1;; ++i) {
52 : std::string istr;
53 683 : Tools::convert( i, istr );
54 683 : if( !parseNumbered(keyname,i,t) ) {
55 : break ;
56 : }
57 30 : keymap.insert(std::pair<std::string,std::string>(keyname + istr,t));
58 15 : }
59 : }
60 : } else {
61 0 : plumed_merror("shortcut keywords should be optional, compulsory or flags");
62 : }
63 : }
64 236 : }
65 :
66 14759 : ActionShortcut::ActionShortcut(const ActionOptions&ao):
67 : Action(ao),
68 14759 : shortcutlabel(actionLabel) {
69 : std::string s;
70 14759 : Tools::convert(plumed.getActionSet().size(),s);
71 14759 : if( shortcutlabel==("@" + s) ) {
72 : std::string t;
73 0 : Tools::convert(plumed.getActionSet().size(),t);
74 0 : shortcutlabel="@" + t;
75 : } else {
76 29518 : actionLabel = ("@s" + s);
77 : }
78 14759 : }
79 :
80 19195 : void ActionShortcut::readInputLine( const std::string& input, bool saveline ) {
81 19195 : std::vector<std::string> words=Tools::getWords(input);
82 19195 : Tools::interpretLabel(words);
83 : // Check if this action name has been registered
84 : bool founds=false;
85 19195 : bool found = keywords.isActionNeeded(words[0]);
86 : // Check if we are just calling something like SUM_VECTOR using just SUM.
87 19195 : if( !found && words[0].find(getName())!=std::string::npos ) {
88 13618 : found =keywords.isActionSuffixed(words[0],getName());
89 : founds=true;
90 : }
91 19195 : if( found ) {
92 19195 : if( !founds && saveline ) {
93 5577 : addToSavedInputLines( input );
94 : }
95 19195 : if( keywords.exists("RESTART") ) {
96 0 : if( restart ) {
97 0 : words.push_back("RESTART=YES");
98 : }
99 0 : if( !restart ) {
100 0 : words.push_back("RESTART=NO");
101 : }
102 : }
103 19195 : plumed.readInputWords( words, false);
104 19180 : if( !founds ) {
105 : ActionWithValue* av=NULL;
106 9219 : for(auto pp=plumed.getActionSet().rbegin(); pp!=plumed.getActionSet().rend(); ++pp) {
107 9219 : av = pp->get()->castToActionWithValue();
108 9219 : if( !av ) {
109 : continue ;
110 : }
111 5577 : if( std::find(savedOutputs.begin(), savedOutputs.end(), av->getLabel() )!=savedOutputs.end() ) {
112 : av=NULL;
113 : }
114 : break;
115 : }
116 5571 : if( av ) {
117 5571 : std::string av_label = av->getLabel();
118 5571 : if( av_label == getShortcutLabel() && av->getNumberOfComponents()==1 ) {
119 1034 : savedOutputs.push_back( av_label );
120 2068 : plumed_massert( keywords.componentHasCorrectType(".#!value",
121 : (av->copyOutput(0))->getRank(), (av->copyOutput(0))->hasDerivatives() ),
122 : "documentation for type of value is incorrect");
123 : } else {
124 32564 : for(const auto& cname: keywords.getOutputComponents()) {
125 28027 : if( av_label == getShortcutLabel() + "_" + cname ) {
126 303 : savedOutputs.push_back( av_label );
127 303 : plumed_massert( keywords.componentHasCorrectType(cname,
128 : (av->copyOutput(0))->getRank(),
129 : (av->copyOutput(0))->hasDerivatives() ),
130 : "documentation for type of component " + cname + " is incorrect");
131 55448 : } else if( keywords.getOutputComponentFlag(cname)!="default" ) {
132 22713 : std::string thisflag = keywords.getOutputComponentFlag(cname);
133 46151 : if( keywords.numbered(thisflag) && av_label.find(getShortcutLabel() + "_" + cname)!=std::string::npos ) {
134 30 : savedOutputs.push_back( av_label );
135 30 : plumed_massert( keywords.componentHasCorrectType(cname,
136 : (av->copyOutput(0))->getRank(),
137 : (av->copyOutput(0))->hasDerivatives() ),
138 : "documentation for type of component " + cname + " is incorrect");
139 : }
140 : }
141 : }
142 : }
143 : }
144 : } else {
145 13603 : ActionWithValue* av = plumed.getActionSet()[plumed.getActionSet().size()-1]->castToActionWithValue();
146 13603 : if( !av ) {
147 0 : error("shortcut is using suffix but action created is not ActionWithValue");
148 : }
149 13603 : Keywords thiskeys;
150 13603 : actionRegister().getKeywords( av->getName(), thiskeys );
151 27206 : if( thiskeys.getDisplayName()!=getName() ) {
152 0 : error("mismatch between display name of hidden action " + thiskeys.getDisplayName() + " and shortcut that creates it " + getName() );
153 : }
154 13603 : }
155 : } else {
156 0 : error("requirement for action " + words[0] + " should be registered in registerKeywords function for shortcut action using keys.useAction");
157 : }
158 19195 : }
159 :
160 0 : void ActionShortcut::addCommentToShortcutOutput( const std::string& input ) {
161 0 : savedInputLines.push_back( input );
162 0 : }
163 :
164 37 : std::string ActionShortcut::getUpdateLimits() const {
165 37 : std::string f_input="";
166 37 : if( update_from!=std::numeric_limits<double>::max() ) {
167 : std::string ufrom;
168 2 : Tools::convert( update_from, ufrom );
169 4 : f_input += " UPDATE_FROM=" + ufrom;
170 : }
171 37 : if( update_until!=std::numeric_limits<double>::max() ) {
172 : std::string util;
173 2 : Tools::convert( update_until, util );
174 4 : f_input += " UPDATE_UNTIL=" + util;
175 : }
176 37 : return f_input;
177 : }
178 :
179 5577 : void ActionShortcut::addToSavedInputLines( const std::string& line ) {
180 5577 : std::vector<std::string> words = Tools::getWords(line);
181 : std::string actname;
182 5577 : if( words[0].find_first_of(":")!=std::string::npos) {
183 : actname = words[1];
184 : } else {
185 : actname = words[0];
186 : }
187 5577 : if( !actionRegister().check(actname) ) {
188 0 : error("found no action with name " + actname + " to create shortcut");
189 : }
190 5577 : Keywords thiskeys;
191 5577 : actionRegister().getKeywords( actname, thiskeys );
192 : std::vector<std::string> numberedkeys;
193 49764 : for (auto& keyname: thiskeys.getKeys()) {
194 44187 : if( thiskeys.numbered( keyname ) ) {
195 2010 : numberedkeys.push_back( keyname );
196 : }
197 : }
198 6643 : if( numberedkeys.size()>0 && actname!="CONCATENATE" ) {
199 : std::string reducedline;
200 353821 : for(unsigned i=0; i<words.size(); ++i) {
201 : bool notnumbered=true;
202 520752 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
203 1219227 : if( words[i].find(numberedkeys[j])!=std::string::npos && words[i].substr(0,numberedkeys[j].length()+1)!=numberedkeys[j]+"=" ) {
204 : notnumbered=false;
205 : break;
206 : }
207 : }
208 352824 : if( notnumbered || words[i]==actname ) {
209 3381 : if( words[i].find(" ")!=std::string::npos) {
210 107 : std::size_t eq=words[i].find_first_of("=");
211 214 : reducedline += words[i].substr(0,eq) + "={" + words[i].substr(eq+1) + "} ";
212 : } else {
213 6548 : reducedline += words[i] + " ";
214 : }
215 : }
216 : }
217 997 : std::vector<unsigned> ninstances( numberedkeys.size(), 0 );
218 2938 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
219 1941 : for(unsigned i=1;; ++i) {
220 : std::string num, val;
221 351208 : Tools::convert(i, num);
222 351208 : bool found = Tools::parse(words, numberedkeys[j] + num, val );
223 351208 : if( !found) {
224 : break ;
225 : }
226 349267 : if( i<6 ) {
227 1592 : reducedline += numberedkeys[j] + num + "=" + val + " ";
228 : } else {
229 348471 : ninstances[j]++;
230 : }
231 349267 : }
232 : }
233 : bool outputcomment=false;
234 2780 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
235 1895 : if( ninstances[j]>0 ) {
236 : outputcomment=true;
237 : break;
238 : }
239 : }
240 997 : if( outputcomment ) {
241 : reducedline += " # Action input conctinues with ";
242 308 : for(unsigned j=0; j<numberedkeys.size(); ++j) {
243 : std::string num;
244 196 : Tools::convert( ninstances[j], num );
245 196 : if( ninstances[j]>0 ) {
246 232 : reducedline += num + " further " + numberedkeys[j] + "n keywords, ";
247 : }
248 : }
249 112 : savedInputLines.push_back( reducedline );
250 : } else {
251 885 : savedInputLines.push_back( line );
252 : }
253 : } else {
254 4580 : savedInputLines.push_back( line );
255 : }
256 11154 : }
257 :
258 63093 : const std::string & ActionShortcut::getShortcutLabel() const {
259 63093 : return shortcutlabel;
260 : }
261 :
262 20 : const std::vector<std::string>& ActionShortcut::getSavedInputLines() const {
263 20 : return savedInputLines;
264 : }
265 :
266 41 : const std::vector<std::string>& ActionShortcut::getSavedOutputs() const {
267 41 : return savedOutputs;
268 : }
269 :
270 13618 : std::string ActionShortcut::convertInputLineToString() {
271 13618 : return linemap.convertToString(true);
272 : }
273 :
274 673 : void ActionShortcut::interpretDataLabel( const std::string& mystr, Action* myuser, std::vector<Value*>& arg ) const {
275 : std::size_t dot=mystr.find_first_of('.');
276 673 : std::string a=mystr.substr(0,dot);
277 673 : std::string dataName=mystr.substr(dot+1);
278 : // Retrieve the keywords for the shortcut
279 673 : Keywords skeys;
280 673 : actionRegister().getKeywords( getName(), skeys );
281 673 : std::vector<std::string> out_comps( skeys.getOutputComponents() );
282 : // Now get the output components
283 673 : if( dataName=="*" ) {
284 2764 : for(unsigned k=0; k<out_comps.size(); ++k) {
285 2328 : 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 4656 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] );
295 2328 : if( action ) {
296 247 : if( action->getNumberOfComponents()!=1 ) {
297 0 : myuser->error("action named " + a + "_" + out_comps[k] + " has more than one component");
298 : }
299 247 : arg.push_back(action->copyOutput(0));
300 : } else {
301 2081 : for(unsigned j=1;; ++j) {
302 : std::string numstr;
303 4190 : Tools::convert( j, numstr );
304 8380 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + out_comps[k] + "-" + numstr );
305 4190 : if( act ) {
306 90 : for(unsigned n=0; n<act->getNumberOfComponents(); ++n ) {
307 45 : arg.push_back(act->copyOutput(n));
308 : }
309 4145 : } else if( j>1 ) {
310 : break; // This ensures that * syntax works with moments, which normally start from 2
311 : }
312 2109 : }
313 : }
314 : }
315 : }
316 : } else {
317 : // Check for an action that has action.component
318 237 : ActionWithValue* act=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a );
319 237 : if( act && act->exists(mystr) ) {
320 : return;
321 : }
322 : // Get components that are actually actions
323 288 : for(unsigned k=0; k<out_comps.size(); ++k) {
324 288 : if(dataName.find_first_of(out_comps[k])!=std::string::npos ) {
325 410 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>( a + "_" + dataName );
326 205 : if( action ) {
327 87 : arg.push_back(action->copyOutput(a+"_"+dataName));
328 : }
329 : break;
330 : }
331 : }
332 : }
333 673 : }
334 :
335 : }
|