LCOV - code coverage report
Current view: top level - maze - Optimizer_Bias.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 94 96 97.9 %
Date: 2024-10-18 14:00:25 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             : Copyright (c) 2019 Jakub Rydzewski (jr@fizyka.umk.pl). All rights reserved.
       3             : 
       4             : See http://www.maze-code.github.io for more information.
       5             : 
       6             : This file is part of maze.
       7             : 
       8             : maze is free software: you can redistribute it and/or modify it under the
       9             : terms of the GNU Lesser General Public License as published by the Free
      10             : Software Foundation, either version 3 of the License, or (at your option)
      11             : any later version.
      12             : 
      13             : maze is distributed in the hope that it will be useful, but WITHOUT ANY
      14             : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      15             : FOR A PARTICULAR PURPOSE.
      16             : 
      17             : See the GNU Lesser General Public License for more details.
      18             : 
      19             : You should have received a copy of the GNU Lesser General Public License
      20             : along with maze. If not, see <https://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : 
      23             : /**
      24             :  * @file Optimizer_Bias.cpp
      25             :  *
      26             :  * @author J. Rydzewski (jr@fizyka.umk.pl)
      27             :  *
      28             :  * @code
      29             :  * @article{rydzewski2018finding,
      30             :  *   title={Finding Multiple Reaction Pathways of Ligand Unbinding},
      31             :  *   author={Rydzewski, J and Valsson, O},
      32             :  *   journal={arXiv preprint arXiv:1808.08089},
      33             :  *   year={2018}
      34             :  * }
      35             :  * @endcode
      36             :  */
      37             : 
      38             : #include "core/ActionRegister.h"
      39             : #include "core/PlumedMain.h"
      40             : 
      41             : #include "bias/Bias.h"
      42             : 
      43             : #include "Optimizer.h"
      44             : #include "Tools.h"
      45             : 
      46             : namespace PLMD {
      47             : namespace maze {
      48             : 
      49             : //+PLUMEDOC MAZE_BIAS MAZE_OPTIMIZER_BIAS
      50             : /*
      51             : 
      52             : Biases the ligand along the direction calculated by the chosen MAZE_OPTIMIZER.
      53             : 
      54             : OptimizerBias is a class deriving from Bias, and it is used to adaptively
      55             : bias a ligand-protein system toward an optimal solution found by the chosen
      56             : \ref MAZE_OPTIMIZER.
      57             : 
      58             : Remember to define the loss function (\ref MAZE_LOSS) and the optimizer
      59             : (\ref MAZE_OPTIMIZER) prior to the adaptive bias for the optimizer.
      60             : 
      61             : The adaptive bias potential is the following:
      62             : \f[
      63             :   V({\bf x}_t)=\alpha
      64             :     \left(wt -
      65             :       ({\bf x} - {\bf x}^*_{t-\tau})
      66             :       \cdot
      67             :       \frac{{\bf x}^*_t - {\bf x}_t}{\|{\bf x}^*_t-{\bf x}_t\|}
      68             :     \right)^2,
      69             : \f]
      70             : where \f${\bf x}^*_t\f$ is the optimal solution at time \f$t\f$, \f$w\f$ is the
      71             : biasing rate, \f$\tau\f$ is the interval at which the loss function is minimized,
      72             : and \f$\alpha\f$ is a scaled force constant.
      73             : 
      74             : \par Examples
      75             : 
      76             : In the following example the bias potential biases a ligand atom (which have to be
      77             : given as an argument) with the biasing rate equal to 0.02 A/ps, and the biasing
      78             : constant equal to 3.6 kcal/(mol A). It also takes an optimizer (see
      79             : \ref MAZE_OPTIMIZER).
      80             : 
      81             : \plumedfile
      82             : UNITS LENGTH=A TIME=ps ENERGY=kcal/mol
      83             : 
      84             : p: POSITION ATOM=2635 NOPBC
      85             : 
      86             : MAZE_OPTIMIZER_BIAS ...
      87             :   LABEL=bias
      88             : 
      89             :   ARG=p.x,p.y,p.z
      90             : 
      91             :   BIASING_RATE=0.02
      92             :   ALPHA=3.6
      93             : 
      94             :   OPTIMIZER=opt
      95             : ... MAZE_OPTIMIZER_BIAS
      96             : \endplumedfile
      97             : 
      98             : */
      99             : //+ENDPLUMEDOC
     100             : 
     101             : /**
     102             :  * @class OptimizerBias OptimizerBias.cpp "maze/OptimizerBias.cpp"
     103             :  *
     104             :  * @brief Adaptive bias for the maze optimizers.
     105             :  *
     106             :  * OptimizerBias is a class deriving from Bias, and it is used to adaptively
     107             :  * bias a system toward an optimal solution found by an optimizer.
     108             :  */
     109             : class OptimizerBias: public bias::Bias {
     110             : public:
     111             :   /**
     112             :    * Standard PLUMED2 constructor.
     113             :    *
     114             :    * @param ao ActionOptions&.
     115             :    */
     116             :   explicit OptimizerBias(const ActionOptions& ao);
     117             : 
     118             :   /**
     119             :    * Destructor.
     120             :    */
     121           3 :   ~OptimizerBias() { /* Nothing to do. */ }
     122             : 
     123             :   /**
     124             :    * Register PLUMED2 keywords.
     125             :    *
     126             :    * @param keys Keywords.
     127             :    */
     128             :   static void registerKeywords(Keywords& keys);
     129             : 
     130             :   /**
     131             :    * Calculate the adaptive biasing potential for ligand unbinding.
     132             :    */
     133             :   void calculate();
     134             : 
     135             : private:
     136             :   /**
     137             :    * Biased collective variable with Cartesian components, i.e., position,
     138             :    * center of mass.
     139             :    */
     140             :   std::vector<Value*> args_;
     141             : 
     142             :   /**
     143             :    * Pointer to the optimizer used to minimize the collective variable for
     144             :    * ligand unbinding.
     145             :    */
     146             :   Optimizer* optimizer_;
     147             :   std::vector<Optimizer*> opt_pntrs_;
     148             : 
     149             :   //! Adaptive bias potential and the corresponding force.
     150             :   double bias_;
     151             :   double force_;
     152             : 
     153             :   /*
     154             :    * Parameters of the adaptive biasing potential:
     155             :    *  alpha_            rescaled force constant
     156             :    *  biasing_speed     biasing rate
     157             :    *  biasing_stride    biasing stride
     158             :    *  biasing_direction biasing direction
     159             :    */
     160             : 
     161             :   //! Rescaled force constant.
     162             :   double alpha_;
     163             :   //! Biasing rate.
     164             :   double biasing_speed_;
     165             :   //! Biasing stride.
     166             :   int biasing_stride_;
     167             : 
     168             :   /**
     169             :    * Biasing direction is approximated by an optimal solution found by an
     170             :    * optimizer.
     171             :    */
     172             :   Vector biasing_direction_;
     173             : 
     174             :   //! Total distance traveled by biased atoms.
     175             :   double total_distance_;
     176             : 
     177             :   //! Previous value of the collective variable.
     178             :   Vector cv0_;
     179             : 
     180             :   /*
     181             :    * Pointers to PLUMED2 output components.
     182             :    */
     183             : 
     184             :   //! Biased collective variable components.
     185             :   Value* value_dir_x_;
     186             :   Value* value_dir_y_;
     187             :   Value* value_dir_z_;
     188             : 
     189             :   //! Values of the bias and its force.
     190             :   Value* value_bias_;
     191             :   Value* value_force_;
     192             : 
     193             :   //! Total distance.
     194             :   Value* value_total_distance_;
     195             : };
     196             : 
     197             : // Register OPTIMIZER_BIAS as a keyword for PLUMED2 input files.
     198             : PLUMED_REGISTER_ACTION(OptimizerBias, "MAZE_OPTIMIZER_BIAS")
     199             : 
     200           3 : void OptimizerBias::registerKeywords(Keywords& keys) {
     201           3 :   Bias::registerKeywords(keys);
     202             : 
     203           3 :   keys.use("ARG");
     204             : 
     205           6 :   keys.add(
     206             :     "compulsory",
     207             :     "BIASING_RATE",
     208             :     "Biasing rate."
     209             :   );
     210             : 
     211           6 :   keys.add(
     212             :     "compulsory",
     213             :     "ALPHA",
     214             :     "Rescaled force constant."
     215             :   );
     216             : 
     217           6 :   keys.add(
     218             :     "compulsory",
     219             :     "OPTIMIZER",
     220             :     "Optimization technique to minimize the collective variable for ligand\
     221             :      unbinding: RANDOM_WALK,\
     222             :                 STEERED_MD,\
     223             :                 RANDOM_ACCELERATION_MD,\
     224             :                 SIMULATED_ANNEALING,\
     225             :                 MEMETIC_SAMPLING"
     226             :   );
     227             : 
     228           6 :   keys.addOutputComponent(
     229             :     "force2",
     230             :     "default",
     231             :     "Square of the biasing force."
     232             :   );
     233             : 
     234           6 :   keys.addOutputComponent(
     235             :     "x",
     236             :     "default",
     237             :     "Optimal biasing direction: x component."
     238             :   );
     239             : 
     240           6 :   keys.addOutputComponent(
     241             :     "y",
     242             :     "default",
     243             :     "Optimal biasing direction: y component."
     244             :   );
     245             : 
     246           6 :   keys.addOutputComponent(
     247             :     "z",
     248             :     "default",
     249             :     "Optimal biasing direction: z component."
     250             :   );
     251             : 
     252           6 :   keys.addOutputComponent(
     253             :     "tdist",
     254             :     "default",
     255             :     "Total distance traveled by biased atoms."
     256             :   );
     257           3 : }
     258             : 
     259           1 : OptimizerBias::OptimizerBias(const ActionOptions& ao)
     260             :   : PLUMED_BIAS_INIT(ao),
     261           1 :     bias_(0.0),
     262           1 :     force_(0.0),
     263           1 :     total_distance_(0.0)
     264             : {
     265           1 :   log.printf(
     266             :     "maze> You are using the maze module of PLUMED2,\
     267             :     please read and cite "
     268             :   );
     269             : 
     270           2 :   log << plumed.cite("Rydzewski J. and Valsson O., arXiv:1808.08089, 2018");
     271           1 :   log.printf("\n");
     272             : 
     273           1 :   args_ = getArguments();
     274           1 :   log.printf(
     275             :     "maze> Number of args %zu\n",
     276             :     args_.size()
     277             :   );
     278             : 
     279           1 :   if (!args_.empty()) {
     280           1 :     log.printf("maze> With arguments");
     281           4 :     for (unsigned i = 0; i < args_.size(); i++) {
     282           3 :       log.printf(" %s", args_[i]->getName().c_str());
     283             :     }
     284           1 :     log.printf("\n");
     285             :   }
     286             : 
     287           2 :   if (keywords.exists("ALPHA")) {
     288           1 :     parse("ALPHA", alpha_);
     289             : 
     290           1 :     plumed_massert(
     291             :       alpha_>0,
     292             :       "maze> ALPHA should be explicitly specified and positive.\n"
     293             :     );
     294             : 
     295           1 :     log.printf(
     296             :       "maze> ALPHA read: %f [kcal/mol/A].\n",
     297             :       alpha_
     298             :     );
     299             :   }
     300             : 
     301           2 :   if (keywords.exists("BIASING_RATE")) {
     302           1 :     parse("BIASING_RATE", biasing_speed_);
     303             : 
     304           1 :     plumed_massert(
     305             :       biasing_speed_>0,
     306             :       "maze> BIASING_RATE should be explicitly specified and positive.\n"
     307             :     );
     308             : 
     309           1 :     log.printf(
     310             :       "maze> BIASING_RATE read: %f [A/ps].\n",
     311             :       biasing_speed_
     312             :     );
     313             :   }
     314             : 
     315           2 :   if (keywords.exists("OPTIMIZER")) {
     316           1 :     std::vector<std::string> opt_labels(0);
     317           2 :     parseVector("OPTIMIZER", opt_labels);
     318             : 
     319           1 :     plumed_massert(
     320             :       opt_labels.size() > 0,
     321             :       "maze> Problem with OPTIMIZER keyword.\n"
     322             :     );
     323             : 
     324           1 :     std::string error_msg = "";
     325           2 :     opt_pntrs_ = tls::get_pointers_labels<Optimizer*>(
     326             :                    opt_labels,
     327           1 :                    plumed.getActionSet(),
     328             :                    error_msg
     329             :                  );
     330             : 
     331           1 :     if (error_msg.size() > 0) {
     332           0 :       plumed_merror(
     333             :         "maze> Error in keyword OPTIMIZER of " + getName() + ": " + error_msg
     334             :       );
     335             :     }
     336             : 
     337           1 :     optimizer_ = opt_pntrs_[0];
     338           1 :     log.printf(
     339             :       "maze> Optimizer linked: %s.\n",
     340           0 :       optimizer_->get_label().c_str()
     341             :     );
     342             : 
     343           1 :     biasing_stride_=optimizer_->get_optimizer_stride();
     344           1 :   }
     345             : 
     346           1 :   checkRead();
     347             : 
     348           2 :   addComponent("force2");
     349           2 :   componentIsNotPeriodic("force2");
     350             : 
     351           2 :   addComponent("x");
     352           2 :   componentIsNotPeriodic("x");
     353             : 
     354           2 :   addComponent("y");
     355           2 :   componentIsNotPeriodic("y");
     356             : 
     357           2 :   addComponent("z");
     358           2 :   componentIsNotPeriodic("z");
     359             : 
     360           2 :   addComponent("tdist");
     361           1 :   componentIsNotPeriodic("tdist");
     362             : 
     363           1 :   biasing_direction_.zero();
     364           1 :   cv0_.zero();
     365             : 
     366           1 :   value_bias_ = getPntrToComponent("bias");
     367           1 :   value_force_ = getPntrToComponent("force2");
     368             : 
     369           1 :   value_dir_x_ = getPntrToComponent("x");
     370           1 :   value_dir_y_ = getPntrToComponent("y");
     371           1 :   value_dir_z_ = getPntrToComponent("z");
     372             : 
     373           1 :   value_total_distance_=getPntrToComponent("tdist");
     374           1 : }
     375             : 
     376          30 : void OptimizerBias::calculate() {
     377             :   // Unpack arguments and optimizers.
     378             :   Vector cv(
     379             :     args_[0]->get(),
     380             :     args_[1]->get(),
     381             :     args_[2]->get()
     382          30 :   );
     383             : 
     384             :   Vector opt_direction(
     385          30 :     optimizer_->value_x_->get(),
     386          30 :     optimizer_->value_y_->get(),
     387          30 :     optimizer_->value_z_->get()
     388          30 :   );
     389             : 
     390          30 :   if (getStep() == 0) {
     391           1 :     cv0_=cv;
     392             :   }
     393             : 
     394             :   /*
     395             :    * For details see a paper by Rydzewski and Valsson.
     396             :    */
     397          30 :   double dot = dotProduct(cv - cv0_, biasing_direction_);
     398          30 :   double delta_cv = biasing_speed_ * getTime() - (dot + total_distance_);
     399             : 
     400          30 :   double sign = tls::sgn(delta_cv);
     401             : 
     402          30 :   bias_ = alpha_ * delta_cv * delta_cv;
     403          30 :   force_ = 2.0 * sign * alpha_ * fabs(delta_cv);
     404             : 
     405          30 :   if (getStep() % biasing_stride_ == 0) {
     406           3 :     biasing_direction_ = opt_direction;
     407           3 :     cv0_ = cv;
     408           3 :     total_distance_ += dot;
     409             :   }
     410             : 
     411             :   /*
     412             :    * Return the biasing force to MD engine.
     413             :    */
     414          30 :   setOutputForce(0, force_ * biasing_direction_[0]);
     415          30 :   setOutputForce(1, force_ * biasing_direction_[1]);
     416          30 :   setOutputForce(2, force_ * biasing_direction_[2]);
     417             : 
     418             :   /*
     419             :    * Set values for PLUMED2 outputs.
     420             :    */
     421          30 :   value_bias_->set(bias_);
     422          30 :   value_force_->set(force_);
     423             : 
     424          30 :   value_total_distance_->set(total_distance_);
     425             : 
     426          30 :   value_dir_x_->set(biasing_direction_[0]);
     427          30 :   value_dir_y_->set(biasing_direction_[1]);
     428          30 :   value_dir_z_->set(biasing_direction_[2]);
     429          30 : }
     430             : 
     431             : } // namespace maze
     432             : } // namespace PLMD

Generated by: LCOV version 1.16