1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2013-2021 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;
calculate(const unsigned & current,MultiValue & myvals,std::vector<double> & buffer,std::vector<unsigned> & der_list) const46   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 PLUMED_REGISTER_VESSEL(Moments,"MOMENTS")
52 
registerKeywords(Keywords & keys)53 void Moments::registerKeywords( Keywords& keys ) {
54   Vessel::registerKeywords( keys );
55   keys.remove("LABEL");
56   keys.add("compulsory","COMPONENT","1","the component of the vector for which to calculate this quantity");
57   keys.add("compulsory","MOMENTS","the list of moments that you would like to calculate");
58 }
59 
reserveKeyword(Keywords & keys)60 void Moments::reserveKeyword( Keywords& keys ) {
61   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   keys.reset_style("MOMENTS","vessel");
68   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 }
72 
Moments(const vesselbase::VesselOptions & da)73 Moments::Moments( const vesselbase::VesselOptions& da) :
74   Vessel(da)
75 {
76   mystash = getAction()->buildDataStashes( NULL );
77   ActionWithValue* a=dynamic_cast<ActionWithValue*>( getAction() );
78   plumed_massert(a,"cannot create passable values as base action does not inherit from ActionWithValue");
79 
80   std::vector<std::string> moments; std::string valstr;
81   if( getNumericalLabel()==0 ) {
82     valstr = "moment-";
83     moments=Tools::getWords(getAllInput(),"\t\n ,");
84     Tools::interpretRanges(moments); mycomponent=1;
85   } else {
86     std::string numstr; parse("COMPONENT",mycomponent);
87     Tools::convert( mycomponent, numstr); valstr = "moment-"  + numstr + "-";
88     parseVector("MOMENTS",moments); Tools::interpretRanges(moments);
89   }
90   unsigned nn;
91   for(unsigned i=0; i<moments.size(); ++i) {
92     a->addComponentWithDerivatives( valstr + moments[i] );
93     a->componentIsNotPeriodic( valstr + moments[i] );
94     value_out.push_back( a->copyOutput( a->getNumberOfComponents()-1 ) );
95     Tools::convert( moments[i], nn );
96     if( nn<2 ) error("moments are only possible for m>=2" );
97     powers.push_back( nn ); std::string num; Tools::convert(powers[i],num);
98   }
99 }
100 
resize()101 void Moments::resize() {
102   resizeBuffer(0);
103   if( getAction()->derivativesAreRequired() ) {
104     for(unsigned i=0; i<value_out.size(); ++i) value_out[i]->resizeDerivatives( getAction()->getNumberOfDerivatives() );
105   }
106 }
107 
description()108 std::string Moments::description() {
109   std::string descri, num;
110   Tools::convert(powers[0],num);
111   if( getNumericalLabel()==0 ) {
112     descri = "value " + getAction()->getLabel() + "." + "moment-" + num + " contains the " + num + "th moment of the distribution";
113     for(unsigned i=1; i<powers.size(); ++i) {
114       Tools::convert(powers[i],num);
115       descri = descri + "\n  value " + getAction()->getLabel() + "." + "moment-" + num + " contains the " + num + "th moment of the distribution";
116     }
117   } else {
118     std::string numlab; Tools::convert( mycomponent, numlab );
119     descri = "value " + getAction()->getLabel() + "." + "moment-" + numlab + "-" + num + " contains the " + num + "th moment for the distribution of component " + numlab;
120     for(unsigned i=1; i<powers.size(); ++i) {
121       Tools::convert(powers[i],num);
122       descri = descri + "\n  value " + getAction()->getLabel() + "." + "moment-" + numlab + "-" + num + " contains the " + num + "th moment for the distribution of component " + numlab;
123     }
124   }
125   return descri;
126 }
127 
finish(const std::vector<double> & buffer)128 void Moments::finish( const std::vector<double>& buffer ) {
129   const double pi=3.141592653589793238462643383279502884197169399375105820974944592307;
130   unsigned nvals=getAction()->getFullNumberOfTasks();
131   std::vector<double>  myvalues( getAction()->getNumberOfQuantities() );
132 
133   double mean=0; Value myvalue;
134   if( getAction()->isPeriodic() ) {
135     std::string str_min, str_max; getAction()->retrieveDomain( str_min, str_max );
136     double pfactor, min, max; Tools::convert(str_min,min); Tools::convert(str_max,max);
137     pfactor = 2*pi / ( max-min ); myvalue.setDomain( str_min, str_max );
138     double sinsum=0, cossum=0, val;
139     for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
140       mystash->retrieveSequentialValue( i, false, myvalues );
141       val=pfactor*( myvalues[mycomponent] - min );
142       sinsum+=sin(val); cossum+=cos(val);
143     }
144     mean = 0.5 + atan2( sinsum / static_cast<double>( nvals ), cossum / static_cast<double>( nvals ) ) / (2*pi);
145     mean = min + (max-min)*mean;
146   } else {
147     for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
148       mystash->retrieveSequentialValue( i, false, myvalues ); mean+=myvalues[mycomponent];
149     }
150     mean/=static_cast<double>( nvals ); myvalue.setNotPeriodic();
151   }
152 
153   for(unsigned npow=0; npow<powers.size(); ++npow) {
154     double dev1=0;
155     if( value_out[0]->getNumberOfDerivatives()>0 ) {
156       for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
157         mystash->retrieveSequentialValue( i, false, myvalues );
158         dev1+=pow( myvalue.difference( mean, myvalues[mycomponent] ), powers[npow] - 1 );
159       }
160       dev1/=static_cast<double>( nvals );
161     }
162 
163     double moment=0;
164     MultiValue myvals( getAction()->getNumberOfQuantities(), getAction()->getNumberOfDerivatives() ); myvals.clearAll();
165     for(unsigned i=0; i<mystash->getNumberOfStoredValues(); ++i) {
166       mystash->retrieveSequentialValue( i, false, myvalues );
167       double tmp=myvalue.difference( mean, myvalues[mycomponent] );
168       moment+=pow( tmp, powers[npow] );
169       if( value_out[npow]->getNumberOfDerivatives() ) {
170         double pref=pow( tmp, powers[npow] - 1 ) - dev1;
171         mystash->retrieveDerivatives( i, false, myvals );
172         for(unsigned j=0; j<myvals.getNumberActive(); ++j) {
173           unsigned jatom=myvals.getActiveIndex(j);
174           value_out[npow]->addDerivative(jatom, pref*myvals.getDerivative( mycomponent, jatom ) );
175         }
176         myvals.clearAll();
177       }
178     }
179     if( value_out[npow]->getNumberOfDerivatives()>0 ) value_out[npow]->chainRule( powers[npow] / static_cast<double>( nvals ) );
180     value_out[npow]->set( moment / static_cast<double>( nvals ) );
181   }
182 }
183 
applyForce(std::vector<double> & forces)184 bool Moments::applyForce( std::vector<double>& forces ) {
185   std::vector<double> tmpforce( forces.size() );
186   forces.assign(forces.size(),0.0); bool wasforced=false;
187   for(unsigned i=0; i<value_out.size(); ++i) {
188     if( value_out[i]->applyForce( tmpforce ) ) {
189       wasforced=true;
190       for(unsigned j=0; j<forces.size(); ++j) forces[j]+=tmpforce[j];
191     }
192   }
193   return wasforced;
194 }
195 
196 }
197 }
198