LCOV - code coverage report
Current view: top level - colvar - GyrationShortcut.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 79 133 59.4 %
Date: 2025-12-04 11:19:34 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2020 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             :    See http://www.plumed.org for more information.
       5             :    This file is part of plumed, version 2.
       6             :    plumed is free software: you can redistribute it and/or modify
       7             :    it under the terms of the GNU Lesser General Public License as published by
       8             :    the Free Software Foundation, either version 3 of the License, or
       9             :    (at your option) any later version.
      10             :    plumed is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :    GNU Lesser General Public License for more details.
      14             :    You should have received a copy of the GNU Lesser General Public License
      15             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      16             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      17             : #include "core/ActionRegister.h"
      18             : #include "core/PlumedMain.h"
      19             : #include "core/ActionSet.h"
      20             : #include "core/ActionWithValue.h"
      21             : #include "core/ActionShortcut.h"
      22             : 
      23             : namespace PLMD {
      24             : namespace colvar {
      25             : 
      26             : //+PLUMEDOC MCOLVAR GYRATION_TENSOR
      27             : /*
      28             : Calculate the gyration tensor using a user specified vector of weights
      29             : 
      30             : The elements of the $3 \times 3$ gyration tensor are defined as:
      31             : 
      32             : $$
      33             : G_{\alpha\beta} = \frac{\sum_i^{n} w_i (\alpha_i-\alpha_{\rm COM})(\beta_i - \beta_{\rm COM})}{\sum_i^{n} w_i}
      34             : $$
      35             : 
      36             : where $\alpha_i$ and $\beta_i$ can be the $x$, $y$ or $z$ coordinates of atom $i$ and $\alpha_{\rm COM}$ and
      37             : $\beta_{\rm COM}$ can be the $x$, $y$ or $z$ components of the center, which is calculated using:
      38             : 
      39             : $$
      40             : {r}_{\rm COM}=\frac{\sum_i^{n} {r}_i\ w_i }{\sum_i^{n} w_i}
      41             : $$
      42             : 
      43             : The following example shows how you can calculate and print the gyration tensor from the positions of the fist 10 atoms using PLUMED
      44             : 
      45             : ```plumed
      46             : g: GYRATION_TENSOR ATOMS=1-10
      47             : PRINT ARG=g FILE=colvar
      48             : ```
      49             : 
      50             : In this example input the weights of all the atoms are set equal to one. The 9 elements of the resulting gyration matrix will
      51             : be output to the file `colvar` here.  If you want the weights of the atoms to be set equal to the masses you can use either of the following
      52             : equivalent commands:
      53             : 
      54             : ```plumed
      55             : g: GYRATION_TENSOR ATOMS=1-10 MASS
      56             : g2: GYRATION_TENSOR ATOMS=1-10 WEIGHTS=@Masses
      57             : g3: GYRATION_TENSOR ATOMS=1-10 MASS_WEIGHTED
      58             : PRINT ARG=g,g2,g3 FILE=colvar
      59             : ```
      60             : 
      61             : This input above should output three identical $3\times 3$ matrices.
      62             : 
      63             : If you want to use an arbitrary set of weights for the atoms you can use the following syntax.
      64             : 
      65             : ```plumed
      66             : c: CONSTANT VALUES=1,3,2,4
      67             : g: GYRATION_TENSOR ATOMS=1-4 WEIGHTS=c UNORMALIZED
      68             : PRINT ARG=g FILE=colvar
      69             : ```
      70             : 
      71             : Although the input weights are [CONSTANT](CONSTANT.md) here that is not a requirement.  You can use a vector of weights from any action
      72             : that outputs a vector in the input for the WEIGHTS keyword here.  Notice, also that the denominator in the expression for $G_{\alpha\beta}$
      73             : is set equal to one rather than the sum of the weights as we used the UNORMALIZED flag.
      74             : 
      75             : Similar functionality to the functionality in the examples above is used in the [GYRATION](GYRATION.md) shortcut.  There is, however,
      76             : no fast version of the GYRATION_TENSOR command in the way that there is a fast version of the [GYRATION](GYRATION.md) command that is
      77             : used when the weights are all one or when the masses are used as the weights.
      78             : 
      79             : ## A note on periodic boundary conditions
      80             : 
      81             : Calculating the gyration tensor is normally used to determine the shape of a molecule so all the specified atoms
      82             : would normally be part of the same molecule.  When computing gyration tensors it is important to ensure that the periodic boundaries
      83             : are calculated correctly.  There are two ways that you can manage periodic boundary conditions when using this action.  The
      84             : first and simplest is to reconstruct the molecule similarly to the way that [WHOLEMOLECULES](WHOLEMOLECULES.md) operates.
      85             : This reconstruction of molecules has been done automatically since PLUMED 2.2.  If for some reason you want to turn it off
      86             : you can use the NOPBC flag as shown below:
      87             : 
      88             : ```plumed
      89             : g: GYRATION_TENSOR ATOMS=1-5 NOPBC
      90             : PRINT ARG=g FILE=colvar
      91             : ```
      92             : 
      93             : An alternative approach to handling PBC is to use the PHASES keyword.  This keyword instructs PLUMED to use the PHASES option
      94             : when computing the position of the center using the [CENTER](CENTER.md) command.  Distances of atoms from this center are then
      95             : computed using PBC as usual. The example shown below shows you how to use this option
      96             : 
      97             : ```plumed
      98             : g: GYRATION_TENSOR ATOMS=1-5 PHASES
      99             : PRINT ARG=g FILE=colvar
     100             : ```
     101             : 
     102             : 
     103             : */
     104             : //+ENDPLUMEDOC
     105             : 
     106             : class GyrationShortcut : public ActionShortcut {
     107             : public:
     108             :   static void registerKeywords( Keywords& keys );
     109             :   explicit GyrationShortcut(const ActionOptions&);
     110             : };
     111             : 
     112             : PLUMED_REGISTER_ACTION(GyrationShortcut,"GYRATION")
     113             : PLUMED_REGISTER_ACTION(GyrationShortcut,"GYRATION_TENSOR")
     114             : 
     115         122 : void GyrationShortcut::registerKeywords( Keywords& keys ) {
     116         122 :   ActionShortcut::registerKeywords( keys );
     117         122 :   keys.add("atoms","ATOMS","the group of atoms that you are calculating the Gyration Tensor for");
     118         244 :   if( keys.getDisplayName()=="GYRATION" ) {
     119         116 :     keys.add("compulsory","TYPE","RADIUS","The type of calculation relative to the Gyration Tensor you want to perform");
     120             :   }
     121         122 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     122         122 :   keys.add("optional","WEIGHTS","what weights should be used when calculating the center.  If this keyword is not present the geometric center is computed. "
     123             :            "If WEIGHTS=@Masses is used the center of mass is computed.  If WEIGHTS=@charges the center of charge is computed.  If "
     124             :            "the label of an action is provided PLUMED assumes that that action calculates a list of symmetry functions that can be used "
     125             :            "as weights. Lastly, an explicit list of numbers to use as weights can be provided");
     126         122 :   keys.addFlag("PHASES",false,"use trigonometric phases when computing position of center of mass");
     127         122 :   keys.addFlag("MASS",false,"calculate the center of mass");
     128         122 :   keys.addFlag("MASS_WEIGHTED",false,"set the masses of all the atoms equal to one");
     129         122 :   keys.addFlag("UNORMALIZED",false,"do not divide by the sum of the weights");
     130         244 :   if( keys.getDisplayName()=="GYRATION" ) {
     131         232 :     keys.setValueDescription("scalar","the radius that was computed from the weights");
     132         232 :     keys.addActionNameSuffix("_FAST");
     133          12 :   } else if( keys.getDisplayName()=="GYRATION_TENSOR" ) {
     134          12 :     keys.setValueDescription("matrix","the gyration tensor that was computed from the weights");
     135             :   }
     136         122 :   keys.needsAction("CENTER");
     137         122 :   keys.needsAction("CONSTANT");
     138         122 :   keys.needsAction("ONES");
     139         122 :   keys.needsAction("MASS");
     140         122 :   keys.needsAction("DISTANCE");
     141         122 :   keys.needsAction("COVARIANCE_MATRIX");
     142         122 :   keys.needsAction("SELECT_COMPONENTS");
     143         122 :   keys.needsAction("SUM");
     144         122 :   keys.needsAction("CUSTOM");
     145         122 :   keys.needsAction("DIAGONALIZE");
     146         122 :   keys.addDOI("10.1021/jp2065612");
     147         122 : }
     148             : 
     149         116 : GyrationShortcut::GyrationShortcut(const ActionOptions& ao):
     150             :   Action(ao),
     151         116 :   ActionShortcut(ao) {
     152             :   bool usemass, phases, massw;
     153         116 :   parseFlag("MASS",usemass);
     154         116 :   parseFlag("MASS_WEIGHTED",massw);
     155         116 :   if( massw ) {
     156           0 :     usemass = true;
     157             :   }
     158         232 :   parseFlag("PHASES",phases);
     159             :   std::vector<std::string> str_weights;
     160         232 :   parseVector("WEIGHTS",str_weights);
     161             :   std::string wflab;
     162         230 :   if( !phases && getName()=="GYRATION" ) {
     163         112 :     if( usemass || str_weights.size()==0 || (str_weights.size()==1 && str_weights[0]=="@Masses") ) {
     164             :       std::string wt_str;
     165         112 :       if( str_weights.size()>0 ) {
     166           0 :         wt_str="WEIGHTS=" + str_weights[0];
     167           0 :         for(unsigned i=1; i<str_weights.size(); ++i) {
     168           0 :           wt_str += "," + str_weights[i];
     169             :         }
     170             :       }
     171         112 :       if( usemass || (str_weights.size()==1 && str_weights[0]=="@Masses") ) {
     172             :         wt_str = "MASS_WEIGHTED";
     173             :       }
     174         232 :       readInputLine( getShortcutLabel() + ": GYRATION_FAST " + wt_str + " " + convertInputLineToString() );
     175             :       return;
     176             :     }
     177             :   }
     178           4 :   if( usemass ) {
     179           0 :     str_weights.resize(1);
     180             :     str_weights[0]="@Masses";
     181             :   }
     182          10 :   log<<"  Bibliography "<<plumed.cite("Jirí Vymetal and Jirí Vondrasek, J. Phys. Chem. A 115, 11455 (2011)")<<"\n";
     183             :   // Read in the atoms involved
     184             :   std::vector<std::string> atoms;
     185           4 :   parseVector("ATOMS",atoms);
     186           4 :   Tools::interpretRanges(atoms);
     187           4 :   std::string gtype, atlist=atoms[0];
     188          12 :   for(unsigned i=1; i<atoms.size(); ++i) {
     189          16 :     atlist += "," + atoms[i];
     190             :   }
     191             :   bool nopbc;
     192           8 :   parseFlag("NOPBC",nopbc);
     193             :   std::string pbcstr;
     194           4 :   if(nopbc) {
     195             :     pbcstr = " NOPBC";
     196             :   }
     197             :   std::string phasestr;
     198           4 :   if(phases) {
     199             :     phasestr = " PHASES";
     200             :   }
     201             :   // Create the geometric center of the molecule
     202           4 :   std::string weights_str="";
     203           4 :   if( str_weights.size()>0 ) {
     204           4 :     weights_str=" WEIGHTS=" + str_weights[0];
     205           8 :     for(unsigned i=1; i<str_weights.size(); ++i) {
     206           8 :       weights_str += "," + str_weights[i];
     207             :     }
     208             :   }
     209           8 :   readInputLine( getShortcutLabel() + "_cent: CENTER ATOMS=" + atlist + pbcstr + phasestr + weights_str );
     210           4 :   if( str_weights.size()==0 ) {
     211           0 :     wflab = getShortcutLabel() + "_w";
     212             :     std::string str_natoms;
     213           0 :     Tools::convert( atoms.size(), str_natoms );
     214           0 :     readInputLine( getShortcutLabel() + "_w: ONES SIZE=" + str_natoms );
     215           6 :   } else if( str_weights.size()==1 && str_weights[0]=="@Masses" ) {
     216           0 :     wflab = getShortcutLabel() + "_m";
     217           0 :     readInputLine( getShortcutLabel() + "_m: MASS ATOMS=" + atlist );
     218           4 :   } else if( str_weights.size()>1 ) {
     219           2 :     std::string vals=str_weights[0];
     220           6 :     for(unsigned i=1; i<str_weights.size(); ++i) {
     221           8 :       vals += "," + str_weights[i];
     222             :     }
     223           4 :     readInputLine( getShortcutLabel() + "_w: CONSTANT VALUES=" + vals );
     224           4 :     wflab=getShortcutLabel() + "_w";
     225             :   } else {
     226           2 :     plumed_assert( str_weights.size()==1 );
     227           2 :     wflab = getShortcutLabel() + "_cent_w";
     228           2 :     ActionWithValue* av=plumed.getActionSet().selectWithLabel<ActionWithValue*>( wflab );
     229           2 :     if( !av ) {
     230             :       wflab = str_weights[0];
     231             :     }
     232             :   }
     233             :   // Check for normalisation
     234             :   bool unorm;
     235           8 :   parseFlag("UNORMALIZED",unorm);
     236             :   // Find out the type
     237           4 :   if( getName()!="GYRATION_TENSOR" ) {
     238           0 :     parse("TYPE",gtype);
     239           0 :     if( gtype!="RADIUS" && gtype!="TRACE" && gtype!="GTPC_1" && gtype!="GTPC_2" && gtype!="GTPC_3" && gtype!="ASPHERICITY" && gtype!="ACYLINDRICITY"
     240           0 :         && gtype!= "KAPPA2" && gtype!="RGYR_1" && gtype!="RGYR_2" && gtype!="RGYR_3" ) {
     241           0 :       error("type " + gtype + " is invalid");
     242             :     }
     243             :     // Check if we need to calculate the unormlised radius
     244           0 :     if( gtype=="TRACE" || gtype=="KAPPA2" ) {
     245           0 :       unorm=true;
     246             :     }
     247             :   }
     248             :   // Compute all the vectors separating all the positions from the center
     249           4 :   std::string distance_act = getShortcutLabel() + "_dists: DISTANCE COMPONENTS" + pbcstr;
     250          16 :   for(unsigned i=0; i<atoms.size(); ++i) {
     251             :     std::string num;
     252          12 :     Tools::convert( i+1, num );
     253          24 :     distance_act += " ATOMS" + num + "=" + getShortcutLabel() + "_cent," + atoms[i];
     254             :   }
     255           4 :   readInputLine( distance_act );
     256             :   // And calculate the covariance
     257             :   std::string norm_str;
     258           4 :   if( unorm ) {
     259             :     norm_str = " UNORMALIZED";
     260             :   }
     261           4 :   if( getName()=="GYRATION_TENSOR" ) {
     262           8 :     readInputLine( getShortcutLabel() + ": COVARIANCE_MATRIX ARG=" + getShortcutLabel() + "_dists.x," + getShortcutLabel() + "_dists.y," + getShortcutLabel() + "_dists.z WEIGHTS=" + wflab + norm_str );
     263             :     return;
     264             :   }
     265           0 :   readInputLine( getShortcutLabel() + "_tensor: COVARIANCE_MATRIX ARG=" + getShortcutLabel() + "_dists.x," + getShortcutLabel() + "_dists.y," + getShortcutLabel() + "_dists.z WEIGHTS=" + wflab + norm_str );
     266             :   // Pick out the diagonal elements
     267           0 :   readInputLine( getShortcutLabel() + "_diag_elements: SELECT_COMPONENTS ARG=" + getShortcutLabel() + "_tensor COMPONENTS=1.1,2.2,3.3");
     268           0 :   if( gtype=="RADIUS") {
     269             :     // And now we need the average trace for the gyration radius
     270           0 :     readInputLine( getShortcutLabel() + "_trace: SUM ARG=" + getShortcutLabel() + "_diag_elements PERIODIC=NO");
     271             :     // Square root the radius
     272           0 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_trace FUNC=sqrt(x) PERIODIC=NO");
     273           0 :   } else if( gtype=="TRACE" ) {
     274             :     // Compte the trace of the gyration tensor
     275           0 :     readInputLine( getShortcutLabel() + "_trace: SUM ARG=" + getShortcutLabel() + "_diag_elements PERIODIC=NO");
     276             :     // And double it
     277           0 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_trace FUNC=2*x PERIODIC=NO");
     278             :   } else {
     279             :     // Diagonalize the gyration tensor
     280           0 :     readInputLine( getShortcutLabel() + "_diag: DIAGONALIZE ARG=" + getShortcutLabel() + "_tensor VECTORS=all" );
     281           0 :     if( gtype.find("GTPC")!=std::string::npos ) {
     282           0 :       std::size_t und=gtype.find_first_of("_");
     283           0 :       if( und==std::string::npos ) {
     284           0 :         error( gtype + " is not a valid type for gyration radius");
     285             :       }
     286           0 :       std::string num = gtype.substr(und+1);
     287           0 :       if( num!="1" && num!="2" && num!="3" ) {
     288           0 :         error( gtype + " is not a valid type for gyration radius");
     289             :       }
     290             :       // Now get the appropriate eigenvalue
     291           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-" + num + " FUNC=sqrt(x) PERIODIC=NO");
     292           0 :     } else if( gtype.find("RGYR")!=std::string::npos ) {
     293           0 :       std::size_t und=gtype.find_first_of("_");
     294           0 :       if( und==std::string::npos ) {
     295           0 :         error( gtype + " is not a valid type for gyration radius");
     296             :       }
     297             :       unsigned ind;
     298           0 :       Tools::convert( gtype.substr(und+1), ind );
     299             :       // Now get the appropriate quantity
     300           0 :       if( ind==3 ) {
     301           0 :         readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-2 FUNC=sqrt(x+y) PERIODIC=NO");
     302           0 :       } else if( ind==2 ) {
     303           0 :         readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x+y) PERIODIC=NO");
     304           0 :       } else if( ind==1 ) {
     305           0 :         readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x+y) PERIODIC=NO");
     306             :       } else {
     307           0 :         error( gtype + " is not a valid type for gyration radius");
     308             :       }
     309           0 :     } else if( gtype=="ASPHERICITY" ) {
     310           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x-0.5*(y+z)) PERIODIC=NO" );
     311           0 :     } else if( gtype=="ACYLINDRICITY" ) {
     312           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x-y) PERIODIC=NO" );
     313           0 :     } else if( gtype=="KAPPA2" ) {
     314           0 :       readInputLine( getShortcutLabel() + "_numer: CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=x*y+x*z+y*z PERIODIC=NO" );
     315           0 :       readInputLine( getShortcutLabel() + "_denom: CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=x+y+z PERIODIC=NO" );
     316           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=1-3*(x/(y*y)) PERIODIC=NO");
     317             :     } else {
     318           0 :       error( gtype + " is not a valid type for gyration radius");
     319             :     }
     320             :   }
     321         124 : }
     322             : 
     323             : }
     324             : }

Generated by: LCOV version 1.16