LCOV - code coverage report
Current view: top level - secondarystructure - SecondaryStructureRMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 159 171 93.0 %
Date: 2026-03-30 13:16:06 Functions: 11 14 78.6 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-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 "SecondaryStructureRMSD.h"
      23             : #include "core/PlumedMain.h"
      24             : #include "core/ActionSet.h"
      25             : #include "core/GenericMolInfo.h"
      26             : #include "core/Atoms.h"
      27             : #include "vesselbase/Vessel.h"
      28             : #include "reference/MetricRegister.h"
      29             : #include "reference/SingleDomainRMSD.h"
      30             : 
      31             : namespace PLMD {
      32             : namespace secondarystructure {
      33             : 
      34          55 : void SecondaryStructureRMSD::registerKeywords( Keywords& keys ) {
      35          55 :   Action::registerKeywords( keys );
      36          55 :   ActionWithValue::registerKeywords( keys );
      37          55 :   ActionAtomistic::registerKeywords( keys );
      38         110 :   keys.add("residues","RESIDUES","this command is used to specify the set of residues that could conceivably form part of the secondary structure. "
      39             :            "It is possible to use residues numbers as the various chains and residues should have been identified else using an instance of the "
      40             :            "\\ref MOLINFO action. If you wish to use all the residues from all the chains in your system you can do so by "
      41             :            "specifying all. Alternatively, if you wish to use a subset of the residues you can specify the particular residues "
      42             :            "you are interested in as a list of numbers. Please be aware that to form secondary structure elements your chain "
      43             :            "must contain at least N residues, where N is dependent on the particular secondary structure you are interested in. "
      44             :            "As such if you define portions of the chain with fewer than N residues the code will crash.");
      45         110 :   keys.add("compulsory","TYPE","DRMSD","the manner in which RMSD alignment is performed. Should be OPTIMAL, SIMPLE or DRMSD. "
      46             :            "For more details on the OPTIMAL and SIMPLE methods see \\ref RMSD. For more details on the "
      47             :            "DRMSD method see \\ref DRMSD.");
      48         110 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions");
      49         110 :   keys.add("compulsory","R_0","0.08","The r_0 parameter of the switching function.");
      50         110 :   keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
      51         110 :   keys.add("compulsory","NN","8","The n parameter of the switching function");
      52         110 :   keys.add("compulsory","MM","12","The m parameter of the switching function");
      53         110 :   keys.reserve("optional","STRANDS_CUTOFF","If in a segment of protein the two strands are further apart then the calculation "
      54             :                "of the actual RMSD is skipped as the structure is very far from being beta-sheet like. "
      55             :                "This keyword speeds up the calculation enormously when you are using the LESS_THAN option. "
      56             :                "However, if you are using some other option, then this cannot be used");
      57         110 :   keys.addFlag("VERBOSE",false,"write a more detailed output");
      58         110 :   keys.add("hidden","NL_STRIDE","the frequency with which the neighbor list should be updated. Between neighbour list update steps all quantities "
      59             :            "that contributed less than TOL at the previous neighbor list update step are ignored.");
      60          55 :   ActionWithVessel::registerKeywords( keys );
      61          55 :   keys.use("LESS_THAN");
      62          55 :   keys.use("MIN");
      63          55 :   keys.use("ALT_MIN");
      64          55 :   keys.use("LOWEST");
      65          55 :   keys.use("HIGHEST");
      66          55 :   keys.setComponentsIntroduction("By default this Action calculates the number of structural units that are within a certain "
      67             :                                  "distance of a idealized secondary structure element. This quantity can then be referenced "
      68             :                                  "elsewhere in the input by using the label of the action. However, this Action can also be used to "
      69             :                                  "calculate the following quantities by using the keywords as described below.  The quantities then "
      70             :                                  "calculated can be referenced using the label of the action followed by a dot and then the name "
      71             :                                  "from the table below.  Please note that you can use the LESS_THAN keyword more than once.  The resulting "
      72             :                                  "components will be labelled <em>label</em>.lessthan-1, <em>label</em>.lessthan-2 and so on unless you "
      73             :                                  "exploit the fact that these labels can be given custom labels by using the LABEL keyword in the "
      74             :                                  "description of you LESS_THAN function that you are computing");
      75          55 : }
      76             : 
      77          43 : SecondaryStructureRMSD::SecondaryStructureRMSD(const ActionOptions&ao):
      78             :   Action(ao),
      79             :   ActionAtomistic(ao),
      80             :   ActionWithValue(ao),
      81             :   ActionWithVessel(ao),
      82          43 :   nopbc(false),
      83          43 :   align_strands(false),
      84          43 :   s_cutoff2(0),
      85          43 :   align_atom_1(0),
      86          43 :   align_atom_2(0) {
      87          43 :   parse("TYPE",alignType);
      88          43 :   parseFlag("NOPBC",nopbc);
      89          43 :   log.printf("  distances from secondary structure elements are calculated using %s algorithm\n",alignType.c_str() );
      90          86 :   log<<"  Bibliography "<<plumed.cite("Pietrucci and Laio, J. Chem. Theory Comput. 5, 2197 (2009)");
      91          43 :   log<<"\n";
      92             : 
      93          43 :   parseFlag("VERBOSE",verbose_output);
      94             : 
      95          86 :   if( keywords.exists("STRANDS_CUTOFF") ) {
      96          40 :     double s_cutoff = 0;
      97          40 :     parse("STRANDS_CUTOFF",s_cutoff);
      98          40 :     align_strands=true;
      99          40 :     if( s_cutoff>0) {
     100          38 :       log.printf("  ignoring contributions from strands that are more than %f apart\n",s_cutoff);
     101             :     }
     102          40 :     s_cutoff2=s_cutoff*s_cutoff;
     103             :   }
     104          43 : }
     105             : 
     106          43 : SecondaryStructureRMSD::~SecondaryStructureRMSD() {
     107             : // destructor needed to delete forward declarated objects
     108          86 : }
     109             : 
     110         110 : void SecondaryStructureRMSD::turnOnDerivatives() {
     111         110 :   ActionWithValue::turnOnDerivatives();
     112         110 :   needsDerivatives();
     113         110 : }
     114             : 
     115          40 : void SecondaryStructureRMSD::setAtomsFromStrands( const unsigned& atom1, const unsigned& atom2 ) {
     116          40 :   align_atom_1=atom1;
     117          40 :   align_atom_2=atom2;
     118          40 : }
     119             : 
     120          43 : void SecondaryStructureRMSD::readBackboneAtoms( const std::string& moltype, std::vector<unsigned>& chain_lengths ) {
     121          43 :   auto* moldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(this);
     122          43 :   if( ! moldat ) {
     123           0 :     error("Unable to find MOLINFO in input");
     124             :   }
     125             : 
     126             :   std::vector<std::string> resstrings;
     127          43 :   parseVector( "RESIDUES", resstrings );
     128          43 :   if( !verbose_output ) {
     129          43 :     if(resstrings.size()==0) {
     130           0 :       error("residues are not defined, check the keyword RESIDUES");
     131          43 :     } else if(resstrings[0]=="all") {
     132          39 :       log.printf("  examining all possible secondary structure combinations\n");
     133             :     } else {
     134           4 :       log.printf("  examining secondary structure in residue positions : %s \n",resstrings[0].c_str() );
     135           6 :       for(unsigned i=1; i<resstrings.size(); ++i) {
     136           2 :         log.printf(", %s",resstrings[i].c_str() );
     137             :       }
     138           4 :       log.printf("\n");
     139             :     }
     140             :   }
     141             :   std::vector< std::vector<AtomNumber> > backatoms;
     142          43 :   moldat->getBackbone( resstrings, moltype, backatoms );
     143             : 
     144          43 :   chain_lengths.resize( backatoms.size() );
     145         700 :   for(unsigned i=0; i<backatoms.size(); ++i) {
     146         657 :     chain_lengths[i]=backatoms[i].size();
     147       26877 :     for(unsigned j=0; j<backatoms[i].size(); ++j) {
     148       26220 :       all_atoms.push_back( backatoms[i][j] );
     149             :     }
     150             :   }
     151          43 :   ActionAtomistic::requestAtoms( all_atoms );
     152          43 :   forcesToApply.resize( getNumberOfDerivatives() );
     153          43 : }
     154             : 
     155      187632 : void SecondaryStructureRMSD::addColvar( const std::vector<unsigned>& newatoms ) {
     156      187632 :   if( colvar_atoms.size()>0 ) {
     157      187590 :     plumed_assert( colvar_atoms[0].size()==newatoms.size() );
     158             :   }
     159      187632 :   if( verbose_output ) {
     160           0 :     log.printf("  Secondary structure segment %u contains atoms : ", static_cast<unsigned>(colvar_atoms.size()+1));
     161           0 :     for(unsigned i=0; i<newatoms.size(); ++i) {
     162           0 :       log.printf("%d ",all_atoms[newatoms[i]].serial() );
     163             :     }
     164           0 :     log.printf("\n");
     165             :   }
     166      187632 :   addTaskToList( colvar_atoms.size() );
     167      187632 :   colvar_atoms.push_back( newatoms );
     168      187632 : }
     169             : 
     170          62 : void SecondaryStructureRMSD::setSecondaryStructure( std::vector<Vector>& structure, double bondlength, double units ) {
     171             :   // If we are in natural units get conversion factor from nm into natural length units
     172          62 :   if( plumed.getAtoms().usingNaturalUnits() ) {
     173           0 :     error("cannot use this collective variable when using natural units");
     174             :   }
     175          62 :   plumed_massert( !(align_strands && align_atom_1==0 && align_atom_2==0), "you must use setAtomsFromStrands with strands cutoff");
     176             : 
     177             :   // Convert into correct units
     178        1922 :   for(unsigned i=0; i<structure.size(); ++i) {
     179        1860 :     structure[i][0]*=units;
     180        1860 :     structure[i][1]*=units;
     181        1860 :     structure[i][2]*=units;
     182             :   }
     183             : 
     184          62 :   if( references.size()==0 ) {
     185          43 :     readVesselKeywords();
     186          43 :     if( getNumberOfVessels()==0 ) {
     187             :       double r0;
     188           2 :       parse("R_0",r0);
     189             :       double d0;
     190           2 :       parse("D_0",d0);
     191             :       int nn;
     192           2 :       parse("NN",nn);
     193             :       int mm;
     194           2 :       parse("MM",mm);
     195           2 :       std::ostringstream ostr;
     196           2 :       ostr<<"RATIONAL R_0="<<r0<<" D_0="<<d0<<" NN="<<nn<<" MM="<<mm;
     197             :       std::string input=ostr.str();
     198           2 :       addVessel( "LESS_THAN", input, -1 ); // -1 here means that this value will be named getLabel()
     199           2 :       readVesselKeywords();  // This makes sure resizing is done
     200           2 :     }
     201             :   }
     202             : 
     203             :   // Set the reference structure
     204          62 :   references.emplace_back( metricRegister().create<SingleDomainRMSD>( alignType ) );
     205          62 :   unsigned nn=references.size()-1;
     206          62 :   std::vector<double> align( structure.size(), 1.0 ), displace( structure.size(), 1.0 );
     207          62 :   references[nn]->setBoundsOnDistances( true, bondlength );   // We always use pbc
     208          62 :   references[nn]->setReferenceAtoms( structure, align, displace );
     209             : //  references[nn]->setNumberOfAtoms( structure.size() );
     210             : 
     211             :   // And prepare the task list
     212          62 :   deactivateAllTasks();
     213      281340 :   for(unsigned i=0; i<getFullNumberOfTasks(); ++i) {
     214      281278 :     taskFlags[i]=1;
     215             :   }
     216          62 :   lockContributors();
     217          62 : }
     218             : 
     219        2676 : void SecondaryStructureRMSD::calculate() {
     220        2676 :   runAllTasks();
     221        2676 : }
     222             : 
     223      929772 : void SecondaryStructureRMSD::performTask( const unsigned& task_index, const unsigned& current, MultiValue& myvals ) const {
     224             :   // Retrieve the positions
     225      929772 :   std::vector<Vector> pos( references[0]->getNumberOfAtoms() );
     226      929772 :   const unsigned n=pos.size();
     227    28822932 :   for(unsigned i=0; i<n; ++i) {
     228    27893160 :     pos[i]=ActionAtomistic::getPosition( getAtomIndex(current,i) );
     229             :   }
     230             : 
     231             :   // This does strands cutoff
     232      929772 :   Vector distance;
     233      929772 :   if( nopbc ) {
     234           0 :     distance=delta( pos[align_atom_1],pos[align_atom_2] );
     235             :   } else {
     236      929772 :     distance=pbcDistance( pos[align_atom_1],pos[align_atom_2] );
     237             :   }
     238      929772 :   if( s_cutoff2>0 ) {
     239      927264 :     if( distance.modulo2()>s_cutoff2 ) {
     240             :       myvals.setValue( 0, 0.0 );
     241      842102 :       return;
     242             :     }
     243             :   }
     244             : 
     245             :   // This aligns the two strands if this is required
     246       87670 :   if( alignType!="DRMSD" && align_strands && !nopbc ) {
     247      928380 :     for(unsigned i=0; i<14; ++i) {
     248      866488 :       const Vector & first (pos[i]);
     249      866488 :       Vector & second (pos[i+1]);
     250      866488 :       second=first+pbcDistance(first,second);
     251             :     }
     252      866488 :     for(unsigned i=16; i<n-1; ++i) {
     253      804596 :       const Vector & first (pos[i]);
     254      804596 :       Vector & second (pos[i+1]);
     255      804596 :       second=first+pbcDistance(first,second);
     256             :     }
     257       61892 :     Vector origin_old, origin_new;
     258       61892 :     origin_old=pos[align_atom_2];
     259       61892 :     origin_new=pos[align_atom_1]+distance;
     260      990272 :     for(unsigned i=15; i<30; ++i) {
     261      928380 :       pos[i]+=( origin_new - origin_old );
     262             :     }
     263       25778 :   } else if( alignType!="DRMSD" && !nopbc ) {
     264           0 :     for(unsigned i=0; i<n-1; ++i) {
     265           0 :       const Vector & first (pos[i]);
     266           0 :       Vector & second (pos[i+1]);
     267           0 :       second=first+pbcDistance(first,second);
     268             :     }
     269             :   }
     270             :   // Create a holder for the derivatives
     271       87670 :   ReferenceValuePack mypack( 0, pos.size(), myvals );
     272             :   mypack.setValIndex( 1 );
     273     2717770 :   for(unsigned i=0; i<n; ++i) {
     274             :     mypack.setAtomIndex( i, getAtomIndex(current,i) );
     275             :   }
     276             : 
     277             :   // And now calculate the RMSD
     278             :   const Pbc& pbc=getPbc();
     279             :   unsigned closest=0;
     280       87670 :   double r = references[0]->calculate( pos, pbc, mypack, false );
     281       87670 :   const unsigned rs = references.size();
     282      130169 :   for(unsigned i=1; i<rs; ++i) {
     283       42499 :     mypack.setValIndex( i+1 );
     284       42499 :     double nr=references[i]->calculate( pos, pbc, mypack, false );
     285       42499 :     if( nr<r ) {
     286             :       closest=i;
     287             :       r=nr;
     288             :     }
     289             :   }
     290             : 
     291             :   // Transfer everything to the value
     292             :   myvals.setValue( 0, 1.0 );
     293             :   myvals.setValue( 1, r );
     294       87670 :   if( closest>0 ) {
     295       19914 :     mypack.moveDerivatives( closest+1, 1 );
     296             :   }
     297             : 
     298       87670 :   if( !mypack.virialWasSet() ) {
     299       61892 :     Tensor vir;
     300       61892 :     const unsigned cacs = colvar_atoms[current].size();
     301     1918652 :     for(unsigned i=0; i<cacs; ++i) {
     302     1856760 :       vir+=(-1.0*Tensor( pos[i], mypack.getAtomDerivative(i) ));
     303             :     }
     304             :     mypack.setValIndex(1);
     305       61892 :     mypack.addBoxDerivatives( vir );
     306             :   }
     307             : 
     308             :   return;
     309       87670 : }
     310             : 
     311         300 : void SecondaryStructureRMSD::apply() {
     312         300 :   if( getForcesFromVessels( forcesToApply ) ) {
     313         276 :     setForcesOnAtoms( forcesToApply );
     314             :   }
     315         300 : }
     316             : 
     317             : }
     318             : }

Generated by: LCOV version 1.16