LCOV - code coverage report
Current view: top level - fisst - FISST.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 254 317 80.1 %
Date: 2026-03-30 13:16:06 Functions: 18 22 81.8 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             : Copyright (c) 2020 of Glen Hocky
       3             : 
       4             : The FISST module is free software: you can redistribute it and/or modify
       5             : it under the terms of the GNU Lesser General Public License as published by
       6             : the Free Software Foundation, either version 3 of the License, or
       7             : (at your option) any later version.
       8             : 
       9             : The FISST module is distributed in the hope that it will be useful,
      10             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : GNU Lesser General Public License for more details.
      13             : 
      14             : You should have received a copy of the GNU Lesser General Public License
      15             : along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      16             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      17             : #include "bias/Bias.h"
      18             : #include "core/ActionRegister.h"
      19             : #include "core/Atoms.h"
      20             : #include "core/PlumedMain.h"
      21             : #include "tools/File.h"
      22             : #include "tools/Matrix.h"
      23             : #include "tools/Random.h"
      24             : #include "legendre_rule_fast.h"
      25             : 
      26             : #include <iostream>
      27             : 
      28             : 
      29             : using namespace PLMD;
      30             : using namespace bias;
      31             : 
      32             : //namespace is lowercase to match
      33             : //module names being all lowercase
      34             : 
      35             : namespace PLMD {
      36             : namespace fisst {
      37             : 
      38             : //+PLUMEDOC FISSTMOD_BIAS FISST
      39             : /*
      40             : Compute and apply the optimal linear force on an observable to enhance sampling of conformational distributions over a range of applied forces.
      41             : 
      42             : This method is described in \cite Hartmann-FISST-2019
      43             : 
      44             : If the system's Hamiltonian is given by:
      45             : \f[
      46             :     H(\vec{p},\vec{q}) = \sum_{j} \frac{p_j^2}{2m_j} + U(\vec{q}),
      47             : \f]
      48             : 
      49             : This bias modifies the Hamiltonian to be:
      50             : \f[
      51             :   H'(\vec{p},\vec{q}) = H(\vec{p},\vec{q}) - \bar{F} Q
      52             : \f]
      53             : 
      54             : where for CV \f$Q\f$, a coupling constant \f${\bar{F}}\f$ is determined
      55             : adaptively according to the FISST algorithm.
      56             : 
      57             : Specifically,
      58             : \f[
      59             : \bar{F}(Q)=\frac{ \int_{F_{min}}^{F_{max}} e^{\beta F Q(\vec{q})} \omega(F) F dF}{\int_{F_{min}}^{F_{max}} e^{\beta F Q(\vec{q})} \omega(F) dF},
      60             : \f]
      61             : 
      62             : where \f$\vec{q}\f$ are the molecular coordinates of the system, and \f$w(F)\f$ is a weighting function that is learned on the fly for each force by the FISST algorithm (starting from an initial weight distribution, uniform by default).
      63             : 
      64             : The target for \f$w(F)=1/Z_q(F)\f$, where
      65             : \f[
      66             :     Z_q(F) \equiv \int d\vec{q} e^{-\beta U(\vec{q}) + \beta F Q(\vec{q})}.
      67             : \f]
      68             : 
      69             : FISST also computes and writes Observable Weights \f$W_F(\vec{q}_t)\f$ for a molecular configuration at time \f$t\f$, so that averages of other quantities \f$A(\vec{q})\f$ can be reconstructed later at different force values (over a trajectory with \f$T\f$ samples):
      70             : \f[
      71             :     \langle A \rangle_F = \frac{1}{T} \sum_t W_F(\vec{q}_t) A(\vec{q}_t).
      72             : \f]
      73             : 
      74             : 
      75             : \par Examples
      76             : 
      77             : In the following example, an adaptive restraint is learned to bias the distance between two atoms in a system, for a force range of 0-100 pN.
      78             : 
      79             : \plumedfile
      80             : UNITS LENGTH=A TIME=fs ENERGY=kcal/mol
      81             : 
      82             : b1: GROUP ATOMS=1
      83             : b2: GROUP ATOMS=12
      84             : 
      85             : dend: DISTANCE ATOMS=b1,b2
      86             : 
      87             : #The conversion factor is 69.4786 pN = 1 kcal/mol/Angstrom
      88             : 
      89             : #0 pN to 100 pN
      90             : f: FISST MIN_FORCE=0 MAX_FORCE=1.44 PERIOD=100 NINTERPOLATE=31 ARG=dend OUT_RESTART=pull.restart.txt OUT_OBSERVABLE=pull.observable.txt OBSERVABLE_FREQ=1000
      91             : 
      92             : PRINT ARG=dend,f.dend_fbar,f.bias,f.force2 FILE=pull.colvar.txt STRIDE=1000
      93             : \endplumedfile
      94             : 
      95             : 
      96             : */
      97             : //+ENDPLUMEDOC
      98             : 
      99             : 
     100             : class FISST : public Bias {
     101             : 
     102             : 
     103             : private:
     104             :   /*We will get this and store it once, since on-the-fly changing number of CVs will be fatal*/
     105             :   const unsigned int ncvs_;
     106             :   std::vector<double> center_;
     107             :   std::vector<double> current_avg_force_;
     108             : 
     109             :   std::vector<double> forces_;
     110             :   std::vector<double> force_weight_;
     111             :   std::vector<double> gauss_weight_;
     112             :   std::vector<double> partition_estimate_;
     113             :   std::vector<double> observable_weight_;
     114             : 
     115             :   std::string in_restart_name_;
     116             :   std::string out_restart_name_;
     117             :   std::string out_observable_name_;
     118             :   std::string fmt_;
     119             :   std::string initial_weight_dist_;
     120             :   OFile out_restart_;
     121             :   OFile out_observable_;
     122             :   IFile in_restart_;
     123             :   bool b_freeze_;
     124             :   bool b_adaptive_;
     125             :   bool b_restart_;
     126             :   bool b_write_restart_;
     127             :   bool b_write_observable_;
     128             :   bool b_first_restart_sample_;
     129             :   int period_;
     130             :   int reset_period_;
     131             :   int observable_freq_;
     132             :   int n_interpolation_;
     133             :   int n_samples_;
     134             :   double kbt_;
     135             :   double beta_;
     136             :   //change min_force and max_force to vectors if going to do more than one cv
     137             :   double max_force_;
     138             :   double min_force_;
     139             :   double initial_weight_rate_;
     140             :   double threshold_;
     141             :   Random rand_;
     142             : 
     143             : 
     144             :   Value* value_force2_;
     145             :   void readInRestart();
     146             :   void NormalizeForceWeights();
     147             :   /*setup output restart*/
     148             :   void setupOutRestart();
     149             :   void setupOutObservable();
     150             :   /*write output restart*/
     151             :   void writeOutRestart();
     152             :   void writeOutObservable();
     153             :   void update_statistics();
     154             :   void update_bias();
     155             :   void apply_bias();
     156             :   void compute_observable_weight();
     157             : 
     158             : public:
     159             :   explicit FISST(const ActionOptions&);
     160             :   void calculate();
     161             :   void update();
     162             :   void turnOnDerivatives();
     163             :   static void registerKeywords(Keywords& keys);
     164             :   ~FISST();
     165             : };
     166             : 
     167       13789 : PLUMED_REGISTER_ACTION(FISST,"FISST")
     168             : 
     169           6 : void FISST::registerKeywords(Keywords& keys) {
     170           6 :   Bias::registerKeywords(keys);
     171           6 :   keys.use("ARG");
     172          12 :   keys.add("compulsory","PERIOD","Steps corresponding to the learning rate");
     173          12 :   keys.add("optional","RESET_PERIOD","Reset the learning statistics every time this number of steps comes around.");
     174          12 :   keys.add("compulsory","NINTERPOLATE","Number of grid points on which to do interpolation.");
     175          12 :   keys.add("compulsory","MIN_FORCE","Minimum force (per CV) to use for sampling. Units: [Energy]/[CV]  (can be negative).");
     176          12 :   keys.add("compulsory","MAX_FORCE","Maximum force (per CV) to use for sampling.");
     177          12 :   keys.add("compulsory","CENTER","0","The CV value at which the applied bias energy will be zero");
     178          12 :   keys.add("optional","KBT","The system temperature in units of KB*T. If not provided will be taken from MD code (if available)");
     179             : 
     180          12 :   keys.add("optional","INITIAL_WEIGHT_DIST","Starting distribution for the force weights (options: UNIFORM, EXP, GAUSS).");
     181          12 :   keys.add("optional","INITIAL_WEIGHT_RATE","Rate of decay for exponential and gaussian distributions. W(F)~exp(-r |F|^d).");
     182             : 
     183          12 :   keys.add("optional","RESTART_FMT","the format that should be used to output real numbers in FISST restarts.");
     184          12 :   keys.add("optional","OUT_RESTART","Output file for all information needed to continue FISST simulation."
     185             :            "If you have the RESTART directive set (global or for FISST), this file will be appended to."
     186             :            "Note that the header will be printed again if appending.");
     187          12 :   keys.add("optional","IN_RESTART","Read this file to continue an FISST simulation. "
     188             :            "If same as OUT_RESTART and you have not set the RESTART directive, the file will be backed-up and overwritten with new output."
     189             :            "If you do have the RESTART flag set and it is the same name as OUT_RESTART, this file will be appended.");
     190          12 :   keys.add("optional","OUT_OBSERVABLE","Output file putting weights needed to compute observables at different force values."
     191             :            "If you have the RESTART directive set (global or for FISST), this file will be appended to. "
     192             :            "Note that the header will be printed again if appending.");
     193          12 :   keys.add("optional","OBSERVABLE_FREQ","How often to write out observable weights (default=period).");
     194          12 :   keys.addFlag("FREEZE",false,"Fix bias weights at current level (only used for restarting).");
     195           6 :   keys.use("RESTART");
     196          12 :   keys.addOutputComponent("force2","default","squared value of force from the bias.");
     197          12 :   keys.addOutputComponent("_fbar","default", "For each named CV biased, there will be a corresponding output CV_fbar storing the current linear bias prefactor.");
     198           6 : }
     199             : 
     200           2 : FISST::FISST(const ActionOptions&ao):
     201             :   PLUMED_BIAS_INIT(ao),
     202           2 :   ncvs_(getNumberOfArguments()),
     203           0 :   current_avg_force_(ncvs_,0.0),
     204           2 :   center_(ncvs_,0.0),
     205             :   //change min_force and max_force to vectors if going to do more than one cv
     206           2 :   min_force_(0.0),
     207           2 :   max_force_(0.0),
     208           2 :   in_restart_name_(""),
     209           2 :   out_restart_name_(""),
     210           2 :   out_observable_name_(""),
     211           2 :   fmt_("%e"),
     212           2 :   b_freeze_(false),
     213           2 :   b_restart_(false),
     214           2 :   b_write_restart_(false),
     215           2 :   b_write_observable_(false),
     216           2 :   b_first_restart_sample_(true),
     217           2 :   n_interpolation_(0),
     218           2 :   n_samples_(0),
     219           2 :   initial_weight_rate_(0),
     220           2 :   initial_weight_dist_("UNIFORM"),
     221           2 :   period_(0),
     222           2 :   reset_period_(0),
     223           2 :   observable_freq_(0),
     224           2 :   kbt_(0.0),
     225           6 :   value_force2_(NULL) {
     226           2 :   if(ncvs_==0) {
     227           0 :     error("Must specify at least one CV with ARG");
     228             :   }
     229             : 
     230             :   //temporary
     231           2 :   if(ncvs_>1) {
     232           0 :     error("FISST only supports using one CV right now");
     233             :   }
     234             : 
     235           2 :   addComponent("force2");
     236           2 :   componentIsNotPeriodic("force2");
     237           2 :   value_force2_ = getPntrToComponent("force2");
     238             : 
     239           4 :   for(unsigned int i = 0; i<ncvs_; i++) {
     240           2 :     std::string comp = getPntrToArgument(i)->getName() + "_fbar";
     241           2 :     addComponent(comp);
     242           2 :     componentIsNotPeriodic(comp);
     243             :   }
     244             : 
     245           2 :   parseVector("CENTER",center_);
     246             :   //change min_force and max_force to vectors if going to do more than one cv
     247           2 :   parse("MIN_FORCE",min_force_);
     248           2 :   parse("MAX_FORCE",max_force_);
     249           2 :   parse("PERIOD",period_);
     250           2 :   parse("RESET_PERIOD",reset_period_);
     251           2 :   parse("INITIAL_WEIGHT_DIST",initial_weight_dist_);
     252           2 :   parse("INITIAL_WEIGHT_RATE",initial_weight_rate_);
     253           2 :   parse("OBSERVABLE_FREQ",observable_freq_);
     254           2 :   parse("NINTERPOLATE",n_interpolation_);
     255           2 :   parseFlag("FREEZE",b_freeze_);
     256           2 :   parse("KBT",kbt_);
     257           2 :   parse("RESTART_FMT", fmt_);
     258           2 :   fmt_ = " " + fmt_;//add space since parse strips them
     259           2 :   parse("OUT_RESTART",out_restart_name_);
     260           2 :   parse("OUT_OBSERVABLE",out_observable_name_);
     261           2 :   parse("IN_RESTART",in_restart_name_);
     262           2 :   checkRead();
     263             : 
     264           2 :   if(center_.size() != ncvs_) {
     265           0 :     error("Must have same number of CENTER arguments as ARG arguments");
     266             :   }
     267             : 
     268           2 :   if(in_restart_name_ != "") {
     269           0 :     b_restart_ = true;
     270           0 :     log.printf("  reading simulation information from file: %s\n",in_restart_name_.c_str());
     271           0 :     readInRestart();
     272             :   } else {
     273             : 
     274           2 :     if(! kbt_ > 0.0) {
     275           2 :       kbt_ = plumed.getAtoms().getKbT();
     276             :     }
     277             : 
     278             :     //in driver, this results in kbt of 0
     279           2 :     if(kbt_ == 0) {
     280           0 :       error("  Unable to determine valid kBT. "
     281             :             "Could be because you are runnning from driver or MD didn't give temperature.\n"
     282             :             "Consider setting temperature manually with the KBT keyword.");
     283             :     }
     284             : 
     285           2 :     log.printf("  kBT = %f\n",kbt_);
     286           2 :     log.printf("  Updating with a time scale of %i steps\n",period_);
     287             : 
     288           2 :     log.printf("  Using centers for CVs of:");
     289           4 :     for(unsigned int i = 0; i< ncvs_; i++) {
     290           2 :       log.printf(" %f ",center_[i]);
     291             :     }
     292           2 :     log.printf("\n");
     293           2 :     observable_weight_.resize(n_interpolation_);
     294          64 :     for(unsigned int i = 0; i<n_interpolation_; i++) {
     295          62 :       observable_weight_[i] = 1.0;
     296             :     }
     297             : 
     298           2 :     forces_.resize(n_interpolation_);
     299           2 :     force_weight_.resize(n_interpolation_);
     300             :     //using code from the MIST project
     301           2 :     gauss_weight_.resize(n_interpolation_);
     302           2 :     legendre_compute_glr(n_interpolation_, &forces_[0], &gauss_weight_[0]);
     303           2 :     rescale(min_force_, max_force_, n_interpolation_, &forces_[0], &gauss_weight_[0]);
     304             : 
     305           2 :     log.printf("Using weight distribution %s with rate %f\n",initial_weight_dist_.c_str(),initial_weight_rate_);
     306           2 :     if(initial_weight_dist_ == "UNIFORM" ) {
     307          32 :       for(unsigned int i = 0; i<n_interpolation_; i++) {
     308          31 :         force_weight_[i] = 1.0;
     309             :       }
     310           1 :     } else if (initial_weight_dist_ == "EXP" ) {
     311          32 :       for(unsigned int i = 0; i<n_interpolation_; i++) {
     312          31 :         force_weight_[i] = exp(-fabs(forces_[i])*initial_weight_rate_);
     313             :       }
     314           0 :     } else if (initial_weight_dist_ == "GAUSS" ) {
     315           0 :       for(unsigned int i = 0; i<n_interpolation_; i++) {
     316           0 :         force_weight_[i] = exp(-pow(forces_[i],2)*initial_weight_rate_);
     317             :       }
     318             :     } else {
     319           0 :       error("  Specified weight distribution is not from the allowed list.");
     320             : 
     321             :     }
     322             : 
     323           2 :     partition_estimate_.resize(n_interpolation_);
     324           2 :     NormalizeForceWeights();
     325             :     double sum = 0.0;
     326          64 :     for(unsigned int i = 0; i<n_interpolation_; i++) {
     327             :       //setting partition estimate as 1/w_i
     328          62 :       partition_estimate_[i] = 1/force_weight_[i];
     329          62 :       log.printf("force/gauss weight/force_weight: %i %f %f %f\n",i,forces_[i],gauss_weight_[i],force_weight_[i]);
     330          62 :       sum+=gauss_weight_[i]*force_weight_[i];
     331             :     }
     332           2 :     log.printf("--Sum_i w_i g_i: %f\n",sum);
     333             : 
     334             :   }
     335             : 
     336             :   //set inverse temperature
     337           2 :   beta_ = 1/kbt_;
     338             : 
     339           2 :   if(b_freeze_ && b_restart_) {
     340           0 :     log.printf("  freezing weights read in from the restart file\n");
     341             :   }
     342             : 
     343           2 :   if(out_restart_name_.length()>0) {
     344           2 :     log.printf("  writing restart information every %i steps to file %s with format %s\n",abs(period_),out_restart_name_.c_str(), fmt_.c_str());
     345           2 :     b_write_restart_ = true;
     346           2 :     setupOutRestart();
     347             :   }
     348           2 :   if(out_observable_name_.length()>0) {
     349           2 :     if(observable_freq_==0) {
     350           2 :       observable_freq_ = period_;
     351             :     }
     352           2 :     log.printf("  writing observable information every %i steps to file %s with format %s\n",observable_freq_,out_observable_name_.c_str(), fmt_.c_str());
     353           2 :     b_write_observable_ = true;
     354           2 :     setupOutObservable();
     355             :   }
     356             : 
     357             :   //add citation later:
     358             :   //log<<"  Bibliography "<<plumed.cite("")<<"\n";
     359           2 : }
     360             : 
     361          12 : void FISST::NormalizeForceWeights() {
     362             :   double denom = 0.0;
     363             : 
     364         384 :   for(unsigned i=0; i<n_interpolation_; i++) {
     365         372 :     denom += gauss_weight_[i] * force_weight_[i];
     366             :   }
     367             : 
     368         384 :   for(unsigned i=0; i<n_interpolation_; i++) {
     369         372 :     force_weight_[i] /= denom;
     370             :   }
     371          12 : }
     372             : 
     373           0 : void FISST::readInRestart() {
     374           0 :   in_restart_.open(in_restart_name_);
     375             : 
     376           0 :   if(in_restart_.FieldExist("kbt")) {
     377           0 :     in_restart_.scanField("kbt",kbt_);
     378             :   } else {
     379           0 :     error("No field 'kbt' in restart file");
     380             :   }
     381           0 :   log.printf("  with kBT = %f\n",kbt_);
     382             : 
     383           0 :   if(in_restart_.FieldExist("period")) {
     384           0 :     in_restart_.scanField("period",period_);
     385             :   } else {
     386           0 :     error("No field 'period' in restart file");
     387             :   }
     388           0 :   log.printf("  Updating every %i steps\n",period_);
     389             : 
     390             : //this one can be optional
     391           0 :   if(in_restart_.FieldExist("reset_period")) {
     392           0 :     in_restart_.scanField("reset_period",reset_period_);
     393             :   }
     394           0 :   log.printf("  Resetting statistics every %i steps\n",reset_period_);
     395             : 
     396           0 :   if(in_restart_.FieldExist("n_interpolation")) {
     397           0 :     in_restart_.scanField("n_interpolation",n_interpolation_);
     398             :   } else {
     399           0 :     error("No field 'n_interpolation' in restart file");
     400             :   }
     401             : 
     402           0 :   if(in_restart_.FieldExist("min_force")) {
     403           0 :     in_restart_.scanField("min_force",min_force_);
     404             :   } else {
     405           0 :     error("No field 'min_force' in restart file");
     406             :   }
     407           0 :   if(in_restart_.FieldExist("max_force")) {
     408           0 :     in_restart_.scanField("max_force",max_force_);
     409             :   } else {
     410           0 :     error("No field 'max_force' in restart file");
     411             :   }
     412           0 :   log.printf("  with forces from min_force=%e to max_force=%e over %i bins\n",min_force_,max_force_,n_interpolation_);
     413             : 
     414             :   unsigned int N = 0;
     415             :   std::string cv_name;
     416             :   double tmp, time;
     417             : 
     418           0 :   while(in_restart_.scanField("time",time)) {
     419           0 :     in_restart_.scanField("nsamples",n_samples_);
     420             : 
     421           0 :     observable_weight_.resize(n_interpolation_);
     422           0 :     partition_estimate_.resize(n_interpolation_);
     423           0 :     force_weight_.resize(n_interpolation_);
     424           0 :     gauss_weight_.resize(n_interpolation_);
     425           0 :     forces_.resize(n_interpolation_);
     426             : 
     427           0 :     for(unsigned int i = 0; i<ncvs_; ++i) {
     428             :       cv_name = getPntrToArgument(i)->getName();
     429           0 :       in_restart_.scanField(cv_name,tmp);
     430           0 :       for(unsigned int j =0; j<n_interpolation_; ++j) {
     431           0 :         in_restart_.scanField(cv_name + "_f"+std::to_string(j),forces_[j]);
     432           0 :         in_restart_.scanField(cv_name + "_g"+std::to_string(j),gauss_weight_[j]);
     433           0 :         in_restart_.scanField(cv_name + "_w"+std::to_string(j),force_weight_[j]);
     434           0 :         in_restart_.scanField(cv_name + "_z"+std::to_string(j),partition_estimate_[j]);
     435             :       }
     436             :     }
     437             :     N++;
     438             : 
     439           0 :     in_restart_.scanField();
     440             :   }
     441             : 
     442             :   double sum = 0.0;
     443           0 :   for(unsigned int j =0; j<n_interpolation_; ++j) {
     444             :     //clear observable weight, which will be set later
     445           0 :     observable_weight_[j] = 1.0;
     446             : 
     447             :     //setting partition estimate as 1/w_i
     448           0 :     log.printf("force/gauss weight/force_weight: %i %e %e %e\n",j,forces_[j],gauss_weight_[j],force_weight_[j]);
     449           0 :     sum+=gauss_weight_[j]*force_weight_[j];
     450             :   }
     451           0 :   log.printf("--Sum_i w_i g_i: %f\n",sum);
     452             : 
     453           0 :   in_restart_.close();
     454           0 : }
     455             : 
     456           2 : void FISST::setupOutObservable() {
     457           2 :   out_observable_.link(*this);
     458           2 :   out_observable_.fmtField(fmt_);
     459           2 :   out_observable_.open(out_observable_name_);
     460             :   out_observable_.setHeavyFlush();
     461             : 
     462           4 :   out_observable_.addConstantField("kbt").printField("kbt",kbt_);
     463           4 :   out_observable_.addConstantField("n_interpolation").printField("n_interpolation",n_interpolation_);
     464           4 :   out_observable_.addConstantField("period").printField("period",period_);
     465           4 :   out_observable_.addConstantField("min_force").printField("min_force",min_force_);
     466           4 :   out_observable_.addConstantField("max_force").printField("max_force",max_force_);
     467           2 : }
     468             : 
     469           2 : void FISST::setupOutRestart() {
     470           2 :   out_restart_.link(*this);
     471           2 :   out_restart_.fmtField(fmt_);
     472           2 :   out_restart_.open(out_restart_name_);
     473             :   out_restart_.setHeavyFlush();
     474             : 
     475           4 :   out_restart_.addConstantField("kbt").printField("kbt",kbt_);
     476           4 :   out_restart_.addConstantField("n_interpolation").printField("n_interpolation",n_interpolation_);
     477           4 :   out_restart_.addConstantField("period").printField("period",period_);
     478           2 :   if(reset_period_>0) {
     479           0 :     out_restart_.addConstantField("reset_period").printField("reset_period",reset_period_);
     480             :   }
     481           4 :   out_restart_.addConstantField("min_force").printField("min_force",min_force_);
     482           4 :   out_restart_.addConstantField("max_force").printField("max_force",max_force_);
     483           2 : }
     484             : 
     485          10 : void FISST::writeOutRestart() {
     486             :   std::string cv_name;
     487          10 :   out_restart_.printField("time",getTimeStep()*getStep());
     488          10 :   out_restart_.printField("nsamples",n_samples_);
     489             : 
     490          20 :   for(unsigned int i = 0; i<ncvs_; ++i) {
     491             :     cv_name = getPntrToArgument(i)->getName();
     492          10 :     double Q_i = difference(i, center_[i], getArgument(i));
     493          10 :     out_restart_.printField(cv_name,Q_i);
     494         320 :     for(int j = 0; j < n_interpolation_; j++ ) {
     495             : //have to update this for multiple cvs
     496         620 :       out_restart_.printField(cv_name + "_f"+std::to_string(j),forces_[j]);
     497         620 :       out_restart_.printField(cv_name + "_g"+std::to_string(j),gauss_weight_[j]);
     498         620 :       out_restart_.printField(cv_name + "_w"+std::to_string(j),force_weight_[j]);
     499         620 :       out_restart_.printField(cv_name + "_z"+std::to_string(j),partition_estimate_[j]);
     500             :     }
     501             :   }
     502          10 :   out_restart_.printField();
     503          10 : }
     504             : 
     505          10 : void FISST::writeOutObservable() {
     506             :   std::string cv_name;
     507          10 :   out_observable_.printField("time",getTimeStep()*getStep());
     508          10 :   out_observable_.printField("nsamples",n_samples_);
     509             : 
     510          20 :   for(unsigned int i = 0; i<ncvs_; ++i) {
     511             :     cv_name = getPntrToArgument(i)->getName();
     512          10 :     double Q_i = difference(i, center_[i], getArgument(i));
     513          10 :     out_observable_.printField(cv_name,Q_i);
     514          10 :     out_observable_.printField(cv_name + "_fbar",current_avg_force_[i]);
     515         320 :     for(int j = 0; j < n_interpolation_; j++ ) {
     516             : //have to update this for multiple cvs
     517         620 :       out_observable_.printField(cv_name + "_f"+std::to_string(j),forces_[j]);
     518         620 :       out_observable_.printField(cv_name + "_ow"+std::to_string(j),observable_weight_[j]);
     519             :     }
     520             :   }
     521          10 :   out_observable_.printField();
     522          10 : }
     523             : 
     524             : 
     525          10 : void FISST::calculate() {
     526          10 :   if(getStep() == 0 ) {
     527           2 :     if(b_write_restart_) {
     528           2 :       writeOutRestart();
     529             :     }
     530           2 :     if(b_write_observable_) {
     531           2 :       writeOutObservable();
     532             :     }
     533             :   }
     534             : 
     535          10 :   if(! b_freeze_) {
     536          10 :     if(b_restart_ && b_first_restart_sample_) {
     537             :       //dont' update statistics if restarting and first sample
     538           0 :       b_first_restart_sample_ = false;
     539             :     } else {
     540          10 :       update_statistics();
     541             :     }
     542             :   }
     543          10 :   update_bias();
     544          10 :   apply_bias();
     545             : 
     546             :   //check about writing restart file
     547          10 :   if(getStep()>0 && getStep()%period_==0) {
     548           8 :     if(b_write_restart_) {
     549           8 :       writeOutRestart();
     550             :     }
     551             :   }
     552          10 :   if(getStep()>0 && getStep()%observable_freq_==0) {
     553           8 :     if(b_write_observable_) {
     554           8 :       compute_observable_weight();
     555           8 :       writeOutObservable();
     556             :     }
     557             :   }
     558          10 : }
     559             : 
     560             : 
     561          10 : void FISST::apply_bias() {
     562             :   //Compute linear force as in "restraint"
     563             :   double ene = 0, totf2 = 0, cv, m, f;
     564             : 
     565          20 :   for(unsigned int i = 0; i < ncvs_; ++i) {
     566          10 :     cv = difference(i, center_[i], getArgument(i));
     567          10 :     double fbar = current_avg_force_[i];
     568          10 :     ene -= fbar*cv;
     569             :     setOutputForce(i,fbar);
     570          10 :     totf2 += fbar*fbar;
     571             : 
     572          10 :     std::string fbar_name_ = getPntrToArgument(i)->getName() + "_fbar";
     573          10 :     Value* fbar_ = getPntrToComponent(fbar_name_);
     574             :     fbar_->set(fbar);
     575             :   };
     576             : 
     577             :   setBias(ene);
     578          10 :   value_force2_->set(totf2);
     579             :   //log.flush();
     580          10 : }
     581             : 
     582          10 : void FISST::update_statistics()  {
     583             : //get stride is for multiple time stepping
     584          10 :   double dt=getTimeStep()*getStride();
     585          10 :   double h = dt/(period_*getTimeStep());
     586             :   double fbar_denum_integral = 0.0;
     587             : 
     588          10 :   int step = getStep();
     589          10 :   if(reset_period_>0 && step>0 && step%reset_period_==0) {
     590           0 :     n_samples_=1;
     591             :   } else {
     592          10 :     n_samples_++;
     593             :   }
     594          10 :   double d_n_samples = (double)n_samples_;
     595             : 
     596          20 :   for(unsigned int i = 0; i < ncvs_; ++i) {
     597          10 :     double Q_i = difference(i, center_[i], getArgument(i));
     598         320 :     for(unsigned int j=0; j<n_interpolation_; j++) {
     599             :       //if multiple cvs, these need to be updated to have 2 columns
     600         310 :       double f_j = forces_[j];
     601         310 :       double w_j = force_weight_[j];
     602         310 :       double g_j = gauss_weight_[j];
     603             : 
     604         310 :       fbar_denum_integral += g_j * w_j * exp(beta_*f_j * Q_i);
     605             :     }
     606             : 
     607         320 :     for(unsigned int j=0; j<n_interpolation_; j++) {
     608         310 :       double f_j = forces_[j];
     609         310 :       double sample_weight = exp(beta_*f_j * Q_i) / fbar_denum_integral;
     610             : 
     611         310 :       partition_estimate_[j] = sample_weight/d_n_samples + partition_estimate_[j]*(d_n_samples-1)/(d_n_samples);
     612             : 
     613         310 :       double w_jn = force_weight_[j];
     614         310 :       double z_jn = partition_estimate_[j];
     615             : 
     616         310 :       double w_jp1 = (1.0 - h) * w_jn + h / z_jn;
     617         310 :       force_weight_[j] = w_jp1;
     618             :     }
     619             :   }
     620             : 
     621             :   // make sure that the weights are normalised
     622          10 :   NormalizeForceWeights();
     623          10 : }
     624             : 
     625             : 
     626          10 : void FISST::update_bias() {
     627          20 :   for(unsigned int i = 0; i < ncvs_; ++i) {
     628          10 :     double Q_i = difference(i, center_[i], getArgument(i));
     629             :     double fbar_num_integral = 0.0;
     630             :     double fbar_denum_integral = 0.0;
     631             : 
     632         320 :     for(unsigned int j=0; j<n_interpolation_; j++ ) {
     633         310 :       double f_j = forces_[j];
     634         310 :       double w_j = force_weight_[j];
     635         310 :       double g_j = gauss_weight_[j];
     636             : 
     637         310 :       fbar_num_integral += g_j * f_j * w_j * exp(beta_*f_j*Q_i);
     638         310 :       fbar_denum_integral += g_j * w_j * exp(beta_*f_j*Q_i);
     639             :     }
     640             : 
     641          10 :     current_avg_force_[i] = fbar_num_integral/fbar_denum_integral;
     642             :   }
     643          10 : }
     644             : 
     645           8 : void FISST::compute_observable_weight() {
     646           8 :   double obs_num = (max_force_ - min_force_);
     647             : 
     648          16 :   for(unsigned int i = 0; i < ncvs_; ++i) {
     649           8 :     double Q_i = difference(i, center_[i], getArgument(i));
     650             : 
     651         256 :     for(unsigned int j=0; j<n_interpolation_; j++ ) {
     652         248 :       double z_j = partition_estimate_[j];
     653         248 :       double f_j = forces_[j];
     654             :       double denum_integral = 0.0;
     655             : 
     656        7936 :       for( unsigned int k=0; k<n_interpolation_; k++ ) {
     657        7688 :         double f_k = forces_[k];
     658        7688 :         double w_k = force_weight_[k];
     659        7688 :         double g_k = gauss_weight_[k];
     660             : 
     661        7688 :         denum_integral += g_k * w_k * exp(beta_*(f_k-f_j)*Q_i);
     662             :       }
     663         248 :       observable_weight_[j] = obs_num/(denum_integral*z_j);
     664             :     }
     665             :   }
     666           8 : }
     667             : 
     668             : 
     669             : 
     670          10 : void FISST::update() {
     671             :   //pass
     672          10 : }
     673             : 
     674           4 : FISST::~FISST() {
     675           2 :   out_restart_.close();
     676           2 :   out_observable_.close();
     677           6 : }
     678             : 
     679           0 : void FISST::turnOnDerivatives() {
     680             :   // do nothing
     681             :   // this is to avoid errors triggered when a bias is used as a CV
     682             :   // (This is done in ExtendedLagrangian.cpp)
     683           0 : }
     684             : 
     685             : 
     686             : }
     687             : }//close the 2 namespaces

Generated by: LCOV version 1.16