LCOV - code coverage report
Current view: top level - core - ActionWithValue.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 184 200 92.0 %
Date: 2025-12-04 11:19:34 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       30576 : void ActionWithValue::registerKeywords(Keywords& keys) {
      33       61152 :   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       30576 :   keys.addFlag("NUMERICAL_DERIVATIVES", false, "calculate the derivatives for these quantities numerically");
      39       30576 :   keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
      40       30576 : }
      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        2347 : void ActionWithValue::useCustomisableComponents(Keywords& keys) {
      48        4694 :   if( !keys.outputComponentExists(".#!custom") ) {
      49        3218 :     keys.addOutputComponent(".#!custom","default","scalar","the names of the output components for this action depend on the actions input file see the example inputs below for details");
      50             :   }
      51        2347 :   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        2347 : }
      55             : 
      56       30583 : ActionWithValue::ActionWithValue(const ActionOptions&ao):
      57             :   Action(ao),
      58       30583 :   firststep(true),
      59       30583 :   noderiv(true),
      60       30583 :   numericalDerivatives(false) {
      61       30583 :   if( keywords.exists("NUMERICAL_DERIVATIVES") ) {
      62       13782 :     parseFlag("NUMERICAL_DERIVATIVES",numericalDerivatives);
      63             :   }
      64       30583 :   if(!keywords.exists("NO_ACTION_LOG") && numericalDerivatives) {
      65          66 :     log.printf("  using numerical derivatives\n");
      66             :   }
      67       30583 : }
      68             : 
      69       30583 : ActionWithValue::~ActionWithValue() {
      70             : // empty destructor to delete unique_ptr
      71       30583 : }
      72             : 
      73     2413401 : void ActionWithValue::clearInputForces( const bool& force ) {
      74     5707680 :   for(unsigned i=0; i<values.size(); i++) {
      75             :     values[i]->clearInputForce();
      76             :   }
      77     2413401 : }
      78             : 
      79     1851182 : void ActionWithValue::clearDerivatives( const bool& force ) {
      80             : #ifdef _OPENMP
      81             :   //nt is unused if openmp is not declared
      82     1851182 :   const unsigned nt=OpenMP::getNumThreads();
      83             : #endif //_OPENMP
      84     1851182 :   #pragma omp parallel num_threads(nt)
      85             :   {
      86             :     #pragma omp for
      87             :     for(unsigned i=0; i<values.size(); i++) {
      88             :       values[i]->clearDerivatives();
      89             :     }
      90             :   }
      91     1851182 : }
      92             : 
      93             : // -- These are the routine for copying the value pointers to other classes -- //
      94             : 
      95    11840099 : bool ActionWithValue::exists( const std::string& valname ) const {
      96   107508540 :   for(unsigned i=0; i<values.size(); ++i) {
      97    95690604 :     if (values[i]->name==valname) {
      98             :       return true;
      99             :     }
     100             :   }
     101             :   return false;
     102             : }
     103             : 
     104          19 : void ActionWithValue::getMatrixColumnTitles( std::vector<std::string>& argnames ) const {
     105          19 :   plumed_assert( getNumberOfComponents()==1 && getConstPntrToComponent(0)->getRank()==2 );
     106          19 :   unsigned nargs = getConstPntrToComponent(0)->getShape()[1];
     107          19 :   std::string aname = getConstPntrToComponent(0)->getName();
     108         206 :   for(unsigned j=0; j<nargs; ++j) {
     109             :     std::string nn;
     110         187 :     Tools::convert( j+1, nn );
     111         374 :     argnames.push_back( aname + "." + nn );
     112             :   }
     113          19 : }
     114             : 
     115    33412553 : Value* ActionWithValue::copyOutput( const std::string& valname ) const {
     116   111471179 :   for(unsigned i=0; i<values.size(); ++i) {
     117   111471179 :     if (values[i]->name==valname) {
     118    33412553 :       return values[i].get();
     119             :     }
     120             :   }
     121           0 :   plumed_merror("there is no pointer with name " + valname);
     122             : }
     123             : 
     124     1338559 : Value* ActionWithValue::copyOutput( const unsigned& n ) const {
     125     1338559 :   plumed_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     126     1338559 :   return values[n].get();
     127             : }
     128             : 
     129             : // -- HERE WE HAVE THE STUFF FOR THE DEFAULT VALUE -- //
     130             : 
     131       13643 : void ActionWithValue::addValue( const std::vector<std::size_t>& shape ) {
     132       27286 :   if( !keywords.outputComponentExists(".#!value") ) {
     133          40 :     warning("documentation for the value calculated by this action has not been included");
     134             :   } else {
     135       27246 :     plumed_massert( keywords.componentHasCorrectType(".#!value",shape.size(),false), "documentation for type of value is incorrect");
     136             :   }
     137       13643 :   plumed_massert(values.empty(),"You have already added the default value for this action");
     138       13643 :   values.emplace_back(Tools::make_unique<Value>(this,getLabel(), false, shape ) );
     139       13643 : }
     140             : 
     141        3341 : void ActionWithValue::addValueWithDerivatives( const std::vector<std::size_t>& shape ) {
     142        6682 :   if( !keywords.outputComponentExists(".#!value") ) {
     143           4 :     warning("documentation for the value calculated by this action has not been included");
     144             :   } else {
     145        6678 :     plumed_massert( keywords.componentHasCorrectType(".#!value",shape.size(),true), "documentation for type of value is incorrect");
     146             :   }
     147        3341 :   plumed_massert(values.empty(),"You have already added the default value for this action");
     148        3341 :   values.emplace_back(Tools::make_unique<Value>(this,getLabel(), true, shape ) );
     149        3341 : }
     150             : 
     151       15870 : void ActionWithValue::setNotPeriodic() {
     152       15870 :   plumed_massert(values.size()==1,"The number of components is not equal to one");
     153       15870 :   plumed_massert(values[0]->name==getLabel(), "The value you are trying to set is not the default");
     154       15870 :   values[0]->min=0;
     155       15870 :   values[0]->max=0;
     156       15870 :   values[0]->setupPeriodicity();
     157       15870 : }
     158             : 
     159         843 : void ActionWithValue::setPeriodic( const std::string& min, const std::string& max ) {
     160         843 :   plumed_massert(values.size()==1,"The number of components is not equal to one");
     161         843 :   plumed_massert(values[0]->name==getLabel(), "The value you are trying to set is not the default");
     162         843 :   values[0]->setDomain( min, max );
     163         843 : }
     164             : 
     165             : // -- HERE WE HAVE THE STUFF FOR NAMED VALUES / COMPONENTS -- //
     166             : 
     167       46326 : void ActionWithValue::addComponent( const std::string& valname, const std::vector<std::size_t>& shape ) {
     168       46326 :   if( !keywords.outputComponentExists(valname) ) {
     169           0 :     plumed_merror("a description of component " + valname + " has not been added to the manual. Components should be registered like keywords in "
     170             :                   "registerKeywords as described in the developer docs.");
     171             :   }
     172       46326 :   plumed_massert( keywords.componentHasCorrectType(valname,shape.size(),false), "documentation for type of component " + valname + " is incorrect");
     173             :   std::string thename;
     174       92652 :   thename=getLabel() + "." + valname;
     175    18800954 :   for(unsigned i=0; i<values.size(); ++i) {
     176    18754628 :     plumed_massert(values[i]->name!=getLabel(),"Cannot mix single values with components");
     177    18754628 :     plumed_massert(values[i]->name!=thename,"there is already a value with this name: "+thename);
     178    18754628 :     plumed_massert(values[i]->name!=thename&&valname!="bias","Since PLUMED 2.3 the component 'bias' is automatically added to all biases by the general constructor!\n"
     179             :                    "Remove the line addComponent(\"bias\") from your bias.");
     180             :   }
     181       46326 :   values.emplace_back(Tools::make_unique<Value>(this,thename, false, shape ) );
     182       46326 :   log.printf("  added component to this action: %s \n", thename.c_str() );
     183       46326 : }
     184             : 
     185       42057 : void ActionWithValue::addComponentWithDerivatives( const std::string& valname, const std::vector<std::size_t>& shape ) {
     186       42057 :   if( !keywords.outputComponentExists(valname) ) {
     187           0 :     plumed_merror("a description of component " + valname + " has not been added to the manual. Components should be registered like keywords in "
     188             :                   "registerKeywords as described in the developer doc.");
     189             :   }
     190       42057 :   plumed_massert( keywords.componentHasCorrectType(valname,shape.size(),true), "documentation for type of component " + valname + " is incorrect");
     191             :   std::string thename;
     192       84114 :   thename=getLabel() + "." + valname;
     193     2505241 :   for(unsigned i=0; i<values.size(); ++i) {
     194     2463184 :     plumed_massert(values[i]->name!=getLabel(),"Cannot mix single values with components");
     195     2463184 :     plumed_massert(values[i]->name!=thename,"there is already a value with this name: "+thename);
     196     2463184 :     plumed_massert(values[i]->name!=thename&&valname!="bias","Since PLUMED 2.3 the component 'bias' is automatically added to all biases by the general constructor!\n"
     197             :                    "Remove the line addComponentWithDerivatives(\"bias\") from your bias.");
     198             :   }
     199       42057 :   values.emplace_back(Tools::make_unique<Value>(this,thename, true, shape ) );
     200       42057 :   log.printf("  added component to this action: %s \n", thename.c_str() );
     201       42057 : }
     202             : 
     203          20 : std::string ActionWithValue::getOutputComponentDescription( const std::string& cname, const Keywords& keys ) const {
     204          40 :   if( keys.outputComponentExists(".#!custom") ) {
     205           0 :     return "a quantity calculated by the action " + getName() + " with label " + getLabel();
     206             :   }
     207          20 :   std::size_t und=cname.find_last_of("_");
     208          20 :   std::size_t hyph=cname.find_first_of("-");
     209          20 :   if( und!=std::string::npos ) {
     210           0 :     return keys.getOutputComponentDescription(cname.substr(und)) + " This particular component measures this quantity for the input CV named " + cname.substr(0,und);
     211             :   }
     212          20 :   if( hyph!=std::string::npos ) {
     213          14 :     return keys.getOutputComponentDescription(cname.substr(0,hyph)) + "  This is the " + cname.substr(hyph+1) + "th of these quantities";
     214             :   }
     215          13 :   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" );
     216          13 :   return keys.getOutputComponentDescription( cname );
     217             : }
     218             : 
     219    11817306 : int ActionWithValue::getComponent( const std::string& valname ) const {
     220    11817306 :   plumed_massert( !exists( getLabel() ), "You should not be calling this routine if you are using a value");
     221             :   std::string thename;
     222    23634612 :   thename=getLabel() + "." + valname;
     223    65539434 :   for(unsigned i=0; i<values.size(); ++i) {
     224    65539434 :     if (values[i]->name==thename) {
     225    11817306 :       return i;
     226             :     }
     227             :   }
     228           0 :   plumed_merror("there is no component with name " + valname);
     229             : }
     230             : 
     231           0 : std::string ActionWithValue::getComponentsList( ) const {
     232             :   std::string complist;
     233           0 :   for(unsigned i=0; i<values.size(); ++i) {
     234           0 :     complist+=values[i]->name+" ";
     235             :   }
     236           0 :   return complist;
     237             : }
     238             : 
     239       24076 : std::vector<std::string> ActionWithValue::getComponentsVector( ) const {
     240             :   std::vector<std::string> complist;
     241      349847 :   for(unsigned i=0; i<values.size(); ++i) {
     242      325771 :     complist.push_back(values[i]->name);
     243             :   }
     244       24076 :   return complist;
     245           0 : }
     246             : 
     247       84450 : void ActionWithValue::componentIsNotPeriodic( const std::string& valname ) {
     248       84450 :   int kk=getComponent(valname);
     249       84450 :   values[kk]->min=0;
     250       84450 :   values[kk]->max=0;
     251       84450 :   values[kk]->setupPeriodicity();
     252       84450 : }
     253             : 
     254         139 : void ActionWithValue::componentIsPeriodic( const std::string& valname, const std::string& min, const std::string& max ) {
     255         139 :   int kk=getComponent(valname);
     256         139 :   values[kk]->setDomain(min,max);
     257         139 : }
     258             : 
     259     1886605 : void ActionWithValue::setGradientsIfNeeded() {
     260     3773210 :   if(isOptionOn("GRADIENTS")) {
     261         201 :     ActionAtomistic* aa=castToActionAtomistic();
     262         201 :     if(aa) {
     263         308 :       for(unsigned i=0; i<values.size(); i++) {
     264         190 :         unsigned start=0;
     265             :         values[i]->gradients.clear();
     266         190 :         values[i]->setGradients( aa, start );
     267             :       }
     268             :     } else {
     269          83 :       ActionWithArguments* aarg = castToActionWithArguments();
     270          83 :       if( !aarg ) {
     271           0 :         plumed_merror( "failing in " + getLabel() );
     272             :       }
     273         166 :       for(unsigned i=0; i<values.size(); i++) {
     274          83 :         unsigned start=0;
     275             :         values[i]->gradients.clear();
     276          83 :         aarg->setGradients( values[i].get(), start );
     277             :       }
     278             :     }
     279             :   }
     280     1886605 : }
     281             : 
     282     1652653 : void ActionWithValue::turnOnDerivatives() {
     283             :   // Turn on the derivatives
     284     1652653 :   noderiv=false;
     285             :   // Resize the derivatives
     286     6011471 :   for(unsigned i=0; i<values.size(); ++i) {
     287     4358818 :     values[i]->resizeDerivatives( getNumberOfDerivatives() );
     288             :   }
     289             :   // And turn on the derivatives in all actions on which we are dependent
     290     3299196 :   for(unsigned i=0; i<getDependencies().size(); ++i) {
     291     1646543 :     ActionWithValue* vv=getDependencies()[i]->castToActionWithValue();
     292     1646543 :     if(vv) {
     293     1646543 :       vv->turnOnDerivatives();
     294             :     }
     295             :   }
     296     1652653 : }
     297             : 
     298    11732717 : Value* ActionWithValue::getPntrToComponent( const std::string& valname ) {
     299    11732717 :   int kk=getComponent(valname);
     300    11732717 :   return values[kk].get();
     301             : }
     302             : 
     303    39657375 : const Value* ActionWithValue::getConstPntrToComponent(unsigned n) const {
     304             :   plumed_dbg_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     305    39657375 :   return values[n].get();
     306             : }
     307             : 
     308   139963366 : Value* ActionWithValue::getPntrToComponent( unsigned n ) {
     309             :   plumed_dbg_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     310   139963366 :   return values[n].get();
     311             : }
     312             : 
     313     4719781 : bool ActionWithValue::calculateOnUpdate() {
     314     4719781 :   if( firststep ) {
     315      205126 :     ActionWithArguments* aa=dynamic_cast<ActionWithArguments*>(this);
     316      205126 :     if(aa) {
     317      182752 :       const std::vector<Value*> & args(aa->getArguments());
     318     1259016 :       for(const auto & p : args ) {
     319     1076671 :         if( p->calculateOnUpdate() ) {
     320         855 :           for(unsigned i=0; i<values.size(); ++i) {
     321         896 :             values[i]->setValType("calcFromAverage");
     322             :           }
     323             :           break;
     324             :         }
     325             :       }
     326             :     }
     327      205126 :     firststep=false;
     328             :   }
     329    11325582 :   for(unsigned i=0; i<values.size(); ++i) {
     330     6614619 :     if( values[i]->calculateOnUpdate() ) {
     331             :       return true;
     332             :     }
     333             :   }
     334             :   return false;
     335             : }
     336             : 
     337      462923 : bool ActionWithValue::checkForForces() {
     338      462923 :   const unsigned    ncp=getNumberOfComponents();
     339      462923 :   unsigned    nder=getNumberOfDerivatives();
     340      462923 :   if( ncp==0 || nder==0 ) {
     341             :     return false;
     342             :   }
     343             : 
     344             :   unsigned nvalsWithForce=0;
     345      461777 :   valsToForce.resize(ncp);
     346     1338888 :   for(unsigned i=0; i<ncp; ++i) {
     347      877111 :     if( values[i]->hasForce && !values[i]->isConstant() ) {
     348      256383 :       valsToForce[nvalsWithForce]=i;
     349      256383 :       nvalsWithForce++;
     350             :     }
     351             :   }
     352      461777 :   if( nvalsWithForce==0 ) {
     353             :     return false;
     354             :   }
     355             : 
     356             :   // Make sure forces to apply is empty of forces
     357      205216 :   if( forcesForApply.size()!=nder ) {
     358        3428 :     forcesForApply.resize( nder );
     359             :   }
     360             :   std::fill(forcesForApply.begin(),forcesForApply.end(),0);
     361             : 
     362             :   unsigned stride=1;
     363             :   unsigned rank=0;
     364      205216 :   if(ncp>static_cast<unsigned>(4*comm.Get_size())) {
     365       22980 :     stride=comm.Get_size();
     366       22980 :     rank=comm.Get_rank();
     367             :   }
     368             : 
     369      205216 :   unsigned nt=OpenMP::getNumThreads();
     370      205216 :   if(nt>ncp/(4*stride)) {
     371             :     nt=1;
     372             :   }
     373             : 
     374      205216 :   #pragma omp parallel num_threads(nt)
     375             :   {
     376             :     std::vector<double> omp_f;
     377             :     if( nt>1 ) {
     378             :       omp_f.resize(nder,0);
     379             :     }
     380             :     #pragma omp for
     381             :     for(unsigned i=rank; i<nvalsWithForce; i+=stride) {
     382             :       double ff=values[valsToForce[i]]->inputForce[0];
     383             :       std::vector<double> & thisderiv( values[valsToForce[i]]->data );
     384             :       int nn=nder;
     385             :       int one1=1;
     386             :       int one2=1;
     387             :       if( nt>1 ) {
     388             :         plumed_blas_daxpy(&nn,&ff,thisderiv.data()+1,&one1,omp_f.data(),&one2);
     389             :       } else {
     390             :         plumed_blas_daxpy(&nn,&ff,thisderiv.data()+1,&one1,forcesForApply.data(),&one2);
     391             :       }
     392             :       // if( nt>1 ) for(unsigned j=0; j<nder; ++j) omp_f[j] += ff*thisderiv[1+j];
     393             :       //else for(unsigned j=0; j<nder; ++j) forcesForApply[j] += ff*thisderiv[1+j];
     394             :     }
     395             :     #pragma omp critical
     396             :     {
     397             :       if( nt>1 ) {
     398             :         int nn=forcesForApply.size();
     399             :         double one0=1.0;
     400             :         int one1=1;
     401             :         int one2=1;
     402             :         plumed_blas_daxpy(&nn,&one0,omp_f.data(),&one1,forcesForApply.data(),&one2);
     403             :       }
     404             :       // for(unsigned j=0; j<forcesForApply.size(); ++j) {
     405             :       // forcesForApply[j]+=omp_f[j];
     406             :       // }
     407             :     }
     408             :   }
     409             : 
     410      205216 :   if(ncp>static_cast<unsigned>(4*comm.Get_size())) {
     411       22980 :     comm.Sum(&forcesForApply[0],nder);
     412             :   }
     413             :   return true;
     414             : }
     415             : 
     416             : }

Generated by: LCOV version 1.16