LCOV - code coverage report
Current view: top level - vesselbase - Moments.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 82 92 89.1 %
Date: 2024-10-11 08:09:47 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-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 "VesselRegister.h"
      23             : #include "Vessel.h"
      24             : #include "StoreDataVessel.h"
      25             : #include "ActionWithVessel.h"
      26             : 
      27             : namespace PLMD {
      28             : namespace vesselbase {
      29             : 
      30             : // This is not the most efficient implementation
      31             : // The calculation of all the colvars is parallelized
      32             : // but the loops for calculating moments are not
      33             : // Feel free to reimplement this if you know how
      34             : class Moments : public Vessel {
      35             : private:
      36             :   unsigned mycomponent;
      37             :   StoreDataVessel* mystash;
      38             :   std::vector<unsigned> powers;
      39             :   std::vector<Value*> value_out;
      40             : public:
      41             :   static void registerKeywords( Keywords& keys );
      42             :   static void reserveKeyword( Keywords& keys );
      43             :   explicit Moments( const vesselbase::VesselOptions& da );
      44             :   std::string description() override;
      45             :   void resize() override;
      46        6936 :   void calculate( const unsigned& current, MultiValue& myvals, std::vector<double>& buffer, std::vector<unsigned>& der_list ) const override {}
      47             :   void finish( const std::vector<double>& buffer ) override;
      48             :   bool applyForce( std::vector<double>& forces ) override;
      49             : };
      50             : 
      51       10427 : PLUMED_REGISTER_VESSEL(Moments,"MOMENTS")
      52             : 
      53           8 : void Moments::registerKeywords( Keywords& keys ) {
      54           8 :   Vessel::registerKeywords( keys );
      55           8 :   keys.remove("LABEL");
      56          16 :   keys.add("compulsory","COMPONENT","1","the component of the vector for which to calculate this quantity");
      57          16 :   keys.add("optional","MOMENTS","the list of moments that you would like to calculate");
      58           8 : }
      59             : 
      60        3473 : void Moments::reserveKeyword( Keywords& keys ) {
      61        6946 :   keys.reserve("optional","MOMENTS","calculate the moments of the distribution of collective variables. "
      62             :                "The mth moment of a distribution is calculated using \\f$\\frac{1}{N} \\sum_{i=1}^N ( s_i - \\overline{s} )^m \\f$, where \\f$\\overline{s}\\f$ is "
      63             :                "the average for the distribution. The moments keyword takes a lists of integers as input or a range. Each integer is a value of \\f$m\\f$. The final "
      64             :                "calculated values can be referenced using moment-\\f$m\\f$.  You can use the COMPONENT keyword in this action but the syntax is slightly different. "
      65             :                "If you would like the second and third moments of the third component you would use MOMENTS={COMPONENT=3 MOMENTS=2-3}.  The moments would then be referred to "
      66             :                "using the labels moment-3-2 and moment-3-3.  This syntax is also required if you are using numbered MOMENT keywords i.e. MOMENTS1, MOMENTS2...");
      67        6946 :   keys.reset_style("MOMENTS","vessel");
      68        6946 :   keys.addOutputComponent("moment","MOMENTS","the central moments of the distribution of values. The second moment "
      69             :                           "would be referenced elsewhere in the input file using "
      70             :                           "<em>label</em>.moment-2, the third as <em>label</em>.moment-3, etc.");
      71        3473 : }
      72             : 
      73           8 : Moments::Moments( const vesselbase::VesselOptions& da) :
      74           8 :   Vessel(da)
      75             : {
      76           8 :   mystash = getAction()->buildDataStashes( NULL );
      77           8 :   ActionWithValue* a=dynamic_cast<ActionWithValue*>( getAction() );
      78           8 :   plumed_massert(a,"cannot create passable values as base action does not inherit from ActionWithValue");
      79             : 
      80           8 :   std::vector<std::string> moments; std::string valstr = "moment-";
      81          24 :   parse("COMPONENT",mycomponent); parseVector("MOMENTS",moments);
      82          10 :   if( getNumericalLabel()!=0 ) { std::string numstr; Tools::convert( mycomponent, numstr); valstr = "moment-" + numstr + "-"; }
      83          14 :   if( moments.size()==0 ) moments=Tools::getWords(getAllInput(),"\t\n ,");
      84           8 :   Tools::interpretRanges(moments); unsigned nn;
      85          20 :   for(unsigned i=0; i<moments.size(); ++i) {
      86          24 :     a->addComponentWithDerivatives( valstr + moments[i] );
      87          24 :     a->componentIsNotPeriodic( valstr + moments[i] );
      88          12 :     value_out.push_back( a->copyOutput( a->getNumberOfComponents()-1 ) );
      89          12 :     Tools::convert( moments[i], nn );
      90          12 :     if( nn<2 ) error("moments are only possible for m>=2" );
      91          12 :     powers.push_back( nn ); std::string num; Tools::convert(powers[i],num);
      92             :   }
      93           8 : }
      94             : 
      95          28 : void Moments::resize() {
      96             :   resizeBuffer(0);
      97          28 :   if( getAction()->derivativesAreRequired() ) {
      98          56 :     for(unsigned i=0; i<value_out.size(); ++i) value_out[i]->resizeDerivatives( getAction()->getNumberOfDerivatives() );
      99             :   }
     100          28 : }
     101             : 
     102           8 : std::string Moments::description() {
     103             :   std::string descri, num;
     104           8 :   Tools::convert(powers[0],num);
     105           8 :   if( getNumericalLabel()==0 ) {
     106          12 :     descri = "value " + getAction()->getLabel() + "." + "moment-" + num + " contains the " + num + "th moment of the distribution";
     107           8 :     for(unsigned i=1; i<powers.size(); ++i) {
     108           2 :       Tools::convert(powers[i],num);
     109           4 :       descri = descri + "\n  value " + getAction()->getLabel() + "." + "moment-" + num + " contains the " + num + "th moment of the distribution";
     110             :     }
     111             :   } else {
     112           2 :     std::string numlab; Tools::convert( mycomponent, numlab );
     113           4 :     descri = "value " + getAction()->getLabel() + "." + "moment-" + numlab + "-" + num + " contains the " + num + "th moment for the distribution of component " + numlab;
     114           4 :     for(unsigned i=1; i<powers.size(); ++i) {
     115           2 :       Tools::convert(powers[i],num);
     116           4 :       descri = descri + "\n  value " + getAction()->getLabel() + "." + "moment-" + numlab + "-" + num + " contains the " + num + "th moment for the distribution of component " + numlab;
     117             :     }
     118             :   }
     119           8 :   return descri;
     120             : }
     121             : 
     122         822 : void Moments::finish( const std::vector<double>& buffer ) {
     123             :   const double pi=3.141592653589793238462643383279502884197169399375105820974944592307;
     124             :   unsigned nvals=getAction()->getFullNumberOfTasks();
     125         822 :   std::vector<double>  myvalues( getAction()->getNumberOfQuantities() );
     126             : 
     127         822 :   double mean=0; Value myvalue;
     128         822 :   if( getAction()->isPeriodic() ) {
     129           0 :     std::string str_min, str_max; getAction()->retrieveDomain( str_min, str_max );
     130           0 :     double pfactor, min, max; Tools::convert(str_min,min); Tools::convert(str_max,max);
     131           0 :     pfactor = 2*pi / ( max-min ); myvalue.setDomain( str_min, str_max );
     132             :     double sinsum=0, cossum=0, val;
     133           0 :     for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
     134           0 :       mystash->retrieveSequentialValue( i, false, myvalues );
     135           0 :       val=pfactor*( myvalues[mycomponent] - min );
     136           0 :       sinsum+=std::sin(val); cossum+=std::cos(val);
     137             :     }
     138           0 :     mean = 0.5 + std::atan2( sinsum / static_cast<double>( nvals ), cossum / static_cast<double>( nvals ) ) / (2*pi);
     139           0 :     mean = min + (max-min)*mean;
     140             :   } else {
     141        7758 :     for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
     142        6936 :       mystash->retrieveSequentialValue( i, false, myvalues ); mean+=myvalues[mycomponent];
     143             :     }
     144         822 :     mean/=static_cast<double>( nvals ); myvalue.setNotPeriodic();
     145             :   }
     146             : 
     147        2056 :   for(unsigned npow=0; npow<powers.size(); ++npow) {
     148             :     double dev1=0;
     149        1234 :     if( value_out[0]->getNumberOfDerivatives()>0 ) {
     150        4606 :       for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
     151        4172 :         mystash->retrieveSequentialValue( i, false, myvalues );
     152        4172 :         dev1+=pow( myvalue.difference( mean, myvalues[mycomponent] ), powers[npow] - 1 );
     153             :       }
     154         434 :       dev1/=static_cast<double>( nvals );
     155             :     }
     156             : 
     157             :     double moment=0;
     158        1234 :     MultiValue myvals( getAction()->getNumberOfQuantities(), getAction()->getNumberOfDerivatives() ); myvals.clearAll();
     159       11006 :     for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
     160        9772 :       mystash->retrieveSequentialValue( i, false, myvalues );
     161        9772 :       double tmp=myvalue.difference( mean, myvalues[mycomponent] );
     162        9772 :       moment+=pow( tmp, powers[npow] );
     163        9772 :       if( value_out[npow]->getNumberOfDerivatives() ) {
     164        4172 :         double pref=pow( tmp, powers[npow] - 1 ) - dev1;
     165        4172 :         mystash->retrieveDerivatives( i, false, myvals );
     166      166880 :         for(unsigned j=0; j<myvals.getNumberActive(); ++j) {
     167             :           unsigned jatom=myvals.getActiveIndex(j);
     168      162708 :           value_out[npow]->addDerivative(jatom, pref*myvals.getDerivative( mycomponent, jatom ) );
     169             :         }
     170        4172 :         myvals.clearAll();
     171             :       }
     172             :     }
     173        1234 :     if( value_out[npow]->getNumberOfDerivatives()>0 ) value_out[npow]->chainRule( powers[npow] / static_cast<double>( nvals ) );
     174        1234 :     value_out[npow]->set( moment / static_cast<double>( nvals ) );
     175        1234 :   }
     176        1644 : }
     177             : 
     178         432 : bool Moments::applyForce( std::vector<double>& forces ) {
     179         432 :   std::vector<double> tmpforce( forces.size() );
     180         432 :   forces.assign(forces.size(),0.0); bool wasforced=false;
     181        1276 :   for(unsigned i=0; i<value_out.size(); ++i) {
     182         844 :     if( value_out[i]->applyForce( tmpforce ) ) {
     183             :       wasforced=true;
     184           0 :       for(unsigned j=0; j<forces.size(); ++j) forces[j]+=tmpforce[j];
     185             :     }
     186             :   }
     187         432 :   return wasforced;
     188             : }
     189             : 
     190             : }
     191             : }

Generated by: LCOV version 1.15