LCOV - code coverage report
Current view: top level - ves - VesBias.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 334 438 76.3 %
Date: 2026-03-30 13:16:06 Functions: 27 45 60.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-2021 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 "VesBias.h"
      24             : #include "BasisFunctions.h"
      25             : #include "CoeffsVector.h"
      26             : #include "CoeffsMatrix.h"
      27             : #include "Optimizer.h"
      28             : #include "FermiSwitchingFunction.h"
      29             : #include "VesTools.h"
      30             : #include "TargetDistribution.h"
      31             : 
      32             : #include "tools/Communicator.h"
      33             : #include "core/ActionSet.h"
      34             : #include "core/PlumedMain.h"
      35             : #include "core/Atoms.h"
      36             : #include "tools/File.h"
      37             : 
      38             : 
      39             : namespace PLMD {
      40             : namespace ves {
      41             : 
      42          90 : VesBias::VesBias(const ActionOptions&ao):
      43             :   Action(ao),
      44             :   Bias(ao),
      45          90 :   ncoeffssets_(0),
      46          90 :   sampled_averages(0),
      47          90 :   sampled_cross_averages(0),
      48          90 :   use_multiple_coeffssets_(false),
      49          90 :   coeffs_fnames(0),
      50          90 :   ncoeffs_total_(0),
      51          90 :   optimizer_pntr_(NULL),
      52          90 :   optimize_coeffs_(false),
      53          90 :   compute_hessian_(false),
      54          90 :   diagonal_hessian_(true),
      55          90 :   aver_counters(0),
      56          90 :   kbt_(0.0),
      57          90 :   targetdist_pntrs_(0),
      58          90 :   dynamic_targetdist_(false),
      59          90 :   grid_bins_(0),
      60          90 :   grid_min_(0),
      61          90 :   grid_max_(0),
      62          90 :   bias_filename_(""),
      63          90 :   fes_filename_(""),
      64          90 :   targetdist_filename_(""),
      65          90 :   coeffs_id_prefix_("c-"),
      66          90 :   bias_file_fmt_("14.9f"),
      67          90 :   fes_file_fmt_("14.9f"),
      68          90 :   targetdist_file_fmt_("14.9f"),
      69          90 :   targetdist_restart_file_fmt_("30.16e"),
      70          90 :   filenames_have_iteration_number_(false),
      71          90 :   bias_fileoutput_active_(false),
      72          90 :   fes_fileoutput_active_(false),
      73          90 :   fesproj_fileoutput_active_(false),
      74          90 :   dynamic_targetdist_fileoutput_active_(false),
      75          90 :   static_targetdist_fileoutput_active_(true),
      76          90 :   bias_cutoff_active_(false),
      77          90 :   bias_cutoff_value_(0.0),
      78          90 :   bias_current_max_value(0.0),
      79          90 :   calc_reweightfactor_(false),
      80         270 :   optimization_threshold_(0.0) {
      81          90 :   log.printf("  VES bias, please read and cite ");
      82         180 :   log << plumed.cite("Valsson and Parrinello, Phys. Rev. Lett. 113, 090601 (2014)");
      83          90 :   log.printf("\n");
      84             : 
      85          90 :   double temp=0.0;
      86          90 :   parse("TEMP",temp);
      87          90 :   if(temp>0.0) {
      88          90 :     kbt_=plumed.getAtoms().getKBoltzmann()*temp;
      89             :   } else {
      90           0 :     kbt_=plumed.getAtoms().getKbT();
      91             :   }
      92          90 :   if(kbt_>0.0) {
      93          90 :     log.printf("  KbT: %f\n",kbt_);
      94             :   }
      95             :   // NOTE: the check for that the temperature is given is done when linking the optimizer later on.
      96             : 
      97         180 :   if(keywords.exists("COEFFS")) {
      98         180 :     parseVector("COEFFS",coeffs_fnames);
      99             :   }
     100             : 
     101         180 :   if(keywords.exists("GRID_BINS")) {
     102         180 :     parseMultipleValues<unsigned int>("GRID_BINS",grid_bins_,getNumberOfArguments(),100);
     103             :   }
     104             : 
     105          90 :   if(keywords.exists("GRID_MIN") && keywords.exists("GRID_MAX")) {
     106           0 :     parseMultipleValues("GRID_MIN",grid_min_,getNumberOfArguments());
     107           0 :     parseMultipleValues("GRID_MAX",grid_max_,getNumberOfArguments());
     108             :   }
     109             : 
     110             :   std::vector<std::string> targetdist_labels;
     111         180 :   if(keywords.exists("TARGET_DISTRIBUTION")) {
     112         180 :     parseVector("TARGET_DISTRIBUTION",targetdist_labels);
     113          90 :     if(targetdist_labels.size()>1) {
     114           0 :       plumed_merror(getName()+" with label "+getLabel()+": multiple target distribution labels not allowed");
     115             :     }
     116           0 :   } else if(keywords.exists("TARGET_DISTRIBUTIONS")) {
     117           0 :     parseVector("TARGET_DISTRIBUTIONS",targetdist_labels);
     118             :   }
     119             : 
     120          90 :   std::string error_msg = "";
     121         180 :   targetdist_pntrs_ = VesTools::getPointersFromLabels<TargetDistribution*>(targetdist_labels,plumed.getActionSet(),error_msg);
     122          90 :   if(error_msg.size()>0) {
     123           0 :     plumed_merror("Problem with target distribution in "+getName()+": "+error_msg);
     124             :   }
     125             : 
     126         135 :   for(unsigned int i=0; i<targetdist_pntrs_.size(); i++) {
     127          45 :     targetdist_pntrs_[i]->linkVesBias(this);
     128             :   }
     129             : 
     130             : 
     131          90 :   if(getNumberOfArguments()>2) {
     132             :     disableStaticTargetDistFileOutput();
     133             :   }
     134             : 
     135             : 
     136         180 :   if(keywords.exists("BIAS_FILE")) {
     137         180 :     parse("BIAS_FILE",bias_filename_);
     138          90 :     if(bias_filename_.size()==0) {
     139         180 :       bias_filename_ = "bias." + getLabel() + ".data";
     140             :     }
     141             :   }
     142         180 :   if(keywords.exists("FES_FILE")) {
     143         180 :     parse("FES_FILE",fes_filename_);
     144          90 :     if(fes_filename_.size()==0) {
     145         180 :       fes_filename_ = "fes." + getLabel() + ".data";
     146             :     }
     147             :   }
     148         180 :   if(keywords.exists("TARGETDIST_FILE")) {
     149         180 :     parse("TARGETDIST_FILE",targetdist_filename_);
     150          90 :     if(targetdist_filename_.size()==0) {
     151         180 :       targetdist_filename_ = "targetdist." + getLabel() + ".data";
     152             :     }
     153             :   }
     154             :   //
     155         180 :   if(keywords.exists("BIAS_FILE_FMT")) {
     156           0 :     parse("BIAS_FILE_FMT",bias_file_fmt_);
     157             :   }
     158         180 :   if(keywords.exists("FES_FILE_FMT")) {
     159           0 :     parse("FES_FILE_FMT",fes_file_fmt_);
     160             :   }
     161         180 :   if(keywords.exists("TARGETDIST_FILE_FMT")) {
     162           0 :     parse("TARGETDIST_FILE_FMT",targetdist_file_fmt_);
     163             :   }
     164         180 :   if(keywords.exists("TARGETDIST_RESTART_FILE_FMT")) {
     165           0 :     parse("TARGETDIST_RESTART_FILE_FMT",targetdist_restart_file_fmt_);
     166             :   }
     167             : 
     168             :   //
     169         180 :   if(keywords.exists("BIAS_CUTOFF")) {
     170          90 :     double cutoff_value=0.0;
     171          90 :     parse("BIAS_CUTOFF",cutoff_value);
     172          90 :     if(cutoff_value<0.0) {
     173           0 :       plumed_merror("the value given in BIAS_CUTOFF doesn't make sense, it should be larger than 0.0");
     174             :     }
     175             :     //
     176          90 :     if(cutoff_value>0.0) {
     177           3 :       double fermi_lambda=10.0;
     178           3 :       parse("BIAS_CUTOFF_FERMI_LAMBDA",fermi_lambda);
     179           3 :       setupBiasCutoff(cutoff_value,fermi_lambda);
     180           3 :       log.printf("  Employing a bias cutoff of %f (the lambda value for the Fermi switching function is %f), see and cite ",cutoff_value,fermi_lambda);
     181           6 :       log << plumed.cite("McCarty, Valsson, Tiwary, and Parrinello, Phys. Rev. Lett. 115, 070601 (2015)");
     182           3 :       log.printf("\n");
     183             :     }
     184             :   }
     185             : 
     186             : 
     187         180 :   if(keywords.exists("PROJ_ARG")) {
     188             :     std::vector<std::string> proj_arg;
     189          90 :     for(int i=1;; i++) {
     190         212 :       if(!parseNumberedVector("PROJ_ARG",i,proj_arg)) {
     191             :         break;
     192             :       }
     193             :       // checks
     194          16 :       if(proj_arg.size() > (getNumberOfArguments()-1) ) {
     195           0 :         plumed_merror("PROJ_ARG must be a subset of ARG");
     196             :       }
     197             :       //
     198          32 :       for(unsigned int k=0; k<proj_arg.size(); k++) {
     199             :         bool found = false;
     200          24 :         for(unsigned int l=0; l<getNumberOfArguments(); l++) {
     201          24 :           if(proj_arg[k]==getPntrToArgument(l)->getName()) {
     202             :             found = true;
     203             :             break;
     204             :           }
     205             :         }
     206          16 :         if(!found) {
     207             :           std::string s1;
     208           0 :           Tools::convert(i,s1);
     209           0 :           std::string error = "PROJ_ARG" + s1 + ": label " + proj_arg[k] + " is not among the arguments given in ARG";
     210           0 :           plumed_merror(error);
     211             :         }
     212             :       }
     213             :       //
     214          16 :       projection_args_.push_back(proj_arg);
     215          16 :     }
     216          90 :   }
     217             : 
     218         180 :   if(keywords.exists("CALC_REWEIGHT_FACTOR")) {
     219           0 :     parseFlag("CALC_REWEIGHT_FACTOR",calc_reweightfactor_);
     220           0 :     if(calc_reweightfactor_) {
     221           0 :       addComponent("rct");
     222           0 :       componentIsNotPeriodic("rct");
     223           0 :       updateReweightFactor();
     224             :     }
     225             :   }
     226             : 
     227         180 :   if(keywords.exists("OPTIMIZATION_THRESHOLD")) {
     228          90 :     parse("OPTIMIZATION_THRESHOLD",optimization_threshold_);
     229          90 :     if(optimization_threshold_ < 0.0) {
     230           0 :       plumed_merror("OPTIMIZATION_THRESHOLD should be a postive value");
     231             :     }
     232          90 :     if(optimization_threshold_!=0.0) {
     233           1 :       log.printf("  Employing a threshold value of %e for optimization of localized basis functions.\n",optimization_threshold_);
     234             :     }
     235             :   }
     236             : 
     237          90 : }
     238             : 
     239             : 
     240          90 : VesBias::~VesBias() {
     241         180 : }
     242             : 
     243             : 
     244          94 : void VesBias::registerKeywords( Keywords& keys ) {
     245          94 :   Bias::registerKeywords(keys);
     246         188 :   keys.add("optional","TEMP","the system temperature - this is needed if the MD code does not pass the temperature to PLUMED.");
     247             :   //
     248         188 :   keys.reserve("optional","COEFFS","read in the coefficients from files.");
     249             :   //
     250         188 :   keys.reserve("optional","TARGET_DISTRIBUTION","the label of the target distribution to be used.");
     251         188 :   keys.reserve("optional","TARGET_DISTRIBUTIONS","the label of the target distribution to be used. Here you are allows to use multiple labels.");
     252             :   //
     253         188 :   keys.reserve("optional","GRID_BINS","the number of bins used for the grid. The default value is 100 bins per dimension.");
     254         188 :   keys.reserve("optional","GRID_MIN","the lower bounds used for the grid.");
     255         188 :   keys.reserve("optional","GRID_MAX","the upper bounds used for the grid.");
     256             :   //
     257         188 :   keys.add("optional","BIAS_FILE","filename of the file on which the bias should be written out. By default it is bias.LABEL.data. Note that suffixes indicating the iteration number (iter-#) are added to the filename when optimizing coefficients.");
     258         188 :   keys.add("optional","FES_FILE","filename of the file on which the FES should be written out. By default it is fes.LABEL.data. Note that suffixes indicating the iteration number (iter-#) are added to the filename when optimizing coefficients.");
     259         188 :   keys.add("optional","TARGETDIST_FILE","filename of the file on which the target distribution should be written out. By default it is targetdist.LABEL.data. Note that suffixes indicating the iteration number (iter-#) are added to the filename when optimizing coefficients and the target distribution is dynamic.");
     260             :   //
     261             :   // keys.add("optional","BIAS_FILE_FMT","the format of the bias files, by default it is %14.9f.");
     262             :   // keys.add("optional","FES_FILE_FMT","the format of the FES files, by default it is %14.9f.");
     263             :   // keys.add("optional","TARGETDIST_FILE_FMT","the format of the target distribution files, by default it is %14.9f.");
     264             :   // keys.add("hidden","TARGETDIST_RESTART_FILE_FMT","the format of the target distribution files that are used for restarting, by default it is %30.16e.");
     265             :   //
     266         188 :   keys.reserve("optional","BIAS_CUTOFF","cutoff the bias such that it only fills the free energy surface up to certain level F_cutoff, here you should give the value of the F_cutoff.");
     267         188 :   keys.reserve("optional","BIAS_CUTOFF_FERMI_LAMBDA","the lambda value used in the Fermi switching function for the bias cutoff (BIAS_CUTOFF), the default value is 10.0.");
     268             :   //
     269         188 :   keys.reserve("numbered","PROJ_ARG","arguments for doing projections of the FES or the target distribution.");
     270             :   //
     271         188 :   keys.reserveFlag("CALC_REWEIGHT_FACTOR",false,"enable the calculation of the reweight factor c(t). You should also give a stride for updating the reweight factor in the optimizer by using the REWEIGHT_FACTOR_STRIDE keyword if the coefficients are updated.");
     272         188 :   keys.add("optional","OPTIMIZATION_THRESHOLD","Threshold value to turn off optimization of localized basis functions.");
     273             : 
     274          94 : }
     275             : 
     276             : 
     277          94 : void VesBias::useInitialCoeffsKeywords(Keywords& keys) {
     278          94 :   keys.use("COEFFS");
     279          94 : }
     280             : 
     281             : 
     282          94 : void VesBias::useTargetDistributionKeywords(Keywords& keys) {
     283         188 :   plumed_massert(!keys.exists("TARGET_DISTRIBUTIONS"),"you cannot use both useTargetDistributionKeywords and useMultipleTargetDistributionKeywords");
     284          94 :   keys.use("TARGET_DISTRIBUTION");
     285          94 : }
     286             : 
     287             : 
     288           0 : void VesBias::useMultipleTargetDistributionKeywords(Keywords& keys) {
     289           0 :   plumed_massert(!keys.exists("TARGET_DISTRIBUTION"),"you cannot use both useTargetDistributionKeywords and useMultipleTargetDistributionKeywords");
     290           0 :   keys.use("TARGET_DISTRIBUTIONS");
     291           0 : }
     292             : 
     293             : 
     294          94 : void VesBias::useGridBinKeywords(Keywords& keys) {
     295          94 :   keys.use("GRID_BINS");
     296          94 : }
     297             : 
     298             : 
     299           0 : void VesBias::useGridLimitsKeywords(Keywords& keys) {
     300           0 :   keys.use("GRID_MIN");
     301           0 :   keys.use("GRID_MAX");
     302           0 : }
     303             : 
     304             : 
     305          94 : void VesBias::useBiasCutoffKeywords(Keywords& keys) {
     306          94 :   keys.use("BIAS_CUTOFF");
     307          94 :   keys.use("BIAS_CUTOFF_FERMI_LAMBDA");
     308          94 : }
     309             : 
     310             : 
     311          94 : void VesBias::useProjectionArgKeywords(Keywords& keys) {
     312          94 :   keys.use("PROJ_ARG");
     313          94 : }
     314             : 
     315             : 
     316           0 : void VesBias::useReweightFactorKeywords(Keywords& keys) {
     317           0 :   keys.use("CALC_REWEIGHT_FACTOR");
     318           0 :   keys.addOutputComponent("rct","CALC_REWEIGHT_FACTOR","the reweight factor c(t).");
     319           0 : }
     320             : 
     321             : 
     322           0 : void VesBias::addCoeffsSet(const std::vector<std::string>& dimension_labels,const std::vector<unsigned int>& indices_shape) {
     323           0 :   auto coeffs_pntr_tmp = Tools::make_unique<CoeffsVector>("coeffs",dimension_labels,indices_shape,comm,true);
     324           0 :   initializeCoeffs(std::move(coeffs_pntr_tmp));
     325           0 : }
     326             : 
     327             : 
     328          90 : void VesBias::addCoeffsSet(std::vector<Value*>& args,std::vector<BasisFunctions*>& basisf) {
     329          90 :   auto coeffs_pntr_tmp = Tools::make_unique<CoeffsVector>("coeffs",args,basisf,comm,true);
     330          90 :   initializeCoeffs(std::move(coeffs_pntr_tmp));
     331          90 : }
     332             : 
     333             : 
     334           0 : void VesBias::addCoeffsSet(std::unique_ptr<CoeffsVector> coeffs_pntr_in) {
     335           0 :   initializeCoeffs(std::move(coeffs_pntr_in));
     336           0 : }
     337             : 
     338             : 
     339          90 : void VesBias::initializeCoeffs(std::unique_ptr<CoeffsVector> coeffs_pntr_in) {
     340             :   //
     341          90 :   coeffs_pntr_in->linkVesBias(this);
     342             :   //
     343             :   std::string label;
     344          90 :   if(!use_multiple_coeffssets_ && ncoeffssets_==1) {
     345           0 :     plumed_merror("you are not allowed to use multiple coefficient sets");
     346             :   }
     347             :   //
     348         180 :   label = getCoeffsSetLabelString("coeffs",ncoeffssets_);
     349          90 :   coeffs_pntr_in->setLabels(label);
     350             : 
     351          90 :   coeffs_pntrs_.emplace_back(std::move(coeffs_pntr_in));
     352          90 :   auto aver_ps_tmp = Tools::make_unique<CoeffsVector>(*coeffs_pntrs_.back());
     353         180 :   label = getCoeffsSetLabelString("targetdist_averages",ncoeffssets_);
     354          90 :   aver_ps_tmp->setLabels(label);
     355          90 :   aver_ps_tmp->setValues(0.0);
     356          90 :   targetdist_averages_pntrs_.emplace_back(std::move(aver_ps_tmp));
     357             :   //
     358          90 :   auto gradient_tmp = Tools::make_unique<CoeffsVector>(*coeffs_pntrs_.back());
     359         180 :   label = getCoeffsSetLabelString("gradient",ncoeffssets_);
     360          90 :   gradient_tmp->setLabels(label);
     361          90 :   gradient_pntrs_.emplace_back(std::move(gradient_tmp));
     362             :   //
     363         180 :   label = getCoeffsSetLabelString("hessian",ncoeffssets_);
     364          90 :   auto hessian_tmp = Tools::make_unique<CoeffsMatrix>(label,coeffs_pntrs_.back().get(),comm,diagonal_hessian_);
     365             : 
     366          90 :   hessian_pntrs_.emplace_back(std::move(hessian_tmp));
     367             :   //
     368             :   std::vector<double> aver_sampled_tmp;
     369          90 :   aver_sampled_tmp.assign(coeffs_pntrs_.back()->numberOfCoeffs(),0.0);
     370          90 :   sampled_averages.push_back(aver_sampled_tmp);
     371             :   //
     372             :   std::vector<double> cross_aver_sampled_tmp;
     373          90 :   cross_aver_sampled_tmp.assign(hessian_pntrs_.back()->getSize(),0.0);
     374          90 :   sampled_cross_averages.push_back(cross_aver_sampled_tmp);
     375             :   //
     376          90 :   aver_counters.push_back(0);
     377             :   //
     378          90 :   ncoeffssets_++;
     379         180 : }
     380             : 
     381             : 
     382          90 : bool VesBias::readCoeffsFromFiles() {
     383          90 :   plumed_assert(ncoeffssets_>0);
     384         180 :   plumed_massert(keywords.exists("COEFFS"),"you are not allowed to use this function as the COEFFS keyword is not enabled");
     385             :   bool read_coeffs = false;
     386          90 :   if(coeffs_fnames.size()>0) {
     387           4 :     plumed_massert(coeffs_fnames.size()==ncoeffssets_,"COEFFS keyword is of the wrong size");
     388           4 :     if(ncoeffssets_==1) {
     389           4 :       log.printf("  Read in coefficients from file ");
     390             :     } else {
     391           0 :       log.printf("  Read in coefficients from files:\n");
     392             :     }
     393           8 :     for(unsigned int i=0; i<ncoeffssets_; i++) {
     394           4 :       IFile ifile;
     395           4 :       ifile.link(*this);
     396           4 :       ifile.open(coeffs_fnames[i]);
     397           8 :       if(!ifile.FieldExist(coeffs_pntrs_[i]->getDataLabel())) {
     398           0 :         std::string error_msg = "Problem with reading coefficients from file " + ifile.getPath() + ": no field with name " + coeffs_pntrs_[i]->getDataLabel() + "\n";
     399           0 :         plumed_merror(error_msg);
     400             :       }
     401           4 :       size_t ncoeffs_read = coeffs_pntrs_[i]->readFromFile(ifile,false,false);
     402           4 :       coeffs_pntrs_[i]->setIterationCounterAndTime(0,getTime());
     403           4 :       if(ncoeffssets_==1) {
     404           8 :         log.printf("%s (read %zu of %zu values)\n", ifile.getPath().c_str(),ncoeffs_read,coeffs_pntrs_[i]->numberOfCoeffs());
     405             :       } else {
     406           0 :         log.printf("   coefficient %u: %s (read %zu of %zu values)\n",i,ifile.getPath().c_str(),ncoeffs_read,coeffs_pntrs_[i]->numberOfCoeffs());
     407             :       }
     408           4 :       ifile.close();
     409           4 :     }
     410             :     read_coeffs = true;
     411             :   }
     412          90 :   return read_coeffs;
     413             : }
     414             : 
     415             : 
     416       22810 : void VesBias::updateGradientAndHessian(const bool use_mwalkers_mpi) {
     417       45620 :   for(unsigned int k=0; k<ncoeffssets_; k++) {
     418             :     //
     419       22810 :     comm.Sum(sampled_averages[k]);
     420       22810 :     comm.Sum(sampled_cross_averages[k]);
     421       22810 :     if(use_mwalkers_mpi) {
     422             :       double walker_weight=1.0;
     423         120 :       if(aver_counters[k]==0) {
     424             :         walker_weight=0.0;
     425             :       }
     426         120 :       multiSimSumAverages(k,walker_weight);
     427             :     }
     428             :     // NOTE: this assumes that all walkers have the same TargetDist, might change later on!!
     429       22810 :     Gradient(k).setValues( TargetDistAverages(k) - sampled_averages[k] );
     430       22810 :     Hessian(k) = computeCovarianceFromAverages(k);
     431       22810 :     Hessian(k) *= getBeta();
     432             : 
     433       22810 :     if(optimization_threshold_ != 0.0) {
     434         390 :       for(size_t c_id=0; c_id < sampled_averages[k].size(); ++c_id) {
     435         380 :         if(fabs(sampled_averages[k][c_id]) < optimization_threshold_) {
     436         219 :           Gradient(k).setValue(c_id, 0.0);
     437         219 :           Hessian(k).setValue(c_id, c_id, 0.0);
     438             :         }
     439             :       }
     440             :     }
     441             :     //
     442             :     Gradient(k).activate();
     443             :     Hessian(k).activate();
     444             :     //
     445             :     // Check the total number of samples (from all walkers) and deactivate the Gradient and Hessian if it
     446             :     // is zero
     447       22810 :     unsigned int total_samples = aver_counters[k];
     448       22810 :     if(use_mwalkers_mpi) {
     449         120 :       if(comm.Get_rank()==0) {
     450         120 :         multi_sim_comm.Sum(total_samples);
     451             :       }
     452         120 :       comm.Bcast(total_samples,0);
     453             :     }
     454       22810 :     if(total_samples==0) {
     455             :       Gradient(k).deactivate();
     456          95 :       Gradient(k).clear();
     457             :       Hessian(k).deactivate();
     458          95 :       Hessian(k).clear();
     459             :     }
     460             :     //
     461             :     std::fill(sampled_averages[k].begin(), sampled_averages[k].end(), 0.0);
     462             :     std::fill(sampled_cross_averages[k].begin(), sampled_cross_averages[k].end(), 0.0);
     463       22810 :     aver_counters[k]=0;
     464             :   }
     465       22810 : }
     466             : 
     467             : 
     468         120 : void VesBias::multiSimSumAverages(const unsigned int c_id, const double walker_weight) {
     469         120 :   plumed_massert(walker_weight>=0.0,"the weight of the walker cannot be negative!");
     470         120 :   if(walker_weight!=1.0) {
     471        7860 :     for(size_t i=0; i<sampled_averages[c_id].size(); i++) {
     472        7800 :       sampled_averages[c_id][i] *= walker_weight;
     473             :     }
     474        7860 :     for(size_t i=0; i<sampled_cross_averages[c_id].size(); i++) {
     475        7800 :       sampled_cross_averages[c_id][i] *= walker_weight;
     476             :     }
     477             :   }
     478             :   //
     479         120 :   if(comm.Get_rank()==0) {
     480         120 :     multi_sim_comm.Sum(sampled_averages[c_id]);
     481         120 :     multi_sim_comm.Sum(sampled_cross_averages[c_id]);
     482         120 :     double norm_weights = walker_weight;
     483         120 :     multi_sim_comm.Sum(norm_weights);
     484         120 :     if(norm_weights>0.0) {
     485          60 :       norm_weights=1.0/norm_weights;
     486             :     }
     487        8580 :     for(size_t i=0; i<sampled_averages[c_id].size(); i++) {
     488        8460 :       sampled_averages[c_id][i] *= norm_weights;
     489             :     }
     490        8580 :     for(size_t i=0; i<sampled_cross_averages[c_id].size(); i++) {
     491        8460 :       sampled_cross_averages[c_id][i] *= norm_weights;
     492             :     }
     493             :   }
     494         120 :   comm.Bcast(sampled_averages[c_id],0);
     495         120 :   comm.Bcast(sampled_cross_averages[c_id],0);
     496         120 : }
     497             : 
     498             : 
     499       23556 : void VesBias::addToSampledAverages(const std::vector<double>& values, const unsigned int c_id) {
     500             :   /*
     501             :   use the following online equation to calculate the average and covariance
     502             :   (see https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Covariance)
     503             :       xm[n+1] = xm[n] + (x[n+1]-xm[n])/(n+1)
     504             :   */
     505       23556 :   double counter_dbl = static_cast<double>(aver_counters[c_id]);
     506             :   size_t ncoeffs = numberOfCoeffs(c_id);
     507       23556 :   std::vector<double> deltas(ncoeffs,0.0);
     508       23556 :   size_t stride = comm.Get_size();
     509       23556 :   size_t rank = comm.Get_rank();
     510             :   // update average and diagonal part of Hessian
     511     1830228 :   for(size_t i=rank; i<ncoeffs; i+=stride) {
     512             :     size_t midx = getHessianIndex(i,i,c_id);
     513     1806672 :     deltas[i] = (values[i]-sampled_averages[c_id][i])/(counter_dbl+1); // (x[n+1]-xm[n])/(n+1)
     514     1806672 :     sampled_averages[c_id][i] += deltas[i];
     515     1806672 :     sampled_cross_averages[c_id][midx] += (values[i]*values[i]-sampled_cross_averages[c_id][midx])/(counter_dbl+1);
     516             :   }
     517       23556 :   comm.Sum(deltas);
     518             :   // update off-diagonal part of the Hessian
     519       23556 :   if(!diagonal_hessian_) {
     520           0 :     for(size_t i=rank; i<ncoeffs; i+=stride) {
     521           0 :       for(size_t j=(i+1); j<ncoeffs; j++) {
     522             :         size_t midx = getHessianIndex(i,j,c_id);
     523           0 :         sampled_cross_averages[c_id][midx] += (values[i]*values[j]-sampled_cross_averages[c_id][midx])/(counter_dbl+1);
     524             :       }
     525             :     }
     526             :   }
     527             :   // NOTE: the MPI sum for sampled_averages and sampled_cross_averages is done later
     528       23556 :   aver_counters[c_id] += 1;
     529       23556 : }
     530             : 
     531             : 
     532           0 : void VesBias::setTargetDistAverages(const std::vector<double>& coeffderivs_aver_ps, const unsigned int coeffs_id) {
     533           0 :   TargetDistAverages(coeffs_id) = coeffderivs_aver_ps;
     534           0 :   TargetDistAverages(coeffs_id).setIterationCounterAndTime(this->getIterationCounter(),this->getTime());
     535           0 : }
     536             : 
     537             : 
     538         453 : void VesBias::setTargetDistAverages(const CoeffsVector& coeffderivs_aver_ps, const unsigned int coeffs_id) {
     539         453 :   TargetDistAverages(coeffs_id).setValues( coeffderivs_aver_ps );
     540         453 :   TargetDistAverages(coeffs_id).setIterationCounterAndTime(this->getIterationCounter(),this->getTime());
     541         453 : }
     542             : 
     543             : 
     544           0 : void VesBias::setTargetDistAveragesToZero(const unsigned int coeffs_id) {
     545           0 :   TargetDistAverages(coeffs_id).setAllValuesToZero();
     546           0 :   TargetDistAverages(coeffs_id).setIterationCounterAndTime(this->getIterationCounter(),this->getTime());
     547           0 : }
     548             : 
     549             : 
     550         175 : void VesBias::checkThatTemperatureIsGiven() {
     551         175 :   if(kbt_==0.0) {
     552           0 :     std::string err_msg = "VES bias " + getLabel() + " of type " + getName() + ": the temperature is needed so you need to give it using the TEMP keyword as the MD engine does not pass it to PLUMED.";
     553           0 :     plumed_merror(err_msg);
     554             :   }
     555         175 : }
     556             : 
     557             : 
     558        1005 : unsigned int VesBias::getIterationCounter() const {
     559             :   unsigned int iteration = 0;
     560        1005 :   if(optimizeCoeffs()) {
     561             :     iteration = getOptimizerPntr()->getIterationCounter();
     562             :   } else {
     563         113 :     iteration = getCoeffsPntrs()[0]->getIterationCounter();
     564             :   }
     565        1005 :   return iteration;
     566             : }
     567             : 
     568             : 
     569          85 : void VesBias::linkOptimizer(Optimizer* optimizer_pntr_in) {
     570             :   //
     571          85 :   if(optimizer_pntr_==NULL) {
     572          85 :     optimizer_pntr_ = optimizer_pntr_in;
     573             :   } else {
     574           0 :     std::string err_msg = "VES bias " + getLabel() + " of type " + getName() + " has already been linked with optimizer " + optimizer_pntr_->getLabel() + " of type " + optimizer_pntr_->getName() + ". You cannot link two optimizer to the same VES bias.";
     575           0 :     plumed_merror(err_msg);
     576             :   }
     577          85 :   checkThatTemperatureIsGiven();
     578          85 :   optimize_coeffs_ = true;
     579          85 :   filenames_have_iteration_number_ = true;
     580          85 : }
     581             : 
     582             : 
     583          82 : void VesBias::enableHessian(const bool diagonal_hessian) {
     584          82 :   compute_hessian_=true;
     585          82 :   diagonal_hessian_=diagonal_hessian;
     586          82 :   sampled_cross_averages.clear();
     587         164 :   for (unsigned int i=0; i<ncoeffssets_; i++) {
     588          82 :     std::string label = getCoeffsSetLabelString("hessian",i);
     589         164 :     hessian_pntrs_[i] = Tools::make_unique<CoeffsMatrix>(label,coeffs_pntrs_[i].get(),comm,diagonal_hessian_);
     590             :     //
     591             :     std::vector<double> cross_aver_sampled_tmp;
     592          82 :     cross_aver_sampled_tmp.assign(hessian_pntrs_[i]->getSize(),0.0);
     593          82 :     sampled_cross_averages.push_back(cross_aver_sampled_tmp);
     594             :   }
     595          82 : }
     596             : 
     597             : 
     598           0 : void VesBias::disableHessian() {
     599           0 :   compute_hessian_=false;
     600           0 :   diagonal_hessian_=true;
     601           0 :   sampled_cross_averages.clear();
     602           0 :   for (unsigned int i=0; i<ncoeffssets_; i++) {
     603           0 :     std::string label = getCoeffsSetLabelString("hessian",i);
     604           0 :     hessian_pntrs_[i] = Tools::make_unique<CoeffsMatrix>(label,coeffs_pntrs_[i].get(),comm,diagonal_hessian_);
     605             :     //
     606             :     std::vector<double> cross_aver_sampled_tmp;
     607           0 :     cross_aver_sampled_tmp.assign(hessian_pntrs_[i]->getSize(),0.0);
     608           0 :     sampled_cross_averages.push_back(cross_aver_sampled_tmp);
     609             :   }
     610           0 : }
     611             : 
     612             : 
     613         442 : std::string VesBias::getCoeffsSetLabelString(const std::string& type, const unsigned int coeffs_id) const {
     614         442 :   std::string label_prefix = getLabel() + ".";
     615         442 :   std::string label_postfix = "";
     616         442 :   if(use_multiple_coeffssets_) {
     617           0 :     Tools::convert(coeffs_id,label_postfix);
     618           0 :     label_postfix = "-" + label_postfix;
     619             :   }
     620        1326 :   return label_prefix+type+label_postfix;
     621             : }
     622             : 
     623             : 
     624         567 : std::unique_ptr<OFile> VesBias::getOFile(const std::string& filepath, const bool multi_sim_single_file, const bool enforce_backup) {
     625         567 :   auto ofile_pntr = Tools::make_unique<OFile>();
     626         567 :   std::string fp = filepath;
     627         567 :   ofile_pntr->link(*static_cast<Action*>(this));
     628         567 :   if(enforce_backup) {
     629         567 :     ofile_pntr->enforceBackup();
     630             :   }
     631         567 :   if(multi_sim_single_file) {
     632          56 :     unsigned int r=0;
     633          56 :     if(comm.Get_rank()==0) {
     634          56 :       r=multi_sim_comm.Get_rank();
     635             :     }
     636          56 :     comm.Bcast(r,0);
     637          56 :     if(r>0) {
     638             :       fp="/dev/null";
     639             :     }
     640         112 :     ofile_pntr->enforceSuffix("");
     641             :   }
     642         567 :   ofile_pntr->open(fp);
     643         567 :   return ofile_pntr;
     644           0 : }
     645             : 
     646             : 
     647           0 : void VesBias::setGridBins(const std::vector<unsigned int>& grid_bins_in) {
     648           0 :   plumed_massert(grid_bins_in.size()==getNumberOfArguments(),"the number of grid bins given doesn't match the number of arguments");
     649           0 :   grid_bins_=grid_bins_in;
     650           0 : }
     651             : 
     652             : 
     653           0 : void VesBias::setGridBins(const unsigned int nbins) {
     654           0 :   std::vector<unsigned int> grid_bins_in(getNumberOfArguments(),nbins);
     655           0 :   grid_bins_=grid_bins_in;
     656           0 : }
     657             : 
     658             : 
     659           0 : void VesBias::setGridMin(const std::vector<double>& grid_min_in) {
     660           0 :   plumed_massert(grid_min_in.size()==getNumberOfArguments(),"the number of lower bounds given for the grid doesn't match the number of arguments");
     661           0 :   grid_min_=grid_min_in;
     662           0 : }
     663             : 
     664             : 
     665           0 : void VesBias::setGridMax(const std::vector<double>& grid_max_in) {
     666           0 :   plumed_massert(grid_max_in.size()==getNumberOfArguments(),"the number of upper bounds given for the grid doesn't match the number of arguments");
     667           0 :   grid_max_=grid_max_in;
     668           0 : }
     669             : 
     670             : 
     671         383 : std::string VesBias::getCurrentOutputFilename(const std::string& base_filename, const std::string& suffix) const {
     672         383 :   std::string filename = base_filename;
     673         383 :   if(suffix.size()>0) {
     674          82 :     filename = FileBase::appendSuffix(filename,"."+suffix);
     675             :   }
     676         383 :   if(filenamesIncludeIterationNumber()) {
     677         756 :     filename = FileBase::appendSuffix(filename,"."+getIterationFilenameSuffix());
     678             :   }
     679         383 :   return filename;
     680             : }
     681             : 
     682             : 
     683         192 : std::string VesBias::getCurrentTargetDistOutputFilename(const std::string& suffix) const {
     684         192 :   std::string filename = targetdist_filename_;
     685         192 :   if(suffix.size()>0) {
     686         204 :     filename = FileBase::appendSuffix(filename,"."+suffix);
     687             :   }
     688         192 :   if(filenamesIncludeIterationNumber() && dynamicTargetDistribution()) {
     689         348 :     filename = FileBase::appendSuffix(filename,"."+getIterationFilenameSuffix());
     690             :   }
     691         192 :   return filename;
     692             : }
     693             : 
     694             : 
     695         552 : std::string VesBias::getIterationFilenameSuffix() const {
     696             :   std::string iter_str;
     697         552 :   Tools::convert(getIterationCounter(),iter_str);
     698         552 :   iter_str = "iter-" + iter_str;
     699         552 :   return iter_str;
     700             : }
     701             : 
     702             : 
     703           0 : std::string VesBias::getCoeffsSetFilenameSuffix(const unsigned int coeffs_id) const {
     704           0 :   std::string suffix = "";
     705           0 :   if(use_multiple_coeffssets_) {
     706           0 :     Tools::convert(coeffs_id,suffix);
     707           0 :     suffix = coeffs_id_prefix_ + suffix;
     708             :   }
     709           0 :   return suffix;
     710             : }
     711             : 
     712             : 
     713           3 : void VesBias::setupBiasCutoff(const double bias_cutoff_value, const double fermi_lambda) {
     714             :   //
     715             :   double fermi_exp_max = 100.0;
     716             :   //
     717             :   std::string str_bias_cutoff_value;
     718             :   VesTools::convertDbl2Str(bias_cutoff_value,str_bias_cutoff_value);
     719             :   std::string str_fermi_lambda;
     720             :   VesTools::convertDbl2Str(fermi_lambda,str_fermi_lambda);
     721             :   std::string str_fermi_exp_max;
     722             :   VesTools::convertDbl2Str(fermi_exp_max,str_fermi_exp_max);
     723           6 :   std::string swfunc_keywords = "FERMI R_0=" + str_bias_cutoff_value + " FERMI_LAMBDA=" + str_fermi_lambda + " FERMI_EXP_MAX=" + str_fermi_exp_max;
     724             :   //
     725           3 :   std::string swfunc_errors="";
     726           6 :   bias_cutoff_swfunc_pntr_ = Tools::make_unique<FermiSwitchingFunction>();
     727           3 :   bias_cutoff_swfunc_pntr_->set(swfunc_keywords,swfunc_errors);
     728           3 :   if(swfunc_errors.size()>0) {
     729           0 :     plumed_merror("problem with setting up Fermi switching function: " + swfunc_errors);
     730             :   }
     731             :   //
     732           3 :   bias_cutoff_value_=bias_cutoff_value;
     733           3 :   bias_cutoff_active_=true;
     734             :   enableDynamicTargetDistribution();
     735           3 : }
     736             : 
     737             : 
     738        3263 : double VesBias::getBiasCutoffSwitchingFunction(const double bias, double& deriv_factor) const {
     739        3263 :   plumed_massert(bias_cutoff_active_,"The bias cutoff is not active so you cannot call this function");
     740        3263 :   double arg = -(bias-bias_current_max_value);
     741        3263 :   double deriv=0.0;
     742        3263 :   double value = bias_cutoff_swfunc_pntr_->calculate(arg,deriv);
     743             :   // as FermiSwitchingFunction class has different behavior from normal SwitchingFunction class
     744             :   // I was having problems with NaN as it was dividing with zero
     745             :   // deriv *= arg;
     746        3263 :   deriv_factor = value-bias*deriv;
     747        3263 :   return value;
     748             : }
     749             : 
     750             : 
     751         567 : bool VesBias::useMultipleWalkers() const {
     752             :   bool use_mwalkers_mpi=false;
     753         567 :   if(optimizeCoeffs() && getOptimizerPntr()->useMultipleWalkers()) {
     754             :     use_mwalkers_mpi=true;
     755             :   }
     756         567 :   return use_mwalkers_mpi;
     757             : }
     758             : 
     759             : 
     760           0 : void VesBias::updateReweightFactor() {
     761           0 :   if(calc_reweightfactor_) {
     762           0 :     double value = calculateReweightFactor();
     763           0 :     getPntrToComponent("rct")->set(value);
     764             :   }
     765           0 : }
     766             : 
     767             : 
     768           0 : double VesBias::calculateReweightFactor() const {
     769           0 :   plumed_merror(getName()+" with label "+getLabel()+": calculation of the reweight factor c(t) has not been implemented for this type of VES bias");
     770             :   return 0.0;
     771             : }
     772             : 
     773             : 
     774             : }
     775             : }

Generated by: LCOV version 1.16