LCOV - code coverage report
Current view: top level - symfunc - HexaticParameter.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 46 57 80.7 %
Date: 2025-12-04 11:19:34 Functions: 3 4 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 "core/ActionShortcut.h"
      23             : #include "multicolvar/MultiColvarShortcuts.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "core/ActionSet.h"
      26             : #include "core/ActionRegister.h"
      27             : #include "core/ActionWithValue.h"
      28             : #include "CoordinationNumbers.h"
      29             : 
      30             : #include <complex>
      31             : 
      32             : namespace PLMD {
      33             : namespace symfunc {
      34             : 
      35             : //+PLUMEDOC MCOLVAR HEXACTIC_PARAMETER
      36             : /*
      37             : Calculate the hexatic order parameter
      38             : 
      39             : This [symmetry function](https://www.plumed-tutorials.org/lessons/23/001/data/SymmetryFunction.html) can be used to understand phase transitions in two dimensional systems.
      40             : The symmetry function for atom $k$ is calculated using:
      41             : 
      42             : $$
      43             : s_k = \left| \frac{\sum_j \sigma(r_{kj}) e^{6i\theta_j} }{\sum_j \sigma(r_{kj})} \right|
      44             : $$
      45             : 
      46             : In this expression, the sum run over all the atoms of interest and $r_{kj}$ is the distance between atom $k$ and atom $j$ and $\sigma$ is
      47             : a switching function that acts upon this distance.  $\theta_j$ is the angle between either the $x$, $y$ or $z$ axis and the bond connecting
      48             : atom $k$ and atom $j$.  This angle is multiplied by the imaginary number $i$ - the square root of minus one.  In the code, we thus calculate
      49             : $e^{i\theta_j}$ as follows:
      50             : 
      51             : $$
      52             : e^{i\theta_j} = \frac{x_{kj}}{r_{kj}} + i \frac{y_{kj}}{r_{kj}}
      53             : $$
      54             : 
      55             : We then take the 6th power of this complex number directly before compupting the magnitude by multiplying the result by its complex conjugate.
      56             : Notice, furthermore, that we can replace $x_{kj}$ or $y_{kj}$ with $z_{kj}$ by using PLANE=xz or PLANE=yz in place of PLANE=xy.
      57             : 
      58             : An example that shows how you can use this shortcut is shown below:
      59             : 
      60             : ```plumed
      61             : hex: HEXACTIC_PARAMETER SPECIES=1-400 PLANE=xy R_0=0.2 D_0=1.4 NN=6 MM=12
      62             : hex_mean: MEAN ARG=hex_norm PERIODIC=NO
      63             : PRINT ARG=hex_mean FILE=colvar
      64             : ```
      65             : 
      66             : As you can see if you expand the shortcut above, this input calculates the quantity defined in the equation above for the 400 atoms in the simulated system and stores them in a vector.
      67             : The elements of this vector are then added together so the mean value can be computed.
      68             : 
      69             : In the input above we use a 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             : 
      75             : ```plumed
      76             : hex: HEXACTIC_PARAMETER SPECIES=1-400 PLANE=xy SWITCH={RATIONAL D_0=1.4 R_0=0.2 D_MAX=3.0}
      77             : hex_mean: MEAN ARG=hex_norm PERIODIC=NO
      78             : PRINT ARG=hex_mean FILE=colvar
      79             : ```
      80             : 
      81             : 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
      82             : of the documentation for [CONTACT_MATRIX](CONTACT_MATRIX.md).
      83             : 
      84             : ## The VMEAN and VSUM options
      85             : 
      86             : If you expand the inputs in the previous section you will see that the value `hex_norm` that we are calculating the average of is a vector that contains the magnitude of the
      87             : complex number defined in the equation above.  If you would like to add up the vector of complex numbers directly (or take an average of the vector) __before__ computing the
      88             : magnitude of the complex number you can use the VSUM and VMEAN flags as sillstrated below:
      89             : 
      90             : ```plumed
      91             : hex: HEXACTIC_PARAMETER SPECIES=1-400 PLANE=xy SWITCH={RATIONAL D_0=1.4 R_0=0.2} VMEAN VSUM
      92             : PRINT ARG=hex_vmean,hex_vsum FILE=colvar
      93             : ```
      94             : 
      95             : If you expand the shortcut in the input above you will see that this input adds the complex numbers together directly before calculating the modulus.  This contrasts with the approach
      96             : in the inputs above, which computes a vector that contains the square moduli for each of the individual hexatic order parameters and then computes the average from this vector.
      97             : 
      98             : ## Using two types of atom
      99             : 
     100             : If you would like to calculate the hexatic parameters using the directors of the bonds connecting the atoms in GROUPA to the atoms in GROUPB you can use an input like the one
     101             : shown below:
     102             : 
     103             : ```plumed
     104             : hex: HEXACTIC_PARAMETER SPECIESA=1-200 SPECIESB=201-400 PLANE=xy SWITCH={RATIONAL D_0=1.4 R_0=0.2 D_MAX=3.0}
     105             : hex_mean: MEAN ARG=hex_norm PERIODIC=NO
     106             : PRINT ARG=hex_mean FILE=colvar
     107             : ```
     108             : 
     109             : ## The MASK keyword
     110             : 
     111             : 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
     112             : input, which tells PLUMED the atoms for which you do not need to calculate the function.  As illustrated below, this is useful if you are using functionality
     113             : from the [volumes module](module_volumes.md) to calculate the average value of the hexatic parameter for only those atoms that lie in a certain part of the simulation box.
     114             : 
     115             : ```plumed
     116             : # Fixed virtual atom which serves as the probe volume's center (pos. in nm)
     117             : center: FIXEDATOM AT=2.5,2.5,2.5
     118             : # Vector in which element i is one if atom i is in sphere of interest and zero otherwise
     119             : sphere: INSPHERE ATOMS=1-400 CENTER=center RADIUS={GAUSSIAN D_0=0.5 R_0=0.01 D_MAX=0.52}
     120             : # Calculate the tetrahedral parameter of the atoms
     121             : hex: HEXACTIC_PARAMETER ...
     122             :    SPECIES=1-400 MASK=sphere PLANE=xy
     123             :    SWITCH={RATIONAL D_0=3.0 R_0=1.5 D_MAX=6.0}
     124             : ...
     125             : # Multiply hexatic parameters by sphere vector
     126             : prod_rm: CUSTOM ARG=hex_rm,sphere FUNC=x*y PERIODIC=NO
     127             : prod_im: CUSTOM ARG=hex_im,sphere FUNC=x*y PERIODIC=NO
     128             : # Sum of coordination numbers for atoms that are in the sphere of interest
     129             : numer_rm: SUM ARG=prod_rm PERIODIC=NO
     130             : numer_im: SUM ARG=prod_im PERIODIC=NO
     131             : # Number of atoms that are in sphere of interest
     132             : denom: SUM ARG=sphere PERIODIC=NO
     133             : # Average coordination number for atoms in sphere of interest
     134             : av_rm: CUSTOM ARG=numer_rm,denom FUNC=x/y PERIODIC=NO
     135             : av_im: CUSTOM ARG=numer_im,denom FUNC=x/y PERIODIC=NO
     136             : # Take the square modulus
     137             : av: CUSTOM ARG=av_rm,av_im FUNC=x*x+y*y PERIODIC=NO
     138             : # And print out final CV to a file
     139             : PRINT ARG=av FILE=colvar STRIDE=1
     140             : ```
     141             : 
     142             : This input calculate the average value of the (complex) hexatic parameter for only those atoms that are within a spherical region that is centered on the point
     143             : $(2.5,2.5,2.5)$.  The square modulus of this average is then output.
     144             : 
     145             : ## Using nearest neighbours
     146             : 
     147             : In papers where symmetry functions similar to this one have been used a switching function is not employed. The sums over $j$ in the expression above are replaced by sums over the
     148             : six nearest neighbours to each atom.  If you would like to calculate this quantity using PLUMED you can use an input like this:
     149             : 
     150             : ```plumed
     151             : dmat: DISTANCE_MATRIX GROUP=1-400 CUTOFF=3.0 COMPONENTS
     152             : neigh: NEIGHBORS ARG=dmat.w NLOWEST=6
     153             : harm: CYLINDRICAL_HARMONIC DEGREE=6 ARG=dmat.x,dmat.y
     154             : rprod: CUSTOM ARG=neigh,harm.rm FUNC=x*y PERIODIC=NO
     155             : iprod: CUSTOM ARG=neigh,harm.im FUNC=x*y PERIODIC=NO
     156             : hex2_ones: ONES SIZE=400
     157             : hex2_denom: MATRIX_VECTOR_PRODUCT ARG=neigh,hex2_ones
     158             : harm_rm: MATRIX_VECTOR_PRODUCT ARG=rprod,hex2_ones
     159             : harm_im: MATRIX_VECTOR_PRODUCT ARG=iprod,hex2_ones
     160             : hex2_rmn: CUSTOM ARG=harm_rm,hex2_denom FUNC=x/y PERIODIC=NO
     161             : hex2_imn: CUSTOM ARG=harm_im,hex2_denom FUNC=x/y PERIODIC=NO
     162             : DUMPATOMS ATOMS=1-400 ARG=hex2_rmn,hex2_imn,hex2_denom FILE=hexparam.xyz
     163             : ```
     164             : 
     165             : This input outputs the values of the order parameters for all the atoms to an extended xyz file .
     166             : 
     167             : !!! warning "Broken virial"
     168             : 
     169             :     Virial is not working currently
     170             : 
     171             : 
     172             : */
     173             : //+ENDPLUMEDOC
     174             : 
     175             : 
     176             : class HexacticParameter : public ActionShortcut {
     177             : private:
     178             :   void createVectorNormInput( const std::string& ilab, const std::string& olab, const std::string& vlab );
     179             : public:
     180             :   static void registerKeywords( Keywords& keys );
     181             :   explicit HexacticParameter(const ActionOptions&);
     182             : };
     183             : 
     184             : PLUMED_REGISTER_ACTION(HexacticParameter,"HEXACTIC_PARAMETER")
     185             : 
     186           5 : void HexacticParameter::registerKeywords( Keywords& keys ) {
     187           5 :   CoordinationNumbers::shortcutKeywords( keys );
     188           5 :   keys.add("compulsory","PLANE","the plane to use when calculating the value of the order parameter should be xy, xz or yz");
     189          10 :   keys.setValueDescription("matrix","the value of the cylindrical harmonic for each bond vector specified");
     190           5 :   keys.addFlag("VMEAN",false,"calculate the norm of the mean vector.");
     191          10 :   keys.addOutputComponent("_vmean","VMEAN","scalar","the norm of the mean vector");
     192           5 :   keys.addFlag("VSUM",false,"calculate the norm of the sum of all the vectors");
     193          10 :   keys.addOutputComponent("_vsum","VSUM","scalar","the norm of the mean vector");
     194           5 :   keys.needsAction("CYLINDRICAL_HARMONIC_MATRIX");
     195           5 :   keys.needsAction("ONES");
     196           5 :   keys.needsAction("MATRIX_VECTOR_PRODUCT");
     197           5 :   keys.needsAction("CUSTOM");
     198           5 :   keys.needsAction("MEAN");
     199           5 :   keys.needsAction("SUM");
     200           5 :   keys.needsAction("COMBINE");
     201           5 :   keys.addDOI("10.1103/PhysRevLett.99.215701");
     202           5 : }
     203             : 
     204           1 : HexacticParameter::HexacticParameter( const ActionOptions& ao):
     205             :   Action(ao),
     206           1 :   ActionShortcut(ao) {
     207             :   std::string sp_str, specA, specB;
     208           1 :   parse("SPECIES",sp_str);
     209           1 :   parse("SPECIESA",specA);
     210           1 :   parse("SPECIESB",specB);
     211           1 :   CoordinationNumbers::expandMatrix( true, getShortcutLabel(), sp_str, specA, specB, this );
     212             :   std::string myplane;
     213           2 :   parse("PLANE",myplane);
     214           1 :   if( myplane=="xy" ) {
     215           2 :     readInputLine( getShortcutLabel() + ": CYLINDRICAL_HARMONIC_MATRIX DEGREE=6 ARG=" + getShortcutLabel() + "_mat.x," + getShortcutLabel() + "_mat.y," + getShortcutLabel() + "_mat.w" );
     216           0 :   } else if( myplane=="xz" ) {
     217           0 :     readInputLine( getShortcutLabel() + ": CYLINDRICAL_HARMONIC_MATRIX DEGREE=6 ARG=" + getShortcutLabel() + "_mat.x," + getShortcutLabel() + "_mat.z," + getShortcutLabel() + "_mat.w" );
     218           0 :   } else if( myplane=="yz" ) {
     219           0 :     readInputLine( getShortcutLabel() + ": CYLINDRICAL_HARMONIC_MATRIX DEGREE=6 ARG=" + getShortcutLabel() + "_mat.y," + getShortcutLabel() + "_mat.z," + getShortcutLabel() + "_mat.w" );
     220             :   } else {
     221           0 :     error("invalid input for plane -- should be xy, xz or yz");
     222             :   }
     223             :   // And coordination number
     224           1 :   ActionWithValue* av = plumed.getActionSet().selectWithLabel<ActionWithValue*>( getShortcutLabel() + "_mat");
     225           1 :   plumed_assert( av && av->getNumberOfComponents()>0 && (av->copyOutput(0))->getRank()==2 );
     226             :   std::string size;
     227           1 :   Tools::convert( (av->copyOutput(0))->getShape()[1], size );
     228           2 :   readInputLine( getShortcutLabel() + "_ones: ONES SIZE=" + size );
     229           2 :   readInputLine( getShortcutLabel() + "_rm: MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + ".rm," + getShortcutLabel() + "_ones");
     230           2 :   readInputLine( getShortcutLabel() + "_im: MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + ".im," + getShortcutLabel() + "_ones");
     231             :   // Input for denominator (coord)
     232           2 :   readInputLine( getShortcutLabel() + "_denom: MATRIX_VECTOR_PRODUCT ARG=" + getShortcutLabel() + "_mat.w," + getShortcutLabel() + "_ones");
     233             :   // Divide real part by coordination numbers
     234           2 :   readInputLine( getShortcutLabel() + "_rmn: CUSTOM ARG=" + getShortcutLabel() + "_rm," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     235             :   // Devide imaginary part by coordination number
     236           2 :   readInputLine( getShortcutLabel() + "_imn: CUSTOM ARG=" + getShortcutLabel() + "_im," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     237             : 
     238             :   // If we are doing VMEAN determine sum of vector components
     239             :   bool do_vmean;
     240           1 :   parseFlag("VMEAN",do_vmean);
     241           1 :   if( do_vmean ) {
     242             :     // Real part
     243           0 :     readInputLine( getShortcutLabel() + "_rms: MEAN ARG=" + getShortcutLabel() + "_rmn PERIODIC=NO");
     244             :     // Imaginary part
     245           0 :     readInputLine( getShortcutLabel() + "_ims: MEAN ARG=" + getShortcutLabel() + "_imn PERIODIC=NO");
     246             :     // Now calculate the total length of the vector
     247           0 :     createVectorNormInput( getShortcutLabel(), getShortcutLabel() + "_vmean", "ms" );
     248             :   }
     249             :   bool do_vsum;
     250           1 :   parseFlag("VSUM",do_vsum);
     251           1 :   if( do_vsum ) {
     252             :     // Real part
     253           0 :     readInputLine( getShortcutLabel() + "_rmz: SUM ARG=" + getShortcutLabel() + "_rmn PERIODIC=NO");
     254             :     // Imaginary part
     255           0 :     readInputLine( getShortcutLabel() + "_imz: SUM ARG=" + getShortcutLabel() + "_imn PERIODIC=NO");
     256             :     // Now calculate the total length of the vector
     257           0 :     createVectorNormInput( getShortcutLabel(), getShortcutLabel() + "_vsum", "mz" );
     258             :   }
     259             : 
     260             :   // Now calculate the total length of the vector
     261           2 :   createVectorNormInput( getShortcutLabel(), getShortcutLabel() + "_norm", "mn" );
     262           2 :   multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(), getShortcutLabel() + "_norm", "", this );
     263           1 : }
     264             : 
     265           1 : void HexacticParameter::createVectorNormInput( const std::string& ilab, const std::string& olab, const std::string& vlab ) {
     266           2 :   readInputLine( olab + "2: COMBINE PERIODIC=NO ARG=" + ilab + "_r" + vlab + "," + ilab + "_i" + vlab + " POWERS=2,2" );
     267           2 :   readInputLine( olab + ": CUSTOM ARG=" + olab + "2 FUNC=sqrt(x) PERIODIC=NO");
     268           1 : }
     269             : 
     270             : }
     271             : }
     272             : 

Generated by: LCOV version 1.16