LCOV - code coverage report
Current view: top level - core - ActionWithValue.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 162 175 92.6 %
Date: 2024-10-18 13:59:31 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       32340 : void ActionWithValue::registerKeywords(Keywords& keys) {
      33       32340 :   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       64680 :   keys.addFlag("NUMERICAL_DERIVATIVES", false, "calculate the derivatives for these quantities numerically");
      39       64680 :   keys.add("hidden","HAS_VALUES","this is used in json output to determine those actions that have values");
      40       32340 : }
      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         854 : void ActionWithValue::useCustomisableComponents(Keywords& keys) {
      48        2562 :   if( !keys.outputComponentExists(".#!custom") ) 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");
      49         854 :   keys.setComponentsIntroduction("The names of the components in this action can be customized by the user in the "
      50             :                                  "actions input file.  However, in addition to the components that can be customized the "
      51             :                                  "following quantities will always be output");
      52         854 : }
      53             : 
      54       31378 : ActionWithValue::ActionWithValue(const ActionOptions&ao):
      55             :   Action(ao),
      56       31378 :   firststep(true),
      57       31378 :   noderiv(true),
      58       31378 :   numericalDerivatives(false)
      59             : {
      60       78821 :   if( keywords.exists("NUMERICAL_DERIVATIVES") ) parseFlag("NUMERICAL_DERIVATIVES",numericalDerivatives);
      61       62756 :   if(!keywords.exists("NO_ACTION_LOG") && numericalDerivatives) log.printf("  using numerical derivatives\n");
      62       31378 : }
      63             : 
      64       31378 : ActionWithValue::~ActionWithValue() {
      65             : // empty destructor to delete unique_ptr
      66       31378 : }
      67             : 
      68     2409500 : void ActionWithValue::clearInputForces( const bool& force ) {
      69     5665575 :   for(unsigned i=0; i<values.size(); i++) values[i]->clearInputForce();
      70     2409500 : }
      71             : 
      72     1888715 : void ActionWithValue::clearDerivatives( const bool& force ) {
      73     1888715 :   unsigned nt = OpenMP::getNumThreads();
      74     1888715 :   #pragma omp parallel num_threads(nt)
      75             :   {
      76             :     #pragma omp for
      77             :     for(unsigned i=0; i<values.size(); i++) values[i]->clearDerivatives();
      78             :   }
      79     1888715 : }
      80             : 
      81             : // -- These are the routine for copying the value pointers to other classes -- //
      82             : 
      83    10717649 : bool ActionWithValue::exists( const std::string& name ) const {
      84   101878095 :   for(unsigned i=0; i<values.size(); ++i) {
      85    91185159 :     if (values[i]->name==name) return true;
      86             :   }
      87             :   return false;
      88             : }
      89             : 
      90          19 : void ActionWithValue::getMatrixColumnTitles( std::vector<std::string>& argnames ) const {
      91          19 :   plumed_assert( getNumberOfComponents()==1 && getConstPntrToComponent(0)->getRank()==2 );
      92          19 :   unsigned nargs = getConstPntrToComponent(0)->getShape()[1]; std::string aname = getConstPntrToComponent(0)->getName();
      93         393 :   for(unsigned j=0; j<nargs; ++j) { std::string nn; Tools::convert( j+1, nn ); argnames.push_back( aname + "." + nn ); }
      94          19 : }
      95             : 
      96    33605533 : Value* ActionWithValue::copyOutput( const std::string& name ) const {
      97   112036508 :   for(unsigned i=0; i<values.size(); ++i) {
      98   112036508 :     if (values[i]->name==name) return values[i].get();
      99             :   }
     100           0 :   plumed_merror("there is no pointer with name " + name);
     101             : }
     102             : 
     103     1143728 : Value* ActionWithValue::copyOutput( const unsigned& n ) const {
     104     1143728 :   plumed_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     105     1143728 :   return values[n].get();
     106             : }
     107             : 
     108             : // -- HERE WE HAVE THE STUFF FOR THE DEFAULT VALUE -- //
     109             : 
     110       12714 : void ActionWithValue::addValue( const std::vector<unsigned>& shape ) {
     111       25428 :   if( !keywords.outputComponentExists(".#!value") ) warning("documentation for the value calculated by this action has not been included");
     112       25428 :   else plumed_massert( keywords.componentHasCorrectType(".#!value",shape.size(),false), "documentation for type of value is incorrect");
     113       12714 :   plumed_massert(values.empty(),"You have already added the default value for this action");
     114       12714 :   values.emplace_back(Tools::make_unique<Value>(this,getLabel(), false, shape ) );
     115       12714 : }
     116             : 
     117        5142 : void ActionWithValue::addValueWithDerivatives( const std::vector<unsigned>& shape ) {
     118       10286 :   if( !keywords.outputComponentExists(".#!value") ) warning("documentation for the value calculated by this action has not been included");
     119       10280 :   else plumed_massert( keywords.componentHasCorrectType(".#!value",shape.size(),true), "documentation for type of value is incorrect");
     120        5142 :   plumed_massert(values.empty(),"You have already added the default value for this action");
     121        5142 :   values.emplace_back(Tools::make_unique<Value>(this,getLabel(), true, shape ) );
     122        5142 : }
     123             : 
     124       17030 : void ActionWithValue::setNotPeriodic() {
     125       17030 :   plumed_massert(values.size()==1,"The number of components is not equal to one");
     126       17030 :   plumed_massert(values[0]->name==getLabel(), "The value you are trying to set is not the default");
     127       17030 :   values[0]->min=0; values[0]->max=0;
     128       17030 :   values[0]->setupPeriodicity();
     129       17030 : }
     130             : 
     131         825 : void ActionWithValue::setPeriodic( const std::string& min, const std::string& max ) {
     132         825 :   plumed_massert(values.size()==1,"The number of components is not equal to one");
     133         825 :   plumed_massert(values[0]->name==getLabel(), "The value you are trying to set is not the default");
     134         825 :   values[0]->setDomain( min, max );
     135         825 : }
     136             : 
     137             : // -- HERE WE HAVE THE STUFF FOR NAMED VALUES / COMPONENTS -- //
     138             : 
     139       46056 : void ActionWithValue::addComponent( const std::string& name, const std::vector<unsigned>& shape ) {
     140       46056 :   if( !keywords.outputComponentExists(name) ) {
     141           0 :     plumed_merror("a description of component " + name + " has not been added to the manual. Components should be registered like keywords in "
     142             :                   "registerKeywords as described in the developer docs.");
     143             :   }
     144       46056 :   plumed_massert( keywords.componentHasCorrectType(name,shape.size(),false), "documentation for type of component " + name + " is incorrect");
     145       92112 :   std::string thename; thename=getLabel() + "." + name;
     146    18798986 :   for(unsigned i=0; i<values.size(); ++i) {
     147    18752930 :     plumed_massert(values[i]->name!=getLabel(),"Cannot mix single values with components");
     148    18752930 :     plumed_massert(values[i]->name!=thename,"there is already a value with this name: "+thename);
     149    18752930 :     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"
     150             :                    "Remove the line addComponent(\"bias\") from your bias.");
     151             :   }
     152       46056 :   values.emplace_back(Tools::make_unique<Value>(this,thename, false, shape ) );
     153       46056 :   std::string msg="  added component to this action:  "+thename+" \n";
     154       46056 :   log.printf(msg.c_str());
     155       46056 : }
     156             : 
     157       42173 : void ActionWithValue::addComponentWithDerivatives( const std::string& name, const std::vector<unsigned>& shape ) {
     158       42173 :   if( !keywords.outputComponentExists(name) ) {
     159           0 :     plumed_merror("a description of component " + name + " has not been added to the manual. Components should be registered like keywords in "
     160             :                   "registerKeywords as described in the developer doc.");
     161             :   }
     162       42173 :   plumed_massert( keywords.componentHasCorrectType(name,shape.size(),true), "documentation for type of component " + name + " is incorrect");
     163       84346 :   std::string thename; thename=getLabel() + "." + name;
     164     2505321 :   for(unsigned i=0; i<values.size(); ++i) {
     165     2463148 :     plumed_massert(values[i]->name!=getLabel(),"Cannot mix single values with components");
     166     2463148 :     plumed_massert(values[i]->name!=thename,"there is already a value with this name: "+thename);
     167     2463148 :     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"
     168             :                    "Remove the line addComponentWithDerivatives(\"bias\") from your bias.");
     169             :   }
     170       42173 :   values.emplace_back(Tools::make_unique<Value>(this,thename, true, shape ) );
     171       42173 :   std::string msg="  added component to this action:  "+thename+" \n";
     172       42173 :   log.printf(msg.c_str());
     173       42173 : }
     174             : 
     175          20 : std::string ActionWithValue::getOutputComponentDescription( const std::string& cname, const Keywords& keys ) const {
     176          40 :   if( keys.outputComponentExists(".#!custom") ) return "a quantity calculated by the action " + getName() + " with label " + getLabel();
     177          20 :   std::size_t und=cname.find_last_of("_"); std::size_t hyph=cname.find_first_of("-");
     178          20 :   if( und!=std::string::npos ) return keys.getOutputComponentDescription(cname.substr(und)) + " This particular component measures this quantity for the input CV named " + cname.substr(0,und);
     179          27 :   if( hyph!=std::string::npos ) return keys.getOutputComponentDescription(cname.substr(0,hyph)) + "  This is the " + cname.substr(hyph+1) + "th of these quantities";
     180          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" );
     181          13 :   return keys.getOutputComponentDescription( cname );
     182             : }
     183             : 
     184    10692363 : int ActionWithValue::getComponent( const std::string& name ) const {
     185    10692363 :   plumed_massert( !exists( getLabel() ), "You should not be calling this routine if you are using a value");
     186    21384726 :   std::string thename; thename=getLabel() + "." + name;
     187    64408643 :   for(unsigned i=0; i<values.size(); ++i) {
     188    64408643 :     if (values[i]->name==thename) return i;
     189             :   }
     190           0 :   plumed_merror("there is no component with name " + name);
     191             : }
     192             : 
     193           0 : std::string ActionWithValue::getComponentsList( ) const {
     194             :   std::string complist;
     195           0 :   for(unsigned i=0; i<values.size(); ++i) {
     196           0 :     complist+=values[i]->name+" ";
     197             :   }
     198           0 :   return complist;
     199             : }
     200             : 
     201       24075 : std::vector<std::string> ActionWithValue::getComponentsVector( ) const {
     202             :   std::vector<std::string> complist;
     203      349845 :   for(unsigned i=0; i<values.size(); ++i) {
     204      325770 :     complist.push_back(values[i]->name);
     205             :   }
     206       24075 :   return complist;
     207           0 : }
     208             : 
     209       85009 : void ActionWithValue::componentIsNotPeriodic( const std::string& name ) {
     210       85009 :   int kk=getComponent(name);
     211       85009 :   values[kk]->min=0; values[kk]->max=0;
     212       85009 :   values[kk]->setupPeriodicity();
     213       85009 : }
     214             : 
     215         139 : void ActionWithValue::componentIsPeriodic( const std::string& name, const std::string& min, const std::string& max ) {
     216         139 :   int kk=getComponent(name);
     217         139 :   values[kk]->setDomain(min,max);
     218         139 : }
     219             : 
     220     1854053 : void ActionWithValue::setGradientsIfNeeded() {
     221     3708106 :   if(isOptionOn("GRADIENTS")) {
     222         201 :     ActionAtomistic* aa=castToActionAtomistic();
     223         201 :     if(aa) {
     224         308 :       for(unsigned i=0; i<values.size(); i++) { unsigned start=0; values[i]->gradients.clear(); values[i]->setGradients( aa, start ); }
     225             :     } else {
     226          83 :       ActionWithArguments* aarg = castToActionWithArguments();
     227          83 :       if( !aarg ) plumed_merror( "failing in " + getLabel() );
     228         166 :       for(unsigned i=0; i<values.size(); i++) { unsigned start=0; values[i]->gradients.clear(); aarg->setGradients( values[i].get(), start ); }
     229             :     }
     230             :   }
     231     1854053 : }
     232             : 
     233     1640136 : void ActionWithValue::turnOnDerivatives() {
     234             :   // Turn on the derivatives
     235     1640136 :   noderiv=false;
     236             :   // Resize the derivatives
     237     5993745 :   for(unsigned i=0; i<values.size(); ++i) values[i]->resizeDerivatives( getNumberOfDerivatives() );
     238             :   // And turn on the derivatives in all actions on which we are dependent
     239     3274319 :   for(unsigned i=0; i<getDependencies().size(); ++i) {
     240     1634183 :     ActionWithValue* vv=getDependencies()[i]->castToActionWithValue();
     241     1634183 :     if(vv) vv->turnOnDerivatives();
     242             :   }
     243     1640136 : }
     244             : 
     245    10607215 : Value* ActionWithValue::getPntrToComponent( const std::string& name ) {
     246    10607215 :   int kk=getComponent(name);
     247    10607215 :   return values[kk].get();
     248             : }
     249             : 
     250  1190410193 : const Value* ActionWithValue::getConstPntrToComponent(int n) const {
     251             :   plumed_dbg_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     252  1190410193 :   return values[n].get();
     253             : }
     254             : 
     255    87336097 : Value* ActionWithValue::getPntrToComponent( int n ) {
     256             :   plumed_dbg_massert(n<values.size(),"you have requested a pointer that is out of bounds");
     257    87336097 :   return values[n].get();
     258             : }
     259             : 
     260     4616827 : bool ActionWithValue::calculateOnUpdate() {
     261     4616827 :   if( firststep ) {
     262      205953 :     ActionWithArguments* aa=dynamic_cast<ActionWithArguments*>(this);
     263      205953 :     if(aa) {
     264      183959 :       const std::vector<Value*> & args(aa->getArguments());
     265     1261557 :       for(const auto & p : args ) {
     266     1078012 :         if( p->calculateOnUpdate() ) {
     267         869 :           for(unsigned i=0; i<values.size(); ++i) values[i]->setValType("calcFromAverage");
     268             :           break;
     269             :         }
     270             :       }
     271             :     }
     272      205953 :     firststep=false;
     273             :   }
     274    11056154 :   for(unsigned i=0; i<values.size(); ++i) {
     275     6448185 :     if( values[i]->calculateOnUpdate() ) return true;
     276             :   }
     277             :   return false;
     278             : }
     279             : 
     280      500897 : bool ActionWithValue::checkForForces() {
     281      500897 :   const unsigned    ncp=getNumberOfComponents();
     282      500897 :   unsigned    nder=getNumberOfDerivatives();
     283      500897 :   if( ncp==0 || nder==0 ) return false;
     284             : 
     285             :   unsigned nvalsWithForce=0;
     286      499751 :   valsToForce.resize(ncp);
     287     1398434 :   for(unsigned i=0; i<ncp; ++i) {
     288      898683 :     if( values[i]->hasForce && !values[i]->isConstant() ) {
     289      290687 :       valsToForce[nvalsWithForce]=i; nvalsWithForce++;
     290             :     }
     291             :   }
     292      499751 :   if( nvalsWithForce==0 ) return false;
     293             : 
     294             :   // Make sure forces to apply is empty of forces
     295      239948 :   if( forcesForApply.size()!=nder ) forcesForApply.resize( nder );
     296             :   std::fill(forcesForApply.begin(),forcesForApply.end(),0);
     297             : 
     298             :   unsigned stride=1;
     299             :   unsigned rank=0;
     300      239948 :   if(ncp>4*comm.Get_size()) {
     301       22970 :     stride=comm.Get_size();
     302       22970 :     rank=comm.Get_rank();
     303             :   }
     304             : 
     305      239948 :   unsigned nt=OpenMP::getNumThreads();
     306      239948 :   if(nt>ncp/(4*stride)) nt=1;
     307             : 
     308      239948 :   #pragma omp parallel num_threads(nt)
     309             :   {
     310             :     std::vector<double> omp_f;
     311             :     if( nt>1 ) omp_f.resize(nder,0);
     312             :     #pragma omp for
     313             :     for(unsigned i=rank; i<nvalsWithForce; i+=stride) {
     314             :       double ff=values[valsToForce[i]]->inputForce[0];
     315             :       std::vector<double> & thisderiv( values[valsToForce[i]]->data );
     316             :       int nn=nder;
     317             :       int one1=1;
     318             :       int one2=1;
     319             :       if( nt>1 ) plumed_blas_daxpy(&nn,&ff,thisderiv.data()+1,&one1,omp_f.data(),&one2);
     320             :       else       plumed_blas_daxpy(&nn,&ff,thisderiv.data()+1,&one1,forcesForApply.data(),&one2);
     321             :       // if( nt>1 ) for(unsigned j=0; j<nder; ++j) omp_f[j] += ff*thisderiv[1+j];
     322             :       //else for(unsigned j=0; j<nder; ++j) forcesForApply[j] += ff*thisderiv[1+j];
     323             :     }
     324             :     #pragma omp critical
     325             :     {
     326             :       if( nt>1 ) {
     327             :         int nn=forcesForApply.size();
     328             :         double one0=1.0;
     329             :         int one1=1;
     330             :         int one2=1;
     331             :         plumed_blas_daxpy(&nn,&one0,omp_f.data(),&one1,forcesForApply.data(),&one2);
     332             :       }
     333             :       // for(unsigned j=0; j<forcesForApply.size(); ++j) {
     334             :       // forcesForApply[j]+=omp_f[j];
     335             :       // }
     336             :     }
     337             :   }
     338             : 
     339      239948 :   if(ncp>4*comm.Get_size()) comm.Sum(&forcesForApply[0],nder);
     340             :   return true;
     341             : }
     342             : 
     343             : }

Generated by: LCOV version 1.16