LCOV - code coverage report
Current view: top level - function - FuncPathGeneral.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 101 130 77.7 %
Date: 2024-10-11 08:09:47 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2020-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : 
      23             : #include "Function.h"
      24             : #include "ActionRegister.h"
      25             : #include "tools/IFile.h"
      26             : #include <limits>
      27             : 
      28             : namespace PLMD {
      29             : namespace function {
      30             : 
      31             : //+PLUMEDOC FUNCTION FUNCPATHGENERAL
      32             : /*
      33             : This function calculates path collective variables (PCVs) using an arbitrary combination of collective variables (see \cite Hovan2019).
      34             : 
      35             : This variable computes the progress along a given set of frames that is provided in an input file ("s" component) and the distance from them ("z" component).
      36             : The input file could be a colvar file generated with plumed driver on a trajectory containing the frames.
      37             : 
      38             : The metric for the path collective variables takes the following form:
      39             : 
      40             : \f[
      41             : R[X - X_i] = \sum_{j=1}^M c_j^2 (x_j - x_{i,j})^2\,.
      42             : \f]
      43             : 
      44             : Here, the coefficients \f$c_j\f$ determine the relative weights of the collective variables \f$c_j\f$ in the metric.
      45             : A value for the lambda coefficient also needs to be provided, typically chosen in such a way that it ensures a smooth variation of the "s" component.
      46             : 
      47             : \par Examples
      48             : 
      49             : This command calculates the PCVs using the values from the file COLVAR_TRAJ and the provided values for the lambda and the coefficients.
      50             : Since the columns in the file were not specified, the first one will be ignored (assumed to correspond to the time) and the rest used.
      51             : 
      52             : \plumedfile
      53             : FUNCPATHGENERAL ...
      54             : LABEL=path
      55             : LAMBDA=12.2
      56             : REFERENCE=COLVAR_TRAJ
      57             : COEFFICIENTS=0.3536,0.3536,0.3536,0.3536,0.7071
      58             : ARG=d1,d2,d,t,drmsd
      59             : ... FUNCPATHGENERAL
      60             : \endplumedfile
      61             : 
      62             : The command below is a variation of the previous one, specifying a subset of the collective variables and using a neighbor list.
      63             : The columns are zero-indexed.
      64             : The neighbor list will include the 10 closest frames and will be recalculated every 20 steps.
      65             : 
      66             : \plumedfile
      67             : FUNCPATHGENERAL ...
      68             : LABEL=path
      69             : LAMBDA=5.0
      70             : REFERENCE=COLVAR_TRAJ
      71             : COLUMNS=2,3,4
      72             : COEFFICIENTS=0.3536,0.3536,0.3536
      73             : ARG=d2,d,t
      74             : NEIGH_SIZE=10
      75             : NEIGH_STRIDE=20
      76             : ... FUNCPATHGENERAL
      77             : \endplumedfile
      78             : 
      79             : */
      80             : //+ENDPLUMEDOC
      81             : 
      82             : class FuncPathGeneral : public Function {
      83             :   double lambda;
      84             :   int neigh_size;
      85             :   double neigh_stride;
      86             : 
      87             :   std::vector<double> coefficients;
      88             :   std::vector< std::vector<double> > path_cv_values;
      89             : 
      90             :   // For faster calculation
      91             :   std::vector<double> expdists;
      92             : 
      93             :   // For calculating derivatives
      94             :   std::vector< std::vector<double> > numerators;
      95             :   std::vector<double> s_path_ders;
      96             :   std::vector<double> z_path_ders;
      97             : 
      98             :   // For handling periodicity
      99             :   std::vector<double> domains;
     100             : 
     101             :   std::string reference;
     102             :   std::vector<int> columns;
     103             : 
     104             :   std::vector< std::pair<int,double> > neighpair;
     105             :   std::vector <Value*> allArguments;
     106             : 
     107             :   // Methods
     108             :   void loadReference();
     109             : 
     110             :   struct pairordering {
     111             :     bool operator ()(std::pair<int, double> const& a, std::pair<int, double> const& b) {
     112           0 :       return (a).second < (b).second;
     113             :     }
     114             :   };
     115             : 
     116             : public:
     117             :   explicit FuncPathGeneral(const ActionOptions&);
     118             : // Active methods:
     119             :   virtual void calculate();
     120             :   virtual void prepare();
     121             :   static void registerKeywords(Keywords& keys);
     122             : };
     123             : 
     124       10421 : PLUMED_REGISTER_ACTION(FuncPathGeneral, "FUNCPATHGENERAL")
     125             : 
     126           1 : void FuncPathGeneral::loadReference() {
     127           1 :   IFile input;
     128           1 :   input.open(reference);
     129           1 :   if (!input)
     130           0 :     plumed_merror("Could not open the reference file!");
     131          18 :   while (input)
     132             :   {
     133             :     std::vector<std::string> strings;
     134          17 :     Tools::getParsedLine(input, strings);
     135          17 :     if (strings.empty())
     136             :       continue;
     137             :     std::vector<double> colvarLine;
     138             :     double value;
     139          16 :     int max = columns.empty() ? strings.size() : columns.size();
     140         128 :     for (int i = 0; i < max; ++i)
     141             :     {
     142         112 :       int col = columns.empty() ? i : columns[i];
     143             :       // If no columns have been entered, ignore the first (time) and take the rest
     144         112 :       if (columns.empty() && i == 0)
     145          16 :         continue;
     146             : 
     147          96 :       Tools::convert(strings[col], value);
     148          96 :       colvarLine.push_back(value);
     149             :     }
     150          16 :     path_cv_values.push_back(colvarLine);
     151          17 :   }
     152           1 : }
     153             : 
     154           2 : void FuncPathGeneral::registerKeywords(Keywords& keys) {
     155           2 :   Function::registerKeywords(keys);
     156           2 :   keys.use("ARG");
     157           4 :   keys.add("compulsory", "LAMBDA", "Lambda parameter required for smoothing");
     158           4 :   keys.add("compulsory", "COEFFICIENTS", "Coefficients to be assigned to the CVs");
     159           4 :   keys.add("compulsory", "REFERENCE", "Colvar file needed to provide the CV milestones");
     160           4 :   keys.add("optional", "COLUMNS", "List of columns in the reference colvar file specifying the CVs");
     161           4 :   keys.add("optional", "NEIGH_SIZE", "Size of the neighbor list");
     162           4 :   keys.add("optional", "NEIGH_STRIDE", "How often the neighbor list needs to be calculated in time units");
     163           2 :   componentsAreNotOptional(keys);
     164           4 :   keys.addOutputComponent("s", "default", "Position on the path");
     165           4 :   keys.addOutputComponent("z", "default", "Distance from the path");
     166           2 : }
     167             : 
     168           1 : FuncPathGeneral::FuncPathGeneral(const ActionOptions&ao):
     169             :   Action(ao),
     170             :   Function(ao),
     171           1 :   neigh_size(-1),
     172           1 :   neigh_stride(-1.)
     173             : {
     174           1 :   parse("LAMBDA", lambda);
     175           1 :   parse("NEIGH_SIZE", neigh_size);
     176           1 :   parse("NEIGH_STRIDE", neigh_stride);
     177           1 :   parse("REFERENCE", reference);
     178           1 :   parseVector("COEFFICIENTS", coefficients);
     179           1 :   parseVector("COLUMNS", columns);
     180           1 :   checkRead();
     181           1 :   log.printf("  lambda is %f\n", lambda);
     182           1 :   if (getNumberOfArguments() != coefficients.size())
     183           0 :     plumed_merror("The numbers of coefficients and CVs are different!");
     184           1 :   if (!columns.empty()) {
     185           0 :     if (columns.size() != coefficients.size())
     186           0 :       plumed_merror("The numbers of coefficients and columns are different!");
     187             :   }
     188           1 :   log.printf("  Consistency check completed! Your path cvs look good!\n");
     189             : 
     190             :   // Load the reference colvar file
     191           1 :   loadReference();
     192             : 
     193             :   // Do some neighbour printout
     194           1 :   if (neigh_stride > 0. || neigh_size > 0) {
     195           0 :     if (static_cast<unsigned>(neigh_size) > path_cv_values.size()) {
     196           0 :       log.printf(" List size required ( %d ) is too large: resizing to the maximum number of arg required: %d  \n", neigh_size, getNumberOfArguments());
     197           0 :       neigh_size = path_cv_values.size();
     198             :     }
     199           0 :     log.printf("  Neighbour list enabled: \n");
     200           0 :     log.printf("                 size   :  %d elements\n", neigh_size);
     201           0 :     log.printf("                 stride :  %f time \n", neigh_stride);
     202             :   } else {
     203           1 :     log.printf("  Neighbour list NOT enabled \n");
     204             :   }
     205             : 
     206           2 :   addComponentWithDerivatives("s"); componentIsNotPeriodic("s");
     207           3 :   addComponentWithDerivatives("z"); componentIsNotPeriodic("z");
     208             : 
     209             :   // Initialise vectors
     210           1 :   std::vector<double> temp (coefficients.size());
     211          17 :   for (unsigned i = 0; i < path_cv_values.size(); ++i) {
     212          16 :     numerators.push_back(temp);
     213          16 :     expdists.push_back(0.);
     214          16 :     s_path_ders.push_back(0.);
     215          16 :     z_path_ders.push_back(0.);
     216             :   }
     217             : 
     218             :   // Store the arguments
     219           7 :   for (unsigned i=0; i<getNumberOfArguments(); i++)
     220           6 :     allArguments.push_back(getPntrToArgument(i));
     221             : 
     222             :   // Get periodic domains, negative for not periodic, stores half the domain length (maximum difference)
     223           7 :   for (unsigned i = 0; i < allArguments.size(); ++i) {
     224           6 :     if (allArguments[i]->isPeriodic()) {
     225             :       double min_lim, max_lim;
     226           0 :       allArguments[i]->getDomain(min_lim, max_lim);
     227           0 :       domains.push_back((max_lim - min_lim) / 2);
     228             :     }
     229             :     else
     230           6 :       domains.push_back(-1.);
     231             :   }
     232           1 : }
     233             : 
     234             : // Calculator
     235      175001 : void FuncPathGeneral::calculate() {
     236             :   double s_path = 0.;
     237             :   double partition = 0.;
     238             :   double tmp, value, diff, expdist, s_der, z_der;
     239             :   int ii;
     240             : 
     241             :   typedef std::vector< std::pair< int,double> >::iterator pairiter;
     242             : 
     243     2975001 :   for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
     244     2800000 :     (*it).second = 0.;
     245             :   }
     246             : 
     247      175001 :   if (neighpair.empty()) {
     248             :     // Resize at the first step
     249           1 :     neighpair.resize(path_cv_values.size());
     250          17 :     for (unsigned i = 0; i < path_cv_values.size(); ++i)
     251          16 :       neighpair[i].first = i;
     252             :   }
     253             : 
     254      175001 :   Value* val_s_path=getPntrToComponent("s");
     255      175001 :   Value* val_z_path=getPntrToComponent("z");
     256             : 
     257     1225007 :   for(unsigned j = 0; j < allArguments.size(); ++j) {
     258     1050006 :     value = allArguments[j]->get();
     259    17850102 :     for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
     260    16800096 :       diff = (value - path_cv_values[(*it).first][j]);
     261    16800096 :       if (domains[j] > 0) {
     262           0 :         if (diff > domains[j])
     263           0 :           diff -= 2 * domains[j];
     264           0 :         if (diff < -domains[j])
     265           0 :           diff += 2 * domains[j];
     266             :       }
     267    33600192 :       (*it).second += Tools::fastpow(coefficients[j] * diff, 2);
     268    33600192 :       numerators[(*it).first][j] = 2 * Tools::fastpow(coefficients[j], 2) * diff;
     269             :     }
     270             :   }
     271             : 
     272     2975017 :   for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
     273     2800016 :     expdist = std::exp(-lambda * (*it).second);
     274     2800016 :     expdists[(*it).first] = expdist;
     275     2800016 :     s_path += ((*it).first + 1) * expdist;
     276     2800016 :     partition += expdist;
     277             :   }
     278             : 
     279      175001 :   if(partition==0.0) partition=std::numeric_limits<double>::min();
     280             : 
     281      175001 :   s_path /= partition;
     282             :   val_s_path->set(s_path);
     283      175001 :   val_z_path->set(-(1. / lambda) * std::log(partition));
     284             : 
     285             :   // Derivatives
     286     2975017 :   for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
     287     2800016 :     ii = (*it).first;
     288     2800016 :     tmp = lambda * expdists[ii] * (s_path - (ii + 1)) / partition;
     289     2800016 :     s_path_ders[ii] = tmp;
     290     2800016 :     z_path_ders[ii] = expdists[ii] / partition;
     291             :   }
     292     1225007 :   for (unsigned i = 0; i < coefficients.size(); ++i) {
     293             :     s_der = 0.;
     294             :     z_der = 0.;
     295    17850102 :     for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
     296    16800096 :       ii = (*it).first;
     297    16800096 :       s_der += s_path_ders[ii] * numerators[ii][i];
     298    16800096 :       z_der += z_path_ders[ii] * numerators[ii][i];
     299             :     }
     300             :     setDerivative(val_s_path, i, s_der);
     301             :     setDerivative(val_z_path, i, z_der);
     302             :   }
     303      175001 : }
     304             : 
     305             : // Prepare the required arguments
     306      175001 : void FuncPathGeneral::prepare() {
     307             :   // Neighbour list: rank and activate the chain for the next step
     308             : 
     309             :   // Neighbour list: if neigh_size < 0 never sort and keep the full vector
     310             :   // Neighbour list: if neigh_size > 0
     311             :   //                 if the size is full -> sort the vector and decide the dependencies for next step
     312             :   //                 if the size is not full -> check if next step will need the full dependency otherwise keep these dependencies
     313             : 
     314      175001 :   if (neigh_size > 0) {
     315           0 :     if (neighpair.size() == path_cv_values.size()) {
     316             :       // The complete round has been done: need to sort, shorten and give it a go
     317             :       // Sort the values
     318           0 :       std::sort(neighpair.begin(), neighpair.end(), pairordering());
     319             :       // Resize the effective list
     320           0 :       neighpair.resize(neigh_size);
     321           0 :       log.printf("  NEIGHBOUR LIST NOW INCLUDES INDICES: ");
     322           0 :       for (int i = 0; i < neigh_size; ++i)
     323           0 :         log.printf(" %i ",neighpair[i].first);
     324           0 :       log.printf(" \n");
     325             :     } else {
     326           0 :       if (int(getStep()) % int(neigh_stride / getTimeStep()) == 0) {
     327           0 :         log.printf(" Time %f : recalculating full neighbour list \n", getStep() * getTimeStep());
     328           0 :         neighpair.resize(path_cv_values.size());
     329           0 :         for (unsigned i = 0; i < path_cv_values.size(); ++i)
     330           0 :           neighpair[i].first = i;
     331             :       }
     332             :     }
     333             :   }
     334             : 
     335      175001 :   requestArguments(allArguments);
     336      175001 : }
     337             : 
     338             : }
     339             : }

Generated by: LCOV version 1.15