LCOV - code coverage report
Current view: top level - secondarystructure - SecondaryStructureDRMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 58 59 98.3 %
Date: 2025-12-04 11:19:34 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2017-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             : #ifdef __PLUMED_HAS_OPENACC
      23             : #define __PLUMED_USE_OPENACC 1
      24             : #endif //__PLUMED_HAS_OPENACC
      25             : #include "SecondaryStructureBase.h"
      26             : #include "core/ActionRegister.h"
      27             : #include "tools/Matrix.h"
      28             : 
      29             : #include<vector>
      30             : 
      31             : //+PLUMEDOC MCOLVAR SECONDARY_STRUCTURE_DRMSD
      32             : /*
      33             : Calclulate the DRMSD between segments of a protein and a reference structure of interest
      34             : 
      35             : This action is used in the shortcuts [ALPHARMSD](ALPHARMSD.md), [ANTIBETARMSD](ANTIBETARMSD.md) and [PARABETARMSD](PARABETARMSD.md).  It calculates a
      36             : vector of [DRMSD](DRMSD.md) values between a single reference multiple configurations and the instantaneous
      37             : positions of various groups of atoms.  For example, in the following input we define a single set of reference
      38             : set of coordinates for 3 atoms.
      39             : 
      40             : ```plumed
      41             : c1: SECONDARY_STRUCTURE_DRMSD ...
      42             :     ATOMS=13-24
      43             :     BONDLENGTH=0.17 STRUCTURE1=1,0,0,0,1,0,0,0,1
      44             :     SEGMENT1=0,1,2 SEGMENT2=3,4,5
      45             :     SEGMENT3=6,7,8 SEGMENT4=9,10,11
      46             : ...
      47             : PRINT ARG=c1 FILE=colvar
      48             : ```
      49             : 
      50             : A four dimensional vector is then returned that contains the DRMSD distances between the 4 sets of 3 atoms that were specified using the `SEGMENT` keywords
      51             : and the reference coordinates.  Notice that you can use multiple instances of the `STRUCTURE` keyword.  In general the the number of vectors output
      52             : is equal to the number of times the `STRUCTURE` keyword is used.  Further note that the indices specified to the SEGMENT keywords give the indices in the list
      53             : of atoms specified by the ATOMS keyword.  `SEGMENT1=0,1,2` in the above input is measuring the distance between the instaneous positions of atoms 13, 14 and 15
      54             : and the reference structure.
      55             : 
      56             : ## Periodic boundary conditions
      57             : 
      58             : If you turn off periodic boundary conditions by using the NOPBC flag as shown below:
      59             : 
      60             : ```plumed
      61             : c1: SECONDARY_STRUCTURE_DRMSD ...
      62             :     ATOMS=13-24 NOPBC
      63             :     BONDLENGTH=0.17 STRUCTURE1=1,0,0,0,1,0,0,0,1
      64             :     SEGMENT1=0,1,2 SEGMENT2=3,4,5
      65             :     SEGMENT3=6,7,8 SEGMENT4=9,10,11
      66             : ...
      67             : PRINT ARG=c1 FILE=colvar
      68             : ```
      69             : 
      70             : the distances between in the instaneous structure are evaluated in a way that does not take the periodic boundary conditions
      71             : into account.  Whenver this flag is __not__ present the distances in the instaneous structure are evaluated in a way that takes the periodic boundary conditions
      72             : into account.
      73             : 
      74             : If you use the `ALIGN_STRANDS` flag to evaluate the distances in a way that takes the periodic boundary conditions into accounts means that
      75             : calculating the distance between the positions of the 7th and 22nd atoms ($\mathbf{x}_7$ and $\mathbf{x}_{22}$) of the instanenous structures
      76             : using the periodic boundary conditions.  The 22 atom is the repositioned at:
      77             : 
      78             : $$
      79             : \mathbf{x}_{22} = \mathbf{x}_7 + ( \mathbf{x}_{22} - \mathbf{x}_7 )
      80             : $$
      81             : 
      82             : where the difference in the second term on the right of the equality sign is evaluated using the periodic boundary conditions. New positions are then determined
      83             : for atoms 16 through 30 by adding the difference (evaluated without PBC) between the new position for atom 22 after the above transformation has been perfomed and the
      84             : original position of this atom.  This treatment is designed for making sure that the two strands of the [ANTIBETARMSD](ANTIBETARMSD.md) and [PARABETARMSD](PARABETARMSD.md)
      85             : sheets are not broken by the periodic boundaries.
      86             : 
      87             : ## Working with beta sheet like structures
      88             : 
      89             : As discussed in the documentation for [ANTIBETARMSD](ANTIBETARMSD.md) and [PARABETARMSD](PARABETARMSD.md) using the STRANDS_KEYWORD cutoff can speed up the calculation
      90             : dramatically.  The reason this keyword gives such dramatric speed ups is illustrated by the example input:
      91             : 
      92             : ```plumed
      93             : ab_cut_dists: DISTANCE ...
      94             :   ATOMS1=19,69 ATOMS2=19,79
      95             :   ATOMS3=19,89 ATOMS4=19,99
      96             :   ATOMS5=19,109
      97             : ...
      98             : ab_cut: CUSTOM ARG=ab_cut_dists FUNC=step(1-x) PERIODIC=NO
      99             : ab_rmsd: SECONDARY_STRUCTURE_DRMSD ...
     100             :    BONDLENGTH=0.17 ALIGN_STRANDS MASK=ab_cut
     101             :    ATOMS=7,9,11,15,16,17,19,21,25,26,27,29,31,35,36,37,39,41,45,46,47,49,51,55,56,57,59,61,65,66,67,69,71,75,76,77,79,81,85,86,87,89,91,95,96,97,99,101,105,106,107,109,111,115,116,117,119,121,125,126
     102             :    SEGMENT1=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39
     103             :    SEGMENT2=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44
     104             :    SEGMENT3=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49
     105             :    SEGMENT4=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54
     106             :    SEGMENT5=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59
     107             :    STRUCTURE1=2.263,-3.795,1.722,2.493,-2.426,2.263,3.847,-1.838,1.761,1.301,-1.517,1.921,0.852,-1.504,0.739,0.818,-0.738,2.917,-0.299,0.243,2.748,-1.421,-0.076,3.757,0.273,1.68,2.854,0.902,1.993,3.888,0.119,2.532,1.813,0.683,3.916,1.68,1.58,3.94,0.395,-0.394,5.011,1.63,-1.459,4.814,0.982,-2.962,3.559,-1.359,-2.439,2.526,-2.287,-1.189,3.006,-3.087,-2.081,1.231,-1.52,-1.524,1.324,-0.409,-2.326,0.037,-2.095,-1.858,-1.269,-1.554,-3.053,-2.199,-1.291,-0.869,-1.949,-2.512,-1.255,-2.07,-3.71,0.326,-2.363,-2.072,1.405,-2.992,-2.872,2.699,-2.129,-2.917,1.745,-4.399,-2.33,1.899,-4.545,-1.102
     108             : ...
     109             : ab_ltu: LESS_THAN ARG=ab_rmsd SWITCH={RATIONAL R_0=0.1 D_0=0.0 NN=8 MM=12} MASK=ab_cut
     110             : ab_lt: CUSTOM ARG=ab_ltu,ab_cut FUNC=x*y PERIODIC=NO
     111             : ab: SUM ARG=ab_lt PERIODIC=NO
     112             : PRINT ARG=ab FILE=colvar
     113             : ```
     114             : 
     115             : The input above illustrates the input that is used by the shortcut that computes the [ANTIBETARMSD](ANTIBETARMSD.md). You can see that the distances between the atoms on the two strands of the
     116             : beta sheet are computed in a [DISTANCE](DISTANCE.md) action before we computed the SECONDARY_STRUCTURE_DRMSD values.  These distances are then transformed by a Heavyside function that is 1 if
     117             : the distance is less than 1 nm and zero otherwise.  This vector of ones and zeros is then fed into the SECONDARY_STRUCTURE_DRMSD action as a mask.  By doing this we ensure that SECONDARY_STRUCTURE_DRMSD
     118             : values are only computed for parts of the strand that are close together. This avoids performing unecessarily expensive calculations and is also a reasonable thing to do as if the two strands are more than
     119             : 1 nm appart the residues in question are not at all similar to a beta sheet structure.
     120             : 
     121             : */
     122             : //+ENDPLUMEDOC
     123             : 
     124             : namespace PLMD {
     125             : namespace secondarystructure {
     126             : 
     127             : ///This is a sort of compact std::vector<std::vector<T>>
     128             : template<typename T>
     129             : class flexibleMemory {
     130             :   ///Stores the data
     131             :   std::vector<T> data{};
     132             :   ///Stores the boundaries
     133             :   std::vector<size_t> sizes{};
     134             :   //helpers for openACC
     135             :   T* d;
     136             :   size_t* sz;
     137             :   void update() {
     138          84 :     d=data.data();
     139          84 :     sz=sizes.data();
     140          34 :   }
     141             : public:
     142             :   flexibleMemory() {
     143             :     update();
     144             :   };
     145             :   ~flexibleMemory() =default;
     146             :   flexibleMemory(const flexibleMemory& m) :
     147             :     data(m.data),
     148             :     sizes(m.sizes) {
     149             :     update();
     150             :   }
     151          34 :   flexibleMemory& operator=(const flexibleMemory& m) {
     152          34 :     if (this!=&m) {
     153          34 :       data=m.data;
     154          34 :       sizes=m.sizes;
     155             :       update();
     156             :     }
     157          34 :     return *this;
     158             :   }
     159             :   constexpr size_t size() const {
     160             :     return sizes.size();
     161             :   }
     162             : //Access the i-th view of the data, use this to access the elements
     163      169823 :   constexpr View< const T> get(size_t i) const {
     164             :     //use this  at the start of your function, to not calculate again and again the initial index
     165             :     size_t init=0;
     166      231298 :     for(size_t j=0; j<i; j++) {
     167       61475 :       init+=sz[j];
     168             :     }
     169      169823 :     return View<const T>(d+init,sz[i]);
     170             :   }
     171          50 :   void extend(const std::vector<T>& v) {
     172          50 :     data.insert(data.end(),v.begin(),v.end());
     173          50 :     sizes.push_back(v.size());
     174             :     update();
     175          50 :   }
     176             :   void toACCDevice()const {
     177             : #pragma acc enter data copyin(this[0:1], d[0:data.size()], sz[0:sizes.size()])
     178             :   }
     179             :   void removeFromACCDevice() const {
     180             : #pragma acc exit data delete(sz[0:sizes.size()], d[0:data.size()], this[0:1])
     181             :   }
     182             : };
     183             : 
     184             : class SecondaryStructureDRMSDInput {
     185             : private:
     186             :   struct pairDistance {
     187             :     double distance;
     188             :     unsigned i;
     189             :     unsigned j;
     190             :   };
     191             : /// The list of reference configurations
     192             :   flexibleMemory<pairDistance> drmsd_targets{};
     193             :   flexibleMemory<unsigned> drmsd_atoms{};
     194             : /// The general input for the secondary structure variable
     195             : public:
     196             :   size_t natoms{0};
     197             :   size_t nstructures{0};
     198             :   size_t nindices_per_task{0};
     199             : /// Are we operating without periodic boundary conditions
     200             :   bool nopbc{false};
     201             : /// Variables for strands cutoff
     202             :   bool align_strands{false};
     203             : /// The atoms involved in each of the secondary structure segments
     204             :   Matrix<unsigned> colvar_atoms{};
     205             :   static void calculateDistance( unsigned n,
     206             :                                  bool noderiv,
     207             :                                  const SecondaryStructureDRMSDInput& actiondata,
     208             :                                  View<Vector> pos,
     209             :                                  ColvarOutput& output );
     210             :   void setReferenceStructure( std::string type, double bondlength, std::vector<Vector>& structure );
     211             :   void toACCDevice()const {
     212             : #pragma acc enter data copyin(this[0:1], natoms,nstructures,nindices_per_task,nopbc,align_strands)
     213             :     drmsd_targets.toACCDevice();
     214             :     drmsd_atoms.toACCDevice();
     215             :     colvar_atoms.toACCDevice();
     216             :   }
     217             :   void removeFromACCDevice() const {
     218             :     colvar_atoms.removeFromACCDevice();
     219             :     drmsd_atoms.removeFromACCDevice();
     220             :     drmsd_targets.removeFromACCDevice();
     221             : #pragma acc exit data delete(align_strands,nopbc,nindices_per_task,nstructures,natoms,this[0:1])
     222             : 
     223             :   }
     224             : };
     225             : 
     226             : typedef SecondaryStructureBase<SecondaryStructureDRMSDInput> colv;
     227             : PLUMED_REGISTER_ACTION(colv,"SECONDARY_STRUCTURE_DRMSD")
     228             : 
     229          25 : void SecondaryStructureDRMSDInput::setReferenceStructure(
     230             :   std::string /*type*/,
     231             :   double bondlength,
     232             :   std::vector<Vector>& structure ) {
     233             :   std::vector<pairDistance> targets;
     234             :   //set is ordered and contains no duplicated data
     235             :   std::set<unsigned> atoms_targets;
     236             :   //targets.reserve( (structure.size()*(structure.size()-1))/2 );?
     237         750 :   for(unsigned i=0; i<structure.size()-1; ++i) {
     238       11600 :     for(unsigned j=i+1; j<structure.size(); ++j) {
     239       10875 :       double distance = delta( structure[i], structure[j] ).modulo();
     240       10875 :       if(distance > bondlength) {
     241       10172 :         targets.emplace_back(pairDistance{distance,i,j});
     242       10172 :         atoms_targets.emplace(i);
     243       10172 :         atoms_targets.emplace(j);
     244             :       }
     245             :     }
     246             :   }
     247          25 :   drmsd_targets.extend( targets );
     248             : 
     249          50 :   drmsd_atoms.extend(std::vector<unsigned> {atoms_targets.begin(),atoms_targets.end()});
     250          25 :   nstructures = drmsd_targets.size();
     251          25 :   if (natoms==0) {
     252          17 :     natoms = structure.size();
     253           8 :   } else if (natoms!=structure.size()) {
     254           0 :     plumed_merror("input structures have a different number of atoms");
     255             :   }
     256             : 
     257          25 : }
     258             : 
     259       68042 : void SecondaryStructureDRMSDInput::calculateDistance(
     260             :   const unsigned n,
     261             :   const bool noderiv,
     262             :   const SecondaryStructureDRMSDInput& actiondata,
     263             :   const View<Vector> pos,
     264             :   ColvarOutput& output ) {
     265             :   {
     266       68042 :     const auto targetList = actiondata.drmsd_atoms.get(n);
     267             :     const auto targetAtoms=targetList.size();
     268       68042 :     if( !noderiv ) {
     269       33739 :       output.virial.set( n, Tensor(0,0,0,0,0,0,0,0,0) );
     270     1045909 :       for(unsigned i=0; i<targetAtoms; ++i ) {
     271     1012170 :         output.derivs[n][targetList[i]] =Vector(0.0,0.0,0.0);
     272             :       }
     273             :     }
     274             :   }
     275             : 
     276             :   double drmsd = 0.0;
     277             :   Vector distance;
     278             :   Vector dd;
     279             : 
     280       68042 :   const auto target = actiondata.drmsd_targets.get(n);
     281             :   const auto targetSize=target.size();
     282             : 
     283    27760860 :   for (unsigned i=0; i<targetSize; ++i) {
     284    82848990 :     const auto& [reference,k,j] = target[i];
     285    27692818 :     distance=delta( pos[k], pos[j] );
     286    27692818 :     const double len = distance.modulo();
     287    27692818 :     const double diff = len - reference;
     288    27692818 :     drmsd += diff*diff;
     289             : 
     290    27692818 :     if( !noderiv ) {
     291    13731677 :       dd=distance*(diff / len);
     292    13731677 :       output.derivs[n][k] -= dd;//-3 mul
     293    13731677 :       output.derivs[n][j] += dd;//-3mul
     294    13731677 :       output.virial.set( n, output.virial[n] - Tensor(dd,distance) );//-9 mul
     295             :     }
     296             :   }
     297       68042 :   const double inpairs = 1./static_cast<double>(targetSize);
     298       68042 :   output.values[n] = sqrt(inpairs*drmsd);
     299             : 
     300       68042 :   if( !noderiv ) {
     301       33739 :     double scalef = inpairs / output.values[n];
     302             : 
     303       33739 :     PLMD::LoopUnroller<9>::_mul(output.virial.getView(n).data(),scalef);
     304       33739 :     const auto targetList = actiondata.drmsd_atoms.get(n);
     305     1045909 :     for(unsigned i=0; i<actiondata.natoms; ++i ) {
     306     1012170 :       output.derivs[n][targetList[i]] *= scalef;
     307             :     }
     308             :   }
     309       68042 : }
     310             : 
     311             : }
     312             : }

Generated by: LCOV version 1.16