LCOV - code coverage report
Current view: top level - function - FuncSumHills.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 478 563 84.9 %
Date: 2026-11-05 10:19:04 Functions: 14 16 87.5 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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             : #include "core/ActionRegister.h"
      23             : #include "Function.h"
      24             : #include "tools/Exception.h"
      25             : #include "tools/Communicator.h"
      26             : #include "tools/BiasRepresentation.h"
      27             : #include "tools/KernelFunctions.h"
      28             : #include "tools/File.h"
      29             : #include <limits>
      30             : #include "tools/Tools.h"
      31             : #include "tools/Stopwatch.h"
      32             : #include "tools/Grid.h"
      33             : 
      34             : namespace PLMD {
      35             : namespace function {
      36             : 
      37             : 
      38             : //+PLUMEDOC FUNCTION FUNCSUMHILLS
      39             : /*
      40             : This function is used by the command line tool [sum_hills](sum_hills.md) to sum the hills of a metadynamics simulation
      41             : or to compute a histogram. It does all its work when it is created and it cannot be used in a simulation (it stops with an error).
      42             : 
      43             : All its options are set by [sum_hills](sum_hills.md): see the documentation of [sum_hills](sum_hills.md) for their meaning.
      44             : 
      45             : ## Examples
      46             : 
      47             : There are currently no examples for this action: use [sum_hills](sum_hills.md) instead.
      48             : 
      49             : */
      50             : //+ENDPLUMEDOC
      51             : 
      52             : class FilesHandler {
      53             :   std::vector <std::string> filenames;
      54             :   std::vector <std::unique_ptr<IFile>>  ifiles;
      55             :   Action *action;
      56             :   Log *log;
      57             :   bool parallelread;
      58             :   unsigned beingread=0;
      59             :   bool isopen=false;
      60             : /// key used to merge the files ("time" or "clock"); empty to read the files one after the other
      61             :   std::string sortby;
      62             : /// a hill read from one of the files and not yet added (used when merging)
      63           6 :   struct PendingHill {
      64             :     std::unique_ptr<KernelFunctions> kernel;
      65             :     double biasf=1.0;
      66             :     double clock=0.0;
      67             :     double time=0.0;
      68             :     bool valid=false;
      69             :   };
      70             :   std::vector<PendingHill> pending;
      71             :   bool mergestarted=false;
      72             :   bool unsortedwarning=false;
      73             : /// read the next hill of file i into pending[i] (merge mode)
      74             :   void readPending(BiasRepresentation *br, unsigned i);
      75             :   bool readBunchMerged(BiasRepresentation *br, int stride);
      76             : public:
      77             :   FilesHandler(const std::vector<std::string> &filenames,
      78             :                bool parallelread,
      79             :                Action &myaction,
      80             :                Log &mylog,
      81             :                const std::string & sortby="");
      82             :   bool readBunch(BiasRepresentation *br, int stride=-1);
      83             :   bool scanOneHill(BiasRepresentation *br, IFile *ifile );
      84             :   void getMinMaxBin(const std::vector<Value*> & vals,
      85             :                     Communicator &cc,
      86             :                     std::vector<double> &vmin,
      87             :                     std::vector<double> &vmax,
      88             :                     std::vector<unsigned> &vbin);
      89             :   void getMinMaxBin(const std::vector<Value*> & vals,
      90             :                     Communicator &cc,
      91             :                     std::vector<double> &vmin,
      92             :                     std::vector<double> &vmax,
      93             :                     std::vector<unsigned> &vbin,
      94             :                     const std::vector<double> &histosigma);
      95             : };
      96          23 : FilesHandler::FilesHandler(const std::vector<std::string> &fnames,
      97             :                            const bool doparallelread,
      98             :                            Action &act,
      99             :                            Log &mylog,
     100          23 :                            const std::string & sort)
     101          23 :   :filenames(fnames),
     102          23 :    log(&mylog),
     103          23 :    parallelread(doparallelread),
     104          23 :    sortby(sort) {
     105          23 :   plumed_massert(sortby=="" || sortby=="time" || sortby=="clock","files can only be sorted by time or clock");
     106          23 :   action=&act;
     107          49 :   for(unsigned i=0; i<filenames.size(); i++) {
     108             :     auto ifile=Tools::make_unique<IFile>();
     109          26 :     ifile->link(act);
     110          26 :     plumed_massert((ifile->FileExist(filenames[i])), "the file "+filenames[i]+" does not exist " );
     111          26 :     ifiles.emplace_back(std::move(ifile));
     112          26 :   }
     113             : 
     114          23 : }
     115             : 
     116             : // note that the FileHandler is completely transparent respect to the biasrepresentation
     117             : // no check are made at this level
     118         123 : void FilesHandler::readPending(BiasRepresentation *br, unsigned i) {
     119         123 :   IFile *ff=ifiles[i].get();
     120             :   PendingHill & p=pending[i];
     121             :   double t;
     122         246 :   if(!ff->scanField("time",t)) {
     123           3 :     (*log)<<"  closing file "<<filenames[i]<<"\n";
     124           3 :     ff->close();
     125           3 :     p.valid=false;
     126             :     p.kernel.reset();
     127           3 :     return;
     128             :   }
     129         120 :   double c=0.0;
     130         240 :   if(ff->FieldExist("clock")) {
     131         240 :     ff->scanField("clock",c);
     132           0 :   } else if(sortby=="clock") {
     133           0 :     plumed_merror("cannot sort by clock: there is no clock field in file "+filenames[i]);
     134             :   }
     135         120 :   if(p.valid && !unsortedwarning) {
     136         117 :     const bool back = (sortby=="clock") ? (c<p.clock || (c==p.clock && t<p.time)) : (t<p.time);
     137         117 :     if(back) {
     138           0 :       (*log)<<"  WARNING: file "<<filenames[i]<<" is not ordered by "<<sortby<<": hills are merged in the order in which they appear in each file\n";
     139           0 :       unsortedwarning=true;
     140             :     }
     141             :   }
     142         120 :   p.time=t;
     143         120 :   p.clock=c;
     144         240 :   p.kernel=br->readKernel(ff,p.biasf);
     145         120 :   if(br->hasSigmaInInput()) {
     146           0 :     ff->allowIgnoredFields();
     147             :   }
     148         120 :   ff->scanField();
     149         120 :   p.valid=true;
     150             : }
     151             : 
     152           5 : bool FilesHandler::readBunchMerged(BiasRepresentation *br, int stride) {
     153           5 :   if(!mergestarted) {
     154           1 :     (*log)<<"  merging the hills of "<<filenames.size()<<" files in order of "<<sortby<<"\n";
     155           1 :     pending.resize(ifiles.size());
     156           4 :     for(unsigned i=0; i<ifiles.size(); i++) {
     157           3 :       (*log)<<"  opening file "<<filenames[i]<<"\n";
     158           3 :       ifiles[i]->open(filenames[i]);
     159           3 :       readPending(br,i);
     160             :     }
     161           1 :     mergestarted=true;
     162             :   }
     163             :   while(true) {
     164             :     // the next hill is the earliest; ties are resolved with time (for clock) and then with the order of the files
     165             :     int next=-1;
     166         484 :     for(unsigned i=0; i<pending.size(); i++) {
     167         363 :       if(!pending[i].valid) {
     168           6 :         continue;
     169             :       }
     170         357 :       if(next<0) {
     171         120 :         next=i;
     172         120 :         continue;
     173             :       }
     174             :       const auto & a=pending[i];
     175         237 :       const auto & b=pending[next];
     176         237 :       const bool earlier = (sortby=="clock") ? (a.clock<b.clock || (a.clock==b.clock && a.time<b.time)) : (a.time<b.time);
     177         237 :       if(earlier) {
     178          78 :         next=i;
     179             :       }
     180             :     }
     181         121 :     if(next<0) {
     182           1 :       (*log)<<"  final chunk: now with "<<br->getNumberOfKernels()<<" kernels  \n";
     183           5 :       return false;
     184             :     }
     185         120 :     br->addKernel(std::move(pending[next].kernel),pending[next].biasf,pending[next].time);
     186         120 :     readPending(br,next);
     187         120 :     int n=br->getNumberOfKernels();
     188         120 :     if(stride>0 && n%stride==0) {
     189             :       // stop here unless all files are over
     190           4 :       for(const auto & p : pending)
     191           4 :         if(p.valid) {
     192           4 :           (*log)<<"  done with this chunk: now with "<<n<<" kernels  \n";
     193             :           return true;
     194             :         }
     195             :     }
     196         116 :   }
     197             : }
     198             : 
     199          32 : bool FilesHandler::readBunch(BiasRepresentation *br, int stride) {
     200          32 :   if(sortby!="" && ifiles.size()>1) {
     201           5 :     return readBunchMerged(br,stride);
     202             :   }
     203             :   bool morefiles=true;
     204          27 :   if(parallelread) {
     205           0 :     (*log)<<"  doing parallelread \n";
     206           0 :     plumed_merror("parallelread is not yet implemented !!!");
     207             :   } else {
     208          27 :     (*log)<<"  doing serialread \n";
     209             :     // read one by one hills
     210             :     // is the type defined? if not, assume it is a gaussian
     211             :     IFile *ff;
     212          27 :     ff=ifiles[beingread].get();
     213          27 :     if(!isopen) {
     214          22 :       (*log)<<"  opening file "<<filenames[beingread]<<"\n";
     215          22 :       ff->open(filenames[beingread]);
     216          22 :       isopen=true;
     217             :     }
     218          27 :     int n=0;
     219             :     while(true) {
     220             :       bool fileisover=true;
     221        6046 :       while(scanOneHill(br,ff)) {
     222             :         // here do the dump if needed
     223        6023 :         n=br->getNumberOfKernels();
     224        6023 :         if(stride>0 && n%stride==0 && n!=0  ) {
     225           5 :           (*log)<<"  done with this chunk: now with "<<n<<" kernels  \n";
     226             :           fileisover=false;
     227             :           break;
     228             :         }
     229             :       }
     230             :       if(fileisover) {
     231          23 :         (*log)<<"  closing file "<<filenames[beingread]<<"\n";
     232          23 :         ff->close();
     233          23 :         isopen=false;
     234          23 :         (*log)<<"  now total "<<br->getNumberOfKernels()<<" kernels \n";
     235          23 :         beingread++;
     236          23 :         if(beingread<ifiles.size()) {
     237             :           ff=ifiles[beingread].get();
     238           1 :           ff->open(filenames[beingread]);
     239           1 :           (*log)<<"  opening file "<<filenames[beingread]<<"\n";
     240           1 :           isopen=true;
     241             :         } else {
     242             :           morefiles=false;
     243          22 :           (*log)<<"  final chunk: now with "<<n<<" kernels  \n";
     244             :           break;
     245             :         }
     246             :       }
     247             :       // if there are no more files to read and this file is over then quit
     248             :       if(fileisover && !morefiles) {
     249             :         break;
     250             :       }
     251             :       // if you are in the middle of a file and you are here
     252             :       // then means that you read what you need to read
     253           6 :       if(!fileisover ) {
     254             :         break;
     255             :       }
     256             :     }
     257             :   }
     258          27 :   return morefiles;
     259             : }
     260           7 : void FilesHandler::getMinMaxBin(const std::vector<Value*> & vals,
     261             :                                 Communicator &cc,
     262             :                                 std::vector<double> &vmin,
     263             :                                 std::vector<double> &vmax,
     264             :                                 std::vector<unsigned> &vbin) {
     265             :   // create the representation (no grid)
     266           7 :   BiasRepresentation br(vals,cc);
     267             :   // read all the kernels
     268           7 :   readBunch(&br);
     269             :   // loop over the kernels and get the support
     270           7 :   br.getMinMaxBin(vmin,vmax,vbin);
     271           7 : }
     272           1 : void FilesHandler::getMinMaxBin(const std::vector<Value*> & vals,
     273             :                                 Communicator &cc,
     274             :                                 std::vector<double> &vmin,
     275             :                                 std::vector<double> &vmax,
     276             :                                 std::vector<unsigned> &vbin,
     277             :                                 const std::vector<double> &histosigma) {
     278           1 :   BiasRepresentation br(vals,cc,histosigma);
     279             :   // read all the kernels
     280           1 :   readBunch(&br);
     281             :   // loop over the kernels and get the support
     282           1 :   br.getMinMaxBin(vmin,vmax,vbin);
     283           1 : }
     284        6046 : bool FilesHandler::scanOneHill(BiasRepresentation *br, IFile *ifile ) {
     285             :   double time;
     286       12092 :   if(ifile->scanField("time",time)) {
     287             :     // the other fields (e.g. clock) are ignored
     288        6023 :     br->pushKernel(ifile,time);
     289        6023 :     if(br->hasSigmaInInput()) {
     290        1092 :       ifile->allowIgnoredFields();
     291             :     }
     292        6023 :     ifile->scanField();
     293        6023 :     return true;
     294             :   } else {
     295             :     return false;
     296             :   }
     297             : }
     298             : 
     299             : 
     300         900 : double  mylog( double v1 ) {
     301         900 :   return std::log(v1);
     302             : }
     303             : 
     304        1800 : double  mylogder( double v1 ) {
     305        1800 :   return 1./v1;
     306             : }
     307             : 
     308             : 
     309             : 
     310             : class FuncSumHills :
     311             :   public Function {
     312             :   std::vector<std::string> hillsFiles,histoFiles;
     313             :   std::vector<std::string> proj;
     314             :   int initstride=-1;
     315             :   bool iscltool=false;
     316             :   bool integratehills=false;
     317             :   bool integratehisto=false;
     318             :   bool parallelread=false;
     319             :   bool negativebias=false;
     320             :   bool nohistory=false;
     321             :   bool minTOzero=false;
     322             :   bool doInt=false;
     323             :   double lowI_=-1.0;
     324             :   double uppI_=-1.0;
     325             :   double beta=-1.0;
     326             :   std::string outhills;
     327             :   std::string outhisto;
     328             :   std::string fmt="%14.9f";
     329             :   std::string sortby;
     330             : /// time average of the bias
     331             :   std::vector<double> average;
     332             : /// boxes (min and max for each variable) defining the basins whose free energies are written
     333             :   std::vector<std::vector<double>> basinmin,basinmax;
     334             :   std::string outbasins;
     335             : /// write the free energy of each basin relative to the first one
     336             :   void writeBasins(OFile & ofile, const Grid & fes, double time, int nkernels);
     337             :   std::unique_ptr<BiasRepresentation> biasrep;
     338             :   std::unique_ptr<BiasRepresentation> historep;
     339             : public:
     340             :   explicit FuncSumHills(const ActionOptions&);
     341             :   void calculate() override; // this probably is not needed
     342             :   bool checkFilesAreExisting(const std::vector<std::string> & hills );
     343             :   static void registerKeywords(Keywords& keys);
     344             : };
     345             : 
     346             : PLUMED_REGISTER_ACTION(FuncSumHills,"FUNCSUMHILLS")
     347             : 
     348          19 : void FuncSumHills::registerKeywords(Keywords& keys) {
     349          19 :   Function::registerKeywords(keys);
     350          19 :   keys.add("optional","HILLSFILES"," source file for hills creation(may be the same as HILLS)"); // this can be a vector!
     351          19 :   keys.add("optional","HISTOFILES"," source file for histogram creation(may be the same as HILLS)"); // also this can be a vector!
     352          19 :   keys.add("optional","HISTOSIGMA"," sigmas for binning when the histogram correction is needed    ");
     353          19 :   keys.add("optional","PROJ"," only with sumhills: the projection on the CVs");
     354          19 :   keys.add("optional","KT"," only with sumhills: the kt factor when projection on CVs");
     355          19 :   keys.add("optional","GRID_MIN","the lower bounds for the grid");
     356          19 :   keys.add("optional","GRID_MAX","the upper bounds for the grid");
     357          19 :   keys.add("optional","GRID_BIN","the number of bins for the grid");
     358          19 :   keys.add("optional","GRID_SPACING","the approximate grid spacing (to be used as an alternative or together with GRID_BIN)");
     359          19 :   keys.add("optional","INTERVAL","set one dimensional INTERVAL");
     360          19 :   keys.add("optional","OUTHILLS"," output file for hills ");
     361          19 :   keys.add("optional","OUTHISTO"," output file for histogram ");
     362          19 :   keys.add("optional","INITSTRIDE"," stride if you want an initial dump ");
     363          19 :   keys.add("optional","STRIDE"," stride when you do it on the fly ");
     364          19 :   keys.addFlag("ISCLTOOL",false,"use via plumed command line: calculate at read phase and then go");
     365          19 :   keys.addFlag("PARALLELREAD",false,"read parallel HILLS file");
     366          19 :   keys.addFlag("NEGBIAS",false,"dump  negative bias ( -bias )   instead of the free energy: needed in well tempered with flexible hills ");
     367          19 :   keys.addFlag("NOHISTORY",false,"to be used with INITSTRIDE:  it splits the bias/histogram in pieces without previous history  ");
     368          19 :   keys.addFlag("MINTOZERO",false,"translate the resulting bias/histogram to have the minimum to zero  ");
     369          19 :   keys.add("optional","FMT","the format that should be used to output real numbers");
     370          19 :   keys.add("optional","SORT","merge the hills of multiple files in order of time or clock instead of reading the files one after the other");
     371          19 :   keys.add("optional","AVERAGE","time average of the bias: from time t0 (and until t1 if given) the hills are scaled by (t1-t)/(t1-t0), where t1 is by default the time of the last hill");
     372          19 :   keys.add("optional","BASINS","basins (separated by colons) given by the minimum and maximum of each variable of the output, e.g. -3,-1:1,3; their free energies relative to the first basin are written at each output");
     373          19 :   keys.add("optional","OUTBASINS","output file for the free energies of the basins");
     374          38 :   keys.setValueDescription("scalar","a scalar");
     375          19 : }
     376             : 
     377          15 : FuncSumHills::FuncSumHills(const ActionOptions&ao):
     378             :   Action(ao),
     379          15 :   Function(ao) {
     380             :   // format
     381          15 :   parse("FMT",fmt);
     382          15 :   log<<"  Output format is "<<fmt<<"\n";
     383             :   // here read
     384             :   // Grid Stuff
     385          15 :   std::vector<std::string> gmin;
     386          30 :   parseVector("GRID_MIN",gmin);
     387          15 :   if(gmin.size()!=getNumberOfArguments() && gmin.size()!=0) {
     388           0 :     error("not enough values for GRID_MIN");
     389             :   }
     390          15 :   plumed_massert(gmin.size()==getNumberOfArguments() || gmin.size()==0,"need GRID_MIN argument for this") ;
     391          15 :   std::vector<std::string> gmax;
     392          30 :   parseVector("GRID_MAX",gmax);
     393          15 :   if(gmax.size()!=getNumberOfArguments() && gmax.size()!=0) {
     394           0 :     error("not enough values for GRID_MAX");
     395             :   }
     396          15 :   plumed_massert(gmax.size()==getNumberOfArguments() || gmax.size()==0,"need GRID_MAX argument for this") ;
     397          15 :   std::vector<unsigned> gbin;
     398          15 :   std::vector<double>   gspacing;
     399          30 :   parseVector("GRID_BIN",gbin);
     400          15 :   plumed_massert(gbin.size()==getNumberOfArguments() || gbin.size()==0,"need GRID_BIN argument for this") ;
     401          15 :   if(gbin.size()!=getNumberOfArguments() && gbin.size()!=0) {
     402           0 :     error("not enough values for GRID_BIN");
     403             :   }
     404          30 :   parseVector("GRID_SPACING",gspacing);
     405          15 :   plumed_massert(gspacing.size()==getNumberOfArguments() || gspacing.size()==0,"need GRID_SPACING argument for this") ;
     406          15 :   if(gspacing.size()!=getNumberOfArguments() && gspacing.size()!=0) {
     407           0 :     error("not enough values for GRID_SPACING");
     408             :   }
     409          15 :   if(gspacing.size()!=0 && gbin.size()==0) {
     410           2 :     log<<"  The number of bins will be estimated from GRID_SPACING\n";
     411          13 :   } else if(gspacing.size()!=0 && gbin.size()!=0) {
     412           0 :     log<<"  You specified both GRID_BIN and GRID_SPACING\n";
     413           0 :     log<<"  The more conservative (highest) number of bins will be used for each variable\n";
     414             :   }
     415             :   // the number of bins from GRID_SPACING is computed once the boundaries are known,
     416             :   // possibly after they have been detected from the hills
     417             :   const bool userbin=(gbin.size()!=0);
     418          11 :   auto applySpacing=[&]() {
     419          11 :     if(gspacing.size()==0) {
     420             :       return;
     421             :     }
     422           2 :     if(gbin.size()==0) {
     423           2 :       gbin.assign(gmin.size(),1);
     424             :     }
     425           5 :     for(unsigned i=0; i<gmin.size(); i++) {
     426             :       double a,b;
     427           3 :       Tools::convert(gmin[i],a);
     428           3 :       Tools::convert(gmax[i],b);
     429           3 :       unsigned n=std::ceil((b-a)/gspacing[i]);
     430           3 :       if(gbin[i]<n) {
     431           3 :         gbin[i]=n;
     432             :       }
     433             :     }
     434          15 :   };
     435          15 :   if(gmin.size()!=0 && gmax.size()!=0) {
     436           7 :     applySpacing();
     437             :   }
     438             : 
     439             :   // Inteval keyword
     440          15 :   std::vector<double> tmpI(2);
     441          30 :   parseVector("INTERVAL",tmpI);
     442          15 :   if(tmpI.size()!=2&&tmpI.size()!=0) {
     443           0 :     error("both a lower and an upper limits must be provided with INTERVAL");
     444          15 :   } else if(tmpI.size()==2) {
     445           0 :     lowI_=tmpI.at(0);
     446           0 :     uppI_=tmpI.at(1);
     447           0 :     if(getNumberOfArguments()!=1) {
     448           0 :       error("INTERVAL limits correction works only for monodimensional metadynamics!");
     449             :     }
     450           0 :     if(uppI_<lowI_) {
     451           0 :       error("The Upper limit must be greater than the Lower limit!");
     452             :     }
     453           0 :     doInt=true;
     454             :   }
     455          15 :   if(doInt) {
     456           0 :     log << "  Upper and Lower limits boundaries for the bias are activated at " << lowI_ << " - " << uppI_<<"\n";
     457           0 :     log << "  Using the same values as boundaries for the grid if not other value was defined (default: 200 bins)\n";
     458           0 :     std::ostringstream strsmin, strsmax;
     459           0 :     strsmin << lowI_;
     460           0 :     strsmax << uppI_;
     461           0 :     if(gmin.size()==0) {
     462           0 :       gmin.push_back(strsmin.str());
     463             :     }
     464           0 :     if(gmax.size()==0) {
     465           0 :       gmax.push_back(strsmax.str());
     466             :     }
     467           0 :     if(gbin.size()==0 && gspacing.size()==0) {
     468           0 :       gbin.push_back(200);
     469           0 :     } else if(gbin.size()==0) {
     470           0 :       applySpacing();
     471             :     }
     472           0 :   }
     473             : 
     474             : 
     475             :   // hills file:
     476          30 :   parseVector("HILLSFILES",hillsFiles);
     477          15 :   if(hillsFiles.size()==0) {
     478           1 :     integratehills=false; // default behaviour
     479             :   } else {
     480          14 :     integratehills=true;
     481          31 :     for(unsigned i=0; i<hillsFiles.size(); i++) {
     482          17 :       log<<"  hillsfile  : "<<hillsFiles[i]<<"\n";
     483             :     }
     484             :   }
     485             :   // histo file:
     486          30 :   parseVector("HISTOFILES",histoFiles);
     487          15 :   if(histoFiles.size()==0) {
     488          14 :     integratehisto=false;
     489             :   } else {
     490           1 :     integratehisto=true;
     491           2 :     for(unsigned i=0; i<histoFiles.size(); i++) {
     492           1 :       log<<"  histofile  : "<<histoFiles[i]<<"\n";
     493             :     }
     494             :   }
     495          15 :   std::vector<double> histoSigma;
     496          15 :   if(integratehisto) {
     497           1 :     parseVector("HISTOSIGMA",histoSigma);
     498           3 :     for(unsigned i=0; i<histoSigma.size(); i++) {
     499           2 :       log<<"  histosigma  : "<<histoSigma[i]<<"\n";
     500             :     }
     501             :   }
     502             : 
     503             :   // needs a projection?
     504          15 :   proj.clear();
     505          15 :   parseVector("PROJ",proj);
     506          15 :   if(integratehills) {
     507          14 :     plumed_massert(proj.size()<getNumberOfArguments()," The number of projection must be less than the full list of arguments ");
     508             :   }
     509          15 :   if(integratehisto) {
     510           1 :     plumed_massert(proj.size()<=getNumberOfArguments()," The number of projection must be less or equal to the full list of arguments ");
     511             :   }
     512          15 :   if(integratehisto&&proj.size()==0) {
     513           3 :     for(unsigned i=0; i<getNumberOfArguments(); i++) {
     514           2 :       proj.push_back(getPntrToArgument(i)->getName());
     515             :     }
     516             :   }
     517             : 
     518             :   // add some automatic hills width: not in case stride is defined
     519             :   // since when you start from zero the automatic size will be zero!
     520          15 :   if(gmin.size()==0 || gmax.size()==0) {
     521           8 :     log<<"   \n";
     522           8 :     log<<"  No boundaries defined: need to do a prescreening of hills \n";
     523           8 :     std::vector<Value*> tmphillsvalues, tmphistovalues;
     524           8 :     if(integratehills) {
     525          21 :       for(unsigned i=0; i<getNumberOfArguments(); i++) {
     526          14 :         tmphillsvalues.push_back( getPntrToArgument(i) );
     527             :       }
     528             :     }
     529           8 :     if(integratehisto) {
     530           3 :       for(unsigned i=0; i<getNumberOfArguments(); i++) {
     531           2 :         std::string ss = getPntrToArgument(i)->getName();
     532           6 :         for(unsigned j=0; j<proj.size(); j++) {
     533           4 :           if(proj[j]==ss) {
     534           2 :             tmphistovalues.push_back( getPntrToArgument(i) );
     535             :           }
     536             :         }
     537             :       }
     538             :     }
     539             : 
     540           8 :     if(integratehills) {
     541           7 :       FilesHandler hillsHandler(hillsFiles,parallelread,*this, log);
     542           7 :       std::vector<double> vmin,vmax;
     543           7 :       std::vector<unsigned> vbin;
     544           7 :       hillsHandler.getMinMaxBin(tmphillsvalues,comm,vmin,vmax,vbin);
     545           7 :       log<<"  found boundaries from hillsfile: \n";
     546           7 :       gmin.resize(vmin.size());
     547           7 :       gmax.resize(vmax.size());
     548          21 :       for(unsigned i=0; i<getNumberOfArguments(); i++) {
     549          14 :         Tools::convert(vmin[i],gmin[i]);
     550          14 :         Tools::convert(vmax[i],gmax[i]);
     551             :       }
     552           7 :       if(userbin || gspacing.size()!=0) {
     553           3 :         log<<"  found nbins or spacing in input, this overrides the automatic choice \n";
     554           3 :         applySpacing();
     555             :       } else {
     556           4 :         gbin=vbin;
     557             :       }
     558          21 :       for(unsigned i=0; i<getNumberOfArguments(); i++) {
     559          14 :         log<<"  variable "<< getPntrToArgument(i)->getName()<<" min: "<<gmin[i]<<" max: "<<gmax[i]<<" nbin: "<<gbin[i]<<"\n";
     560             :       }
     561          14 :     }
     562             :     // if at this stage bins are not there then do it with histo
     563           8 :     if(gmin.size()==0) {
     564           1 :       FilesHandler histoHandler(histoFiles,parallelread,*this, log);
     565           1 :       std::vector<double> vmin,vmax;
     566           1 :       std::vector<unsigned> vbin;
     567           1 :       histoHandler.getMinMaxBin(tmphistovalues,comm,vmin,vmax,vbin,histoSigma);
     568           1 :       log<<"  found boundaries from histofile: \n";
     569           1 :       gmin.resize(vmin.size());
     570           1 :       gmax.resize(vmax.size());
     571           3 :       for(unsigned i=0; i<proj.size(); i++) {
     572           2 :         Tools::convert(vmin[i],gmin[i]);
     573           2 :         Tools::convert(vmax[i],gmax[i]);
     574             :       }
     575           1 :       if(userbin || gspacing.size()!=0) {
     576           1 :         log<<"  found nbins or spacing in input, this overrides the automatic choice \n";
     577           1 :         applySpacing();
     578             :       } else {
     579           0 :         gbin=vbin;
     580             :       }
     581           3 :       for(unsigned i=0; i<proj.size(); i++) {
     582           2 :         log<<"  variable "<< proj[i] <<" min: "<<gmin[i]<<" max: "<<gmax[i]<<" nbin: "<<gbin[i]<<"\n";
     583             :       }
     584           2 :     }
     585           8 :     log<<"  done!\n";
     586           8 :     log<<"   \n";
     587             :   }
     588             : 
     589             : 
     590          30 :   parseVector("AVERAGE",average);
     591          15 :   if(average.size()!=0) {
     592           1 :     if(average.size()>2) {
     593           0 :       error("AVERAGE needs the initial time and optionally the final time");
     594             :     }
     595           1 :     if(!integratehills) {
     596           0 :       error("AVERAGE can only be used with hills files");
     597             :     }
     598           1 :     if(average.size()==2 && average[1]<=average[0]) {
     599           0 :       error("the final time of AVERAGE should be larger than the initial time");
     600             :     }
     601           1 :     log<<"  time average of the bias from time "<<average[0];
     602           1 :     if(average.size()==2) {
     603           1 :       log<<" to time "<<average[1];
     604             :     }
     605           1 :     log<<"\n";
     606             :   }
     607             :   std::string basins;
     608          30 :   parse("BASINS",basins);
     609          15 :   if(basins.length()>0) {
     610             :     // the basins are defined on the variables of the output
     611           1 :     const unsigned nout=(proj.size()>0 ? proj.size() : getNumberOfArguments());
     612           4 :     for(const auto & b : Tools::getWords(basins,":")) {
     613           3 :       std::vector<std::string> words=Tools::getWords(b,",");
     614           3 :       if(words.size()!=2*nout) {
     615           0 :         error("each basin needs a minimum and a maximum for each of the "+std::to_string(nout)+" variables of the output: "+b);
     616             :       }
     617           3 :       std::vector<double> bmin(nout),bmax(nout);
     618           9 :       for(unsigned i=0; i<nout; i++) {
     619           6 :         if(!Tools::convertNoexcept(words[2*i],bmin[i]) || !Tools::convertNoexcept(words[2*i+1],bmax[i])) {
     620           0 :           error("cannot read basin "+b);
     621             :         }
     622             :       }
     623           3 :       basinmin.push_back(bmin);
     624           3 :       basinmax.push_back(bmax);
     625           4 :     }
     626           1 :     if(basinmin.size()<2) {
     627           0 :       error("BASINS needs at least two basins");
     628             :     }
     629           1 :     log<<"  free energies of "<<basinmin.size()<<" basins relative to the first one are written at each output\n";
     630             :   }
     631          15 :   if( proj.size() != 0 || integratehisto==true || basinmin.size()>0 ) {
     632           4 :     parse("KT",beta);
     633           8 :     for(unsigned i=0; i<proj.size(); i++) {
     634           4 :       log<<"  projection "<<i<<" : "<<proj[i]<<"\n";
     635             :     }
     636             :     // this should be only for projection or free energy from histograms
     637           4 :     plumed_massert(beta>0.,"if you make a projection, a histogram correction or use basins then you need KT flag!");
     638           4 :     beta=1./beta;
     639           4 :     log<<"  beta is "<<beta<<"\n";
     640             :   }
     641             :   // is a cltool: then you start and then die
     642          15 :   parseFlag("ISCLTOOL",iscltool);
     643             :   //
     644          15 :   parseFlag("NEGBIAS",negativebias);
     645             :   //
     646          15 :   parseFlag("PARALLELREAD",parallelread);
     647             :   // stride
     648          15 :   parse("INITSTRIDE",initstride);
     649             :   // output suffix or names
     650          15 :   if(initstride<0) {
     651          11 :     log<<"  Doing only one integration: no stride \n";
     652          11 :     outhills="fes.dat";
     653          11 :     outhisto="histo.dat";
     654             :   } else {
     655           4 :     outhills="fes_";
     656           4 :     outhisto="histo_";
     657           4 :     log<<"  Doing integration slices every "<<initstride<<" kernels\n";
     658           4 :     parseFlag("NOHISTORY",nohistory);
     659           4 :     if(nohistory) {
     660           0 :       log<<"  nohistory: each stride block has no memory of the previous block\n";
     661             :     }
     662             :   }
     663          15 :   parse("SORT",sortby);
     664          15 :   if(sortby!="" && sortby!="time" && sortby!="clock") {
     665           0 :     error("SORT can only be time or clock");
     666             :   }
     667          15 :   if(sortby!="") {
     668           1 :     log<<"  hills from multiple files are merged in order of "<<sortby<<"\n";
     669             :   }
     670          15 :   parseFlag("MINTOZERO",minTOzero);
     671          15 :   if(minTOzero) {
     672           0 :     log<<"  mintozero: bias/histogram will be translated to have the minimum value equal to zero\n";
     673             :   }
     674             :   //what might it be this?
     675             :   // here start
     676             :   // want something right now?? do it and return
     677             :   // your argument is a set of cvs
     678             :   // then you need: a hills / a colvar-like file (to do a histogram)
     679             :   // create a bias representation for this
     680          15 :   if(iscltool) {
     681             : 
     682          15 :     std::vector<Value*> tmphillsvalues, tmphistovalues;
     683          15 :     if(integratehills) {
     684          38 :       for(unsigned i=0; i<getNumberOfArguments(); i++) {
     685             :         // allocate a new value from the old one: no deriv here
     686             :         // if we are summing hills then all the arguments are needed
     687          24 :         tmphillsvalues.push_back( getPntrToArgument(i) );
     688             :       }
     689             :     }
     690          15 :     if(integratehisto) {
     691           3 :       for(unsigned i=0; i<getNumberOfArguments(); i++) {
     692           2 :         std::string ss = getPntrToArgument(i)->getName();
     693           6 :         for(unsigned j=0; j<proj.size(); j++) {
     694           4 :           if(proj[j]==ss) {
     695           2 :             tmphistovalues.push_back( getPntrToArgument(i) );
     696             :           }
     697             :         }
     698             :       }
     699             :     }
     700             : 
     701             :     // check if the files exists
     702          15 :     if(integratehills) {
     703          14 :       checkFilesAreExisting(hillsFiles);
     704          28 :       biasrep=Tools::make_unique<BiasRepresentation>(tmphillsvalues,comm, gmin, gmax, gbin, doInt, lowI_, uppI_);
     705          14 :       if(average.size()>0) {
     706           1 :         biasrep->setTimeAverage(average[0],average.size()==2,(average.size()==2?average[1]:0.0));
     707             :       }
     708          14 :       if(negativebias && basinmin.size()>0) {
     709           0 :         error("BASINS requires a free energy and cannot be used with NEGBIAS");
     710             :       }
     711          14 :       if(negativebias) {
     712           2 :         biasrep->setRescaledToBias(true);
     713           2 :         log<<"  required the -bias instead of the free energy \n";
     714           2 :         if(initstride<0) {
     715           2 :           outhills="negativebias.dat";
     716             :         } else {
     717           0 :           outhills="negativebias_";
     718             :         }
     719             :       }
     720             :     }
     721             : 
     722          15 :     parse("OUTHILLS",outhills);
     723          15 :     parse("OUTHISTO",outhisto);
     724          15 :     outbasins="basins.dat";
     725          15 :     parse("OUTBASINS",outbasins);
     726          15 :     if(integratehills) {
     727          14 :       log<<"  output file for fes/bias  is :  "<<outhills<<"\n";
     728             :     }
     729          15 :     if(integratehisto) {
     730           1 :       log<<"  output file for histogram is :  "<<outhisto<<"\n";
     731             :     }
     732          15 :     checkRead();
     733             : 
     734          15 :     log<<"\n";
     735          15 :     log<<"  Now calculating...\n";
     736          15 :     log<<"\n";
     737             : 
     738             :     // here it defines the column to be histogrammed, tmpvalues should be only
     739             :     // the list of the collective variable one want to consider
     740          15 :     if(integratehisto) {
     741           1 :       checkFilesAreExisting(histoFiles);
     742           2 :       historep=Tools::make_unique<BiasRepresentation>(tmphistovalues,comm,gmin,gmax,gbin,histoSigma);
     743             :     }
     744             : 
     745             :     // decide how to source hills ( serial/parallel )
     746             :     // here below the input control
     747             :     // say how many hills and it will read them from the
     748             :     // bunch of files provided, will update the representation
     749             :     // of hills (i.e. a list of hills and the associated grid)
     750             : 
     751             :     // decide how to source colvars ( serial parallel )
     752          15 :     std::unique_ptr<FilesHandler> hillsHandler;
     753          15 :     std::unique_ptr<FilesHandler> histoHandler;
     754             : 
     755          15 :     if(integratehills)  {
     756          28 :       hillsHandler=Tools::make_unique<FilesHandler>(hillsFiles,parallelread,*this, log, sortby);
     757             :     }
     758          15 :     if(integratehisto)  {
     759           2 :       histoHandler=Tools::make_unique<FilesHandler>(histoFiles,parallelread,*this, log, sortby);
     760             :     }
     761             : 
     762             : // Stopwatch is logged when it goes out of scope
     763          15 :     OFile basinsfile, histobasinsfile;
     764          15 :     if(basinmin.size()>0) {
     765           1 :       if(integratehills) {
     766           1 :         basinsfile.link(*this);
     767           1 :         basinsfile.open(outbasins);
     768           1 :         basinsfile.fmtField(fmt);
     769           1 :         log<<"  output file for the free energies of the basins is :  "<<outbasins<<"\n";
     770             :       }
     771           1 :       if(integratehisto) {
     772           0 :         histobasinsfile.link(*this);
     773           0 :         histobasinsfile.open("histo_"+outbasins);
     774           0 :         histobasinsfile.fmtField(fmt);
     775           0 :         log<<"  output file for the free energies of the basins from the histogram is :  histo_"<<outbasins<<"\n";
     776             :       }
     777             :     }
     778             : 
     779          15 :     Stopwatch sw(log);
     780             : 
     781             : // Stopwatch is stopped when swh goes out of scope
     782          15 :     auto swh=sw.startStop("0 Summing hills");
     783             : 
     784             :     // read a number of hills and put in the bias representation
     785             :     int nfiles=0;
     786          15 :     bool ibias=integratehills;
     787          15 :     bool ihisto=integratehisto;
     788             :     while(true) {
     789          24 :       if(  integratehills  && ibias  ) {
     790          23 :         if(nohistory) {
     791           0 :           biasrep->clear();
     792           0 :           log<<"  clearing history before reading a new block\n";
     793             :         };
     794          23 :         log<<"  reading hills: \n";
     795          23 :         const int before=biasrep->getNumberOfKernels();
     796          23 :         ibias=hillsHandler->readBunch(biasrep.get(),initstride) ;
     797          23 :         log<<"\n";
     798             :         // when the number of hills is a multiple of the stride the last block is empty: do not write it again
     799          23 :         if(!ibias && nfiles>0 && biasrep->getNumberOfKernels()==before) {
     800           1 :           integratehills=false;
     801             :         }
     802             :       }
     803             : 
     804          24 :       if(  integratehisto  && ihisto ) {
     805           1 :         if(nohistory) {
     806           0 :           historep->clear();
     807           0 :           log<<"  clearing history before reading a new block\n";
     808             :         };
     809           1 :         log<<"  reading histogram: \n";
     810           1 :         const int before=historep->getNumberOfKernels();
     811           1 :         ihisto=histoHandler->readBunch(historep.get(),initstride) ;
     812           1 :         log<<"\n";
     813           1 :         if(!ihisto && nfiles>0 && historep->getNumberOfKernels()==before) {
     814           0 :           integratehisto=false;
     815             :         }
     816             :       }
     817             : 
     818             :       // dump: need to project?
     819          24 :       if(proj.size()!=0) {
     820             : 
     821           4 :         if(integratehills) {
     822             : 
     823           3 :           log<<"  Bias: Projecting on subgrid... \n";
     824           3 :           BiasWeight Bw(beta);
     825           3 :           Grid biasGrid=*(biasrep->getGridPtr());
     826           3 :           Grid smallGrid=biasGrid.project(proj,&Bw);
     827           3 :           OFile gridfile;
     828           3 :           gridfile.link(*this);
     829           3 :           std::ostringstream ostr;
     830           3 :           ostr<<nfiles;
     831             :           std::string myout;
     832           3 :           if(initstride>0) {
     833           6 :             myout=outhills+ostr.str()+".dat" ;
     834             :           } else {
     835             :             myout=outhills;
     836             :           }
     837           3 :           log<<"  Bias: Writing subgrid on file "<<myout<<" \n";
     838           3 :           gridfile.open(myout);
     839           3 :           if(minTOzero) {
     840           0 :             smallGrid.setMinToZero();
     841             :           }
     842             :           smallGrid.setOutputFmt(fmt);
     843           3 :           smallGrid.writeToFile(gridfile);
     844           3 :           if(basinmin.size()>0) {
     845           0 :             writeBasins(basinsfile,smallGrid,biasrep->getLastTime(),biasrep->getNumberOfKernels());
     846             :           }
     847           3 :           gridfile.close();
     848           3 :           if(!ibias) {
     849           2 :             integratehills=false;  // once you get to the final bunch just give up
     850             :           }
     851           3 :         }
     852             :         // this should be removed
     853           4 :         if(integratehisto) {
     854             : 
     855           1 :           log<<"  Histo: Projecting on subgrid... \n";
     856           1 :           Grid histoGrid=*(historep->getGridPtr());
     857             : 
     858           1 :           OFile gridfile;
     859           1 :           gridfile.link(*this);
     860           1 :           std::ostringstream ostr;
     861           1 :           ostr<<nfiles;
     862             :           std::string myout;
     863           1 :           if(initstride>0) {
     864           0 :             myout=outhisto+ostr.str()+".dat" ;
     865             :           } else {
     866             :             myout=outhisto;
     867             :           }
     868           1 :           log<<"  Histo: Writing subgrid on file "<<myout<<" \n";
     869           1 :           gridfile.open(myout);
     870             : 
     871           1 :           histoGrid.applyFunctionAllValuesAndDerivatives(&mylog,&mylogder);
     872           1 :           histoGrid.scaleAllValuesAndDerivatives(-1./beta);
     873           1 :           if(minTOzero) {
     874           0 :             histoGrid.setMinToZero();
     875             :           }
     876             :           histoGrid.setOutputFmt(fmt);
     877           1 :           histoGrid.writeToFile(gridfile);
     878           1 :           if(basinmin.size()>0) {
     879           0 :             writeBasins(histobasinsfile,histoGrid,historep->getLastTime(),historep->getNumberOfKernels());
     880             :           }
     881             : 
     882           1 :           if(!ihisto) {
     883           1 :             integratehisto=false;  // once you get to the final bunch just give up
     884             :           }
     885           1 :         }
     886             : 
     887             :       } else {
     888             : 
     889          20 :         if(integratehills) {
     890             : 
     891          19 :           Grid biasGrid=*(biasrep->getGridPtr());
     892          19 :           biasGrid.scaleAllValuesAndDerivatives(-1.);
     893             : 
     894          19 :           OFile gridfile;
     895          19 :           gridfile.link(*this);
     896          19 :           std::ostringstream ostr;
     897          19 :           ostr<<nfiles;
     898             :           std::string myout;
     899          19 :           if(initstride>0) {
     900          30 :             myout=outhills+ostr.str()+".dat" ;
     901             :           } else {
     902             :             myout=outhills;
     903             :           }
     904          19 :           log<<"  Writing full grid on file "<<myout<<" \n";
     905          19 :           gridfile.open(myout);
     906             : 
     907          19 :           if(minTOzero) {
     908           0 :             biasGrid.setMinToZero();
     909             :           }
     910             :           biasGrid.setOutputFmt(fmt);
     911          19 :           biasGrid.writeToFile(gridfile);
     912          19 :           if(basinmin.size()>0) {
     913           3 :             writeBasins(basinsfile,biasGrid,biasrep->getLastTime(),biasrep->getNumberOfKernels());
     914             :           }
     915             :           // rescale back prior to accumulate
     916          19 :           if(!ibias) {
     917          11 :             integratehills=false;  // once you get to the final bunch just give up
     918             :           }
     919          19 :         }
     920          20 :         if(integratehisto) {
     921             : 
     922           0 :           Grid histoGrid=*(historep->getGridPtr());
     923             :           // do this if you want a free energy from a grid, otherwise do not
     924           0 :           histoGrid.applyFunctionAllValuesAndDerivatives(&mylog,&mylogder);
     925           0 :           histoGrid.scaleAllValuesAndDerivatives(-1./beta);
     926             : 
     927           0 :           OFile gridfile;
     928           0 :           gridfile.link(*this);
     929           0 :           std::ostringstream ostr;
     930           0 :           ostr<<nfiles;
     931             :           std::string myout;
     932           0 :           if(initstride>0) {
     933           0 :             myout=outhisto+ostr.str()+".dat" ;
     934             :           } else {
     935             :             myout=outhisto;
     936             :           }
     937           0 :           log<<"  Writing full grid on file "<<myout<<" \n";
     938           0 :           gridfile.open(myout);
     939             : 
     940             :           // also this is useful only for free energy
     941           0 :           if(minTOzero) {
     942           0 :             histoGrid.setMinToZero();
     943             :           }
     944             :           histoGrid.setOutputFmt(fmt);
     945           0 :           histoGrid.writeToFile(gridfile);
     946           0 :           if(basinmin.size()>0) {
     947           0 :             writeBasins(histobasinsfile,histoGrid,historep->getLastTime(),historep->getNumberOfKernels());
     948             :           }
     949             : 
     950           0 :           if(!ihisto) {
     951           0 :             integratehisto=false;  // once you get to the final bunch just give up
     952             :           }
     953           0 :         }
     954             :       }
     955          24 :       if ( !ibias && !ihisto) {
     956             :         break;  //when both are over then just quit
     957             :       }
     958             : 
     959           9 :       nfiles++;
     960           9 :     }
     961             : 
     962             :     return;
     963          15 :   }
     964             :   // just an initialization but you need to do something on the fly?: need to connect with a metad run and its grid representation
     965             :   // your argument is a metad run
     966             :   // if the grid does not exist crash and say that you need some data
     967             :   // otherwise just link with it
     968             : 
     969          15 : }
     970             : 
     971           3 : void FuncSumHills::writeBasins(OFile & ofile, const Grid & fes, double time, int nkernels) {
     972           3 :   const unsigned nb=basinmin.size();
     973           3 :   const unsigned nd=fes.getDimension();
     974           3 :   const auto pbc=fes.getIsPeriodic();
     975             :   // -kT log sum_s exp(-F(s)/kT) over the points of each basin, computed with a running maximum
     976           3 :   std::vector<double> fmax(nb,-std::numeric_limits<double>::max()), sum(nb,0.0);
     977           3 :   std::vector<double> x(nd);
     978       10251 :   for(Grid::index_t i=0; i<fes.getSize(); i++) {
     979       10248 :     const double mbf=-beta*fes.getValue(i);
     980       10248 :     if(!std::isfinite(mbf)) {
     981           0 :       continue;
     982             :     }
     983       10248 :     fes.getPoint(i,x);
     984       40992 :     for(unsigned b=0; b<nb; b++) {
     985             :       bool inside=true;
     986       74592 :       for(unsigned d=0; d<nd && inside; d++) {
     987       43848 :         const double lo=basinmin[b][d];
     988       43848 :         const double hi=basinmax[b][d];
     989       43848 :         if(pbc[d] && lo>hi) {
     990             :           // along a periodic variable a basin can cross the boundary
     991           0 :           inside=(x[d]>=lo || x[d]<=hi);
     992             :         } else {
     993       43848 :           inside=(x[d]>=lo && x[d]<=hi);
     994             :         }
     995             :       }
     996       30744 :       if(!inside) {
     997       20208 :         continue;
     998             :       }
     999       10536 :       if(mbf>fmax[b]) {
    1000         349 :         sum[b]=sum[b]*std::exp(fmax[b]-mbf)+1.0;
    1001         349 :         fmax[b]=mbf;
    1002             :       } else {
    1003       10187 :         sum[b]+=std::exp(mbf-fmax[b]);
    1004             :       }
    1005             :     }
    1006             :   }
    1007           3 :   std::vector<double> f(nb);
    1008          12 :   for(unsigned b=0; b<nb; b++) {
    1009           9 :     plumed_massert(sum[b]>0.0,"basin "+std::to_string(b)+" does not contain any point of the grid");
    1010           9 :     f[b]=-(std::log(sum[b])+fmax[b])/beta;
    1011             :   }
    1012           3 :   ofile.printField("time",time);
    1013           3 :   ofile.printField("nkernels",nkernels);
    1014           9 :   for(unsigned b=1; b<nb; b++) {
    1015          12 :     ofile.printField("deltaf_"+std::to_string(b),f[b]-f[0]);
    1016             :   }
    1017           3 :   ofile.printField();
    1018           3 : }
    1019             : 
    1020           0 : void FuncSumHills::calculate() {
    1021             :   // this should be connected only with a grid representation to metadynamics
    1022             :   // at regular time just dump it
    1023           0 :   plumed_merror("You should have never got here: this stuff is not yet implemented!");
    1024             : }
    1025             : 
    1026          15 : bool FuncSumHills::checkFilesAreExisting(const std::vector<std::string> & hills ) {
    1027          15 :   plumed_massert(hills.size()!=0,"the number of  files provided should be at least one" );
    1028             :   auto ifile=Tools::make_unique<IFile>();
    1029          15 :   ifile->link(*this);
    1030          33 :   for(unsigned i=0; i< hills.size(); i++) {
    1031          18 :     plumed_massert(ifile->FileExist(hills[i]),"missing file "+hills[i]);
    1032             :   }
    1033          15 :   return true;
    1034             : 
    1035          15 : }
    1036             : 
    1037             : }
    1038             : 
    1039             : }
    1040             : 
    1041             : 

Generated by: LCOV version 1.16