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