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