LCOV - code coverage report
Current view: top level - symfunc - LocalSteinhardt.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 92 119 77.3 %
Date: 2025-11-25 13:55:50 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2015-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 "core/ActionShortcut.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "multicolvar/MultiColvarShortcuts.h"
      25             : #include "core/PlumedMain.h"
      26             : #include "core/ActionSet.h"
      27             : #include "core/ActionWithValue.h"
      28             : 
      29             : namespace PLMD {
      30             : namespace symfunc {
      31             : 
      32             : //+PLUMEDOC MCOLVARF LOCAL_Q1
      33             : /*
      34             : Calculate the local degree of order around an atoms by taking the average dot product between the q_1 vector on the central atom and the q_3 vector on the atoms in the first coordination sphere.
      35             : 
      36             : \par Examples
      37             : 
      38             : */
      39             : //+ENDPLUMEDOC
      40             : 
      41             : //+PLUMEDOC MCOLVARF LOCAL_Q3
      42             : /*
      43             : Calculate the local degree of order around an atoms by taking the average dot product between the q_3 vector on the central atom and the q_3 vector on the atoms in the first coordination sphere.
      44             : 
      45             : The \ref Q3 command allows one to calculate one complex vectors for each of the atoms in your system that describe the degree of order in the coordination sphere
      46             : around a particular atom. The difficulty with these vectors comes when combining the order parameters from all of the individual atoms/molecules so as to get a
      47             : measure of the global degree of order for the system. The simplest way of doing this - calculating the average Steinhardt parameter - can be problematic. If one is
      48             : examining nucleation say only the order parameters for those atoms in the nucleus will change significantly when the nucleus forms. The order parameters for the
      49             : atoms in the surrounding liquid will remain pretty much the same. As such if one models a small nucleus embedded in a very large amount of solution/melt any
      50             : change in the average order parameter will be negligible. Substantial changes in the value of this average can be observed in simulations of nucleation but only
      51             : because the number of atoms is relatively small.
      52             : 
      53             : When the average \ref Q3 parameter is used to bias the dynamics a problems
      54             : can occur. These averaged coordinates cannot distinguish between the correct,
      55             : single-nucleus pathway and a concerted pathway in which all the atoms rearrange
      56             : themselves into their solid-like configuration simultaneously. This second type
      57             : of pathway would be impossible in reality because there is a large entropic
      58             : barrier that prevents concerted processes like this from happening.  However,
      59             : in the finite sized systems that are commonly simulated this barrier is reduced
      60             : substantially. As a result in simulations where average Steinhardt parameters
      61             : are biased there are often quite dramatic system size effects
      62             : 
      63             : If one wants to simulate nucleation using some form on
      64             : biased dynamics what is really required is an order parameter that measures:
      65             : 
      66             : - Whether or not the coordination spheres around atoms are ordered
      67             : - Whether or not the atoms that are ordered are clustered together in a crystalline nucleus
      68             : 
      69             : \ref LOCAL_AVERAGE and \ref NLINKS are variables that can be combined with the Steinhardt parameters allow to calculate variables that satisfy these requirements.
      70             : LOCAL_Q3 is another variable that can be used in these sorts of calculations. The LOCAL_Q3 parameter for a particular atom is a number that measures the extent to
      71             : which the orientation of the atoms in the first coordination sphere of an atom match the orientation of the central atom.  It does this by calculating the following
      72             : quantity for each of the atoms in the system:
      73             : 
      74             : \f[
      75             :  s_i = \frac{ \sum_j \sigma( r_{ij} ) \sum_{m=-3}^3 q_{3m}^{*}(i)q_{3m}(j) }{ \sum_j \sigma( r_{ij} ) }
      76             : \f]
      77             : 
      78             : where \f$q_{3m}(i)\f$ and \f$q_{3m}(j)\f$ are the 3rd order Steinhardt vectors calculated for atom \f$i\f$ and atom \f$j\f$ respectively and the asterisk denotes complex
      79             : conjugation.  The function
      80             : \f$\sigma( r_{ij} )\f$ is a \ref switchingfunction that acts on the distance between atoms \f$i\f$ and \f$j\f$.  The parameters of this function should be set
      81             : so that it the function is equal to one when atom \f$j\f$ is in the first coordination sphere of atom \f$i\f$ and is zero otherwise.  The sum in the numerator
      82             : of this expression is the dot product of the Steinhardt parameters for atoms \f$i\f$ and \f$j\f$ and thus measures the degree to which the orientations of these
      83             : adjacent atoms is correlated.
      84             : 
      85             : \par Examples
      86             : 
      87             : The following command calculates the average value of the LOCAL_Q3 parameter for the 64 Lennard Jones atoms in the system under study and prints this
      88             : quantity to a file called colvar.
      89             : 
      90             : \plumedfile
      91             : Q3 SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=q3
      92             : LOCAL_Q3 SPECIES=q3 SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LABEL=lq3
      93             : PRINT ARG=lq3.mean FILE=colvar
      94             : \endplumedfile
      95             : 
      96             : The following input calculates the distribution of LOCAL_Q3 parameters at any given time and outputs this information to a file.
      97             : 
      98             : \plumedfile
      99             : Q3 SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=q3
     100             : LOCAL_Q3 SPECIES=q3 SWITCH={RATIONAL D_0=1.3 R_0=0.2} HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1} LABEL=lq3
     101             : PRINT ARG=lq3.* FILE=colvar
     102             : \endplumedfile
     103             : 
     104             : The following calculates the LOCAL_Q3 parameters for atoms 1-5 only. For each of these atoms comparisons of the geometry of the coordination sphere
     105             : are done with those of all the other atoms in the system.  The final quantity is the average and is outputted to a file
     106             : 
     107             : \plumedfile
     108             : Q3 SPECIESA=1-5 SPECIESB=1-64 D_0=1.3 R_0=0.2 LABEL=q3a
     109             : Q3 SPECIESA=6-64 SPECIESB=1-64 D_0=1.3 R_0=0.2 LABEL=q3b
     110             : 
     111             : LOCAL_Q3 SPECIES=q3a,q3b SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LOWMEM LABEL=w3
     112             : PRINT ARG=w3.* FILE=colvar
     113             : \endplumedfile
     114             : 
     115             : */
     116             : //+ENDPLUMEDOC
     117             : 
     118             : //+PLUMEDOC MCOLVARF LOCAL_Q4
     119             : /*
     120             : Calculate the local degree of order around an atoms by taking the average dot product between the q_4 vector on the central atom and the q_4 vector on the atoms in the first coordination sphere.
     121             : 
     122             : The \ref Q4 command allows one to calculate one complex vectors for each of the atoms in your system that describe the degree of order in the coordination sphere
     123             : around a particular atom. The difficulty with these vectors comes when combining the order parameters from all of the individual atoms/molecules so as to get a
     124             : measure of the global degree of order for the system. The simplest way of doing this - calculating the average Steinhardt parameter - can be problematic. If one is
     125             : examining nucleation say only the order parameters for those atoms in the nucleus will change significantly when the nucleus forms. The order parameters for the
     126             : atoms in the surrounding liquid will remain pretty much the same. As such if one models a small nucleus embedded in a very large amount of solution/melt any
     127             : change in the average order parameter will be negligible. Substantial changes in the value of this average can be observed in simulations of nucleation but only
     128             : because the number of atoms is relatively small.
     129             : 
     130             : When the average \ref Q4 parameter is used to bias the dynamics a problems
     131             : can occur. These averaged coordinates cannot distinguish between the correct,
     132             : single-nucleus pathway and a concerted pathway in which all the atoms rearrange
     133             : themselves into their solid-like configuration simultaneously. This second type
     134             : of pathway would be impossible in reality because there is a large entropic
     135             : barrier that prevents concerted processes like this from happening.  However,
     136             : in the finite sized systems that are commonly simulated this barrier is reduced
     137             : substantially. As a result in simulations where average Steinhardt parameters
     138             : are biased there are often quite dramatic system size effects
     139             : 
     140             : If one wants to simulate nucleation using some form on
     141             : biased dynamics what is really required is an order parameter that measures:
     142             : 
     143             : - Whether or not the coordination spheres around atoms are ordered
     144             : - Whether or not the atoms that are ordered are clustered together in a crystalline nucleus
     145             : 
     146             : \ref LOCAL_AVERAGE and \ref NLINKS are variables that can be combined with the Steinhardt parameters allow to calculate variables that satisfy these requirements.
     147             : LOCAL_Q4 is another variable that can be used in these sorts of calculations. The LOCAL_Q4 parameter for a particular atom is a number that measures the extent to
     148             : which the orientation of the atoms in the first coordination sphere of an atom match the orientation of the central atom.  It does this by calculating the following
     149             : quantity for each of the atoms in the system:
     150             : 
     151             : \f[
     152             :  s_i = \frac{ \sum_j \sigma( r_{ij} ) \sum_{m=-4}^4 q_{4m}^{*}(i)q_{4m}(j) }{ \sum_j \sigma( r_{ij} ) }
     153             : \f]
     154             : 
     155             : where \f$q_{4m}(i)\f$ and \f$q_{4m}(j)\f$ are the fourth order Steinhardt vectors calculated for atom \f$i\f$ and atom \f$j\f$ respectively and the asterisk denotes
     156             : complex conjugation.  The function
     157             : \f$\sigma( r_{ij} )\f$ is a \ref switchingfunction that acts on the distance between atoms \f$i\f$ and \f$j\f$.  The parameters of this function should be set
     158             : so that it the function is equal to one when atom \f$j\f$ is in the first coordination sphere of atom \f$i\f$ and is zero otherwise.  The sum in the numerator
     159             : of this expression is the dot product of the Steinhardt parameters for atoms \f$i\f$ and \f$j\f$ and thus measures the degree to which the orientations of these
     160             : adjacent atoms is correlated.
     161             : 
     162             : \par Examples
     163             : 
     164             : The following command calculates the average value of the LOCAL_Q4 parameter for the 64 Lennard Jones atoms in the system under study and prints this
     165             : quantity to a file called colvar.
     166             : 
     167             : \plumedfile
     168             : Q4 SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=q4
     169             : LOCAL_Q4 SPECIES=q4 SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LABEL=lq4
     170             : PRINT ARG=lq4.mean FILE=colvar
     171             : \endplumedfile
     172             : 
     173             : The following input calculates the distribution of LOCAL_Q4 parameters at any given time and outputs this information to a file.
     174             : 
     175             : \plumedfile
     176             : Q4 SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=q4
     177             : LOCAL_Q4 SPECIES=q4 SWITCH={RATIONAL D_0=1.3 R_0=0.2} HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1} LABEL=lq4
     178             : PRINT ARG=lq4.* FILE=colvar
     179             : \endplumedfile
     180             : 
     181             : The following calculates the LOCAL_Q4 parameters for atoms 1-5 only. For each of these atoms comparisons of the geometry of the coordination sphere
     182             : are done with those of all the other atoms in the system.  The final quantity is the average and is outputted to a file
     183             : 
     184             : \plumedfile
     185             : Q4 SPECIESA=1-5 SPECIESB=1-64 D_0=1.3 R_0=0.2 LABEL=q4a
     186             : Q4 SPECIESA=6-64 SPECIESB=1-64 D_0=1.3 R_0=0.2 LABEL=q4b
     187             : 
     188             : LOCAL_Q4 SPECIES=q4a,q4b SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LOWMEM LABEL=w4
     189             : PRINT ARG=w4.* FILE=colvar
     190             : \endplumedfile
     191             : 
     192             : */
     193             : //+ENDPLUMEDOC
     194             : 
     195             : //+PLUMEDOC MCOLVARF LOCAL_Q6
     196             : /*
     197             : Calculate the local degree of order around an atoms by taking the average dot product between the q_6 vector on the central atom and the q_6 vector on the atoms in the first coordination sphere.
     198             : 
     199             : The \ref Q6 command allows one to calculate one complex vectors for each of the atoms in your system that describe the degree of order in the coordination sphere
     200             : around a particular atom. The difficulty with these vectors comes when combining the order parameters from all of the individual atoms/molecules so as to get a
     201             : measure of the global degree of order for the system. The simplest way of doing this - calculating the average Steinhardt parameter - can be problematic. If one is
     202             : examining nucleation say only the order parameters for those atoms in the nucleus will change significantly when the nucleus forms. The order parameters for the
     203             : atoms in the surrounding liquid will remain pretty much the same. As such if one models a small nucleus embedded in a very large amount of solution/melt any
     204             : change in the average order parameter will be negligible. Substantial changes in the value of this average can be observed in simulations of nucleation but only
     205             : because the number of atoms is relatively small.
     206             : 
     207             : When the average \ref Q6 parameter is used to bias the dynamics a problems
     208             : can occur. These averaged coordinates cannot distinguish between the correct,
     209             : single-nucleus pathway and a concerted pathway in which all the atoms rearrange
     210             : themselves into their solid-like configuration simultaneously. This second type
     211             : of pathway would be impossible in reality because there is a large entropic
     212             : barrier that prevents concerted processes like this from happening.  However,
     213             : in the finite sized systems that are commonly simulated this barrier is reduced
     214             : substantially. As a result in simulations where average Steinhardt parameters
     215             : are biased there are often quite dramatic system size effects
     216             : 
     217             : If one wants to simulate nucleation using some form on
     218             : biased dynamics what is really required is an order parameter that measures:
     219             : 
     220             : - Whether or not the coordination spheres around atoms are ordered
     221             : - Whether or not the atoms that are ordered are clustered together in a crystalline nucleus
     222             : 
     223             : \ref LOCAL_AVERAGE and \ref NLINKS are variables that can be combined with the Steinhardt parameters allow to calculate variables that satisfy these requirements.
     224             : LOCAL_Q6 is another variable that can be used in these sorts of calculations. The LOCAL_Q6 parameter for a particular atom is a number that measures the extent to
     225             : which the orientation of the atoms in the first coordination sphere of an atom match the orientation of the central atom.  It does this by calculating the following
     226             : quantity for each of the atoms in the system:
     227             : 
     228             : \f[
     229             :  s_i = \frac{ \sum_j \sigma( r_{ij} ) \sum_{m=-6}^6 q_{6m}^{*}(i)q_{6m}(j) }{ \sum_j \sigma( r_{ij} ) }
     230             : \f]
     231             : 
     232             : where \f$q_{6m}(i)\f$ and \f$q_{6m}(j)\f$ are the sixth order Steinhardt vectors calculated for atom \f$i\f$ and atom \f$j\f$ respectively and the asterisk denotes
     233             : complex conjugation.  The function
     234             : \f$\sigma( r_{ij} )\f$ is a \ref switchingfunction that acts on the distance between atoms \f$i\f$ and \f$j\f$.  The parameters of this function should be set
     235             : so that it the function is equal to one when atom \f$j\f$ is in the first coordination sphere of atom \f$i\f$ and is zero otherwise.  The sum in the numerator
     236             : of this expression is the dot product of the Steinhardt parameters for atoms \f$i\f$ and \f$j\f$ and thus measures the degree to which the orientations of these
     237             : adjacent atoms is correlated.
     238             : 
     239             : \par Examples
     240             : 
     241             : The following command calculates the average value of the LOCAL_Q6 parameter for the 64 Lennard Jones atoms in the system under study and prints this
     242             : quantity to a file called colvar.
     243             : 
     244             : \plumedfile
     245             : Q6 SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=q6
     246             : LOCAL_Q6 SPECIES=q6 SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LABEL=lq6
     247             : PRINT ARG=lq6.mean FILE=colvar
     248             : \endplumedfile
     249             : 
     250             : The following input calculates the distribution of LOCAL_Q6 parameters at any given time and outputs this information to a file.
     251             : 
     252             : \plumedfile
     253             : Q6 SPECIES=1-64 D_0=1.3 R_0=0.2 LABEL=q6
     254             : LOCAL_Q6 SPECIES=q6 SWITCH={RATIONAL D_0=1.3 R_0=0.2} HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1} LABEL=lq6
     255             : PRINT ARG=lq6.* FILE=colvar
     256             : \endplumedfile
     257             : 
     258             : The following calculates the LOCAL_Q6 parameters for atoms 1-5 only. For each of these atoms comparisons of the geometry of the coordination sphere
     259             : are done with those of all the other atoms in the system.  The final quantity is the average and is outputted to a file
     260             : 
     261             : \plumedfile
     262             : Q6 SPECIESA=1-5 SPECIESB=1-64 D_0=1.3 R_0=0.2 LABEL=q6a
     263             : Q6 SPECIESA=6-64 SPECIESB=1-64 D_0=1.3 R_0=0.2 LABEL=q6b
     264             : 
     265             : LOCAL_Q6 SPECIES=q6a,q6b SWITCH={RATIONAL D_0=1.3 R_0=0.2} MEAN LOWMEM LABEL=w6
     266             : PRINT ARG=w6.* FILE=colvar
     267             : \endplumedfile
     268             : 
     269             : */
     270             : //+ENDPLUMEDOC
     271             : 
     272             : class LocalSteinhardt : public ActionShortcut {
     273             : private:
     274             :   std::string getSymbol( const int& m ) const ;
     275             :   std::string getArgsForStack( const int& l, const std::string& lab ) const;
     276             : public:
     277             :   static void registerKeywords( Keywords& keys );
     278             :   explicit LocalSteinhardt(const ActionOptions&);
     279             : };
     280             : 
     281             : PLUMED_REGISTER_ACTION(LocalSteinhardt,"LOCAL_Q1")
     282             : PLUMED_REGISTER_ACTION(LocalSteinhardt,"LOCAL_Q3")
     283             : PLUMED_REGISTER_ACTION(LocalSteinhardt,"LOCAL_Q4")
     284             : PLUMED_REGISTER_ACTION(LocalSteinhardt,"LOCAL_Q6")
     285             : 
     286          20 : void LocalSteinhardt::registerKeywords( Keywords& keys ) {
     287          20 :   ActionShortcut::registerKeywords( keys );
     288          40 :   keys.add("optional","SPECIES","");
     289          40 :   keys.add("optional","SPECIESA","");
     290          40 :   keys.add("optional","SPECIESB","");
     291          40 :   keys.add("optional","SWITCH","This keyword is used if you want to employ an alternative to the continuous swiching function defined above. "
     292             :            "The following provides information on the \\ref switchingfunction that are available. "
     293             :            "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords.");
     294          40 :   keys.addFlag("LOWMEM",false,"this flag does nothing and is present only to ensure back-compatibility");
     295          20 :   multicolvar::MultiColvarShortcuts::shortcutKeywords( keys );
     296          20 :   keys.needsAction("CONTACT_MATRIX");
     297          20 :   keys.needsAction("MATRIX_PRODUCT");
     298          20 :   keys.needsAction("GROUP");
     299          20 :   keys.needsAction("ONES");
     300          20 :   keys.needsAction("OUTER_PRODUCT");
     301          20 :   keys.needsAction("VSTACK");
     302          20 :   keys.needsAction("CONCATENATE");
     303          20 :   keys.needsAction("CUSTOM");
     304          20 :   keys.needsAction("TRANSPOSE");
     305          20 :   keys.needsAction("MATRIX_VECTOR_PRODUCT");
     306          20 : }
     307             : 
     308          98 : std::string LocalSteinhardt::getSymbol( const int& m ) const {
     309          98 :   if( m<0 ) {
     310             :     std::string num;
     311          40 :     Tools::convert( -1*m, num );
     312          40 :     return "n" + num;
     313          58 :   } else if( m>0 ) {
     314             :     std::string num;
     315          49 :     Tools::convert( m, num );
     316          49 :     return "p" + num;
     317             :   }
     318           9 :   return "0";
     319             : }
     320             : 
     321           9 : std::string LocalSteinhardt::getArgsForStack( const int& l, const std::string& sp_lab ) const {
     322             :   std::string numstr;
     323           9 :   Tools::convert( l, numstr );
     324          18 :   std::string data_mat = " ARG=" + sp_lab + "_sp.rm-n" + numstr + "," + sp_lab + "_sp.im-n" + numstr;
     325         107 :   for(int i=-l+1; i<=l; ++i) {
     326          98 :     numstr = getSymbol( i );
     327         196 :     data_mat += "," + sp_lab + "_sp.rm-" + numstr + "," + sp_lab + "_sp.im-" + numstr;
     328             :   }
     329           9 :   return data_mat;
     330             : }
     331             : 
     332           7 : LocalSteinhardt::LocalSteinhardt(const ActionOptions& ao):
     333             :   Action(ao),
     334           7 :   ActionShortcut(ao) {
     335             :   bool lowmem;
     336           7 :   parseFlag("LOWMEM",lowmem);
     337           7 :   if( lowmem ) {
     338           0 :     warning("LOWMEM flag is deprecated and is no longer required for this action");
     339             :   }
     340             :   // Get the Q value
     341             :   int l;
     342          14 :   Tools::convert( getName().substr(7), l);
     343             :   // Create a vector filled with ones
     344             :   std::string twolplusone;
     345           7 :   Tools::convert( 2*(2*l+1), twolplusone );
     346          14 :   readInputLine( getShortcutLabel() + "_uvec: ONES SIZE=" + twolplusone );
     347             :   // Read in species keyword
     348             :   std::string sp_str;
     349          14 :   parse("SPECIES",sp_str);
     350             :   std::string spa_str;
     351          14 :   parse("SPECIESA",spa_str);
     352           7 :   if( sp_str.length()>0 ) {
     353             :     // Create a group with these atoms
     354          12 :     readInputLine( getShortcutLabel() + "_grp: GROUP ATOMS=" + sp_str );
     355           6 :     std::vector<std::string> sp_lab = Tools::getWords(sp_str, "\t\n ,");
     356             :     // This creates the stash to hold all the vectors
     357           6 :     if( sp_lab.size()==1 ) {
     358             :       // The lengths of all the vectors in a vector
     359          12 :       readInputLine( getShortcutLabel() + "_nmat: OUTER_PRODUCT ARG=" + sp_lab[0] + "_norm," + getShortcutLabel() + "_uvec");
     360             :       // The unormalised vectors
     361          12 :       readInputLine( getShortcutLabel() + "_uvecs: VSTACK" + getArgsForStack( l, sp_lab[0] ) );
     362             :     } else {
     363           0 :       std::string len_vec = getShortcutLabel() + "_mags: CONCATENATE ARG=" + sp_lab[0] + "_norm";
     364           0 :       for(unsigned i=1; i<sp_lab.size(); ++i) {
     365           0 :         len_vec += "," + sp_lab[i] + "_norm";
     366             :       }
     367             :       // This is the vector that contains all the magnitudes
     368           0 :       readInputLine( len_vec );
     369           0 :       std::string concat_str = getShortcutLabel() + "_uvecs: CONCATENATE";
     370           0 :       for(unsigned i=0; i<sp_lab.size(); ++i) {
     371             :         std::string snum;
     372           0 :         Tools::convert( i+1, snum );
     373           0 :         concat_str += " MATRIX" + snum + "1=" + getShortcutLabel() + "_uvecs" + snum;
     374           0 :         readInputLine( getShortcutLabel() + "_uvecs" + snum + ": VSTACK" + getArgsForStack( l, sp_lab[i] ) );
     375             :       }
     376             :       // And the normalising matrix by taking the column vector of magnitudes and multiplying by the row vector of ones
     377           0 :       readInputLine( getShortcutLabel() + "_nmat: OUTER_PRODUCT ARG=" + getShortcutLabel() + "_mags," + getShortcutLabel() + "_uvec");
     378             :       // The unormalised vectors
     379           0 :       readInputLine( concat_str );
     380             :     }
     381             :     // Now normalise all the vectors by doing Hadammard "product" with normalising matrix
     382          12 :     readInputLine( getShortcutLabel() + "_vecs: CUSTOM ARG=" + getShortcutLabel() + "_uvecs," + getShortcutLabel() + "_nmat FUNC=x/y PERIODIC=NO");
     383             :     // And transpose the matrix
     384          12 :     readInputLine( getShortcutLabel() + "_vecsT: TRANSPOSE ARG=" + getShortcutLabel() + "_vecs" );
     385             :     std::string sw_str;
     386           6 :     parse("SWITCH",sw_str);
     387          12 :     readInputLine( getShortcutLabel() + "_cmap: CONTACT_MATRIX GROUP=" + sp_str + " SWITCH={" + sw_str + "}");
     388             :     // And the matrix of dot products
     389          12 :     readInputLine( getShortcutLabel() + "_dpmat: MATRIX_PRODUCT ARG=" + getShortcutLabel() + "_vecs," + getShortcutLabel() + "_vecsT" );
     390           7 :   } else if( spa_str.length()>0 ) {
     391             :     // Create a group with these atoms
     392           2 :     readInputLine( getShortcutLabel() + "_grp: GROUP ATOMS=" + spa_str );
     393             :     std::string spb_str;
     394           2 :     parse("SPECIESB",spb_str);
     395           1 :     if( spb_str.length()==0 ) {
     396           0 :       plumed_merror("need both SPECIESA and SPECIESB in input");
     397             :     }
     398           1 :     std::vector<std::string> sp_laba = Tools::getWords(spa_str, "\t\n ,");
     399           1 :     std::vector<std::string> sp_labb = Tools::getWords(spb_str, "\t\n ,");
     400           1 :     if( sp_laba.size()==1 ) {
     401             :       // The matrix that is used for normalising
     402           2 :       readInputLine( getShortcutLabel() + "_nmatA: OUTER_PRODUCT ARG=" +  sp_laba[0] + "_norm," + getShortcutLabel() + "_uvec");
     403             :       // The unormalised vectors
     404           2 :       readInputLine( getShortcutLabel() + "_uvecsA: VSTACK" + getArgsForStack( l, sp_laba[0] ) );
     405             :     } else {
     406           0 :       std::string len_vec = getShortcutLabel() + "_magsA: CONCATENATE ARG=" + sp_laba[0] + "_norm";
     407           0 :       for(unsigned i=1; i<sp_laba.size(); ++i) {
     408           0 :         len_vec += "," + sp_laba[i] + "_norm";
     409             :       }
     410             :       //  This is the vector that contains all the magnitudes
     411           0 :       readInputLine( len_vec );
     412           0 :       std::string concat_str = getShortcutLabel() + "_uvecsA: CONCATENATE";
     413           0 :       for(unsigned i=0; i<sp_laba.size(); ++i) {
     414             :         std::string snum;
     415           0 :         Tools::convert( i+1, snum );
     416           0 :         concat_str += " MATRIX" + snum + "1=" + getShortcutLabel() + "_uvecsA" + snum;
     417           0 :         readInputLine( getShortcutLabel() + "_uvecsA" + snum + ": VSTACK" + getArgsForStack( l, sp_laba[i] ) );
     418             :       }
     419             :       // And the normalising matrix by taking the column vector of magnitudes and multiplying by the row vector of ones
     420           0 :       readInputLine( getShortcutLabel() + "_nmatA: OUTER_PRODUCT ARG=" + getShortcutLabel() + "_magsA," + getShortcutLabel() + "_uvec");
     421             :       // The unormalised vector
     422           0 :       readInputLine( concat_str );
     423             :     }
     424             :     // Now normalise all the vectors by doing Hadammard "product" with normalising matrix
     425           2 :     readInputLine( getShortcutLabel() + "_vecsA: CUSTOM ARG=" + getShortcutLabel() + "_uvecsA," + getShortcutLabel() + "_nmatA FUNC=x/y PERIODIC=NO");
     426             :     // Now do second matrix
     427           1 :     if( sp_labb.size()==1 ) {
     428           0 :       readInputLine( getShortcutLabel() + "_nmatB: OUTER_PRODUCT ARG=" +  sp_laba[0] + "_norm," + getShortcutLabel() + "_uvec");
     429           0 :       readInputLine( getShortcutLabel() + "_uvecsBT: VSTACK" + getArgsForStack( l, sp_labb[0] ) );
     430           0 :       readInputLine( getShortcutLabel() + "_uvecsB: TRANSPOSE ARG=" + getShortcutLabel() + "_uvecsBT");
     431             :     } else {
     432           2 :       std::string len_vec = getShortcutLabel() + "_magsB: CONCATENATE ARG=" +  sp_labb[0] + "_norm";
     433           2 :       for(unsigned i=1; i<sp_labb.size(); ++i) {
     434           2 :         len_vec += "," + sp_labb[i] + "_norm";
     435             :       }
     436             :       //  This is the vector that contains all the magnitudes
     437           1 :       readInputLine( len_vec );
     438           1 :       std::string concat_str = getShortcutLabel() + "_uvecsB: CONCATENATE";
     439           3 :       for(unsigned i=0; i<sp_labb.size(); ++i) {
     440             :         std::string snum;
     441           2 :         Tools::convert( i+1, snum );
     442           4 :         concat_str += " MATRIX1" + snum + "=" + getShortcutLabel() + "_uvecsB" + snum;
     443           4 :         readInputLine( getShortcutLabel() + "_uvecsBT" + snum + ": VSTACK" + getArgsForStack( l, sp_labb[i] ) );
     444           4 :         readInputLine( getShortcutLabel() + "_uvecsB" + snum + ": TRANSPOSE ARG=" + getShortcutLabel() + "_uvecsBT" + snum );
     445             :       }
     446             :       // And the normalising matrix
     447           2 :       readInputLine( getShortcutLabel() + "_nmatB: OUTER_PRODUCT ARG=" + getShortcutLabel() + "_uvec," + getShortcutLabel() + "_magsB");
     448             :       // The unormalised vectors
     449           1 :       readInputLine( concat_str );
     450             :     }
     451             :     // Now normalise all the vectors by doing Hadammard "product" with normalising matrix
     452           2 :     readInputLine( getShortcutLabel() + "_vecsB: CUSTOM ARG=" + getShortcutLabel() + "_uvecsB," + getShortcutLabel() + "_nmatB FUNC=x/y PERIODIC=NO");
     453             :     std::string sw_str;
     454           1 :     parse("SWITCH",sw_str);
     455           2 :     readInputLine( getShortcutLabel() + "_cmap: CONTACT_MATRIX GROUPA=" + spa_str + " GROUPB=" + spb_str + " SWITCH={" + sw_str + "}");
     456           2 :     readInputLine( getShortcutLabel() + "_dpmat: MATRIX_PRODUCT ARG=" + getShortcutLabel() + "_vecsA," + getShortcutLabel() + "_vecsB" );
     457           1 :   }
     458             : 
     459             :   // Now create the product matrix
     460          14 :   readInputLine( getShortcutLabel() + "_prod: CUSTOM ARG=" + getShortcutLabel() + "_cmap," + getShortcutLabel() + "_dpmat FUNC=x*y PERIODIC=NO");
     461             :   // Now the sum of coordination numbers times the switching functions
     462           7 :   ActionWithValue* av = plumed.getActionSet().selectWithLabel<ActionWithValue*>( getShortcutLabel() + "_cmap");
     463           7 :   plumed_assert( av && av->getNumberOfComponents()>0 && (av->copyOutput(0))->getRank()==2 );
     464             :   std::string size;
     465           7 :   Tools::convert( (av->copyOutput(0))->getShape()[1], size );
     466          14 :   readInputLine( getShortcutLabel() + "_ones: ONES SIZE=" + size );
     467          14 :   readInputLine( getShortcutLabel() + ": MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() +"_prod," + getShortcutLabel() +"_ones");
     468             :   // And just the sum of the coordination numbers
     469          14 :   readInputLine( getShortcutLabel() + "_denom: MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + "_cmap," + getShortcutLabel() +"_ones");
     470             :   // And matheval to get the final quantity
     471          14 :   readInputLine( getShortcutLabel() + "_av: CUSTOM ARG=" + getShortcutLabel() + "," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     472             :   // And this expands everything
     473          14 :   multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(), getShortcutLabel() + "_av", "", this );
     474           7 : }
     475             : 
     476             : }
     477             : }

Generated by: LCOV version 1.16