1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2016-2018 The VES code team
3    (see the PEOPLE-VES file at the root of this folder for a list of names)
4 
5    See http://www.ves-code.org for more information.
6 
7    This file is part of VES code module.
8 
9    The VES code module 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    The VES code module 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 the VES code module.  If not, see <http://www.gnu.org/licenses/>.
21 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 
23 #include "BasisFunctions.h"
24 #include "TargetDistribution.h"
25 #include "VesBias.h"
26 #include "VesTools.h"
27 #include "GridIntegrationWeights.h"
28 
29 #include "tools/Grid.h"
30 #include "tools/Tools.h"
31 
32 
33 namespace PLMD {
34 namespace ves {
35 
registerKeywords(Keywords & keys)36 void BasisFunctions::registerKeywords(Keywords& keys) {
37   Action::registerKeywords(keys);
38   keys.add("compulsory","ORDER","The order of the basis function expansion.");
39   keys.add("compulsory","MINIMUM","The minimum of the interval on which the basis functions are defined.");
40   keys.add("compulsory","MAXIMUM","The maximum of the interval on which the basis functions are defined.");
41   keys.add("hidden","NGRID_POINTS","The number of grid points used for numerical integrals");
42   keys.addFlag("DEBUG_INFO",false,"Print out more detailed information about the basis set. Useful for debugging.");
43   keys.addFlag("NUMERICAL_INTEGRALS",false,"Calculate basis function integral for the uniform distribution numerically. Useful for debugging.");
44 }
45 
46 
BasisFunctions(const ActionOptions & ao)47 BasisFunctions::BasisFunctions(const ActionOptions&ao):
48   Action(ao),
49   print_debug_info_(false),
50   has_been_set(false),
51   description_("Undefined"),
52   type_("Undefined"),
53   norder_(0),
54   nbasis_(1),
55   bf_label_prefix_("f"),
56   bf_labels_(nbasis_,"f0"),
57   periodic_(false),
58   interval_bounded_(true),
59   interval_intrinsic_min_str_("1.0"),
60   interval_intrinsic_max_str_("-1.0"),
61   interval_intrinsic_min_(1.0),
62   interval_intrinsic_max_(-1.0),
63   interval_intrinsic_range_(0.0),
64   interval_intrinsic_mean_(0.0),
65   interval_min_str_(""),
66   interval_max_str_(""),
67   interval_min_(0.0),
68   interval_max_(0.0),
69   interval_range_(0.0),
70   interval_mean_(0.0),
71   argT_derivf_(1.0),
72   numerical_uniform_integrals_(false),
73   nbins_(1001),
74   uniform_integrals_(nbasis_,0.0),
75   vesbias_pntr_(NULL),
76   action_pntr_(NULL)
77 {
78   bf_keywords_.push_back(getName());
79   if(keywords.exists("ORDER")) {
80     parse("ORDER",norder_); addKeywordToList("ORDER",norder_);
81   }
82   nbasis_=norder_+1;
83   //
84   std::string str_imin; std::string str_imax;
85   if(keywords.exists("MINIMUM") && keywords.exists("MAXIMUM")) {
86     parse("MINIMUM",str_imin); addKeywordToList("MINIMUM",str_imin);
87     parse("MAXIMUM",str_imax); addKeywordToList("MAXIMUM",str_imax);
88   }
89   else {
90     str_imin = "-1.0";
91     str_imax = "1.0";
92   }
93   interval_min_str_ = str_imin;
94   interval_max_str_ = str_imax;
95   if(!Tools::convert(str_imin,interval_min_)) {
96     plumed_merror(getName()+": cannot convert the value given in MINIMUM to a double");
97   }
98   if(!Tools::convert(str_imax,interval_max_)) {
99     plumed_merror(getName()+": cannot convert the value given in MAXIMUM to a double");
100   }
101   if(interval_min_>interval_max_) {plumed_merror(getName()+": MINIMUM and MAXIMUM are not correctly defined");}
102   //
103   parseFlag("DEBUG_INFO",print_debug_info_);
104   if(keywords.exists("NUMERICAL_INTEGRALS")) {
105     parseFlag("NUMERICAL_INTEGRALS",numerical_uniform_integrals_);
106   }
107   if(keywords.exists("NGRID_POINTS")) {
108     parse("NGRID_POINTS",nbins_);
109   }
110   // log.printf(" %s \n",getKeywordString().c_str());
111 
112 }
113 
114 
setIntrinsicInterval(const double interval_intrinsic_min_in,const double interval_intrinsic_max_in)115 void BasisFunctions::setIntrinsicInterval(const double interval_intrinsic_min_in, const double interval_intrinsic_max_in) {
116   interval_intrinsic_min_ = interval_intrinsic_min_in;
117   interval_intrinsic_max_ = interval_intrinsic_max_in;
118   VesTools::convertDbl2Str(interval_intrinsic_min_,interval_intrinsic_min_str_);
119   VesTools::convertDbl2Str(interval_intrinsic_max_,interval_intrinsic_max_str_);
120   plumed_massert(interval_intrinsic_min_<interval_intrinsic_max_,"setIntrinsicInterval: intrinsic intervals are not defined correctly");
121 }
122 
123 
setIntrinsicInterval(const std::string & interval_intrinsic_min_str_in,const std::string & interval_intrinsic_max_str_in)124 void BasisFunctions::setIntrinsicInterval(const std::string& interval_intrinsic_min_str_in, const std::string& interval_intrinsic_max_str_in) {
125   interval_intrinsic_min_str_ = interval_intrinsic_min_str_in;
126   interval_intrinsic_max_str_ = interval_intrinsic_max_str_in;
127   if(!Tools::convert(interval_intrinsic_min_str_,interval_intrinsic_min_)) {
128     plumed_merror("setIntrinsicInterval: cannot convert string value given for the minimum of the intrinsic interval to a double");
129   }
130   if(!Tools::convert(interval_intrinsic_max_str_,interval_intrinsic_max_)) {
131     plumed_merror("setIntrinsicInterval: cannot convert string value given for the maximum of the intrinsic interval to a double");
132   }
133   plumed_massert(interval_intrinsic_min_<interval_intrinsic_max_,"setIntrinsicInterval: intrinsic intervals are not defined correctly");
134 }
135 
136 
setInterval(const double interval_min_in,const double interval_max_in)137 void BasisFunctions::setInterval(const double interval_min_in, const double interval_max_in) {
138   interval_min_ = interval_min_in;
139   interval_max_ = interval_max_in;
140   VesTools::convertDbl2Str(interval_min_,interval_min_str_);
141   VesTools::convertDbl2Str(interval_max_,interval_max_str_);
142   plumed_massert(interval_min_<interval_max_,"setInterval: intervals are not defined correctly");
143 }
144 
145 
setInterval(const std::string & interval_min_str_in,const std::string & interval_max_str_in)146 void BasisFunctions::setInterval(const std::string& interval_min_str_in, const std::string& interval_max_str_in) {
147   interval_min_str_ = interval_min_str_in;
148   interval_max_str_ = interval_max_str_in;
149   if(!Tools::convert(interval_min_str_,interval_min_)) {
150     plumed_merror("setInterval: cannot convert string value given for the minimum of the interval to a double");
151   }
152   if(!Tools::convert(interval_max_str_,interval_max_)) {
153     plumed_merror("setInterval: cannot convert string value given for the maximum of the interval to a double");
154   }
155   plumed_massert(interval_min_<interval_max_,"setInterval: intervals are not defined correctly");
156 }
157 
158 
setupInterval()159 void BasisFunctions::setupInterval() {
160   // if(!intervalBounded()){plumed_merror("setupInterval() only works for bounded interval");}
161   interval_intrinsic_range_ = interval_intrinsic_max_-interval_intrinsic_min_;
162   interval_intrinsic_mean_  = 0.5*(interval_intrinsic_max_+interval_intrinsic_min_);
163   interval_range_ = interval_max_-interval_min_;
164   interval_mean_  = 0.5*(interval_max_+interval_min_);
165   argT_derivf_ = interval_intrinsic_range_/interval_range_;
166 }
167 
168 
setupLabels()169 void BasisFunctions::setupLabels() {
170   for(unsigned int i=0; i < nbasis_; i++) {
171     std::string is; Tools::convert(i,is);
172     bf_labels_[i]=bf_label_prefix_+is+"(s)";
173   }
174 }
175 
176 
setupUniformIntegrals()177 void BasisFunctions::setupUniformIntegrals() {
178   numerical_uniform_integrals_=true;
179   numericalUniformIntegrals();
180 }
181 
182 
setupBF()183 void BasisFunctions::setupBF() {
184   if(interval_intrinsic_min_>interval_intrinsic_max_) {plumed_merror("setupBF: default intervals are not correctly set");}
185   setupInterval();
186   setupLabels();
187   if(bf_labels_.size()==1) {plumed_merror("setupBF: the labels of the basis functions are not correct.");}
188   if(!numerical_uniform_integrals_) {setupUniformIntegrals();}
189   else {numericalUniformIntegrals();}
190   if(uniform_integrals_.size()==1) {plumed_merror("setupBF: the integrals of the basis functions is not correct.");}
191   if(type_=="Undefined") {plumed_merror("setupBF: the type of the basis function is not defined.");}
192   if(description_=="Undefined") {plumed_merror("setupBF: the description of the basis function is not defined.");}
193   has_been_set=true;
194   printInfo();
195 }
196 
197 
printInfo() const198 void BasisFunctions::printInfo() const {
199   if(!has_been_set) {plumed_merror("the basis set has not be setup correctly");}
200   log.printf("  One-dimensional basis set\n");
201   log.printf("   Description: %s\n",description_.c_str());
202   log.printf("   Type: %s\n",type_.c_str());
203   if(periodic_) {log.printf("   The basis functions are periodic\n");}
204   log.printf("   Order of basis set: %u\n",norder_);
205   log.printf("   Number of basis functions: %u\n",nbasis_);
206   // log.printf("   Interval of basis set: %f to %f\n",interval_min_,interval_max_);
207   log.printf("   Interval of basis set: %s to %s\n",interval_min_str_.c_str(),interval_max_str_.c_str());
208   log.printf("   Description of basis functions:\n");
209   for(unsigned int i=0; i < nbasis_; i++) {log.printf("    %2u       %10s\n",i,bf_labels_[i].c_str());}
210   //
211   if(print_debug_info_) {
212     log.printf("  Debug information:\n");
213     // log.printf("   Default interval of basis set: [%f,%f]\n",interval_intrinsic_min_,interval_intrinsic_max_);
214     log.printf("   Intrinsic interval of basis set: [%s,%s]\n",interval_intrinsic_min_str_.c_str(),interval_intrinsic_max_str_.c_str());
215     log.printf("   Intrinsic interval of basis set: range=%f,  mean=%f\n",interval_intrinsic_range_,interval_intrinsic_mean_);
216     // log.printf("   Defined interval of basis set: [%f,%f]\n",interval_min_,interval_max_);
217     log.printf("   Defined interval of basis set: [%s,%s]\n",interval_min_str_.c_str(),interval_max_str_.c_str());
218     log.printf("   Defined interval of basis set: range=%f,  mean=%f\n",interval_range_,interval_mean_);
219     log.printf("   Derivative factor due to interval translation: %f\n",argT_derivf_);
220     log.printf("   Integral of basis functions over the interval:\n");
221     if(numerical_uniform_integrals_) {log.printf("   Note: calculated numerically\n");}
222     for(unsigned int i=0; i < nbasis_; i++) {log.printf("    %2u       %16.10f\n",i,uniform_integrals_[i]);}
223     log.printf("   --------------------------\n");
224   }
225 }
226 
227 
linkVesBias(VesBias * vesbias_pntr_in)228 void BasisFunctions::linkVesBias(VesBias* vesbias_pntr_in) {
229   vesbias_pntr_ = vesbias_pntr_in;
230   action_pntr_ = static_cast<Action*>(vesbias_pntr_in);
231 }
232 
233 
linkAction(Action * action_pntr_in)234 void BasisFunctions::linkAction(Action* action_pntr_in) {
235   action_pntr_ = action_pntr_in;
236 }
237 
238 
numericalUniformIntegrals()239 void BasisFunctions::numericalUniformIntegrals() {
240   std::vector<std::string> grid_min(1); grid_min[0]=intervalMinStr();
241   std::vector<std::string> grid_max(1); grid_max[0]=intervalMaxStr();
242   std::vector<unsigned int> grid_bins(1); grid_bins[0]=nbins_;
243   std::vector<Value*> arguments(1); arguments[0]= new Value(NULL,"arg",false);
244   if(arePeriodic()) {arguments[0]->setDomain(intervalMinStr(),intervalMaxStr());}
245   else {arguments[0]->setNotPeriodic();}
246   Grid* uniform_grid = new Grid("uniform",arguments,grid_min,grid_max,grid_bins,false,false);
247   //
248   double inverse_normalization = 1.0/(intervalMax()-intervalMin());
249   for(Grid::index_t l=0; l<uniform_grid->getSize(); l++) {
250     uniform_grid->setValue(l,inverse_normalization);
251   }
252   uniform_integrals_ = numericalTargetDistributionIntegralsFromGrid(uniform_grid);
253   delete arguments[0]; arguments.clear();
254   delete uniform_grid;
255 }
256 
257 
numericalTargetDistributionIntegralsFromGrid(const Grid * grid_pntr) const258 std::vector<double> BasisFunctions::numericalTargetDistributionIntegralsFromGrid(const Grid* grid_pntr) const {
259   plumed_massert(grid_pntr!=NULL,"the grid is not defined");
260   plumed_massert(grid_pntr->getDimension()==1,"the target distribution grid should be one dimensional");
261   //
262   std::vector<double> targetdist_integrals(nbasis_,0.0);
263   std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(grid_pntr);
264 
265   for(Grid::index_t k=0; k < grid_pntr->getSize(); k++) {
266     double arg = grid_pntr->getPoint(k)[0];
267     std::vector<double> bf_values(nbasis_);
268     std::vector<double> bf_derivs(nbasis_);
269     bool inside=true;
270     double argT=0.0;
271     getAllValues(arg,argT,inside,bf_values,bf_derivs);
272     for(unsigned int i=0; i < nbasis_; i++) {
273       targetdist_integrals[i] += (integration_weights[k] * grid_pntr->getValue(k)) * bf_values[i];
274     }
275   }
276   // assume that the first function is the constant
277   bool inside=true;
278   double argT=0.0;
279   targetdist_integrals[0] = getValue(0.0,0,argT,inside);
280   return targetdist_integrals;
281 }
282 
283 
getTargetDistributionIntegrals(const TargetDistribution * targetdist_pntr) const284 std::vector<double> BasisFunctions::getTargetDistributionIntegrals(const TargetDistribution* targetdist_pntr) const {
285   if(targetdist_pntr==NULL) {
286     return getUniformIntegrals();
287   }
288   else {
289     Grid* targetdist_grid = targetdist_pntr->getTargetDistGridPntr();
290     return numericalTargetDistributionIntegralsFromGrid(targetdist_grid);
291   }
292 }
293 
294 
getKeywordString() const295 std::string BasisFunctions::getKeywordString() const {
296   std::string str_keywords=bf_keywords_[0];
297   for(unsigned int i=1; i<bf_keywords_.size(); i++) {str_keywords+=" "+bf_keywords_[i];}
298   return str_keywords;
299 }
300 
301 
getValue(const double arg,const unsigned int n,double & argT,bool & inside_range) const302 double BasisFunctions::getValue(const double arg, const unsigned int n, double& argT, bool& inside_range) const {
303   plumed_massert(n<numberOfBasisFunctions(),"getValue: n is outside range of the defined order of the basis set");
304   inside_range=true;
305   std::vector<double> tmp_values(numberOfBasisFunctions());
306   std::vector<double> tmp_derivs(numberOfBasisFunctions());
307   getAllValues(arg, argT, inside_range, tmp_values, tmp_derivs);
308   return tmp_values[n];
309 }
310 
311 
getAllValuesNumericalDerivs(const double arg,double & argT,bool & inside_range,std::vector<double> & values,std::vector<double> & derivs) const312 void BasisFunctions::getAllValuesNumericalDerivs(const double arg, double& argT, bool& inside_range, std::vector<double>& values, std::vector<double>& derivs) const {
313   // use forward difference, unless very close to the boundary
314   double delta = sqrt(epsilon);
315   if((arg+delta)>intervalMax()) {
316     delta *= -1.0;
317   }
318   inside_range=true;
319   std::vector<double> values_delta(numberOfBasisFunctions());
320   std::vector<double> derivs_dummy(numberOfBasisFunctions());
321   getAllValues(arg+delta, argT, inside_range, values_delta, derivs_dummy);
322   getAllValues(arg, argT, inside_range, values, derivs_dummy);
323   for(unsigned int i=0; i<numberOfBasisFunctions(); i++) {
324     derivs[i] = (values_delta[i]-values[i])/delta;
325   }
326 }
327 
328 
getMultipleValue(const std::vector<double> & args,std::vector<double> & argsT,std::vector<std::vector<double>> & values,std::vector<std::vector<double>> & derivs,const bool numerical_deriv) const329 void BasisFunctions::getMultipleValue(const std::vector<double>& args, std::vector<double>& argsT, std::vector<std::vector<double> >& values, std::vector<std::vector<double> >& derivs, const bool numerical_deriv) const {
330   argsT.resize(args.size());
331   values.clear();
332   derivs.clear();
333   for(unsigned int i=0; i<args.size(); i++) {
334     std::vector<double> tmp_values(getNumberOfBasisFunctions());
335     std::vector<double> tmp_derivs(getNumberOfBasisFunctions());
336     bool inside_interval=true;
337     if(!numerical_deriv) {
338       getAllValues(args[i],argsT[i],inside_interval,tmp_values,tmp_derivs);
339     } else {
340       getAllValuesNumericalDerivs(args[i],argsT[i],inside_interval,tmp_values,tmp_derivs);
341     }
342     values.push_back(tmp_values);
343     derivs.push_back(tmp_derivs);
344   }
345 }
346 
347 
writeBasisFunctionsToFile(OFile & ofile_values,OFile & ofile_derivs,const std::string & min_in,const std::string & max_in,unsigned int nbins_in,const bool ignore_periodicity,const std::string & output_fmt,const bool numerical_deriv) const348 void BasisFunctions::writeBasisFunctionsToFile(OFile& ofile_values, OFile& ofile_derivs, const std::string& min_in, const std::string& max_in, unsigned int nbins_in, const bool ignore_periodicity, const std::string& output_fmt, const bool numerical_deriv) const {
349   std::vector<std::string> min(1); min[0]=min_in;
350   std::vector<std::string> max(1); max[0]=max_in;
351   std::vector<unsigned int> nbins(1); nbins[0]=nbins_in;
352   std::vector<Value*> value_pntr(1);
353   value_pntr[0]= new Value(NULL,"arg",false);
354   if(arePeriodic() && !ignore_periodicity) {value_pntr[0]->setDomain(intervalMinStr(),intervalMaxStr());}
355   else {value_pntr[0]->setNotPeriodic();}
356   Grid args_grid = Grid("grid",value_pntr,min,max,nbins,false,false);
357 
358   std::vector<double> args(args_grid.getSize(),0.0);
359   for(unsigned int i=0; i<args.size(); i++) {
360     args[i] = args_grid.getPoint(i)[0];
361   }
362   std::vector<double> argsT;
363   std::vector<std::vector<double> > values;
364   std::vector<std::vector<double> > derivs;
365 
366   ofile_values.addConstantField("bf_keywords").printField("bf_keywords","{"+getKeywordString()+"}");
367   ofile_derivs.addConstantField("bf_keywords").printField("bf_keywords","{"+getKeywordString()+"}");
368 
369   ofile_values.addConstantField("min").printField("min",intervalMinStr());
370   ofile_values.addConstantField("max").printField("max",intervalMaxStr());
371 
372   ofile_derivs.addConstantField("min").printField("min",intervalMinStr());
373   ofile_derivs.addConstantField("max").printField("max",intervalMaxStr());
374 
375   ofile_values.addConstantField("nbins").printField("nbins",static_cast<int>(args_grid.getNbin()[0]));
376   ofile_derivs.addConstantField("nbins").printField("nbins",static_cast<int>(args_grid.getNbin()[0]));
377 
378   if(arePeriodic()) {
379     ofile_values.addConstantField("periodic").printField("periodic","true");
380     ofile_derivs.addConstantField("periodic").printField("periodic","true");
381   }
382   else {
383     ofile_values.addConstantField("periodic").printField("periodic","false");
384     ofile_derivs.addConstantField("periodic").printField("periodic","false");
385   }
386 
387   getMultipleValue(args,argsT,values,derivs,numerical_deriv);
388   ofile_values.fmtField(output_fmt);
389   ofile_derivs.fmtField(output_fmt);
390   for(unsigned int i=0; i<args.size(); i++) {
391     ofile_values.printField("arg",args[i]);
392     ofile_derivs.printField("arg",args[i]);
393     for(unsigned int k=0; k<getNumberOfBasisFunctions(); k++) {
394       ofile_values.printField(getBasisFunctionLabel(k),values[i][k]);
395       ofile_derivs.printField("d_"+getBasisFunctionLabel(k),derivs[i][k]);
396     }
397     ofile_values.printField();
398     ofile_derivs.printField();
399   }
400   ofile_values.fmtField();
401   ofile_derivs.fmtField();
402 
403   delete value_pntr[0]; value_pntr.clear();
404 
405 }
406 
407 
408 }
409 }
410