LCOV - code coverage report
Current view: top level - colvar - Gyration.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 147 186 79.0 %
Date: 2020-11-18 11:20:57 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2019 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 "Colvar.h"
      23             : #include "ActionRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "tools/Matrix.h"
      26             : 
      27             : #include <string>
      28             : #include <cmath>
      29             : 
      30             : using namespace std;
      31             : 
      32             : namespace PLMD {
      33             : namespace colvar {
      34             : 
      35             : //+PLUMEDOC COLVAR GYRATION
      36             : /*
      37             : Calculate the radius of gyration, or other properties related to it.
      38             : 
      39             : The different properties can be calculated and selected by the TYPE keyword:
      40             : the Radius of Gyration (RADIUS); the Trace of the Gyration Tensor (TRACE);
      41             : the Largest Principal Moment of the Gyration Tensor (GTPC_1); the middle Principal Moment of the Gyration Tensor (GTPC_2);
      42             : the Smallest Principal Moment of the Gyration Tensor (GTPC_3); the Asphericiry (ASPHERICITY); the Acylindricity (ACYLINDRICITY);
      43             : the Relative Shape Anisotropy (KAPPA2); the Smallest Principal Radius Of Gyration (GYRATION_3);
      44             : the Middle Principal Radius of Gyration (GYRATION_2); the Largest Principal Radius of Gyration (GYRATION_1).
      45             : A derivation of all these different variants can be found in \cite Vymetal:2011gv
      46             : 
      47             : The radius of gyration is calculated using:
      48             : 
      49             : \f[
      50             : s_{\rm Gyr}=\Big ( \frac{\sum_i^{n}
      51             :  m_i \vert {r}_i -{r}_{\rm COM} \vert ^2 }{\sum_i^{n} m_i} \Big)^{1/2}
      52             : \f]
      53             : 
      54             : with the position of the center of mass \f${r}_{\rm COM}\f$ given by:
      55             : 
      56             : \f[
      57             : {r}_{\rm COM}=\frac{\sum_i^{n} {r}_i\ m_i }{\sum_i^{n} m_i}
      58             : \f]
      59             : 
      60             : The radius of gyration usually makes sense when atoms used for the calculation
      61             : are all part of the same molecule.
      62             : When running with periodic boundary conditions, the atoms should be
      63             : in the proper periodic image. This is done automatically since PLUMED 2.2,
      64             : by considering the ordered list of atoms and rebuilding PBCs with a procedure
      65             : that is equivalent to that done in \ref WHOLEMOLECULES . Notice that
      66             : rebuilding is local to this action. This is different from \ref WHOLEMOLECULES
      67             : which actually modifies the coordinates stored in PLUMED.
      68             : 
      69             : In case you want to recover the old behavior you should use the NOPBC flag.
      70             : In that case you need to take care that atoms are in the correct
      71             : periodic image.
      72             : 
      73             : 
      74             : \par Examples
      75             : 
      76             : The following input tells plumed to print the radius of gyration of the
      77             : chain containing atoms 10 to 20.
      78             : \plumedfile
      79             : GYRATION TYPE=RADIUS ATOMS=10-20 LABEL=rg
      80             : PRINT ARG=rg STRIDE=1 FILE=colvar
      81             : \endplumedfile
      82             : 
      83             : */
      84             : //+ENDPLUMEDOC
      85             : 
      86          48 : class Gyration : public Colvar {
      87             : private:
      88             :   enum CV_TYPE {RADIUS, TRACE, GTPC_1, GTPC_2, GTPC_3, ASPHERICITY, ACYLINDRICITY, KAPPA2, GYRATION_3, GYRATION_2, GYRATION_1, TOT};
      89             :   int rg_type;
      90             :   bool use_masses;
      91             :   bool nopbc;
      92             : public:
      93             :   static void registerKeywords(Keywords& keys);
      94             :   explicit Gyration(const ActionOptions&);
      95             :   virtual void calculate();
      96             : };
      97             : 
      98        6478 : PLUMED_REGISTER_ACTION(Gyration,"GYRATION")
      99             : 
     100          27 : void Gyration::registerKeywords(Keywords& keys) {
     101          27 :   Colvar::registerKeywords(keys);
     102         108 :   keys.add("atoms","ATOMS","the group of atoms that you are calculating the Gyration Tensor for");
     103         135 :   keys.add("compulsory","TYPE","RADIUS","The type of calculation relative to the Gyration Tensor you want to perform");
     104          81 :   keys.addFlag("MASS_WEIGHTED",false,"set the masses of all the atoms equal to one");
     105          27 : }
     106             : 
     107          26 : Gyration::Gyration(const ActionOptions&ao):
     108             :   PLUMED_COLVAR_INIT(ao),
     109             :   use_masses(false),
     110          28 :   nopbc(false)
     111             : {
     112             :   std::vector<AtomNumber> atoms;
     113          52 :   parseAtomList("ATOMS",atoms);
     114          25 :   if(atoms.size()==0) error("no atoms specified");
     115          50 :   parseFlag("MASS_WEIGHTED",use_masses);
     116             :   std::string Type;
     117          50 :   parse("TYPE",Type);
     118          50 :   parseFlag("NOPBC",nopbc);
     119          25 :   checkRead();
     120             : 
     121          25 :   if(Type=="RADIUS") rg_type=RADIUS;
     122          21 :   else if(Type=="TRACE") rg_type=TRACE;
     123          19 :   else if(Type=="GTPC_1") rg_type=GTPC_1;
     124          17 :   else if(Type=="GTPC_2") rg_type=GTPC_2;
     125          15 :   else if(Type=="GTPC_3") rg_type=GTPC_3;
     126          13 :   else if(Type=="ASPHERICITY") rg_type=ASPHERICITY;
     127          11 :   else if(Type=="ACYLINDRICITY") rg_type=ACYLINDRICITY;
     128           9 :   else if(Type=="KAPPA2") rg_type=KAPPA2;
     129           7 :   else if(Type=="RGYR_3") rg_type=GYRATION_3;
     130           5 :   else if(Type=="RGYR_2") rg_type=GYRATION_2;
     131           3 :   else if(Type=="RGYR_1") rg_type=GYRATION_1;
     132           2 :   else error("Unknown GYRATION type");
     133             : 
     134          24 :   switch(rg_type)
     135             :   {
     136           4 :   case RADIUS:   log.printf("  GYRATION RADIUS (Rg);"); break;
     137           2 :   case TRACE:  log.printf("  TRACE OF THE GYRATION TENSOR;"); break;
     138           2 :   case GTPC_1: log.printf("  THE LARGEST PRINCIPAL MOMENT OF THE GYRATION TENSOR (S'_1);"); break;
     139           2 :   case GTPC_2: log.printf("  THE MIDDLE PRINCIPAL MOMENT OF THE GYRATION TENSOR (S'_2);");  break;
     140           2 :   case GTPC_3: log.printf("  THE SMALLEST PRINCIPAL MOMENT OF THE GYRATION TENSOR (S'_3);"); break;
     141           2 :   case ASPHERICITY: log.printf("  THE ASPHERICITY (b');"); break;
     142           2 :   case ACYLINDRICITY: log.printf("  THE ACYLINDRICITY (c');"); break;
     143           2 :   case KAPPA2: log.printf("  THE RELATIVE SHAPE ANISOTROPY (kappa^2);"); break;
     144           2 :   case GYRATION_3: log.printf("  THE SMALLEST PRINCIPAL RADIUS OF GYRATION (r_g3);"); break;
     145           2 :   case GYRATION_2: log.printf("  THE MIDDLE PRINCIPAL RADIUS OF GYRATION (r_g2);"); break;
     146           2 :   case GYRATION_1: log.printf("  THE LARGEST PRINCIPAL RADIUS OF GYRATION (r_g1);"); break;
     147             :   }
     148          60 :   if(rg_type>TRACE) log<<"  Bibliography "<<plumed.cite("Jirí Vymetal and Jirí Vondrasek, J. Phys. Chem. A 115, 11455 (2011)");
     149          24 :   log<<"\n";
     150             : 
     151          24 :   log.printf("  atoms involved : ");
     152         444 :   for(unsigned i=0; i<atoms.size(); ++i) {
     153         132 :     if(i%25==0) log<<"\n";
     154         264 :     log.printf("%d ",atoms[i].serial());
     155             :   }
     156          24 :   log.printf("\n");
     157             : 
     158          24 :   if(nopbc) {
     159           4 :     log<<"  PBC will be ignored\n";
     160             :   } else {
     161          20 :     log<<"  broken molecules will be rebuilt assuming atoms are in the proper order\n";
     162             :   }
     163             : 
     164          24 :   addValueWithDerivatives(); setNotPeriodic();
     165          24 :   requestAtoms(atoms);
     166          24 : }
     167             : 
     168        1188 : void Gyration::calculate() {
     169             : 
     170        1188 :   if(!nopbc) makeWhole();
     171             : 
     172        1188 :   Vector com;
     173             :   double totmass = 0.;
     174        1188 :   if( use_masses ) {
     175           0 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     176           0 :       totmass+=getMass(i);
     177           0 :       com+=getMass(i)*getPosition(i);
     178             :     }
     179             :   } else {
     180        1188 :     totmass = static_cast<double>(getNumberOfAtoms());
     181       19404 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     182       18216 :       com+=getPosition(i);
     183             :     }
     184             :   }
     185        1188 :   com /= totmass;
     186             : 
     187        1188 :   double rgyr=0.;
     188        1188 :   vector<Vector> derivatives( getNumberOfAtoms() );
     189        1188 :   Tensor virial;
     190             : 
     191        1188 :   if(rg_type==RADIUS||rg_type==TRACE) {
     192         788 :     if( use_masses ) {
     193           0 :       for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     194           0 :         const Vector diff = delta( com, getPosition(i) );
     195           0 :         rgyr          += getMass(i)*diff.modulo2();
     196           0 :         derivatives[i] = diff*getMass(i);
     197           0 :         virial        -= Tensor(getPosition(i),derivatives[i]);
     198             :       }
     199             :     } else {
     200       15004 :       for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     201       14216 :         const Vector diff = delta( com, getPosition(i) );
     202        7108 :         rgyr          += diff.modulo2();
     203       14216 :         derivatives[i] = diff;
     204        7108 :         virial        -= Tensor(getPosition(i),derivatives[i]);
     205             :       }
     206             :     }
     207             :     double fact;
     208         788 :     if(rg_type==RADIUS) {
     209         658 :       rgyr = sqrt(rgyr/totmass);
     210         658 :       fact = 1./(rgyr*totmass);
     211             :     } else {
     212         130 :       rgyr = 2.*rgyr;
     213             :       fact = 4;
     214             :     }
     215         788 :     setValue(rgyr);
     216       22112 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) setAtomsDerivatives(i,fact*derivatives[i]);
     217        1576 :     setBoxDerivatives(fact*virial);
     218             :     return;
     219             :   }
     220             : 
     221             : 
     222             :   Matrix<double> gyr_tens(3,3);
     223        5200 :   for(unsigned i=0; i<3; i++)  for(unsigned j=0; j<3; j++) gyr_tens(i,j)=0.;
     224             :   //calculate gyration tensor
     225         400 :   if( use_masses ) {
     226           0 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     227           0 :       const Vector diff=delta( com, getPosition(i) );
     228           0 :       gyr_tens[0][0]+=getMass(i)*diff[0]*diff[0];
     229           0 :       gyr_tens[1][1]+=getMass(i)*diff[1]*diff[1];
     230           0 :       gyr_tens[2][2]+=getMass(i)*diff[2]*diff[2];
     231           0 :       gyr_tens[0][1]+=getMass(i)*diff[0]*diff[1];
     232           0 :       gyr_tens[0][2]+=getMass(i)*diff[0]*diff[2];
     233           0 :       gyr_tens[1][2]+=getMass(i)*diff[1]*diff[2];
     234             :     }
     235             :   } else {
     236        4400 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     237        4000 :       const Vector diff=delta( com, getPosition(i) );
     238        2000 :       gyr_tens[0][0]+=diff[0]*diff[0];
     239        2000 :       gyr_tens[1][1]+=diff[1]*diff[1];
     240        2000 :       gyr_tens[2][2]+=diff[2]*diff[2];
     241        2000 :       gyr_tens[0][1]+=diff[0]*diff[1];
     242        2000 :       gyr_tens[0][2]+=diff[0]*diff[2];
     243        2000 :       gyr_tens[1][2]+=diff[1]*diff[2];
     244             :     }
     245             :   }
     246             : 
     247             :   // first make the matrix symmetric
     248         400 :   gyr_tens[1][0] = gyr_tens[0][1];
     249         400 :   gyr_tens[2][0] = gyr_tens[0][2];
     250         400 :   gyr_tens[2][1] = gyr_tens[1][2];
     251             :   Matrix<double> ttransf(3,3), transf(3,3);
     252         400 :   vector<double> princ_comp(3), prefactor(3);
     253         400 :   prefactor[0]=prefactor[1]=prefactor[2]=0.;
     254             :   //diagonalize gyration tensor
     255         400 :   diagMat(gyr_tens, princ_comp, ttransf);
     256         400 :   transpose(ttransf, transf);
     257             :   //sort eigenvalues and eigenvectors
     258         400 :   if (princ_comp[0]<princ_comp[1]) {
     259         800 :     double tmp=princ_comp[0]; princ_comp[0]=princ_comp[1]; princ_comp[1]=tmp;
     260        1600 :     for (unsigned i=0; i<3; i++) {tmp=transf[i][0]; transf[i][0]=transf[i][1]; transf[i][1]=tmp;}
     261             :   }
     262         400 :   if (princ_comp[1]<princ_comp[2]) {
     263         400 :     double tmp=princ_comp[1]; princ_comp[1]=princ_comp[2]; princ_comp[2]=tmp;
     264        1600 :     for (unsigned i=0; i<3; i++) {tmp=transf[i][1]; transf[i][1]=transf[i][2]; transf[i][2]=tmp;}
     265             :   }
     266         400 :   if (princ_comp[0]<princ_comp[1]) {
     267         800 :     double tmp=princ_comp[0]; princ_comp[0]=princ_comp[1]; princ_comp[1]=tmp;
     268        1600 :     for (unsigned i=0; i<3; i++) {tmp=transf[i][0]; transf[i][0]=transf[i][1]; transf[i][1]=tmp;}
     269             :   }
     270             :   //calculate determinant of transformation matrix
     271        1200 :   double det = transf[0][0]*transf[1][1]*transf[2][2]+transf[0][1]*transf[1][2]*transf[2][0]+
     272        1600 :                transf[0][2]*transf[1][0]*transf[2][1]-transf[0][2]*transf[1][1]*transf[2][0]-
     273         800 :                transf[0][1]*transf[1][0]*transf[2][2]-transf[0][0]*transf[1][2]*transf[2][1];
     274             :   // trasformation matrix for rotation must have positive determinant, otherwise multiply one column by (-1)
     275         400 :   if(det<0) {
     276        1600 :     for(unsigned j=0; j<3; j++) transf[j][2]=-transf[j][2];
     277         400 :     det = -det;
     278             :   }
     279         400 :   if(fabs(det-1.)>0.0001) error("Plumed Error: Cannot diagonalize gyration tensor\n");
     280         400 :   switch(rg_type) {
     281         135 :   case GTPC_1:
     282             :   case GTPC_2:
     283             :   case GTPC_3:
     284             :   {
     285         135 :     int pc_index = rg_type-2; //index of principal component
     286         270 :     rgyr=sqrt(princ_comp[pc_index]/totmass);
     287         135 :     double rm = rgyr*totmass;
     288         270 :     if(rm>1e-6) prefactor[pc_index]=1.0/rm; //some parts of derivate
     289             :     break;
     290             :   }
     291             :   case GYRATION_3:        //the smallest principal radius of gyration
     292             :   {
     293           0 :     rgyr=sqrt((princ_comp[1]+princ_comp[2])/totmass);
     294           0 :     double rm = rgyr*totmass;
     295           0 :     if (rm>1e-6) {
     296           0 :       prefactor[1]=1.0/rm;
     297           0 :       prefactor[2]=1.0/rm;
     298             :     }
     299             :     break;
     300             :   }
     301             :   case GYRATION_2:       //the midle principal radius of gyration
     302             :   {
     303         130 :     rgyr=sqrt((princ_comp[0]+princ_comp[2])/totmass);
     304         130 :     double rm = rgyr*totmass;
     305         130 :     if (rm>1e-6) {
     306         130 :       prefactor[0]=1.0/rm;
     307         130 :       prefactor[2]=1.0/rm;
     308             :     }
     309             :     break;
     310             :   }
     311             :   case GYRATION_1:      //the largest principal radius of gyration
     312             :   {
     313           0 :     rgyr=sqrt((princ_comp[0]+princ_comp[1])/totmass);
     314           0 :     double rm = rgyr*totmass;
     315           0 :     if (rm>1e-6) {
     316           0 :       prefactor[0]=1.0/rm;
     317           0 :       prefactor[1]=1.0/rm;
     318             :     }
     319             :     break;
     320             :   }
     321             :   case ASPHERICITY:
     322             :   {
     323           5 :     rgyr=sqrt((princ_comp[0]-0.5*(princ_comp[1]+princ_comp[2]))/totmass);
     324           5 :     double rm = rgyr*totmass;
     325           5 :     if (rm>1e-6) {
     326           5 :       prefactor[0]= 1.0/rm;
     327           5 :       prefactor[1]=-0.5/rm;
     328           5 :       prefactor[2]=-0.5/rm;
     329             :     }
     330             :     break;
     331             :   }
     332             :   case ACYLINDRICITY:
     333             :   {
     334           0 :     rgyr=sqrt((princ_comp[1]-princ_comp[2])/totmass);
     335           0 :     double rm = rgyr*totmass;
     336           0 :     if (rm>1e-6) {  //avoid division by zero
     337           0 :       prefactor[1]= 1.0/rm;
     338           0 :       prefactor[2]=-1.0/rm;
     339             :     }
     340             :     break;
     341             :   }
     342             :   case KAPPA2: // relative shape anisotropy
     343             :   {
     344         130 :     double trace = princ_comp[0]+princ_comp[1]+princ_comp[2];
     345         130 :     double tmp=princ_comp[0]*princ_comp[1]+ princ_comp[1]*princ_comp[2]+ princ_comp[0]*princ_comp[2];
     346         130 :     rgyr=1.0-3*(tmp/(trace*trace));
     347         130 :     if (rgyr>1e-6) {
     348         260 :       prefactor[0]= -3*((princ_comp[1]+princ_comp[2])-2*tmp/trace)/(trace*trace) *2;
     349         260 :       prefactor[1]= -3*((princ_comp[0]+princ_comp[2])-2*tmp/trace)/(trace*trace) *2;
     350         130 :       prefactor[2]= -3*((princ_comp[0]+princ_comp[1])-2*tmp/trace)/(trace*trace) *2;
     351             :     }
     352             :     break;
     353             :   }
     354             :   }
     355             : 
     356         400 :   if(use_masses) {
     357           0 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     358           0 :       Vector tX;
     359           0 :       const Vector diff=delta( com,getPosition(i) );
     360             :       //project atomic postional vectors to diagonalized frame
     361           0 :       for(unsigned j=0; j<3; j++) tX[j]=transf[0][j]*diff[0]+transf[1][j]*diff[1]+transf[2][j]*diff[2];
     362           0 :       for(unsigned j=0; j<3; j++) derivatives[i][j]=getMass(i)*(prefactor[0]*transf[j][0]*tX[0]+
     363           0 :             prefactor[1]*transf[j][1]*tX[1]+
     364           0 :             prefactor[2]*transf[j][2]*tX[2]);
     365           0 :       setAtomsDerivatives(i,derivatives[i]);
     366             :     }
     367             :   } else {
     368        4400 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     369        2000 :       Vector tX;
     370        4000 :       const Vector diff=delta( com,getPosition(i) );
     371             :       //project atomic postional vectors to diagonalized frame
     372        8000 :       for(unsigned j=0; j<3; j++) tX[j]=transf[0][j]*diff[0]+transf[1][j]*diff[1]+transf[2][j]*diff[2];
     373       44000 :       for(unsigned j=0; j<3; j++) derivatives[i][j]=prefactor[0]*transf[j][0]*tX[0]+
     374       18000 :             prefactor[1]*transf[j][1]*tX[1]+
     375       12000 :             prefactor[2]*transf[j][2]*tX[2];
     376        2000 :       setAtomsDerivatives(i,derivatives[i]);
     377             :     }
     378             :   }
     379             : 
     380         400 :   setValue(rgyr);
     381         400 :   setBoxDerivativesNoPbc();
     382             : }
     383             : 
     384             : }
     385        4839 : }

Generated by: LCOV version 1.13