Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2011-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 "ActionWithArguments.h"
23 : #include "ActionWithValue.h"
24 : #include "tools/PDB.h"
25 : #include "PlumedMain.h"
26 : #include "ActionSet.h"
27 : #include <iostream>
28 : #ifdef __PLUMED_HAS_CREGEX
29 : #include <cstring>
30 : #include <regex.h>
31 : #endif
32 :
33 : namespace PLMD {
34 :
35 2906 : void ActionWithArguments::registerKeywords(Keywords& keys) {
36 5812 : keys.reserve("numbered","ARG","the input for this action is the scalar output from one or more other actions. The particular scalars that you will use "
37 : "are referenced using the label of the action. If the label appears on its own then it is assumed that the Action calculates "
38 : "a single scalar value. The value of this scalar is thus used as the input to this new action. If * or *.* appears the "
39 : "scalars calculated by all the proceeding actions in the input file are taken. Some actions have multi-component outputs and "
40 : "each component of the output has a specific label. For example a \\ref DISTANCE action labelled dist may have three components "
41 : "x, y and z. To take just the x component you should use dist.x, if you wish to take all three components then use dist.*."
42 : "More information on the referencing of Actions can be found in the section of the manual on the PLUMED \\ref Syntax. "
43 : "Scalar values can also be "
44 : "referenced using POSIX regular expressions as detailed in the section on \\ref Regex. To use this feature you you must compile "
45 : "PLUMED with the appropriate flag.");
46 2906 : }
47 :
48 2848 : void ActionWithArguments::parseArgumentList(const std::string&key,std::vector<Value*>&arg) {
49 2848 : std::string def; std::vector<std::string> c; arg.clear(); parseVector(key,c);
50 3306 : if( c.size()==0 && (keywords.style(key,"compulsory") || keywords.style(key,"hidden")) ) {
51 0 : if( keywords.getDefaultValue(key,def) ) c.push_back( def );
52 : else return;
53 : }
54 2848 : interpretArgumentList(c,arg);
55 2848 : }
56 :
57 6 : bool ActionWithArguments::parseArgumentList(const std::string&key,int i,std::vector<Value*>&arg) {
58 : std::vector<std::string> c;
59 : arg.clear();
60 6 : if(parseNumberedVector(key,i,c)) {
61 6 : interpretArgumentList(c,arg);
62 : return true;
63 : } else return false;
64 6 : }
65 :
66 2950 : void ActionWithArguments::interpretArgumentList(const std::vector<std::string>& c, std::vector<Value*>&arg) {
67 8361 : for(unsigned i=0; i<c.size(); i++) {
68 : // is a regex? then just interpret it. The signal is ()
69 5414 : if(!c[i].compare(0,1,"(")) {
70 196 : unsigned l=c[i].length();
71 196 : if(!c[i].compare(l-1,1,")")) {
72 : // start regex parsing
73 : #ifdef __PLUMED_HAS_CREGEX
74 : // take the string enclosed in quotes and put in round brackets
75 195 : std::string myregex=c[i];
76 : //log<<" Evaluating regexp for this action: "<<myregex<<"\n";
77 :
78 : regex_t reg; // regular expression
79 :
80 195 : int errcode=regcomp(®, myregex.c_str(),REG_EXTENDED|REG_NEWLINE); // compile the regular expression
81 195 : if(errcode) {
82 : // one can check the errors asking to regerror
83 1 : size_t errbuf_size = regerror(errcode, ®, NULL, 0);
84 2 : std::vector<char> errbuf(errbuf_size);
85 1 : regerror(errcode, ®, errbuf.data(), errbuf_size);
86 3 : plumed_error()<<"Error parsing regular expression: "<<errbuf.data();
87 : }
88 :
89 : // call regfree when reg goes out of scope
90 194 : auto deleter=[](regex_t* r) { regfree(r); };
91 : std::unique_ptr<regex_t,decltype(deleter)> reg_deleter(®,deleter);
92 :
93 194 : plumed_massert(reg.re_nsub==1,"I can parse with only one subexpression");
94 : regmatch_t match;
95 : // select all the actions that have a value
96 194 : std::vector<ActionWithValue*> all=plumed.getActionSet().select<ActionWithValue*>();
97 194 : if( all.empty() ) error("your input file is not telling plumed to calculate anything");
98 : bool found_something=false;
99 1495 : for(unsigned j=0; j<all.size(); j++) {
100 1301 : std::vector<std::string> ss=all[j]->getComponentsVector();
101 220214 : for(unsigned k=0; k<ss.size(); ++k) {
102 218913 : unsigned ll=std::strlen(ss[k].c_str())+1;
103 218913 : std::vector<char> str(ll);
104 : std::strcpy(&str[0],ss[k].c_str());
105 : const char *ppstr=&str[0];
106 218913 : if(!regexec(®, ppstr, reg.re_nsub, &match, 0)) {
107 : //log.printf(" Something matched with \"%s\" : ",ss[k].c_str());
108 : do {
109 27814 : if (match.rm_so != -1) { /* The regex is matching part of a string */
110 27814 : size_t matchlen = match.rm_eo - match.rm_so;
111 27814 : std::vector<char> submatch(matchlen+1);
112 27814 : std::strncpy(submatch.data(), ppstr+match.rm_so, matchlen+1);
113 27814 : submatch[matchlen]='\0';
114 : //log.printf(" subpattern %s\n", submatch.data());
115 : // this is the match: try to see if it is a valid action
116 27814 : std::string putativeVal(submatch.data());
117 27814 : if( all[j]->exists(putativeVal) ) {
118 21327 : arg.push_back(all[j]->copyOutput(putativeVal));
119 : found_something=true;
120 : //log.printf(" Action %s added! \n",putativeVal.c_str());
121 : }
122 : }
123 27814 : ppstr += match.rm_eo; /* Restart from last match */
124 27814 : } while(!regexec(®,ppstr,reg.re_nsub,&match,0));
125 : }
126 : }
127 1301 : }
128 194 : if(!found_something) plumed_error()<<"There isn't any action matching your regex " << myregex;
129 : #else
130 : plumed_merror("Regexp support not compiled!");
131 : #endif
132 : } else {
133 2 : plumed_merror("did you want to use regexp to input arguments? enclose it between two round braces (...) with no spaces!");
134 : }
135 : } else {
136 : std::size_t dot=c[i].find_first_of('.');
137 5218 : std::string a=c[i].substr(0,dot);
138 5218 : std::string name=c[i].substr(dot+1);
139 5218 : if(c[i].find(".")!=std::string::npos) { // if it contains a dot:
140 1853 : if(a=="*" && name=="*") {
141 : // Take all values from all actions
142 1 : std::vector<ActionWithValue*> all=plumed.getActionSet().select<ActionWithValue*>();
143 1 : if( all.empty() ) error("your input file is not telling plumed to calculate anything");
144 9 : for(unsigned j=0; j<all.size(); j++) {
145 18 : for(int k=0; k<all[j]->getNumberOfComponents(); ++k) arg.push_back(all[j]->copyOutput(k));
146 : }
147 1843 : } else if ( name=="*") {
148 : // Take all the values from an action with a specific name
149 510 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(a);
150 510 : if(!action) {
151 0 : std::string str=" (hint! the actions with value in this ActionSet are: ";
152 0 : str+=plumed.getActionSet().getLabelList<ActionWithValue*>()+")";
153 0 : error("cannot find action named " + a + str);
154 : }
155 510 : if( action->getNumberOfComponents()==0 ) error("found " + a +".* indicating use all components calculated by action with label " + a + " but this action has no components");
156 5434 : for(int k=0; k<action->getNumberOfComponents(); ++k) arg.push_back(action->copyOutput(k));
157 1333 : } else if ( a=="*" ) {
158 : // Take components from all actions with a specific name
159 8 : std::vector<ActionWithValue*> all=plumed.getActionSet().select<ActionWithValue*>();
160 8 : if( all.empty() ) error("your input file is not telling plumed to calculate anything");
161 : unsigned nval=0;
162 36 : for(unsigned j=0; j<all.size(); j++) {
163 56 : std::string flab; flab=all[j]->getLabel() + "." + name;
164 28 : if( all[j]->exists(flab) ) { arg.push_back(all[j]->copyOutput(flab)); nval++; }
165 : }
166 8 : if(nval==0) error("found no actions with a component called " + name );
167 : } else {
168 : // Take values with a specific name
169 1325 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(a);
170 1325 : if(!action) {
171 0 : std::string str=" (hint! the actions with value in this ActionSet are: ";
172 0 : str+=plumed.getActionSet().getLabelList<ActionWithValue*>()+")";
173 0 : error("cannot find action named " + a +str);
174 : }
175 1325 : if( !(action->exists(c[i])) ) {
176 0 : std::string str=" (hint! the components in this actions are: ";
177 0 : str+=action->getComponentsList()+")";
178 0 : error("action " + a + " has no component named " + name + str);
179 : } ;
180 1325 : arg.push_back(action->copyOutput(c[i]));
181 : }
182 : } else { // if it doesn't contain a dot
183 3374 : if(c[i]=="*") {
184 : // Take all values from all actions
185 101 : std::vector<ActionWithValue*> all=plumed.getActionSet().select<ActionWithValue*>();
186 101 : if( all.empty() ) error("your input file is not telling plumed to calculate anything");
187 685 : for(unsigned j=0; j<all.size(); j++) {
188 1257 : for(int k=0; k<all[j]->getNumberOfComponents(); ++k) arg.push_back(all[j]->copyOutput(k));
189 : }
190 : } else {
191 3273 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(c[i]);
192 3273 : if(!action) {
193 1 : std::string str=" (hint! the actions with value in this ActionSet are: ";
194 2 : str+=plumed.getActionSet().getLabelList<ActionWithValue*>()+")";
195 3 : error("cannot find action named " + c[i] + str );
196 : }
197 3272 : if( !(action->exists(c[i])) ) {
198 0 : std::string str=" (hint! the components in this actions are: ";
199 0 : str+=action->getComponentsList()+")";
200 0 : error("action " + c[i] + " has no component named " + c[i] +str);
201 : };
202 3273 : arg.push_back(action->copyOutput(c[i]));
203 : }
204 : }
205 : }
206 : }
207 2947 : }
208 :
209 416 : void ActionWithArguments::expandArgKeywordInPDB( const PDB& pdb ) {
210 416 : std::vector<std::string> arg_names = pdb.getArgumentNames();
211 416 : if( arg_names.size()>0 ) {
212 : std::vector<Value*> arg_vals;
213 80 : interpretArgumentList( arg_names, arg_vals );
214 : }
215 416 : }
216 :
217 178924 : void ActionWithArguments::requestArguments(const std::vector<Value*> &arg) {
218 178924 : plumed_massert(!lockRequestArguments,"requested argument list can only be changed in the prepare() method");
219 178924 : arguments=arg;
220 178924 : clearDependencies();
221 : std::string fullname;
222 : std::string name;
223 1255035 : for(unsigned i=0; i<arguments.size(); i++) {
224 1076111 : fullname=arguments[i]->getName();
225 1076111 : if(fullname.find(".")!=std::string::npos) {
226 : std::size_t dot=fullname.find_first_of('.');
227 738352 : name=fullname.substr(0,dot);
228 : } else {
229 : name=fullname;
230 : }
231 1076111 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(name);
232 1076111 : plumed_massert(action,"cannot find action named (in requestArguments - this is weird)" + name);
233 1076111 : addDependency(action);
234 : }
235 178924 : }
236 :
237 4 : void ActionWithArguments::requestExtraDependencies(const std::vector<Value*> &extra) {
238 4 : plumed_massert(!lockRequestArguments,"requested argument list can only be changed in the prepare() method");
239 : std::string fullname;
240 : std::string name;
241 9 : for(unsigned i=0; i<extra.size(); i++) {
242 5 : fullname=extra[i]->getName();
243 5 : if(fullname.find(".")!=std::string::npos) {
244 : std::size_t dot=fullname.find_first_of('.');
245 0 : name=fullname.substr(0,dot);
246 : } else {
247 : name=fullname;
248 : }
249 5 : ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(name);
250 5 : plumed_massert(action,"cannot find action named (in requestArguments - this is weird)" + name);
251 5 : addDependency(action);
252 : }
253 4 : }
254 :
255 2808 : ActionWithArguments::ActionWithArguments(const ActionOptions&ao):
256 : Action(ao),
257 2808 : lockRequestArguments(false)
258 : {
259 5619 : if( keywords.exists("ARG") ) {
260 : std::vector<Value*> arg;
261 5430 : parseArgumentList("ARG",arg);
262 :
263 2712 : if(!arg.empty()) {
264 2583 : log.printf(" with arguments");
265 25674 : for(unsigned i=0; i<arg.size(); i++) log.printf(" %s",arg[i]->getName().c_str());
266 2583 : log.printf("\n");
267 : }
268 2712 : requestArguments(arg);
269 : }
270 2805 : }
271 :
272 58 : void ActionWithArguments::calculateNumericalDerivatives( ActionWithValue* a ) {
273 58 : if(!a) {
274 58 : a=dynamic_cast<ActionWithValue*>(this);
275 58 : plumed_massert(a,"cannot compute numerical derivatives for an action without values");
276 : }
277 :
278 58 : const size_t nval=a->getNumberOfComponents();
279 : const size_t npar=arguments.size();
280 58 : std::vector<double> value (nval*npar);
281 161 : for(int i=0; i<npar; i++) {
282 103 : double arg0=arguments[i]->get();
283 103 : arguments[i]->set(arg0+std::sqrt(epsilon));
284 103 : a->calculate();
285 103 : arguments[i]->set(arg0);
286 1367 : for(int j=0; j<nval; j++) {
287 1264 : value[i*nval+j]=a->getOutputQuantity(j);
288 : }
289 : }
290 58 : a->calculate();
291 58 : a->clearDerivatives();
292 1192 : for(int j=0; j<nval; j++) {
293 1134 : Value* v=a->copyOutput(j);
294 1534 : if( v->hasDerivatives() ) for(int i=0; i<npar; i++) v->addDerivative(i,(value[i*nval+j]-a->getOutputQuantity(j))/std::sqrt(epsilon));
295 : }
296 58 : }
297 :
298 261 : double ActionWithArguments::getProjection(unsigned i,unsigned j)const {
299 261 : plumed_massert(i<arguments.size()," making projections with an index which is too large");
300 261 : plumed_massert(j<arguments.size()," making projections with an index which is too large");
301 261 : const Value* v1=arguments[i];
302 261 : const Value* v2=arguments[j];
303 261 : return Value::projection(*v1,*v2);
304 : }
305 :
306 350 : void ActionWithArguments::addForcesOnArguments( const std::vector<double>& forces ) {
307 474 : for(unsigned i=0; i<arguments.size(); ++i) arguments[i]->addForce( forces[i] );
308 350 : }
309 :
310 : }
|