LCOV - code coverage report
Current view: top level - colvar - GyrationShortcut.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 88 141 62.4 %
Date: 2026-11-05 10:19:04 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         156 : void GyrationShortcut::registerKeywords( Keywords& keys ) {
     116         156 :   ActionShortcut::registerKeywords( keys );
     117         156 :   keys.add("atoms","ATOMS","the group of atoms that you are calculating the Gyration Tensor for");
     118         156 :   if( keys.getDisplayName()=="GYRATION" ) {
     119         148 :     keys.add("compulsory","TYPE","RADIUS","The type of calculation relative to the Gyration Tensor you want to perform");
     120             :   }
     121         156 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     122         156 :   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         156 :   keys.addFlag("PHASES",false,"use trigonometric phases when computing position of center of mass");
     127         156 :   keys.addFlag("MASS",false,"calculate the center of mass");
     128         156 :   keys.addFlag("MASS_WEIGHTED",false,"set the masses of all the atoms equal to one");
     129         156 :   keys.addFlag("UNORMALIZED",false,"do not divide by the sum of the weights");
     130         156 :   if( keys.getDisplayName()=="GYRATION" ) {
     131         296 :     keys.setValueDescription("scalar","the radius that was computed from the weights");
     132         148 :     keys.addActionNameSuffix("_SCALAR");
     133         296 :     keys.addActionNameSuffix("_VECTOR");
     134           8 :   } else if( keys.getDisplayName()=="GYRATION_TENSOR" ) {
     135          16 :     keys.setValueDescription("matrix","the gyration tensor that was computed from the weights");
     136             :   }
     137         156 :   keys.needsAction("CENTER");
     138         156 :   keys.needsAction("CONSTANT");
     139         156 :   keys.needsAction("ONES");
     140         156 :   keys.needsAction("MASS");
     141         156 :   keys.needsAction("DISTANCE");
     142         156 :   keys.needsAction("COVARIANCE_MATRIX");
     143         156 :   keys.needsAction("SELECT_COMPONENTS");
     144         156 :   keys.needsAction("SUM");
     145         156 :   keys.needsAction("CUSTOM");
     146         156 :   keys.needsAction("DIAGONALIZE");
     147         156 :   keys.addDOI("10.1021/jp2065612");
     148         156 : }
     149             : 
     150         146 : GyrationShortcut::GyrationShortcut(const ActionOptions& ao):
     151             :   Action(ao),
     152         146 :   ActionShortcut(ao) {
     153             :   bool usemass, phases, massw;
     154         146 :   parseFlag("MASS",usemass);
     155         146 :   parseFlag("MASS_WEIGHTED",massw);
     156         146 :   if( massw ) {
     157          22 :     usemass = true;
     158             :   }
     159         146 :   parseFlag("PHASES",phases);
     160         146 :   std::vector<std::string> str_weights;
     161         292 :   parseVector("WEIGHTS",str_weights);
     162             :   std::string wflab;
     163         146 :   if( !phases && getName()=="GYRATION" ) {
     164         142 :     if( usemass || str_weights.size()==0 || (str_weights.size()==1 && str_weights[0]=="@Masses") ) {
     165             :       std::string wt_str;
     166         142 :       if( str_weights.size()>0 ) {
     167           0 :         wt_str="WEIGHTS=" + str_weights[0];
     168           0 :         for(unsigned i=1; i<str_weights.size(); ++i) {
     169           2 :           wt_str += "," + str_weights[i];
     170             :         }
     171             :       }
     172         142 :       if( usemass || (str_weights.size()==1 && str_weights[0]=="@Masses") ) {
     173             :         wt_str = "MASS_WEIGHTED";
     174             :       }
     175             :       std::string inpt;
     176         284 :       parseNumbered( "ATOMS", 1, inpt );
     177         142 :       if( inpt.length()>0 ) {
     178           4 :         readInputLine( getShortcutLabel() + ": GYRATION_VECTOR " + wt_str + " ATOMS1=" + inpt + " " +  convertInputLineToString() );
     179             :       } else {
     180         425 :         readInputLine( getShortcutLabel() + ": GYRATION_SCALAR " + wt_str + " " + convertInputLineToString() );
     181             :       }
     182             :       return;
     183             :     }
     184             :   }
     185           4 :   if( usemass ) {
     186           0 :     str_weights.resize(1);
     187             :     str_weights[0]="@Masses";
     188             :   }
     189           6 :   log<<"  Bibliography "<<plumed.cite("Jirí Vymetal and Jirí Vondrasek, J. Phys. Chem. A 115, 11455 (2011)")<<"\n";
     190             :   // Read in the atoms involved
     191           4 :   std::vector<std::string> atoms;
     192           4 :   parseVector("ATOMS",atoms);
     193           4 :   Tools::interpretRanges(atoms);
     194           4 :   if( atoms.empty() ) {
     195           0 :     plumed_merror("no atoms were specified, use the ATOMS keyword");
     196             :   }
     197           4 :   std::string gtype, atlist=atoms[0];
     198          12 :   for(unsigned i=1; i<atoms.size(); ++i) {
     199          16 :     atlist += "," + atoms[i];
     200             :   }
     201             :   bool nopbc;
     202           8 :   parseFlag("NOPBC",nopbc);
     203             :   std::string pbcstr;
     204           4 :   if(nopbc) {
     205             :     pbcstr = " NOPBC";
     206             :   }
     207             :   std::string phasestr;
     208           4 :   if(phases) {
     209             :     phasestr = " PHASES";
     210             :   }
     211             :   // Create the geometric center of the molecule
     212           4 :   std::string weights_str="";
     213           4 :   if( str_weights.size()>0 ) {
     214           4 :     weights_str=" WEIGHTS=" + str_weights[0];
     215           8 :     for(unsigned i=1; i<str_weights.size(); ++i) {
     216           8 :       weights_str += "," + str_weights[i];
     217             :     }
     218             :   }
     219           8 :   readInputLine( getShortcutLabel() + "_cent: CENTER ATOMS=" + atlist + pbcstr + phasestr + weights_str );
     220           4 :   if( str_weights.size()==0 ) {
     221           0 :     wflab = getShortcutLabel() + "_w";
     222             :     std::string str_natoms;
     223           0 :     Tools::convert( atoms.size(), str_natoms );
     224           0 :     readInputLine( getShortcutLabel() + "_w: ONES SIZE=" + str_natoms );
     225           4 :   } else if( str_weights.size()==1 && str_weights[0]=="@Masses" ) {
     226           0 :     wflab = getShortcutLabel() + "_m";
     227           0 :     readInputLine( getShortcutLabel() + "_m: MASS ATOMS=" + atlist );
     228           4 :   } else if( str_weights.size()>1 ) {
     229           2 :     std::string vals=str_weights[0];
     230           6 :     for(unsigned i=1; i<str_weights.size(); ++i) {
     231           8 :       vals += "," + str_weights[i];
     232             :     }
     233           2 :     readInputLine( getShortcutLabel() + "_w: CONSTANT VALUES=" + vals );
     234           4 :     wflab=getShortcutLabel() + "_w";
     235             :   } else {
     236           2 :     plumed_assert( str_weights.size()==1 );
     237           2 :     wflab = getShortcutLabel() + "_cent_w";
     238           2 :     ActionWithValue* av=plumed.getActionSet().selectWithLabel<ActionWithValue*>( wflab );
     239           2 :     if( !av ) {
     240             :       wflab = str_weights[0];
     241             :     }
     242             :   }
     243             :   // Check for normalisation
     244             :   bool unorm;
     245           8 :   parseFlag("UNORMALIZED",unorm);
     246             :   // Find out the type
     247           4 :   if( getName()!="GYRATION_TENSOR" ) {
     248           0 :     parse("TYPE",gtype);
     249           0 :     if( gtype!="RADIUS" && gtype!="TRACE" && gtype!="GTPC_1" && gtype!="GTPC_2" && gtype!="GTPC_3" && gtype!="ASPHERICITY" && gtype!="ACYLINDRICITY"
     250           0 :         && gtype!= "KAPPA2" && gtype!="RGYR_1" && gtype!="RGYR_2" && gtype!="RGYR_3" ) {
     251           0 :       error("type " + gtype + " is invalid");
     252             :     }
     253             :     // Check if we need to calculate the unormlised radius
     254           0 :     if( gtype=="TRACE" || gtype=="KAPPA2" ) {
     255           0 :       unorm=true;
     256             :     }
     257             :   }
     258             :   // Compute all the vectors separating all the positions from the center
     259           4 :   std::string distance_act = getShortcutLabel() + "_dists: DISTANCE COMPONENTS" + pbcstr;
     260          16 :   for(unsigned i=0; i<atoms.size(); ++i) {
     261             :     std::string num;
     262          12 :     Tools::convert( i+1, num );
     263          48 :     distance_act += " ATOMS" + num + "=" + getShortcutLabel() + "_cent," + atoms[i];
     264             :   }
     265           4 :   readInputLine( distance_act );
     266             :   // And calculate the covariance
     267             :   std::string norm_str;
     268           4 :   if( unorm ) {
     269             :     norm_str = " UNORMALIZED";
     270             :   }
     271           4 :   if( getName()=="GYRATION_TENSOR" ) {
     272          20 :     readInputLine( getShortcutLabel() + ": COVARIANCE_MATRIX ARG=" + getShortcutLabel() + "_dists.x," + getShortcutLabel() + "_dists.y," + getShortcutLabel() + "_dists.z WEIGHTS=" + wflab + norm_str );
     273             :     return;
     274             :   }
     275           0 :   readInputLine( getShortcutLabel() + "_tensor: COVARIANCE_MATRIX ARG=" + getShortcutLabel() + "_dists.x," + getShortcutLabel() + "_dists.y," + getShortcutLabel() + "_dists.z WEIGHTS=" + wflab + norm_str );
     276             :   // Pick out the diagonal elements
     277           0 :   readInputLine( getShortcutLabel() + "_diag_elements: SELECT_COMPONENTS ARG=" + getShortcutLabel() + "_tensor COMPONENTS=1.1,2.2,3.3");
     278           0 :   if( gtype=="RADIUS") {
     279             :     // And now we need the average trace for the gyration radius
     280           0 :     readInputLine( getShortcutLabel() + "_trace: SUM ARG=" + getShortcutLabel() + "_diag_elements PERIODIC=NO");
     281             :     // Square root the radius
     282           0 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_trace FUNC=sqrt(x) PERIODIC=NO");
     283           0 :   } else if( gtype=="TRACE" ) {
     284             :     // Compte the trace of the gyration tensor
     285           0 :     readInputLine( getShortcutLabel() + "_trace: SUM ARG=" + getShortcutLabel() + "_diag_elements PERIODIC=NO");
     286             :     // And double it
     287           0 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_trace FUNC=2*x PERIODIC=NO");
     288             :   } else {
     289             :     // Diagonalize the gyration tensor
     290           0 :     readInputLine( getShortcutLabel() + "_diag: DIAGONALIZE ARG=" + getShortcutLabel() + "_tensor VECTORS=all" );
     291           0 :     if( gtype.find("GTPC")!=std::string::npos ) {
     292           0 :       std::size_t und=gtype.find_first_of("_");
     293           0 :       if( und==std::string::npos ) {
     294           0 :         error( gtype + " is not a valid type for gyration radius");
     295             :       }
     296           0 :       std::string num = gtype.substr(und+1);
     297           0 :       if( num!="1" && num!="2" && num!="3" ) {
     298           0 :         error( gtype + " is not a valid type for gyration radius");
     299             :       }
     300             :       // Now get the appropriate eigenvalue
     301           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-" + num + " FUNC=sqrt(x) PERIODIC=NO");
     302           0 :     } else if( gtype.find("RGYR")!=std::string::npos ) {
     303           0 :       std::size_t und=gtype.find_first_of("_");
     304           0 :       if( und==std::string::npos ) {
     305           0 :         error( gtype + " is not a valid type for gyration radius");
     306             :       }
     307             :       unsigned ind;
     308           0 :       Tools::convert( gtype.substr(und+1), ind );
     309             :       // Now get the appropriate quantity
     310           0 :       if( ind==3 ) {
     311           0 :         readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-2 FUNC=sqrt(x+y) PERIODIC=NO");
     312           0 :       } else if( ind==2 ) {
     313           0 :         readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x+y) PERIODIC=NO");
     314           0 :       } else if( ind==1 ) {
     315           0 :         readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x+y) PERIODIC=NO");
     316             :       } else {
     317           0 :         error( gtype + " is not a valid type for gyration radius");
     318             :       }
     319           0 :     } else if( gtype=="ASPHERICITY" ) {
     320           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" );
     321           0 :     } else if( gtype=="ACYLINDRICITY" ) {
     322           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=sqrt(x-y) PERIODIC=NO" );
     323           0 :     } else if( gtype=="KAPPA2" ) {
     324           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" );
     325           0 :       readInputLine( getShortcutLabel() + "_denom: CUSTOM ARG=" + getShortcutLabel() + "_diag.vals-1," + getShortcutLabel() + "_diag.vals-2," + getShortcutLabel() + "_diag.vals-3 FUNC=x+y+z PERIODIC=NO" );
     326           0 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=1-3*(x/(y*y)) PERIODIC=NO");
     327             :     } else {
     328           0 :       error( gtype + " is not a valid type for gyration radius");
     329             :     }
     330             :   }
     331         154 : }
     332             : 
     333             : }
     334             : }

Generated by: LCOV version 1.16