LCOV - code coverage report
Current view: top level - secondarystructure - SecondaryStructureRMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 181 191 94.8 %
Date: 2024-10-18 13:59:31 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-2020 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 "SecondaryStructureRMSD.h"
      23             : #include "core/PlumedMain.h"
      24             : #include "core/ActionSet.h"
      25             : #include "core/GenericMolInfo.h"
      26             : #include "core/ActionShortcut.h"
      27             : #include "core/ActionRegister.h"
      28             : 
      29             : //+PLUMEDOC MCOLVAR SECONDARY_STRUCTURE_RMSD
      30             : /*
      31             : Calclulate the distance between segments of a protein and a reference structure of interest
      32             : 
      33             : \par Examples
      34             : 
      35             : */
      36             : //+ENDPLUMEDOC
      37             : 
      38             : namespace PLMD {
      39             : namespace secondarystructure {
      40             : 
      41             : PLUMED_REGISTER_ACTION(SecondaryStructureRMSD,"SECONDARY_STRUCTURE_RMSD");
      42             : 
      43          32 : bool SecondaryStructureRMSD::readShortcutWords( std::string& ltmap, ActionShortcut* action ) {
      44          64 :   action->parse("LESS_THAN",ltmap);
      45          32 :   if( ltmap.length()==0 ) {
      46           6 :     std::string nn, mm, d_0, r_0; action->parse("R_0",r_0);
      47           3 :     if( r_0.length()==0 ) r_0="0.08";
      48           9 :     action->parse("NN",nn); action->parse("D_0",d_0); action->parse("MM",mm);
      49           6 :     ltmap = "RATIONAL R_0=" + r_0 + " D_0=" + d_0 + " NN=" + nn + " MM=" + mm;
      50             :     return false;
      51             :   }
      52             :   return true;
      53             : }
      54             : 
      55          32 : void SecondaryStructureRMSD::expandShortcut( const bool& uselessthan, const std::string& labout, const std::string& labin, const std::string& ltmap, ActionShortcut* action ) {
      56          64 :   action->readInputLine( labout + "_lt: LESS_THAN ARG=" + labin + " SWITCH={" + ltmap  +"}");
      57          61 :   if( uselessthan ) action->readInputLine( labout + "_lessthan: SUM ARG=" + labout + "_lt PERIODIC=NO");
      58           6 :   else action->readInputLine( labout + ": SUM ARG=" + labout + "_lt PERIODIC=NO");
      59          32 : }
      60             : 
      61         242 : void SecondaryStructureRMSD::registerKeywords( Keywords& keys ) {
      62         242 :   ActionWithVector::registerKeywords( keys );
      63         484 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions");
      64         484 :   keys.add("residues","RESIDUES","this command is used to specify the set of residues that could conceivably form part of the secondary structure. "
      65             :            "It is possible to use residues numbers as the various chains and residues should have been identified else using an instance of the "
      66             :            "\\ref MOLINFO action. If you wish to use all the residues from all the chains in your system you can do so by "
      67             :            "specifying all. Alternatively, if you wish to use a subset of the residues you can specify the particular residues "
      68             :            "you are interested in as a list of numbers. Please be aware that to form secondary structure elements your chain "
      69             :            "must contain at least N residues, where N is dependent on the particular secondary structure you are interested in. "
      70             :            "As such if you define portions of the chain with fewer than N residues the code will crash.");
      71         484 :   keys.add("atoms","ATOMS","this is the full list of atoms that we are investigating");
      72         484 :   keys.add("numbered","SEGMENT","this is the lists of atoms in the segment that are being considered");
      73         484 :   keys.add("compulsory","BONDLENGTH","the length to use for bonds");
      74         484 :   keys.add("numbered","STRUCTURE","the reference structure");
      75         484 :   keys.add("compulsory","TYPE","DRMSD","the manner in which RMSD alignment is performed. Should be OPTIMAL, SIMPLE or DRMSD. "
      76             :            "For more details on the OPTIMAL and SIMPLE methods see \\ref RMSD. For more details on the "
      77             :            "DRMSD method see \\ref DRMSD.");
      78         484 :   keys.add("optional","STRANDS_CUTOFF","If in a segment of protein the two strands are further apart then the calculation "
      79             :            "of the actual RMSD is skipped as the structure is very far from being beta-sheet like. "
      80             :            "This keyword speeds up the calculation enormously when you are using the LESS_THAN option. "
      81             :            "However, if you are using some other option, then this cannot be used");
      82         484 :   keys.add("optional","CUTOFF_ATOMS","the pair of atoms that are used to calculate the strand cutoff");
      83         484 :   keys.addFlag("VERBOSE",false,"write a more detailed output");
      84         484 :   keys.add("optional","LESS_THAN","calculate the number of a residue segments that are within a certain target distance of this secondary structure type. "
      85             :            "This quantity is calculated using \\f$\\sum_i \\sigma(s_i)\\f$, where \\f$\\sigma(s)\\f$ is a \\ref switchingfunction.");
      86         484 :   keys.add("optional","R_0","The r_0 parameter of the switching function.");
      87         484 :   keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
      88         484 :   keys.add("compulsory","NN","8","The n parameter of the switching function");
      89         484 :   keys.add("compulsory","MM","12","The m parameter of the switching function");
      90         484 :   keys.add("hidden","NO_ACTION_LOG","suppresses printing from action on the log");
      91         484 :   keys.setValueDescription("vector","a vector containing the rmsd distance between each of the residue segments and the reference structure");
      92         484 :   keys.addOutputComponent("struct","default","vector","the vectors containing the rmsd distances between the residues and each of the reference structures");
      93         484 :   keys.addOutputComponent("lessthan","default","scalar","the number blocks of residues that have an RMSD from the secondary structure that is less than the threshold");
      94         726 :   keys.needsAction("SECONDARY_STRUCTURE_RMSD"); keys.needsAction("LESS_THAN"); keys.needsAction("SUM");
      95         242 : }
      96             : 
      97          32 : void SecondaryStructureRMSD::readBackboneAtoms( ActionShortcut* action, PlumedMain& plumed, const std::string& moltype, std::vector<unsigned>& chain_lengths, std::string& all_atoms ) {
      98          32 :   auto* moldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(action);
      99          32 :   if( ! moldat ) action->error("Unable to find MOLINFO in input");
     100             : 
     101          64 :   std::vector<std::string> resstrings; action->parseVector( "RESIDUES", resstrings );
     102          32 :   if(resstrings.size()==0) action->error("residues are not defined, check the keyword RESIDUES");
     103          64 :   else if( Tools::caseInSensStringCompare(resstrings[0], "all") ) {
     104             :     resstrings[0]="all";
     105          30 :     action->log.printf("  examining all possible secondary structure combinations\n");
     106             :   } else {
     107           2 :     action->log.printf("  examining secondary structure in residue positions : %s ",resstrings[0].c_str() );
     108           3 :     for(unsigned i=1; i<resstrings.size(); ++i) action->log.printf(", %s",resstrings[i].c_str() );
     109           2 :     action->log.printf("\n");
     110             :   }
     111             :   std::vector< std::vector<AtomNumber> > backatoms;
     112          32 :   moldat->getBackbone( resstrings, moltype, backatoms );
     113             : 
     114          32 :   chain_lengths.resize( backatoms.size() );
     115         473 :   for(unsigned i=0; i<backatoms.size(); ++i) {
     116         441 :     chain_lengths[i]=backatoms[i].size();
     117       18141 :     for(unsigned j=0; j<backatoms[i].size(); ++j) {
     118       17700 :       std::string bat_str; Tools::convert( backatoms[i][j].serial(), bat_str );
     119       17732 :       if( i==0 && j==0 ) all_atoms = "ATOMS=" + bat_str;
     120       35336 :       else all_atoms += "," + bat_str;
     121             :     }
     122             :   }
     123          32 : }
     124             : 
     125             : 
     126          32 : SecondaryStructureRMSD::SecondaryStructureRMSD(const ActionOptions&ao):
     127             :   Action(ao),
     128             :   ActionWithVector(ao),
     129          32 :   nopbc(false),
     130          32 :   align_strands(false),
     131          32 :   s_cutoff2(0),
     132          32 :   align_atom_1(0),
     133          32 :   align_atom_2(0)
     134             : {
     135          32 :   if( plumed.usingNaturalUnits() ) error("cannot use this collective variable when using natural units");
     136             : 
     137          64 :   parse("TYPE",alignType); parseFlag("NOPBC",nopbc);
     138          32 :   log.printf("  distances from secondary structure elements are calculated using %s algorithm\n",alignType.c_str() );
     139          64 :   log<<"  Bibliography "<<plumed.cite("Pietrucci and Laio, J. Chem. Theory Comput. 5, 2197 (2009)"); log<<"\n";
     140             : 
     141          32 :   parseFlag("VERBOSE",verbose_output);
     142             : 
     143          64 :   if( keywords.exists("STRANDS_CUTOFF") ) {
     144          32 :     double s_cutoff = 0;
     145          32 :     parse("STRANDS_CUTOFF",s_cutoff); align_strands=true;
     146          32 :     if( s_cutoff>0) {
     147          26 :       log.printf("  ignoring contributions from strands that are more than %f apart\n",s_cutoff);
     148          52 :       std::vector<unsigned> cutatoms; parseVector("CUTOFF_ATOMS",cutatoms);
     149          26 :       if( cutatoms.size()==2 ) {
     150          26 :         align_atom_1=cutatoms[0]; align_atom_2=cutatoms[1];
     151           0 :       } else error("did not find CUTOFF_ATOMS in input");
     152             :     }
     153          32 :     s_cutoff2=s_cutoff*s_cutoff;
     154             :   }
     155             : 
     156             :   // Read in the atoms
     157          64 :   std::vector<AtomNumber> all_atoms; parseAtomList("ATOMS",all_atoms); requestAtoms( all_atoms );
     158             : 
     159      132469 :   for(unsigned i=1;; ++i) {
     160             :     std::vector<unsigned> newatoms;
     161      265002 :     if( !parseNumberedVector("SEGMENT",i,newatoms) ) break;
     162      132469 :     if( verbose_output ) {
     163           0 :       log.printf("  Secondary structure segment %u contains atoms : ", static_cast<unsigned>(colvar_atoms.size()+1));
     164           0 :       for(unsigned i=0; i<newatoms.size(); ++i) log.printf("%d ",all_atoms[newatoms[i]].serial() );
     165           0 :       log.printf("\n");
     166             :     }
     167      132469 :     colvar_atoms.push_back( newatoms );
     168      132469 :   }
     169          32 :   if( colvar_atoms.size()==0 ) error("missing SEGMENT keywords -- no atoms have been specified for comparison");
     170             : 
     171          64 :   double bondlength; parse("BONDLENGTH",bondlength); bondlength=bondlength/getUnits().getLength();
     172             : 
     173             :   // Read in the reference structure
     174          47 :   for(unsigned ii=1;; ++ii) {
     175             :     std::vector<double> cstruct;
     176         158 :     if( !parseNumberedVector("STRUCTURE",ii,cstruct) ) break ;
     177          47 :     plumed_assert( cstruct.size()%3==0 && cstruct.size()/3==colvar_atoms[0].size() );
     178          47 :     std::vector<Vector> structure( cstruct.size()/3 );
     179        1457 :     for(unsigned i=0; i<structure.size(); ++i) {
     180        5640 :       for(unsigned j=0; j<3; ++j) structure[i][j] = 0.1*cstruct[3*i+j]/getUnits().getLength();
     181             :     }
     182          47 :     if( alignType=="DRMSD" ) {
     183             :       std::map<std::pair<unsigned,unsigned>, double> targets;
     184         660 :       for(unsigned i=0; i<structure.size()-1; ++i) {
     185       10208 :         for(unsigned j=i+1; j<structure.size(); ++j) {
     186        9570 :           double distance = delta( structure[i], structure[j] ).modulo();
     187        9570 :           if(distance > bondlength) targets[std::make_pair(i,j)] = distance;
     188             :         }
     189             :       }
     190          22 :       drmsd_targets.push_back( targets );
     191             :     } else {
     192          25 :       Vector center; std::vector<double> align( structure.size(), 1.0 ), displace( structure.size(), 1.0 );
     193         775 :       for(unsigned i=0; i<structure.size(); ++i) center+=structure[i]*align[i];
     194         775 :       for(unsigned i=0; i<structure.size(); ++i) structure[i] -= center;
     195          25 :       RMSD newrmsd; newrmsd.clear();
     196          25 :       newrmsd.set(align,displace,structure,alignType,true,true);
     197          25 :       myrmsd.push_back( newrmsd );
     198          25 :     }
     199          47 :   }
     200             : 
     201             :   // And create values to hold everything
     202          32 :   unsigned nref = myrmsd.size(); if( alignType=="DRMSD" ) nref=drmsd_targets.size();
     203          32 :   plumed_assert( nref>0 );
     204          32 :   std::vector<unsigned> shape(1); shape[0]=colvar_atoms.size();
     205          32 :   if( nref==1 ) { addValue( shape ); setNotPeriodic(); }
     206             :   else {
     207             :     std::string num;
     208          45 :     for(unsigned i=0; i<nref; ++i) {
     209          30 :       Tools::convert( i+1, num ); addComponent( "struct-" + num, shape );
     210          60 :       componentIsNotPeriodic( "struct-" + num );
     211             :     }
     212             :   }
     213          32 : }
     214             : 
     215          79 : void SecondaryStructureRMSD::areAllTasksRequired( std::vector<ActionWithVector*>& task_reducing_actions ) {
     216          79 :   if( s_cutoff2>0 ) task_reducing_actions.push_back(this);
     217          79 : }
     218             : 
     219      596136 : int SecondaryStructureRMSD::checkTaskStatus( const unsigned& taskno, int& flag ) const {
     220      596136 :   if( s_cutoff2>0 ) {
     221      596136 :     Vector distance=pbcDistance( ActionAtomistic::getPosition( getAtomIndex(taskno,align_atom_1) ),
     222             :                                  ActionAtomistic::getPosition( getAtomIndex(taskno,align_atom_2) ) );
     223      596136 :     if( distance.modulo2()<s_cutoff2 ) return 1;
     224      541322 :     return 0;
     225           0 :   } return flag;
     226             : }
     227             : 
     228         240 : void SecondaryStructureRMSD::calculate() {
     229         240 :   runAllTasks();
     230         240 : }
     231             : 
     232       55246 : void SecondaryStructureRMSD::performTask( const unsigned& current, MultiValue& myvals ) const {
     233             :   // Resize the derivatives if need be
     234       55246 :   unsigned nderi = 3*getNumberOfAtoms()+9;
     235       55246 :   if( myvals.getNumberOfDerivatives()!=nderi ) myvals.resize( myvals.getNumberOfValues(), nderi, 0, 0 );
     236             :   // Retrieve the positions
     237       55246 :   const unsigned natoms = colvar_atoms[current].size();
     238       55246 :   std::vector<Vector> pos( natoms ), deriv( natoms );
     239     1712626 :   for(unsigned i=0; i<natoms; ++i) pos[i]=ActionAtomistic::getPosition( getAtomIndex(current,i) );
     240             : 
     241             :   // This aligns the two strands if this is required
     242       55246 :   Vector distance=pbcDistance( pos[align_atom_1],pos[align_atom_2] );
     243       55246 :   if( align_strands ) {
     244       55246 :     Vector origin_old, origin_new; origin_old=pos[align_atom_2];
     245       55246 :     origin_new=pos[align_atom_1]+distance;
     246      883936 :     for(unsigned i=15; i<30; ++i) {
     247      828690 :       pos[i]+=( origin_new - origin_old );
     248             :     }
     249           0 :   } else if( !nopbc ) {
     250           0 :     for(unsigned i=0; i<natoms-1; ++i) {
     251           0 :       const Vector & first (pos[i]);
     252           0 :       Vector & second (pos[i+1]);
     253           0 :       second=first+pbcDistance(first,second);
     254             :     }
     255             :   }
     256             :   // Create a holder for the derivatives
     257       55246 :   if( alignType=="DRMSD" ) {
     258             :     // And now calculate the DRMSD
     259       18826 :     const unsigned rs = drmsd_targets.size();
     260       47061 :     for(unsigned i=0; i<rs; ++i) {
     261       28235 :       double drmsd=0; Vector distance; Tensor vir; vir.zero();
     262      875285 :       for(unsigned j=0; j<natoms; ++j) deriv[j].zero();
     263    11519700 :       for(const auto & it : drmsd_targets[i] ) {
     264    11491465 :         const unsigned k=it.first.first;
     265    11491465 :         const unsigned j=it.first.second;
     266             : 
     267    11491465 :         distance=delta( pos[k], pos[j] );
     268    11491465 :         const double len = distance.modulo();
     269    11491465 :         const double diff = len - it.second;
     270    11491465 :         const double der = diff / len;
     271    11491465 :         drmsd += diff*diff;
     272             : 
     273    11491465 :         if( !doNotCalculateDerivatives() ) {
     274    11262001 :           deriv[k] += -der*distance; deriv[j] += der*distance;
     275    11262001 :           vir += -der*Tensor(distance,distance);
     276             :         }
     277             :       }
     278             : 
     279       28235 :       const double inpairs = 1./static_cast<double>(drmsd_targets[i].size());
     280       28235 :       unsigned ostrn = getConstPntrToComponent(i)->getPositionInStream();
     281       28235 :       drmsd = sqrt(inpairs*drmsd); myvals.setValue( ostrn, drmsd );
     282             : 
     283       28235 :       if( !doNotCalculateDerivatives() ) {
     284       27671 :         double scalef = inpairs / drmsd;
     285      857801 :         for(unsigned j=0; j<natoms; ++j) {
     286             :           const unsigned ja = getAtomIndex( current, j );
     287      830130 :           myvals.addDerivative( ostrn, 3*ja + 0, scalef*deriv[j][0] ); myvals.updateIndex( ostrn, 3*ja+0 );
     288      830130 :           myvals.addDerivative( ostrn, 3*ja + 1, scalef*deriv[j][1] ); myvals.updateIndex( ostrn, 3*ja+1 );
     289      830130 :           myvals.addDerivative( ostrn, 3*ja + 2, scalef*deriv[j][2] ); myvals.updateIndex( ostrn, 3*ja+2 );
     290             :         }
     291       27671 :         unsigned nbase = myvals.getNumberOfDerivatives() - 9;
     292      110684 :         for(unsigned k=0; k<3; ++k) {
     293      332052 :           for(unsigned j=0; j<3; ++j) {
     294      249039 :             myvals.addDerivative( ostrn, nbase + 3*k + j, scalef*vir(k,j) );
     295      249039 :             myvals.updateIndex( ostrn, nbase + 3*k + j );
     296             :           }
     297             :         }
     298             :       }
     299             :     }
     300             :   } else {
     301       36420 :     const unsigned rs = myrmsd.size();
     302       91020 :     for(unsigned i=0; i<rs; ++i) {
     303       54600 :       double nr = myrmsd[i].calculate( pos, deriv, false );
     304       54600 :       unsigned ostrn = getConstPntrToComponent(i)->getPositionInStream();
     305       54600 :       myvals.setValue( ostrn, nr );
     306             : 
     307       54600 :       if( !doNotCalculateDerivatives() ) {
     308       54600 :         Tensor vir; vir.zero();
     309     1692600 :         for(unsigned j=0; j<natoms; ++j) {
     310             :           const unsigned ja = getAtomIndex( current, j );
     311     1638000 :           myvals.addDerivative( ostrn, 3*ja + 0, deriv[j][0] ); myvals.updateIndex( ostrn, 3*colvar_atoms[current][j]+0 );
     312     1638000 :           myvals.addDerivative( ostrn, 3*ja + 1, deriv[j][1] ); myvals.updateIndex( ostrn, 3*colvar_atoms[current][j]+1 );
     313     1638000 :           myvals.addDerivative( ostrn, 3*ja + 2, deriv[j][2] ); myvals.updateIndex( ostrn, 3*colvar_atoms[current][j]+2 );
     314     1638000 :           vir+=(-1.0*Tensor( pos[j], deriv[j] ));
     315             :         }
     316       54600 :         unsigned nbase = myvals.getNumberOfDerivatives() - 9;
     317      218400 :         for(unsigned k=0; k<3; ++k) {
     318      655200 :           for(unsigned j=0; j<3; ++j) {
     319      491400 :             myvals.addDerivative( ostrn, nbase + 3*k + j, vir(k,j) );
     320      491400 :             myvals.updateIndex( ostrn, nbase + 3*k + j );
     321             :           }
     322             :         }
     323             :       }
     324             :     }
     325             :   }
     326       55246 :   return;
     327             : }
     328             : 
     329             : }
     330             : }

Generated by: LCOV version 1.16