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

Generated by: LCOV version 1.16