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 : }
|