LCOV - code coverage report
Current view: top level - core - ActionWithArguments.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 214 246 87.0 %
Date: 2026-03-30 11:13:23 Functions: 12 14 85.7 %

          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 "ActionAtomistic.h"
      25             : #include "ActionForInterface.h"
      26             : #include "ActionWithVector.h"
      27             : #include "ActionWithVirtualAtom.h"
      28             : #include "ActionShortcut.h"
      29             : #include "tools/PDB.h"
      30             : #include "PlumedMain.h"
      31             : #include "ActionSet.h"
      32             : #include <iostream>
      33             : #include <regex>
      34             : 
      35             : namespace PLMD {
      36             : 
      37       18411 : void ActionWithArguments::registerKeywords(Keywords& keys) {
      38       36822 :   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 "
      39             :                "are referenced using the label of the action. If the label appears on its own then it is assumed that the Action calculates "
      40             :                "a single scalar value.  The value of this scalar is thus used as the input to this new action.  If * or *.* appears the "
      41             :                "scalars calculated by all the proceeding actions in the input file are taken.  Some actions have multi-component outputs and "
      42             :                "each component of the output has a specific label.  For example a \\ref DISTANCE action labelled dist may have three components "
      43             :                "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.*."
      44             :                "More information on the referencing of Actions can be found in the section of the manual on the PLUMED \\ref Syntax.  "
      45             :                "Scalar values can also be "
      46             :                "referenced using POSIX regular expressions as detailed in the section on \\ref Regex. To use this feature you you must compile "
      47             :                "PLUMED with the appropriate flag.");
      48       18411 : }
      49             : 
      50       10021 : void ActionWithArguments::parseArgumentList(const std::string&key,std::vector<Value*>&arg) {
      51             :   std::string def;
      52             :   std::vector<std::string> c;
      53             :   arg.clear();
      54       10021 :   parseVector(key,c);
      55       10970 :   if( c.size()==0 && (keywords.style(key,"compulsory") || keywords.style(key,"hidden")) ) {
      56           7 :     if( keywords.getDefaultValue(key,def) ) {
      57           0 :       c.push_back( def );
      58             :     } else {
      59             :       return;
      60             :     }
      61             :   }
      62       10014 :   interpretArgumentList(c,plumed.getActionSet(),this,arg);
      63       10021 : }
      64             : 
      65         104 : bool ActionWithArguments::parseArgumentList(const std::string&key,int i,std::vector<Value*>&arg) {
      66             :   std::vector<std::string> c;
      67             :   arg.clear();
      68         104 :   if(parseNumberedVector(key,i,c)) {
      69          44 :     interpretArgumentList(c,plumed.getActionSet(),this,arg);
      70             :     return true;
      71             :   } else {
      72             :     return false;
      73             :   }
      74         104 : }
      75             : 
      76       15387 : void ActionWithArguments::interpretArgumentList(const std::vector<std::string>& c, const ActionSet& as, Action* readact, std::vector<Value*>&arg) {
      77       41261 :   for(unsigned i=0; i<c.size(); i++) {
      78             :     // is a regex? then just interpret it. The signal is ()
      79       25877 :     if(!c[i].compare(0,1,"(")) {
      80         219 :       unsigned l=c[i].length();
      81         219 :       if(!c[i].compare(l-1,1,")")) {
      82             :         // start regex parsing
      83             :         bool found_something=false;
      84             :         // take the string enclosed in quotes and put in round brackets
      85         218 :         std::string myregex=c[i];
      86         218 :         std::vector<ActionWithValue*> all=as.select<ActionWithValue*>();
      87         218 :         if( all.empty() ) {
      88           1 :           readact->error("your input file is not telling plumed to calculate anything");
      89             :         }
      90             : 
      91             :         try {
      92         218 :           std::regex txt_regex(myregex,std::regex::extended);
      93         217 :           plumed_massert(txt_regex.mark_count()==1,"I can parse with only one subexpression");
      94       24239 :           for(unsigned j=0; j<all.size(); j++) {
      95       24022 :             std::vector<std::string> ss=all[j]->getComponentsVector();
      96      349707 :             for(unsigned  k=0; k<ss.size(); ++k) {
      97      325685 :               if(std::regex_match(ss[k],txt_regex)) {
      98       21601 :                 arg.push_back(all[j]->copyOutput(ss[k]));
      99             :                 found_something=true;
     100             :               }
     101             :             }
     102       24022 :           }
     103         218 :         } catch(std::regex_error & e) {
     104           3 :           plumed_error()<<"Error parsing regular expression: "<<e.what();
     105           1 :         }
     106         217 :         if(!found_something) {
     107           0 :           plumed_error()<<"There isn't any action matching your regex " << myregex;
     108             :         }
     109             :       } else {
     110           2 :         plumed_merror("did you want to use regexp to input arguments? enclose it between two round braces (...) with no spaces!");
     111             :       }
     112             :     } else {
     113             :       std::size_t dot=c[i].find_first_of('.');
     114       25658 :       std::string a=c[i].substr(0,dot);
     115       25658 :       std::string name=c[i].substr(dot+1);
     116       25658 :       if(c[i].find(".")!=std::string::npos) {   // if it contains a dot:
     117        6534 :         if(a=="*" && name=="*") {
     118             :           // Take all values from all actions
     119           1 :           std::vector<ActionWithValue*> all=as.select<ActionWithValue*>();
     120           1 :           if( all.empty() ) {
     121           0 :             readact->error("your input file is not telling plumed to calculate anything");
     122             :           }
     123          17 :           for(unsigned j=0; j<all.size(); j++) {
     124          16 :             plumed_assert(all[j]); // needed for following calls, see #1046
     125          16 :             ActionForInterface* ap=all[j]->castToActionForInterface();
     126          16 :             if( ap ) {
     127           8 :               continue;
     128             :             }
     129          18 :             for(int k=0; k<all[j]->getNumberOfComponents(); ++k) {
     130          10 :               arg.push_back(all[j]->copyOutput(k));
     131             :             }
     132             :           }
     133        6515 :         } else if ( name=="*") {
     134             :           unsigned carg=arg.size();
     135             :           // Take all the values from an action with a specific name
     136         650 :           ActionShortcut* shortcut=as.getShortcutActionWithLabel(a);
     137         650 :           if( shortcut ) {
     138         822 :             shortcut->interpretDataLabel( a + "." + name, readact, arg );
     139             :           }
     140         650 :           if( arg.size()==carg ) {
     141             :             // Take all the values from an action with a specific name
     142         395 :             ActionWithValue* action=as.selectWithLabel<ActionWithValue*>(a);
     143         395 :             if(!action) {
     144           0 :               std::string str=" (hint! the actions with value in this ActionSet are: ";
     145           0 :               str+=as.getLabelList<ActionWithValue*>()+")";
     146           0 :               readact->error("cannot find action named " + a + str);
     147             :             }
     148         395 :             if( action->getNumberOfComponents()==0 ) {
     149           0 :               readact->error("found " + a +".* indicating use all components calculated by action with label " + a + " but this action has no components");
     150             :             }
     151        6306 :             for(int k=0; k<action->getNumberOfComponents(); ++k) {
     152        5911 :               arg.push_back(action->copyOutput(k));
     153             :             }
     154             :           }
     155        5865 :         } else if ( a=="*" ) {
     156          17 :           std::vector<ActionShortcut*> shortcuts=as.select<ActionShortcut*>();
     157             :           // Take components from all actions with a specific name
     158          17 :           std::vector<ActionWithValue*> all=as.select<ActionWithValue*>();
     159          17 :           if( all.empty() ) {
     160           0 :             readact->error("your input file is not telling plumed to calculate anything");
     161             :           }
     162             :           unsigned carg=arg.size();
     163         161 :           for(unsigned j=0; j<shortcuts.size(); ++j) {
     164         288 :             shortcuts[j]->interpretDataLabel( shortcuts[j]->getShortcutLabel() + "." + name, readact, arg );
     165             :           }
     166             :           unsigned nval=0;
     167         320 :           for(unsigned j=0; j<all.size(); j++) {
     168             :             std::string flab;
     169         606 :             flab=all[j]->getLabel() + "." + name;
     170         303 :             if( all[j]->exists(flab) ) {
     171          44 :               arg.push_back(all[j]->copyOutput(flab));
     172          44 :               nval++;
     173             :             }
     174             :           }
     175          17 :           if(nval==0 && arg.size()==carg) {
     176           0 :             readact->error("found no actions with a component called " + name );
     177             :           }
     178             :         } else {
     179             :           // Take values with a specific name
     180        5848 :           ActionWithValue* action=as.selectWithLabel<ActionWithValue*>(a);
     181        5848 :           ActionShortcut* shortcut=as.getShortcutActionWithLabel(a);
     182        5848 :           if( !shortcut && !action ) {
     183           0 :             std::string str=" (hint! the actions with value in this ActionSet are: ";
     184           0 :             str+=as.getLabelList<ActionWithValue*>()+")";
     185           0 :             readact->error("cannot find action named " + a +str);
     186        5848 :           } else if( action && action->exists(c[i]) ) {
     187        5760 :             arg.push_back(action->copyOutput(c[i]));
     188          88 :           } else if( shortcut ) {
     189             :             unsigned narg=arg.size();
     190         176 :             shortcut->interpretDataLabel( a + "." + name, readact, arg );
     191          88 :             if( arg.size()==narg ) {
     192           0 :               readact->error("found no element in " + a + " with label " + name );
     193             :             }
     194             :           } else {
     195           0 :             std::string str=" (hint! the components in this actions are: ";
     196           0 :             str+=action->getComponentsList()+")";
     197           0 :             readact->error("action " + a + " has no component named " + name + str);
     198             :           }
     199             :         }
     200             :       } else {    // if it doesn't contain a dot
     201       19142 :         if(c[i]=="*") {
     202             :           // Take all values from all actions
     203         107 :           std::vector<ActionWithValue*> all=as.select<ActionWithValue*>();
     204         107 :           if( all.empty() ) {
     205           0 :             readact->error("your input file is not telling plumed to calculate anything");
     206             :           }
     207        1615 :           for(unsigned j=0; j<all.size(); j++) {
     208        1508 :             plumed_assert(all[j]); // needed for following calls, see #1046
     209        1508 :             ActionWithVirtualAtom* av=all[j]->castToActionWithVirtualAtom();
     210        1508 :             if( av ) {
     211          57 :               continue;
     212             :             }
     213        1451 :             ActionForInterface* ap=all[j]->castToActionForInterface();
     214        1451 :             if( ap && all[j]->getName()!="ENERGY" ) {
     215         834 :               continue;
     216             :             }
     217        1320 :             for(int k=0; k<all[j]->getNumberOfComponents(); ++k) {
     218         703 :               arg.push_back(all[j]->copyOutput(k));
     219             :             }
     220             :           }
     221             :         } else {
     222       19035 :           ActionWithValue* action=as.selectWithLabel<ActionWithValue*>(c[i]);
     223       19035 :           if(!action) {
     224           1 :             std::string str=" (hint! the actions with value in this ActionSet are: ";
     225           2 :             str+=as.getLabelList<ActionWithValue*>()+")";
     226           3 :             readact->error("cannot find action named " + c[i] + str );
     227             :           }
     228       19034 :           if( !(action->exists(c[i])) ) {
     229           0 :             std::string str=" (hint! the components in this actions are: ";
     230           0 :             str+=action->getComponentsList()+")";
     231           0 :             readact->error("action " + c[i] + " has no component named " + c[i] +str);
     232             :           };
     233       19035 :           arg.push_back(action->copyOutput(c[i]));
     234             :         }
     235             :       }
     236             :     }
     237             :   }
     238       15384 : }
     239             : 
     240           0 : void ActionWithArguments::expandArgKeywordInPDB( const PDB& pdb ) {
     241           0 :   std::vector<std::string> arg_names = pdb.getArgumentNames();
     242           0 :   if( arg_names.size()>0 ) {
     243             :     std::vector<Value*> arg_vals;
     244           0 :     interpretArgumentList( arg_names, plumed.getActionSet(), this, arg_vals );
     245             :   }
     246           0 : }
     247             : 
     248      186077 : void ActionWithArguments::requestArguments(const std::vector<Value*> &arg) {
     249      186077 :   plumed_massert(!lockRequestArguments,"requested argument list can only be changed in the prepare() method");
     250      186077 :   arguments=arg;
     251      186077 :   clearDependencies();
     252             :   std::string fullname;
     253             :   std::string name;
     254     1276136 :   for(unsigned i=0; i<arguments.size(); i++) {
     255     1090059 :     fullname=arguments[i]->getName();
     256     1090059 :     if(fullname.find(".")!=std::string::npos) {
     257             :       std::size_t dot=fullname.find_first_of('.');
     258      746184 :       name=fullname.substr(0,dot);
     259             :     } else {
     260             :       name=fullname;
     261             :     }
     262     1090059 :     ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(name);
     263     1090059 :     plumed_massert(action,"cannot find action named (in requestArguments - this is weird)" + name);
     264     1090059 :     addDependency(action);
     265             :   }
     266      186077 :   ActionWithValue* av=dynamic_cast<ActionWithValue*>(this);
     267      186077 :   if(av) {
     268      176400 :     av->firststep=true;
     269             :   }
     270      186077 : }
     271             : 
     272           4 : void ActionWithArguments::requestExtraDependencies(const std::vector<Value*> &extra) {
     273           4 :   plumed_massert(!lockRequestArguments,"requested argument list can only be changed in the prepare() method");
     274             :   std::string fullname;
     275             :   std::string name;
     276           9 :   for(unsigned i=0; i<extra.size(); i++) {
     277           5 :     fullname=extra[i]->getName();
     278           5 :     if(fullname.find(".")!=std::string::npos) {
     279             :       std::size_t dot=fullname.find_first_of('.');
     280           0 :       name=fullname.substr(0,dot);
     281             :     } else {
     282             :       name=fullname;
     283             :     }
     284           5 :     ActionWithValue* action=plumed.getActionSet().selectWithLabel<ActionWithValue*>(name);
     285           5 :     plumed_massert(action,"cannot find action named (in requestArguments - this is weird)" + name);
     286           5 :     addDependency(action);
     287             :   }
     288           4 : }
     289             : 
     290       10345 : ActionWithArguments::ActionWithArguments(const ActionOptions&ao):
     291             :   Action(ao),
     292       10345 :   lockRequestArguments(false) {
     293       20690 :   if( keywords.exists("ARG") ) {
     294             :     std::vector<Value*> arg;
     295       19332 :     parseArgumentList("ARG",arg);
     296             : 
     297        9666 :     if(!arg.empty()) {
     298        9328 :       log.printf("  with arguments : \n");
     299       45663 :       for(unsigned i=0; i<arg.size(); i++) {
     300       36335 :         if( arg[i]->hasDerivatives() && arg[i]->getRank()>0 ) {
     301        1009 :           log.printf(" function on grid with label %s \n",arg[i]->getName().c_str());
     302       35326 :         } else if( arg[i]->getRank()==2 ) {
     303        2611 :           log.printf("   matrix with label %s \n",arg[i]->getName().c_str());
     304       32715 :         } else if( arg[i]->getRank()==1 ) {
     305        6135 :           log.printf("   vector with label %s \n",arg[i]->getName().c_str());
     306       26580 :         } else if( arg[i]->getRank()==0 ) {
     307       26580 :           log.printf("   scalar with label %s \n",arg[i]->getName().c_str());
     308             :         } else {
     309           0 :           error("type of argument does not make sense");
     310             :         }
     311             :       }
     312             :     }
     313        9666 :     requestArguments(arg);
     314             :   }
     315       10345 : }
     316             : 
     317          58 : void ActionWithArguments::calculateNumericalDerivatives( ActionWithValue* a ) {
     318          58 :   if(!a) {
     319          58 :     a=castToActionWithValue();
     320          58 :     plumed_massert(a,"cannot compute numerical derivatives for an action without values");
     321             :   }
     322             : 
     323          58 :   const size_t nval=a->getNumberOfComponents();
     324             :   const size_t npar=arguments.size();
     325          58 :   std::vector<double> value (nval*npar);
     326         161 :   for(int i=0; i<npar; i++) {
     327         103 :     double arg0=arguments[i]->get();
     328         103 :     arguments[i]->set(arg0+std::sqrt(epsilon));
     329         103 :     a->calculate();
     330         103 :     arguments[i]->set(arg0);
     331        1367 :     for(int j=0; j<nval; j++) {
     332        1264 :       value[i*nval+j]=a->getOutputQuantity(j);
     333             :     }
     334             :   }
     335          58 :   a->calculate();
     336          58 :   a->clearDerivatives();
     337        1192 :   for(int j=0; j<nval; j++) {
     338        1134 :     Value* v=a->copyOutput(j);
     339        1134 :     if( v->hasDerivatives() )
     340         670 :       for(int i=0; i<npar; i++) {
     341         400 :         v->addDerivative(i,(value[i*nval+j]-a->getOutputQuantity(j))/std::sqrt(epsilon));
     342             :       }
     343             :   }
     344          58 : }
     345             : 
     346         261 : double ActionWithArguments::getProjection(unsigned i,unsigned j)const {
     347         261 :   plumed_massert(i<arguments.size()," making projections with an index which  is too large");
     348         261 :   plumed_massert(j<arguments.size()," making projections with an index which  is too large");
     349         261 :   const Value* v1=arguments[i];
     350         261 :   const Value* v2=arguments[j];
     351         261 :   return Value::projection(*v1,*v2);
     352             : }
     353             : 
     354      201798 : void ActionWithArguments::addForcesOnArguments( const unsigned& argstart, const std::vector<double>& forces, unsigned& ind, const std::string& c  ) {
     355      529572 :   for(unsigned i=0; i<arguments.size(); ++i) {
     356      327774 :     if( i==0 && getName().find("EVALUATE_FUNCTION_FROM_GRID")!=std::string::npos ) {
     357        3350 :       continue ;
     358             :     }
     359      324424 :     if( !arguments[i]->ignoreStoredValue(c) || arguments[i]->getRank()==0 || (arguments[i]->getRank()>0 && arguments[i]->hasDerivatives()) ) {
     360      302069 :       unsigned nvals = arguments[i]->getNumberOfStoredValues();
     361    33595635 :       for(unsigned j=0; j<nvals; ++j) {
     362    33293566 :         arguments[i]->addForce( j, forces[ind], false );
     363    33293566 :         ind++;
     364             :       }
     365             :     }
     366             :   }
     367      201798 : }
     368             : 
     369          83 : void ActionWithArguments::setGradients( Value* myval, unsigned& start ) const {
     370          83 :   if( !myval->hasDeriv ) {
     371             :     return;
     372             :   }
     373          83 :   plumed_assert( myval->getRank()==0 );
     374             : 
     375             :   bool scalar=true;
     376         249 :   for(unsigned i=0; i<arguments.size(); ++i ) {
     377         166 :     if( arguments[i]->getRank()!=0 ) {
     378             :       scalar=false;
     379             :       break;
     380             :     }
     381             :   }
     382          83 :   if( !scalar ) {
     383             :     bool constant=true;
     384           0 :     for(unsigned i=0; i<arguments.size(); ++i ) {
     385           0 :       if( !arguments[i]->isConstant() ) {
     386             :         constant=false;
     387             :         break;
     388             :       } else {
     389           0 :         start += arguments[i]->getNumberOfValues();
     390             :       }
     391             :     }
     392           0 :     if( !constant ) {
     393           0 :       error("cannot set gradient as unable to handle non-constant actions that take vectors/matrices/grids in input");
     394             :     }
     395             :   }
     396             :   // Now pass the gradients
     397         249 :   for(unsigned i=0; i<arguments.size(); ++i ) {
     398         166 :     arguments[i]->passGradients( myval->getDerivative(i), myval->gradients );
     399             :   }
     400             : }
     401             : 
     402       18273 : bool ActionWithArguments::calculateConstantValues( const bool& haveatoms ) {
     403       18273 :   ActionWithValue* av = castToActionWithValue();
     404       18273 :   if( !av || arguments.size()==0 ) {
     405             :     return false;
     406             :   }
     407             :   bool constant = true, atoms=false;
     408       14792 :   for(unsigned i=0; i<arguments.size(); ++i) {
     409       13680 :     auto * ptr=arguments[i]->getPntrToAction();
     410       13680 :     plumed_assert(ptr); // needed for following calls, see #1046
     411       13680 :     ActionAtomistic* aa=ptr->castToActionAtomistic();
     412       13680 :     if( aa ) {
     413       10796 :       ActionWithVector* av=dynamic_cast<ActionWithVector*>( arguments[i]->getPntrToAction() );
     414       10796 :       if( !av || aa->getNumberOfAtoms()>0 ) {
     415             :         atoms=true;
     416             :       }
     417             :     }
     418       13680 :     if( !arguments[i]->isConstant() ) {
     419             :       constant=false;
     420             :       break;
     421             :     }
     422             :   }
     423       13432 :   if( constant ) {
     424             :     // Set everything constant first as we need to set the shape
     425        2248 :     for(unsigned i=0; i<av->getNumberOfComponents(); ++i) {
     426        1136 :       (av->copyOutput(i))->setConstant();
     427             :     }
     428        1112 :     if( !haveatoms ) {
     429        1094 :       log.printf("  values stored by this action are computed during startup and stay fixed during the simulation\n");
     430             :     }
     431        1112 :     if( atoms ) {
     432          36 :       return haveatoms;
     433             :     }
     434             :   }
     435             :   // Now do the calculation and store the values if we don't need anything from the atoms
     436       13396 :   if( constant && !haveatoms ) {
     437        1076 :     plumed_assert( !atoms );
     438        1076 :     activate();
     439        1076 :     calculate();
     440        1076 :     deactivate();
     441        2176 :     for(unsigned i=0; i<av->getNumberOfComponents(); ++i) {
     442        1100 :       unsigned nv = av->copyOutput(i)->getNumberOfValues();
     443        1100 :       log.printf("  %d values stored in component labelled %s are : ", nv, (av->copyOutput(i))->getName().c_str() );
     444        3939 :       for(unsigned j=0; j<nv; ++j) {
     445        2839 :         log.printf(" %f", (av->copyOutput(i))->get(j) );
     446             :       }
     447        1100 :       log.printf("\n");
     448             :     }
     449             :   }
     450             :   return constant;
     451             : }
     452             : 
     453             : }

Generated by: LCOV version 1.16