LCOV - code coverage report
Current view: top level - drr - DynamicReferenceRestraining.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 419 458 91.5 %
Date: 2026-11-05 10:19:04 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :     Copyright (c) 2017 of Haochuan Chen (excluding colvar_UIestimator.h)
       3             :     Copyright (c) 2017 of Haohao Fu (colvar_UIestimator.h)
       4             : 
       5             :     This program is free software: you can redistribute it and/or modify
       6             :     it under the terms of the GNU Lesser General Public License as published
       7             :     by the Free Software Foundation, either version 3 of the License, or
       8             :     (at your option) any later version.
       9             : 
      10             :     This program is distributed in the hope that it will be useful,
      11             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :     GNU Lesser General Public License for more details.
      14             : 
      15             :     You should have received a copy of the GNU Lesser General Public License
      16             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      18             : #ifdef __PLUMED_HAS_BOOST_SERIALIZATION
      19             : #include "core/ActionRegister.h"
      20             : #include "bias/Bias.h"
      21             : #include "core/PlumedMain.h"
      22             : #include "DRR.h"
      23             : #include "tools/Random.h"
      24             : #include "tools/Tools.h"
      25             : #include "colvar_UIestimator.h"
      26             : 
      27             : #include <boost/archive/binary_iarchive.hpp>
      28             : #include <boost/archive/binary_oarchive.hpp>
      29             : #include <boost/serialization/vector.hpp>
      30             : #include <cmath>
      31             : #include <fstream>
      32             : #include <iomanip>
      33             : #include <iostream>
      34             : #include <limits>
      35             : #include <random>
      36             : #include <string>
      37             : 
      38             : using namespace PLMD;
      39             : using namespace bias;
      40             : using namespace std;
      41             : 
      42             : namespace PLMD {
      43             : namespace drr {
      44             : 
      45             : //+PLUMEDOC EABFMOD_BIAS DRR
      46             : /*
      47             : Used to performed extended-system adaptive biasing force (eABF)
      48             : 
      49             : This method was introduced in the first paper cited below.  It is used
      50             :  on one or more collective variables. This method is also
      51             :  called dynamic reference restraining(DRR) as in the second paper cited below. A detailed description
      52             :  of this module can be found in the third paper cited below.
      53             : 
      54             : For each collective variable $\xi_i$, a fictitious variable $\lambda_i$
      55             : is attached through a spring. The fictitious variable $\lambda_i$ undergoes
      56             : overdamped Langevin dynamics just like [EXTENDED_LAGRANGIAN](EXTENDED_LAGRANGIAN.md). The ABF
      57             : algorithm applies bias force on $\lambda_i$. The bias force acts on
      58             : $\lambda_i$ is the negative average spring force on $\lambda_i$, which
      59             : enhances the sampling of $\lambda_i$.
      60             : 
      61             : $$
      62             : F_{bias}(\lambda_i)=k(\lambda_i-\langle\xi_i\rangle_{\lambda_i})
      63             : $$
      64             : 
      65             : If spring force constant k is large enough, then $\xi_i$ synchronizes with
      66             : $\lambda_i$. The naive(ABF) estimator is just the negative
      67             : average spring force of $\lambda_i$.
      68             : 
      69             : The naive(ABF) estimator is biased. There are unbiased estimators such as
      70             : CZAR(Corrected z-averaged restraint) discussed in the fourth paper cited below and UI(Umbrella
      71             : Integration).
      72             : The CZAR estimates the gradients as:
      73             : 
      74             : $$
      75             : \frac{\partial{A}}{\partial{\xi_i}}\left({\xi}\right)=-\frac{1}{\beta}\frac{\partial\ln\tilde{\rho}\left(\xi\right)}{\partial{\xi_i}}+k\left(\langle\lambda_i\rangle_\xi-\xi_i\right)
      76             : $$
      77             : 
      78             : The UI estimates the gradients as:
      79             : 
      80             : $$
      81             : A'(\xi^*)=\frac{{\sum_\lambda}N\left(\xi^*,\lambda\right)\left[\frac{\xi^*-\langle\xi\rangle_\lambda}{\beta\sigma_\lambda^2}-k(\xi^*-\lambda)\right]}{{\sum_\lambda}N\left(\xi^*,\lambda\right)}
      82             : $$
      83             : 
      84             : The code performing UI(colvar_UIestimator.h) is contributed by Haohao Fu (see fifth paper cited below).
      85             : It may be slow. I only change the Boltzmann constant and output
      86             : precision in it. For new version and issues, please see:
      87             : https://github.com/fhh2626/colvars
      88             : 
      89             : After running eABF/DRR, the [drr_tool](drr_tool.md) utility can be used to extract the gradients and counts files from .drrstate.
      90             : Naive(ABF) estimator's result is in .abf.grad and .abf.count files and CZAR estimator's result is in .czar.grad and .czar.count files.
      91             : The additional .zcount and .zgrad files contain the number of samples of $\boldsymbol{\xi}$, and the negative of $\boldsymbol{\xi}$-averaged
      92             : spring forces, respectively, which are mainly for inspecting and debugging purpose. To get PMF, the abf_integrate(https://github.com/Colvars/colvars/tree/master/colvartools) is useful for numerically integrating the .czar.grad file.
      93             : 
      94             : ## Examples
      95             : 
      96             : The following input tells plumed to perform a eABF/DRR simulation on two
      97             : torsional angles.
      98             : 
      99             : ```plumed
     100             : phi: TORSION ATOMS=5,7,9,15
     101             : psi: TORSION ATOMS=7,9,15,17
     102             : 
     103             : DRR ...
     104             : LABEL=eabf
     105             : ARG=phi,psi
     106             : FULLSAMPLES=500
     107             : GRID_MIN=-pi,-pi
     108             : GRID_MAX=pi,pi
     109             : GRID_BIN=180,180
     110             : FRICTION=8.0,8.0
     111             : TAU=0.5,0.5
     112             : OUTPUTFREQ=50000
     113             : HISTORYFREQ=500000
     114             : ... DRR
     115             : 
     116             : # monitor the two variables, their fictitious variables and applied forces.
     117             : PRINT STRIDE=10 ARG=phi,psi,eabf.phi_fict,eabf.psi_fict,eabf.phi_biasforce,eabf.psi_biasforce FILE=COLVAR
     118             : ```
     119             : 
     120             : The following input tells plumed to perform a eABF/DRR simulation on the
     121             : distance of atom 10 and 92. The distance is restraint by [LOWER_WALLS](LOWER_WALLS.md) and
     122             : [UPPER_WALLS](UPPER_WALLS.md).
     123             : 
     124             : ```plumed
     125             : dist1: DISTANCE ATOMS=10,92
     126             : eabf_winall: DRR ARG=dist1 FULLSAMPLES=2000 GRID_MIN=1.20 GRID_MAX=3.20 GRID_BIN=200 FRICTION=8.0 TAU=0.5 OUTPUTFREQ=5000 HISTORYFREQ=500000
     127             : uwall: UPPER_WALLS ARG=eabf_winall.dist1_fict AT=3.2 KAPPA=418.4
     128             : lwall: LOWER_WALLS ARG=eabf_winall.dist1_fict AT=1.2 KAPPA=418.4
     129             : PRINT STRIDE=10 ARG=dist1,eabf_winall.dist1_fict,eabf_winall.dist1_biasforce FILE=COLVAR
     130             : ```
     131             : 
     132             : It's also possible to run extended generalized adaptive biasing force (egABF) described in the sixth paper cited below.
     133             : An egABF example:
     134             : 
     135             : ```plumed
     136             : phi: TORSION ATOMS=5,7,9,15
     137             : psi: TORSION ATOMS=7,9,15,17
     138             : 
     139             : DRR ...
     140             : LABEL=gabf_phi
     141             : ARG=phi
     142             : FULLSAMPLES=500
     143             : GRID_MIN=-pi
     144             : GRID_MAX=pi
     145             : GRID_BIN=180
     146             : FRICTION=8.0
     147             : TAU=0.5
     148             : OUTPUTFREQ=50000
     149             : HISTORYFREQ=500000
     150             : ... DRR
     151             : 
     152             : DRR ...
     153             : LABEL=gabf_psi
     154             : ARG=psi
     155             : FULLSAMPLES=500
     156             : GRID_MIN=-pi
     157             : GRID_MAX=pi
     158             : GRID_BIN=180
     159             : FRICTION=8.0
     160             : TAU=0.5
     161             : OUTPUTFREQ=50000
     162             : HISTORYFREQ=500000
     163             : ... DRR
     164             : 
     165             : DRR ...
     166             : LABEL=gabf_2d
     167             : ARG=phi,psi
     168             : EXTERNAL_FORCE=gabf_phi.phi_springforce,gabf_psi.psi_springforce
     169             : EXTERNAL_FICT=gabf_phi.phi_fictNoPBC,gabf_psi.psi_fictNoPBC
     170             : GRID_MIN=-pi,-pi
     171             : GRID_MAX=pi,pi
     172             : GRID_BIN=180,180
     173             : NOBIAS
     174             : OUTPUTFREQ=50000
     175             : HISTORYFREQ=500000
     176             : ... DRR
     177             : 
     178             : PRINT STRIDE=10 ARG=phi,psi FILE=COLVAR
     179             : ```
     180             : 
     181             :  */
     182             : //+ENDPLUMEDOC
     183             : 
     184             : using std::vector;
     185             : using std::string;
     186             : 
     187             : class DynamicReferenceRestraining : public Bias {
     188             : private:
     189             :   bool firsttime;
     190             :   bool nobias;
     191             :   vector<double> fictNoPBC;
     192             :   vector<double> real;
     193             :   vector<double> springlength; // spring lengths
     194             :   vector<double> fict;         // coordinates of extended variables
     195             :   vector<double> vfict;        // velocities of extended variables
     196             :   vector<double> vfict_laststep;
     197             :   vector<double> ffict; // forces exerted on extended variables
     198             :   vector<double> fbias; // bias forces from eABF
     199             :   vector<double> kappa;
     200             :   vector<double> tau;
     201             :   vector<double> friction;
     202             :   vector<double> etemp;
     203             :   vector<double> ffict_measured;
     204             :   vector<double> force_external;
     205             :   vector<double> fict_external;
     206             :   vector<Value *> biasforceValue;
     207             :   vector<Value *> springforceValue;
     208             :   vector<Value *> fictValue;
     209             :   vector<Value *> vfictValue;
     210             :   vector<Value *> fictNoPBCValue;
     211             :   vector<Value *> externalForceValue;
     212             :   vector<Value *> externalFictValue;
     213             :   vector<double> c1;
     214             :   vector<double> c2;
     215             :   vector<double> mass;
     216             :   vector<DRRAxis> delim;
     217             :   string outputname;
     218             :   string cptname;
     219             :   string outputprefix;
     220             :   string fmt_;
     221             :   const size_t ndims;
     222             :   double dt;
     223             :   double kbt;
     224             :   double outputfreq;
     225             :   double historyfreq;
     226             :   bool isRestart;
     227             :   bool useCZARestimator;
     228             :   bool useUIestimator;
     229             :   bool mergeHistoryFiles;
     230             :   bool textoutput;
     231             :   bool withExternalForce;
     232             :   bool withExternalFict;
     233             :   vector<unsigned> reflectingWall;
     234             :   ABF ABFGrid;
     235             :   CZAR CZARestimator;
     236             :   double fullsamples;
     237             :   vector<double> maxFactors;
     238             :   UIestimator::UIestimator eabf_UI;
     239             :   Random rand;
     240             : 
     241             : public:
     242             :   explicit DynamicReferenceRestraining(const ActionOptions &);
     243             :   void calculate();
     244             :   void update();
     245             :   void save(const string &filename, long long int step);
     246             :   void load(const string &filename);
     247             :   void backupFile(const string &filename);
     248             :   static void registerKeywords(Keywords &keys);
     249             :   bool is_file_exist(const char *fileName);
     250             : };
     251             : 
     252             : PLUMED_REGISTER_ACTION(DynamicReferenceRestraining, "DRR")
     253             : 
     254          16 : void DynamicReferenceRestraining::registerKeywords(Keywords &keys) {
     255          16 :   Bias::registerKeywords(keys);
     256          16 :   keys.add("optional", "KAPPA", "specifies that the restraint is harmonic and "
     257             :            "what the values of the force constants on "
     258             :            "each of the variables are (default to "
     259             :            "k_BT/(GRID_SPACING)^2)");
     260          16 :   keys.add("compulsory", "TAU", "0.5", "specifies relaxation time on each of "
     261             :            "variables are, similar to "
     262             :            "extended Time Constant in Colvars");
     263          16 :   keys.add("compulsory", "FRICTION", "8.0",
     264             :            "add a friction to the variable, similar to extended Langevin Damping "
     265             :            "in Colvars");
     266          16 :   keys.add("compulsory", "GRID_MIN", "the lower bounds for the grid (GRID_BIN "
     267             :            "or GRID_SPACING should be specified)");
     268          16 :   keys.add("compulsory", "GRID_MAX", "the upper bounds for the grid (GRID_BIN "
     269             :            "or GRID_SPACING should be specified)");
     270          16 :   keys.add("compulsory", "REFLECTINGWALL", "0", "whether add reflecting walls "
     271             :            "for each CV at GRID_MIN and GRID_MAX. Setting non-zero values will "
     272             :            "enable this feature");
     273          16 :   keys.add("optional", "GRID_BIN", "the number of bins for the grid");
     274          16 :   keys.add("optional", "GRID_SPACING", "the approximate grid spacing (to be "
     275             :            "used as an alternative or together "
     276             :            "with GRID_BIN)");
     277          16 :   keys.add("optional", "ZGRID_MIN", "the lower bounds for the grid (ZGRID_BIN"
     278             :            " or ZGRID_SPACING should be specified)");
     279          16 :   keys.add("optional", "ZGRID_MAX", "the upper bounds for the grid (ZGRID_BIN"
     280             :            " or ZGRID_SPACING should be specified)");
     281          16 :   keys.add("optional", "ZGRID_BIN", "the number of bins for the grid");
     282          16 :   keys.add("optional", "ZGRID_SPACING", "the approximate grid spacing (to be "
     283             :            "used as an alternative or together "
     284             :            "with ZGRID_BIN)");
     285          16 :   keys.add("optional", "EXTERNAL_FORCE", "use forces from other action instead"
     286             :            " of internal spring force, this disable the extended system!");
     287          16 :   keys.add("optional", "EXTERNAL_FICT", "position of external fictitious "
     288             :            "particles, useful for UIESTIMATOR");
     289          16 :   keys.add("compulsory", "FULLSAMPLES", "500",
     290             :            "number of samples in a bin prior to application of the ABF");
     291          16 :   keys.add("compulsory", "MAXFACTOR", "1.0",
     292             :            "maximum scaling factor of biasing force");
     293          16 :   keys.add("compulsory", "OUTPUTFREQ", "write results to a file every N steps");
     294          16 :   keys.add("optional", "HISTORYFREQ", "save history to a file every N steps");
     295          16 :   keys.addFlag("NOCZAR", false, "disable the CZAR estimator");
     296          16 :   keys.addFlag("UI", false,
     297             :                "enable the umbrella integration estimator");
     298          16 :   keys.add("optional", "UIRESTARTPREFIX",
     299             :            "specify the restart files for umbrella integration");
     300          16 :   keys.add("optional", "OUTPUTPREFIX",
     301             :            "specify the output prefix (default to the label name)");
     302          16 :   keys.add("optional", "TEMP", "the system temperature - needed when FRICTION "
     303             :            "is present. If not provided will be taken from "
     304             :            "MD code (if available)");
     305          16 :   keys.add(
     306             :     "optional", "EXTTEMP",
     307             :     "the temperature of extended variables (default to system temperature)");
     308          16 :   keys.add("optional", "DRR_RFILE",
     309             :            "specifies the restart file (.drrstate file)");
     310          16 :   keys.addFlag("NOBIAS", false, "DO NOT apply bias forces.");
     311          16 :   keys.addFlag("TEXTOUTPUT", false, "use text output for grad and count files "
     312             :                "instead of boost::serialization binary "
     313             :                "output");
     314          16 :   keys.addFlag("MERGEHISTORYFILES", false, "output all historic results "
     315             :                "to a single file rather than multiple .drrstate files. "
     316             :                "This option is effective only when textOutput is on.");
     317          16 :   keys.add("optional","FMT","specify format for outfiles files (useful for decrease the number of digits in regtests)");
     318          32 :   keys.addOutputComponent(
     319             :     "_fict", "default", "scalar",
     320             :     "one or multiple instances of this quantity can be referenced "
     321             :     "elsewhere in the input file. "
     322             :     "These quantities will named with the arguments of the bias followed by "
     323             :     "the character string _tilde. It is possible to add forces on these "
     324             :     "variable.");
     325          32 :   keys.addOutputComponent(
     326             :     "_vfict", "default", "scalar",
     327             :     "one or multiple instances of this quantity can be referenced "
     328             :     "elsewhere in the input file. "
     329             :     "These quantities will named with the arguments of the bias followed by "
     330             :     "the character string _tilde. It is NOT possible to add forces on these "
     331             :     "variable.");
     332          32 :   keys.addOutputComponent(
     333             :     "_biasforce", "default", "scalar",
     334             :     "The bias force from eABF/DRR of the fictitious particle.");
     335          32 :   keys.addOutputComponent("_springforce", "default", "scalar", "Spring force between real CVs and extended CVs");
     336          32 :   keys.addOutputComponent("_fictNoPBC", "default", "scalar",
     337             :                           "the positions of fictitious particles (without PBC).");
     338          16 :   keys.addDOI("10.1063/1.2711185");
     339          16 :   keys.addDOI("10.1021/ct200726v");
     340          16 :   keys.addDOI("10.1021/acs.jcim.8b00115");
     341          16 :   keys.addDOI("10.1021/acs.jpcb.6b10055");
     342          16 :   keys.addDOI("10.1021/acs.jctc.6b00447");
     343          16 :   keys.addDOI("10.1021/acs.jctc.7b00032");
     344          16 : }
     345             : 
     346          12 : DynamicReferenceRestraining::DynamicReferenceRestraining(
     347          12 :   const ActionOptions &ao)
     348          12 :   : PLUMED_BIAS_INIT(ao), firsttime(true), nobias(false),
     349          12 :     fictNoPBC(getNumberOfArguments(), 0.0), real(getNumberOfArguments(), 0.0),
     350          12 :     springlength(getNumberOfArguments(), 0.0),
     351          12 :     fict(getNumberOfArguments(), 0.0), vfict(getNumberOfArguments(), 0.0),
     352          12 :     vfict_laststep(getNumberOfArguments(), 0.0),
     353          12 :     ffict(getNumberOfArguments(), 0.0), fbias(getNumberOfArguments(), 0.0),
     354          12 :     kappa(getNumberOfArguments(), 0.0), tau(getNumberOfArguments(), 0.0),
     355          12 :     friction(getNumberOfArguments(), 0.0), etemp(getNumberOfArguments(), 0.0),
     356          12 :     ffict_measured(getNumberOfArguments(), 0.0),
     357          12 :     biasforceValue(getNumberOfArguments(), NULL),
     358          12 :     springforceValue(getNumberOfArguments(), NULL),
     359          12 :     fictValue(getNumberOfArguments(), NULL),
     360          12 :     vfictValue(getNumberOfArguments(), NULL),
     361          12 :     fictNoPBCValue(getNumberOfArguments(), NULL),
     362          12 :     externalForceValue(getNumberOfArguments(), NULL),
     363          12 :     externalFictValue(getNumberOfArguments(), NULL),
     364          12 :     c1(getNumberOfArguments(), 0.0),
     365          12 :     c2(getNumberOfArguments(), 0.0), mass(getNumberOfArguments(), 0.0),
     366          12 :     delim(getNumberOfArguments()), outputname(""), cptname(""),
     367          12 :     outputprefix(""), fmt_("%.9f"), ndims(getNumberOfArguments()), dt(0.0), kbt(0.0),
     368          12 :     outputfreq(0.0), historyfreq(-1.0), isRestart(false),
     369          12 :     useCZARestimator(true), useUIestimator(false), mergeHistoryFiles(false),
     370          12 :     textoutput(false), withExternalForce(false), withExternalFict(false),
     371          12 :     reflectingWall(getNumberOfArguments(), 0),
     372          24 :     maxFactors(getNumberOfArguments(), 1.0) {
     373          12 :   log << "eABF/DRR: You now are using the extended adaptive biasing "
     374             :       "force(eABF) method."
     375          12 :       << '\n';
     376          12 :   log << "eABF/DRR: Some people also refer to it as dynamic reference "
     377             :       "restraining(DRR) method."
     378          12 :       << '\n';
     379          12 :   log << "eABF/DRR: Currently the CZAR and naive(ABF on extended variables) "
     380             :       "estimator is enabled by default."
     381          12 :       << '\n';
     382          12 :   log << "eABF/DRR: For reasons of performance, the umbrella integration "
     383             :       "estimator is not enabled by default."
     384          12 :       << '\n';
     385          12 :   log << "eABF/DRR: This method is originally implemented in "
     386             :       "colvars(https://github.com/colvars/colvars)."
     387          12 :       << '\n';
     388          12 :   log << "eABF/DRR: The code in plumed is heavily modified from "
     389             :       "ExtendedLagrangian.cpp and doesn't implemented all variants of "
     390             :       "eABF/DRR."
     391          12 :       << '\n';
     392          12 :   log << "eABF/DRR: The thermostat used here may be different from Colvars."
     393          12 :       << '\n';
     394          12 :   log << "eABF/DRR: To integrate the gradients file, you can use abf_integrate "
     395             :       "from https://github.com/colvars/colvars/tree/master/colvartools."
     396          12 :       << '\n';
     397          12 :   log << "eABF/DRR: Please read relevant articles and use this biasing "
     398             :       "method carefully!"
     399          12 :       << '\n';
     400          12 :   parseFlag("NOBIAS", nobias);
     401          12 :   parseFlag("UI", useUIestimator);
     402          12 :   bool noCZAR = false;
     403          12 :   parseFlag("NOCZAR", noCZAR);
     404          12 :   parseFlag("TEXTOUTPUT", textoutput);
     405          12 :   parseFlag("MERGEHISTORYFILES", mergeHistoryFiles);
     406          12 :   parseVector("TAU", tau);
     407          12 :   parseVector("FRICTION", friction);
     408          12 :   parseVector("EXTTEMP", etemp);
     409          12 :   parseVector("KAPPA", kappa);
     410          12 :   parseVector("REFLECTINGWALL", reflectingWall);
     411          12 :   parse("FULLSAMPLES", fullsamples);
     412          12 :   parseVector("MAXFACTOR", maxFactors);
     413          12 :   parse("OUTPUTFREQ", outputfreq);
     414          12 :   parse("HISTORYFREQ", historyfreq);
     415          24 :   parse("OUTPUTPREFIX", outputprefix);
     416             :   string restart_prefix;
     417          24 :   parse("DRR_RFILE", restart_prefix);
     418             :   string uirprefix;
     419          12 :   parse("UIRESTARTPREFIX", uirprefix);
     420          12 :   parseArgumentList("EXTERNAL_FORCE", externalForceValue);
     421          12 :   parseArgumentList("EXTERNAL_FICT", externalFictValue);
     422          24 :   parse("FMT",fmt_);
     423          12 :   if (externalForceValue.empty()) {
     424          11 :     withExternalForce = false;
     425           1 :   } else if (externalForceValue.size() != ndims) {
     426           0 :     error("eABF/DRR: Number of forces doesn't match ARGS!");
     427             :   } else {
     428           1 :     withExternalForce = true;
     429             :   }
     430          12 :   if (withExternalForce && useUIestimator) {
     431           1 :     if (externalFictValue.empty()) {
     432           0 :       error("eABF/DRR: No external fictitious particles specified. UI estimator needs it.");
     433           1 :     } else if(externalFictValue.size() != ndims) {
     434           0 :       error("eABF/DRR: Number of fictitious particles doesn't match ARGS!");
     435             :     } else {
     436           1 :       withExternalFict = true;
     437             :     }
     438             :   }
     439          12 :   kbt = getkBT();
     440          12 :   if (kbt <= std::numeric_limits<double>::epsilon()) {
     441           0 :     error("eABF/DRR: It seems the MD engine does not setup the temperature correctly for PLUMED."
     442             :           "Please set it by the TEMP keyword manually.");
     443             :   }
     444          12 :   if (fullsamples < 0.5) {
     445           0 :     fullsamples = 500.0;
     446           0 :     log << "eABF/DRR: The fullsamples parametre is not set. Set it to "
     447             :         "500(default)."
     448           0 :         << '\n';
     449             :   }
     450          12 :   if (getRestart()) {
     451           1 :     if (restart_prefix.length() != 0) {
     452           1 :       isRestart = true;
     453           1 :       firsttime = false;
     454           1 :       load(restart_prefix);
     455             :     } else {
     456           0 :       log << "eABF/DRR: You don't specify the file for restarting." << '\n';
     457           0 :       log << "eABF/DRR: So I assume you are splitting windows." << '\n';
     458           0 :       isRestart = false;
     459           0 :       firsttime = true;
     460             :     }
     461             :   }
     462             : 
     463          12 :   vector<string> gmin(ndims);
     464          12 :   vector<string> zgmin(ndims);
     465          12 :   parseVector("GRID_MIN", gmin);
     466          24 :   parseVector("ZGRID_MIN", zgmin);
     467          12 :   if (gmin.size() != ndims) {
     468           0 :     error("eABF/DRR: not enough values for GRID_MIN");
     469             :   }
     470          12 :   if (zgmin.size() != ndims) {
     471          33 :     log << "eABF/DRR: You didn't specify ZGRID_MIN. " << '\n'
     472          11 :         << "eABF/DRR: The GRID_MIN will be used instead.";
     473          11 :     zgmin = gmin;
     474             :   }
     475          12 :   vector<string> gmax(ndims);
     476          12 :   vector<string> zgmax(ndims);
     477          12 :   parseVector("GRID_MAX", gmax);
     478          24 :   parseVector("ZGRID_MAX", zgmax);
     479          12 :   if (gmax.size() != ndims) {
     480           0 :     error("eABF/DRR: not enough values for GRID_MAX");
     481             :   }
     482          12 :   if (zgmax.size() != ndims) {
     483          33 :     log << "eABF/DRR: You didn't specify ZGRID_MAX. " << '\n'
     484          11 :         << "eABF/DRR: The GRID_MAX will be used instead.";
     485          11 :     zgmax = gmax;
     486             :   }
     487          12 :   vector<unsigned> gbin(ndims);
     488          12 :   vector<unsigned> zgbin(ndims);
     489          12 :   vector<double> gspacing(ndims);
     490          12 :   vector<double> zgspacing(ndims);
     491          12 :   parseVector("GRID_BIN", gbin);
     492          12 :   parseVector("ZGRID_BIN", zgbin);
     493          12 :   parseVector("GRID_SPACING", gspacing);
     494          24 :   parseVector("ZGRID_SPACING", zgspacing);
     495          12 :   if (gbin.size() != ndims) {
     496           3 :     log << "eABF/DRR: You didn't specify GRID_BIN. Trying to use GRID_SPACING "
     497             :         "instead."
     498           3 :         << '\n';
     499           3 :     if (gspacing.size() != ndims) {
     500           0 :       error("eABF/DRR: not enough values for GRID_BIN");
     501             :     } else {
     502           3 :       gbin.resize(ndims);
     503           6 :       for (size_t i = 0; i < ndims; ++i) {
     504             :         double l, h;
     505           3 :         PLMD::Tools::convert(gmin[i], l);
     506           3 :         PLMD::Tools::convert(gmax[i], h);
     507           3 :         gbin[i] = std::nearbyint((h - l) / gspacing[i]);
     508           3 :         gspacing[i] = (h - l) / gbin[i];
     509           3 :         log << "GRID_BIN[" << i << "] is " << gbin[i] << '\n';
     510             :       }
     511             :     }
     512             :   }
     513          12 :   if (zgbin.size() != ndims) {
     514          11 :     log << "eABF/DRR: You didn't specify ZGRID_BIN. Trying to use ZGRID_SPACING instead." << '\n';
     515          11 :     if (zgspacing.size() != ndims) {
     516          11 :       log << "eABF/DRR: You didn't specify ZGRID_SPACING. Trying to use GRID_SPACING or GRID_BIN instead." << '\n';
     517          11 :       zgbin = gbin;
     518          11 :       zgspacing = gspacing;
     519             :     } else {
     520           0 :       zgbin.resize(ndims);
     521           0 :       for (size_t i = 0; i < ndims; ++i) {
     522             :         double l, h;
     523           0 :         PLMD::Tools::convert(zgmin[i], l);
     524           0 :         PLMD::Tools::convert(zgmax[i], h);
     525           0 :         zgbin[i] = std::nearbyint((h - l) / zgspacing[i]);
     526           0 :         zgspacing[i] = (h - l) / zgbin[i];
     527           0 :         log << "ZGRID_BIN[" << i << "] is " << zgbin[i] << '\n';
     528             :       }
     529             :     }
     530             :   }
     531          12 :   checkRead();
     532             : 
     533             :   // Set up kbt for extended system
     534          12 :   log << "eABF/DRR: The fullsamples is " << fullsamples << '\n';
     535          12 :   log << "eABF/DRR: The kbt(real system) is " << kbt << '\n';
     536          12 :   dt = getTimeStep() * getStride();
     537          12 :   log << "eABF/DRR: timestep = " << getTimeStep() << " ps with stride = " << getStride() << " steps\n";
     538          12 :   vector<double> ekbt(ndims, 0.0);
     539          12 :   if (etemp.size() != ndims) {
     540          12 :     etemp.assign(ndims, kbt / getKBoltzmann());
     541             :   }
     542          12 :   if (tau.size() != ndims) {
     543           0 :     tau.assign(ndims, 0.5);
     544             :   }
     545          12 :   if (friction.size() != ndims) {
     546           0 :     friction.assign(ndims, 8.0);
     547             :   }
     548          12 :   if (maxFactors.size() != ndims) {
     549           0 :     maxFactors.assign(ndims, 1.0);
     550             :   }
     551          31 :   for (size_t i = 0; i < ndims; ++i) {
     552          19 :     log << "eABF/DRR: The maximum scaling factor [" << i << "] is " << maxFactors[i] << '\n';
     553          19 :     if (maxFactors[i] > 1.0) {
     554           0 :       log << "eABF/DRR: Warning! The maximum scaling factor larger than 1.0 is not recommended!" << '\n';
     555             :     }
     556             :   }
     557          31 :   for (size_t i = 0; i < ndims; ++i) {
     558          19 :     ekbt[i] = etemp[i] * getKBoltzmann();
     559          19 :     log << "eABF/DRR: The kbt(extended system) of [" << i << "] is " << ekbt[i]
     560          19 :         << '\n';
     561          19 :     log << "eABF/DRR: relaxation time tau [" << i << "] is " << tau[i] << '\n';
     562          19 :     log << "eABF/DRR: Extended variable [" << i << "] has friction: " << friction[i] << '\n';
     563             :   }
     564             : 
     565             :   // Set up the force grid
     566          12 :   vector<DRRAxis> zdelim(ndims);
     567          31 :   for (size_t i = 0; i < ndims; ++i) {
     568          19 :     log << "eABF/DRR: The " << i << " dimensional grid minimum is " << gmin[i]
     569          19 :         << '\n';
     570          19 :     log << "eABF/DRR: The " << i << " dimensional grid maximum is " << gmax[i]
     571          19 :         << '\n';
     572          19 :     log << "eABF/DRR: The " << i << " dimensional grid has " << gbin[i]
     573          19 :         << " bins" << '\n';
     574          19 :     log << "eABF/DRR: The " << i << " dimensional zgrid minimum is " << zgmin[i]
     575          19 :         << '\n';
     576          19 :     log << "eABF/DRR: The " << i << " dimensional zgrid maximum is " << zgmax[i]
     577          19 :         << '\n';
     578          19 :     log << "eABF/DRR: The " << i << " dimensional zgrid has " << zgbin[i]
     579          19 :         << " bins" << '\n';
     580             :     double l, h;
     581          19 :     PLMD::Tools::convert(gmin[i], l);
     582          19 :     PLMD::Tools::convert(gmax[i], h);
     583          19 :     delim[i].set(l, h, gbin[i]);
     584             :     double zl,zh;
     585          19 :     PLMD::Tools::convert(zgmin[i], zl);
     586          19 :     PLMD::Tools::convert(zgmax[i], zh);
     587          19 :     zdelim[i].set(zl, zh, zgbin[i]);
     588             :   }
     589          12 :   if (kappa.size() != ndims) {
     590           9 :     kappa.resize(ndims, 0.0);
     591          25 :     for (size_t i = 0; i < ndims; ++i) {
     592          16 :       if (kappa[i] <= 0) {
     593          16 :         log << "eABF/DRR: The spring force constant kappa[" << i
     594          16 :             << "] is not set." << '\n';
     595          16 :         kappa[i] = ekbt[i] / (delim[i].getWidth() * delim[i].getWidth());
     596          16 :         log << "eABF/DRR: set kappa[" << i
     597          16 :             << "] according to bin width(ekbt/(binWidth^2))." << '\n';
     598             :       }
     599          16 :       log << "eABF/DRR: The spring force constant kappa[" << i << "] is "
     600          16 :           << std::fixed << std::setprecision(10) << kappa[i] << '\n';
     601             :     }
     602             :   } else {
     603           3 :     log << "eABF/DRR: The kappa have been set manually." << '\n';
     604           6 :     for (size_t i = 0; i < ndims; ++i) {
     605           3 :       log << "eABF/DRR: The spring force constant kappa[" << i << "] is "
     606           3 :           << std::fixed << std::setprecision(10) << kappa[i] << '\n';
     607             :     }
     608             :   }
     609             : 
     610          31 :   for (size_t i = 0; i < ndims; ++i) {
     611          19 :     mass[i] = kappa[i] * tau[i] * tau[i] / (4 * pi * pi);
     612          19 :     log << "eABF/DRR: Fictitious mass[" << i << "] is " << mass[i] << '\n';
     613          19 :     c1[i] = exp(-0.5 * friction[i] * dt);
     614          19 :     c2[i] = sqrt(ekbt[i] * (1.0 - c1[i] * c1[i]) / mass[i]);
     615             :   }
     616             : 
     617          31 :   for (size_t i = 0; i < ndims; ++i) {
     618             :     // Position output
     619          19 :     string comp = getPntrToArgument(i)->getName() + "_fict";
     620          19 :     addComponentWithDerivatives(comp);
     621          19 :     if (getPntrToArgument(i)->isPeriodic()) {
     622             :       string a, b;
     623             :       double c, d;
     624          16 :       getPntrToArgument(i)->getDomain(a, b);
     625          16 :       getPntrToArgument(i)->getDomain(c, d);
     626          16 :       componentIsPeriodic(comp, a, b);
     627          16 :       delim[i].setPeriodicity(c, d);
     628             :       zdelim[i].setPeriodicity(c, d);
     629             :     } else {
     630           3 :       componentIsNotPeriodic(comp);
     631             :     }
     632          19 :     fictValue[i] = getPntrToComponent(comp);
     633             :     // Velocity output
     634          38 :     comp = getPntrToArgument(i)->getName() + "_vfict";
     635          19 :     addComponent(comp);
     636          19 :     componentIsNotPeriodic(comp);
     637          19 :     vfictValue[i] = getPntrToComponent(comp);
     638             :     // Bias force from eABF/DRR output
     639          38 :     comp = getPntrToArgument(i)->getName() + "_biasforce";
     640          19 :     addComponent(comp);
     641          19 :     componentIsNotPeriodic(comp);
     642          19 :     biasforceValue[i] = getPntrToComponent(comp);
     643             :     // Spring force output, useful for perform egABF and other analysis
     644          38 :     comp = getPntrToArgument(i)->getName() + "_springforce";
     645          19 :     addComponent(comp);
     646          19 :     componentIsNotPeriodic(comp);
     647          19 :     springforceValue[i] = getPntrToComponent(comp);
     648             :     // Position output, no pbc-aware
     649          38 :     comp = getPntrToArgument(i)->getName() + "_fictNoPBC";
     650          19 :     addComponent(comp);
     651          19 :     componentIsNotPeriodic(comp);
     652          19 :     fictNoPBCValue[i] = getPntrToComponent(comp);
     653             :   }
     654             : 
     655          12 :   if (outputprefix.length() == 0) {
     656           0 :     outputprefix = getLabel();
     657             :   }
     658             :   // Support multiple replica
     659          12 :   string replica_suffix = plumed.getSuffix();
     660          12 :   if (replica_suffix.empty() == false) {
     661           4 :     outputprefix = outputprefix + replica_suffix;
     662             :   }
     663          12 :   outputname = outputprefix + ".drrstate";
     664          12 :   cptname = outputprefix + ".cpt.drrstate";
     665             : 
     666          12 :   if (!isRestart) {
     667             :     // If you want to use on-the-fly text output for CZAR and naive estimator,
     668             :     // you should turn it to true first!
     669          11 :     ABFGrid = ABF(delim, ".abf", fullsamples, maxFactors, textoutput);
     670             :     // Just initialize it even useCZARestimator is off.
     671          11 :     CZARestimator = CZAR(zdelim, ".czar", kbt, textoutput);
     672          11 :     log << "eABF/DRR: The init function of the grid is finished." << '\n';
     673             :   } else {
     674             :     // ABF Parametres are not saved in binary files
     675             :     // So manully set them up
     676           1 :     ABFGrid.setParameters(fullsamples, maxFactors);
     677             :   }
     678          12 :   if (useCZARestimator) {
     679          12 :     log << "eABF/DRR: Using corrected z-average restraint estimator of gradients" << '\n';
     680          12 :     log << "  Bibliography " << plumed.cite("Lesage, Lelièvre, Stoltz and Hénin, "
     681          24 :                                             "J. Phys. Chem. B 3676, 121 (2017)");
     682          24 :     log << plumed.cite("Darve and Pohorille, J. Chem. Phys. 9169, 115 (2001)") << '\n';
     683             :   }
     684          12 :   if (useUIestimator) {
     685           9 :     log << "eABF/DRR: Using umbrella integration(Zheng and Yang's) estimator "
     686             :         "of gradients."
     687           9 :         << '\n';
     688           9 :     log << "eABF/DRR: The UI estimator code is contributed by Haohao Fu."
     689           9 :         << '\n';
     690           9 :     log << "  Bibliography " << plumed.cite(
     691          18 :           "Fu, Shao, Chipot and Cai, J. Chem. Theory Comput. 3506, 12 (2016)");
     692           9 :     log << plumed.cite("Zheng and Yang, J. Chem. Theory Comput. 810, 8 (2012)");
     693           9 :     log << plumed.cite("Darve and Pohorille, J. Chem. Phys. 9169, 115 (2001)") << '\n';
     694           9 :     vector<double> lowerboundary(zdelim.size(), 0);
     695           9 :     vector<double> upperboundary(zdelim.size(), 0);
     696           9 :     vector<double> width(zdelim.size(), 0);
     697          24 :     for (size_t i = 0; i < zdelim.size(); ++i) {
     698          15 :       lowerboundary[i] = zdelim[i].getMin();
     699          15 :       upperboundary[i] = zdelim[i].getMax();
     700          15 :       width[i] = zdelim[i].getWidth();
     701             :     }
     702           9 :     vector<string> input_filename;
     703             :     bool uirestart = false;
     704           9 :     if (isRestart && (uirprefix.length() != 0)) {
     705           1 :       input_filename.push_back(uirprefix);
     706             :       uirestart = true;
     707             :     }
     708           9 :     if (isRestart && (uirprefix.length() == 0)) {
     709           0 :       input_filename.push_back(outputprefix);
     710             :     }
     711           9 :     eabf_UI = UIestimator::UIestimator(
     712           9 :                 lowerboundary, upperboundary, width, kappa, outputprefix, int(outputfreq),
     713          18 :                 uirestart, input_filename, kbt / getKBoltzmann());
     714           9 :   }
     715          24 : }
     716             : 
     717         123 : void DynamicReferenceRestraining::calculate() {
     718         123 :   if (firsttime) {
     719          28 :     for (size_t i = 0; i < ndims; ++i) {
     720          17 :       fict[i] = getArgument(i);
     721          17 :       if(reflectingWall[i] && (fict[i]>=delim[i].getMax() || fict[i]<=delim[i].getMin())) {
     722           0 :         error("eABF/DRR: initial position not in the range of [gmin, gmax]!");
     723             :       }
     724             :     }
     725          11 :     firsttime = false;
     726             :   }
     727         123 :   if (withExternalForce == false) {
     728             :     double ene = 0.0;
     729         294 :     for (size_t i = 0; i < ndims; ++i) {
     730         183 :       real[i] = getArgument(i);
     731         183 :       springlength[i] = difference(i, fict[i], real[i]);
     732         183 :       fictNoPBC[i] = real[i] - springlength[i];
     733         183 :       double f = -kappa[i] * springlength[i];
     734         183 :       ffict_measured[i] = -f;
     735         183 :       ene += 0.5 * kappa[i] * springlength[i] * springlength[i];
     736         183 :       setOutputForce(i, f);
     737         183 :       ffict[i] = -f;
     738         183 :       fict[i] = fictValue[i]->bringBackInPbc(fict[i]);
     739         183 :       fictValue[i]->set(fict[i]);
     740         183 :       vfictValue[i]->set(vfict_laststep[i]);
     741         183 :       springforceValue[i]->set(ffict_measured[i]);
     742         183 :       fictNoPBCValue[i]->set(fictNoPBC[i]);
     743             :     }
     744         111 :     setBias(ene);
     745         111 :     ABFGrid.getbias(fict, ffict_measured, fbias);
     746             :   } else {
     747          36 :     for (size_t i = 0; i < ndims; ++i) {
     748          24 :       real[i] = getArgument(i);
     749          24 :       ffict_measured[i] = externalForceValue[i]->get();
     750          24 :       if (withExternalFict) {
     751          24 :         fictNoPBC[i] = externalFictValue[i]->get();
     752             :       }
     753          24 :       springforceValue[i]->set(ffict_measured[i]);
     754          24 :       fictNoPBCValue[i]->set(fictNoPBC[i]);
     755             :     }
     756          12 :     ABFGrid.getbias(real, ffict_measured, fbias);
     757          12 :     if (!nobias) {
     758           0 :       for (size_t i = 0; i < ndims; ++i) {
     759           0 :         setOutputForce(i, fbias[i]);
     760             :       }
     761             :     }
     762             :   }
     763         123 : }
     764             : 
     765         123 : void DynamicReferenceRestraining::update() {
     766         123 :   const long long int step_now = getStep();
     767         123 :   if (step_now != 0) {
     768         111 :     if ((step_now % int(outputfreq)) == 0) {
     769          15 :       save(outputname, step_now);
     770          15 :       if (textoutput) {
     771          10 :         ABFGrid.writeAll(outputprefix, fmt_);
     772          10 :         if (useCZARestimator) {
     773          10 :           CZARestimator.writeAll(outputprefix, fmt_);
     774          10 :           CZARestimator.writeZCountZGrad(outputprefix);
     775             :         }
     776             :       }
     777             :     }
     778         111 :     if (historyfreq > 0 && (step_now % int(historyfreq)) == 0) {
     779           4 :       if (textoutput) {
     780             :         const string filename =
     781           4 :           outputprefix + ".another.drrstate";
     782           4 :         save(filename, step_now);
     783             :         const string textfilename =
     784           4 :           mergeHistoryFiles ? (outputprefix + ".hist") : (outputprefix + "." + std::to_string(step_now));
     785           4 :         ABFGrid.writeAll(textfilename, fmt_, mergeHistoryFiles);
     786           4 :         if (useCZARestimator) {
     787           4 :           CZARestimator.writeAll(textfilename, fmt_, mergeHistoryFiles);
     788           4 :           CZARestimator.writeZCountZGrad(textfilename, mergeHistoryFiles);
     789             :         }
     790             :       } else {
     791             :         const string filename =
     792           0 :           outputprefix + "." + std::to_string(step_now) + ".drrstate";
     793           0 :         save(filename, step_now);
     794             :       }
     795             :     }
     796         111 :     if (getCPT()) {
     797           0 :       log << "eABF/DRR: The MD engine is writing checkpoint so we also write a "
     798             :           "DRR state file at step: "
     799           0 :           << step_now << ".\n";
     800           0 :       save(cptname, step_now);
     801             :     }
     802             :   }
     803         123 :   if (withExternalForce == false) {
     804         294 :     for (size_t i = 0; i < ndims; ++i) {
     805         183 :       real[i] = getArgument(i);
     806         183 :       springlength[i] = difference(i, fict[i], real[i]);
     807         183 :       ffict_measured[i] = kappa[i] * springlength[i];
     808             :     }
     809         111 :     ABFGrid.store_force(fict, ffict_measured);
     810             :   } else {
     811          36 :     for (size_t i = 0; i < ndims; ++i) {
     812          24 :       real[i] = getArgument(i);
     813          24 :       ffict_measured[i] = externalForceValue[i]->get();
     814             :     }
     815          12 :     ABFGrid.store_force(real, ffict_measured);
     816             :   }
     817         123 :   if (useCZARestimator) {
     818         123 :     CZARestimator.store(real, ffict_measured);
     819             :   }
     820         123 :   if (useUIestimator) {
     821          87 :     eabf_UI.update_output_filename(outputprefix);
     822          87 :     eabf_UI.update(int(step_now), real, fictNoPBC);
     823             :   }
     824         123 :   if (withExternalForce == false) {
     825         294 :     for (size_t i = 0; i < ndims; ++i) {
     826             :       // consider additional forces on the fictitious particle
     827             :       // (e.g. MetaD stuff)
     828         183 :       ffict[i] += fictValue[i]->getForce();
     829         183 :       if (!nobias) {
     830         183 :         ffict[i] += fbias[i];
     831             :       }
     832         183 :       biasforceValue[i]->set(fbias[i]);
     833             :       // update velocity (half step)
     834         183 :       vfict[i] += ffict[i] * 0.5 * dt / mass[i];
     835             :       // thermostat (half step)
     836         183 :       vfict[i] = c1[i] * vfict[i] + c2[i] * rand.Gaussian();
     837             :       // save full step velocity to be dumped at next step
     838         183 :       vfict_laststep[i] = vfict[i];
     839             :       // thermostat (half step)
     840         183 :       vfict[i] = c1[i] * vfict[i] + c2[i] * rand.Gaussian();
     841             :       // update velocity (half step)
     842         183 :       vfict[i] += ffict[i] * 0.5 * dt / mass[i];
     843             :       // update position (full step)
     844         183 :       fict[i] += vfict[i] * dt;
     845             :       // reflecting boundary
     846         183 :       if (reflectingWall[i]) {
     847          24 :         if (fict[i]<delim[i].getMin()) {
     848           1 :           fict[i]=delim[i].getMin()+(delim[i].getMin()-fict[i]);
     849           1 :           vfict[i]*=-1.0;
     850          23 :         } else if (fict[i]>delim[i].getMax()) {
     851           0 :           fict[i]=delim[i].getMax()-(fict[i]-delim[i].getMax());
     852           0 :           vfict[i]*=-1.0;
     853             :         }
     854             :       }
     855             :     }
     856             :   }
     857         123 : }
     858             : 
     859          19 : void DynamicReferenceRestraining::save(const string &filename,
     860             :                                        long long int step) {
     861          19 :   std::ofstream out;
     862          19 :   out.open(filename.c_str(), std::ios::binary);
     863          19 :   boost::archive::binary_oarchive oa(out);
     864          19 :   oa << step << fict << vfict << vfict_laststep << ffict << ABFGrid
     865          19 :      << CZARestimator;
     866          19 :   out.close();
     867          19 : }
     868             : 
     869           1 : void DynamicReferenceRestraining::load(const string &rfile_prefix) {
     870           1 :   string replica_suffix = plumed.getSuffix();
     871             :   string filename;
     872           1 :   if (replica_suffix.empty() == true) {
     873           2 :     filename = rfile_prefix + ".drrstate";
     874             :   } else {
     875           0 :     filename = rfile_prefix + "." + replica_suffix + ".drrstate";
     876             :   }
     877           1 :   std::ifstream in;
     878             :   long long int step;
     879           1 :   in.open(filename.c_str(), std::ios::binary);
     880           1 :   log << "eABF/DRR: Read restart file: " << filename << '\n';
     881           1 :   boost::archive::binary_iarchive ia(in);
     882           1 :   ia >> step >> fict >> vfict >> vfict_laststep >> ffict >> ABFGrid >>
     883           1 :      CZARestimator;
     884           1 :   in.close();
     885           1 :   log << "eABF/DRR: Restart at step: " << step << '\n';
     886           1 :   backupFile(filename);
     887           2 : }
     888             : 
     889           1 : void DynamicReferenceRestraining::backupFile(const string &filename) {
     890             :   bool isSuccess = false;
     891             :   long int i = 0;
     892           2 :   while (!isSuccess) {
     893             :     // If libstdc++ support C++17 we can simplify following code.
     894           3 :     const string bckname = "bck." + filename + "." + std::to_string(i);
     895           1 :     if (is_file_exist(bckname.c_str())) {
     896           0 :       ++i;
     897             :     } else {
     898           1 :       log << "eABF/DRR: Backup original restart file to " << bckname << '\n';
     899           1 :       std::ifstream src(filename.c_str(), std::ios::binary);
     900           1 :       std::ofstream dst(bckname.c_str(), std::ios::binary);
     901           1 :       dst << src.rdbuf();
     902           1 :       src.close();
     903           1 :       dst.close();
     904             :       isSuccess = true;
     905           1 :     }
     906             :   }
     907           1 : }
     908             : 
     909             : // Copy from
     910             : // stackoverflow(https://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c)
     911           1 : bool DynamicReferenceRestraining::is_file_exist(const char *fileName) {
     912           1 :   std::ifstream infile(fileName);
     913           1 :   return infile.good();
     914           1 : }
     915             : }
     916             : }
     917             : 
     918             : #endif

Generated by: LCOV version 1.16