LCOV - code coverage report
Current view: top level - symfunc - Steinhardt.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 106 119 89.1 %
Date: 2025-12-04 11:19:34 Functions: 6 8 75.0 %

          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 "CoordinationNumbers.h"
      23             : #include "core/ActionShortcut.h"
      24             : #include "multicolvar/MultiColvarShortcuts.h"
      25             : #include "core/ActionWithValue.h"
      26             : #include "core/PlumedMain.h"
      27             : #include "core/ActionSet.h"
      28             : #include "core/ActionRegister.h"
      29             : 
      30             : #include <complex>
      31             : 
      32             : namespace PLMD {
      33             : namespace symfunc {
      34             : 
      35             : //+PLUMEDOC MCOLVAR Q1
      36             : /*
      37             : Calculate 1st order Steinhardt parameters
      38             : 
      39             : The 1st order Steinhardt parameters allow us to measure the degree to which the first coordination shell
      40             : around an atom is ordered with the atoms aranged on a line.  The Steinhardt parameter for atom, $i$ is complex vector whose components are
      41             : calculated using the following formula:
      42             : 
      43             : $$
      44             : q_{1m}(i) = \sum_j \sigma( r_{ij} ) Y_{1m}(\mathbf{r}_{ij})
      45             : $$
      46             : 
      47             : where $Y_{1m}$ is one of the 1st order spherical harmonics so $m$ is a number that runs from $-1$ to
      48             : $+1$.  The function $\sigma( r_{ij} )$ is a switching function that acts on the distance between
      49             : atoms $i$ and $j$.  The parameters of this function should be set so that it the function is equal to one
      50             : when atom $j$ is in the first coordination sphere of atom $i$ and is zero otherwise.
      51             : 
      52             : As discussed on [this page](https://www.plumed-tutorials.org/lessons/23/001/data/Steinhardt.html), the Steinhardt parameters can
      53             : be used to measure the degree of order in the system in a variety of different ways.  The
      54             : simplest way of measuring whether or not the coordination sphere is ordered is to simply take the norm of the above vector i.e.
      55             : 
      56             : $$
      57             : Q_1(i) = \frac{1}{\sum_j \sigma(r_{ij}) } \sqrt{ \sum_{m=-1}^1 q_{1m}(i)^{*} q_{1m}(i) }
      58             : $$
      59             : 
      60             : This norm is small when the coordination shell is disordered and larger when the coordination shell is ordered. The following input illustrates
      61             : how by averaging the value of this norm over all the atoms in the system you can measure the global degree of order in the system:
      62             : 
      63             : ```plumed
      64             : q1: Q1 SPECIES=1-64 D_0=1.3 R_0=0.2
      65             : q1_mean: MEAN ARG=q1 PERIODIC=NO
      66             : PRINT ARG=q1_mean FILE=colvar
      67             : ```
      68             : 
      69             : In the above input the rational [switching function](LESS_THAN.md) with the parameters above. We would recommend using SWITCH syntax
      70             : rather than the syntax above when giving the parameters for the switching function as you can then use any of the switching functions described
      71             : in the documentation for [LESS_THAN](LESS_THAN.md).  More importantly, however, using this syntax allows you to set the D_MAX parameter for the
      72             : switching function as demonstrated below:
      73             : 
      74             : ```plumed
      75             : q1: Q1 SPECIES=1-64 SWITCH={RATIONAL D_0=1.3 R_0=0.2 D_MAX=3.0}
      76             : q1_mean: MEAN ARG=q1 PERIODIC=NO
      77             : PRINT ARG=q1_mean FILE=colvar
      78             : ```
      79             : 
      80             : Setting the `D_MAX` can substantially improve PLUMED performance as it turns on the linked list algorithm that is discussed in the optimisation details part
      81             : of the documentation for [CONTACT_MATRIX](CONTACT_MATRIX.md).
      82             : 
      83             : ## Working with two types of atoms
      84             : 
      85             : The command below could be used to measure the Q1 parameters that describe the arrangement of chlorine ions around the
      86             : sodium atoms in sodium chloride.  The imagined system here is composed of 64 NaCl formula units and the atoms are arranged in the input
      87             : with the 64 Na$^+$ ions followed by the 64 Cl$-$ ions.  Once again the average Q1 parameter is calculated and output to a
      88             : file called colvar
      89             : 
      90             : ```plumed
      91             : q1: Q1 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2
      92             : q1_mean: MEAN ARG=q1 PERIODIC=NO
      93             : PRINT ARG=q1_mean FILE=colvar
      94             : ```
      95             : 
      96             : If you simply want to examine the values of the Q1 parameters for each of the atoms in your system you can do so by exploiting the
      97             : command [DUMPATOMS](DUMPATOMS.md) as shown in the example below.  The following output file will output a file in an extended xyz format
      98             : called q1.xyz for each frame of the analyzed MD trajectory.  The first column in this file will contain a dummy name for each of the
      99             : atoms, columns 2-4 will then contain the x, y and z positions of the atoms, column 5 will contain the value of the Q1 parameter, columns
     100             : 6-8 will contain the real parts of the director of the $q_{1m}$ vector while columns 9-11 will contain the imaginary parts of this director.
     101             : 
     102             : ```plumed
     103             : q1: Q1 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2 MEAN
     104             : DUMPATOMS ATOMS=q1 ARG=q1 FILE=q1.xyz
     105             : ```
     106             : 
     107             : ## The MASK keyword
     108             : 
     109             : 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
     110             : input, which tells Q1 that it is safe not to calculate the Q1 parameter for some of the atoms.  As illustrated below, this is useful if you are using functionality
     111             : from the [volumes module](module_volumes.md) to calculate the average value of the Q1 parameter for only those atoms that lie in a certain part of the simulation box.
     112             : 
     113             : ```plumed
     114             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     115             : center: FIXEDATOM AT=2.5,2.5,2.5
     116             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     117             : sphere: INSPHERE ATOMS=1-400 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     118             : # Calculate the fccubic parameter of the atoms
     119             : cc: Q1 ...
     120             :   SPECIES=1-400 MASK=sphere
     121             :   SWITCH={RATIONAL D_0=3.0 R_0=1.5 D_MAX=6.0}
     122             : ...
     123             : # Multiply fccubic parameters numbers by sphere vector
     124             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     125             : # Sum of coordination numbers for atoms that are in the sphere of interest
     126             : numer: SUM ARG=prod PERIODIC=NO
     127             : # Number of atoms that are in sphere of interest
     128             : denom: SUM ARG=sphere PERIODIC=NO
     129             : # Average coordination number for atoms in sphere of interest
     130             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     131             : # And print out final CV to a file
     132             : PRINT ARG=av FILE=colvar STRIDE=1
     133             : ```
     134             : 
     135             : This input calculate the average value of the Q1 parameter for only those atoms that are within a spherical region that is centered on the point
     136             : $(2.5,2.5,2.5)$.
     137             : 
     138             : ## Deprecated syntax
     139             : 
     140             : More information on the deprecated keywords that are given below is available in the documentation for the [DISTANCES](DISTANCES.md) command.
     141             : Below is an example where these deprecated keywords are used to calculate the histogram of Q1 parameters for the 64 atoms in a box of Lennard Jones print them
     142             : to a file called colvar:
     143             : 
     144             : ```plumed
     145             : q1: Q1 SPECIES=1-64 D_0=1.3 R_0=0.2 HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1}
     146             : PRINT ARG=q1.* FILE=colvar
     147             : ```
     148             : 
     149             : The following example illustrates how you can use VSUM to calculate a global vector of $Q_{1m}$ values as follows:
     150             : 
     151             : $$
     152             : Q_{1m} = \sum_i \frac{q_{1m}(i)}{\sum_j \sigma(r_{ij})}
     153             : $$
     154             : 
     155             : where the sum runs over all the atoms.  You can then take these $Q_{1m}$ values and compute the following norm:
     156             : 
     157             : $$
     158             : s = \sqrt{ \sum_{m=-1}^1 Q_{1m}^{*} Q_{1m} }
     159             : $$
     160             : 
     161             : The VMEAN command that is also used in the input below performs a similar operations.  The only difference is that
     162             : we divide the sums in the first expression above by the number of atoms.
     163             : 
     164             : ```plumed
     165             : q1: Q1 SPECIES=1-64 D_0=1.3 R_0=0.2 VMEAN VSUM
     166             : PRINT ARG=q1.* FILE=colvar
     167             : ```
     168             : 
     169             : */
     170             : //+ENDPLUMEDOC
     171             : 
     172             : //+PLUMEDOC MCOLVAR Q3
     173             : /*
     174             : Calculate 3rd order Steinhardt parameters.
     175             : 
     176             : The 3rd order Steinhardt parameters allow us to measure the degree to which the first coordination shell
     177             : around an atom is ordered.  The Steinhardt parameter for atom, $i$ is complex vector whose components are
     178             : calculated using the following formula:
     179             : 
     180             : $$
     181             : q_{3m}(i) = \sum_j \sigma( r_{ij} ) Y_{3m}(\mathbf{r}_{ij})
     182             : $$
     183             : 
     184             : where $Y_{3m}$ is one of the 3rd order spherical harmonics so $m$ is a number that runs from $-3$ to
     185             : $+3$.  The function $\sigma( r_{ij} )$ is a switching function that acts on the distance between
     186             : atoms $i$ and $j$.  The parameters of this function should be set so that it the function is equal to one
     187             : when atom $j$ is in the first coordination sphere of atom $i$ and is zero otherwise.
     188             : 
     189             : As discussed on [this page](https://www.plumed-tutorials.org/lessons/23/001/data/Steinhardt.html), the Steinhardt parameters can
     190             : be used to measure the degree of order in the system in a variety of different ways.  The
     191             : simplest way of measuring whether or not the coordination sphere is ordered is to simply take the norm of the above vector i.e.
     192             : 
     193             : $$
     194             : Q_3(i) = \frac{1}{\sum_j \sigma(r_{ij}) } \sqrt{ \sum_{m=-3}^3 q_{3m}(i)^{*} q_{3m}(i) }
     195             : $$
     196             : 
     197             : This norm is small when the coordination shell is disordered and larger when the coordination shell is ordered.  The following input illustrates
     198             : how by averaging the value of this norm over all the atoms in the system you can measure the global degree of order in the system:
     199             : 
     200             : ```plumed
     201             : q3: Q3 SPECIES=1-64 D_0=1.3 R_0=0.2
     202             : q3_mean: MEAN ARG=q3 PERIODIC=NO
     203             : PRINT ARG=q3_mean FILE=colvar
     204             : ```
     205             : 
     206             : In the above input the rational [switching function](LESS_THAN.md) with the parameters above. We would recommend using SWITCH syntax
     207             : rather than the syntax above when giving the parameters for the switching function as you can then use any of the switching functions described
     208             : in the documentation for [LESS_THAN](LESS_THAN.md).  More importantly, however, using this syntax allows you to set the D_MAX parameter for the
     209             : switching function as demonstrated below:
     210             : 
     211             : ```plumed
     212             : q3: Q3 SPECIES=1-64 SWITCH={RATIONAL D_0=1.3 R_0=0.2 D_MAX=3.0}
     213             : q3_mean: MEAN ARG=q3 PERIODIC=NO
     214             : PRINT ARG=q3_mean FILE=colvar
     215             : ```
     216             : 
     217             : Setting the `D_MAX` can substantially improve PLUMED performance as it turns on the linked list algorithm that is discussed in the optimisation details part
     218             : of the documentation for [CONTACT_MATRIX](CONTACT_MATRIX.md).
     219             : 
     220             : ## Working with two types of atoms
     221             : 
     222             : The command below could be used to measure the Q3 parameters that describe the arrangement of chlorine ions around the
     223             : sodium atoms in sodium chloride.  The imagined system here is composed of 64 NaCl formula units and the atoms are arranged in the input
     224             : with the 64 Na$^+$ ions followed by the 64 Cl$-$ ions.  Once again the average Q3 parameter is calculated and output to a
     225             : file called colvar
     226             : 
     227             : ```plumed
     228             : q3: Q3 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2
     229             : q3_mean: MEAN ARG=q3 PERIODIC=NO
     230             : PRINT ARG=q3_mean FILE=colvar
     231             : ```
     232             : 
     233             : If you simply want to examine the values of the Q3 parameters for each of the atoms in your system you can do so by exploiting the
     234             : command [DUMPATOMS](DUMPATOMS.md) as shown in the example below.  The following output file will output a file in an extended xyz format
     235             : called q3.xyz for each frame of the analyzed MD trajectory.  The first column in this file will contain a dummy name for each of the
     236             : atoms, columns 2-4 will then contain the x, y and z positions of the atoms, column 5 will contain the value of the Q3 parameter, columns
     237             : 6-12 will contain the real parts of the director of the $q_{3m}$ vector while columns 13-19 will contain the imaginary parts of this director.
     238             : 
     239             : ```plumed
     240             : q3: Q3 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2 MEAN
     241             : DUMPATOMS ATOMS=q3 ARG=q3 FILE=q3.xyz
     242             : ```
     243             : 
     244             : ## The MASK keyword
     245             : 
     246             : 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
     247             : input, which tells Q3 that it is safe not to calculate the Q3 parameter for some of the atoms.  As illustrated below, this is useful if you are using functionality
     248             : from the [volumes module](module_volumes.md) to calculate the average value of the Q3 parameter for only those atoms that lie in a certain part of the simulation box.
     249             : 
     250             : ```plumed
     251             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     252             : center: FIXEDATOM AT=2.5,2.5,2.5
     253             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     254             : sphere: INSPHERE ATOMS=1-400 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     255             : # Calculate the fccubic parameter of the atoms
     256             : cc: Q3 ...
     257             :   SPECIES=1-400 MASK=sphere
     258             :   SWITCH={RATIONAL D_0=3.0 R_0=1.5 D_MAX=6.0}
     259             : ...
     260             : # Multiply fccubic parameters numbers by sphere vector
     261             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     262             : # Sum of coordination numbers for atoms that are in the sphere of interest
     263             : numer: SUM ARG=prod PERIODIC=NO
     264             : # Number of atoms that are in sphere of interest
     265             : denom: SUM ARG=sphere PERIODIC=NO
     266             : # Average coordination number for atoms in sphere of interest
     267             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     268             : # And print out final CV to a file
     269             : PRINT ARG=av FILE=colvar STRIDE=1
     270             : ```
     271             : 
     272             : This input calculate the average value of the Q3 parameter for only those atoms that are within a spherical region that is centered on the point
     273             : $(2.5,2.5,2.5)$.
     274             : 
     275             : ## Deprecated syntax
     276             : 
     277             : More information on the deprecated keywords that are given below is available in the documentation for the [DISTANCES](DISTANCES.md) command.
     278             : Below is an example where these deprecated keywords are used to calculate the histogram of Q3 parameters for the 64 atoms in a box of Lennard Jones print them
     279             : to a file called colvar:
     280             : 
     281             : ```plumed
     282             : q3: Q3 SPECIES=1-64 D_0=1.3 R_0=0.2 HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1}
     283             : PRINT ARG=q3.* FILE=colvar
     284             : ```
     285             : 
     286             : The following example illustrates how you can use VSUM to calculate a global vector of $Q_{3m}$ values as follows:
     287             : 
     288             : $$
     289             : Q_{3m} = \sum_i \frac{q_{3m}(i)}{\sum_j \sigma(r_{ij})}
     290             : $$
     291             : 
     292             : where the sum runs over all the atoms.  You can then take these $Q_{3m}$ values and compute the following norm:
     293             : 
     294             : $$
     295             : s = \sqrt{ \sum_{m=-3}^3 Q_{3m}^{*} Q_{3m} }
     296             : $$
     297             : 
     298             : The VMEAN command that is also used in the input below performs a similar operations.  The only difference is that
     299             : we divide the sums in the first expression above by the number of atoms.
     300             : 
     301             : ```plumed
     302             : q3: Q3 SPECIES=1-64 D_0=1.3 R_0=0.2 VMEAN VSUM
     303             : PRINT ARG=q3.* FILE=colvar
     304             : ```
     305             : 
     306             : */
     307             : //+ENDPLUMEDOC
     308             : 
     309             : //+PLUMEDOC MCOLVAR Q4
     310             : /*
     311             : Calculate fourth order Steinhardt parameters.
     312             : 
     313             : The 4th order Steinhardt parameters allow us to measure the degree to which the first coordination shell
     314             : around an atom is ordered.  The Steinhardt parameter for atom, $i$ is complex vector whose components are
     315             : calculated using the following formula:
     316             : 
     317             : $$
     318             : q_{4m}(i) = \sum_j \sigma( r_{ij} ) Y_{4m}(\mathbf{r}_{ij})
     319             : $$
     320             : 
     321             : where $Y_{4m}$ is one of the 4th order spherical harmonics so $m$ is a number that runs from $-4$ to
     322             : $+4$.  The function $\sigma( r_{ij} )$ is a switching function that acts on the distance between
     323             : atoms $i$ and $j$.  The parameters of this function should be set so that it the function is equal to one
     324             : when atom $j$ is in the first coordination sphere of atom $i$ and is zero otherwise.
     325             : 
     326             : As discussed on [this page](https://www.plumed-tutorials.org/lessons/23/001/data/Steinhardt.html), the Steinhardt parameters can
     327             : be used to measure the degree of order in the system in a variety of different ways.  The
     328             : simplest way of measuring whether or not the coordination sphere is ordered is to simply take the norm of the above vector i.e.
     329             : 
     330             : $$
     331             : Q_4(i) = \frac{1}{\sum_j \sigma(r_{ij}) } \sqrt{ \sum_{m=-4}^4 q_{4m}(i)^{*} q_{4m}(i) }
     332             : $$
     333             : 
     334             : This norm is small when the coordination shell is disordered and larger when the coordination shell is ordered. The following input illustrates
     335             : how by averaging the value of this norm over all the atoms in the system you can measure the global degree of order in the system:
     336             : 
     337             : ```plumed
     338             : q4: Q4 SPECIES=1-64 D_0=1.3 R_0=0.2
     339             : q4_mean: MEAN ARG=q4 PERIODIC=NO
     340             : PRINT ARG=q4_mean FILE=colvar
     341             : ```
     342             : 
     343             : In the above input the rational [switching function](LESS_THAN.md) with the parameters above. We would recommend using SWITCH syntax
     344             : rather than the syntax above when giving the parameters for the switching function as you can then use any of the switching functions described
     345             : in the documentation for [LESS_THAN](LESS_THAN.md).  More importantly, however, using this syntax allows you to set the D_MAX parameter for the
     346             : switching function as demonstrated below:
     347             : 
     348             : ```plumed
     349             : q4: Q4 SPECIES=1-64 SWITCH={RATIONAL D_0=1.3 R_0=0.2 D_MAX=3.0}
     350             : q4_mean: MEAN ARG=q4 PERIODIC=NO
     351             : PRINT ARG=q4_mean FILE=colvar
     352             : ```
     353             : 
     354             : Setting the `D_MAX` can substantially improve PLUMED performance as it turns on the linked list algorithm that is discussed in the optimisation details part
     355             : of the documentation for [CONTACT_MATRIX](CONTACT_MATRIX.md).
     356             : 
     357             : ## Working with two types of atoms
     358             : 
     359             : The command below could be used to measure the Q4 parameters that describe the arrangement of chlorine ions around the
     360             : sodium atoms in sodium chloride.  The imagined system here is composed of 64 NaCl formula units and the atoms are arranged in the input
     361             : with the 64 Na$^+$ ions followed by the 64 Cl$-$ ions.  Once again the average Q4 parameter is calculated and output to a
     362             : file called colvar
     363             : 
     364             : ```plumed
     365             : q4: Q4 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2
     366             : q4_mean: MEAN ARG=q4 PERIODIC=NO
     367             : PRINT ARG=q4_mean FILE=colvar
     368             : ```
     369             : 
     370             : If you simply want to examine the values of the Q4 parameters for each of the atoms in your system you can do so by exploiting the
     371             : command [DUMPATOMS](DUMPATOMS.md) as shown in the example below.  The following output file will output a file in an extended xyz format
     372             : called q4.xyz for each frame of the analyzed MD trajectory.  The first column in this file will contain a dummy name for each of the
     373             : atoms, columns 2-4 will then contain the x, y and z positions of the atoms, column 5 will contain the value of the Q4 parameter, columns
     374             : 6-14 will contain the real parts of the director of the $q_{4m}$ vector while columns 15-23 will contain the imaginary parts of this director.
     375             : 
     376             : ```plumed
     377             : q4: Q4 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2 MEAN
     378             : DUMPATOMS ATOMS=q4 ARG=q4 FILE=q4.xyz
     379             : ```
     380             : 
     381             : ## The MASK keyword
     382             : 
     383             : 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
     384             : input, which tells Q4 that it is safe not to calculate the Q4 parameter for some of the atoms.  As illustrated below, this is useful if you are using functionality
     385             : from the [volumes module](module_volumes.md) to calculate the average value of the Q4 parameter for only those atoms that lie in a certain part of the simulation box.
     386             : 
     387             : ```plumed
     388             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     389             : center: FIXEDATOM AT=2.5,2.5,2.5
     390             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     391             : sphere: INSPHERE ATOMS=1-400 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     392             : # Calculate the fccubic parameter of the atoms
     393             : cc: Q4 ...
     394             :   SPECIES=1-400 MASK=sphere
     395             :   SWITCH={RATIONAL D_0=3.0 R_0=1.5 D_MAX=6.0}
     396             : ...
     397             : # Multiply fccubic parameters numbers by sphere vector
     398             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     399             : # Sum of coordination numbers for atoms that are in the sphere of interest
     400             : numer: SUM ARG=prod PERIODIC=NO
     401             : # Number of atoms that are in sphere of interest
     402             : denom: SUM ARG=sphere PERIODIC=NO
     403             : # Average coordination number for atoms in sphere of interest
     404             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     405             : # And print out final CV to a file
     406             : PRINT ARG=av FILE=colvar STRIDE=1
     407             : ```
     408             : 
     409             : This input calculate the average value of the Q4 parameter for only those atoms that are within a spherical region that is centered on the point
     410             : $(2.5,2.5,2.5)$.
     411             : 
     412             : ## Deprecated syntax
     413             : 
     414             : More information on the deprecated keywords that are given below is available in the documentation for the [DISTANCES](DISTANCES.md) command.
     415             : Below is an example where these deprecated keywords are used to calculate the histogram of Q4 parameters for the 64 atoms in a box of Lennard Jones print them
     416             : to a file called colvar:
     417             : 
     418             : ```plumed
     419             : q4: Q4 SPECIES=1-64 D_0=1.3 R_0=0.2 HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1}
     420             : PRINT ARG=q4.* FILE=colvar
     421             : ```
     422             : 
     423             : The following example illustrates how you can use VSUM to calculate a global vector of $Q_{4m}$ values as follows:
     424             : 
     425             : $$
     426             : Q_{4m} = \sum_i \frac{q_{4m}(i)}{\sum_j \sigma(r_{ij})}
     427             : $$
     428             : 
     429             : where the sum runs over all the atoms.  You can then take these $Q_{4m}$ values and compute the following norm:
     430             : 
     431             : $$
     432             : s = \sqrt{ \sum_{m=-4}^1 Q_{4m}^{*} Q_{4m} }
     433             : $$
     434             : 
     435             : The VMEAN command that is also used in the input below performs a similar operations.  The only difference is that
     436             : we divide the sums in the first expression above by the number of atoms.
     437             : 
     438             : ```plumed
     439             : q4: Q4 SPECIES=1-64 D_0=1.3 R_0=0.2 VMEAN VSUM
     440             : PRINT ARG=q4.* FILE=colvar
     441             : ```
     442             : 
     443             : */
     444             : //+ENDPLUMEDOC
     445             : 
     446             : //+PLUMEDOC MCOLVAR Q6
     447             : /*
     448             : Calculate sixth order Steinhardt parameters.
     449             : 
     450             : The 6th order Steinhardt parameters allow us to measure the degree to which the first coordination shell
     451             : around an atom is ordered.  The Steinhardt parameter for atom, $i$ is complex vector whose components are
     452             : calculated using the following formula:
     453             : 
     454             : $$
     455             : q_{6m}(i) = \sum_j \sigma( r_{ij} ) Y_{6m}(\mathbf{r}_{ij})
     456             : $$
     457             : 
     458             : where $Y_{6m}$ is one of the 6th order spherical harmonics so $m$ is a number that runs from $-6$ to
     459             : $+6$.  The function $\sigma( r_{ij} )$ is a switching function that acts on the distance between
     460             : atoms $i$ and $j$.  The parameters of this function should be set so that it the function is equal to one
     461             : when atom $j$ is in the first coordination sphere of atom $i$ and is zero otherwise.
     462             : 
     463             : As discussed on [this page](https://www.plumed-tutorials.org/lessons/23/001/data/Steinhardt.html), the Steinhardt parameters can
     464             : be used to measure the degree of order in the system in a variety of different ways.  The
     465             : simplest way of measuring whether or not the coordination sphere is ordered is to simply take the norm of the above vector i.e.
     466             : 
     467             : $$
     468             : Q_6(i) = \frac{1}{\sum_j \sigma(r_{ij}) } \sqrt{ \sum_{m=-6}^6 q_{6m}(i)^{*} q_{6m}(i) }
     469             : $$
     470             : 
     471             : This norm is small when the coordination shell is disordered and larger when the coordination shell is ordered. The following input illustrates
     472             : how by averaging the value of this norm over all the atoms in the system you can measure the global degree of order in the system:
     473             : 
     474             : ```plumed
     475             : q6: Q6 SPECIES=1-64 D_0=1.3 R_0=0.2
     476             : q6_mean: MEAN ARG=q6 PERIODIC=NO
     477             : PRINT ARG=q6_mean FILE=colvar
     478             : ```
     479             : 
     480             : In the above input the rational [switching function](LESS_THAN.md) with the parameters above. We would recommend using SWITCH syntax
     481             : rather than the syntax above when giving the parameters for the switching function as you can then use any of the switching functions described
     482             : in the documentation for [LESS_THAN](LESS_THAN.md).  More importantly, however, using this syntax allows you to set the D_MAX parameter for the
     483             : switching function as demonstrated below:
     484             : 
     485             : ```plumed
     486             : q6: Q6 SPECIES=1-64 SWITCH={RATIONAL D_0=1.3 R_0=0.2 D_MAX=3.0}
     487             : q6_mean: MEAN ARG=q6 PERIODIC=NO
     488             : PRINT ARG=q6_mean FILE=colvar
     489             : ```
     490             : 
     491             : Setting the `D_MAX` can substantially improve PLUMED performance as it turns on the linked list algorithm that is discussed in the optimisation details part
     492             : of the documentation for [CONTACT_MATRIX](CONTACT_MATRIX.md).
     493             : 
     494             : ## Working with two types of atoms
     495             : 
     496             : The command below could be used to measure the Q6 parameters that describe the arrangement of chlorine ions around the
     497             : sodium atoms in sodium chloride.  The imagined system here is composed of 64 NaCl formula units and the atoms are arranged in the input
     498             : with the 64 Na$^+$ ions followed by the 64 Cl$-$ ions.  Once again the average Q6 parameter is calculated and output to a
     499             : file called colvar
     500             : 
     501             : ```plumed
     502             : q6: Q6 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2
     503             : q6_mean: MEAN ARG=q6 PERIODIC=NO
     504             : PRINT ARG=q6_mean FILE=colvar
     505             : ```
     506             : 
     507             : If you simply want to examine the values of the Q6 parameters for each of the atoms in your system you can do so by exploiting the
     508             : command [DUMPATOMS](DUMPATOMS.md) as shown in the example below.  The following output file will output a file in an extended xyz format
     509             : called q6.xyz for each frame of the analyzed MD trajectory.  The first column in this file will contain a dummy name for each of the
     510             : atoms, columns 2-4 will then contain the x, y and z positions of the atoms, column 5 will contain the value of the Q6 parameter, columns
     511             : 6-18 will contain the real parts of the director of the $q_{6m}$ vector while columns 19-31 will contain the imaginary parts of this director.
     512             : 
     513             : ```plumed
     514             : q6: Q6 SPECIESA=1-64 SPECIESB=65-128 D_0=1.3 R_0=0.2 MEAN
     515             : DUMPATOMS ATOMS=q6 ARG=q6 FILE=q6.xyz
     516             : ```
     517             : 
     518             : ## The MASK keyword
     519             : 
     520             : 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
     521             : input, which tells Q6 that it is safe not to calculate the Q6 parameter for some of the atoms.  As illustrated below, this is useful if you are using functionality
     522             : from the [volumes module](module_volumes.md) to calculate the average value of the Q6 parameter for only those atoms that lie in a certain part of the simulation box.
     523             : 
     524             : ```plumed
     525             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     526             : center: FIXEDATOM AT=2.5,2.5,2.5
     527             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     528             : sphere: INSPHERE ATOMS=1-400 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     529             : # Calculate the fccubic parameter of the atoms
     530             : cc: Q6 ...
     531             :   SPECIES=1-400 MASK=sphere
     532             :   SWITCH={RATIONAL D_0=3.0 R_0=1.5 D_MAX=6.0}
     533             : ...
     534             : # Multiply fccubic parameters numbers by sphere vector
     535             : prod: CUSTOM ARG=cc,sphere FUNC=x*y PERIODIC=NO
     536             : # Sum of coordination numbers for atoms that are in the sphere of interest
     537             : numer: SUM ARG=prod PERIODIC=NO
     538             : # Number of atoms that are in sphere of interest
     539             : denom: SUM ARG=sphere PERIODIC=NO
     540             : # Average coordination number for atoms in sphere of interest
     541             : av: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     542             : # And print out final CV to a file
     543             : PRINT ARG=av FILE=colvar STRIDE=1
     544             : ```
     545             : 
     546             : This input calculate the average value of the Q6 parameter for only those atoms that are within a spherical region that is centered on the point
     547             : $(2.5,2.5,2.5)$.
     548             : 
     549             : ## Deprecated syntax
     550             : 
     551             : More information on the deprecated keywords that are given below is available in the documentation for the [DISTANCES](DISTANCES.md) command.
     552             : Below is an example where these deprecated keywords are used to calculate the histogram of Q6 parameters for the 64 atoms in a box of Lennard Jones print them
     553             : to a file called colvar:
     554             : 
     555             : ```plumed
     556             : q6: Q6 SPECIES=1-64 D_0=1.3 R_0=0.2 HISTOGRAM={GAUSSIAN LOWER=0.0 UPPER=1.0 NBINS=20 SMEAR=0.1}
     557             : PRINT ARG=q6.* FILE=colvar
     558             : ```
     559             : 
     560             : The following example illustrates how you can use VSUM to calculate a global vector of $Q_{6m}$ values as follows:
     561             : 
     562             : $$
     563             : Q_{6m} = \sum_i \frac{q_{6m}(i)}{\sum_j \sigma(r_{ij})}
     564             : $$
     565             : 
     566             : where the sum runs over all the atoms.  You can then take these $Q_{6m}$ values and compute the following norm:
     567             : 
     568             : $$
     569             : s = \sqrt{ \sum_{m=-6}^6 Q_{6m}^{*} Q_{6m} }
     570             : $$
     571             : 
     572             : The VMEAN command that is also used in the input below performs a similar operations.  The only difference is that
     573             : we divide the sums in the first expression above by the number of atoms.
     574             : 
     575             : ```plumed
     576             : q6: Q6 SPECIES=1-64 D_0=1.3 R_0=0.2 VMEAN VSUM
     577             : PRINT ARG=q6.* FILE=colvar
     578             : ```
     579             : 
     580             : */
     581             : //+ENDPLUMEDOC
     582             : 
     583             : class Steinhardt : public ActionShortcut {
     584             : private:
     585             :   static std::string getSymbol( int m );
     586             :   void createVectorNormInput( const std::string& ilab,
     587             :                               const std::string& olab,
     588             :                               int l,
     589             :                               const std::string& sep,
     590             :                               const std::string& vlab,
     591             :                               bool usegpu = false);
     592             : public:
     593             :   static void registerKeywords( Keywords& keys );
     594             :   explicit Steinhardt(const ActionOptions&);
     595             : };
     596             : 
     597             : PLUMED_REGISTER_ACTION(Steinhardt,"Q1")
     598             : PLUMED_REGISTER_ACTION(Steinhardt,"Q3")
     599             : PLUMED_REGISTER_ACTION(Steinhardt,"Q4")
     600             : PLUMED_REGISTER_ACTION(Steinhardt,"Q6")
     601             : 
     602          60 : void Steinhardt::registerKeywords( Keywords& keys ) {
     603          60 :   CoordinationNumbers::shortcutKeywords( keys );
     604         120 :   keys.addDeprecatedFlag("LOWMEM","");
     605          60 :   keys.addFlag("VMEAN",false,"calculate the norm of the mean vector.");
     606         120 :   keys.addOutputComponent("_vmean","VMEAN","scalar","the norm of the mean vector");
     607          60 :   keys.addFlag("VSUM",false,"calculate the norm of the sum of all the vectors");
     608         120 :   keys.addOutputComponent("_vsum","VSUM","scalar","the norm of the mean vector");
     609          60 :   keys.needsAction("GROUP");
     610          60 :   keys.needsAction("CONTACT_MATRIX");
     611          60 :   keys.needsAction("SPHERICAL_HARMONIC");
     612          60 :   keys.needsAction("ONES");
     613          60 :   keys.needsAction("MATRIX_VECTOR_PRODUCT");
     614          60 :   keys.needsAction("COMBINE");
     615          60 :   keys.needsAction("CUSTOM");
     616          60 :   keys.needsAction("MEAN");
     617          60 :   keys.needsAction("SUM");
     618         120 :   keys.setValueDescription("vector",
     619             :                            "the norms of the vectors of spherical harmonic coefficients");
     620          60 :   keys.addFlag("USEGPU",false,"run part of this calculation on the GPU");
     621          60 : }
     622             : 
     623          42 : Steinhardt::Steinhardt( const ActionOptions& ao):
     624             :   Action(ao),
     625          42 :   ActionShortcut(ao) {
     626             :   {
     627             :     bool lowmem;
     628          42 :     parseFlag("LOWMEM",lowmem);
     629          42 :     if( lowmem ) {
     630           0 :       warning("LOWMEM flag is deprecated and is no longer required for this action");
     631             :     }
     632             :   }
     633             : 
     634             :   bool usegpu;
     635          42 :   parseFlag("USEGPU",usegpu);
     636          84 :   const std::string doUSEGPU = usegpu?" USEGPU":"";
     637             : 
     638             :   std::string sp_str;
     639          84 :   parse("SPECIES",sp_str);
     640             :   std::string specA;
     641          84 :   parse("SPECIESA",specA);
     642             :   std::string specB;
     643          42 :   parse("SPECIESB",specB);
     644          42 :   CoordinationNumbers::expandMatrix( true,
     645             :                                      getShortcutLabel(),
     646             :                                      sp_str,
     647             :                                      specA,
     648             :                                      specB,
     649             :                                      this );
     650             :   int l;
     651          84 :   std::string sph_input = getShortcutLabel() + "_sh: SPHERICAL_HARMONIC ARG="
     652         126 :                           + getShortcutLabel() + "_mat.x,"
     653         126 :                           + getShortcutLabel() + "_mat.y,"
     654         126 :                           + getShortcutLabel() + "_mat.z,"
     655         126 :                           + getShortcutLabel() + "_mat.w";
     656             : 
     657          42 :   if( getName()=="Q1" ) {
     658             :     sph_input +=" L=1";
     659             :     l=1;
     660          23 :   } else if( getName()=="Q3" ) {
     661             :     sph_input += " L=3";
     662             :     l=3;
     663          22 :   } else if( getName()=="Q4" ) {
     664             :     sph_input += " L=4";
     665             :     l=4;
     666          19 :   } else if( getName()=="Q6" ) {
     667             :     sph_input += " L=6";
     668             :     l=6;
     669             :   } else {
     670           0 :     plumed_merror("invalid input");
     671             :   }
     672          42 :   readInputLine( sph_input + doUSEGPU);
     673             : 
     674             :   // Input for denominator (coord)
     675          42 :   ActionWithValue* av = plumed.getActionSet()
     676          42 :                         .selectWithLabel<ActionWithValue*>( getShortcutLabel() + "_mat");
     677          42 :   plumed_assert( av && av->getNumberOfComponents()>0 && (av->copyOutput(0))->getRank()==2 );
     678             :   std::string size;
     679          42 :   Tools::convert( (av->copyOutput(0))->getShape()[1], size );
     680          84 :   readInputLine( getShortcutLabel() + "_denom_ones: ONES SIZE=" + size );
     681          84 :   readInputLine( getShortcutLabel() + "_denom: MATRIX_VECTOR_PRODUCT "
     682         126 :                  "ARG=" + getShortcutLabel() + "_mat.w,"
     683         126 :                  + getShortcutLabel() + "_denom_ones"
     684          84 :                  + doUSEGPU);
     685         126 :   readInputLine( getShortcutLabel() + "_sp: MATRIX_VECTOR_PRODUCT "
     686         126 :                  "ARG=" + getShortcutLabel() + "_sh.*,"
     687         126 :                  + getShortcutLabel() + "_denom_ones"
     688          84 :                  + doUSEGPU);
     689             : 
     690             :   // If we are doing VMEAN determine sum of vector components
     691             :   std::string snum;
     692             :   bool do_vmean;
     693          42 :   parseFlag("VMEAN",do_vmean);
     694             :   bool do_vsum;
     695          42 :   parseFlag("VSUM",do_vsum);
     696          42 :   if( do_vmean || do_vsum ) {
     697          26 :     auto makeString=[&](const std::string& numstr,
     698             :     const char realImg)->std::string{
     699             :       //realImg is "r" or "i"
     700          52 :       return getShortcutLabel() + "_" +realImg + "mn-" + numstr + ": CUSTOM "
     701          78 :       "ARG=" + getShortcutLabel() + "_sp." +realImg + "m-" + numstr + ","
     702         104 :       + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO";
     703           1 :     };
     704             :     // Divide all components by coordination numbers
     705          14 :     for(int i=-l; i<=l; ++i) {
     706          13 :       snum = getSymbol( i );
     707             :       // Real part
     708          13 :       readInputLine(makeString(snum,'r'));
     709             :       // Imaginary part
     710          26 :       readInputLine(makeString(snum,'i'));
     711             :     }
     712             :   }
     713             : 
     714          42 :   if( do_vmean ) {
     715          26 :     auto makeString=[&](const std::string& numstr,
     716             :     const char realImg)->std::string{
     717             :       //realImg is "r" or "i"
     718          52 :       return getShortcutLabel() + "_" +realImg + "ms-" + numstr + ": MEAN "
     719          78 :       "ARG=" + getShortcutLabel() + "_" +realImg + "mn-" + numstr
     720          52 :       + " PERIODIC=NO";
     721           1 :     };
     722          14 :     for(int i=-l; i<=l; ++i) {
     723          13 :       snum = getSymbol( i );
     724             :       // Real part
     725          13 :       readInputLine(makeString(snum,'r'));
     726             :       // Imaginary part
     727          26 :       readInputLine(makeString(snum,'i'));
     728             :     }
     729             :     // Now calculate the total length of the vector
     730           3 :     createVectorNormInput( getShortcutLabel(),
     731           2 :                            getShortcutLabel() + "_vmean",
     732             :                            l,
     733             :                            "_",
     734             :                            "ms",
     735             :                            usegpu );
     736             :   }
     737          42 :   if( do_vsum ) {
     738           0 :     auto makeString=[&](const std::string& numstr,
     739             :     const std::string& realImg)->std::string{
     740           0 :       return getShortcutLabel() + "_" +realImg + "mz-" + numstr + ": SUM "
     741           0 :       "ARG=" + getShortcutLabel() + "_" + realImg + "mn-" + numstr
     742           0 :       + " PERIODIC=NO";
     743           0 :     };
     744           0 :     for(int i=-l; i<=l; ++i) {
     745           0 :       snum = getSymbol( i );
     746             :       // Real part
     747           0 :       readInputLine(makeString(snum,"r"));
     748             :       // Imaginary part
     749           0 :       readInputLine(makeString(snum,"i"));
     750             :     }
     751             :     // Now calculate the total length of the vector
     752           0 :     createVectorNormInput( getShortcutLabel(),
     753           0 :                            getShortcutLabel() + "_vsum",
     754             :                            l,
     755             :                            "_",
     756             :                            "mz",
     757             :                            usegpu );
     758             :   }
     759             : 
     760             :   // Now calculate the total length of the vector
     761         168 :   createVectorNormInput( getShortcutLabel() + "_sp",
     762          84 :                          getShortcutLabel() + "_norm",
     763             :                          l,
     764             :                          ".",
     765             :                          "m",
     766             :                          usegpu );
     767             :   // And take average
     768          84 :   readInputLine( getShortcutLabel() + ": CUSTOM "
     769         126 :                  "ARG=" + getShortcutLabel() + "_norm,"
     770         126 :                  + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     771          84 :   multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(),
     772             :       getShortcutLabel(), "", this );
     773          42 : }
     774             : 
     775          43 : void Steinhardt::createVectorNormInput( const std::string& ilab,
     776             :                                         const std::string& olab,
     777             :                                         const int l,
     778             :                                         const std::string& sep,
     779             :                                         const std::string& vlab,
     780             :                                         const bool usegpu) {
     781          43 :   std::string norm_input = olab + "2: COMBINE PERIODIC=NO POWERS=";
     782          43 :   std::string snum = getSymbol( -l );
     783          43 :   std::string arg_inp = "";
     784             : 
     785          86 :   std::string arg_inp_real = ilab + sep + "r" + vlab + "-";
     786          86 :   std::string arg_inp_img = ilab + sep + "i" + vlab + "-";
     787          43 :   std::string comma="";
     788         394 :   for(int i=-l; i<=l; ++i) {
     789         351 :     snum = getSymbol( i );
     790         702 :     arg_inp += comma + arg_inp_real + snum + "," + arg_inp_img + snum;
     791         702 :     norm_input += comma + "2,2";
     792             :     comma=",";
     793             :   }
     794         129 :   readInputLine( norm_input + " ARG=" + arg_inp + (usegpu?" USEGPU":"") );
     795          86 :   readInputLine( olab + ": CUSTOM ARG=" + olab + "2 FUNC=sqrt(x) PERIODIC=NO");
     796          43 : }
     797             : 
     798         420 : std::string Steinhardt::getSymbol( const int m ) {
     799         420 :   if( m<0 ) {
     800             :     std::string num;
     801         209 :     Tools::convert( -m, num );
     802         209 :     return "n" + num;
     803         211 :   } else if( m>0 ) {
     804             :     std::string num;
     805         166 :     Tools::convert( m, num );
     806         166 :     return "p" + num;
     807             :   }
     808          45 :   return "0";
     809             : }
     810             : 
     811             : } // namespace symfunc
     812             : } // namespace PLMD

Generated by: LCOV version 1.16