LCOV - code coverage report
Current view: top level - ves - TD_Multicanonical.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 169 173 97.7 %
Date: 2019-08-13 10:15:31 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-2018 The VES code team
       3             :    (see the PEOPLE-VES file at the root of this folder for a list of names)
       4             : 
       5             :    See http://www.ves-code.org for more information.
       6             : 
       7             :    This file is part of VES code module.
       8             : 
       9             :    The VES code module is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    The VES code module is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with the VES code module.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : 
      23             : #include "TargetDistribution.h"
      24             : #include "GridIntegrationWeights.h"
      25             : #include "core/ActionRegister.h"
      26             : #include "tools/Grid.h"
      27             : #include "core/PlumedMain.h"
      28             : #include "core/Atoms.h"
      29             : #include <cfloat>
      30             : 
      31             : 
      32             : namespace PLMD {
      33             : namespace ves {
      34             : 
      35             : //+PLUMEDOC VES_TARGETDIST TD_MULTICANONICAL
      36             : /*
      37             : Multicanonical target distribution (dynamic).
      38             : 
      39             : Use the target distribution to sample the multicanonical ensemble \cite Berg-PRL-1992 \cite Piaggi-PRL-2019.
      40             : In this way, in a single molecular dynamics simulation one can obtain information about the system in a range of temperatures.
      41             : This range is determined through the keywords MIN_TEMP and MAX_TEMP.
      42             : 
      43             : The collective variables (CVs) used to construct the bias potential must be:
      44             :  1. the energy or,
      45             :  2. the energy and an order parameter.
      46             : 
      47             : Other choices of CVs or a different order of the above mentioned CVs are nonsensical.
      48             : The second CV, the order parameter, must be used when one aims at studying a first order phase transition in the chosen temperature interval \cite Piaggi-arXiv-2019.
      49             : 
      50             : The algorithm will explore the free energy at each temperature up to a predefined free
      51             :  energy threshold \f$\epsilon\f$ specified through the keyword THRESHOLD (in kT units).
      52             : If only the energy is biased, i.e. no phase transition is considered, then TRESHOLD can be set to 1.
      53             : If also an order parameter is used then the THRESHOLD should be greater than the barrier for the transformation in kT.
      54             : For small systems undergoing a freezing transition THRESHOLD is typically between 20 and 50.
      55             : 
      56             : When only the potential energy is used as CV the method is equivalent to the Wang-Landau algorithm \cite wanglandau.
      57             : The advantage with respect to Wang-Landau is that instead of sampling the potential energy indiscriminately, an interval is chosen on the fly based on the minimum and maximum targeted temperatures.
      58             : 
      59             : The algorithm works as follows.
      60             : The target distribution for the potential energy is chosen to be:
      61             : \f[
      62             : p(E)= \begin{cases}
      63             :          \frac{1}{E_2-E_1} & \mathrm{if} \quad E_1<E<E_2 \\
      64             :          0 & \mathrm{otherwise}
      65             :       \end{cases}
      66             : \f]
      67             : where the energy limits \f$E_1\f$ and \f$E_2\f$ are yet to be determined.
      68             : Clearly the interval \f$E_1–E_2\f$ chosen is related to the interval of temperatures \f$T_1-T_2\f$.
      69             : To link these two intervals we make use of the following relation:
      70             : \f[
      71             : \beta' F_{\beta'}(E) = \beta F_{\beta}(E) + (\beta' - \beta) E + C,
      72             : \f]
      73             : where \f$F_{\beta}(E)\f$ is determined during the optimization and we shall choose \f$C\f$ such that \f$F_{\beta'}(E_{m})=0\f$ with \f$E_{m}\f$ the position of the free energy minimum.
      74             : Using this relation we employ an iterative procedure to find the energy interval.
      75             : At iteration \f$k\f$ we have the estimates \f$E_1^k\f$ and \f$E_2^k\f$ for \f$E_1\f$ and \f$E_2\f$, and the target distribution is:
      76             : \f[
      77             : p^k(E)=\frac{1}{E_2^k-E_1^k} \quad \mathrm{for} \quad E_1^k<E<E_2^k.
      78             : \f]
      79             : \f$E_1^k\f$ and \f$E_2^k\f$ are obtained from the leftmost solution of \f$\beta_2 F_{\beta_2}^{k-1}(E_1^k)=\epsilon\f$ and the rightmost solution of \f$\beta_1 F_{\beta_1}^{k-1}(E_2^k)=\epsilon\f$.
      80             : The procedure is repeated until convergence.
      81             : This iterative approach is similar to that in \ref TD_WELLTEMPERED.
      82             : 
      83             : The version of this algorithm in which the energy and an order parameter are biased is similar to the one described in \ref TD_MULTITHERMAL_MULTIBARIC.
      84             : 
      85             : The output of these simulations can be reweighted in order to obtain information at all temperatures in the targeted temperature interval.
      86             : The reweighting can be performed using the action \ref REWEIGHT_TEMP_PRESS.
      87             : 
      88             : \par Examples
      89             : 
      90             : The following input can be used to run a simulation in the multicanonical ensemble.
      91             : The temperature interval to be explored is 400-600 K.
      92             : The energy is used as collective variable.
      93             : Legendre polynomials are used to construct the bias potential.
      94             : The averaged stochastic gradient descent algorithm is chosen to optimize the VES functional.
      95             : The target distribution is updated every 100 optimization steps (200 ps here) using the last estimation of the free energy.
      96             : 
      97             : \plumedfile
      98             : # Use energy and volume as CVs
      99             : energy: ENERGY
     100             : 
     101             : # Basis functions
     102             : bf1: BF_LEGENDRE ORDER=20 MINIMUM=-25000 MAXIMUM=-23500
     103             : 
     104             : # Target distributions
     105             : TD_MULTICANONICAL ...
     106             :  LABEL=td_multi
     107             :  SIGMA=50.0
     108             :  MIN_TEMP=400
     109             :  MAX_TEMP=600
     110             :  THRESHOLD=1
     111             : ... TD_MULTICANONICAL
     112             : 
     113             : # Expansion
     114             : VES_LINEAR_EXPANSION ...
     115             :  ARG=energy
     116             :  BASIS_FUNCTIONS=bf1
     117             :  TEMP=500.0
     118             :  GRID_BINS=1000
     119             :  TARGET_DISTRIBUTION=td_multi
     120             :  LABEL=b1
     121             : ... VES_LINEAR_EXPANSION
     122             : 
     123             : # Optimization algorithm
     124             : OPT_AVERAGED_SGD ...
     125             :   BIAS=b1
     126             :   STRIDE=500
     127             :   LABEL=o1
     128             :   STEPSIZE=1.0
     129             :   FES_OUTPUT=500
     130             :   BIAS_OUTPUT=500
     131             :   TARGETDIST_OUTPUT=500
     132             :   COEFFS_OUTPUT=10
     133             :   TARGETDIST_STRIDE=100
     134             : ... OPT_AVERAGED_SGD
     135             : 
     136             : \endplumedfile
     137             : 
     138             : The multicanonical target distribution can also be used to explore a temperature interval in which a first order phase transitions is observed.
     139             : 
     140             : */
     141             : //+ENDPLUMEDOC
     142             : 
     143             : class TD_Multicanonical: public TargetDistribution {
     144             : private:
     145             :   double threshold_, min_temp_, max_temp_;
     146             :   std::vector<double> sigma_;
     147             :   unsigned steps_temp_;
     148             : public:
     149             :   static void registerKeywords(Keywords&);
     150             :   explicit TD_Multicanonical(const ActionOptions& ao);
     151             :   void updateGrid();
     152             :   double getValue(const std::vector<double>&) const;
     153           6 :   ~TD_Multicanonical() {}
     154             :   double GaussianSwitchingFunc(const double, const double, const double) const;
     155             : };
     156             : 
     157             : 
     158        7836 : PLUMED_REGISTER_ACTION(TD_Multicanonical,"TD_MULTICANONICAL")
     159             : 
     160             : 
     161           3 : void TD_Multicanonical::registerKeywords(Keywords& keys) {
     162           3 :   TargetDistribution::registerKeywords(keys);
     163          15 :   keys.add("compulsory","THRESHOLD","1","Maximum exploration free energy in kT.");
     164          12 :   keys.add("compulsory","MIN_TEMP","Minimum temperature.");
     165          12 :   keys.add("compulsory","MAX_TEMP","Maximum temperature.");
     166          12 :   keys.add("optional","STEPS_TEMP","Number of temperature steps. Only for the 2D version, i.e. energy and order parameter.");
     167          12 :   keys.add("optional","SIGMA","The standard deviation parameters of the Gaussian kernels used for smoothing the target distribution. One value must be specified for each argument, i.e. one value per CV. A value of 0.0 means that no smooting is performed, this is the default behavior.");
     168           3 : }
     169             : 
     170             : 
     171           2 : TD_Multicanonical::TD_Multicanonical(const ActionOptions& ao):
     172             :   PLUMED_VES_TARGETDISTRIBUTION_INIT(ao),
     173             :   threshold_(1.0),
     174             :   min_temp_(0.0),
     175             :   max_temp_(1000.0),
     176             :   sigma_(0.0),
     177           2 :   steps_temp_(20)
     178             : {
     179           2 :   log.printf("  Multicanonical target distribution");
     180           2 :   log.printf("\n");
     181           2 :   log.printf("  Please read and cite ");
     182           6 :   log << plumed.cite("Piaggi and Parrinello, Phys. Rev. Lett. 122 (5), 050601 (2019)");
     183           2 :   log.printf(" and ");
     184           6 :   log << plumed.cite("Piaggi and Parrinello, arXiv preprint arXiv:1904.05624 (2019)");
     185           2 :   log.printf("\n");
     186           4 :   parse("THRESHOLD",threshold_);
     187           2 :   if(threshold_<=0.0) {
     188           0 :     plumed_merror("TD_MULTICANONICAL target distribution: the value of the threshold should be positive.");
     189             :   }
     190             : 
     191           4 :   parse("MIN_TEMP",min_temp_);
     192           4 :   parse("MAX_TEMP",max_temp_);
     193           4 :   parseVector("SIGMA",sigma_);
     194           4 :   if(sigma_.size()<1 || sigma_.size()>2) plumed_merror(getName()+": SIGMA takes 1 or 2 values as input.");
     195           4 :   parse("STEPS_TEMP",steps_temp_); // Only used in the 2D version
     196           2 :   steps_temp_ += 1;
     197             : 
     198             :   setDynamic();
     199             :   setFesGridNeeded();
     200           2 :   checkRead();
     201           2 : }
     202             : 
     203             : 
     204           0 : double TD_Multicanonical::getValue(const std::vector<double>& argument) const {
     205           0 :   plumed_merror("getValue not implemented for TD_Multicanonical");
     206             :   return 0.0;
     207             : }
     208             : 
     209             : 
     210          14 : void TD_Multicanonical::updateGrid() {
     211          14 :   if (getStep() == 0) {
     212           2 :     if(targetDistGrid().getDimension()>2 && targetDistGrid().getDimension()<1) plumed_merror(getName()+" works only with 1 or 2 arguments, i.e. energy, or energy and CV");
     213           2 :     if(sigma_.size()!=targetDistGrid().getDimension()) plumed_merror(getName()+": mismatch between SIGMA dimension and number of arguments");
     214             :     // Use uniform TD
     215           8 :     std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(getTargetDistGridPntr());
     216             :     double norm = 0.0;
     217        5408 :     for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     218             :       double value = 1.0;
     219        2702 :       norm += integration_weights[l]*value;
     220        2702 :       targetDistGrid().setValue(l,value);
     221             :     }
     222           4 :     targetDistGrid().scaleAllValuesAndDerivatives(1.0/norm);
     223           2 :     logTargetDistGrid().setMinToZero();
     224             :   } else {
     225             :     // Two variants: 1D and 2D
     226             :     // This could be done with one variant but the 1D variant is useful for pedagogical purposes.
     227          12 :     if(targetDistGrid().getDimension()==1) {
     228             :       // 1D variant: Multicanonical without order parameter
     229             :       // In this variant we find the minimum and maximum relevant potential energies.
     230             :       // Using this information we construct a uniform target distribution inbetween these two.
     231          10 :       double beta = getBeta();
     232          20 :       double beta_prime_min = 1./(plumed.getAtoms().getKBoltzmann()*min_temp_);
     233          20 :       double beta_prime_max = 1./(plumed.getAtoms().getKBoltzmann()*max_temp_);
     234          10 :       plumed_massert(getFesGridPntr()!=NULL,"the FES grid has to be linked to use TD_Multicanonical!");
     235             :       // Find minimum of F(U) at temperature min
     236             :       double minval=DBL_MAX;
     237          10 :       Grid::index_t minindex = (targetDistGrid().getSize())/2;
     238          30 :       double minpos = targetDistGrid().getPoint(minindex)[0];
     239        2040 :       for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     240        1010 :         double value = getFesGridPntr()->getValue(l);
     241        3030 :         double argument = targetDistGrid().getPoint(l)[0];
     242        1010 :         value = beta*value + (beta_prime_min-beta)*argument;
     243        1010 :         if(value<minval) {
     244             :           minval=value;
     245             :           minpos=argument;
     246             :           minindex=l;
     247             :         }
     248             :       }
     249             :       // Find minimum energy at low temperature
     250             :       double minimum_low = minpos;
     251          12 :       for(Grid::index_t l=minindex; l>1; l-=1) {
     252          36 :         double argument = targetDistGrid().getPoint(l)[0];
     253          48 :         double argument_next = targetDistGrid().getPoint(l-1)[0];
     254          12 :         double value = getFesGridPntr()->getValue(l);
     255          12 :         double value_next = getFesGridPntr()->getValue(l-1);
     256          12 :         value = beta*value + (beta_prime_min-beta)*argument - minval;
     257          12 :         value_next = beta*value_next + (beta_prime_min-beta)*argument_next - minval;
     258          12 :         if (value<threshold_ && value_next>threshold_) {
     259             :           minimum_low = argument_next;
     260             :           break;
     261             :         }
     262             :       }
     263             :       // Find maximum energy at low temperature
     264             :       double maximum_low = minpos;
     265          22 :       for(Grid::index_t l=minindex; l<(targetDistGrid().getSize()-1); l++) {
     266          36 :         double argument = targetDistGrid().getPoint(l)[0];
     267          48 :         double argument_next = targetDistGrid().getPoint(l+1)[0];
     268          12 :         double value = getFesGridPntr()->getValue(l);
     269          12 :         double value_next = getFesGridPntr()->getValue(l+1);
     270          12 :         value = beta*value + (beta_prime_min-beta)*argument - minval;
     271          12 :         value_next = beta*value_next + (beta_prime_min-beta)*argument_next - minval;
     272          12 :         if (value<threshold_ && value_next>threshold_) {
     273             :           maximum_low = argument_next;
     274             :           break;
     275             :         }
     276             :       }
     277             :       // Find minimum of F(U) at temperature max
     278             :       minval=DBL_MAX;
     279        2040 :       for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     280        1010 :         double value = getFesGridPntr()->getValue(l);
     281        3030 :         double argument = targetDistGrid().getPoint(l)[0];
     282        1010 :         value = beta*value + (beta_prime_max-beta)*argument;
     283        1010 :         if(value<minval) {
     284             :           minval=value;
     285             :           minpos=argument;
     286             :           minindex=l;
     287             :         }
     288             :       }
     289             :       // Find minimum energy at high temperature
     290             :       double minimum_high = minpos;
     291          13 :       for(Grid::index_t l=minindex; l>1; l-=1) {
     292          39 :         double argument = targetDistGrid().getPoint(l)[0];
     293          52 :         double argument_next = targetDistGrid().getPoint(l-1)[0];
     294          13 :         double value = getFesGridPntr()->getValue(l);
     295          13 :         double value_next = getFesGridPntr()->getValue(l-1);
     296          13 :         value = beta*value + (beta_prime_max-beta)*argument - minval;
     297          13 :         value_next = beta*value_next + (beta_prime_max-beta)*argument_next - minval;
     298          13 :         if (value<threshold_ && value_next>threshold_) {
     299             :           minimum_high = argument_next;
     300             :           break;
     301             :         }
     302             :       }
     303             :       // Find maximum energy at high temperature
     304             :       double maximum_high = minpos;
     305          21 :       for(Grid::index_t l=minindex; l<(targetDistGrid().getSize()-1); l++) {
     306          33 :         double argument = targetDistGrid().getPoint(l)[0];
     307          44 :         double argument_next = targetDistGrid().getPoint(l+1)[0];
     308          11 :         double value = getFesGridPntr()->getValue(l);
     309          11 :         double value_next = getFesGridPntr()->getValue(l+1);
     310          11 :         value = beta*value + (beta_prime_max-beta)*argument - minval;
     311          11 :         value_next = beta*value_next + (beta_prime_max-beta)*argument_next - minval;
     312          11 :         if (value<threshold_ && value_next>threshold_) {
     313             :           maximum_high = argument_next;
     314             :           break;
     315             :         }
     316             :       }
     317             :       double minimum = minimum_low;
     318          10 :       if (minimum_high<minimum_low) minimum=minimum_high;
     319             :       double maximum = maximum_low;
     320          10 :       if (maximum_high>maximum_low) maximum=maximum_high;
     321             :       // Construct uniform TD in the interval between minimum and maximum
     322          40 :       std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(getTargetDistGridPntr());
     323             :       double norm = 0.0;
     324        2040 :       for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     325        3030 :         double argument = targetDistGrid().getPoint(l)[0];
     326             :         double value = 1.0;
     327             :         double tmp;
     328        1010 :         if(argument < minimum) {
     329         209 :           tmp = GaussianSwitchingFunc(argument,minimum,sigma_[0]);
     330             :         }
     331         801 :         else if(argument > maximum) {
     332         188 :           tmp = GaussianSwitchingFunc(argument,maximum,sigma_[0]);
     333             :         }
     334             :         else {
     335             :           tmp = 1.0;
     336             :         }
     337             :         value *= tmp;
     338        1010 :         norm += integration_weights[l]*value;
     339        1010 :         targetDistGrid().setValue(l,value);
     340             :       }
     341          20 :       targetDistGrid().scaleAllValuesAndDerivatives(1.0/norm);
     342          10 :       logTargetDistGrid().setMinToZero();
     343           2 :     } else if(targetDistGrid().getDimension()==2) {
     344             :       // 2D variant: Multicanonical with order parameter
     345             :       // In this variant we find for each temperature the relevant region of potential energy and order parameter.
     346             :       // The target distribution will be the union of the relevant regions at all temperatures in the temperature interval.
     347           2 :       double beta = getBeta();
     348           4 :       double beta_prime_min = 1./(plumed.getAtoms().getKBoltzmann()*min_temp_);
     349           4 :       double beta_prime_max = 1./(plumed.getAtoms().getKBoltzmann()*max_temp_);
     350           2 :       plumed_massert(getFesGridPntr()!=NULL,"the FES grid has to be linked to use TD_MulticanonicalWithCV!");
     351             :       // Set all to zero
     352       10406 :       for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     353             :         double value = 0.0;
     354        5202 :         targetDistGrid().setValue(l,value);
     355             :       }
     356             :       // Loop over temperatures
     357          42 :       for(unsigned i=0; i<steps_temp_; i++) {
     358          42 :         double beta_prime=beta_prime_min + (beta_prime_max-beta_prime_min)*i/(steps_temp_-1);
     359             :         // Find minimum for this temperature
     360             :         double minval=DBL_MAX;
     361      218568 :         for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     362      327726 :           double energy = targetDistGrid().getPoint(l)[0];
     363      109242 :           double value = getFesGridPntr()->getValue(l);
     364      109242 :           value = beta*value + (beta_prime-beta)*energy;
     365      109242 :           if(value<minval) {
     366             :             minval=value;
     367             :           }
     368             :         }
     369             :         // Now check which energies and volumes are below X kt
     370      218526 :         for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     371      327726 :           double energy = targetDistGrid().getPoint(l)[0];
     372      109242 :           double value = getFesGridPntr()->getValue(l);
     373      109242 :           value = beta*value + (beta_prime-beta)*energy - minval;
     374      109242 :           if (value<threshold_) {
     375             :             double value = 1.0;
     376        7151 :             targetDistGrid().setValue(l,value);
     377             :           }
     378             :         }
     379             :       }
     380           2 :       std::vector<unsigned> nbin=targetDistGrid().getNbin();
     381           2 :       std::vector<double> dx=targetDistGrid().getDx();
     382             :       // Smoothening
     383         206 :       for(unsigned i=0; i<nbin[0]; i++) {
     384       10506 :         for(unsigned j=0; j<nbin[1]; j++) {
     385        5202 :           std::vector<unsigned> indices(2);
     386        5202 :           indices[0]=i;
     387        5202 :           indices[1]=j;
     388        5202 :           Grid::index_t index = targetDistGrid().getIndex(indices);
     389       15606 :           double energy = targetDistGrid().getPoint(index)[0];
     390       15606 :           double volume = targetDistGrid().getPoint(index)[1];
     391        5202 :           double value = targetDistGrid().getValue(index);
     392        5202 :           if (value>(1-1.e-5)) { // Apply only if this grid point was 1.
     393             :             // Apply gaussians around
     394         777 :             std::vector<int> minBin(2), maxBin(2), deltaBin(2); // These cannot be unsigned
     395             :             // Only consider contributions less than n*sigma bins apart from the actual distance
     396        1554 :             deltaBin[0]=std::floor(5*sigma_[0]/dx[0]);;
     397        1554 :             deltaBin[1]=std::floor(5*sigma_[1]/dx[1]);;
     398             :             // For energy
     399         777 :             minBin[0]=i - deltaBin[0];
     400         777 :             if (minBin[0] < 0) minBin[0]=0;
     401        1554 :             if (minBin[0] > (nbin[0]-1)) minBin[0]=nbin[0]-1;
     402         777 :             maxBin[0]=i +  deltaBin[0];
     403        1554 :             if (maxBin[0] > (nbin[0]-1)) maxBin[0]=nbin[0]-1;
     404             :             // For volume
     405         777 :             minBin[1]=j - deltaBin[1];
     406         777 :             if (minBin[1] < 0) minBin[1]=0;
     407        1554 :             if (minBin[1] > (nbin[1]-1)) minBin[1]=nbin[1]-1;
     408         777 :             maxBin[1]=j +  deltaBin[1];
     409        1554 :             if (maxBin[1] > (nbin[1]-1)) maxBin[1]=nbin[1]-1;
     410       58472 :             for(unsigned l=minBin[0]; l<maxBin[0]+1; l++) {
     411      966356 :               for(unsigned m=minBin[1]; m<maxBin[1]+1; m++) {
     412      454719 :                 std::vector<unsigned> indices_prime(2);
     413      454719 :                 indices_prime[0]=l;
     414      454719 :                 indices_prime[1]=m;
     415      454719 :                 Grid::index_t index_prime = targetDistGrid().getIndex(indices_prime);
     416     1364157 :                 double energy_prime = targetDistGrid().getPoint(index_prime)[0];
     417     1364157 :                 double volume_prime = targetDistGrid().getPoint(index_prime)[1];
     418      454719 :                 double value_prime = targetDistGrid().getValue(index_prime);
     419             :                 // Apply gaussian
     420     1364157 :                 double gaussian_value = GaussianSwitchingFunc(energy_prime,energy,sigma_[0])*GaussianSwitchingFunc(volume_prime,volume,sigma_[1]);
     421      454719 :                 if (value_prime<gaussian_value) {
     422       39745 :                   targetDistGrid().setValue(index_prime,gaussian_value);
     423             :                 }
     424             :               }
     425             :             }
     426             :           }
     427             :         }
     428             :       }
     429             :       // Normalize
     430           8 :       std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(getTargetDistGridPntr());
     431             :       double norm = 0.0;
     432       10408 :       for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     433        5202 :         double value = targetDistGrid().getValue(l);
     434        5202 :         norm += integration_weights[l]*value;
     435             :       }
     436           4 :       targetDistGrid().scaleAllValuesAndDerivatives(1.0/norm);
     437           2 :       logTargetDistGrid().setMinToZero();
     438           0 :     } else plumed_merror(getName()+": Number of arguments for this target distribution must be 1 or 2");
     439             :   }
     440          14 : }
     441             : 
     442             : inline
     443             : double TD_Multicanonical::GaussianSwitchingFunc(const double argument, const double center, const double sigma) const {
     444      909835 :   if(sigma>0.0) {
     445      909835 :     double arg=(argument-center)/sigma;
     446      909835 :     return exp(-0.5*arg*arg);
     447             :   }
     448             :   else {
     449             :     return 0.0;
     450             :   }
     451             : }
     452             : 
     453             : 
     454             : }
     455        5874 : }

Generated by: LCOV version 1.14