LCOV - code coverage report
Current view: top level - cltools - SumHills.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 239 280 85.4 %
Date: 2026-11-05 10:19:04 Functions: 9 9 100.0 %

          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 "CLTool.h"
      23             : #include "core/CLToolRegister.h"
      24             : #include "tools/Tools.h"
      25             : #include "core/Action.h"
      26             : #include "core/ActionRegister.h"
      27             : #include "core/PlumedMain.h"
      28             : #include "tools/Communicator.h"
      29             : #include "tools/Random.h"
      30             : #include <algorithm>
      31             : #include <cstdio>
      32             : #include <string>
      33             : #include <vector>
      34             : #include "tools/File.h"
      35             : #include "core/Value.h"
      36             : #include "tools/Matrix.h"
      37             : 
      38             : namespace PLMD {
      39             : namespace cltools {
      40             : 
      41             : //+PLUMEDOC TOOLS sum_hills
      42             : /*
      43             : sum_hills is a tool that sums the Gaussians in a hills file and writes the resulting free energy on a grid.
      44             : 
      45             : This tool is most frequently used after a [METAD](METAD.md) simulation to sum all the Gaussians in the hills file
      46             : and output the free energy surface:
      47             : 
      48             : ```plumed
      49             : plumed sum_hills --hills PATHTOMYHILLSFILE
      50             : ```
      51             : 
      52             : The default name for the output file is `fes.dat`. It contains, for each point of the grid, the free energy
      53             : and its derivatives with respect to the variables. The free energy is minus the sum of the Gaussians: with
      54             : well-tempered metadynamics the heights written in the hills file are already scaled by $\gamma/(\gamma-1)$
      55             : (where $\gamma$ is the bias factor), so that their sum is directly the estimate of minus the free energy.
      56             : As always, the free energy is defined up to an additive constant; use `--mintozero` to shift its minimum to zero.
      57             : 
      58             : sum_hills automatically detects the variables in the hills file and their periodicity. A column `X` of the file
      59             : is a variable if the file also contains its width (`sigma_X`, or `sigma_X_X` for multivariate Gaussians),
      60             : whatever the name of the variable. If you used flexible hills (multivariate Gaussian kernels), sum_hills
      61             : understands it from the hills file.
      62             : 
      63             : ## Choosing the grid
      64             : 
      65             : By default the grid is chosen so as to contain all the Gaussians, with a spacing of about a third of the smallest width.
      66             : You can instead choose the number of bins:
      67             : 
      68             : ```plumed
      69             : plumed sum_hills --bin 99,99 --hills PATHTOMYHILLSFILE
      70             : ```
      71             : 
      72             : In the command above the minimum and maximum of the grid are still detected for you. If you want to set them yourself
      73             : you can do so as shown below:
      74             : 
      75             : ```plumed
      76             : plumed sum_hills --bin 99,99 --min -pi,-pi --max pi,pi --hills PATHTOMYHILLSFILE
      77             : ```
      78             : 
      79             : You can of course use numbers instead of -pi/pi. `--min` and `--max` must be given together. Instead of the number of bins
      80             : you can give the grid spacing with `--spacing`, with or without `--min` and `--max`. If you give both `--bin` and `--spacing`,
      81             : the larger number of bins is used for each variable.
      82             : 
      83             : ## Multiple files and multiple walkers
      84             : 
      85             : sum_hills can also accept multiple files:
      86             : 
      87             : ```plumed
      88             : plumed sum_hills --hills PATHTOMYHILLSFILE1,PATHTOMYHILLSFILE2,PATHTOMYHILLSFILE3
      89             : ```
      90             : 
      91             : The files are integrated one after the other. This does not change the final free energy, but when
      92             : you use `--stride` (see below) with the hills files of multiple walkers the intermediate free energies would
      93             : not follow the simulation. With `--sort time` the hills of all the files are merged in order of their time
      94             : (hills with the same time are taken in the order of the files), so that each intermediate free energy
      95             : contains the hills deposited by all the walkers up to a given time:
      96             : 
      97             : ```plumed
      98             : plumed sum_hills --hills HILLS.0,HILLS.1,HILLS.2 --sort time --stride 300
      99             : ```
     100             : 
     101             : With `--sort clock` the hills are merged in order of the `clock` field (the wall-clock time at which they were
     102             : deposited, written with multiple walkers), which is more appropriate when the walkers run at different speeds.
     103             : Each file should be ordered by the chosen field.
     104             : 
     105             : ## Integrating out variables
     106             : 
     107             : If you want to integrate out some variables from your free energy surface you can use `--idw`:
     108             : 
     109             : ```plumed
     110             : plumed sum_hills --hills PATHTOMYHILLSFILE --idw t1 --kt 0.6
     111             : ```
     112             : 
     113             : With `--idw` you list the variables that you want in the output free energy surface.
     114             : All the other variables in the hills file are integrated out, i.e.
     115             : $F(s_1) = -k_BT \log \sum_{s_2} e^{-F(s_1,s_2)/k_BT}$, where the sum runs over the points of the grid.
     116             : `--kt` is the temperature of the system in energy units: be consistent with the units of your hills file,
     117             : since plumed will not check this for you. If you need more variables you can list them separated by commas:
     118             : 
     119             : ```plumed
     120             : plumed sum_hills --hills PATHTOMYHILLSFILE --idw t1,t2 --kt 0.6
     121             : ```
     122             : 
     123             : ## Following the convergence
     124             : 
     125             : With `--stride` you can write the free energy after each bunch of hills (the stride is a number of hills):
     126             : 
     127             : ```plumed
     128             : plumed sum_hills --stride 300 --hills PATHTOMYHILLSFILE
     129             : ```
     130             : 
     131             : The output files are then called `fes_0.dat`, `fes_1.dat`, etc. (see below), and the last one contains all the hills.
     132             : 
     133             : With `--nohistory` each output contains only the hills of its own block of hills, instead of all the hills up to that point:
     134             : 
     135             : ```plumed
     136             : plumed sum_hills --stride 300 --hills PATHTOMYHILLSFILE --nohistory
     137             : ```
     138             : 
     139             : In this way you can monitor the bias deposited in blocks of time as a metadynamics simulation goes on:
     140             : when the bias deposited in a block is constant over the whole domain, the simulation can be considered to be at convergence.
     141             : 
     142             : To check the convergence you can also follow the free-energy difference between basins as the
     143             : simulation goes on. With `--basins` you can define two or more basins as boxes, each given by the minimum and
     144             : the maximum of each variable of the output, with the basins separated by colons. Along a periodic variable a
     145             : basin can cross the boundary if its minimum is larger than its maximum. For example
     146             : 
     147             : ```plumed
     148             : plumed sum_hills --hills PATHTOMYHILLSFILE --stride 500 --kt 2.5 --basins -pi,0,-pi,pi:0,pi,-pi,pi
     149             : ```
     150             : 
     151             : writes, at each output, the time of the last hill read, the number of hills, and the free energy of each basin
     152             : relative to the first one, computed as $-k_BT \log \sum_{s\in\mathrm{basin}} e^{-F(s)/k_BT}$ over the points of
     153             : the grid, in the file `basins.dat` (the name can be set with `--outbasins`). With `--idw` the basins are defined on
     154             : the projected variables. With `--histo` (see below) the same is done for the free energy from the histogram, in the file `histo_basins.dat`.
     155             : A plateau of these differences is a necessary (not sufficient) indication of convergence.
     156             : 
     157             : ## Time average of the bias
     158             : 
     159             : With standard (not well-tempered) metadynamics the bias does not converge but fluctuates around the free energy.
     160             : A better estimate is the time average of the bias after an initial transient, which you can obtain with `--average`:
     161             : 
     162             : ```plumed
     163             : plumed sum_hills --hills PATHTOMYHILLSFILE --average 1000
     164             : ```
     165             : 
     166             : computes the average of the bias between time 1000 (in the units of the time column of the hills file) and the time of
     167             : the last hill. This is done by scaling the height of each hill deposited at time $t$ after 1000 by $(t_1-t)/(t_1-1000)$, where
     168             : $t_1$ is the final time. You can also give the final time explicitly, e.g. `--average 1000,5000`: hills deposited after
     169             : 5000 are ignored. The same average is useful for well-tempered metadynamics, where it reduces the fluctuations of the
     170             : estimate of the free energy. Combined with `--stride`, each output is the average from the initial time up to the last hill read.
     171             : This is the same average computed by the function `hills_time_average` of the plumed python module.
     172             : 
     173             : ## Bias instead of free energy
     174             : 
     175             : If you have run well-tempered metadynamics you can also ask sum_hills to output the negative bias, $-V(s)=(\gamma-1)/\gamma\, F(s)$,
     176             : instead of the free energy by using `--negbias`:
     177             : 
     178             : ```plumed
     179             : plumed sum_hills --negbias --hills PATHTOMYHILLSFILE
     180             : ```
     181             : 
     182             : Here the default output file name is `negativebias.dat`. If the simulation was not well tempered the bias is minus the free energy,
     183             : and the output is the same as without `--negbias`.
     184             : 
     185             : ## Histograms
     186             : 
     187             : From time to time you might need to use a hills or a colvar file as a set of points from which you want to build
     188             : a free energy as $-k_BT \log P(s)$. You can do this with `--histo`:
     189             : 
     190             : ```plumed
     191             : plumed sum_hills --histo PATHTOMYCOLVARORHILLSFILE --sigma 0.2,0.2 --kt 0.6
     192             : ```
     193             : 
     194             : The histogram is computed with Gaussian kernels whose widths are given with `--sigma`, one for each variable,
     195             : so it is a continuous histogram, and `--kt` is needed to convert it into a free energy. The default output file is `histo.dat`.
     196             : With a hills file the variables are those of the hills; with a colvar file all the columns but time are variables, so you
     197             : will usually select some of them with `--idw` (and give one width for each of them).
     198             : Multiple input files can be given, separated by commas.
     199             : 
     200             : All the points have the same weight: the result is the distribution sampled by the simulation, which is the
     201             : free energy only if the simulation was not biased. **sum_hills does not reweight the points.** To compute a free
     202             : energy from a biased simulation (e.g. from the COLVAR file of a metadynamics simulation) use [driver](driver.md) with
     203             : [READ](READ.md), [REWEIGHT_BIAS](REWEIGHT_BIAS.md) (or [REWEIGHT_METAD](REWEIGHT_METAD.md)), [HISTOGRAM](HISTOGRAM.md)
     204             : and [CONVERT_TO_FES](CONVERT_TO_FES.md), for example
     205             : 
     206             : ```plumed
     207             : #SETTINGS INPUTFILES=regtest/basic/rt-fametad-2-restart/COLVAR
     208             : s: READ FILE=regtest/basic/rt-fametad-2-restart/COLVAR VALUES=sum_abs IGNORE_TIME IGNORE_FORCES
     209             : metad: READ FILE=regtest/basic/rt-fametad-2-restart/COLVAR VALUES=MetaD.bias IGNORE_TIME IGNORE_FORCES
     210             : w: REWEIGHT_BIAS TEMP=300 ARG=metad.bias
     211             : hh: HISTOGRAM ARG=s GRID_MIN=0 GRID_MAX=1 GRID_BIN=100 BANDWIDTH=0.02 LOGWEIGHTS=w NORMALIZATION=true
     212             : ff: CONVERT_TO_FES ARG=hh TEMP=300
     213             : DUMPGRID ARG=ff FILE=fes.dat
     214             : ```
     215             : 
     216             : run with `plumed driver --plumed plumed.dat --noatoms`. With well-tempered metadynamics the bias changes with time:
     217             : use the `rbias` component of [METAD](METAD.md) (with `CALC_RCT`) instead of the bias, or [REWEIGHT_METAD](REWEIGHT_METAD.md).
     218             : Block averages, which give the statistical error of the free energy, are discussed in the documentation of [HISTOGRAM](HISTOGRAM.md)
     219             : and in the tutorial linked there.
     220             : 
     221             : You can compute the free energy from the hills and the histogram at the same time, e.g. from the same file:
     222             : 
     223             : ```plumed
     224             : plumed sum_hills --hills PATHTOMYHILLSFILE --histo PATHTOMYHILLSFILE --sigma 0.2,0.2 --kt 0.6
     225             : ```
     226             : 
     227             : `--nohistory` and `--stride` can also be used with `--histo`, to compute the histogram in blocks of points:
     228             : 
     229             : ```plumed
     230             : plumed sum_hills --histo PATHTOMYCOLVARORHILLSFILE --sigma 0.2,0.2 --kt 0.6 --stride 1000 --nohistory
     231             : ```
     232             : 
     233             : ## Output
     234             : 
     235             : The output format can be controlled by using the `--fmt` option as shown below
     236             : 
     237             : ```plumed
     238             : plumed sum_hills --hills PATHTOMYHILLSFILE --fmt %8.3f
     239             : ```
     240             : 
     241             : where here we chose a float with length of 8 and 3 digits.
     242             : 
     243             : The output file can be named in an arbitrary way by using the `--outfile` option as shown below:
     244             : 
     245             : ```plumed
     246             : plumed sum_hills --hills PATHTOMYHILLSFILE --outfile myfes.dat
     247             : ```
     248             : 
     249             : This command produces a file `myfes.dat` which contains the free energy surface.
     250             : 
     251             : If you use `--stride`, the name specified with `--outfile` is the prefix of the files, so this command:
     252             : 
     253             : ```plumed
     254             : plumed sum_hills --hills PATHTOMYHILLSFILE --outfile myfes_ --stride 100
     255             : ```
     256             : 
     257             : produces output files called `myfes_0.dat`, `myfes_1.dat`, `myfes_2.dat` etc.
     258             : 
     259             : The same is true for the output of the histogram, which is named with `--outhisto`. So this example
     260             : 
     261             : ```plumed
     262             : plumed sum_hills --histo HILLS --kt 2.5 --sigma 0.01 --outhisto myhisto.dat
     263             : ```
     264             : 
     265             : produces a file `myhisto.dat`, while this input
     266             : 
     267             : ```plumed
     268             : plumed sum_hills --histo HILLS --kt 2.5 --sigma 0.01 --outhisto myhisto_ --stride 100
     269             : ```
     270             : 
     271             : produces output files called `myhisto_0.dat`, `myhisto_1.dat`, `myhisto_2.dat` etc.
     272             : 
     273             : */
     274             : //+ENDPLUMEDOC
     275             : 
     276             : class CLToolSumHills : public CLTool {
     277             : public:
     278             :   static void registerKeywords( Keywords& keys );
     279             :   explicit CLToolSumHills(const CLToolOptions& co );
     280             :   int main(FILE* in,FILE*out,Communicator& pc) override;
     281             :   std::string description()const override;
     282             : /// find a list of variables present, if they are periodic and which is the period
     283             : /// return false if the file does not exist
     284             :   static bool findCvsAndPeriodic(const std::string & filename, std::vector< std::vector <std::string> > &cvs,std::vector<std::string> &pmin,std::vector<std::string> &pmax, bool &multivariate, std::string &lowI_, std::string &uppI_);
     285             : };
     286             : 
     287        7321 : void CLToolSumHills::registerKeywords( Keywords& keys ) {
     288        7321 :   CLTool::registerKeywords( keys );
     289        7321 :   keys.addFlag("--help-debug",false,"print special options that can be used to create regtests");
     290        7321 :   keys.add("optional","--hills","the hills files to sum, separated by commas");
     291        7321 :   keys.add("optional","--histo","the files (hills or colvar) from which a histogram is computed, separated by commas; the points are not reweighted");
     292        7321 :   keys.add("optional","--stride","write the free energy (or histogram) every this number of hills (or points); by default only at the end");
     293        7321 :   keys.add("optional","--min","the lower bounds of the grid, one for each variable; by default they are detected from the hills");
     294        7321 :   keys.add("optional","--max","the upper bounds of the grid, one for each variable; by default they are detected from the hills");
     295        7321 :   keys.add("optional","--bin","the number of bins of the grid, one for each variable");
     296        7321 :   keys.add("optional","--spacing","the grid spacing, one for each variable, alternative to the number of bins");
     297        7321 :   keys.add("optional","--idw","the variables of the output (default all). With --hills the other variables are integrated out, with --histo the other variables are not considered");
     298        7321 :   keys.add("optional","--outfile","the output file for the free energy (fes.dat, or negativebias.dat with --negbias); with --stride the prefix of the files");
     299        7321 :   keys.add("optional","--outhisto","the output file for the histogram (histo.dat); with --stride the prefix of the files");
     300        7321 :   keys.add("optional","--kt","the temperature in energy units, needed with --idw, --histo and --basins");
     301        7321 :   keys.add("optional","--sigma","the widths of the Gaussian kernels of the histogram, one for each variable (needed with --histo)");
     302        7321 :   keys.addFlag("--negbias",false,"write minus the bias instead of the free energy (they differ only with well-tempered metadynamics)");
     303        7321 :   keys.addFlag("--nohistory",false,"with --stride, each output contains only the hills (or points) of its own block");
     304        7321 :   keys.addFlag("--mintozero",false,"shift the free energy (or histogram) so that its minimum is zero");
     305        7321 :   keys.add("optional","--fmt","the format of the numbers in the output, e.g. %14.9f");
     306        7321 :   keys.add("optional","--sort","time or clock: with multiple files (e.g. one per walker), merge the hills in order of time or clock instead of reading the files one after the other");
     307        7321 :   keys.add("optional","--average","time average of the bias from time t0 (t0) or between times t0 and t1 (t0,t1); without t1 the average ends at the last hill read");
     308        7321 :   keys.add("optional","--basins","basins separated by colons, each given by the minimum and maximum of each output variable (e.g. -3,-1:1,3); the free energy of each basin relative to the first one is written at each output");
     309        7321 :   keys.add("optional","--outbasins","the output file for the free energies of the basins (basins.dat)");
     310        7321 : }
     311             : 
     312          22 : CLToolSumHills::CLToolSumHills(const CLToolOptions& co ):
     313          22 :   CLTool(co) {
     314          22 :   inputdata=inputType::commandline;
     315          22 : }
     316             : 
     317           7 : std::string CLToolSumHills::description()const {
     318           7 :   return "sum the hills of metadynamics to compute the free energy";
     319             : }
     320             : 
     321          15 : int CLToolSumHills::main(FILE* in,FILE*out,Communicator& pc) {
     322             : 
     323             : // Read the hills input file name
     324          15 :   std::vector<std::string> hillsFiles;
     325             :   bool dohills;
     326          15 :   dohills=parseVector("--hills",hillsFiles);
     327             : // Read the histogram file
     328          15 :   std::vector<std::string> histoFiles;
     329             :   bool dohisto;
     330          15 :   dohisto=parseVector("--histo",histoFiles);
     331             : 
     332          15 :   plumed_massert(dohisto || dohills,"you should use --histo or/and --hills command");
     333             : 
     334          15 :   std::vector< std::vector<std::string> > vcvs;
     335          15 :   std::vector<std::string> vpmin;
     336          15 :   std::vector<std::string> vpmax;
     337             :   std::string lowI_, uppI_;
     338          15 :   if(dohills) {
     339             :     // parse it as it was a restart
     340             :     bool vmultivariate;
     341          14 :     if(!findCvsAndPeriodic(hillsFiles[0], vcvs, vpmin, vpmax, vmultivariate, lowI_, uppI_)) {
     342           0 :       plumed_merror("cannot find file "+hillsFiles[0]);
     343             :     }
     344          14 :     if(vcvs.empty()) {
     345           0 :       plumed_merror("no collective variable found in file "+hillsFiles[0]);
     346             :     }
     347             :   }
     348             : 
     349          15 :   std::vector< std::vector<std::string> > hcvs;
     350          15 :   std::vector<std::string> hpmin;
     351          15 :   std::vector<std::string> hpmax;
     352             : 
     353          15 :   std::vector<std::string> sigma;
     354          15 :   if(dohisto) {
     355             :     bool hmultivariate;
     356           1 :     if(!findCvsAndPeriodic(histoFiles[0], hcvs, hpmin, hpmax, hmultivariate, lowI_, uppI_)) {
     357           0 :       plumed_merror("cannot find file "+histoFiles[0]);
     358             :     }
     359           1 :     if(hcvs.empty()) {
     360           0 :       plumed_merror("no collective variable found in file "+histoFiles[0]);
     361             :     }
     362             :     // here need also the vector of sigmas
     363           2 :     parseVector("--sigma",sigma);
     364           1 :     if(sigma.size()==0) {
     365           0 :       plumed_merror("you should define --sigma vector when using histogram");
     366             :     }
     367             :     lowI_=uppI_="-1.";  // Interval is not use for histograms
     368             :   }
     369             : 
     370           1 :   if(dohisto && dohills) {
     371           0 :     plumed_massert(vcvs==hcvs,"variables for histogram and bias should have the same labels");
     372           0 :     plumed_massert(hpmin==vpmin,"variables for histogram and bias should have the same min for periodicity");
     373           0 :     plumed_massert(hpmax==vpmax,"variables for histogram and bias should have the same max for periodicity");
     374             :   }
     375             : 
     376             :   // now put into a neutral vector
     377             : 
     378          15 :   std::vector< std::vector<std::string> > cvs;
     379          15 :   std::vector<std::string> pmin;
     380          15 :   std::vector<std::string> pmax;
     381             : 
     382          15 :   if(dohills) {
     383          14 :     cvs=vcvs;
     384          14 :     pmin=vpmin;
     385          14 :     pmax=vpmax;
     386             :   }
     387          15 :   if(dohisto) {
     388           1 :     cvs=hcvs;
     389           1 :     pmin=hpmin;
     390           1 :     pmax=hpmax;
     391             :   }
     392             : 
     393             : 
     394             :   // setup grids
     395             :   unsigned grid_check=0;
     396          15 :   std::vector<std::string> gmin(cvs.size());
     397          30 :   if(parseVector("--min",gmin)) {
     398           7 :     if(gmin.size()!=cvs.size() && gmin.size()!=0) {
     399           0 :       plumed_merror("not enough values for --min");
     400             :     }
     401             :     grid_check++;
     402             :   }
     403          15 :   std::vector<std::string> gmax(cvs.size() );
     404          30 :   if(parseVector("--max",gmax)) {
     405           7 :     if(gmax.size()!=cvs.size() && gmax.size()!=0) {
     406           0 :       plumed_merror("not enough values for --max");
     407             :     }
     408           7 :     grid_check++;
     409             :   }
     410          15 :   std::vector<std::string> gbin(cvs.size());
     411             :   bool grid_has_bin;
     412             :   grid_has_bin=false;
     413          30 :   if(parseVector("--bin",gbin)) {
     414           9 :     if(gbin.size()!=cvs.size() && gbin.size()!=0) {
     415           0 :       plumed_merror("not enough values for --bin");
     416             :     }
     417             :     grid_has_bin=true;
     418             :   }
     419          15 :   std::vector<std::string> gspacing(cvs.size());
     420             :   bool grid_has_spacing;
     421             :   grid_has_spacing=false;
     422          30 :   if(parseVector("--spacing",gspacing)) {
     423           2 :     if(gspacing.size()!=cvs.size() && gspacing.size()!=0) {
     424           0 :       plumed_merror("not enough values for --spacing");
     425             :     }
     426             :     grid_has_spacing=true;
     427             :   }
     428             :   // allowed: no grids only bin
     429             :   // not allowed: partial grid definition
     430          15 :   plumed_massert( gmin.size()==gmax.size() && (gmin.size()==0 ||  gmin.size()==cvs.size() ),"you should specify --min and --max together with same number of components");
     431             : 
     432             : 
     433             : 
     434          15 :   PlumedMain plumed;
     435             :   std::string ss;
     436          15 :   unsigned nn=1;
     437             :   ss="setNatoms";
     438          15 :   plumed.cmd(ss,&nn);
     439          15 :   if(Communicator::initialized()) {
     440           0 :     plumed.cmd("setMPIComm",&pc.Get_comm());
     441             :   }
     442          15 :   plumed.cmd("init",&nn);
     443          15 :   std::vector <bool> isdone(cvs.size(),false);
     444          41 :   for(unsigned i=0; i<cvs.size(); i++) {
     445          26 :     if(!isdone[i]) {
     446             :       isdone[i]=true;
     447          26 :       std::vector<std::string> actioninput;
     448          26 :       std::vector <unsigned> inds;
     449          26 :       actioninput.push_back("FAKE");
     450          52 :       actioninput.push_back("ATOMS=1");
     451          26 :       actioninput.push_back("LABEL="+cvs[i][0]);
     452          26 :       std::vector<std::string> comps, periods;
     453          26 :       if(cvs[i].size()>1) {
     454           1 :         comps.push_back(cvs[i][1]);
     455           1 :         inds.push_back(i);
     456             :       }
     457          26 :       periods.push_back(pmin[i]);
     458          26 :       periods.push_back(pmax[i]);
     459          37 :       for(unsigned j=i+1; j<cvs.size(); j++) {
     460          11 :         if(cvs[i][0]==cvs[j][0] && !isdone[j]) {
     461           0 :           if(cvs[i].size()==1 || cvs[j].size()==1  ) {
     462           0 :             plumed_merror("you cannot have twice the same label and no components ");
     463             :           }
     464           0 :           if(cvs[j].size()>1) {
     465           0 :             comps.push_back(cvs[j][1]);
     466           0 :             periods.push_back(pmin[j]);
     467           0 :             periods.push_back(pmax[j]);
     468             :             isdone[j]=true;
     469           0 :             inds.push_back(j);
     470             :           }
     471             :         }
     472             : 
     473             :       }
     474             :       // drain all the components
     475             :       std::string addme;
     476          26 :       if(comps.size()>0) {
     477             :         addme="COMPONENTS=";
     478           1 :         for(unsigned ii=0; ii<comps.size()-1; ii++) {
     479           0 :           addme+=comps[ii]+",";
     480             :         }
     481             :         addme+=comps.back();
     482           1 :         actioninput.push_back(addme);
     483             :       }
     484             :       // periodicity (always explicit here)
     485             :       addme="PERIODIC=";
     486          52 :       for(unsigned j=0; j<periods.size()-1; j++) {
     487          52 :         addme+=periods[j]+",";
     488             :       }
     489             :       addme+=periods.back();
     490          26 :       actioninput.push_back(addme);
     491          27 :       for(unsigned j=0; j<inds.size(); j++) {
     492             :         unsigned jj;
     493           1 :         jj=inds[j];
     494           1 :         if(grid_check==2) {
     495             :           double gm;
     496             :           double pm;
     497           1 :           if(pmin[jj]!="none") {
     498           1 :             Tools::convert(gmin[jj],gm);
     499           1 :             Tools::convert(pmin[jj],pm);
     500           1 :             if(  gm<pm  ) {
     501           0 :               plumed_merror("Periodicity issue : GRID_MIN value ( "+gmin[jj]+" ) is less than periodicity in HILLS file in "+cvs[jj][0]+ " ( "+pmin[jj]+" ) ");
     502             :             }
     503             :           }
     504           1 :           if(pmax[jj]!="none") {
     505           1 :             Tools::convert(gmax[jj],gm);
     506           1 :             Tools::convert(pmax[jj],pm);
     507           1 :             if(  gm>pm ) {
     508           0 :               plumed_merror("Periodicity issue : GRID_MAX value ( "+gmax[jj]+" ) is more than periodicity in HILLS file in "+cvs[jj][0]+ " ( "+pmax[jj]+" ) ");
     509             :             }
     510             :           }
     511             :         }
     512             :       }
     513             : 
     514          26 :       plumed.readInputWords(actioninput,false);
     515          26 :     }
     516             : 
     517             :   }
     518          15 :   unsigned ncv=cvs.size();
     519          15 :   std::vector<std::string> actioninput;
     520          15 :   std::vector<std::string> idw;
     521             :   // check if the variables to be used are correct
     522          30 :   if(parseVector("--idw",idw)) {
     523           4 :     for(unsigned i=0; i<idw.size(); i++) {
     524             :       bool found=false;
     525           6 :       for(unsigned j=0; j<cvs.size(); j++) {
     526           4 :         if(cvs[j].size()>1) {
     527           0 :           if(idw[i]==cvs[j][0]+"."+cvs[j][1]) {
     528             :             found=true;
     529             :           }
     530             :         } else {
     531           4 :           if(idw[i]==cvs[j][0]) {
     532             :             found=true;
     533             :           }
     534             :         }
     535             :       }
     536           2 :       if(!found) {
     537           0 :         plumed_merror("variable "+idw[i]+" is not found in the bunch of cvs: revise your --idw option" );
     538             :       }
     539             :     }
     540           2 :     plumed_massert( idw.size()<=cvs.size(),"the number of variables to be integrated should be at most equal to the total number of cvs  ");
     541             :     // in this case you need a beta factor!
     542             :   }
     543             : 
     544             :   std::string basins;
     545          30 :   parse("--basins",basins);
     546             : 
     547             :   std::string kt;
     548          15 :   kt=std::string("1.");// assign an arbitrary value just in case that idw.size()==cvs.size()
     549          14 :   if ( dohisto || idw.size()!=0 || basins.length()>0 ) {
     550           8 :     plumed_massert(parse("--kt",kt),"if you make a dimensionality reduction (--idw), a histogram (--histo) or use --basins then you need to define --kt ");
     551             :   }
     552             : 
     553             :   std::string addme;
     554             : 
     555          15 :   actioninput.push_back("FUNCSUMHILLS");
     556          30 :   actioninput.push_back("ISCLTOOL");
     557             : 
     558             :   // set names
     559             :   std::string outfile;
     560          30 :   if(parse("--outfile",outfile)) {
     561           0 :     actioninput.push_back("OUTHILLS="+outfile);
     562             :   }
     563             :   std::string outhisto;
     564          30 :   if(parse("--outhisto",outhisto)) {
     565           2 :     actioninput.push_back("OUTHISTO="+outhisto);
     566             :   }
     567             : 
     568             : 
     569             :   addme="ARG=";
     570          26 :   for(unsigned i=0; i<(ncv-1); i++) {
     571          11 :     if(cvs[i].size()==1) {
     572          22 :       addme+=std::string(cvs[i][0])+",";
     573             :     } else {
     574           0 :       addme+=std::string(cvs[i][0])+"."+std::string(cvs[i][1])+",";
     575             :     }
     576             :   }
     577          15 :   if(cvs[ncv-1].size()==1) {
     578          28 :     addme+=std::string(cvs[ncv-1][0]);
     579             :   } else {
     580           3 :     addme+=std::string(cvs[ncv-1][0])+"."+std::string(cvs[ncv-1][1]);
     581             :   }
     582          15 :   actioninput.push_back(addme);
     583          15 :   if(dohills) {
     584             :     addme="HILLSFILES=";
     585          17 :     for(unsigned i=0; i<hillsFiles.size()-1; i++) {
     586           6 :       addme+=hillsFiles[i]+",";
     587             :     }
     588             :     addme+=hillsFiles[hillsFiles.size()-1];
     589          14 :     actioninput.push_back(addme);
     590             :     // set the grid
     591             :   }
     592          15 :   if(grid_check==2) {
     593             :     addme="GRID_MAX=";
     594          10 :     for(unsigned i=0; i<(ncv-1); i++) {
     595           6 :       addme+=gmax[i]+",";
     596             :     }
     597             :     addme+=gmax[ncv-1];
     598           7 :     actioninput.push_back(addme);
     599             :     addme="GRID_MIN=";
     600          10 :     for(unsigned i=0; i<(ncv-1); i++) {
     601           6 :       addme+=gmin[i]+",";
     602             :     }
     603             :     addme+=gmin[ncv-1];
     604           7 :     actioninput.push_back(addme);
     605             :   }
     606          15 :   if(grid_has_bin) {
     607             :     addme="GRID_BIN=";
     608          15 :     for(unsigned i=0; i<(ncv-1); i++) {
     609          12 :       addme+=gbin[i]+",";
     610             :     }
     611             :     addme+=gbin[ncv-1];
     612           9 :     actioninput.push_back(addme);
     613             :   }
     614          15 :   if(grid_has_spacing) {
     615             :     addme="GRID_SPACING=";
     616           3 :     for(unsigned i=0; i<(ncv-1); i++) {
     617           2 :       addme+=gspacing[i]+",";
     618             :     }
     619             :     addme+=gspacing[ncv-1];
     620           2 :     actioninput.push_back(addme);
     621             :   }
     622             :   std::string  stride;
     623             :   stride="";
     624          30 :   if(parse("--stride",stride)) {
     625           4 :     actioninput.push_back("INITSTRIDE="+stride);
     626             :     bool  nohistory;
     627           4 :     parseFlag("--nohistory",nohistory);
     628           4 :     if(nohistory) {
     629           0 :       actioninput.push_back("NOHISTORY");
     630             :     }
     631             :   }
     632             :   bool  mintozero;
     633          15 :   parseFlag("--mintozero",mintozero);
     634          15 :   if(mintozero) {
     635           0 :     actioninput.push_back("MINTOZERO");
     636             :   }
     637          15 :   if(idw.size()!=0) {
     638             :     addme="PROJ=";
     639           2 :     for(unsigned i=0; i<idw.size()-1; i++) {
     640           0 :       addme+=idw[i]+",";
     641             :     }
     642             :     addme+=idw.back();
     643           2 :     actioninput.push_back(addme);
     644             :   }
     645             : 
     646          15 :   if(dohisto) {
     647           1 :     if(idw.size()==0) {
     648           1 :       if(sigma.size()!=hcvs.size()) {
     649           0 :         plumed_merror("you should define as many --sigma vector as the number of collective variable used for the histogram ");
     650             :       }
     651             :     } else {
     652           0 :       if(idw.size()!=sigma.size()) {
     653           0 :         plumed_merror("you should define as many --sigma vector as the number of collective variable used for the histogram ");
     654             :       }
     655             :     }
     656             :   }
     657             : 
     658          15 :   if(idw.size()!=0 || dohisto || basins.length()>0) {
     659           8 :     actioninput.push_back("KT="+kt);
     660             :   }
     661          15 :   if(basins.length()>0) {
     662           2 :     actioninput.push_back("BASINS="+basins);
     663             :     std::string outbasins;
     664           2 :     if(parse("--outbasins",outbasins)) {
     665           0 :       actioninput.push_back("OUTBASINS="+outbasins);
     666             :     }
     667             :   }
     668          15 :   std::vector<std::string> average;
     669          30 :   if(parseVector("--average",average)) {
     670           1 :     if(average.size()!=1 && average.size()!=2) {
     671           0 :       plumed_merror("--average needs the initial time and optionally the final time");
     672             :     }
     673           2 :     actioninput.push_back("AVERAGE="+average[0]+(average.size()==2 ? ","+average[1] : ""));
     674             :   }
     675          15 :   if(dohisto) {
     676             :     addme="HISTOFILES=";
     677           1 :     for(unsigned i=0; i<histoFiles.size()-1; i++) {
     678           0 :       addme+=histoFiles[i]+",";
     679             :     }
     680             :     addme+=histoFiles[histoFiles.size()-1];
     681           1 :     actioninput.push_back(addme);
     682             : 
     683             :     addme="HISTOSIGMA=";
     684           2 :     for(unsigned i=0; i<sigma.size()-1; i++) {
     685           2 :       addme+=sigma[i]+",";
     686             :     }
     687             :     addme+=sigma.back();
     688           1 :     actioninput.push_back(addme);
     689             :   }
     690             : 
     691             :   bool negbias;
     692          15 :   parseFlag("--negbias",negbias);
     693          15 :   if(negbias) {
     694           4 :     actioninput.push_back("NEGBIAS");
     695             :   }
     696             : 
     697          15 :   if(lowI_!=uppI_) {
     698             :     addme="INTERVAL=";
     699           0 :     addme+=lowI_+",";
     700             :     addme+=uppI_;
     701           0 :     actioninput.push_back(addme);
     702             :   }
     703             : 
     704             :   std::string sortby;
     705          30 :   if(parse("--sort",sortby)) {
     706           1 :     if(sortby!="time" && sortby!="clock") {
     707           0 :       plumed_merror("--sort can only be time or clock");
     708             :     }
     709           2 :     actioninput.push_back("SORT="+sortby);
     710             :   }
     711             : 
     712             :   std::string fmt;
     713             :   fmt="";
     714          15 :   parse("--fmt",fmt);
     715          15 :   if(fmt!="") {
     716          30 :     actioninput.push_back("FMT="+fmt);
     717             :   }
     718             : 
     719             : 
     720          15 :   plumed.readInputWords(actioninput,false);
     721             :   // if not a grid, then set it up automatically
     722          15 :   return 0;
     723          90 : }
     724             : 
     725          15 : bool CLToolSumHills::findCvsAndPeriodic(const std::string & filename,
     726             :                                         std::vector< std::vector<std::string>  > &cvs,
     727             :                                         std::vector<std::string> &pmin,
     728             :                                         std::vector<std::string> &pmax,
     729             :                                         bool &multivariate,
     730             :                                         std::string &lowI_,
     731             :                                         std::string &uppI_) {
     732          15 :   IFile ifile;
     733          15 :   ifile.allowIgnoredFields();
     734          15 :   if(!ifile.FileExist(filename)) {
     735             :     return false;
     736             :   }
     737          15 :   cvs.clear();
     738          15 :   pmin.clear();
     739          15 :   pmax.clear();
     740          15 :   ifile.open(filename);
     741          15 :   std::vector<std::string> fields;
     742          15 :   ifile.scanFieldList(fields);
     743         307 :   auto hasField=[&](const std::string & name) {
     744         307 :     return std::find(fields.begin(),fields.end(),name)!=fields.end();
     745          15 :   };
     746             :   // In a hills file a column X is a collective variable if its width is also
     747             :   // there (sigma_X, or sigma_X_X for multivariate kernels). Names are compared
     748             :   // exactly, so that variables such as sigma_1 or lifetime are accepted (see #1226).
     749             :   // In a file without widths (e.g. a COLVAR file used with --histo) all the
     750             :   // columns but time are variables.
     751             :   bool hasSigma=false;
     752          30 :   for(const auto & f : fields) {
     753          69 :     if(f!="time" && (hasField("sigma_"+f) || hasField("sigma_"+f+"_"+f))) {
     754             :       hasSigma=true;
     755             :       break;
     756             :     }
     757             :   }
     758         180 :   for(const auto & f : fields) {
     759         165 :     if(f=="time") {
     760          15 :       continue;
     761             :     }
     762         856 :     if(hasSigma && !hasField("sigma_"+f) && !hasField("sigma_"+f+"_"+f)) {
     763         124 :       continue;
     764             :     }
     765             :     // separate the components
     766             :     auto dot=f.find_first_of('.');
     767          26 :     if(dot!=std::string::npos) {
     768           3 :       cvs.emplace_back(std::vector<std::string> {f.substr(0,dot),f.substr(dot+1)});
     769             :     } else {
     770          50 :       cvs.emplace_back(std::vector<std::string> {f});
     771             :     }
     772             :     // get periodicity
     773          26 :     pmin.push_back("none");
     774          26 :     pmax.push_back("none");
     775          52 :     if(ifile.FieldExist("min_"+f)) {
     776          32 :       ifile.scanField("min_"+f,pmin.back());
     777             :     }
     778          52 :     if(ifile.FieldExist("max_"+f)) {
     779          32 :       ifile.scanField("max_"+f,pmax.back());
     780             :     }
     781             :   }
     782             :   // is multivariate ???
     783          15 :   multivariate=false;
     784          30 :   if(ifile.FieldExist("multivariate")) {
     785             :     std::string sss;
     786          15 :     ifile.scanField("multivariate",sss);
     787          15 :     multivariate=(sss=="true");
     788             :   }
     789             :   // do interval?
     790          30 :   if(ifile.FieldExist("lower_int")) {
     791           0 :     ifile.scanField("lower_int",lowI_);
     792           0 :     ifile.scanField("upper_int",uppI_);
     793             :   } else {
     794             :     lowI_="-1.";
     795             :     uppI_="-1.";
     796             :   }
     797          15 :   ifile.scanField();
     798             :   return true;
     799          41 : }
     800             : 
     801             : 
     802       21985 : PLUMED_REGISTER_CLTOOL(CLToolSumHills,"sum_hills")
     803             : 
     804             : 
     805             : 
     806             : }
     807             : }

Generated by: LCOV version 1.16