LCOV - code coverage report
Current view: top level - pamm - HBPammMatrix.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 198 212 93.4 %
Date: 2026-11-05 10:19:04 Functions: 7 8 87.5 %

          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 "adjmat/AdjacencyMatrixBase.h"
      23             : #include "multicolvar/MultiColvarShortcuts.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "core/ActionShortcut.h"
      26             : #include "core/PlumedMain.h"
      27             : #include "core/ActionSet.h"
      28             : #include "tools/IFile.h"
      29             : 
      30             : //+PLUMEDOC MATRIX HBPAMM_MATRIX
      31             : /*
      32             : Adjacency matrix in which two electronegative atoms are adjacent if they are hydrogen bonded
      33             : 
      34             : This method allows you to calculate an adjacency matrix and use all the methods discussed in the documentation for
      35             : the [CONTACT_MATRIX](CONTACT_MATRIX.md) upon it to define CVs.  The $i,j$ element of the matrix that is calculated
      36             : by this action is one if there is a hydrogen bond connecting atom $i$ to atom $j$.  Furthermore, we determine whether
      37             : there is a hydrogen atom between these two atoms by using the PAMM technique that is discussed in the articles from the
      38             : bibliography below and in the documentation for the [PAMM](PAMM.md) action.
      39             : 
      40             : The example shown below illustrates how the method is used in practice
      41             : 
      42             : ```plumed
      43             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
      44             : m: HBPAMM_MATRIX ...
      45             :   GROUP=1-192:3 GROUPC=2-192:3,3-192:3
      46             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
      47             : ...
      48             : PRINT ARG=m FILE=colvar
      49             : ```
      50             : 
      51             : The input above is outputting the full hbpamm matrix.  However, this action is perhaps more usefully used to investigate the connectivity between
      52             : a collection of water molecules in liquid water. Importantly, however,
      53             : the output matrix here is __not__ symmetric.  We thus calculate the number of hydrogen bonds that these atoms are donating using the
      54             : following input:
      55             : 
      56             : ```plumed
      57             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
      58             : m: HBPAMM_MATRIX ...
      59             :   GROUP=1-192:3 GROUPC=2-192:3,3-192:3
      60             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
      61             : ...
      62             : ones: ONES SIZE=64
      63             : rsums: MATRIX_VECTOR_PRODUCT ARG=m,ones
      64             : DUMPATOMS ATOMS=1-192:3 ARG=rsums FILE=donors.xyz
      65             : ```
      66             : 
      67             : To calculate the number of hydrogen bonds these atoms accept we would use the following input:
      68             : 
      69             : ```plumed
      70             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
      71             : m: HBPAMM_MATRIX ...
      72             :   GROUP=1-192:3 GROUPC=2-192:3,3-192:3
      73             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
      74             : ...
      75             : mT: TRANSPOSE ARG=m
      76             : ones: ONES SIZE=64
      77             : rsums: MATRIX_VECTOR_PRODUCT ARG=mT,ones
      78             : DUMPATOMS ATOMS=1-192:3 ARG=rsums FILE=acceptors.xyz
      79             : ```
      80             : 
      81             : To calculate the total number of hydorgen bonds these atoms participate in you would use an input like this:
      82             : 
      83             : ```plumed
      84             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
      85             : m: HBPAMM_MATRIX ...
      86             :   GROUP=1-192:3 GROUPC=2-192:3,3-192:3
      87             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
      88             : ...
      89             : mT: TRANSPOSE ARG=m
      90             : hbmat: CUSTOM ARG=m,mT FUNC=x+y PERIODIC=NO
      91             : ones: ONES SIZE=64
      92             : rsums: MATRIX_VECTOR_PRODUCT ARG=hbmat,ones
      93             : DUMPATOMS ATOMS=1-192:3 ARG=rsums FILE=hbonds.xyz
      94             : ```
      95             : 
      96             : If you want to investigate whether there are hydrogen bonds between two groups of molecules you can use an input like this:
      97             : 
      98             : ```plumed
      99             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     100             : m: HBPAMM_MATRIX ...
     101             :   GROUPA=1 GROUPB=2-192:3 GROUPC=2-192:3,3-192:3
     102             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     103             : ...
     104             : PRINT ARG=m FILE=colvar
     105             : ```
     106             : 
     107             : This input outputs a $1\times 63$ matrix in which the $1,i$th element tells you whether or not atom 1 donates a hydrogen bond
     108             : to the $i$th element in the group of 63 atoms that was specified using the ACCEPTORS keyword.  The $i,1$th element of the
     109             : transpose of this matrix tells you if the $i$th center donates a hydrogen bond to atom 1.
     110             : 
     111             : In general, it is better to use this action through the [HBPAMM_SA](HBPAMM_SA.md), [HBPAMM_SD](HBPAMM_SD.md) and [HBPAMM_SH](HBPAMM_SH.md)
     112             : keywords, which can be used to calculate the number of hydrogen bonds each donor, acceptor or hydrogen atom in your system participates in.
     113             : 
     114             : ## Periodic boundary conditions
     115             : 
     116             : Notice that in all the inputs above the distances values that enter the pamm expressions are calculated in a way that takes the
     117             : periodic boundary conditions into account.  If you want to ignore the periodic boundary conditions you can use the NOPBC flag as shown below.
     118             : 
     119             : ```plumed
     120             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     121             : m: HBPAMM_MATRIX ...
     122             :   GROUP=1-192:3 GROUPC=2-192:3,3-192:3
     123             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     124             :   NOPBC
     125             : ...
     126             : ```
     127             : 
     128             : ## COMPONENTS flag
     129             : 
     130             : If you add the flag COMPONENTS to the input as shown below:
     131             : 
     132             : ```plumed
     133             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     134             : c4: HBPAMM_MATRIX ...
     135             :   GROUP=1-192:3 GROUPC=2-192:3,3-192:3
     136             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     137             :   COMPONENTS
     138             : ...
     139             : ```
     140             : 
     141             : then four matrices with the labels `c4.w`, `c4.x`, `c4.y` and `c4.z` are output by the action. The matrix with the label `c4.w` is the adjacency matrix
     142             : that would be output if you had not added the COMPONENTS flag. The $i,j$ component of the matrices `c4.x`, `c4.y` and `c4.z` contain the $x$, $y$ and $z$
     143             : components of the vector connecting atoms $j$ and $k$. Importantly, however, the components of these vectors are only stored in `c4.x`, `c4.y` and `c4.z`
     144             : if the elements of `c4.w` are non-zero. Using the COMPONENTS flag in this way ensures that you can use HBPAMM_MATRIX in tandem with many of the functionalities
     145             : that are part of the [symfunc module](module_symfunc.md).  Remember, however, that the $i,j$ element of the HBPAMM_MATRIX is only non-zero if atom $i$ donates
     146             : a hydrogen bond to atom $j$.  __You cannot use HBPAMM_MATRIX to identify the set of atoms that each atom is hydrogen bonded to.__
     147             : 
     148             : ## The MASK keyword
     149             : 
     150             : You use the MASK keyword with HBPAMM_MATRIX in the same way that is used in [CONTACT_MATRIX](CONTACT_MATRIX.md).  This keyword thus expects a vector in input,
     151             : which tells HBOND_MATRIX that it is safe to not calculate certain rows of the output matrix.  An example where this keyword is used is shown below:
     152             : 
     153             : ```plumed
     154             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     155             : # The atoms that are of interest
     156             : ow: GROUP ATOMS=1-1650
     157             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     158             : center: FIXEDATOM AT=2.5,2.5,2.5
     159             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     160             : sphere: INSPHERE ATOMS=ow CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     161             : # Calculates cooordination numbers
     162             : cmap: HBPAMM_MATRIX ...
     163             :   GROUP=ow GROUPC=1650-3000
     164             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     165             :   MASK=sphere
     166             : ...
     167             : ones: ONES SIZE=1650
     168             : cc: MATRIX_VECTOR_PRODUCT ARG=cmap,ones
     169             : # Multiply coordination numbers by sphere vector
     170             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     171             : # Sum of coordination numbers for atoms that are in the sphere of interest
     172             : numer: SUM ARG=prod PERIODIC=NO
     173             : # Number of atoms that are in sphere of interest
     174             : denom: SUM ARG=sphere PERIODIC=NO
     175             : # Average coordination number for atoms in sphere of interest
     176             : av: CUSTOM ARG=prod,denom FUNC=x/y PERIODIC=NO
     177             : # And print out final CV to a file
     178             : PRINT ARG=av FILE=colvar STRIDE=1
     179             : ```
     180             : 
     181             : This input calculates the average number of hydrogen bonds each of the atoms that are within a spherical region that is centered on the point
     182             : $(2.5,2.5,2.5)$ donate.
     183             : 
     184             : */
     185             : //+ENDPLUMEDOC
     186             : 
     187             : //+PLUMEDOC COLVAR HBPAMM_SA
     188             : /*
     189             : Calculate the number of hydrogen bonds each acceptor participates in using the HBPamm method
     190             : 
     191             : This shortcut action allows you to calculate the number of hydrogen bonds each of the atoms specified using the SITES
     192             : keyword accepts from its neighbours. The number of hydrogen bonds that a particular site accepts is determined by using the
     193             : PAMM tehcnique that is discussed in the articles from the bibliography below and in the documentation for the [PAMM](PAMM.md) action
     194             : 
     195             : The following example shows how you can use this action to calculate how many hydrogen bonds each of the water moecules in
     196             : a box of water accepts from its neighbours.
     197             : 
     198             : ```plumed
     199             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     200             : acceptors: HBPAMM_SA ...
     201             :   SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     202             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     203             :   REGULARISE=0.001 GAUSS_CUTOFF=6.25
     204             : ...
     205             : DUMPATOMS ARG=acceptors ATOMS=1-192:3 FILE=acceptors.xyz
     206             : ```
     207             : 
     208             : The output here is an xyz with five columns. As explained in the documentation for [DUMPATOMS](DUMPATOMS.md), the first four
     209             : columns are the usual columns that you would expect in an xyz file.  The fifth column then contains the number of hydrogen bonds
     210             : that have been accepted by each of the atoms.
     211             : 
     212             : In the example input above all the atoms specified using the SITE keyword can both accept and donate hydrogen bonds.  If the group
     213             : of atoms that can accept hydrogen bonds is different from the group of atoms that can donate hydrogen bonds then you use the ACCEPTORS and
     214             : DONORS keyword as shown below:
     215             : 
     216             : ```plumed
     217             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     218             : acceptors: HBPAMM_SA ...
     219             :   ACCEPTORS=1-9:3 DONORS=1-192:3
     220             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     221             :   HYDROGENS=2-192:3,3-192:3
     222             : ...
     223             : DUMPATOMS ARG=acceptors ATOMS=1-9:3 FILE=acceptors.xyz
     224             : ```
     225             : 
     226             : This input will still output an xyz file with five columns. The fifth column in this file will give the number of hydrogen bonds that each
     227             : of the three atoms that were specified using the ACCEPTORS keyword accept from the atoms that were specified by the DONORS keyword.  Notice also that the
     228             : as the REGULARISE and GAUSS_CUTOFF keywords were not used in this input these quantities are set to the default values that are given in the table below.
     229             : 
     230             : ## The NOPBC flag
     231             : 
     232             : When you use the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) you need to calculate the distances between various pairs of atoms.  In all the inputs that have been provided
     233             : above we calculate these distances between pairs of atoms in a way that takes the periodic boundary conditions into account.  If you want to ignore the periodic
     234             : boundary conditions you can use the NOPBC flag as shown below.
     235             : 
     236             : ```plumed
     237             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     238             : acceptors: HBPAMM_SA ...
     239             :    SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     240             :    CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     241             :    NOPBC
     242             : ...
     243             : DUMPATOMS ARG=acceptors ATOMS=1-192:3 FILE=acceptors.xyz
     244             : ```
     245             : 
     246             : ## The MASK keyword
     247             : 
     248             : You can use the MASK keyword with this action in the same way that it is used with [COORDINATIONNUMBER](COORDINATIONNUMBER.md).  This keyword thus expects a vector in
     249             : input, which tells PLUMED the atoms for which you do not need to calculate the number of hydrogen bonds.  As illustrated below, this is useful if you are using functionality
     250             : from the [volumes module](module_volumes.md) to calculate the average number of hydrogen bonds those atoms that lie in a certain part of the simulation box accept from their neighbors.
     251             : 
     252             : ```plumed
     253             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     254             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     255             : center: FIXEDATOM AT=2.5,2.5,2.5
     256             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     257             : sphere: INSPHERE ATOMS=1-192:3 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     258             : # Calculate the number of hydrogen bonds that have been accepted
     259             : acceptors: HBPAMM_SA ...
     260             :    SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     261             :    CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     262             :    MASK=sphere
     263             : ...
     264             : # Multiply number of hydrogen bonds by sphere vector
     265             : prod: CUSTOM ARG=acceptors,sphere FUNC=x*y PERIODIC=NO
     266             : # Total number of hydrogen bonds accepted for atoms that are in the sphere of interest
     267             : numer: SUM ARG=prod PERIODIC=NO
     268             : # Number of atoms that are in sphere of interest
     269             : denom: SUM ARG=sphere PERIODIC=NO
     270             : # Average number of accepted hydrogen bonds for atoms in sphere of interest
     271             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     272             : # And print out final CV to a file
     273             : PRINT ARG=av FILE=colvar STRIDE=1
     274             : ```
     275             : 
     276             : This input calculate the number of hydrogen bonds that are accepted by those atoms that are within a spherical region that is centered on the point
     277             : $(2.5,2.5,2.5)$.
     278             : 
     279             : ## Optimisation details
     280             : 
     281             : If you expand the inputs above you will see that they all the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) action.  The matrix that this action computes is sparse
     282             : as each atom only forms hydroen bonds with a relatively small number of neighbors.  The vast majority of the elements in the [HBPAMM_MATRIX](HBPAMM_MATRIX.md)
     283             : are thsu zero.  To reduce the amount of memory that PLUMED requires PLUMED uses sparse matrix storage.  Consequently, whenever you calculate and store a contact
     284             : matrix only the elements of the matrix that are non-zero are stored.
     285             : 
     286             : We also use the sparsity of the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) to make the time required to compute the matrix scale linearly rather than quadratically with
     287             : the number of atoms. We know that element $i,j$ is only non-zero if the donor and acceptor atoms are within:
     288             : 
     289             : $$
     290             : r_c = \textrm{max}_i \left( \mu_i^{(ad)} + \sqrt(2 g_{cut}) | \sqrt{\lambda_{i,max}} v_\textrm{i,max}^{(ad)} | \right)
     291             : $$
     292             : 
     293             : where $i$ runs over all the PAMM kernels.  $g_{cut}$ is the gaussian cutoff that is specified using the REGULARIZE
     294             : keyword,  $\mu_i^{(ad)}$ is the mean acceptor-donor distance for the $i$th kernel, $\lambda_{i,max}$ is the largest
     295             : eigenvalue of the covariance for the $i$th kernel and $v_\textrm{i,max}^{(ad)}$ is the acceptor-donor distance component
     296             : of its corresponding eigenvector.  We can determine that many pairs of atoms are further apart than $r_c$ without computing the
     297             : distance between these atoms by using divide and conquer strategies such as linked lists and neighbour lists. Furthermore, we do not even
     298             : need to set a `D_MAX` parameter as we would have to if we were using [CONTACT_MATRIX](CONTACT_MATRIX.md) as the above cutoff can be determined
     299             : from the parameters of the Gaussian kernels directly.
     300             : 
     301             : */
     302             : //+ENDPLUMEDOC
     303             : 
     304             : //+PLUMEDOC COLVAR HBPAMM_SD
     305             : /*
     306             : Calculate the number of hydrogen bonds each donor participates in using the HBPamm method
     307             : 
     308             : This shortcut action allows you to calculate the number of hydrogen bonds each of the atoms specified using the SITES
     309             : keyword donates to its neighbours. The number of hydrogen bonds that a particular site donates is determined by using the
     310             : PAMM tehcnique that is discussed in the articles from the bibliography below and in the documentation for the [PAMM](PAMM.md) action
     311             : 
     312             : The following example shows how you can use this action to calculate how many hydrogen bonds each of the water moecules in
     313             : a box of water donates to its neighbours.
     314             : 
     315             : ```plumed
     316             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     317             : donors: HBPAMM_SD ...
     318             :   SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     319             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     320             :   REGULARISE=0.001 GAUSS_CUTOFF=6.25
     321             : ...
     322             : DUMPATOMS ARG=donors ATOMS=1-192:3 FILE=donors.xyz
     323             : ```
     324             : 
     325             : The output here is an xyz with five columns. As explained in the documentation for [DUMPATOMS](DUMPATOMS.md), the first four
     326             : columns are the usual columns that you would expect in an xyz file.  The fifth column then contains the number of hydrogen bonds
     327             : that have been donated by each of the atoms.
     328             : 
     329             : In the example input above all the atoms specified using the SITE keyword can both accept and donate hydrogen bonds.  If the group
     330             : of atoms that can accept hydrogen bonds is different from the group of atoms that can donate hydrogen bonds then you use the ACCEPTORS and
     331             : DONORS keyword as shown below:
     332             : 
     333             : ```plumed
     334             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     335             : donors: HBPAMM_SD ...
     336             :   DONORS=1-9:3 ACCEPTORS=1-192:3
     337             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     338             :   HYDROGENS=2-192:3,3-192:3
     339             : ...
     340             : DUMPATOMS ARG=donors ATOMS=1-9:3 FILE=donors.xyz
     341             : ```
     342             : 
     343             : This input will still output an xyz file with five columns. The fifth column in this file will give the number of hydrogen bonds that each
     344             : of the three atoms that were specified using the DONORS keyword donate to atoms that were specified by the ACCEPTORS keyword.  Notice also that the
     345             : as the REGULARISE and GAUSS_CUTOFF keywords were not used in this input these quantities are set to the default values that are given in the table below.
     346             : 
     347             : ## The NOPBC flag
     348             : 
     349             : When you use the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) you need to calculate the distances between various pairs of atoms.  In all the inputs that have been provided
     350             : above we calculate these distances between pairs of atoms in a way that takes the periodic boundary conditions into account.  If you want to ignore the periodic
     351             : boundary conditions you can use the NOPBC flag as shown below.
     352             : 
     353             : ```plumed
     354             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     355             : donors: HBPAMM_SD ...
     356             :    SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     357             :    CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     358             :    NOPBC
     359             : ...
     360             : DUMPATOMS ARG=donors ATOMS=1-192:3 FILE=donors.xyz
     361             : ```
     362             : 
     363             : ## The MASK keyword
     364             : 
     365             : You can use the MASK keyword with this action in the same way that it is used with [COORDINATIONNUMBER](COORDINATIONNUMBER.md).  This keyword thus expects a vector in
     366             : input, which tells PLUMED the atoms for which you do not need to calculate the number of hydrogen bonds.  As illustrated below, this is useful if you are using functionality
     367             : from the [volumes module](module_volumes.md) to calculate the average number of hydrogen bonds those atoms that lie in a certain part of the simulation box donoate to their neighbors.
     368             : 
     369             : ```plumed
     370             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     371             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     372             : center: FIXEDATOM AT=2.5,2.5,2.5
     373             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     374             : sphere: INSPHERE ATOMS=1-192:3 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     375             : # Calculate the number of hydrogen bonds that have been donated
     376             : donors: HBPAMM_SD ...
     377             :    SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     378             :    CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     379             :    MASK=sphere
     380             : ...
     381             : # Multiply number of hydrogen bonds by sphere vector
     382             : prod: CUSTOM ARG=donors,sphere FUNC=x*y PERIODIC=NO
     383             : # Total number of hydrogen bonds donated for atoms that are in the sphere of interest
     384             : numer: SUM ARG=prod PERIODIC=NO
     385             : # Number of atoms that are in sphere of interest
     386             : denom: SUM ARG=sphere PERIODIC=NO
     387             : # Average number of donated hydrogen bonds for atoms in sphere of interest
     388             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     389             : # And print out final CV to a file
     390             : PRINT ARG=av FILE=colvar STRIDE=1
     391             : ```
     392             : 
     393             : This input calculate the number of hydrogen bonds that have been donated by those atoms that are within a spherical region that is centered on the point
     394             : $(2.5,2.5,2.5)$.
     395             : 
     396             : ## Optimisation details
     397             : 
     398             : If you expand the inputs above you will see that they all the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) action.  The matrix that this action computes is sparse
     399             : as each atom only forms hydroen bonds with a relatively small number of neighbors.  The vast majority of the elements in the [HBPAMM_MATRIX](HBPAMM_MATRIX.md)
     400             : are thsu zero.  To reduce the amount of memory that PLUMED requires PLUMED uses sparse matrix storage.  Consequently, whenever you calculate and store a contact
     401             : matrix only the elements of the matrix that are non-zero are stored.
     402             : 
     403             : We also use the sparsity of the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) to make the time required to compute the matrix scale linearly rather than quadratically with
     404             : the number of atoms. We know that element $i,j$ is only non-zero if the donor and acceptor atoms are within:
     405             : 
     406             : $$
     407             : r_c = \textrm{max}_i \left( \mu_i^{(ad)} + \sqrt(2 g_{cut}) | \sqrt{\lambda_{i,max}} v_\textrm{i,max}^{(ad)} | \right)
     408             : $$
     409             : 
     410             : where $i$ runs over all the PAMM kernels.  $g_{cut}$ is the gaussian cutoff that is specified using the REGULARIZE
     411             : keyword,  $\mu_i^{(ad)}$ is the mean acceptor-donor distance for the $i$th kernel, $\lambda_{i,max}$ is the largest
     412             : eigenvalue of the covariance for the $i$th kernel and $v_\textrm{i,max}^{(ad)}$ is the acceptor-donor distance component
     413             : of its corresponding eigenvector.  We can determine that many pairs of atoms are further apart than $r_c$ without computing the
     414             : distance between these atoms by using divide and conquer strategies such as linked lists and neighbour lists. Furthermore, we do not even
     415             : need to set a `D_MAX` parameter as we would have to if we were using [CONTACT_MATRIX](CONTACT_MATRIX.md) as the above cutoff can be determined
     416             : from the parameters of the Gaussian kernels directly.
     417             : 
     418             : */
     419             : //+ENDPLUMEDOC
     420             : 
     421             : //+PLUMEDOC COLVAR HBPAMM_SH
     422             : /*
     423             : Calculate the number of hydrogen bonds each hydrogen participates in using the HBPamm method
     424             : 
     425             : This shortcut action allows you to calculate the number of hydrogen bonds each of the atoms specified using the SITES
     426             : keyword donates to its neighbours. The number of hydrogen bonds that a particular site donates is determined by using the
     427             : PAMM tehcnique that is discussed in the articles from the bibliography below and in the documentation for the [PAMM](PAMM.md) action
     428             : 
     429             : The following example shows how you can use this action to calculate how many hydrogen bonds each of the water moecules in
     430             : a box of water donates to its neighbours.
     431             : 
     432             : ```plumed
     433             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     434             : hyd: HBPAMM_SH ...
     435             :   SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     436             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     437             :   REGULARISE=0.001 GAUSS_CUTOFF=6.25
     438             : ...
     439             : DUMPATOMS ARG=hyd ATOMS=2-192:3,3-192:3 FILE=hydrogens.xyz
     440             : ```
     441             : 
     442             : The output here is an xyz with five columns. As explained in the documentation for [DUMPATOMS](DUMPATOMS.md), the first four
     443             : columns are the usual columns that you would expect in an xyz file.  The fifth column then contains the number of hydrogen bonds
     444             : that each hydrogen atom participates in.
     445             : 
     446             : In the example input above all the atoms specified using the SITE keyword can both accept and donate hydrogen bonds.  If the group
     447             : of atoms that can accept hydrogen bonds is different from the group of atoms that can donate hydrogen bonds then you use the ACCEPTORS and
     448             : DONORS keyword as shown below:
     449             : 
     450             : ```plumed
     451             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     452             : hyd: HBPAMM_SH ...
     453             :   ACCEPTORS=1-9:3 DONORS=1-192:3
     454             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     455             :   HYDROGENS=2-192:3,3-192:3
     456             : ...
     457             : DUMPATOMS ARG=hyd ATOMS=2-192:3,3-192:3 FILE=hydrogens.xyz
     458             : ```
     459             : 
     460             : This input will still output an xyz file with five columns. The fifth column in this file will give the number of hydrogen bonds that each of the hydrogen
     461             : atoms that were specified in this input participate within.  For this atom for a hydrogen atom to particpiate in a hydrogen bond the second closest atom to
     462             : it must be either atom 1, 4 or 7 as these are the only atoms that can accept hydrogen bonds according to the above input.  Notice also that the
     463             : as the REGULARISE and GAUSS_CUTOFF keywords were not used in this input these quantities are set to the default values that are given in the table below.
     464             : 
     465             : ## The NOPBC flag
     466             : 
     467             : When you use the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) you need to calculate the distances between various pairs of atoms.  In all the inputs that have been provided
     468             : above we calculate these distances between pairs of atoms in a way that takes the periodic boundary conditions into account.  If you want to ignore the periodic
     469             : boundary conditions you can use the NOPBC flag as shown below.
     470             : 
     471             : ```plumed
     472             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     473             : hyd: HBPAMM_SH ...
     474             :   SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     475             :   CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     476             :   NOPBC
     477             : ...
     478             : DUMPATOMS ARG=hyd ATOMS=2-192:3,3-192:3 FILE=hydrogens.xyz
     479             : ```
     480             : 
     481             : ## The MASK keyword
     482             : 
     483             : You can use the MASK keyword with this action in the same way that it is used with [COORDINATIONNUMBER](COORDINATIONNUMBER.md).  This keyword thus expects a vector in
     484             : input, which tells PLUMED the atoms for which you do not need to calculate the number of hydrogen bonds.  As illustrated below, this is useful if you are using functionality
     485             : from the [volumes module](module_volumes.md) to calculate the average number of hydrogen bonds those hydrogen atoms that lie in a certain part of the simulation box partipipate in.
     486             : 
     487             : ```plumed
     488             : #SETTINGS INPUTFILES=regtest/pamm/rt-hbpamm/b3lyp.pamm
     489             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     490             : center: FIXEDATOM AT=2.5,2.5,2.5
     491             : # Vector in which element i is one if hydrogen i is in sphere of interest and zero otherwise
     492             : sphere: INSPHERE ATOMS=2-192:3,3-192:3 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     493             : # Calculate the number of bonds each hydrogen participates in
     494             : hyd: HBPAMM_SH ...
     495             :    SITES=1-192:3 HYDROGENS=2-192:3,3-192:3
     496             :    CLUSTERS=regtest/pamm/rt-hbpamm/b3lyp.pamm
     497             :    MASK=sphere
     498             : ...
     499             : # Multiply number of hydrogen bonds by sphere vector
     500             : prod: CUSTOM ARG=hyd,sphere FUNC=x*y PERIODIC=NO
     501             : # Total number of hydrogn bonds that the atoms in the sphere of interest participate in
     502             : numer: SUM ARG=prod PERIODIC=NO
     503             : # Number of hydrogen atoms that are in sphere of interest
     504             : denom: SUM ARG=sphere PERIODIC=NO
     505             : # Average number of hydrogen bonds each hydrogen atoms in sphere of interest participates in
     506             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     507             : # And print out final CV to a file
     508             : PRINT ARG=av FILE=colvar STRIDE=1
     509             : ```
     510             : 
     511             : This input calculate the average number of hydrogen bonds each of the hydrogen atoms that are within a spherical region that is centered on the point
     512             : $(2.5,2.5,2.5)$ participates in.
     513             : 
     514             : ## Optimisation details
     515             : 
     516             : If you expand the inputs above you will see that they all the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) action.  The matrix that this action computes is sparse
     517             : as each atom only forms hydroen bonds with a relatively small number of neighbors.  The vast majority of the elements in the [HBPAMM_MATRIX](HBPAMM_MATRIX.md)
     518             : are thsu zero.  To reduce the amount of memory that PLUMED requires PLUMED uses sparse matrix storage.  Consequently, whenever you calculate and store a contact
     519             : matrix only the elements of the matrix that are non-zero are stored.
     520             : 
     521             : We also use the sparsity of the [HBPAMM_MATRIX](HBPAMM_MATRIX.md) to make the time required to compute the matrix scale linearly rather than quadratically with
     522             : the number of atoms. We know that element $i,j$ is only non-zero if the donor and acceptor atoms are within:
     523             : 
     524             : $$
     525             : r_c = \textrm{max}_i \left( \mu_i^{(ad)} + \sqrt(2 g_{cut}) | \sqrt{\lambda_{i,max}} v_\textrm{i,max}^{(ad)} | \right)
     526             : $$
     527             : 
     528             : where $i$ runs over all the PAMM kernels.  $g_{cut}$ is the gaussian cutoff that is specified using the REGULARIZE
     529             : keyword,  $\mu_i^{(ad)}$ is the mean acceptor-donor distance for the $i$th kernel, $\lambda_{i,max}$ is the largest
     530             : eigenvalue of the covariance for the $i$th kernel and $v_\textrm{i,max}^{(ad)}$ is the acceptor-donor distance component
     531             : of its corresponding eigenvector.  We can determine that many pairs of atoms are further apart than $r_c$ without computing the
     532             : distance between these atoms by using divide and conquer strategies such as linked lists and neighbour lists. Furthermore, we do not even
     533             : need to set a `D_MAX` parameter as we would have to if we were using [CONTACT_MATRIX](CONTACT_MATRIX.md) as the above cutoff can be determined
     534             : from the parameters of the Gaussian kernels directly.
     535             : 
     536             : */
     537             : //+ENDPLUMEDOC
     538             : 
     539             : namespace PLMD {
     540             : namespace pamm {
     541             : 
     542             : class HBPammMatrix {
     543             : public:
     544             :   double regulariser=0.0;
     545             :   Tensor incoord_to_hbcoord;
     546             :   std::vector<double> weight;
     547             :   std::vector<Vector> centers;
     548             :   std::vector<Tensor> kmat;
     549             :   static void registerKeywords( Keywords& keys );
     550             :   void parseInput( adjmat::AdjacencyMatrixBase<HBPammMatrix>* action );
     551             :   // the assignment is customized (see below): copy construction is the default one
     552           6 :   HBPammMatrix() = default;
     553             :   HBPammMatrix(const HBPammMatrix&) = default;
     554           6 :   HBPammMatrix& operator=( const HBPammMatrix& m ) {
     555           6 :     regulariser = m.regulariser;
     556           6 :     incoord_to_hbcoord = m.incoord_to_hbcoord;
     557           6 :     weight = m.weight;
     558           6 :     centers = m.centers;
     559           6 :     kmat = m.kmat;
     560           6 :     return *this;
     561             :   }
     562             :   static void calculateWeight( const HBPammMatrix& data, const adjmat::AdjacencyMatrixInput& input, adjmat::MatrixOutput output );
     563             : };
     564             : 
     565             : typedef adjmat::AdjacencyMatrixBase<HBPammMatrix> hbpmap;
     566             : PLUMED_REGISTER_ACTION(hbpmap,"HBPAMM_MATRIX")
     567             : 
     568          36 : void HBPammMatrix::registerKeywords( Keywords& keys ) {
     569          36 :   keys.use("GROUPC");
     570          36 :   keys.add("compulsory","ORDER","dah","the order in which the groups are specified in the input.  Can be dah (donor/acceptor/hydrogens), "
     571             :            "adh (acceptor/donor/hydrogens) or hda (hydrogens/donor/acceptors");
     572          36 :   keys.add("compulsory","CLUSTERS","the name of the file that contains the definitions of all the kernels for PAMM");
     573          36 :   keys.add("compulsory","REGULARISE","0.001","don't allow the denominator to be smaller then this value");
     574          36 :   keys.add("compulsory","GAUSS_CUTOFF","6.25","the cutoff at which to stop evaluating the kernel function is set equal to sqrt(2*x)*(max(adc)+cov(adc))");
     575          36 :   keys.needsAction("PAMM");
     576          36 :   keys.needsAction("ONES");
     577          36 :   keys.needsAction("MATRIX_VECTOR_PRODUCT");
     578          36 :   keys.addDOI("10.1063/1.4900655");
     579          36 :   keys.addDOI("10.1021/acs.jctc.7b00993");
     580          36 : }
     581             : 
     582           6 : void HBPammMatrix::parseInput( adjmat::AdjacencyMatrixBase<HBPammMatrix>* action ) {
     583             :   double DP2CUTOFF;
     584          12 :   action->parse("GAUSS_CUTOFF",DP2CUTOFF);
     585             :   std::string sorder;
     586           6 :   action->parse("ORDER",sorder);
     587           6 :   if( sorder=="dah" ) {
     588           2 :     incoord_to_hbcoord(0,0)=1;
     589           2 :     incoord_to_hbcoord(0,1)=-1;
     590           2 :     incoord_to_hbcoord(0,2)=0;
     591           2 :     incoord_to_hbcoord(1,0)=1;
     592           2 :     incoord_to_hbcoord(1,1)=1;
     593           2 :     incoord_to_hbcoord(1,2)=0;
     594           2 :     incoord_to_hbcoord(2,0)=0;
     595           2 :     incoord_to_hbcoord(2,1)=0;
     596           2 :     incoord_to_hbcoord(2,2)=1;
     597           2 :     action->log.printf("  GROUPA is list of donor atoms \n");
     598           4 :   } else if( sorder=="adh" ) {
     599           2 :     incoord_to_hbcoord(0,0)=-1;
     600           2 :     incoord_to_hbcoord(0,1)=1;
     601           2 :     incoord_to_hbcoord(0,2)=0;
     602           2 :     incoord_to_hbcoord(1,0)=1;
     603           2 :     incoord_to_hbcoord(1,1)=1;
     604           2 :     incoord_to_hbcoord(1,2)=0;
     605           2 :     incoord_to_hbcoord(2,0)=0;
     606           2 :     incoord_to_hbcoord(2,1)=0;
     607           2 :     incoord_to_hbcoord(2,2)=1;
     608           2 :     action->log.printf("  GROUPA is list of acceptor atoms \n");
     609           2 :   } else if( sorder=="hda" ) {
     610           2 :     incoord_to_hbcoord(0,0)=-1;
     611           2 :     incoord_to_hbcoord(0,1)=0;
     612           2 :     incoord_to_hbcoord(0,2)=1;
     613           2 :     incoord_to_hbcoord(1,0)=1;
     614           2 :     incoord_to_hbcoord(1,1)=0;
     615           2 :     incoord_to_hbcoord(1,2)=1;
     616           2 :     incoord_to_hbcoord(2,0)=0;
     617           2 :     incoord_to_hbcoord(2,1)=1;
     618           2 :     incoord_to_hbcoord(2,2)=0;
     619           2 :     action->log.printf("  GROUPA is list of hydrogen atoms \n");
     620             :   } else {
     621           0 :     plumed_error();
     622             :   }
     623             :   // Read in the regularisation parameter
     624          12 :   action->parse("REGULARISE",regulariser);
     625             : 
     626             :   // Read in the kernels
     627             :   double sqr2pi = sqrt(2*pi);
     628             :   double sqrt2pi3 = sqr2pi*sqr2pi*sqr2pi;
     629             :   std::string fname;
     630           6 :   action->parse("CLUSTERS", fname);
     631           6 :   double sfmax=0, ww;
     632           6 :   Vector cent;
     633           6 :   Tensor covar;
     634           6 :   IFile ifile;
     635           6 :   ifile.open(fname);
     636           6 :   ifile.allowIgnoredFields();
     637             :   while(true) {
     638         144 :     if( !ifile.scanField("height",ww) ) {
     639             :       break;
     640             :     }
     641          66 :     ifile.scanField("ptc",cent[0]);
     642          66 :     ifile.scanField("ssc",cent[1]);
     643          66 :     ifile.scanField("adc",cent[2]);
     644          66 :     ifile.scanField("sigma_ptc_ptc",covar[0][0]);
     645          66 :     ifile.scanField("sigma_ptc_ssc",covar[0][1]);
     646          66 :     ifile.scanField("sigma_ptc_adc",covar[0][2]);
     647          66 :     covar[1][0] = covar[0][1];
     648          66 :     ifile.scanField("sigma_ssc_ssc",covar[1][1]);
     649          66 :     ifile.scanField("sigma_ssc_adc",covar[1][2]);
     650          66 :     covar[2][0] = covar[0][2];
     651          66 :     covar[2][1] = covar[1][2];
     652          66 :     ifile.scanField("sigma_adc_adc",covar[2][2]);
     653          66 :     weight.push_back( ww / ( sqrt2pi3 * sqrt(covar.determinant()) ) );
     654          66 :     centers.push_back( cent );
     655          66 :     kmat.push_back( covar.inverse() );
     656             : 
     657          66 :     Vector eigval;
     658          66 :     Tensor eigvec;
     659          66 :     diagMatSym( covar, eigval, eigvec );
     660             :     unsigned ind_maxeval=0;
     661          66 :     double max_eval=eigval[0];
     662         198 :     for(unsigned i=1; i<3; ++i) {
     663         132 :       if( eigval[i]>max_eval ) {
     664             :         max_eval=eigval[i];
     665             :         ind_maxeval=i;
     666             :       }
     667             :     }
     668          66 :     double rcut = cent[2] + sqrt(2.0*DP2CUTOFF)*fabs(sqrt(max_eval)*eigvec(2,ind_maxeval));
     669          66 :     if( rcut > sfmax ) {
     670          24 :       sfmax = rcut;
     671             :     }
     672          66 :     ifile.scanField();
     673          66 :   }
     674           6 :   ifile.close();
     675           6 :   action->setLinkCellCutoff( false, sfmax );
     676          12 : }
     677             : 
     678       16316 : void HBPammMatrix::calculateWeight( const HBPammMatrix& data, const adjmat::AdjacencyMatrixInput& input, adjmat::MatrixOutput output ) {
     679       16316 :   Vector ddik, ddin, in_dists, hb_pamm_dists, hb_pamm_ders, real_ders;
     680       16316 :   ddin = input.pos;
     681       16316 :   in_dists[2] = ddin.modulo();
     682       16316 :   if( in_dists[2]<epsilon ) {
     683           0 :     return;
     684             :   }
     685             : 
     686       16316 :   output.val[0]=0;
     687       16316 :   Vector disp, der, tmp_der;
     688     1573048 :   for(unsigned i=0; i<input.natoms; ++i) {
     689     1556732 :     Vector ddij( input.extra_positions[i][0], input.extra_positions[i][1], input.extra_positions[i][2] );
     690     1556732 :     in_dists[0] = ddij.modulo();
     691     1556732 :     ddik = input.pbc->distance( input.pos, ddij );
     692     1556732 :     in_dists[1] = ddik.modulo();
     693     1556732 :     if( in_dists[1]<epsilon ) {
     694     1554476 :       continue;
     695             :     }
     696             : 
     697     1548504 :     hb_pamm_dists = matmul( data.incoord_to_hbcoord, in_dists );
     698     1548504 :     disp = hb_pamm_dists - data.centers[0];
     699     1548504 :     der = matmul( data.kmat[0], disp );
     700     1548504 :     double vv = data.weight[0]*exp( -dotProduct( disp, der ) / 2. );
     701     1548504 :     der *= -vv;
     702             : 
     703     1548504 :     double denom = data.regulariser + vv;
     704     6194016 :     for(unsigned j=0; j<3; ++j) {
     705     4645512 :       hb_pamm_ders[j] = der[j];
     706             :     }
     707    17033544 :     for(unsigned k=1; k<data.weight.size(); ++k) {
     708    15485040 :       disp = hb_pamm_dists - data.centers[k];
     709    15485040 :       tmp_der = matmul( data.kmat[k], disp );
     710    15485040 :       double tval = data.weight[k]*exp( -dotProduct( disp, tmp_der ) / 2. );
     711    15485040 :       denom += tval;
     712             :       hb_pamm_ders += -tmp_der*tval;
     713             :     }
     714     1548504 :     double vf = vv / denom;
     715     1548504 :     output.val[0] += vf;
     716     1548504 :     if( fabs(vf)<epsilon ) {
     717     1546248 :       continue;
     718             :     }
     719             :     // Now get derivatives
     720        2256 :     real_ders = matmul( der / denom - vf*hb_pamm_ders/denom, data.incoord_to_hbcoord );
     721             : 
     722             :     // And add the derivatives to the underlying atoms
     723        2256 :     Vector d1 = -(real_ders[0]/in_dists[0])*ddij - (real_ders[2]/in_dists[2])*ddin;
     724        2256 :     output.deriv[0] += d1[0];
     725        2256 :     output.deriv[1] += d1[1];
     726        2256 :     output.deriv[2] += d1[2];
     727        2256 :     Vector d2 = -(real_ders[1]/in_dists[1])*ddik + (real_ders[2]/in_dists[2])*ddin;
     728        2256 :     output.deriv[3] += d2[0];
     729        2256 :     output.deriv[4] += d2[1];
     730        2256 :     output.deriv[5] += d2[2];
     731             :     Vector d3 = (real_ders[0]/in_dists[0])*ddij + (real_ders[1]/in_dists[1])*ddik;
     732        2256 :     output.deriv[6+i*3+0] = d3[0];
     733        2256 :     output.deriv[6+i*3+1] = d3[1];
     734        2256 :     output.deriv[6+i*3+2] = d3[2];
     735        2256 :     Tensor vir = -(real_ders[0]/in_dists[0])*Tensor( ddij, ddij )
     736        2256 :                  -(real_ders[1]/in_dists[1])*Tensor( ddik, ddik )
     737        2256 :                  -(real_ders[2]/in_dists[2])*Tensor( ddin, ddin );
     738        2256 :     output.deriv[6 + 3*input.natoms + 0] += vir[0][0];
     739        2256 :     output.deriv[6 + 3*input.natoms + 1] += vir[0][1];
     740        2256 :     output.deriv[6 + 3*input.natoms + 2] += vir[0][2];
     741        2256 :     output.deriv[6 + 3*input.natoms + 3] += vir[1][0];
     742        2256 :     output.deriv[6 + 3*input.natoms + 4] += vir[1][1];
     743        2256 :     output.deriv[6 + 3*input.natoms + 5] += vir[1][2];
     744        2256 :     output.deriv[6 + 3*input.natoms + 6] += vir[2][0];
     745        2256 :     output.deriv[6 + 3*input.natoms + 7] += vir[2][1];
     746        2256 :     output.deriv[6 + 3*input.natoms + 8] += vir[2][2];
     747             :   }
     748             : }
     749             : 
     750             : class HBPammShortcut : public ActionShortcut {
     751             : public:
     752             :   static void registerKeywords( Keywords& keys );
     753             :   HBPammShortcut(const ActionOptions&);
     754             : };
     755             : 
     756             : PLUMED_REGISTER_ACTION(HBPammShortcut,"HBPAMM_SD")
     757             : PLUMED_REGISTER_ACTION(HBPammShortcut,"HBPAMM_SA")
     758             : PLUMED_REGISTER_ACTION(HBPammShortcut,"HBPAMM_SH")
     759             : 
     760          20 : void HBPammShortcut::registerKeywords( Keywords& keys ) {
     761          20 :   adjmat::AdjacencyMatrixBase<HBPammMatrix>::registerKeywords( keys );
     762          20 :   keys.remove("GROUP");
     763          20 :   keys.remove("GROUPA");
     764          20 :   keys.remove("GROUPB");
     765          20 :   keys.remove("GROUPC");
     766          20 :   keys.remove("ORDER");
     767          20 :   keys.remove("COMPONENTS");
     768          40 :   keys.reset_style("NL_CUTOFF","hidden");
     769          40 :   keys.reset_style("NL_STRIDE","hidden");
     770          20 :   keys.add("atoms","SITES","The list of atoms which can be part of a hydrogen bond.  When this command is used the set of atoms that can donate a "
     771             :            "hydrogen bond is assumed to be the same as the set of atoms that can form hydrogen bonds.  The atoms involved must be specified "
     772             :            "as a list of labels of multicolvar actions.  If you would just like to use "
     773             :            "the atomic positions you can use a DENSITY command to specify a group of atoms.  Specifying your atomic positions using labels of "
     774             :            "other multicolvar commands is useful, however, as you can then exploit a much wider "
     775             :            "variety of functions of the contact matrix as described in the documentation for CONTACT_MATRIX");
     776          20 :   keys.add("atoms","DONORS","The list of atoms which can donate a hydrogen bond.  The atoms involved must be specified "
     777             :            "as a list of labels of multicolvar actions.  If you would just like to use "
     778             :            "the atomic positions you can use a DENSITY command to specify a group of atoms.  Specifying your atomic positions using labels of "
     779             :            "other multicolvar commands is useful, however, as you can then exploit a much wider "
     780             :            "variety of functions of the contact matrix as described in the documentation for CONTACT_MATRIX");
     781          20 :   keys.add("atoms","ACCEPTORS","The list of atoms which can accept a hydrogen bond.  The atoms involved must be specified "
     782             :            "as a list of labels of multicolvar actions.  If you would just like to use "
     783             :            "the atomic positions you can use a DENSITY command to specify a group of atoms.  Specifying your atomic positions using labels of "
     784             :            "other multicolvar commands is useful, however, as you can then exploit a much wider "
     785             :            "variety of functions of the contact matrix as described in the documentation for CONTACT_MATRIX");
     786          20 :   keys.add("atoms","HYDROGENS","The list of hydrogen atoms that can form part of a hydrogen bond.  The atoms must be specified using a comma separated list, "
     787             :            "an index range or by using a GROUP action");
     788          20 :   multicolvar::MultiColvarShortcuts::shortcutKeywords( keys );
     789          20 :   keys.needsAction("HBPAMM_MATRIX");
     790          40 :   keys.setValueDescription("vector","a vector specifiying the number of hydrogen bonds each of the specified atoms participates within");
     791          20 : }
     792             : 
     793           6 : HBPammShortcut::HBPammShortcut(const ActionOptions&ao):
     794             :   Action(ao),
     795           6 :   ActionShortcut(ao) {
     796           6 :   std::string mwords = getShortcutLabel() + "_mat: HBPAMM_MATRIX";
     797           6 :   if( getName()=="HBPAMM_SD" ) {
     798             :     std::string site_str;
     799           4 :     parse("SITES",site_str);
     800           2 :     if( site_str.length()>0 ) {
     801           2 :       mwords += " GROUP=" + site_str;
     802             :     } else {
     803             :       std::string d_str;
     804           0 :       parse("DONORS",d_str);
     805           0 :       mwords += " GROUPA=" + d_str;
     806             :       std::string a_str;
     807           0 :       parse("ACCEPTORS",a_str);
     808           0 :       mwords += " GROUPB=" + a_str;
     809             :     }
     810             :     std::string h_str;
     811           2 :     parse("HYDROGENS",h_str);
     812           2 :     mwords += " GROUPC=" + h_str + " ORDER=dah";
     813           4 :   } else if( getName()=="HBPAMM_SA" ) {
     814             :     std::string site_str;
     815           4 :     parse("SITES",site_str);
     816           2 :     if( site_str.length()>0 ) {
     817           2 :       mwords += " GROUP=" + site_str;
     818             :     } else {
     819             :       std::string a_str;
     820           0 :       parse("ACCEPTORS",a_str);
     821           0 :       mwords += " GROUPA=" + a_str;
     822             :       std::string d_str;
     823           0 :       parse("DONORS",d_str);
     824           0 :       mwords += " GROUPB=" + d_str;
     825             :     }
     826             :     std::string h_str;
     827           2 :     parse("HYDROGENS",h_str);
     828           2 :     mwords += " GROUPC=" + h_str + " ORDER=adh";
     829           2 :   } else if( getName()=="HBPAMM_SH" ) {
     830             :     std::string h_str;
     831           2 :     parse("HYDROGENS",h_str);
     832           2 :     mwords += " GROUPA=" + h_str + " ORDER=hda";
     833             :     std::string site_str;
     834           4 :     parse("SITES",site_str);
     835           2 :     if( site_str.length()>0 ) {
     836           2 :       mwords += " GROUPB=" + site_str;
     837           2 :       mwords += " GROUPC=" + site_str;
     838             :     } else {
     839             :       std::string d_str;
     840           0 :       parse("DONORS",d_str);
     841           0 :       mwords += " GROUPB=" + d_str;
     842             :       std::string a_str;
     843           0 :       parse("ACCEPTORS",a_str);
     844           0 :       mwords += " GROUPC=" + a_str;
     845             :     }
     846             :   }
     847             :   std::map<std::string,std::string> keymap;
     848           6 :   multicolvar::MultiColvarShortcuts::readShortcutKeywords( keymap, this );
     849          12 :   readInputLine( mwords + " " + convertInputLineToString() );
     850           6 :   ActionWithValue* mb=plumed.getActionSet().selectWithLabel<ActionWithValue*>( getShortcutLabel() + "_mat");
     851           6 :   plumed_assert( mb );
     852             :   std::string nsize;
     853           6 :   Tools::convert( (mb->copyOutput(getShortcutLabel() + "_mat"))->getShape()[1], nsize );
     854           6 :   readInputLine( getShortcutLabel() + "_ones: ONES SIZE=" + nsize );
     855          18 :   readInputLine( getShortcutLabel() + ": MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + "_mat," + getShortcutLabel() + "_ones");
     856          12 :   multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(), getShortcutLabel(), "", keymap, this );
     857          20 : }
     858             : 
     859             : }
     860             : }

Generated by: LCOV version 1.16