LCOV - code coverage report
Current view: top level - core - ActionWithValue.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 182 198 91.9 %
Date: 2026-03-30 11:13:23 Functions: 28 33 84.8 %

          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 "ActionWithValue.h"
      23             : #include "ActionWithArguments.h"
      24             : #include "ActionAtomistic.h"
      25             : #include "tools/Exception.h"
      26             : #include "tools/OpenMP.h"
      27             : #include "tools/Communicator.h"
      28             : #include "blas/blas.h"
      29             : 
      30             : namespace PLMD {
      31             : 
      32       33187 : void ActionWithValue::registerKeywords(Keywords& keys) {
      33       33187 :   keys.setComponentsIntroduction("By default the value of the calculated quantity can be referenced elsewhere in the "
      34             :                                  "input file by using the label of the action.  Alternatively this Action can be used "
      35             :                                  "to calculate the following quantities by employing the keywords listed "
      36             :                                  "below.  These quantities can be referenced elsewhere in the input by using this Action's "
      37             :                                  "label followed by a dot and the name of the quantity required from the list below.");
      38       66374 :   keys.addFlag("NUMERICAL_DERIVATIVES", false, "calculate the derivatives for these quantities numerically");
      39       66374 :   keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
      40       33187 : }
      41             : 
      42           0 : void ActionWithValue::noAnalyticalDerivatives(Keywords& keys) {
      43           0 :   keys.remove("NUMERICAL_DERIVATIVES");
      44           0 :   keys.addFlag("NUMERICAL_DERIVATIVES",false,"analytical derivatives are not implemented for this keyword so numerical derivatives are always used");
      45           0 : }
      46             : 
      47         882 : void ActionWithValue::useCustomisableComponents(Keywords& keys) {
      48        1764 :   if( !keys.outputComponentExists(".#!custom") ) {
      49        1764 :     keys.addOutputComponent(".#!custom","default","the names of the output components for this action depend on the actions input file see the example inputs below for details");
      50             :   }
      51         882 :   keys.setComponentsIntroduction("The names of the components in this action can be customized by the user in the "
      52             :                                  "actions input file.  However, in addition to the components that can be customized the "
      53             :                                  "following quantities will always be output");
      54         882 : }
      55             : 
      56       31405 : ActionWithValue::ActionWithValue(const ActionOptions&ao):
      57             :   Action(ao),
      58       31405 :   firststep(true),
      59       31405 :   noderiv(true),
      60       31405 :   numericalDerivatives(false) {
      61       62810 :   if( keywords.exists("NUMERICAL_DERIVATIVES") ) {
      62       32696 :     parseFlag("NUMERICAL_DERIVATIVES",numericalDerivatives);
      63             :   }
      64       62810 :   if(!keywords.exists("NO_ACTION_LOG") && numericalDerivatives) {
      65          66 :     log.printf("  using numerical derivatives\n");
      66             :   }
      67       31405 : }
      68             : 
      69       31405 : ActionWithValue::~ActionWithValue() {
      70             : // empty destructor to delete unique_ptr
      71       31405 : }
      72             : 
      73     2428875 : void ActionWithValue::clearInputForces( const bool& force ) {
      74     5713021 :   for(unsigned i=0; i<values.size(); i++) {
      75             :     values[i]->clearInputForce();
      76             :   }
      77     2428875 : }
      78             : 
      79     1893270 : void ActionWithValue::clearDerivatives( const bool& force ) {
      80     1893270 :   unsigned nt = OpenMP::getNumThreads();
      81     1893270 :   #pragma omp parallel num_threads(nt)
      82             :   {
      83             :     #pragma omp for
      84             :     for(unsigned i=0; i<values.size(); i++) {
      85             :       values[i]->clearDerivatives();
      86             :     }
      87             :   }
      88     1893270 : }
      89             : 
      90             : // -- These are the routine for copying the value pointers to other classes -- //
      91             : 
      92    11841939 : bool ActionWithValue::exists( const std::string& name ) const {
      93   107520023 :   for(unsigned i=0; i<values.size(); ++i) {
      94    95702965 :     if (values[i]->name==name) {
      95             :       return true;
      96             :     }
      97             :   }
      98             :   return false;
      99             : }
     100             : 
     101          19 : void ActionWithValue::getMatrixColumnTitles( std::vector<std::string>& argnames ) const {
     102          19 :   plumed_assert( getNumberOfComponents()==1 && getConstPntrToComponent(0)->getRank()==2 );
     103          19 :   unsigned nargs = getConstPntrToComponent(0)->getShape()[1];
     104          19 :   std::string aname = getConstPntrToComponent(0)->getName();
     105         206 :   for(unsigned j=0; j<nargs; ++j) {
     106             :     std::string nn;
     107         187 :     Tools::convert( j+1, nn );
     108         374 :     argnames.push_back( aname + "." + nn );
     109             :   }
     110          19 : }
     111             : 
     112    33372905 : Value* ActionWithValue::copyOutput( const std::string& name ) const {
     113   111339166 :   for(unsigned i=0; i<values.size(); ++i) {
     114   111339166 :     if (values[i]->name==name) {
     115    33372905 :       return values[i].get();
     116             :     }
     117             :   }
     118           0 :   plumed_merror("there is no pointer with name " + name);
     119             : }
     120             : 
     121     1152736 : Value* ActionWithValue::copyOutput( const unsigned& n ) const {
     122     1152736 :   plumed_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     123     1152736 :   return values[n].get();
     124             : }
     125             : 
     126             : // -- HERE WE HAVE THE STUFF FOR THE DEFAULT VALUE -- //
     127             : 
     128       12920 : void ActionWithValue::addValue( const std::vector<unsigned>& shape ) {
     129       25840 :   if( !keywords.outputComponentExists(".#!value") ) {
     130         138 :     warning("documentation for the value calculated by this action has not been included");
     131             :   }
     132       12920 :   plumed_massert(values.empty(),"You have already added the default value for this action");
     133       12920 :   values.emplace_back(Tools::make_unique<Value>(this,getLabel(), false, shape ) );
     134       12920 : }
     135             : 
     136        5158 : void ActionWithValue::addValueWithDerivatives( const std::vector<unsigned>& shape ) {
     137       10316 :   if( !keywords.outputComponentExists(".#!value") ) {
     138         104 :     warning("documentation for the value calculated by this action has not been included");
     139             :   }
     140        5158 :   plumed_massert(values.empty(),"You have already added the default value for this action");
     141        5158 :   values.emplace_back(Tools::make_unique<Value>(this,getLabel(), true, shape ) );
     142        5158 : }
     143             : 
     144       17252 : void ActionWithValue::setNotPeriodic() {
     145       17252 :   plumed_massert(values.size()==1,"The number of components is not equal to one");
     146       17252 :   plumed_massert(values[0]->name==getLabel(), "The value you are trying to set is not the default");
     147       17252 :   values[0]->min=0;
     148       17252 :   values[0]->max=0;
     149       17252 :   values[0]->setupPeriodicity();
     150       17252 : }
     151             : 
     152         825 : void ActionWithValue::setPeriodic( const std::string& min, const std::string& max ) {
     153         825 :   plumed_massert(values.size()==1,"The number of components is not equal to one");
     154         825 :   plumed_massert(values[0]->name==getLabel(), "The value you are trying to set is not the default");
     155         825 :   values[0]->setDomain( min, max );
     156         825 : }
     157             : 
     158             : // -- HERE WE HAVE THE STUFF FOR NAMED VALUES / COMPONENTS -- //
     159             : 
     160       45827 : void ActionWithValue::addComponent( const std::string& name, const std::vector<unsigned>& shape ) {
     161       45827 :   if( !keywords.outputComponentExists(name) ) {
     162           0 :     plumed_merror("a description of component " + name + " has not been added to the manual. Components should be registered like keywords in "
     163             :                   "registerKeywords as described in the developer docs.");
     164             :   }
     165             :   std::string thename;
     166       91654 :   thename=getLabel() + "." + name;
     167    18798887 :   for(unsigned i=0; i<values.size(); ++i) {
     168    18753060 :     plumed_massert(values[i]->name!=getLabel(),"Cannot mix single values with components");
     169    18753060 :     plumed_massert(values[i]->name!=thename,"there is already a value with this name: "+thename);
     170    18753060 :     plumed_massert(values[i]->name!=thename&&name!="bias","Since PLUMED 2.3 the component 'bias' is automatically added to all biases by the general constructor!\n"
     171             :                    "Remove the line addComponent(\"bias\") from your bias.");
     172             :   }
     173       45827 :   values.emplace_back(Tools::make_unique<Value>(this,thename, false, shape ) );
     174       45827 :   std::string msg="  added component to this action:  "+thename+" \n";
     175       45827 :   log.printf(msg.c_str());
     176       45827 : }
     177             : 
     178       41426 : void ActionWithValue::addComponentWithDerivatives( const std::string& name, const std::vector<unsigned>& shape ) {
     179       41426 :   if( !keywords.outputComponentExists(name) ) {
     180           0 :     plumed_merror("a description of component " + name + " has not been added to the manual. Components should be registered like keywords in "
     181             :                   "registerKeywords as described in the developer doc.");
     182             :   }
     183             :   std::string thename;
     184       82852 :   thename=getLabel() + "." + name;
     185     2504040 :   for(unsigned i=0; i<values.size(); ++i) {
     186     2462614 :     plumed_massert(values[i]->name!=getLabel(),"Cannot mix single values with components");
     187     2462614 :     plumed_massert(values[i]->name!=thename,"there is already a value with this name: "+thename);
     188     2462614 :     plumed_massert(values[i]->name!=thename&&name!="bias","Since PLUMED 2.3 the component 'bias' is automatically added to all biases by the general constructor!\n"
     189             :                    "Remove the line addComponentWithDerivatives(\"bias\") from your bias.");
     190             :   }
     191       41426 :   values.emplace_back(Tools::make_unique<Value>(this,thename, true, shape ) );
     192       41426 :   std::string msg="  added component to this action:  "+thename+" \n";
     193       41426 :   log.printf(msg.c_str());
     194       41426 : }
     195             : 
     196          23 : std::string ActionWithValue::getOutputComponentDescription( const std::string& cname, const Keywords& keys ) const {
     197          46 :   if( keys.outputComponentExists(".#!custom") ) {
     198           0 :     return "a quantity calculated by the action " + getName() + " with label " + getLabel();
     199             :   }
     200          23 :   std::size_t und=cname.find_last_of("_");
     201          23 :   std::size_t hyph=cname.find_first_of("-");
     202          23 :   if( und!=std::string::npos ) {
     203           0 :     return keys.getOutputComponentDescription(cname.substr(und)) + " This particular component measures this quantity for the input CV named " + cname.substr(0,und);
     204             :   }
     205          23 :   if( hyph!=std::string::npos ) {
     206          14 :     return keys.getOutputComponentDescription(cname.substr(0,hyph)) + "  This is the " + cname.substr(hyph+1) + "th of these quantities";
     207             :   }
     208          16 :   plumed_massert( keys.outputComponentExists(cname), "component " + cname + " does not exist in " + keys.getDisplayName() + " if the component names are customizable then you should override this function" );
     209          16 :   return keys.getOutputComponentDescription( cname );
     210             : }
     211             : 
     212    11816445 : int ActionWithValue::getComponent( const std::string& name ) const {
     213    11816445 :   plumed_massert( !exists( getLabel() ), "You should not be calling this routine if you are using a value");
     214             :   std::string thename;
     215    23632890 :   thename=getLabel() + "." + name;
     216    65542053 :   for(unsigned i=0; i<values.size(); ++i) {
     217    65542053 :     if (values[i]->name==thename) {
     218    11816445 :       return i;
     219             :     }
     220             :   }
     221           0 :   plumed_merror("there is no component with name " + name);
     222             : }
     223             : 
     224           0 : std::string ActionWithValue::getComponentsList( ) const {
     225             :   std::string complist;
     226           0 :   for(unsigned i=0; i<values.size(); ++i) {
     227           0 :     complist+=values[i]->name+" ";
     228             :   }
     229           0 :   return complist;
     230             : }
     231             : 
     232       24075 : std::vector<std::string> ActionWithValue::getComponentsVector( ) const {
     233             :   std::vector<std::string> complist;
     234      349845 :   for(unsigned i=0; i<values.size(); ++i) {
     235      325770 :     complist.push_back(values[i]->name);
     236             :   }
     237       24075 :   return complist;
     238           0 : }
     239             : 
     240       84033 : void ActionWithValue::componentIsNotPeriodic( const std::string& name ) {
     241       84033 :   int kk=getComponent(name);
     242       84033 :   values[kk]->min=0;
     243       84033 :   values[kk]->max=0;
     244       84033 :   values[kk]->setupPeriodicity();
     245       84033 : }
     246             : 
     247         139 : void ActionWithValue::componentIsPeriodic( const std::string& name, const std::string& min, const std::string& max ) {
     248         139 :   int kk=getComponent(name);
     249         139 :   values[kk]->setDomain(min,max);
     250         139 : }
     251             : 
     252     1858602 : void ActionWithValue::setGradientsIfNeeded() {
     253     3717204 :   if(isOptionOn("GRADIENTS")) {
     254         201 :     ActionAtomistic* aa=castToActionAtomistic();
     255         201 :     if(aa) {
     256         308 :       for(unsigned i=0; i<values.size(); i++) {
     257         190 :         unsigned start=0;
     258             :         values[i]->gradients.clear();
     259         190 :         values[i]->setGradients( aa, start );
     260             :       }
     261             :     } else {
     262          83 :       ActionWithArguments* aarg = castToActionWithArguments();
     263          83 :       if( !aarg ) {
     264           0 :         plumed_merror( "failing in " + getLabel() );
     265             :       }
     266         166 :       for(unsigned i=0; i<values.size(); i++) {
     267          83 :         unsigned start=0;
     268             :         values[i]->gradients.clear();
     269          83 :         aarg->setGradients( values[i].get(), start );
     270             :       }
     271             :     }
     272             :   }
     273     1858602 : }
     274             : 
     275     1642126 : void ActionWithValue::turnOnDerivatives() {
     276             :   // Turn on the derivatives
     277     1642126 :   noderiv=false;
     278             :   // Resize the derivatives
     279     5997939 :   for(unsigned i=0; i<values.size(); ++i) {
     280     4355813 :     values[i]->resizeDerivatives( getNumberOfDerivatives() );
     281             :   }
     282             :   // And turn on the derivatives in all actions on which we are dependent
     283     3278154 :   for(unsigned i=0; i<getDependencies().size(); ++i) {
     284     1636028 :     ActionWithValue* vv=getDependencies()[i]->castToActionWithValue();
     285     1636028 :     if(vv) {
     286     1636028 :       vv->turnOnDerivatives();
     287             :     }
     288             :   }
     289     1642126 : }
     290             : 
     291    11732273 : Value* ActionWithValue::getPntrToComponent( const std::string& name ) {
     292    11732273 :   int kk=getComponent(name);
     293    11732273 :   return values[kk].get();
     294             : }
     295             : 
     296  1199391010 : const Value* ActionWithValue::getConstPntrToComponent(int n) const {
     297             :   plumed_dbg_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     298  1199391010 :   return values[n].get();
     299             : }
     300             : 
     301    87512997 : Value* ActionWithValue::getPntrToComponent( int n ) {
     302             :   plumed_dbg_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     303    87512997 :   return values[n].get();
     304             : }
     305             : 
     306     4655603 : bool ActionWithValue::calculateOnUpdate() {
     307     4655603 :   if( firststep ) {
     308      205980 :     ActionWithArguments* aa=dynamic_cast<ActionWithArguments*>(this);
     309      205980 :     if(aa) {
     310      184050 :       const std::vector<Value*> & args(aa->getArguments());
     311     1261770 :       for(const auto & p : args ) {
     312     1078134 :         if( p->calculateOnUpdate() ) {
     313         869 :           for(unsigned i=0; i<values.size(); ++i) {
     314         910 :             values[i]->setValType("calcFromAverage");
     315             :           }
     316             :           break;
     317             :         }
     318             :       }
     319             :     }
     320      205980 :     firststep=false;
     321             :   }
     322    11149540 :   for(unsigned i=0; i<values.size(); ++i) {
     323     6502795 :     if( values[i]->calculateOnUpdate() ) {
     324             :       return true;
     325             :     }
     326             :   }
     327             :   return false;
     328             : }
     329             : 
     330      502958 : bool ActionWithValue::checkForForces() {
     331      502958 :   const unsigned    ncp=getNumberOfComponents();
     332      502958 :   unsigned    nder=getNumberOfDerivatives();
     333      502958 :   if( ncp==0 || nder==0 ) {
     334             :     return false;
     335             :   }
     336             : 
     337             :   unsigned nvalsWithForce=0;
     338      501812 :   valsToForce.resize(ncp);
     339     1402576 :   for(unsigned i=0; i<ncp; ++i) {
     340      900764 :     if( values[i]->hasForce && !values[i]->isConstant() ) {
     341      292738 :       valsToForce[nvalsWithForce]=i;
     342      292738 :       nvalsWithForce++;
     343             :     }
     344             :   }
     345      501812 :   if( nvalsWithForce==0 ) {
     346             :     return false;
     347             :   }
     348             : 
     349             :   // Make sure forces to apply is empty of forces
     350      241999 :   if( forcesForApply.size()!=nder ) {
     351        3679 :     forcesForApply.resize( nder );
     352             :   }
     353             :   std::fill(forcesForApply.begin(),forcesForApply.end(),0);
     354             : 
     355             :   unsigned stride=1;
     356             :   unsigned rank=0;
     357      241999 :   if(ncp>4*comm.Get_size()) {
     358       22970 :     stride=comm.Get_size();
     359       22970 :     rank=comm.Get_rank();
     360             :   }
     361             : 
     362      241999 :   unsigned nt=OpenMP::getNumThreads();
     363      241999 :   if(nt>ncp/(4*stride)) {
     364             :     nt=1;
     365             :   }
     366             : 
     367      241999 :   #pragma omp parallel num_threads(nt)
     368             :   {
     369             :     std::vector<double> omp_f;
     370             :     if( nt>1 ) {
     371             :       omp_f.resize(nder,0);
     372             :     }
     373             :     #pragma omp for
     374             :     for(unsigned i=rank; i<nvalsWithForce; i+=stride) {
     375             :       double ff=values[valsToForce[i]]->inputForce[0];
     376             :       std::vector<double> & thisderiv( values[valsToForce[i]]->data );
     377             :       int nn=nder;
     378             :       int one1=1;
     379             :       int one2=1;
     380             :       if( nt>1 ) {
     381             :         plumed_blas_daxpy(&nn,&ff,thisderiv.data()+1,&one1,omp_f.data(),&one2);
     382             :       } else {
     383             :         plumed_blas_daxpy(&nn,&ff,thisderiv.data()+1,&one1,forcesForApply.data(),&one2);
     384             :       }
     385             :       // if( nt>1 ) for(unsigned j=0; j<nder; ++j) omp_f[j] += ff*thisderiv[1+j];
     386             :       //else for(unsigned j=0; j<nder; ++j) forcesForApply[j] += ff*thisderiv[1+j];
     387             :     }
     388             :     #pragma omp critical
     389             :     {
     390             :       if( nt>1 ) {
     391             :         int nn=forcesForApply.size();
     392             :         double one0=1.0;
     393             :         int one1=1;
     394             :         int one2=1;
     395             :         plumed_blas_daxpy(&nn,&one0,omp_f.data(),&one1,forcesForApply.data(),&one2);
     396             :       }
     397             :       // for(unsigned j=0; j<forcesForApply.size(); ++j) {
     398             :       // forcesForApply[j]+=omp_f[j];
     399             :       // }
     400             :     }
     401             :   }
     402             : 
     403      241999 :   if(ncp>4*comm.Get_size()) {
     404       22970 :     comm.Sum(&forcesForApply[0],nder);
     405             :   }
     406             :   return true;
     407             : }
     408             : 
     409             : }

Generated by: LCOV version 1.16