LCOV - code coverage report
Current view: top level - adjmat - HbondMatrix.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 49 55 89.1 %
Date: 2025-03-25 09:33:27 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2015-2020 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             : #include "AdjacencyMatrixBase.h"
      23             : #include "tools/SwitchingFunction.h"
      24             : #include "tools/Angle.h"
      25             : #include "core/ActionRegister.h"
      26             : 
      27             : #include <string>
      28             : #include <cmath>
      29             : 
      30             : //+PLUMEDOC MATRIX HBOND_MATRIX
      31             : /*
      32             : Adjacency matrix in which two atoms are adjacent if there is a hydrogen bond between them.
      33             : 
      34             : A useful tool for developing complex collective variables is the notion of the
      35             : so called adjacency matrix.  An adjacency matrix is an $N \times N$ matrix in which the $i$th, $j$th element tells you whether
      36             : or not the $i$th and $j$th atoms/molecules from a set of $N$ atoms/molecules are adjacent or not.  As detailed in the documentation
      37             : for [CONTACT_MATRIX](CONTACT_MATRIX.md) there are then a range of further analyses that you can perform on these matrices.
      38             : 
      39             : For this action the elements of the adjacency matrix are calculated using:
      40             : 
      41             : $$
      42             : a_{ij} = \sigma_{oo}( |\mathbf{r}_{ij}| ) \sum_{k=1}^N \sigma_{oh}( |\mathbf{r}_{ik}| ) \sigma_{\theta}( \theta_{kij} )
      43             : $$
      44             : 
      45             : This expression was derived by thinking about how to detect if there is a hydrogen bond between atoms $i$ and $j$.  The notion is that
      46             : if the hydrogen bond is present atoms $i$ and $j$ should be within a certain cutoff distance.  In addition, there should be a hydrogen
      47             : within a certain cutoff distance of atom $i$ and this hydrogen should lie on or close to the vector connecting atoms $i$ and $j$.
      48             : As such $\sigma_{oo}(r_{ij})$ is a switching function that acts on the modulus of the vector connecting atom $i$ to atom
      49             : $j$.  The sum over $k$ then runs over all the hydrogen atoms that are specified using using HYDROGEN keyword.  $\sigma_{oh}(r_{ik})$
      50             : is a switching function that acts on the modulus of the vector connecting atom $i$ to atom $k$ and $\sigma_{\theta}(\theta_{kij})$
      51             : is a switching function that acts on the angle between the vector connecting atoms $i$ and $j$ and the vector connecting atoms $i$ and
      52             : $k$.
      53             : 
      54             : It is important to note that hydrogen bonds, unlike regular bonds, are asymmetric. In other words, the hydrogen atom does not sit at the
      55             : mid point between the two other atoms in this three-center bond.  As a result of this adjacency matrices calculated using HBOND_MATRIX are not
      56             : symmetric like those calculated by [CONTACT_MATRIX](CONTACT_MATRIX.md).
      57             : 
      58             : The following input can be used to analyze the number of hydrogen bonds each of the oxygen atoms in a box of water participates in.  Each
      59             : water molecule can participate in a hydrogen bond in one of two ways.  It can either donate one of its hydrogen atom to the neighboring oxygen or
      60             : it can accept a bond between the hydrogen of a neighboring water molecule and its own oxygen.  The input below allows you to output information
      61             : on the number of hydrogen bonds each of the water molecules donates and accepts.  This information is output in two xyz files which each contain
      62             : five columns of data.  The first four of these columns are a label for the atom and the x, y and z position of the oxygen.  The last column is then
      63             : the number of accepted/donated hydrogen bonds.
      64             : 
      65             : ```plumed
      66             : mat: HBOND_MATRIX ATOMS=1-192:3 HYDROGENS=2-192:3,3-192:3 SWITCH={RATIONAL R_0=3.20} HSWITCH={RATIONAL R_0=2.30} ASWITCH={RATIONAL R_0=0.167pi}
      67             : ones: ONES SIZE=64
      68             : rsums: MATRIX_VECTOR_PRODUCT ARG=mat,ones
      69             : DUMPATOMS ATOMS=1-192:3 ARG=rsums FILE=donors.xyz
      70             : ```
      71             : 
      72             : */
      73             : //+ENDPLUMEDOC
      74             : 
      75             : namespace PLMD {
      76             : namespace adjmat {
      77             : 
      78             : class HbondMatrix : public AdjacencyMatrixBase {
      79             : private:
      80             :   SwitchingFunction distanceOOSwitch;
      81             :   SwitchingFunction distanceOHSwitch;
      82             :   SwitchingFunction angleSwitch;
      83             : public:
      84             :   static void registerKeywords( Keywords& keys );
      85             :   explicit HbondMatrix(const ActionOptions&);
      86             : // active methods:
      87             :   double calculateWeight( const Vector& pos1, const Vector& pos2, const unsigned& natoms, MultiValue& myvals ) const override ;
      88             : };
      89             : 
      90             : PLUMED_REGISTER_ACTION(HbondMatrix,"HBOND_MATRIX")
      91             : 
      92           4 : void HbondMatrix::registerKeywords( Keywords& keys ) {
      93           4 :   AdjacencyMatrixBase::registerKeywords( keys );
      94           4 :   keys.add("atoms-2","DONORS","The list of atoms which can donate a hydrogen bond");
      95           4 :   keys.add("atoms-2","ACCEPTORS","The list of atoms which can accept a hydrogen bond");
      96           4 :   keys.add("atoms","HYDROGENS","The list of atoms that can form the bridge between the two interesting parts "
      97             :            "of the structure.");
      98           4 :   keys.add("numbered","SWITCH","The switchingfunction that specifies how close a pair of atoms must be together for there to be a hydrogen bond between them");
      99           8 :   keys.linkActionInDocs("SWITCH","LESS_THAN");
     100           4 :   keys.add("numbered","HSWITCH","The switchingfunction that specifies how close the hydrogen must be to the donor atom of the hydrogen bond for it to be "
     101             :            "considered a hydrogen bond");
     102           8 :   keys.linkActionInDocs("HSWITCH","LESS_THAN");
     103           4 :   keys.add("numbered","ASWITCH","A switchingfunction that is used to specify what the angle between the vector connecting the donor atom to the acceptor atom and "
     104             :            "the vector connecting the donor atom to the hydrogen must be in order for it considered to be a hydrogen bond");
     105           8 :   keys.linkActionInDocs("ASWITCH","LESS_THAN");
     106           4 : }
     107             : 
     108           2 : HbondMatrix::HbondMatrix(const ActionOptions&ao):
     109             :   Action(ao),
     110           2 :   AdjacencyMatrixBase(ao) {
     111             :   std::string sfinput,errors;
     112           4 :   parse("SWITCH",sfinput);
     113           2 :   if( sfinput.length()==0 ) {
     114           0 :     error("could not find SWITCH keyword");
     115             :   }
     116           2 :   distanceOOSwitch.set(sfinput,errors);
     117           2 :   if( errors.length()!=0 ) {
     118           0 :     error("problem reading SWITCH keyword : " + errors );
     119             :   }
     120             : 
     121             :   std::string hsfinput;
     122           4 :   parse("HSWITCH",hsfinput);
     123           2 :   if( hsfinput.length()==0 ) {
     124           0 :     error("could not find HSWITCH keyword");
     125             :   }
     126           2 :   distanceOHSwitch.set(hsfinput,errors);
     127           2 :   if( errors.length()!=0 ) {
     128           0 :     error("problem reading HSWITCH keyword : " + errors );
     129             :   }
     130             : 
     131             :   std::string asfinput;
     132           4 :   parse("ASWITCH",asfinput);
     133           2 :   if( asfinput.length()==0 ) {
     134           0 :     error("could not find SWITCH keyword");
     135             :   }
     136           2 :   angleSwitch.set(asfinput,errors);
     137           2 :   if( errors.length()!=0 ) {
     138           0 :     error("problem reading SWITCH keyword : " + errors );
     139             :   }
     140             : 
     141             :   // Setup link cells
     142           2 :   setLinkCellCutoff( false, distanceOOSwitch.get_dmax() );
     143             : 
     144             :   // And check everything has been read in correctly
     145           2 :   checkRead();
     146           2 : }
     147             : 
     148        4038 : double HbondMatrix::calculateWeight( const Vector& pos1, const Vector& pos2, const unsigned& natoms, MultiValue& myvals ) const {
     149        4038 :   Vector ood = pos2;
     150        4038 :   double ood_l = ood.modulo2(); // acceptor - donor
     151        4038 :   if( ood_l<epsilon) {
     152             :     return 0;
     153             :   }
     154        4038 :   double ood_df, ood_sw=distanceOOSwitch.calculateSqr( ood_l, ood_df );
     155             : 
     156             :   double value=0;
     157      520170 :   for(unsigned i=0; i<natoms; ++i) {
     158      516132 :     Vector ohd=getPosition(i,myvals);
     159      516132 :     double ohd_l=ohd.modulo2();
     160      516132 :     double ohd_df, ohd_sw=distanceOHSwitch.calculateSqr( ohd_l, ohd_df );
     161             : 
     162             :     Angle a;
     163      516132 :     Vector ood_adf, ohd_adf;
     164      516132 :     double angle=a.compute( ood, ohd, ood_adf, ohd_adf );
     165      516132 :     double angle_df, angle_sw=angleSwitch.calculate( angle, angle_df );
     166      516132 :     value += ood_sw*ohd_sw*angle_sw;
     167             : 
     168      516132 :     if( !doNotCalculateDerivatives() ) {
     169          36 :       addAtomDerivatives( 0, angle_sw*ohd_sw*(-ood_df)*ood + angle_sw*ood_sw*(-ohd_df)*ohd + ood_sw*ohd_sw*angle_df*angle*(-ood_adf-ohd_adf), myvals );
     170          36 :       addAtomDerivatives( 1, angle_sw*ohd_sw*(+ood_df)*ood + ood_sw*ohd_sw*angle_df*angle*ood_adf, myvals );
     171          36 :       addThirdAtomDerivatives( i, angle_sw*ood_sw*(+ohd_df)*ohd + ood_sw*ohd_sw*angle_df*angle*ohd_adf, myvals );
     172          72 :       addBoxDerivatives( angle_sw*ohd_sw*(-ood_df)*Tensor(ood,ood) + angle_sw*ood_sw*(-ohd_df)*Tensor(ohd,ohd) -
     173         108 :                          ood_sw*ohd_sw*angle_df*angle*(Tensor(ood,ood_adf)+Tensor(ohd,ohd_adf)), myvals );
     174             :     }
     175             :   }
     176        4038 :   return value;
     177             : }
     178             : 
     179             : }
     180             : }

Generated by: LCOV version 1.16