LCOV - code coverage report
Current view: top level - secondarystructure - SecondaryStructureBase.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 172 181 95.0 %
Date: 2025-12-04 11:19:34 Functions: 21 21 100.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             : #ifndef __PLUMED_secondarystructure_SecondaryStructureBase_h
      23             : #define __PLUMED_secondarystructure_SecondaryStructureBase_h
      24             : 
      25             : #include "core/ActionWithVector.h"
      26             : #include "core/ActionShortcut.h"
      27             : #include "core/PlumedMain.h"
      28             : #include "core/ActionSet.h"
      29             : #include "core/GenericMolInfo.h"
      30             : #include "core/ParallelTaskManager.h"
      31             : #include "tools/ColvarOutput.h"
      32             : #include <vector>
      33             : 
      34             : namespace PLMD {
      35             : namespace secondarystructure {
      36             : 
      37             : /// Base action for calculating things like AlphRMSD, AntibetaRMSD, etc
      38             : template <class T>
      39             : class SecondaryStructureBase: public ActionWithVector {
      40             : public:
      41             :   using input_type = T;
      42             :   using PTM = ParallelTaskManager<SecondaryStructureBase<T>>;
      43             :   static constexpr size_t virialSize=9;
      44             :   static constexpr unsigned customGatherStep=3;
      45             :   static constexpr unsigned customGatherStopBefore=virialSize;
      46             : 
      47             : private:
      48             :   PTM taskmanager;
      49             : public:
      50             :   static void registerKeywords( Keywords& keys );
      51             :   static void readBackboneAtoms( ActionShortcut* action, PlumedMain& plumed, const std::string& backnames, std::vector<unsigned>& chain_lengths, std::vector<std::string>& all_atoms );
      52             :   static bool readShortcutWords( std::string& ltmap, ActionShortcut* action );
      53             :   explicit SecondaryStructureBase(const ActionOptions&);
      54             :   unsigned getNumberOfDerivatives() override ;
      55             :   void calculate() override;
      56             :   void getInputData( std::vector<double>& inputdata ) const override ;
      57             :   void applyNonZeroRankForces( std::vector<double>& outforces ) override ;
      58             :   static void performTask( unsigned task_index, const T& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output );
      59             :   static int getNumberOfValuesPerTask( std::size_t task_index, const T& actiondata );
      60             :   static void getForceIndices( std::size_t task_index, std::size_t colno, std::size_t ntotal_force, const T& actiondata, const ParallelActionsInput& input, ForceIndexHolder force_indices );
      61             : };
      62             : 
      63             : template <class T>
      64         132 : unsigned SecondaryStructureBase<T>::getNumberOfDerivatives() {
      65         132 :   return 3*getNumberOfAtoms()+virialSize;
      66             : }
      67             : 
      68             : template <class T>
      69          38 : bool SecondaryStructureBase<T>::readShortcutWords( std::string& ltmap, ActionShortcut* action ) {
      70          76 :   action->parse("LESS_THAN",ltmap);
      71          38 :   if( ltmap.length()==0 ) {
      72             :     std::string nn, mm, d_0, r_0;
      73           6 :     action->parse("R_0",r_0);
      74           3 :     if( r_0.length()==0 ) {
      75             :       r_0="0.08";
      76             :     }
      77           3 :     action->parse("NN",nn);
      78           3 :     action->parse("D_0",d_0);
      79           3 :     action->parse("MM",mm);
      80           6 :     ltmap = "RATIONAL R_0=" + r_0 + " D_0=" + d_0 + " NN=" + nn + " MM=" + mm;
      81             :     return false;
      82             :   }
      83             :   return true;
      84             : }
      85             : 
      86             : template <class T>
      87         288 : void SecondaryStructureBase<T>::registerKeywords( Keywords& keys ) {
      88         288 :   ActionWithVector::registerKeywords( keys );
      89         288 :   PTM::registerKeywords( keys );
      90         288 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions");
      91         576 :   keys.addInputKeyword("optional","MASK","vector","a vector which is used to determine which elements of the secondary structure variable should be computed");
      92         288 :   keys.add("atoms","ATOMS","this is the full list of atoms that we are investigating");
      93         288 :   keys.add("numbered","SEGMENT","this is the lists of atoms in the segment that are being considered");
      94         576 :   if( keys.getDisplayName()=="SECONDARY_STRUCTURE_DRMSD" ) {
      95          36 :     keys.add("compulsory","BONDLENGTH","0.17","the length to use for bonds");
      96             :   }
      97         288 :   keys.add("numbered","STRUCTURE","the reference structure");
      98         576 :   if( keys.getDisplayName()=="SECONDARY_STRUCTURE_RMSD" ) {
      99          44 :     keys.add("compulsory","TYPE","OPTIMAL","the manner in which RMSD alignment is performed. Should be OPTIMAL or SIMPLE. "
     100             :              "For more details on the OPTIMAL and SIMPLE methods see \\ref RMSD.");
     101         488 :   } else if( keys.getDisplayName()!="SECONDARY_STRUCTURE_DRMSD" ) {
     102         208 :     keys.add("compulsory","TYPE","DRMSD","the manner in which RMSD alignment is performed. Should be OPTIMAL, SIMPLE or DRMSD. "
     103             :              "For more details on the OPTIMAL and SIMPLE methods see \\ref RMSD. For more details on the "
     104             :              "DRMSD method see \\ref DRMSD.");
     105             :   }
     106         288 :   keys.addFlag("VERBOSE",false,"write a more detailed output");
     107         576 :   keys.reset_style("VERBOSE","hidden");
     108        1080 :   if( keys.getDisplayName()!="SECONDARY_STRUCTURE_DRMSD" && keys.getDisplayName()!="SECONDARY_STRUCTURE_RMSD" ) {
     109         208 :     keys.add("residues","RESIDUES","this command is used to specify the set of residues that could conceivably form part of the secondary structure. "
     110             :              "It is possible to use residues numbers as the various chains and residues should have been identified else using an instance of the "
     111             :              "\\ref MOLINFO action. If you wish to use all the residues from all the chains in your system you can do so by "
     112             :              "specifying all. Alternatively, if you wish to use a subset of the residues you can specify the particular residues "
     113             :              "you are interested in as a list of numbers. Please be aware that to form secondary structure elements your chain "
     114             :              "must contain at least N residues, where N is dependent on the particular secondary structure you are interested in. "
     115             :              "As such if you define portions of the chain with fewer than N residues the code will crash.");
     116         208 :     keys.add("optional","LESS_THAN","calculate the number of a residue segments that are within a certain target distance of this secondary structure type. "
     117             :              "This quantity is calculated using \\f$\\sum_i \\sigma(s_i)\\f$, where \\f$\\sigma(s)\\f$ is a \\ref switchingfunction.");
     118         208 :     keys.add("optional","R_0","The r_0 parameter of the switching function.");
     119         208 :     keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
     120         208 :     keys.add("compulsory","NN","8","The n parameter of the switching function");
     121         208 :     keys.add("compulsory","MM","12","The m parameter of the switching function");
     122             :   }
     123         288 :   keys.addFlag("ALIGN_STRANDS",false,"ensure that the two halves of a beta sheet are not broken by the periodic boundaries before doing alignment");
     124         576 :   keys.addOutputComponent("struct","default","scalar","the vectors containing the rmsd distances between the residues and each of the reference structures");
     125         576 :   keys.addOutputComponent("lessthan","default","scalar","the number blocks of residues that have an RMSD from the secondary structure that is less than the threshold");
     126         288 :   keys.needsAction("SECONDARY_STRUCTURE_RMSD");
     127         288 :   keys.needsAction("SECONDARY_STRUCTURE_DRMSD");
     128         288 :   keys.needsAction("LESS_THAN");
     129         288 :   keys.needsAction("SUM");
     130         288 :   keys.addDOI("10.1021/ct900202f");
     131         288 : }
     132             : 
     133             : template <class T>
     134          38 : void SecondaryStructureBase<T>::readBackboneAtoms( ActionShortcut* action, PlumedMain& plumed, const std::string& moltype, std::vector<unsigned>& chain_lengths, std::vector<std::string>& all_atoms ) {
     135          38 :   auto* moldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(action);
     136          38 :   if( ! moldat ) {
     137           0 :     action->error("Unable to find MOLINFO in input");
     138             :   }
     139             : 
     140             :   std::vector<std::string> resstrings;
     141          76 :   action->parseVector( "RESIDUES", resstrings );
     142          38 :   if(resstrings.size()==0) {
     143           0 :     action->error("residues are not defined, check the keyword RESIDUES");
     144          38 :   } else if( Tools::caseInSensStringCompare(resstrings[0], "all") ) {
     145             :     resstrings[0]="all";
     146          36 :     action->log.printf("  examining all possible secondary structure combinations\n");
     147             :   } else {
     148           2 :     action->log.printf("  examining secondary structure in residue positions : %s ",resstrings[0].c_str() );
     149           3 :     for(unsigned i=1; i<resstrings.size(); ++i) {
     150           1 :       action->log.printf(", %s",resstrings[i].c_str() );
     151             :     }
     152           2 :     action->log.printf("\n");
     153             :   }
     154             :   std::vector< std::vector<AtomNumber> > backatoms;
     155          38 :   moldat->getBackbone( resstrings, moltype, backatoms );
     156             : 
     157          38 :   chain_lengths.resize( backatoms.size() );
     158         587 :   for(unsigned i=0; i<backatoms.size(); ++i) {
     159         549 :     chain_lengths[i]=backatoms[i].size();
     160       22569 :     for(unsigned j=0; j<backatoms[i].size(); ++j) {
     161             :       std::string bat_str;
     162       22020 :       Tools::convert( backatoms[i][j].serial(), bat_str );
     163       22020 :       all_atoms.push_back( bat_str );
     164             :     }
     165             :   }
     166          38 : }
     167             : 
     168             : template <class T>
     169          38 : SecondaryStructureBase<T>::SecondaryStructureBase(const ActionOptions&ao):
     170             :   Action(ao),
     171             :   ActionWithVector(ao),
     172          38 :   taskmanager(this) {
     173          38 :   if( plumed.usingNaturalUnits() ) {
     174           0 :     error("cannot use this collective variable when using natural units");
     175             :   }
     176             : 
     177          38 :   input_type myinput;
     178          38 :   parseFlag("NOPBC",myinput.nopbc);
     179          38 :   std::string alignType="";
     180          38 :   if( getName()=="SECONDARY_STRUCTURE_RMSD" ) {
     181          42 :     parse("TYPE",alignType);
     182             :   }
     183          38 :   log.printf("  distances from secondary structure elements are calculated using %s algorithm\n", alignType.c_str() );
     184          76 :   log<<"  Bibliography "<<plumed.cite("Pietrucci and Laio, J. Chem. Theory Comput. 5, 2197 (2009)");
     185          38 :   log<<"\n";
     186             : 
     187          38 :   parseFlag("ALIGN_STRANDS",myinput.align_strands);
     188          38 :   bool verbose_output=false;
     189          38 :   parseFlag("VERBOSE",verbose_output);
     190          38 :   log.printf("  ensuring atoms 7 and 22 in each residue are not separated by pbc before doing alignment\n");
     191             : 
     192             :   // Read in the atoms
     193             :   std::vector<AtomNumber> all_atoms;
     194          76 :   parseAtomList("ATOMS",all_atoms);
     195          38 :   if( all_atoms.size()==0 ) {
     196           0 :     error("no atoms were specified -- use ATOMS");
     197             :   }
     198          38 :   requestAtoms( all_atoms );
     199             : 
     200             :   std::vector<std::vector<unsigned> > colvar_atoms;
     201      160063 :   for(unsigned i=1;; ++i) {
     202             :     std::vector<unsigned> newatoms;
     203      320202 :     if( !parseNumberedVector("SEGMENT",i,newatoms) ) {
     204             :       break;
     205             :     }
     206      160063 :     if( verbose_output ) {
     207           0 :       log.printf("  Secondary structure segment %u contains atoms : ", static_cast<unsigned>(colvar_atoms.size()+1));
     208           0 :       for(unsigned ii=0; ii<newatoms.size(); ++ii) {
     209           0 :         log.printf("%d ",all_atoms[newatoms[ii]].serial() );
     210             :       }
     211           0 :       log.printf("\n");
     212             :     }
     213      160063 :     colvar_atoms.push_back( newatoms );
     214             :   }
     215          38 :   if( colvar_atoms.size()==0 ) {
     216           0 :     error("did not find any SEGMENT keywords in input");
     217             :   }
     218          38 :   myinput.colvar_atoms.resize( colvar_atoms.size(), colvar_atoms[0].size() );
     219      160101 :   for(unsigned i=0; i<colvar_atoms.size(); ++i) {
     220     4961953 :     for(unsigned j=0; j<colvar_atoms[i].size(); ++j) {
     221     4801890 :       myinput.colvar_atoms[i][j] = colvar_atoms[i][j];
     222             :     }
     223             :   }
     224             : 
     225          38 :   double bondlength=0.0;
     226          38 :   if( getName()=="SECONDARY_STRUCTURE_DRMSD" ) {
     227          17 :     parse("BONDLENGTH",bondlength);
     228          17 :     bondlength=bondlength/getUnits().getLength();
     229             :   }
     230             :   // Read in the reference structure
     231          56 :   for(unsigned ii=1;; ++ii) {
     232             :     std::vector<double> cstruct;
     233         188 :     if( !parseNumberedVector("STRUCTURE",ii,cstruct) ) {
     234             :       break ;
     235             :     }
     236          56 :     plumed_assert( cstruct.size()%3==0 && cstruct.size()/3==colvar_atoms[0].size() );
     237          56 :     std::vector<Vector> structure( cstruct.size()/3 );
     238        1736 :     for(unsigned i=0; i<structure.size(); ++i) {
     239        6720 :       for(unsigned j=0; j<3; ++j) {
     240        5040 :         structure[i][j] = 0.1*cstruct[3*i+j]/getUnits().getLength();
     241             :       }
     242             :     }
     243          81 :     myinput.setReferenceStructure( alignType, bondlength, structure );
     244             :   }
     245             : 
     246             :   // And create values to hold everything
     247          38 :   plumed_assert( myinput.nstructures>0 );
     248          38 :   std::vector<std::size_t> shape(1);
     249          38 :   shape[0]=colvar_atoms.size();
     250          38 :   if( myinput.nstructures==1 ) {
     251          20 :     addValue( shape );
     252          20 :     setNotPeriodic();
     253             :   } else {
     254             :     std::string num;
     255          54 :     for(unsigned i=0; i<myinput.nstructures; ++i) {
     256          36 :       Tools::convert( i+1, num );
     257          36 :       addComponent( "struct-" + num, shape );
     258          72 :       componentIsNotPeriodic( "struct-" + num );
     259             :     }
     260             :   }
     261          94 :   for(unsigned i=0; i<getNumberOfComponents(); ++i) {
     262          56 :     getPntrToComponent(i)->setDerivativeIsZeroWhenValueIsZero();
     263             :   }
     264          38 :   myinput.nindices_per_task = colvar_atoms[0].size();
     265          38 :   taskmanager.setupParallelTaskManager( 3*colvar_atoms[0].size() + virialSize, 3*getNumberOfAtoms() + 9 );
     266          38 :   taskmanager.setActionInput( myinput );
     267          76 : }
     268             : 
     269             : template <class T>
     270         276 : void SecondaryStructureBase<T>::calculate() {
     271         276 :   taskmanager.runAllTasks();
     272         276 : }
     273             : 
     274             : template <class T>
     275         276 : void SecondaryStructureBase<T>::getInputData( std::vector<double>& inputdata ) const {
     276         276 :   if( inputdata.size()!=3*getNumberOfAtoms() ) {
     277          38 :     inputdata.resize( 3*getNumberOfAtoms() );
     278             :   }
     279             : 
     280             :   std::size_t k=0;
     281      134916 :   for(unsigned i=0; i<getNumberOfAtoms(); ++i) {
     282      134640 :     Vector mypos( getPosition(i) );
     283      134640 :     inputdata[k] = mypos[0];
     284      134640 :     k++;
     285      134640 :     inputdata[k] = mypos[1];
     286      134640 :     k++;
     287      134640 :     inputdata[k] = mypos[2];
     288      134640 :     k++;
     289             :   }
     290         276 : }
     291             : 
     292             : template <class T>
     293      140516 : void SecondaryStructureBase<T>::performTask( unsigned task_index, const T& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output ) {
     294             :   // std::vector<Vector> pos( actiondata.natoms );
     295             :   std::array<Vector,30> pos;
     296             : 
     297     4355996 :   for(unsigned i=0; i<actiondata.natoms; ++i) {
     298     4215480 :     const unsigned atno = actiondata.colvar_atoms(task_index,i);
     299     4215480 :     pos[i][0] = input.inputdata[3*atno+0];
     300     4215480 :     pos[i][1] = input.inputdata[3*atno+1];
     301     4215480 :     pos[i][2] = input.inputdata[3*atno+2];
     302             :   }
     303             :   // This aligns the two strands if this is required
     304      140516 :   if( actiondata.align_strands ) {
     305      140240 :     Vector distance=input.pbc->distance( pos[6],pos[21] );
     306             :     Vector origin_old, origin_new;
     307      140240 :     origin_old=pos[21];
     308             :     origin_new=pos[6]+distance;
     309     2243840 :     for(unsigned i=15; i<30; ++i) {
     310     2103600 :       pos[i]+=( origin_new - origin_old );
     311             :     }
     312         276 :   } else if( !actiondata.nopbc ) {
     313        8280 :     for(unsigned i=0; i<actiondata.natoms-1; ++i) {
     314             :       const Vector & first (pos[i]);
     315        8004 :       Vector & second (pos[i+1]);
     316        8004 :       second=first+input.pbc->distance(first,second);
     317             :     }
     318             :   }
     319             : 
     320             :   // Create a holder for the derivatives
     321      140516 :   const unsigned rs = actiondata.nstructures;
     322             :   ColvarOutput rmsd_output( output.values,
     323             :                             3*pos.size()+virialSize,
     324             :                             output.derivatives.data() );
     325             :   // And now calculate the DRMSD
     326      354150 :   for(unsigned i=0; i<rs; ++i) {
     327      213634 :     T::calculateDistance( i, input.noderiv, actiondata, View{pos.data(),pos.size()}, rmsd_output );
     328             :   }
     329      140516 : }
     330             : 
     331             : template <class T>
     332         240 : void SecondaryStructureBase<T>::applyNonZeroRankForces( std::vector<double>& outforces ) {
     333         240 :   taskmanager.applyForces( outforces );
     334         240 : }
     335             : 
     336             : template <class T>
     337       70096 : int SecondaryStructureBase<T>::getNumberOfValuesPerTask( std::size_t task_index,
     338             :     const T& actiondata ) {
     339       70096 :   return 1;
     340             : }
     341             : 
     342             : template <class T>
     343       70096 : void SecondaryStructureBase<T>::getForceIndices( std::size_t task_index,
     344             :     std::size_t /* colno */,
     345             :     std::size_t ntotal_force,
     346             :     const T& actiondata,
     347             :     const ParallelActionsInput& input,
     348             :     ForceIndexHolder force_indices ) {
     349      176631 :   for(unsigned i=0; i<input.ncomponents; ++i) {
     350             :     std::size_t m = 0;
     351     3302585 :     for(unsigned j=0; j<actiondata.nindices_per_task; ++j) {
     352     3196050 :       std::size_t base = 3*actiondata.colvar_atoms[task_index][j];
     353     3196050 :       force_indices.indices[i][m] = base + 0;
     354     3196050 :       ++m;
     355     3196050 :       force_indices.indices[i][m] = base + 1;
     356     3196050 :       ++m;
     357     3196050 :       force_indices.indices[i][m] = base + 2;
     358     3196050 :       ++m;
     359             :     }
     360     1065350 :     for(unsigned n=ntotal_force-virialSize; n<ntotal_force; ++n) {
     361      958815 :       force_indices.indices[i][m] = n;
     362      958815 :       ++m;
     363             :     }
     364      106535 :     force_indices.threadsafe_derivatives_end[i] = 0;
     365      106535 :     force_indices.tot_indices[i] = m;
     366             :   }
     367       70096 : }
     368             : 
     369             : }
     370             : }
     371             : 
     372             : #endif

Generated by: LCOV version 1.16