LCOV - code coverage report
Current view: top level - multicolvar - CoordAngles.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 97 99 98.0 %
Date: 2026-11-05 10:19:04 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2018 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 "core/ActionShortcut.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "MultiColvarShortcuts.h"
      25             : 
      26             : //+PLUMEDOC MCOLVAR COORD_ANGLES
      27             : /*
      28             : Calculate functions of the distribution of angles between bonds in the first coordination spheres of a set of atoms
      29             : 
      30             : This action ncan be used to calculate functions, $g$, such as:
      31             : 
      32             : $$
      33             : f(x) = \sum_{ijk} s(r_{ij})s(r_{jk}) g(\theta_{ijk})
      34             : $$
      35             : 
      36             : where $\theta_{ijk}$ is the angle between the vector connecting atom $i$ and $j$ and the vector connecting atom $j$ and atom $k$ and
      37             : where $s(r)$ is a switching function.  The switching functions in the expression above ensure that we can calculate all the angles in the first coordination
      38             : spheres of an atom using an input like the one shown below:
      39             : 
      40             : ```plumed
      41             : c1: COORD_ANGLES ...
      42             :   CATOMS=1 GROUP=2-100 SWITCH={RATIONAL R_0=1.0} SUM
      43             : ...
      44             : PRINT ARG=c1.sum FILE=colvar
      45             : ```
      46             : 
      47             : The input above will output the sum of all the angles in the first coordination sphere.
      48             : 
      49             : __As you can see if you expand the shortcut in the input above, the calculation of the above sum is obtained by calculating a [Hadamard product](https://en.wikipedia.org/wiki/Hadamard_product_(matrices))
      50             : of an [OUTER_PRODUCT](OUTER_PRODUCT.md) matrix and a [MATRIX_PRODUCT](MATRIX_PRODUCT.md).  We have thus deprecated this action as we believe that there is value in learning to use the more complicated syntax
      51             : directly.__
      52             : 
      53             : */
      54             : //+ENDPLUMEDOC
      55             : 
      56             : namespace PLMD {
      57             : namespace multicolvar {
      58             : 
      59             : class CoordAngles : public ActionShortcut {
      60             : public:
      61             :   static void registerKeywords(Keywords& keys);
      62             :   static void pruneShortcuts(Keywords& keys);
      63             :   explicit CoordAngles(const ActionOptions&);
      64             : private:
      65             :   /// Create the actions that calculate the angles between the bonds that connect catom to the atoms in group
      66             :   void createAngles( const std::string& lab, const std::string& catom, const std::vector<std::string>& group, const std::string& switch_input );
      67             : };
      68             : 
      69             : PLUMED_REGISTER_ACTION(CoordAngles,"COORD_ANGLES")
      70             : 
      71          18 : void CoordAngles::registerKeywords(Keywords& keys) {
      72          18 :   ActionShortcut::registerKeywords( keys );
      73          18 :   keys.add("atoms","CATOMS","all the angles between the bonds that radiate out from these central atom are computed");
      74          18 :   keys.add("atoms","GROUP","a list of angls between pairs of bonds connecting one of the atoms specified using the CATOM command and two of the atoms specified here are computed");
      75          18 :   keys.add("compulsory","SWITCH","the switching function specifies that only those bonds that have a length that is less than a certain threshold are considered");
      76          18 :   MultiColvarShortcuts::shortcutKeywords( keys );
      77          18 :   pruneShortcuts( keys );
      78          18 :   keys.needsAction("DISTANCE");
      79          18 :   keys.needsAction("COMBINE");
      80          18 :   keys.needsAction("CUSTOM");
      81          18 :   keys.needsAction("VSTACK");
      82          18 :   keys.needsAction("TRANSPOSE");
      83          18 :   keys.needsAction("OUTER_PRODUCT");
      84          18 :   keys.needsAction("MATRIX_PRODUCT");
      85          18 :   keys.setDeprecated("MATRIX_PRODUCT");
      86          18 : }
      87             : 
      88          29 : void CoordAngles::pruneShortcuts(Keywords& keys) {
      89          29 :   keys.remove("ALT_MIN");
      90          29 :   keys.remove("MIN");
      91          29 :   keys.remove("MAX");
      92          29 :   keys.remove("HIGHEST");
      93          29 :   keys.remove("LOWEST");
      94          29 : }
      95             : 
      96          15 : void CoordAngles::createAngles( const std::string& lab, const std::string& catom, const std::vector<std::string>& group, const std::string& switch_input ) {
      97             :   // Create the list of atoms.  An atom is not bonded to itself so the central atom is not used as one of its own neighbours
      98             :   std::string atlist;
      99          15 :   unsigned k=1;
     100         295 :   for(unsigned j=0; j<group.size(); ++j) {
     101         280 :     if( group[j]==catom ) {
     102           4 :       continue;
     103             :     }
     104             :     std::string num;
     105         276 :     Tools::convert( k, num );
     106         828 :     atlist += " ATOMS" + num + "=" + catom + "," + group[j];
     107         276 :     k++;
     108             :   }
     109          15 :   if( k==1 ) {
     110           1 :     error("there are no atoms in GROUP that are different from the central atom " + catom );
     111             :   }
     112             :   // Calculate the distances
     113          14 :   readInputLine( lab + "_dd: DISTANCE" + atlist );
     114             :   // Transform with the switching function
     115          42 :   readInputLine( lab + "_sw: LESS_THAN ARG=" + lab + "_dd SWITCH={" + switch_input +"}");
     116             :   // Now get the normalised vectors
     117          28 :   readInputLine( lab + "_comp: DISTANCE" + atlist + " COMPONENTS");
     118          56 :   readInputLine( lab + "_norm2: COMBINE ARG=" + lab + "_comp.x" + "," + lab + "_comp.y," + lab + "_comp.z POWERS=2,2,2 PERIODIC=NO");
     119          28 :   readInputLine( lab + "_norm: CUSTOM ARG=" + lab + "_norm2 FUNC=sqrt(x) PERIODIC=NO");
     120          42 :   readInputLine( lab + "_norm_x: CUSTOM ARG=" + lab + "_comp.x," + lab + "_norm FUNC=x/y PERIODIC=NO");
     121          42 :   readInputLine( lab + "_norm_y: CUSTOM ARG=" + lab + "_comp.y," + lab + "_norm FUNC=x/y PERIODIC=NO");
     122          42 :   readInputLine( lab + "_norm_z: CUSTOM ARG=" + lab + "_comp.z," + lab + "_norm FUNC=x/y PERIODIC=NO");
     123          56 :   readInputLine( lab + "_stack: VSTACK ARG=" + lab + "_norm_x" + "," + lab + "_norm_y," + lab + "_norm_z");
     124          28 :   readInputLine( lab + "_stackT: TRANSPOSE ARG=" + lab + "_stack");
     125             :   // Create the matrix of weights
     126          42 :   readInputLine( lab + "_swd: OUTER_PRODUCT ELEMENTS_ON_DIAGONAL_ARE_ZERO ARG=" + lab + "_sw," + lab + "_sw");
     127             :   // Avoid double counting
     128          28 :   readInputLine( lab + "_wmat: CUSTOM ARG=" + lab + "_swd FUNC=0.5*x PERIODIC=NO");
     129             :   // And the matrix of dot products and the angles
     130          42 :   readInputLine( lab + "_dpmat: MATRIX_PRODUCT ELEMENTS_ON_DIAGONAL_ARE_ZERO ARG=" + lab + "_stack," + lab + "_stackT");
     131          43 :   readInputLine( lab + "_angles: CUSTOM ARG=" + lab + "_dpmat FUNC=acos(x) PERIODIC=NO");
     132          14 : }
     133             : 
     134          11 : CoordAngles::CoordAngles(const ActionOptions& ao):
     135             :   Action(ao),
     136          11 :   ActionShortcut(ao) {
     137             :   // Parse the central atoms
     138          11 :   std::vector<std::string> catoms;
     139          11 :   parseVector("CATOMS",catoms);
     140          11 :   Tools::interpretRanges(catoms);
     141          11 :   if( catoms.size()==0 ) {
     142           0 :     error("no central atoms were found in CATOMS");
     143             :   }
     144             :   // Parse the coordination sphere
     145          11 :   std::vector<std::string> group;
     146          11 :   parseVector("GROUP",group);
     147          11 :   Tools::interpretRanges(group);
     148          11 :   if( group.size()==0 ) {
     149           0 :     error("no atoms were found in GROUP");
     150             :   }
     151             :   std::string switch_input;
     152          12 :   parse("SWITCH",switch_input);
     153             :   // Read the input
     154          11 :   Keywords keys;
     155          11 :   MultiColvarShortcuts::shortcutKeywords( keys );
     156          11 :   pruneShortcuts( keys );
     157             :   bool do_mean;
     158          22 :   parseFlag("MEAN",do_mean);
     159             :   std::map<std::string,std::string> keymap;
     160          11 :   readShortcutKeywords( keys, keymap );
     161          11 :   if( do_mean ) {
     162          11 :     keymap.insert(std::pair<std::string,std::string>("SUM",""));
     163             :   }
     164          11 :   const std::string lab = getShortcutLabel();
     165          11 :   if( catoms.size()==1 ) {
     166           8 :     createAngles( lab, catoms[0], group, switch_input );
     167          14 :     MultiColvarShortcuts::expandFunctions( lab, lab + "_angles", lab + "_wmat", keymap, this );
     168           7 :     if( do_mean ) {
     169           8 :       readInputLine( lab + "_denom: SUM ARG=" + lab + "_wmat PERIODIC=NO");
     170          17 :       readInputLine( lab + "_mean: CUSTOM ARG=" + lab + "_sum," + lab + "_denom FUNC=x/y PERIODIC=NO");
     171             :     }
     172             :     return;
     173             :   }
     174             :   // The angles are calculated separately for each of the central atoms, as the angles between bonds
     175             :   // that start from different atoms are not wanted.  Everything that we output is a sum over the angles
     176             :   // so the final quantities are the sums of the quantities for each central atom.
     177           3 :   std::vector<std::string> outputs, prefixes;
     178          10 :   for(unsigned i=0; i<catoms.size(); ++i) {
     179             :     std::string num;
     180           7 :     Tools::convert( i+1, num );
     181          14 :     prefixes.push_back( lab + "_c" + num );
     182           7 :     createAngles( prefixes[i], catoms[i], group, switch_input );
     183           7 :     std::vector<std::string> thisoutputs;
     184          14 :     MultiColvarShortcuts::expandFunctions( prefixes[i], prefixes[i] + "_angles", prefixes[i] + "_wmat", keymap, this, &thisoutputs );
     185           7 :     if( i==0 ) {
     186           3 :       outputs = thisoutputs;
     187             :     }
     188           7 :     if( do_mean ) {
     189           6 :       readInputLine( prefixes[i] + "_denom: SUM ARG=" + prefixes[i] + "_wmat PERIODIC=NO");
     190             :     }
     191           7 :   }
     192          10 :   for(const auto& suffix : outputs ) {
     193           7 :     std::string args = prefixes[0] + suffix;
     194          15 :     for(unsigned i=1; i<prefixes.size(); ++i) {
     195          16 :       args += "," + prefixes[i] + suffix;
     196             :     }
     197          28 :     readInputLine( lab + suffix + ": COMBINE ARG=" + args + " PERIODIC=NO");
     198             :   }
     199           3 :   if( do_mean ) {
     200           1 :     std::string args = prefixes[0] + "_denom";
     201           2 :     for(unsigned i=1; i<prefixes.size(); ++i) {
     202           2 :       args += "," + prefixes[i] + "_denom";
     203             :     }
     204           2 :     readInputLine( lab + "_denom: COMBINE ARG=" + args + " PERIODIC=NO");
     205           4 :     readInputLine( lab + "_mean: CUSTOM ARG=" + lab + "_sum," + lab + "_denom FUNC=x/y PERIODIC=NO");
     206             :   }
     207          28 : }
     208             : 
     209             : }
     210             : }

Generated by: LCOV version 1.16