LCOV - code coverage report
Current view: top level - colvar - Gyration.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 187 232 80.6 %
Date: 2025-11-25 13:55:50 Functions: 3 4 75.0 %

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

Generated by: LCOV version 1.16