LCOV - code coverage report
Current view: top level - symfunc - CoordinationNumbers.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 82 88 93.2 %
Date: 2025-04-08 21:11:17 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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             : #include "CoordinationNumbers.h"
      23             : #include "multicolvar/MultiColvarShortcuts.h"
      24             : #include "core/ActionWithValue.h"
      25             : #include "core/ActionRegister.h"
      26             : #include "core/PlumedMain.h"
      27             : #include "core/ActionSet.h"
      28             : 
      29             : #include <string>
      30             : #include <cmath>
      31             : 
      32             : namespace PLMD {
      33             : namespace symfunc {
      34             : 
      35             : //+PLUMEDOC MCOLVAR COORDINATIONNUMBER
      36             : /*
      37             : Calculate the coordination numbers of atoms so that you can then calculate functions of the distribution of
      38             :  coordination numbers such as the minimum, the number less than a certain quantity and so on.
      39             : 
      40             : The coordination number of a atom $i$ is the number of atoms that are within a certain cutoff distance of it.
      41             : This quantity can be calculated as follows:
      42             : 
      43             : $$
      44             : s_i = \sum_j \sigma(r_{ij})
      45             : $$
      46             : 
      47             : where $r_{ij}$ is the distance between atoms $i$ and $j$ and $\sigma$ is a switching function. The typical switching
      48             : function that is used in metadynamics is this one:
      49             : 
      50             : $$
      51             : s(r) = \frac{ 1 - \left(\frac{r-d_0}{r_0}\right)^n } { 1 - \left(\frac{r-d_0}{r_0}\right)^m }
      52             : $$
      53             : 
      54             : The following example shows how you can use this shortcut action to calculate and print the coordination numbers of
      55             : one hundred atoms with themselves:
      56             : 
      57             : ```plumed
      58             : c: COORDINATIONNUMBER SPECIES=1-100 R_0=1.0
      59             : DUMPATOMS ATOMS=c ARG=c FILE=coords.xyz
      60             : ```
      61             : 
      62             : This input will produce an output file called coords that contains the coordination numbers of the 100 input atoms.  The cutoff
      63             : that is used to calculate the coordination number in this case is 1.0.
      64             : 
      65             : The vectors that are output by the COORDINATIONNUMBER shortcut can be used in the input for many other functions that are within
      66             : PLUMED. In addition, in order to ensure compatibility with older versions of PLUMED you can add additional keywords on the input
      67             : line for COORDINATIONNUMBER in order to calculate various functions of the input.  For example, the following input tells plumed
      68             : ato calculate the coordination numbers of atoms 1-100 with themselves.  The minimum coordination number is then calculated.
      69             : 
      70             : ```plumed
      71             : c: COORDINATIONNUMBER SPECIES=1-100 R_0=1.0 MIN={BETA=0.1}
      72             : ```
      73             : 
      74             : By constrast, this input tells plumed to calculate how many atoms from 1-100 are within 3.0 of each of the atoms
      75             : from 101-110.  In the first 101 is the central atom, in the second 102 is the central atom and so on.  The
      76             : number of coordination numbers that are more than 6 is then computed.
      77             : 
      78             : ```plumed
      79             : c: COORDINATIONNUMBER SPECIESA=101-110 SPECIESB=1-100 R_0=3.0 MORE_THAN={RATIONAL R_0=6.0 NN=6 MM=12 D_0=0}
      80             : ```
      81             : 
      82             : Notice that these inputs both use shortcuts.  If you expand the inputs above you can determine the set of actions
      83             : that are being used to calculate each of the quantities of interest.
      84             : 
      85             : */
      86             : //+ENDPLUMEDOC
      87             : 
      88             : //+PLUMEDOC MCOLVAR COORDINATION_MOMENTS
      89             : /*
      90             : Calculate moments of the distribution of distances in the first coordination sphere
      91             : 
      92             : This is the CV that was developed by White and Voth and is described in the paper in the bibliograhy below. This action provides a way of indirectly biasing radial distribution functions and computes the following function
      93             : 
      94             : $$
      95             : s_i = \sum_j \sigma(r_{ij})r_{ij}^k
      96             : $$
      97             : 
      98             : where $k$ is the value that is input using the R_POWER keyword, $r_{ij}$ is the distance between atoms $i$ and $j$ and $\sigma$ is a switching function.
      99             : 
     100             : The following example shows how this action can be used.
     101             : 
     102             : ```plumed
     103             : cn0: COORDINATIONNUMBER SPECIES=1-10 SWITCH={RATIONAL R_0=1.0 D_MAX=8} MEAN
     104             : cn1: COORDINATION_MOMENTS SPECIES=1-10 SWITCH={RATIONAL R_0=1.0 D_MAX=8} R_POWER=1 MEAN
     105             : cn2: COORDINATION_MOMENTS SPECIES=1-10 SWITCH={RATIONAL R_0=1.0 D_MAX=8} R_POWER=2 MEAN
     106             : PRINT ARG=cn0.mean,cn1.mean,cn2.mean STRIDE=1 FILE=cn_out
     107             : ```
     108             : 
     109             : As you can see, it works similarlly to [COORDINATIONNUMBER](COORDINATIONNUMBER.md).
     110             : 
     111             : 
     112             : */
     113             : //+ENDPLUMEDOC
     114             : 
     115             : 
     116             : PLUMED_REGISTER_ACTION(CoordinationNumbers,"COORDINATIONNUMBER")
     117             : PLUMED_REGISTER_ACTION(CoordinationNumbers,"COORDINATION_MOMENTS")
     118             : 
     119         176 : void CoordinationNumbers::shortcutKeywords( Keywords& keys ) {
     120         176 :   ActionShortcut::registerKeywords( keys );
     121         176 :   keys.add("atoms-3","SPECIES","the list of atoms for which the symmetry function is being calculated and the atoms that can be in the environments");
     122         176 :   keys.add("atoms-4","SPECIESA","the list of atoms for which the symmetry function is being calculated.  This keyword must be used in conjunction with SPECIESB, which specifies the atoms that are in the environment.");
     123         176 :   keys.add("atoms-4","SPECIESB","the list of atoms that can be in the environments of each of the atoms for which the symmetry function is being calculated.  This keyword must be used in conjunction with SPECIESA, which specifies the atoms for which the symmetry function is being calculated.");
     124         176 :   keys.add("compulsory","NN","6","The n parameter of the switching function ");
     125         176 :   keys.add("compulsory","MM","0","The m parameter of the switching function; 0 implies 2*NN");
     126         176 :   keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
     127         176 :   keys.add("compulsory","R_0","The r_0 parameter of the switching function");
     128         176 :   keys.add("optional","SWITCH","the switching function that it used in the construction of the contact matrix");
     129         352 :   keys.linkActionInDocs("SWITCH","LESS_THAN");
     130         176 :   multicolvar::MultiColvarShortcuts::shortcutKeywords( keys );
     131         176 :   keys.needsAction("CONTACT_MATRIX");
     132         176 :   keys.needsAction("GROUP");
     133         176 :   keys.addDOI("10.1021/ct500320c");
     134         176 : }
     135             : 
     136         108 : void CoordinationNumbers::expandMatrix( const bool& components, const std::string& lab, const std::string& sp_str,
     137             :                                         const std::string& spa_str, const std::string& spb_str, ActionShortcut* action ) {
     138         108 :   if( sp_str.length()==0 && spa_str.length()==0 ) {
     139           0 :     return;
     140             :   }
     141             : 
     142         108 :   std::string matinp = lab  + "_mat: CONTACT_MATRIX";
     143         108 :   if( sp_str.length()>0 ) {
     144          69 :     matinp += " GROUP=" + sp_str;
     145         138 :     action->readInputLine( lab + "_grp: GROUP ATOMS=" + sp_str );
     146          39 :   } else if( spa_str.length()>0 ) {
     147          78 :     matinp += " GROUPA=" + spa_str + " GROUPB=" + spb_str;
     148          78 :     action->readInputLine( lab + "_grp: GROUP ATOMS=" + spa_str );
     149             :   }
     150             : 
     151             :   std::string sw_str;
     152         216 :   action->parse("SWITCH",sw_str);
     153         108 :   if( sw_str.length()>0 ) {
     154         162 :     matinp += " SWITCH={" + sw_str + "}";
     155             :   } else {
     156             :     std::string r0;
     157          54 :     action->parse("R_0",r0);
     158             :     std::string d0;
     159          54 :     action->parse("D_0",d0);
     160          27 :     if( r0.length()==0 ) {
     161           0 :       action->error("missing switching function parameters use SWITCH/R_0");
     162             :     }
     163             :     std::string nn;
     164          54 :     action->parse("NN",nn);
     165             :     std::string mm;
     166          27 :     action->parse("MM",mm);
     167          54 :     matinp += " R_0=" + r0 + " D_0=" + d0 + " NN=" + nn + " MM=" + mm;
     168             :   }
     169         108 :   if( components ) {
     170             :     matinp += " COMPONENTS";
     171             :   }
     172         108 :   action->readInputLine( matinp );
     173             : }
     174             : 
     175          67 : void CoordinationNumbers::registerKeywords( Keywords& keys ) {
     176          67 :   shortcutKeywords( keys );
     177          67 :   keys.add("compulsory","R_POWER","the power to which you want to raise the distance");
     178          67 :   keys.addFlag("LOWMEM",false,"this flag does nothing and is present only to ensure back-compatibility");
     179          67 :   keys.add("optional","MOMENTS","the list of moments that you would like to calculate");
     180         134 :   keys.addOutputComponent("moment","MOMENTS","scalar","the moments of the distribution");
     181          67 :   keys.needsAction("MATRIX_VECTOR_PRODUCT");
     182          67 :   keys.needsAction("ONES");
     183          67 :   keys.needsAction("MOMENTS");
     184         134 :   keys.setValueDescription("vector","the coordination numbers of the specified atoms");
     185          67 : }
     186             : 
     187          45 : CoordinationNumbers::CoordinationNumbers(const ActionOptions& ao):
     188             :   Action(ao),
     189          45 :   ActionShortcut(ao) {
     190             :   bool lowmem;
     191          45 :   parseFlag("LOWMEM",lowmem);
     192          45 :   if( lowmem ) {
     193           0 :     warning("LOWMEM flag is deprecated and is no longer required for this action");
     194             :   }
     195             :   // Setup the contract matrix if that is what is needed
     196             :   std::string matlab, sp_str, specA, specB;
     197          45 :   parse("SPECIES",sp_str);
     198          45 :   parse("SPECIESA",specA);
     199          90 :   parse("SPECIESB",specB);
     200          45 :   if( sp_str.length()>0 || specA.length()>0 ) {
     201          45 :     matlab = getShortcutLabel() + "_mat";
     202          45 :     bool comp=false;
     203          45 :     if( getName()=="COORDINATION_MOMENTS" ) {
     204           3 :       comp=true;
     205           6 :       matlab = getShortcutLabel() + "_mat";
     206             :     }
     207          45 :     expandMatrix( comp, getShortcutLabel(), sp_str, specA, specB, this );
     208             :   } else {
     209           0 :     error("missing atoms input use SPECIES or SPECIESA/SPECIESB");
     210             :   }
     211          45 :   ActionWithValue* mb=plumed.getActionSet().selectWithLabel<ActionWithValue*>( matlab );
     212          45 :   if( !mb ) {
     213           0 :     error("could not find action with name " + matlab );
     214             :   }
     215          45 :   Value*  arg=mb->copyOutput(0);
     216          45 :   if( arg->getRank()!=2 || arg->hasDerivatives() ) {
     217           0 :     error("the input to this action should be a matrix or scalar");
     218             :   }
     219             :   // Create vector of ones to multiply input matrix by
     220             :   std::string nones;
     221          45 :   Tools::convert( arg->getShape()[1], nones );
     222          90 :   readInputLine( getShortcutLabel() + "_ones: ONES SIZE=" + nones );
     223          45 :   if( getName()=="COORDINATION_MOMENTS" ) {
     224             :     // Calculate the lengths of the vectors
     225             :     std::string r_power;
     226           3 :     parse("R_POWER",r_power);
     227           9 :     readInputLine( getShortcutLabel() + "_pow: CUSTOM ARG=" + matlab + ".x," + matlab + ".y," + matlab + ".z," + matlab + ".w VAR=x,y,z,w "
     228           6 :                    + "PERIODIC=NO FUNC=w*(sqrt(x*x+y*y+z*z)^" + r_power +")");
     229           6 :     matlab = getShortcutLabel() + "_pow";
     230             :   }
     231             :   // Calcualte coordination numbers as matrix vector times vector of ones
     232          90 :   readInputLine( getShortcutLabel() + ": MATRIX_VECTOR_PRODUCT  ARG=" + matlab + "," + getShortcutLabel() + "_ones");
     233             :   std::vector<std::string> moments;
     234          45 :   parseVector("MOMENTS",moments);
     235          45 :   Tools::interpretRanges( moments );
     236          90 :   readInputLine( getShortcutLabel() + "_caverage: MEAN ARG=" + getShortcutLabel() + " PERIODIC=NO");
     237          47 :   for(unsigned i=0; i<moments.size(); ++i) {
     238           4 :     readInputLine( getShortcutLabel() + "_diffpow-" + moments[i] + ": CUSTOM ARG=" + getShortcutLabel() + "," + getShortcutLabel() + "_caverage PERIODIC=NO FUNC=(x-y)^" + moments[i] );
     239           4 :     readInputLine( getShortcutLabel() + "_moment-" + moments[i] + ": MEAN ARG=" + getShortcutLabel() + "_diffpow-" + moments[i] + " PERIODIC=NO");
     240             :   }
     241             :   // Read in all the shortcut stuff
     242             :   std::map<std::string,std::string> keymap;
     243          45 :   multicolvar::MultiColvarShortcuts::readShortcutKeywords( keymap, this );
     244          90 :   multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(), getShortcutLabel(), "", keymap, this );
     245          90 : }
     246             : 
     247             : 
     248             : }
     249             : }

Generated by: LCOV version 1.16