LCOV - code coverage report
Current view: top level - gridtools - PairEntropy.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 53 58 91.4 %
Date: 2026-11-05 10:19:04 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2017 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 "RDF.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "core/ActionShortcut.h"
      25             : 
      26             : //+PLUMEDOC MCOLVAR PAIRENTROPY
      27             : /*
      28             : Calculate the KL Entropy from the radial distribution function
      29             : 
      30             : This shortcut provides an implementation of the CV that is described in the paper that is cited below. Although, the implementation
      31             : that is offered here is not the one that was used to produce the results described in that paper the values produced by this
      32             : implementation have been tested against the implementation that was used in the paper, which can be found [here](https://sites.google.com/site/pablompiaggi/scripts/pair-entropy/pair-entropy-cv?authuser=0).
      33             : __If you want to use this implementation for a production application we would recommend further testing.__
      34             : 
      35             : An example input that calculates and prints the PAIRENTROPY CV is shown below:
      36             : 
      37             : ```plumed
      38             : pp: PAIRENTROPY ...
      39             :    GROUP=1-108 MAXR=2.0 GRID_BIN=20
      40             :    BANDWIDTH=0.13 KERNEL=gaussian
      41             : ...
      42             : PRINT ARG=pp FILE=colvar
      43             : ```
      44             : 
      45             : By expanding the shortct in the input above you can see how features that are already avaialble within PLUMED can be reused to calculate this CV.  The resulting implementation is likely slower than the
      46             : direct implementation that is available [here](https://sites.google.com/site/pablompiaggi/scripts/pair-entropy/pair-entropy-cv?authuser=0).  We hope, however, that this implementation helps others to understand
      47             : how this CV is constructed.
      48             : 
      49             : If you would like to calculate the pair entropy based on the radial distribution of the atoms in GROUPB around GROUPA you can use an input similar to the one shown below:
      50             : 
      51             : ```plumed
      52             : pp: PAIRENTROPY ...
      53             :    GROUPA=1-108 GROUPB=109-300
      54             :    GRID_BIN=20 MAXR=2.0
      55             :    BANDWIDTH=0.13 KERNEL=gaussian
      56             :    DENSITY=1.0
      57             : ...
      58             : PRINT ARG=pp FILE=colvar
      59             : ```
      60             : 
      61             : Notice that we have also used the `DENSITY` keyword to set the background density that is used when normalizing the radial distribution function explicity to 1 atom$/nm^{3}$.
      62             : When this keyword is not used, this density is calculated by dividing the number of atoms by the volume of the box as you can see if you expand the shortcut in the
      63             : first input above.
      64             : 
      65             : */
      66             : //+ENDPLUMEDOC
      67             : 
      68             : //+PLUMEDOC MCOLVAR PAIRENTROPIES
      69             : /*
      70             : Calculate the KL entropy from the RDF around each of the atoms
      71             : 
      72             : This shortcut can be used to calculate the descriptors of local structure that are described in the paper that is cited below.  This is not the implementation
      73             : that was used when that paper was written.  However, the values that this implementation generates were tested against the values that the implementation that
      74             : was used in the paper, which can be found [here](https://sites.google.com/site/pablompiaggi/scripts/pair-entropy/pair-entropy-fingerprint?authuser=0).
      75             : __If you want to use this implementation for a production application we would recommend further testing.__
      76             : 
      77             : The following example illustrates how this shortcut can be used to calculate and print the descriptor for a set of 64 atoms:
      78             : 
      79             : ```plumed
      80             : pp: PAIRENTROPIES ...
      81             :    GROUP=1-64 MAXR=2
      82             :    GRID_BIN=20 BANDWIDTH=0.13
      83             :    KERNEL=gaussian
      84             :    DENSITY=1.0
      85             : ...
      86             : DUMPATOMS ATOMS=1-64 ARG=pp FILE=p_entropies.xyz
      87             : ```
      88             : 
      89             : If you expand the shortcut you will notice that this shortcut creates __a lot__ of actions.  It is thus likely to be considerably slower than the implementation
      90             : [here](https://sites.google.com/site/pablompiaggi/scripts/pair-entropy/pair-entropy-fingerprint?authuser=0). However, we hope that the implementation here is a
      91             : useful reference implementation that anyone interested in using this method can use to test their implementations of it. In addition, we hope that the shortcut
      92             : illustrates how the calculation of this complex descriptor can be broken down into a set of simpler calculations.
      93             : 
      94             : Notice also that you do not need to use the DENSITY keyword to set the density as has been done above.  You can instead use an input like the one below:
      95             : 
      96             : ```plumed
      97             : pp: PAIRENTROPIES ...
      98             :    GROUP=1-64 MAXR=2
      99             :    GRID_BIN=20 BANDWIDTH=0.13
     100             :    KERNEL=gaussian
     101             : ...
     102             : DUMPATOMS ATOMS=1-64 ARG=pp FILE=p_entropies.xyz
     103             : ```
     104             : 
     105             : As you can see, if you expand the shortcut in the above input, the density in this case is calculated by dividing the number of atoms by the volume of the box.
     106             : 
     107             : */
     108             : //+ENDPLUMEDOC
     109             : 
     110             : namespace PLMD {
     111             : namespace gridtools {
     112             : 
     113             : class PairEntropy : public ActionShortcut {
     114             : public:
     115             :   static void registerKeywords( Keywords& keys );
     116             :   explicit PairEntropy(const ActionOptions&ao);
     117             : };
     118             : 
     119             : PLUMED_REGISTER_ACTION(PairEntropy,"PAIRENTROPY")
     120             : PLUMED_REGISTER_ACTION(PairEntropy,"PAIRENTROPIES")
     121             : 
     122          13 : void PairEntropy::registerKeywords( Keywords& keys ) {
     123          13 :   RDF::registerKeywords( keys );
     124          13 :   keys.needsAction("RDF");
     125          13 :   keys.addDeprecatedKeyword("SIGMA","BANDWIDTH");
     126          13 :   if( keys.getDisplayName()=="PAIRENTROPIES" ) {
     127          10 :     keys.setValueDescription("grid","the KL-entropy that is computed from the radial distribution function of each atom (a function on a grid with one point per atom)");
     128             :   } else {
     129          16 :     keys.setValueDescription("scalar","the KL-entropy that is computed from the radial distribution function");
     130             :   }
     131          13 :   keys.addDOI("10.1103/PhysRevLett.119.015701");
     132          13 :   keys.needsAction("INTERPOLATE_GRID");
     133          13 :   keys.needsAction("INTEGRATE_GRID");
     134          13 :   keys.needsAction("MARGINAL");
     135          13 : }
     136             : 
     137           5 : PairEntropy::PairEntropy(const ActionOptions&ao):
     138             :   Action(ao),
     139           5 :   ActionShortcut(ao) {
     140             :   std::string ref_str, ref_name;
     141          10 :   parse("REFERENCE",ref_name);
     142           5 :   if( ref_name.length()>0 ) {
     143           0 :     ref_str = "REFERENCE=" + ref_name;
     144             :   } else {
     145          11 :     ref_name = getShortcutLabel() + "_rdf";
     146             :   }
     147             :   // Read in the atoms and get the number of atoms that we are using
     148             :   std::string atom_str, group_str;
     149          10 :   parse("GROUP",group_str);
     150           5 :   if( group_str.length()>0 ) {
     151          11 :     atom_str="GROUP=" + group_str;
     152             :   } else {
     153             :     std::string groupa_str, groupb_str;
     154           0 :     parse("GROUPA",groupa_str);
     155           0 :     parse("GROUPB",groupb_str);
     156           0 :     atom_str="GROUPA=" + groupa_str + " GROUPB=" + groupb_str;
     157             :   }
     158             :   // Read in all other keywords and create the RDF object
     159           6 :   std::string maxr, nbins, dens, bw="", kernel;
     160           5 :   parse("MAXR",maxr);
     161           5 :   parse("GRID_BIN",nbins);
     162           5 :   parse("DENSITY",dens);
     163           5 :   parse("BANDWIDTH",bw);
     164          11 :   parse("KERNEL",kernel);
     165             :   std::string dens_str;
     166           5 :   if( dens.length()>0 ) {
     167           2 :     dens_str = " DENSITY=" + dens;
     168             :   }
     169           6 :   std::string noaverage="";
     170           5 :   if( getName()=="PAIRENTROPIES" ) {
     171             :     noaverage = " NO_AVERAGE";
     172             :   }
     173          30 :   readInputLine( getShortcutLabel() + "_rdf: RDF " + atom_str + " KERNEL={" + kernel + "} GRID_BIN=" + nbins + " MAXR=" + maxr + dens_str + " BANDWIDTH=" + bw + " " + ref_str + noaverage);
     174             :   // Get the shape of the matrix
     175           5 :   std::vector<std::string> shape_str(2);
     176           4 :   RDF::getDistanceMatrixShape( getShortcutLabel() + "_rdf_mat", this, shape_str );
     177             :   // And compute the two functions we are integrating (we use two matheval objects here and sum them in order to avoid nans from taking logarithms of zero)
     178          12 :   readInputLine( getShortcutLabel() + "_conv_t1: CUSTOM ARG=" + getShortcutLabel() + "_rdf," + ref_name + "_x2 FUNC=x*y*log(x) PERIODIC=NO");
     179          12 :   readInputLine( getShortcutLabel() + "_conv_t2: CUSTOM ARG=" + getShortcutLabel() + "_rdf," + ref_name + "_x2 FUNC=(1-x)*y PERIODIC=NO");
     180          12 :   readInputLine( getShortcutLabel() + "_conv: CUSTOM ARG=" + getShortcutLabel() + "_conv_t1," + getShortcutLabel() + "_conv_t2 FUNC=x+y PERIODIC=NO");
     181             :   // Calculate number of midpoints
     182             :   unsigned nb;
     183           4 :   Tools::convert( nbins, nb );
     184           4 :   Tools::convert( nb-1, nbins );
     185             :   // Calculate grid spacing
     186             :   double rmax;
     187           4 :   Tools::convert( maxr, rmax );
     188           4 :   double spacing = rmax / nb;
     189             :   std::string fmid, lmid;
     190           4 :   Tools::convert( 0.5*spacing, fmid );
     191           4 :   Tools::convert( rmax-0.5*spacing, lmid );
     192           4 :   if( getName()=="PAIRENTROPIES" ) {
     193             :     int iatoms;
     194           1 :     Tools::convert( shape_str[0], iatoms );
     195             :     std::string num;
     196           1 :     Tools::convert( iatoms-1, num );
     197             :     // Now integrate using trapezium rule
     198           6 :     readInputLine( getShortcutLabel() + "_midp: INTERPOLATE_GRID ARG=" + getShortcutLabel() + "_conv INTERPOLATION_TYPE=linear GRID_MIN=" + fmid + ",0 GRID_MAX=" + lmid + "," + num + " GRID_BIN=" + nbins + "," + num ); // First interpolate onto midpoints
     199           3 :     readInputLine( getShortcutLabel() + "_int: MARGINAL ARG=" + getShortcutLabel() + "_midp DIR=2");
     200             :   } else {
     201             :     // Now integrate using trapezium rule
     202          12 :     readInputLine( getShortcutLabel() + "_midp: INTERPOLATE_GRID ARG=" + getShortcutLabel() + "_conv INTERPOLATION_TYPE=linear GRID_MIN=" + fmid +" GRID_MAX=" + lmid + " GRID_BIN=" + nbins ); // First interpolate onto midpoints
     203           9 :     readInputLine( getShortcutLabel() + "_int: INTEGRATE_GRID ARG=" + getShortcutLabel() + "_midp PERIODIC=NO"); // And then integrate
     204             :   }
     205             :   // And multiply by final normalizing constant
     206             :   std::string norm_str;
     207           4 :   if( dens.length()>0 ) {
     208           0 :     norm_str = " FUNC=-2*pi*x*" + dens;
     209             :   } else {
     210           8 :     norm_str = "," + ref_name + "_vol FUNC=-(2*pi*x/y)*" + shape_str[1];
     211             :   }
     212          12 :   readInputLine( getShortcutLabel() + ": CUSTOM PERIODIC=NO ARG=" + getShortcutLabel() + "_int" + norm_str );
     213          20 : }
     214             : 
     215             : }
     216             : }

Generated by: LCOV version 1.16