Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2011-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 :
23 : #include "Grid.h"
24 : #include "Tools.h"
25 : #include "core/Value.h"
26 : #include "File.h"
27 : #include "Exception.h"
28 : #include "KernelFunctions.h"
29 : #include "RootFindingBase.h"
30 : #include "Communicator.h"
31 : #include "small_vector/small_vector.h"
32 :
33 : #include <vector>
34 : #include <cmath>
35 : #include <iostream>
36 : #include <sstream>
37 : #include <cstdio>
38 : #include <cfloat>
39 : #include <array>
40 :
41 : namespace PLMD {
42 :
43 : constexpr std::size_t GridBase::maxdim;
44 :
45 : template<unsigned dimension_>
46 2963 : class Accelerator final:
47 : public Grid::AcceleratorBase
48 : {
49 :
50 : public:
51 :
52 1506 : unsigned getDimension() const override {
53 1506 : return dimension_;
54 : }
55 :
56 23376 : std::vector<GridBase::index_t> getNeighbors(const GridBase& grid, const std::vector<unsigned> & nbin_,const std::vector<bool> & pbc_,const unsigned* indices,std::size_t indices_size,const std::vector<unsigned> &nneigh)const override {
57 : plumed_dbg_assert(indices_size==dimension_ && nneigh.size()==dimension_);
58 :
59 : std::vector<Grid::index_t> neighbors;
60 : std::array<unsigned,dimension_> small_bin;
61 : std::array<unsigned,dimension_> small_indices;
62 : std::array<unsigned,dimension_> tmp_indices;
63 :
64 : unsigned small_nbin=1;
65 69616 : for(unsigned j=0; j<dimension_; ++j) {
66 46240 : small_bin[j]=(2*nneigh[j]+1);
67 46240 : small_nbin*=small_bin[j];
68 : }
69 23376 : neighbors.reserve(small_nbin);
70 :
71 69616 : for(unsigned j=0; j<dimension_; j++) small_indices[j]=0;
72 :
73 1292414 : for(unsigned index=0; index<small_nbin; ++index) {
74 : unsigned ll=0;
75 3793642 : for(unsigned i=0; i<dimension_; ++i) {
76 2524604 : int i0=small_indices[i]-nneigh[i]+indices[i];
77 2524604 : if(!pbc_[i] && i0<0) continue;
78 2504882 : if(!pbc_[i] && i0>=static_cast<int>(nbin_[i])) continue;
79 2483910 : if( pbc_[i] && i0<0) i0=nbin_[i]-(-i0)%nbin_[i];
80 2483910 : if( pbc_[i] && i0>=static_cast<int>(nbin_[i])) i0%=nbin_[i];
81 2483910 : tmp_indices[ll]=static_cast<unsigned>(i0);
82 2483910 : ll++;
83 : }
84 1269038 : if(ll==dimension_) {neighbors.push_back(getIndex(grid,nbin_,&tmp_indices[0],dimension_));}
85 :
86 1269038 : small_indices[0]++;
87 2511132 : for(unsigned j=0; j<dimension_-1; j++) if(small_indices[j]==small_bin[j]) {
88 95464 : small_indices[j]=0;
89 95464 : small_indices[j+1]++;
90 : }
91 : }
92 23376 : return neighbors;
93 : }
94 :
95 : // we are flattening arrays using a column-major order
96 7146297 : GridBase::index_t getIndex(const GridBase& grid, const std::vector<unsigned> & nbin_,const unsigned* indices,std::size_t indices_size) const override {
97 19320328 : for(unsigned int i=0; i<dimension_; i++)
98 12174031 : if(indices[i]>=nbin_[i]) plumed_error() << "Looking for a value outside the grid along the " << i << " dimension (arg name: "<<grid.getArgNames()[i]<<")";
99 7146297 : auto index=indices[dimension_-1];
100 9619118 : for(unsigned int i=dimension_-1; i>0; --i) {
101 5027734 : index=index*nbin_[i-1]+indices[i-1];
102 : }
103 7146297 : return index;
104 : }
105 :
106 32656903 : void getPoint(const std::vector<double> & min_,const std::vector<double> & dx_, const unsigned* indices,std::size_t indices_size,double* point,std::size_t point_size) const override {
107 111791203 : for(unsigned int i=0; i<dimension_; ++i) {
108 79134300 : point[i]=min_[i]+(double)(indices[i])*dx_[i];
109 : }
110 32656903 : }
111 :
112 1108031 : void getIndices(const std::vector<double> & min_,const std::vector<double> & dx_, const std::vector<double> & x, unsigned* rindex_data,std::size_t rindex_size) const override {
113 : plumed_dbg_assert(x.size()==dimension_);
114 : plumed_dbg_assert(rindex_size==dimension_);
115 3316314 : for(unsigned int i=0; i<dimension_; ++i) {
116 2208283 : rindex_data[i] = unsigned(std::floor((x[i]-min_[i])/dx_[i]));
117 : }
118 1108031 : }
119 :
120 :
121 : // we are flattening arrays using a column-major order
122 37838893 : void getIndices(const std::vector<unsigned> & nbin_, GridBase::index_t index, unsigned* indices, std::size_t indices_size) const override {
123 37838893 : plumed_assert(indices_size==dimension_)<<indices_size;
124 90954643 : for(unsigned int i=0; i<dimension_-1; ++i) {
125 53951676 : indices[i]=index%nbin_[i];
126 53951676 : index/=nbin_[i];
127 : }
128 37838893 : indices[dimension_-1]=index;
129 37838893 : }
130 :
131 : };
132 :
133 2963 : std::unique_ptr<Grid::AcceleratorBase> Grid::AcceleratorBase::create(unsigned dim) {
134 : // I do this by enumeration.
135 : // Maybe can be simplified using the preprocessor, but I am not sure it's worth it
136 2397 : if(dim==1) return std::make_unique<Accelerator<1>>();
137 558 : if(dim==2) return std::make_unique<Accelerator<2>>();
138 8 : if(dim==3) return std::make_unique<Accelerator<3>>();
139 0 : if(dim==4) return std::make_unique<Accelerator<4>>();
140 0 : if(dim==5) return std::make_unique<Accelerator<5>>();
141 0 : if(dim==6) return std::make_unique<Accelerator<6>>();
142 0 : if(dim==7) return std::make_unique<Accelerator<7>>();
143 0 : if(dim==8) return std::make_unique<Accelerator<8>>();
144 0 : if(dim==9) return std::make_unique<Accelerator<9>>();
145 0 : if(dim==10) return std::make_unique<Accelerator<10>>();
146 0 : if(dim==11) return std::make_unique<Accelerator<11>>();
147 0 : if(dim==12) return std::make_unique<Accelerator<12>>();
148 0 : if(dim==13) return std::make_unique<Accelerator<13>>();
149 0 : if(dim==14) return std::make_unique<Accelerator<14>>();
150 0 : if(dim==15) return std::make_unique<Accelerator<15>>();
151 0 : if(dim==16) return std::make_unique<Accelerator<16>>();
152 : // no need to go beyond this
153 0 : plumed_error();
154 : }
155 :
156 1329 : GridBase::GridBase(const std::string& funcl, const std::vector<Value*> & args, const std::vector<std::string> & gmin,
157 1329 : const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, bool usederiv) {
158 : // various checks
159 1329 : plumed_assert(args.size()<=maxdim) << "grid dim cannot exceed "<<maxdim;
160 1329 : plumed_massert(args.size()==gmin.size(),"grid min dimensions in input do not match number of arguments");
161 1329 : plumed_massert(args.size()==nbin.size(),"number of bins on input do not match number of arguments");
162 1329 : plumed_massert(args.size()==gmax.size(),"grid max dimensions in input do not match number of arguments");
163 1329 : unsigned dim=gmax.size();
164 : std::vector<std::string> names;
165 : std::vector<bool> isperiodic;
166 : std::vector<std::string> pmin,pmax;
167 1329 : names.resize( dim );
168 1329 : isperiodic.resize( dim );
169 1329 : pmin.resize( dim );
170 1329 : pmax.resize( dim );
171 2937 : for(unsigned int i=0; i<dim; ++i) {
172 1608 : names[i]=args[i]->getName();
173 1608 : if( args[i]->isPeriodic() ) {
174 : isperiodic[i]=true;
175 502 : args[i]->getDomain( pmin[i], pmax[i] );
176 : } else {
177 : isperiodic[i]=false;
178 : pmin[i]="0.";
179 : pmax[i]="0.";
180 : }
181 : }
182 : // this is a value-independent initializator
183 1329 : Init(funcl,names,gmin,gmax,nbin,dospline,usederiv,isperiodic,pmin,pmax);
184 2658 : }
185 :
186 128 : GridBase::GridBase(const std::string& funcl, const std::vector<std::string> &names, const std::vector<std::string> & gmin,
187 : const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, bool usederiv,
188 128 : const std::vector<bool> &isperiodic, const std::vector<std::string> &pmin, const std::vector<std::string> &pmax ) {
189 : // this calls the initializator
190 128 : Init(funcl,names,gmin,gmax,nbin,dospline,usederiv,isperiodic,pmin,pmax);
191 128 : }
192 :
193 1457 : void GridBase::Init(const std::string& funcl, const std::vector<std::string> &names, const std::vector<std::string> & gmin,
194 : const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, bool usederiv,
195 : const std::vector<bool> &isperiodic, const std::vector<std::string> &pmin, const std::vector<std::string> &pmax ) {
196 1457 : fmt_="%14.9f";
197 : // various checks
198 1457 : plumed_assert(names.size()<=maxdim) << "grid size cannot exceed "<<maxdim;
199 1457 : plumed_massert(names.size()==gmin.size(),"grid dimensions in input do not match number of arguments");
200 1457 : plumed_massert(names.size()==nbin.size(),"grid dimensions in input do not match number of arguments");
201 1457 : plumed_massert(names.size()==gmax.size(),"grid dimensions in input do not match number of arguments");
202 1457 : dimension_=gmax.size();
203 1457 : accelerator=AcceleratorHandler(dimension_);
204 1457 : str_min_=gmin; str_max_=gmax;
205 1457 : argnames.resize( dimension_ );
206 1457 : min_.resize( dimension_ );
207 1457 : max_.resize( dimension_ );
208 1457 : pbc_.resize( dimension_ );
209 3193 : for(unsigned int i=0; i<dimension_; ++i) {
210 1736 : argnames[i]=names[i];
211 1736 : if( isperiodic[i] ) {
212 : pbc_[i]=true;
213 : str_min_[i]=pmin[i];
214 : str_max_[i]=pmax[i];
215 : } else {
216 : pbc_[i]=false;
217 : }
218 1736 : Tools::convert(str_min_[i],min_[i]);
219 1736 : Tools::convert(str_max_[i],max_[i]);
220 1736 : funcname=funcl;
221 1736 : plumed_massert(max_[i]>min_[i],"maximum in grid must be larger than minimum");
222 1736 : plumed_massert(nbin[i]>0,"number of grid points must be greater than zero");
223 : }
224 1457 : nbin_=nbin;
225 1457 : dospline_=dospline;
226 1457 : usederiv_=usederiv;
227 1457 : if(dospline_) plumed_assert(dospline_==usederiv_);
228 1457 : maxsize_=1;
229 3193 : for(unsigned int i=0; i<dimension_; ++i) {
230 1736 : dx_.push_back( (max_[i]-min_[i])/static_cast<double>( nbin_[i] ) );
231 1736 : if( !pbc_[i] ) { max_[i] += dx_[i]; nbin_[i] += 1; }
232 1736 : maxsize_*=nbin_[i];
233 : }
234 1457 : }
235 :
236 1360396 : std::vector<std::string> GridBase::getMin() const {
237 1360396 : return str_min_;
238 : }
239 :
240 345 : std::vector<std::string> GridBase::getMax() const {
241 345 : return str_max_;
242 : }
243 :
244 1319585 : std::vector<double> GridBase::getDx() const {
245 1319585 : return dx_;
246 : }
247 :
248 11944 : double GridBase::getDx(index_t j) const {
249 11944 : return dx_[j];
250 : }
251 :
252 37 : double GridBase::getBinVolume() const {
253 : double vol=1.;
254 102 : for(unsigned i=0; i<dx_.size(); ++i) vol*=dx_[i];
255 37 : return vol;
256 : }
257 :
258 2240 : std::vector<bool> GridBase::getIsPeriodic() const {
259 2240 : return pbc_;
260 : }
261 :
262 1012510 : std::vector<unsigned> GridBase::getNbin() const {
263 1012510 : return nbin_;
264 : }
265 :
266 9436 : std::vector<std::string> GridBase::getArgNames() const {
267 9436 : return argnames;
268 : }
269 :
270 18321217 : unsigned GridBase::getDimension() const {
271 18321217 : return dimension_;
272 : }
273 :
274 3738 : unsigned GridBase::getSplineNeighbors(const unsigned* indices, std::size_t indices_size, index_t* neighbors, std::size_t neighbors_size)const {
275 3738 : plumed_assert(indices_size==dimension_);
276 3738 : unsigned nneigh=1<<dimension_; // same as unsigned(pow(2.0,dimension_));
277 3738 : plumed_assert(neighbors_size==nneigh);
278 :
279 : unsigned nneighbors = 0;
280 :
281 : std::array<unsigned,maxdim> nindices;
282 : unsigned inind;
283 12704 : for(unsigned int i=0; i<nneigh; ++i) {
284 : unsigned tmp=i; inind=0;
285 20912 : for(unsigned int j=0; j<dimension_; ++j) {
286 11946 : unsigned i0=tmp%2+indices[j];
287 11946 : tmp/=2;
288 11946 : if(!pbc_[j] && i0==nbin_[j]) continue;
289 11944 : if( pbc_[j] && i0==nbin_[j]) i0=0;
290 11944 : nindices[inind++]=i0;
291 : }
292 8966 : if(inind==dimension_) neighbors[nneighbors++]=getIndex(nindices.data(),dimension_);
293 : }
294 3738 : return nneighbors;
295 : }
296 :
297 9577 : std::vector<GridBase::index_t> GridBase::getNearestNeighbors(const index_t index) const {
298 9577 : std::vector<index_t> nearest_neighs = std::vector<index_t>();
299 28730 : for (unsigned i = 0; i < dimension_; i++) {
300 19153 : std::vector<unsigned> neighsneeded = std::vector<unsigned>(dimension_, 0);
301 19153 : neighsneeded[i] = 1;
302 19153 : std::vector<index_t> singledim_nearest_neighs = getNeighbors(index, neighsneeded);
303 74978 : for (unsigned j = 0; j < singledim_nearest_neighs.size(); j++) {
304 55825 : index_t neigh = singledim_nearest_neighs[j];
305 55825 : if (neigh != index) {
306 36672 : nearest_neighs.push_back(neigh);
307 : }
308 : }
309 : }
310 9577 : return nearest_neighs;
311 : }
312 :
313 0 : std::vector<GridBase::index_t> GridBase::getNearestNeighbors(const std::vector<unsigned> &indices) const {
314 : plumed_dbg_assert(indices.size() == dimension_);
315 0 : return getNearestNeighbors(getIndex(indices));
316 : }
317 :
318 0 : void GridBase::addKernel( const KernelFunctions& kernel ) {
319 : plumed_dbg_assert( kernel.ndim()==dimension_ );
320 0 : std::vector<unsigned> nneighb=kernel.getSupport( dx_ );
321 0 : std::vector<index_t> neighbors=getNeighbors( kernel.getCenter(), nneighb );
322 0 : std::vector<double> xx( dimension_ );
323 0 : std::vector<std::unique_ptr<Value>> vv( dimension_ );
324 : std::string str_min, str_max;
325 0 : for(unsigned i=0; i<dimension_; ++i) {
326 0 : vv[i]=Tools::make_unique<Value>();
327 0 : if( pbc_[i] ) {
328 0 : Tools::convert(min_[i],str_min);
329 0 : Tools::convert(max_[i],str_max);
330 0 : vv[i]->setDomain( str_min, str_max );
331 : } else {
332 0 : vv[i]->setNotPeriodic();
333 : }
334 : }
335 :
336 : // vv_ptr contains plain pointers obtained from vv.
337 : // this is the simplest way to replace a unique_ptr here.
338 : // perhaps the interface of kernel.evaluate() should be changed
339 : // in order to accept a std::vector<std::unique_ptr<Value>>
340 0 : auto vv_ptr=Tools::unique2raw(vv);
341 :
342 0 : std::vector<double> der( dimension_ );
343 0 : for(unsigned i=0; i<neighbors.size(); ++i) {
344 0 : index_t ineigh=neighbors[i];
345 0 : getPoint( ineigh, xx );
346 0 : for(unsigned j=0; j<dimension_; ++j) vv[j]->set(xx[j]);
347 0 : double newval = kernel.evaluate( vv_ptr, der, usederiv_ );
348 0 : if( usederiv_ ) addValueAndDerivatives( ineigh, newval, der );
349 0 : else addValue( ineigh, newval );
350 : }
351 0 : }
352 :
353 1193613 : double GridBase::getValue(const std::vector<unsigned> & indices) const {
354 1193613 : return getValue(getIndex(indices));
355 : }
356 :
357 357 : double GridBase::getValue(const std::vector<double> & x) const {
358 357 : if(!dospline_) {
359 18 : return getValue(getIndex(x));
360 : } else {
361 339 : std::vector<double> der(dimension_);
362 339 : return getValueAndDerivatives(x,der);
363 : }
364 : }
365 :
366 3071066 : double GridBase::getValueAndDerivatives(index_t index, std::vector<double>& der) const {
367 : plumed_dbg_assert(index<maxsize_ && usederiv_ && der.size()==dimension_);
368 3071066 : der.resize(dimension_);
369 3071066 : return getValueAndDerivatives(index,der.data(),der.size());
370 : }
371 :
372 2527708 : double GridBase::getValueAndDerivatives(const std::vector<unsigned> & indices, std::vector<double>& der) const {
373 2527708 : return getValueAndDerivatives(getIndex(indices),der);
374 : }
375 :
376 3738 : double GridBase::getValueAndDerivatives(const std::vector<double> & x, std::vector<double>& der) const {
377 : plumed_dbg_assert(der.size()==dimension_ && usederiv_);
378 :
379 3738 : if(dospline_) {
380 : double X,X2,X3,value;
381 : std::array<double,maxdim> fd, C, D;
382 : std::array<double,maxdim> dder;
383 : // reset
384 : value=0.0;
385 8221 : for(unsigned int i=0; i<dimension_; ++i) der[i]=0.0;
386 :
387 : std::array<unsigned,maxdim> indices;
388 3738 : getIndices(x, indices.data(),dimension_);
389 : std::array<double,maxdim> xfloor;
390 3738 : getPoint(indices.data(), dimension_, xfloor.data(),dimension_);
391 3738 : gch::small_vector<index_t,16> neigh(1<<dimension_); // pow(2,dimension_); up to dimension 4 will stay on stack
392 3738 : auto nneigh = getSplineNeighbors(indices.data(),dimension_, neigh.data(), neigh.size());
393 :
394 : // loop over neighbors
395 : std::array<unsigned,maxdim> nindices;
396 12702 : for(unsigned int ipoint=0; ipoint<nneigh; ++ipoint) {
397 8964 : double grid=getValueAndDerivatives(neigh[ipoint],dder.data(),dimension_);
398 8964 : getIndices(neigh[ipoint], nindices.data(), dimension_);
399 : double ff=1.0;
400 :
401 20908 : for(unsigned j=0; j<dimension_; ++j) {
402 : int x0=1;
403 11944 : if(nindices[j]==indices[j]) x0=0;
404 11944 : double dx=getDx(j);
405 11944 : X=std::abs((x[j]-xfloor[j])/dx-(double)x0);
406 11944 : X2=X*X;
407 11944 : X3=X2*X;
408 : double yy;
409 11944 : if(std::abs(grid)<0.0000001) yy=0.0;
410 9032 : else yy=-dder[j]/grid;
411 11944 : C[j]=(1.0-3.0*X2+2.0*X3) - (x0?-1.0:1.0)*yy*(X-2.0*X2+X3)*dx;
412 11944 : D[j]=( -6.0*X +6.0*X2) - (x0?-1.0:1.0)*yy*(1.0-4.0*X +3.0*X2)*dx;
413 11944 : D[j]*=(x0?-1.0:1.0)/dx;
414 11944 : ff*=C[j];
415 : }
416 20908 : for(unsigned j=0; j<dimension_; ++j) {
417 11944 : fd[j]=D[j];
418 29848 : for(unsigned i=0; i<dimension_; ++i) if(i!=j) fd[j]*=C[i];
419 : }
420 8964 : value+=grid*ff;
421 20908 : for(unsigned j=0; j<dimension_; ++j) der[j]+=grid*fd[j];
422 : }
423 : return value;
424 : } else {
425 0 : return getValueAndDerivatives(getIndex(x),der);
426 : }
427 : }
428 :
429 0 : void GridBase::setValue(const std::vector<unsigned> & indices, double value) {
430 0 : setValue(getIndex(indices),value);
431 0 : }
432 :
433 0 : void GridBase::setValueAndDerivatives(const std::vector<unsigned> & indices, double value, std::vector<double>& der) {
434 0 : setValueAndDerivatives(getIndex(indices),value,der);
435 0 : }
436 :
437 0 : void GridBase::addValue(const std::vector<unsigned> & indices, double value) {
438 0 : addValue(getIndex(indices),value);
439 0 : }
440 :
441 0 : void GridBase::addValueAndDerivatives(const std::vector<unsigned> & indices, double value, std::vector<double>& der) {
442 0 : addValueAndDerivatives(getIndex(indices),value,der);
443 0 : }
444 :
445 1147 : void GridBase::writeHeader(OFile& ofile) {
446 2611 : for(unsigned i=0; i<dimension_; ++i) {
447 2928 : ofile.addConstantField("min_" + argnames[i]);
448 2928 : ofile.addConstantField("max_" + argnames[i]);
449 2928 : ofile.addConstantField("nbins_" + argnames[i]);
450 2928 : ofile.addConstantField("periodic_" + argnames[i]);
451 : }
452 1147 : }
453 :
454 1 : void Grid::clear() {
455 1 : grid_.assign(maxsize_,0.0);
456 1 : if(usederiv_) der_.assign(maxsize_*dimension_,0.0);
457 1 : }
458 :
459 1147 : void Grid::writeToFile(OFile& ofile) {
460 1147 : std::vector<double> xx(dimension_);
461 1147 : std::vector<double> der(dimension_);
462 : double f;
463 1147 : writeHeader(ofile);
464 2533412 : for(index_t i=0; i<getSize(); ++i) {
465 2532265 : xx=getPoint(i);
466 2532265 : if(usederiv_) {f=getValueAndDerivatives(i,der);}
467 1989707 : else {f=getValue(i);}
468 4914902 : if(i>0 && dimension_>1 && getIndices(i)[dimension_-2]==0) ofile.printf("\n");
469 7484524 : for(unsigned j=0; j<dimension_; ++j) {
470 9904518 : ofile.printField("min_" + argnames[j], str_min_[j] );
471 9904518 : ofile.printField("max_" + argnames[j], str_max_[j] );
472 9904518 : ofile.printField("nbins_" + argnames[j], static_cast<int>(nbin_[j]) );
473 6986607 : if( pbc_[j] ) ofile.printField("periodic_" + argnames[j], "true" );
474 5835822 : else ofile.printField("periodic_" + argnames[j], "false" );
475 : }
476 7484524 : for(unsigned j=0; j<dimension_; ++j) { ofile.fmtField(" "+fmt_); ofile.printField(argnames[j],xx[j]); }
477 5064530 : ofile.fmtField(" "+fmt_); ofile.printField(funcname,f);
478 5194297 : if(usederiv_) for(unsigned j=0; j<dimension_; ++j) { ofile.fmtField(" "+fmt_); ofile.printField("der_" + argnames[j],der[j]); }
479 2532265 : ofile.printField();
480 : }
481 1147 : }
482 :
483 0 : void GridBase::writeCubeFile(OFile& ofile, const double& lunit) {
484 0 : plumed_assert( dimension_==3 );
485 0 : ofile.printf("PLUMED CUBE FILE\n");
486 0 : ofile.printf("OUTER LOOP: X, MIDDLE LOOP: Y, INNER LOOP: Z\n");
487 : // Number of atoms followed by position of origin (origin set so that center of grid is in center of cell)
488 0 : ofile.printf("%d %f %f %f\n",1,-0.5*lunit*(max_[0]-min_[0]),-0.5*lunit*(max_[1]-min_[1]),-0.5*lunit*(max_[2]-min_[2]));
489 0 : ofile.printf("%u %f %f %f\n",nbin_[0],lunit*dx_[0],0.0,0.0); // Number of bins in each direction followed by
490 0 : ofile.printf("%u %f %f %f\n",nbin_[1],0.0,lunit*dx_[1],0.0); // shape of voxel
491 0 : ofile.printf("%u %f %f %f\n",nbin_[2],0.0,0.0,lunit*dx_[2]);
492 0 : ofile.printf("%d %f %f %f\n",1,0.0,0.0,0.0); // Fake atom otherwise VMD doesn't work
493 0 : std::vector<unsigned> pp(3);
494 0 : for(pp[0]=0; pp[0]<nbin_[0]; ++pp[0]) {
495 0 : for(pp[1]=0; pp[1]<nbin_[1]; ++pp[1]) {
496 0 : for(pp[2]=0; pp[2]<nbin_[2]; ++pp[2]) {
497 0 : ofile.printf("%f ",getValue(pp) );
498 0 : if(pp[2]%6==5) ofile.printf("\n");
499 : }
500 0 : ofile.printf("\n");
501 : }
502 : }
503 0 : }
504 :
505 20 : std::unique_ptr<GridBase> GridBase::create(const std::string& funcl, const std::vector<Value*> & args, IFile& ifile,
506 : const std::vector<std::string> & gmin,const std::vector<std::string> & gmax,
507 : const std::vector<unsigned> & nbin,bool dosparse, bool dospline, bool doder) {
508 20 : std::unique_ptr<GridBase> grid=GridBase::create(funcl,args,ifile,dosparse,dospline,doder);
509 20 : std::vector<unsigned> cbin( grid->getNbin() );
510 20 : std::vector<std::string> cmin( grid->getMin() ), cmax( grid->getMax() );
511 49 : for(unsigned i=0; i<args.size(); ++i) {
512 29 : plumed_massert( cmin[i]==gmin[i], "mismatched grid min" );
513 29 : plumed_massert( cmax[i]==gmax[i], "mismatched grid max" );
514 29 : if( args[i]->isPeriodic() ) {
515 8 : plumed_massert( cbin[i]==nbin[i], "mismatched grid nbins" );
516 : } else {
517 21 : plumed_massert( (cbin[i]-1)==nbin[i], "mismatched grid nbins");
518 : }
519 : }
520 20 : return grid;
521 20 : }
522 :
523 70 : std::unique_ptr<GridBase> GridBase::create(const std::string& funcl, const std::vector<Value*> & args, IFile& ifile, bool dosparse, bool dospline, bool doder)
524 : {
525 70 : std::unique_ptr<GridBase> grid;
526 70 : unsigned nvar=args.size(); bool hasder=false; std::string pstring;
527 70 : std::vector<int> gbin1(nvar); std::vector<unsigned> gbin(nvar);
528 70 : std::vector<std::string> labels(nvar),gmin(nvar),gmax(nvar);
529 70 : std::vector<std::string> fieldnames; ifile.scanFieldList( fieldnames );
530 :
531 : // Retrieve names for fields
532 158 : for(unsigned i=0; i<args.size(); ++i) {
533 88 : labels[i]=args[i]->getName(); bool found=false;
534 106 : for(unsigned j=0; j<fieldnames.size(); ++j) {
535 106 : if( labels[i]==fieldnames[j] ) { found=true; break; }
536 : }
537 : // This is a change to ensure that we can deal with old style names for multicolvars
538 88 : std::size_t und = labels[i].find_first_of("_");
539 88 : if( !found && und!=std::string::npos ) {
540 0 : labels[i] = labels[i].substr(0,und) + "." + labels[i].substr(und+1);
541 0 : for(unsigned j=0; j<fieldnames.size(); ++j) {
542 0 : if( labels[i]==fieldnames[j] ) { found=true; break; }
543 : }
544 : }
545 : }
546 : // And read the stuff from the header
547 70 : plumed_massert( ifile.FieldExist( funcl ), "no column labelled " + funcl + " in in grid input");
548 158 : for(unsigned i=0; i<args.size(); ++i) {
549 176 : ifile.scanField( "min_" + labels[i], gmin[i]);
550 176 : ifile.scanField( "max_" + labels[i], gmax[i]);
551 176 : ifile.scanField( "periodic_" + labels[i], pstring );
552 176 : ifile.scanField( "nbins_" + labels[i], gbin1[i]);
553 88 : plumed_assert( gbin1[i]>0 );
554 88 : if( args[i]->isPeriodic() ) {
555 26 : plumed_massert( pstring=="true", "input value is periodic but grid is not");
556 : std::string pmin, pmax;
557 26 : args[i]->getDomain( pmin, pmax ); gbin[i]=gbin1[i];
558 26 : if( pmin!=gmin[i] || pmax!=gmax[i] ) plumed_merror("mismatch between grid boundaries and periods of values");
559 : } else {
560 62 : gbin[i]=gbin1[i]-1; // Note header in grid file indicates one more bin that there should be when data is not periodic
561 62 : plumed_massert( pstring=="false", "input value is not periodic but grid is");
562 : }
563 88 : hasder=ifile.FieldExist( "der_" + labels[i] );
564 88 : if( doder && !hasder ) plumed_merror("missing derivatives from grid file");
565 106 : for(unsigned j=0; j<fieldnames.size(); ++j) {
566 124 : for(unsigned k=i+1; k<args.size(); ++k) {
567 18 : if( fieldnames[j]==labels[k] ) plumed_merror("arguments in input are not in same order as in grid file");
568 : }
569 106 : if( fieldnames[j]==labels[i] ) break;
570 : }
571 : }
572 :
573 140 : if(!dosparse) {grid=Tools::make_unique<Grid>(funcl,args,gmin,gmax,gbin,dospline,doder);}
574 0 : else {grid=Tools::make_unique<SparseGrid>(funcl,args,gmin,gmax,gbin,dospline,doder);}
575 :
576 70 : std::vector<double> xx(nvar),dder(nvar);
577 70 : std::vector<double> dx=grid->getDx();
578 : double f,x;
579 1099575 : while( ifile.scanField(funcl,f) ) {
580 3294553 : for(unsigned i=0; i<nvar; ++i) {
581 2195048 : ifile.scanField(labels[i],x); xx[i]=x+dx[i]/2.0;
582 4390096 : ifile.scanField( "min_" + labels[i], gmin[i]);
583 4390096 : ifile.scanField( "max_" + labels[i], gmax[i]);
584 4390096 : ifile.scanField( "nbins_" + labels[i], gbin1[i]);
585 4390096 : ifile.scanField( "periodic_" + labels[i], pstring );
586 : }
587 3211140 : if(hasder) { for(unsigned i=0; i<nvar; ++i) { ifile.scanField( "der_" + labels[i], dder[i] ); } }
588 1099505 : index_t index=grid->getIndex(xx);
589 1099505 : if(doder) {grid->setValueAndDerivatives(index,f,dder);}
590 42717 : else {grid->setValue(index,f);}
591 1099505 : ifile.scanField();
592 : }
593 70 : return grid;
594 70 : }
595 :
596 861 : double Grid::getMinValue() const {
597 : double minval;
598 : minval=DBL_MAX;
599 2258311 : for(index_t i=0; i<grid_.size(); ++i) {
600 2257450 : if(grid_[i]<minval)minval=grid_[i];
601 : }
602 861 : return minval;
603 : }
604 :
605 629 : double Grid::getMaxValue() const {
606 : double maxval;
607 : maxval=DBL_MIN;
608 3755246 : for(index_t i=0; i<grid_.size(); ++i) {
609 3754617 : if(grid_[i]>maxval)maxval=grid_[i];
610 : }
611 629 : return maxval;
612 : }
613 :
614 594 : void Grid::scaleAllValuesAndDerivatives( const double& scalef ) {
615 594 : if(usederiv_) {
616 21474 : for(index_t i=0; i<grid_.size(); ++i) {
617 21463 : grid_[i]*=scalef;
618 63888 : for(unsigned j=0; j<dimension_; ++j) der_[i*dimension_+j]*=scalef;
619 : }
620 : } else {
621 1572834 : for(index_t i=0; i<grid_.size(); ++i) grid_[i]*=scalef;
622 : }
623 594 : }
624 :
625 0 : void Grid::logAllValuesAndDerivatives( const double& scalef ) {
626 0 : if(usederiv_) {
627 0 : for(index_t i=0; i<grid_.size(); ++i) {
628 0 : grid_[i] = scalef*std::log(grid_[i]);
629 0 : for(unsigned j=0; j<dimension_; ++j) der_[i*dimension_+j] = scalef/der_[i*dimension_+j];
630 : }
631 : } else {
632 0 : for(index_t i=0; i<grid_.size(); ++i) grid_[i] = scalef*std::log(grid_[i]);
633 : }
634 0 : }
635 :
636 1329 : void Grid::setMinToZero() {
637 1329 : double min=grid_[0];
638 3518541 : for(index_t i=1; i<grid_.size(); ++i) if(grid_[i]<min) min=grid_[i];
639 3519870 : for(index_t i=0; i<grid_.size(); ++i) grid_[i] -= min;
640 1329 : }
641 :
642 1 : void Grid::applyFunctionAllValuesAndDerivatives( double (*func)(double val), double (*funcder)(double valder) ) {
643 1 : if(usederiv_) {
644 901 : for(index_t i=0; i<grid_.size(); ++i) {
645 900 : grid_[i]=func(grid_[i]);
646 2700 : for(unsigned j=0; j<dimension_; ++j) der_[i*dimension_+j]=funcder(der_[i*dimension_+j]);
647 : }
648 : } else {
649 0 : for(index_t i=0; i<grid_.size(); ++i) grid_[i]=func(grid_[i]);
650 : }
651 1 : }
652 :
653 0 : double Grid::getDifferenceFromContour( const std::vector<double>& x, std::vector<double>& der ) const {
654 0 : return getValueAndDerivatives( x, der ) - contour_location;
655 : }
656 :
657 0 : void Grid::findSetOfPointsOnContour(const double& target, const std::vector<bool>& nosearch,
658 : unsigned& npoints, std::vector<std::vector<double> >& points ) {
659 : // Set contour location for function
660 0 : contour_location=target;
661 : // Resize points to maximum possible value
662 0 : points.resize( dimension_*maxsize_ );
663 :
664 : // Two points for search
665 0 : std::vector<unsigned> ind(dimension_);
666 0 : std::vector<double> direction( dimension_, 0 );
667 :
668 : // Run over whole grid
669 0 : npoints=0; RootFindingBase<Grid> mymin( this );
670 0 : for(unsigned i=0; i<maxsize_; ++i) {
671 0 : for(unsigned j=0; j<dimension_; ++j) ind[j]=getIndices(i)[j];
672 :
673 : // Get the value of a point on the grid
674 0 : double val1=getValue(i) - target;
675 :
676 : // Now search for contour in each direction
677 : bool edge=false;
678 0 : for(unsigned j=0; j<dimension_; ++j) {
679 0 : if( nosearch[j] ) continue ;
680 : // Make sure we don't search at the edge of the grid
681 0 : if( !pbc_[j] && (ind[j]+1)==nbin_[j] ) continue;
682 0 : else if( (ind[j]+1)==nbin_[j] ) { edge=true; ind[j]=0; }
683 0 : else ind[j]+=1;
684 0 : double val2=getValue(ind) - target;
685 0 : if( val1*val2<0 ) {
686 : // Use initial point location as first guess for search
687 0 : points[npoints].resize(dimension_); for(unsigned k=0; k<dimension_; ++k) points[npoints][k]=getPoint(i)[k];
688 : // Setup direction vector
689 0 : direction[j]=0.999999999*dx_[j];
690 : // And do proper search for contour point
691 0 : mymin.linesearch( direction, points[npoints], &Grid::getDifferenceFromContour );
692 0 : direction[j]=0.0; npoints++;
693 : }
694 0 : if( pbc_[j] && edge ) { edge=false; ind[j]=nbin_[j]-1; }
695 0 : else ind[j]-=1;
696 : }
697 : }
698 0 : }
699 :
700 : /// OVERRIDES ARE BELOW
701 :
702 28865222 : Grid::index_t Grid::getSize() const {
703 28865222 : return maxsize_;
704 : }
705 :
706 27284983 : double Grid::getValue(index_t index) const {
707 : plumed_dbg_assert(index<maxsize_);
708 27284983 : return grid_[index];
709 : }
710 :
711 3079830 : double Grid::getValueAndDerivatives(index_t index, double* der,std::size_t der_size) const {
712 : plumed_dbg_assert(index<maxsize_ && usederiv_ && der_size==dimension_);
713 6679639 : for(unsigned i=0; i<dimension_; i++) der[i]=der_[dimension_*index+i];
714 3079830 : return grid_[index];
715 : }
716 :
717 6444104 : void Grid::setValue(index_t index, double value) {
718 : plumed_dbg_assert(index<maxsize_ && !usederiv_);
719 6444104 : grid_[index]=value;
720 6444104 : }
721 :
722 2552369 : void Grid::setValueAndDerivatives(index_t index, double value, std::vector<double>& der) {
723 : plumed_dbg_assert(index<maxsize_ && usederiv_ && der.size()==dimension_);
724 2552369 : grid_[index]=value;
725 7609163 : for(unsigned i=0; i<dimension_; i++) der_[dimension_*index+i]=der[i];
726 2552369 : }
727 :
728 1 : void Grid::addValue(index_t index, double value) {
729 : plumed_dbg_assert(index<maxsize_ && !usederiv_);
730 1 : grid_[index]+=value;
731 1 : }
732 :
733 1172186 : void Grid::addValueAndDerivatives(index_t index, double value, std::vector<double>& der) {
734 : plumed_dbg_assert(index<maxsize_ && usederiv_ && der.size()==dimension_);
735 1172186 : grid_[index]+=value;
736 3501367 : for(unsigned int i=0; i<dimension_; ++i) der_[index*dimension_+i]+=der[i];
737 1172186 : }
738 :
739 0 : Grid::index_t SparseGrid::getSize() const {
740 0 : return map_.size();
741 : }
742 :
743 0 : Grid::index_t SparseGrid::getMaxSize() const {
744 0 : return maxsize_;
745 : }
746 :
747 0 : double SparseGrid::getValue(index_t index)const {
748 0 : plumed_assert(index<maxsize_);
749 : double value=0.0;
750 : const auto it=map_.find(index);
751 0 : if(it!=map_.end()) value=it->second;
752 0 : return value;
753 : }
754 :
755 200 : double SparseGrid::getValueAndDerivatives(index_t index, double* der, std::size_t der_size)const {
756 200 : plumed_assert(index<maxsize_ && usederiv_ && der_size==dimension_);
757 : double value=0.0;
758 580 : for(unsigned int i=0; i<dimension_; ++i) der[i]=0.0;
759 : const auto it=map_.find(index);
760 200 : if(it!=map_.end()) value=it->second;
761 : const auto itder=der_.find(index);
762 200 : if(itder!=der_.end()) {
763 : const auto & second(itder->second);
764 384 : for(unsigned i=0; i<second.size(); i++) der[i]=itder->second[i];
765 : }
766 200 : return value;
767 : }
768 :
769 0 : void SparseGrid::setValue(index_t index, double value) {
770 0 : plumed_assert(index<maxsize_ && !usederiv_);
771 0 : map_[index]=value;
772 0 : }
773 :
774 0 : void SparseGrid::setValueAndDerivatives(index_t index, double value, std::vector<double>& der) {
775 0 : plumed_assert(index<maxsize_ && usederiv_ && der.size()==dimension_);
776 0 : map_[index]=value;
777 0 : der_[index]=der;
778 0 : }
779 :
780 0 : void SparseGrid::addValue(index_t index, double value) {
781 0 : plumed_assert(index<maxsize_ && !usederiv_);
782 0 : map_[index]+=value;
783 0 : }
784 :
785 19999 : void SparseGrid::addValueAndDerivatives(index_t index, double value, std::vector<double>& der) {
786 19999 : plumed_assert(index<maxsize_ && usederiv_ && der.size()==dimension_);
787 19999 : map_[index]+=value;
788 19999 : der_[index].resize(dimension_);
789 59682 : for(unsigned int i=0; i<dimension_; ++i) der_[index][i]+=der[i];
790 19999 : }
791 :
792 0 : void SparseGrid::writeToFile(OFile& ofile) {
793 0 : std::vector<double> xx(dimension_);
794 0 : std::vector<double> der(dimension_);
795 : double f;
796 0 : writeHeader(ofile);
797 0 : ofile.fmtField(" "+fmt_);
798 0 : for(const auto & it : map_) {
799 0 : index_t i=it.first;
800 0 : xx=getPoint(i);
801 0 : if(usederiv_) {f=getValueAndDerivatives(i,der);}
802 0 : else {f=getValue(i);}
803 0 : if(i>0 && dimension_>1 && getIndices(i)[dimension_-2]==0) ofile.printf("\n");
804 0 : for(unsigned j=0; j<dimension_; ++j) {
805 0 : ofile.printField("min_" + argnames[j], str_min_[j] );
806 0 : ofile.printField("max_" + argnames[j], str_max_[j] );
807 0 : ofile.printField("nbins_" + argnames[j], static_cast<int>(nbin_[j]) );
808 0 : if( pbc_[j] ) ofile.printField("periodic_" + argnames[j], "true" );
809 0 : else ofile.printField("periodic_" + argnames[j], "false" );
810 : }
811 0 : for(unsigned j=0; j<dimension_; ++j) ofile.printField(argnames[j],xx[j]);
812 0 : ofile.printField(funcname, f);
813 0 : if(usederiv_) { for(unsigned j=0; j<dimension_; ++j) ofile.printField("der_" + argnames[j],der[j]); }
814 0 : ofile.printField();
815 : }
816 0 : }
817 :
818 0 : double SparseGrid::getMinValue() const {
819 : double minval;
820 : minval=0.0;
821 0 : for(auto const & i : map_) {
822 0 : if(i.second<minval) minval=i.second;
823 : }
824 0 : return minval;
825 : }
826 :
827 9 : double SparseGrid::getMaxValue() const {
828 : double maxval;
829 : maxval=0.0;
830 590 : for(auto const & i : map_) {
831 581 : if(i.second>maxval) maxval=i.second;
832 : }
833 9 : return maxval;
834 : }
835 :
836 1010062 : void Grid::projectOnLowDimension(double &val, std::vector<int> &vHigh, WeightBase * ptr2obj ) {
837 : unsigned i=0;
838 3016945 : for(i=0; i<vHigh.size(); i++) {
839 2015726 : if(vHigh[i]<0) { // this bin needs to be integrated out
840 : // parallelize here???
841 1010062 : for(unsigned j=0; j<(getNbin())[i]; j++) {
842 1001219 : vHigh[i]=int(j);
843 1001219 : projectOnLowDimension(val,vHigh,ptr2obj); // recursive function: this is the core of the mechanism
844 1001219 : vHigh[i]=-1;
845 : }
846 : return; //
847 : }
848 : }
849 : // when there are no more bin to dig in then retrieve the value
850 1001219 : if(i==vHigh.size()) {
851 : //std::cerr<<"POINT: ";
852 : //for(unsigned j=0;j<vHigh.size();j++){
853 : // std::cerr<<vHigh[j]<<" ";
854 : //}
855 1001219 : std::vector<unsigned> vv(vHigh.size());
856 3003657 : for(unsigned j=0; j<vHigh.size(); j++)vv[j]=unsigned(vHigh[j]);
857 : //
858 : // this is the real assignment !!!!! (hack this to have bias or other stuff)
859 : //
860 :
861 : // this case: produce fes
862 : //val+=exp(beta*getValue(vv)) ;
863 1001219 : double myv=getValue(vv);
864 1001219 : val=ptr2obj->projectInnerLoop(val,myv) ;
865 : // to be added: bias (same as before without negative sign)
866 : //std::cerr<<" VAL: "<<val <<endl;
867 : }
868 : }
869 :
870 81 : Grid Grid::project(const std::vector<std::string> & proj, WeightBase *ptr2obj ) {
871 : // find extrema only for the projection
872 : std::vector<std::string> smallMin,smallMax;
873 : std::vector<unsigned> smallBin;
874 : std::vector<unsigned> dimMapping;
875 : std::vector<bool> smallIsPeriodic;
876 : std::vector<std::string> smallName;
877 :
878 : // check if the two key methods are there
879 : WeightBase* pp = dynamic_cast<WeightBase*>(ptr2obj);
880 81 : if (!pp)plumed_merror("This WeightBase is not complete: you need a projectInnerLoop and projectOuterLoop ");
881 :
882 162 : for(unsigned j=0; j<proj.size(); j++) {
883 120 : for(unsigned i=0; i<getArgNames().size(); i++) {
884 120 : if(proj[j]==getArgNames()[i]) {
885 : unsigned offset;
886 : // note that at sizetime the non periodic dimension get a bin more
887 162 : if(getIsPeriodic()[i]) {offset=0;} else {offset=1;}
888 81 : smallMax.push_back(getMax()[i]);
889 81 : smallMin.push_back(getMin()[i]);
890 81 : smallBin.push_back(getNbin()[i]-offset);
891 162 : smallIsPeriodic.push_back(getIsPeriodic()[i]);
892 81 : dimMapping.push_back(i);
893 81 : smallName.push_back(getArgNames()[i]);
894 81 : break;
895 : }
896 : }
897 : }
898 81 : Grid smallgrid("projection",smallName,smallMin,smallMax,smallBin,false,false,smallIsPeriodic,smallMin,smallMax);
899 : // check that the two grids are commensurate
900 162 : for(unsigned i=0; i<dimMapping.size(); i++) {
901 81 : plumed_massert( (smallgrid.getMax())[i] == (getMax())[dimMapping[i]], "the two input grids are not compatible in max" );
902 81 : plumed_massert( (smallgrid.getMin())[i] == (getMin())[dimMapping[i]], "the two input grids are not compatible in min" );
903 81 : plumed_massert( (smallgrid.getNbin())[i]== (getNbin())[dimMapping[i]], "the two input grids are not compatible in bin" );
904 : }
905 : std::vector<unsigned> toBeIntegrated;
906 243 : for(unsigned i=0; i<getArgNames().size(); i++) {
907 : bool doappend=true;
908 243 : for(unsigned j=0; j<dimMapping.size(); j++) {
909 162 : if(dimMapping[j]==i) {doappend=false; break;}
910 : }
911 162 : if(doappend)toBeIntegrated.push_back(i);
912 : }
913 :
914 : // loop over all the points in the Grid, find the corresponding fixed index, rotate over all the other ones
915 8924 : for(unsigned i=0; i<smallgrid.getSize(); i++) {
916 : std::vector<unsigned> v;
917 8843 : v=smallgrid.getIndices(i);
918 8843 : std::vector<int> vHigh((getArgNames()).size(),-1);
919 17686 : for(unsigned j=0; j<dimMapping.size(); j++)vHigh[dimMapping[j]]=int(v[j]);
920 : // the vector vhigh now contains at the beginning the index of the low dimension and -1 for the values that need to be integrated
921 8843 : double initval=0.;
922 8843 : projectOnLowDimension(initval,vHigh, ptr2obj);
923 8843 : smallgrid.setValue(i,ptr2obj->projectOuterLoop(initval));
924 : }
925 :
926 81 : return smallgrid;
927 162 : }
928 :
929 0 : double Grid::integrate( std::vector<unsigned>& npoints ) {
930 0 : plumed_dbg_assert( npoints.size()==dimension_ ); plumed_assert( dospline_ );
931 :
932 : unsigned ntotgrid=1; double box_vol=1.0;
933 0 : std::vector<double> ispacing( npoints.size() );
934 0 : for(unsigned j=0; j<dimension_; ++j) {
935 0 : if( !pbc_[j] ) {
936 0 : ispacing[j] = ( max_[j] - dx_[j] - min_[j] ) / static_cast<double>( npoints[j] );
937 0 : npoints[j]+=1;
938 : } else {
939 0 : ispacing[j] = ( max_[j] - min_[j] ) / static_cast<double>( npoints[j] );
940 : }
941 0 : ntotgrid*=npoints[j]; box_vol*=ispacing[j];
942 : }
943 :
944 0 : std::vector<double> vals( dimension_ );
945 0 : std::vector<unsigned> t_index( dimension_ ); double integral=0.0;
946 0 : for(unsigned i=0; i<ntotgrid; ++i) {
947 0 : t_index[0]=(i%npoints[0]);
948 : unsigned kk=i;
949 0 : for(unsigned j=1; j<dimension_-1; ++j) { kk=(kk-t_index[j-1])/npoints[j-1]; t_index[j]=(kk%npoints[j]); }
950 0 : if( dimension_>=2 ) t_index[dimension_-1]=((kk-t_index[dimension_-2])/npoints[dimension_-2]);
951 :
952 0 : for(unsigned j=0; j<dimension_; ++j) vals[j]=min_[j] + t_index[j]*ispacing[j];
953 :
954 0 : integral += getValue( vals );
955 : }
956 :
957 0 : return box_vol*integral;
958 : }
959 :
960 0 : void Grid::mpiSumValuesAndDerivatives( Communicator& comm ) {
961 0 : comm.Sum( grid_ ); for(unsigned i=0; i<der_.size(); ++i) comm.Sum( der_[i] );
962 0 : }
963 :
964 :
965 59573 : bool indexed_lt(std::pair<Grid::index_t, double> const &x, std::pair<Grid::index_t, double> const &y) {
966 59573 : return x.second < y.second;
967 : }
968 :
969 273 : double GridBase::findMaximalPathMinimum(const std::vector<double> &source, const std::vector<double> &sink) {
970 : plumed_dbg_assert(source.size() == dimension_);
971 : plumed_dbg_assert(sink.size() == dimension_);
972 : // Start and end indices
973 273 : index_t source_idx = getIndex(source);
974 273 : index_t sink_idx = getIndex(sink);
975 : // Path cost
976 : double maximal_minimum = 0;
977 : // In one dimension, path searching is very easy--either go one way if it's not periodic,
978 : // or go both ways if it is periodic. There's no reason to pay the cost of Dijkstra.
979 273 : if (dimension_ == 1) {
980 : // Do a search from the grid source to grid sink that does not
981 : // cross the grid boundary.
982 147 : double curr_min_bias = getValue(source_idx);
983 : // Either search from a high source to a low sink.
984 147 : if (source_idx > sink_idx) {
985 735 : for (index_t i = source_idx; i >= sink_idx; i--) {
986 588 : if (curr_min_bias == 0.0) {
987 : break;
988 : }
989 588 : curr_min_bias = fmin(curr_min_bias, getValue(i));
990 : }
991 : // Or search from a low source to a high sink.
992 0 : } else if (source_idx < sink_idx) {
993 0 : for (index_t i = source_idx; i <= sink_idx; i++) {
994 0 : if (curr_min_bias == 0.0) {
995 : break;
996 : }
997 0 : curr_min_bias = fmin(curr_min_bias, getValue(i));
998 : }
999 : }
1000 : maximal_minimum = curr_min_bias;
1001 : // If the grid is periodic, also do the search that crosses
1002 : // the grid boundary.
1003 147 : if (pbc_[0]) {
1004 42 : double curr_min_bias = getValue(source_idx);
1005 : // Either go from a high source to the upper boundary and
1006 : // then from the bottom boundary to the sink
1007 42 : if (source_idx > sink_idx) {
1008 210 : for (index_t i = source_idx; i < maxsize_; i++) {
1009 168 : if (curr_min_bias == 0.0) {
1010 : break;
1011 : }
1012 168 : curr_min_bias = fmin(curr_min_bias, getValue(i));
1013 : }
1014 210 : for (index_t i = 0; i <= sink_idx; i++) {
1015 168 : if (curr_min_bias == 0.0) {
1016 : break;
1017 : }
1018 168 : curr_min_bias = fmin(curr_min_bias, getValue(i));
1019 : }
1020 : // Or go from a low source to the bottom boundary and
1021 : // then from the high boundary to the sink
1022 0 : } else if (source_idx < sink_idx) {
1023 0 : for (index_t i = source_idx; i > 0; i--) {
1024 0 : if (curr_min_bias == 0.0) {
1025 : break;
1026 : }
1027 0 : curr_min_bias = fmin(curr_min_bias, getValue(i));
1028 : }
1029 0 : curr_min_bias = fmin(curr_min_bias, getValue(0));
1030 0 : for (index_t i = maxsize_ - 1; i <= sink_idx; i--) {
1031 0 : if (curr_min_bias == 0.0) {
1032 : break;
1033 : }
1034 0 : curr_min_bias = fmin(curr_min_bias, getValue(i));
1035 : }
1036 : }
1037 : // If the boundary crossing paths was more biased, it's
1038 : // minimal bias replaces the non-boundary-crossing path's
1039 : // minimum.
1040 42 : maximal_minimum = fmax(maximal_minimum, curr_min_bias);
1041 : }
1042 : // The one dimensional path search is complete.
1043 147 : return maximal_minimum;
1044 : // In two or more dimensions, path searching isn't trivial and we really
1045 : // do need to use a path search algorithm. Dijkstra is the simplest decent
1046 : // one. Using it we've never found the path search to be performance
1047 : // limiting in any solvated biomolecule test system, but faster options are
1048 : // easy to imagine if they become necessary. NB-In this case, we're actually
1049 : // using a greedy variant of Dijkstra's algorithm where the first possible
1050 : // path to a point always controls the path cost to that point. The structure
1051 : // of the cost function in this case guarantees that the calculated costs will
1052 : // be correct using this variant even though fine details of the paths may not
1053 : // match a normal Dijkstra search.
1054 126 : } else if (dimension_ > 1) {
1055 : // Prepare calculation temporaries for Dijkstra's algorithm.
1056 : // Minimal path costs from source to a given grid point
1057 126 : std::vector<double> mins_from_source = std::vector<double>(maxsize_, -1.0);
1058 : // Heap for tracking available steps, steps are recorded as std::pairs of
1059 : // an index and a value.
1060 : std::vector< std::pair<index_t, double> > next_steps;
1061 : std::pair<index_t, double> curr_indexed_val;
1062 126 : std::make_heap(next_steps.begin(), next_steps.end(), indexed_lt);
1063 : // The search begins at the source index.
1064 252 : next_steps.push_back(std::pair<index_t, double>(source_idx, getValue(source_idx)));
1065 126 : std::push_heap(next_steps.begin(), next_steps.end(), indexed_lt);
1066 : // At first no points have been examined and the optimal path has not been found.
1067 : index_t n_examined = 0;
1068 : bool path_not_found = true;
1069 : // Until a path is found,
1070 : while (path_not_found) {
1071 : // Examine the grid point currently most accessible from
1072 : // the set of all previously explored grid points by popping
1073 : // it from the top of the heap.
1074 9702 : std::pop_heap(next_steps.begin(), next_steps.end(), indexed_lt);
1075 : curr_indexed_val = next_steps.back();
1076 : next_steps.pop_back();
1077 : n_examined++;
1078 : // Check if this point is the sink point, and if so
1079 : // finish the loop.
1080 9702 : if (curr_indexed_val.first == sink_idx) {
1081 : path_not_found = false;
1082 : maximal_minimum = curr_indexed_val.second;
1083 126 : break;
1084 : // Check if this point has reached the worst possible
1085 : // value, and if so stop looking for paths.
1086 9576 : } else if (curr_indexed_val.second == 0.0) {
1087 : maximal_minimum = 0.0;
1088 : break;
1089 : }
1090 : // If the search is not over, add this grid point's neighbors to the
1091 : // possible next points to search for the sink.
1092 9576 : std::vector<index_t> neighs = getNearestNeighbors(curr_indexed_val.first);
1093 46246 : for (unsigned k = 0; k < neighs.size(); k++) {
1094 36670 : index_t i = neighs[k];
1095 : // If the neighbor has not already been added to the list of possible next steps,
1096 36670 : if (mins_from_source[i] == -1.0) {
1097 : // Set the cost to reach it via a path through the current point being examined.
1098 12284 : mins_from_source[i] = fmin(curr_indexed_val.second, getValue(i));
1099 : // Add the neighboring point to the heap of potential next steps.
1100 12284 : next_steps.push_back(std::pair<index_t, double>(i, mins_from_source[i]));
1101 12284 : std::push_heap(next_steps.begin(), next_steps.end(), indexed_lt);
1102 : }
1103 : }
1104 : // Move on to the next best looking step along any of the paths
1105 : // growing from the source.
1106 : }
1107 : // The multidimensional path search is now complete.
1108 : return maximal_minimum;
1109 : }
1110 : return 0.0;
1111 : }
1112 :
1113 : }
|