LCOV - code coverage report
Current view: top level - secondarystructure - SecondaryStructureBase.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 190 212 89.6 %
Date: 2026-11-05 10:19:04 Functions: 21 23 91.3 %

          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 "tools/TypeUtils.h"
      33             : #include <vector>
      34             : #include <type_traits>
      35             : 
      36             : namespace PLMD {
      37             : namespace secondarystructure {
      38             : 
      39             : namespace helpers {
      40             : //this makes the variable needstype only needed to be declared explicitly
      41             : //needsType activate the reading of "TYPE" during the parsing
      42             : template <typename T, typename=void>
      43             : constexpr bool needsType=false;
      44             : //specialization to intercept needsType
      45             : template <typename T>
      46             : constexpr bool needsType<T,std::void_t<decltype(T::needsType)>> =T::needsType;
      47             : 
      48             : //this makes the variable needstype only needed to be declared explicitly
      49             : //needsBondLength activate the reading of "TYPE" during the parsing
      50             : template <typename T, typename=void>
      51             : constexpr bool needsBondLength=false;
      52             : //specialization to intercept needsBondLength
      53             : template <typename T>
      54             : constexpr bool needsBondLength<T,std::void_t<decltype(T::needsBondLength)>> =T::needsBondLength;
      55             : }
      56             : 
      57             : /// Base action for calculating things like AlphRMSD, AntibetaRMSD, etc
      58             : template <class T, typename myPTM=defaultPTM>
      59             : class SecondaryStructureBase: public ActionWithVector {
      60             : public:
      61             :   using input_type = T;
      62             :   using mytype=SecondaryStructureBase<T,myPTM>;
      63             :   using PTM =typename myPTM::template PTM<mytype>;
      64             :   typedef typename PTM::ParallelActionsInput ParallelActionsInput;
      65             :   typedef typename PTM::ParallelActionsOutput ParallelActionsOutput;
      66             :   using precision = cvprecision_t<T>;
      67             :   static constexpr size_t virialSize=9;
      68             :   static constexpr unsigned customGatherStep=3;
      69             :   static constexpr unsigned customGatherStopBefore=virialSize;
      70             :   /// The largest number of atoms in a segment (the size of the position buffer in performTask).
      71             :   /// ALPHARMSD, ANTIBETARMSD and PARABETARMSD all use segments of exactly this size
      72             :   static constexpr unsigned maxAtomsInSegment=30;
      73             : 
      74             : private:
      75             :   PTM taskmanager;
      76             :   static constexpr bool needsType = helpers::needsType<T>;
      77             :   static constexpr bool needsBondLength = helpers::needsBondLength<T>;
      78             : public:
      79             :   static void registerKeywords( Keywords& keys );
      80             :   static void readBackboneAtoms( ActionShortcut* action, PlumedMain& plumed, const std::string& backnames, std::vector<unsigned>& chain_lengths, std::vector<std::string>& all_atoms );
      81             :   static bool readShortcutWords( std::string& ltmap, ActionShortcut* action );
      82             :   explicit SecondaryStructureBase(const ActionOptions&);
      83             :   unsigned getNumberOfDerivatives() override ;
      84             :   void calculate() override;
      85             :   void getAtomicInputData( std::vector<double>& inputdata ) const override ;
      86             :   void getAtomicInputData( std::vector<float>& inputdata ) const override ;
      87             :   void applyNonZeroRankForces( std::vector<double>& outforces ) override ;
      88             :   static void performTask( unsigned task_index, const T& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output );
      89             :   static int getNumberOfValuesPerTask( std::size_t task_index, const T& actiondata );
      90             :   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 );
      91             : };
      92             : 
      93             : //this is used to get some of the static non template dependant functionalities for Secondary structure
      94             : using SecondaryStructureInput=SecondaryStructureBase<Empty>;
      95             : 
      96             : template <class T,typename myPTM>
      97         198 : unsigned SecondaryStructureBase<T,myPTM>::getNumberOfDerivatives() {
      98         198 :   return 3*getNumberOfAtoms()+virialSize;
      99             : }
     100             : 
     101             : template <class T,typename myPTM>
     102          60 : bool SecondaryStructureBase<T,myPTM>::readShortcutWords( std::string& ltmap, ActionShortcut* action ) {
     103         120 :   action->parse("LESS_THAN",ltmap);
     104          60 :   if( ltmap.length()==0 ) {
     105             :     std::string nn, mm, d_0, r_0;
     106          28 :     action->parse("R_0",r_0);
     107          14 :     if( r_0.length()==0 ) {
     108             :       // The default is 0.08 nm. The distances are in the units that were set with UNITS, so it has to be converted
     109           4 :       Tools::convert( 0.08/action->plumed.getUnits().getLength(), r_0 );
     110             :     }
     111          14 :     action->parse("NN",nn);
     112          14 :     action->parse("D_0",d_0);
     113          14 :     action->parse("MM",mm);
     114          70 :     ltmap = "RATIONAL R_0=" + r_0 + " D_0=" + d_0 + " NN=" + nn + " MM=" + mm;
     115             :     return false;
     116             :   }
     117             :   return true;
     118             : }
     119             : 
     120             : template <class T,typename myPTM>
     121         497 : void SecondaryStructureBase<T,myPTM>::registerKeywords( Keywords& keys ) {
     122         497 :   ActionWithVector::registerKeywords( keys );
     123         221 :   T::registerKeywords( keys );
     124         497 :   PTM::registerKeywords( keys );
     125         497 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions");
     126         994 :   keys.addInputKeyword("optional","MASK","vector","a vector which is used to determine which elements of the secondary structure variable should be computed");
     127         497 :   keys.add("atoms","ATOMS","this is the full list of atoms that we are investigating");
     128         497 :   keys.add("numbered","SEGMENT","this is the lists of atoms in the segment that are being considered");
     129             :   if constexpr ( needsBondLength /*keys.getDisplayName()=="SECONDARY_STRUCTURE_DRMSD"*/ ) {
     130         148 :     keys.add("compulsory","BONDLENGTH","0.17","the length to use for bonds");
     131             :   }
     132         497 :   keys.add("numbered","STRUCTURE","the reference structure");
     133             :   if constexpr ( needsType /*keys.getDisplayName()=="SECONDARY_STRUCTURE_RMSD"*/ ) {
     134          73 :     keys.add("compulsory","TYPE","OPTIMAL","the manner in which RMSD alignment is performed. Should be OPTIMAL or SIMPLE. "
     135             :              "For more details on the OPTIMAL and SIMPLE methods see RMSD.");
     136             :   } else if constexpr ( !needsBondLength /*keys.getDisplayName()!="SECONDARY_STRUCTURE_DRMSD"*/ ) {
     137         276 :     keys.add("compulsory","TYPE","DRMSD","the manner in which RMSD alignment is performed. Should be OPTIMAL, SIMPLE or DRMSD. "
     138             :              "For more details on the OPTIMAL and SIMPLE methods see RMSD. For more details on the "
     139             :              "DRMSD method see DRMSD.");
     140             :   }
     141         497 :   keys.addFlag("VERBOSE",false,"write a more detailed output");
     142         994 :   keys.reset_style("VERBOSE","hidden");
     143             :   if constexpr ( !needsBondLength && !needsType ) {
     144         276 :     keys.add("residues","RESIDUES","this command is used to specify the set of residues that could conceivably form part of the secondary structure. "
     145             :              "It is possible to use residues numbers as the various chains and residues should have been identified else using an instance of the "
     146             :              "MOLINFO action. If you wish to use all the residues from all the chains in your system you can do so by "
     147             :              "specifying all. Alternatively, if you wish to use a subset of the residues you can specify the particular residues "
     148             :              "you are interested in as a list of numbers. Please be aware that to form secondary structure elements your chain "
     149             :              "must contain at least N residues, where N is dependent on the particular secondary structure you are interested in. "
     150             :              "As such if you define portions of the chain with fewer than N residues the code will crash.");
     151         276 :     keys.add("optional","LESS_THAN","calculate the number of a residue segments that are within a certain target distance of this secondary structure type. "
     152             :              "This quantity is calculated using \\f$\\sum_i \\sigma(s_i)\\f$, where \\f$\\sigma(s)\\f$ is a switching function.");
     153         276 :     keys.add("optional","R_0","The r_0 parameter of the switching function.");
     154         276 :     keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
     155         276 :     keys.add("compulsory","NN","8","The n parameter of the switching function");
     156         276 :     keys.add("compulsory","MM","12","The m parameter of the switching function");
     157             :   }
     158         497 :   keys.addFlag("ALIGN_STRANDS",false,"ensure that the two halves of a beta sheet are not broken by the periodic boundaries before doing alignment");
     159         994 :   keys.addOutputComponent("struct","default","vector","the vectors containing the rmsd distances between the residues and each of the reference structures");
     160         994 :   keys.addOutputComponent("lessthan","default","scalar","the number blocks of residues that have an RMSD from the secondary structure that is less than the threshold");
     161         497 :   keys.needsAction("SECONDARY_STRUCTURE_RMSD");
     162         497 :   keys.needsAction("SECONDARY_STRUCTURE_DRMSD");
     163         497 :   keys.needsAction("LESS_THAN");
     164         497 :   keys.needsAction("SUM");
     165         497 :   keys.addDOI("10.1021/ct900202f");
     166         497 : }
     167             : 
     168             : template <class T,typename myPTM>
     169          60 : void SecondaryStructureBase<T,myPTM>::readBackboneAtoms( ActionShortcut* action, PlumedMain& plumed, const std::string& moltype, std::vector<unsigned>& chain_lengths, std::vector<std::string>& all_atoms ) {
     170          60 :   auto* moldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(action);
     171          60 :   if( ! moldat ) {
     172           0 :     action->error("Unable to find MOLINFO in input");
     173             :   }
     174             : 
     175          60 :   std::vector<std::string> resstrings;
     176         120 :   action->parseVector( "RESIDUES", resstrings );
     177          60 :   if(resstrings.size()==0) {
     178           0 :     action->error("residues are not defined, check the keyword RESIDUES");
     179          60 :   } else if( Tools::caseInSensStringCompare(resstrings[0], "all") ) {
     180             :     resstrings[0]="all";
     181          56 :     action->log.printf("  examining all possible secondary structure combinations\n");
     182             :   } else {
     183           4 :     action->log.printf("  examining secondary structure in residue positions : %s ",resstrings[0].c_str() );
     184           6 :     for(unsigned i=1; i<resstrings.size(); ++i) {
     185           2 :       action->log.printf(", %s",resstrings[i].c_str() );
     186             :     }
     187           4 :     action->log.printf("\n");
     188             :   }
     189          60 :   std::vector< std::vector<AtomNumber> > backatoms;
     190          60 :   moldat->getBackbone( resstrings, moltype, backatoms );
     191             : 
     192          60 :   chain_lengths.resize( backatoms.size() );
     193         758 :   for(unsigned i=0; i<backatoms.size(); ++i) {
     194         698 :     chain_lengths[i]=backatoms[i].size();
     195       28908 :     for(unsigned j=0; j<backatoms[i].size(); ++j) {
     196             :       std::string bat_str;
     197       28210 :       Tools::convert( backatoms[i][j].serial(), bat_str );
     198       28210 :       all_atoms.push_back( bat_str );
     199             :     }
     200             :   }
     201          60 :   if( all_atoms.empty() ) {
     202           0 :     action->error("no backbone atoms were found for the residues specified with RESIDUES");
     203             :   }
     204          60 : }
     205             : 
     206             : template <class T,typename myPTM>
     207          76 : SecondaryStructureBase<T,myPTM>::SecondaryStructureBase(const ActionOptions&ao):
     208             :   Action(ao),
     209             :   ActionWithVector(ao),
     210          76 :   taskmanager(this) {
     211          76 :   if( plumed.usingNaturalUnits() ) {
     212           0 :     error("cannot use this collective variable when using natural units");
     213             :   }
     214             : 
     215          76 :   input_type myinput;
     216          76 :   parseFlag("NOPBC",myinput.nopbc);
     217          84 :   std::string alignType="";
     218             :   if constexpr ( needsType ) {
     219          40 :     parse("TYPE",alignType);
     220             :   }
     221          76 :   log.printf("  distances from secondary structure elements are calculated using %s algorithm\n", alignType.c_str() );
     222          76 :   log<<"  Bibliography "<<plumed.cite("Pietrucci and Laio, J. Chem. Theory Comput. 5, 2197 (2009)");
     223          76 :   log<<"\n";
     224             : 
     225          76 :   parseFlag("ALIGN_STRANDS",myinput.align_strands);
     226          76 :   bool verbose_output=false;
     227          87 :   parseFlag("VERBOSE",verbose_output);
     228          76 :   log.printf("  ensuring atoms 7 and 22 in each residue are not separated by pbc before doing alignment\n");
     229             : 
     230             :   // Read in the atoms
     231          76 :   std::vector<AtomNumber> all_atoms;
     232         152 :   parseAtomList("ATOMS",all_atoms);
     233          76 :   if( all_atoms.size()==0 ) {
     234           0 :     error("no atoms were specified -- use ATOMS");
     235             :   }
     236          76 :   requestAtoms( all_atoms );
     237             : 
     238          76 :   std::vector<std::vector<unsigned> > colvar_atoms;
     239      199232 :   for(unsigned i=1;; ++i) {
     240      199308 :     std::vector<unsigned> newatoms;
     241      398616 :     if( !parseNumberedVector("SEGMENT",i,newatoms) ) {
     242             :       break;
     243             :     }
     244             :     // The segments are copied into a rectangular matrix and the positions of their atoms into a
     245             :     // buffer of fixed size, so everything is checked here rather than in performTask
     246      199238 :     if( newatoms.size()<2 || newatoms.size()>maxAtomsInSegment ) {
     247          12 :       error("SEGMENT" + std::to_string(i) + " contains " + std::to_string(newatoms.size()) + " atoms. A segment must contain between 2 and "
     248             :             + std::to_string(maxAtomsInSegment) + " atoms");
     249             :     }
     250      199236 :     if( !colvar_atoms.empty() && newatoms.size()!=colvar_atoms[0].size() ) {
     251          10 :       error("all the SEGMENT keywords must contain the same number of atoms but SEGMENT1 contains "
     252             :             + std::to_string(colvar_atoms[0].size()) + " atoms and SEGMENT" + std::to_string(i) + " contains " + std::to_string(newatoms.size()) );
     253             :     }
     254     6175664 :     for(unsigned ii=0; ii<newatoms.size(); ++ii) {
     255     5976432 :       if( newatoms[ii]>=all_atoms.size() ) {
     256          18 :         error("SEGMENT" + std::to_string(i) + " contains index " + std::to_string(newatoms[ii]) + " but only " + std::to_string(all_atoms.size())
     257             :               + " atoms were specified with ATOMS (indices start from zero)");
     258             :       }
     259             :     }
     260      199232 :     if( verbose_output ) {
     261           0 :       log.printf("  Secondary structure segment %u contains atoms : ", static_cast<unsigned>(colvar_atoms.size()+1));
     262           0 :       for(unsigned ii=0; ii<newatoms.size(); ++ii) {
     263           0 :         log.printf("%d ",all_atoms[newatoms[ii]].serial() );
     264             :       }
     265           0 :       log.printf("\n");
     266             :     }
     267      199232 :     colvar_atoms.push_back( newatoms );
     268             :   }
     269          70 :   if( colvar_atoms.size()==0 ) {
     270           0 :     error("did not find any SEGMENT keywords in input");
     271             :   }
     272          70 :   myinput.colvar_atoms.resize( colvar_atoms.size(), colvar_atoms[0].size() );
     273      199300 :   for(unsigned i=0; i<colvar_atoms.size(); ++i) {
     274     6175650 :     for(unsigned j=0; j<colvar_atoms[i].size(); ++j) {
     275     5976420 :       myinput.colvar_atoms[i][j] = colvar_atoms[i][j];
     276             :     }
     277             :   }
     278             : 
     279          34 :   double bondlength=0.0;
     280             :   if constexpr ( needsBondLength ) {
     281          34 :     parse("BONDLENGTH",bondlength);
     282          34 :     bondlength=bondlength/getUnits().getLength();
     283             :   }
     284             :   // Read in the reference structure
     285         144 :   for(unsigned ii=1;; ++ii) {
     286         165 :     std::vector<double> cstruct;
     287         330 :     if( !parseNumberedVector("STRUCTURE",ii,cstruct) ) {
     288             :       break ;
     289             :     }
     290          98 :     if( cstruct.size()%3!=0 || cstruct.size()/3!=colvar_atoms[0].size() ) {
     291          12 :       error("STRUCTURE" + std::to_string(ii) + " contains " + std::to_string(cstruct.size()) + " numbers. It must contain the three coordinates of each of the "
     292             :             + std::to_string(colvar_atoms[0].size()) + " atoms in a segment");
     293             :     }
     294          99 :     std::vector<Vector> structure( cstruct.size()/3 );
     295        2736 :     for(unsigned i=0; i<structure.size(); ++i) {
     296       10560 :       for(unsigned j=0; j<3; ++j) {
     297        7920 :         structure[i][j] = 0.1*cstruct[3*i+j]/getUnits().getLength();
     298             :       }
     299             :     }
     300          96 :     myinput.setReferenceStructure( alignType, bondlength, structure );
     301             :   }
     302             : 
     303             :   // And create values to hold everything
     304          67 :   if( myinput.nstructures==0 ) {
     305           1 :     error("no reference structure was found in input -- use STRUCTURE");
     306             :   }
     307          66 :   if( myinput.align_strands && colvar_atoms[0].size()!=maxAtomsInSegment ) {
     308           4 :     error("ALIGN_STRANDS assumes that the two strands are the first and second halves of a segment containing " + std::to_string(maxAtomsInSegment)
     309             :           + " atoms, but the segments contain " + std::to_string(colvar_atoms[0].size()) + " atoms");
     310             :   }
     311          65 :   std::vector<std::size_t> shape(1);
     312          65 :   shape[0]=colvar_atoms.size();
     313          65 :   if( myinput.nstructures==1 ) {
     314          37 :     addValue( shape );
     315          37 :     setNotPeriodic();
     316             :   } else {
     317             :     std::string num;
     318          84 :     for(unsigned i=0; i<myinput.nstructures; ++i) {
     319          56 :       Tools::convert( i+1, num );
     320          56 :       addComponent( "struct-" + num, shape );
     321         112 :       componentIsNotPeriodic( "struct-" + num );
     322             :     }
     323             :   }
     324         158 :   for(unsigned i=0; i<getNumberOfComponents(); ++i) {
     325          93 :     getPntrToComponent(i)->setDerivativeIsZeroWhenValueIsZero();
     326             :   }
     327          65 :   myinput.nindices_per_task = colvar_atoms[0].size();
     328          65 :   taskmanager.setupParallelTaskManager( 3*colvar_atoms[0].size() + virialSize, 3*getNumberOfAtoms() + 9 );
     329          65 :   taskmanager.setActionInput( myinput );
     330         188 : }
     331             : 
     332             : template <class T,typename myPTM>
     333         534 : void SecondaryStructureBase<T,myPTM>::calculate() {
     334         534 :   taskmanager.runAllTasks();
     335         534 : }
     336             : 
     337             : template <class T,typename myPTM>
     338         534 : void SecondaryStructureBase<T,myPTM>::getAtomicInputData( std::vector<double>& inputdata ) const {
     339         534 :   if( inputdata.size()!=3*getNumberOfAtoms() ) {
     340          63 :     inputdata.resize( 3*getNumberOfAtoms() );
     341             :   }
     342             : 
     343             :   std::size_t k=0;
     344      179646 :   for(unsigned i=0; i<getNumberOfAtoms(); ++i) {
     345      179112 :     Vector mypos( getPosition(i) );
     346      179112 :     inputdata[k] = mypos[0];
     347      179112 :     k++;
     348      179112 :     inputdata[k] = mypos[1];
     349      179112 :     k++;
     350      179112 :     inputdata[k] = mypos[2];
     351      179112 :     k++;
     352             :   }
     353         534 : }
     354             : 
     355             : template <class T,typename myPTM>
     356           0 : void SecondaryStructureBase<T,myPTM>::getAtomicInputData( std::vector<float>& inputdata ) const {
     357           0 :   if( inputdata.size()!=3*getNumberOfAtoms() ) {
     358           0 :     inputdata.resize( 3*getNumberOfAtoms() );
     359             :   }
     360             : 
     361             :   std::size_t k=0;
     362           0 :   for(unsigned i=0; i<getNumberOfAtoms(); ++i) {
     363           0 :     Vector mypos( getPosition(i) );
     364           0 :     inputdata[k] = mypos[0];
     365           0 :     k++;
     366           0 :     inputdata[k] = mypos[1];
     367           0 :     k++;
     368           0 :     inputdata[k] = mypos[2];
     369           0 :     k++;
     370             :   }
     371           0 : }
     372             : 
     373             : template <class T,typename myPTM>
     374      188954 : void SecondaryStructureBase<T,myPTM>::performTask( unsigned task_index, const T& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output ) {
     375             :   // Empty is only used to reach the static shortcut helpers (see the
     376             :   // SecondaryStructureInput alias); this body is never called for it. MSVC
     377             :   // instantiates member bodies eagerly, hence the guard.
     378             :   if constexpr ( !std::is_same_v<T,Empty> ) {
     379             :     // The constructor checks that natoms<=maxAtomsInSegment
     380      188954 :     std::array<Vector,maxAtomsInSegment> pos;
     381             : 
     382     5851886 :     for(unsigned i=0; i<actiondata.natoms; ++i) {
     383     5662932 :       const unsigned atno = actiondata.colvar_atoms(task_index,i);
     384     5662932 :       pos[i][0] = input.inputdata[3*atno+0];
     385     5662932 :       pos[i][1] = input.inputdata[3*atno+1];
     386     5662932 :       pos[i][2] = input.inputdata[3*atno+2];
     387             :     }
     388      188954 :     if( !actiondata.nopbc ) {
     389             :       // Reconstruct the segment by making each pair of consecutive atoms whole. The two strands of a sheet
     390             :       // (atoms 0-14 and atoms 15-29) are made whole separately, because the last atom of the first strand
     391             :       // and the first atom of the second strand are not bonded
     392      188954 :       const unsigned nstrand = actiondata.align_strands ? actiondata.natoms/2 : actiondata.natoms;
     393      565422 :       for(unsigned start=0; start<actiondata.natoms; start+=nstrand) {
     394     5662932 :         for(unsigned i=start; i+1<start+nstrand; ++i) {
     395     5286464 :           const Vector & first (pos[i]);
     396     5286464 :           Vector & second (pos[i+1]);
     397     5286464 :           second=first+input.pbc->distance(first,second);
     398             :         }
     399             :       }
     400             :       // This aligns the two strands if this is required: atom 22 is moved to the periodic image that is
     401             :       // closest to atom 7 and the rest of the second strand follows
     402      188954 :       if( actiondata.align_strands ) {
     403      187514 :         Vector distance=input.pbc->distance( pos[6],pos[21] );
     404             :         distance += pos[6] - pos[21];
     405     3000224 :         for(unsigned i=actiondata.natoms/2; i<actiondata.natoms; ++i) {
     406             :           pos[i]+=distance;
     407             :         }
     408             :       }
     409             :     }
     410             : 
     411             :     // Create a holder for the derivatives. The number of derivatives per component has to be the one that
     412             :     // was given to setupParallelTaskManager (3*natoms+virialSize), and the view must contain just the atoms of
     413             :     // the segment: the size of the buffer is only an upper limit.
     414      188954 :     const unsigned rs = actiondata.nstructures;
     415      188954 :     ColvarOutput rmsd_output( output.values,
     416      188954 :                               3*actiondata.natoms+virialSize,
     417             :                               output.derivatives.data() );
     418             :     // And now calculate the DRMSD
     419      476202 :     for(unsigned i=0; i<rs; ++i) {
     420      287248 :       T::calculateDistance( i, input.noderiv, actiondata, View{pos.data(),actiondata.natoms}, rmsd_output );
     421             :     }
     422             :   }
     423      188954 : }
     424             : 
     425             : template <class T,typename myPTM>
     426         456 : void SecondaryStructureBase<T,myPTM>::applyNonZeroRankForces( std::vector<double>& outforces ) {
     427         456 :   taskmanager.applyForces( outforces );
     428         456 : }
     429             : 
     430             : template <class T,typename myPTM>
     431       92638 : int SecondaryStructureBase<T,myPTM>::getNumberOfValuesPerTask( std::size_t task_index,
     432             :     const T& actiondata ) {
     433       92638 :   return 1;
     434             : }
     435             : 
     436             : template <class T,typename myPTM>
     437       92638 : void SecondaryStructureBase<T,myPTM>::getForceIndices( std::size_t task_index,
     438             :     std::size_t /* colno */,
     439             :     std::size_t ntotal_force,
     440             :     const T& actiondata,
     441             :     const ParallelActionsInput& input,
     442             :     ForceIndexHolder force_indices ) {
     443             :   // Guarded for the same reason as performTask().
     444             :   if constexpr ( !std::is_same_v<T,Empty> ) {
     445      232668 :     for(unsigned i=0; i<input.ncomponents; ++i) {
     446             :       std::size_t m = 0;
     447     4337150 :       for(unsigned j=0; j<actiondata.nindices_per_task; ++j) {
     448     4197120 :         std::size_t base = 3*actiondata.colvar_atoms[task_index][j];
     449     4197120 :         force_indices.indices[i][m] = base + 0;
     450     4197120 :         ++m;
     451     4197120 :         force_indices.indices[i][m] = base + 1;
     452     4197120 :         ++m;
     453     4197120 :         force_indices.indices[i][m] = base + 2;
     454     4197120 :         ++m;
     455             :       }
     456     1400300 :       for(unsigned n=ntotal_force-virialSize; n<ntotal_force; ++n) {
     457     1260270 :         force_indices.indices[i][m] = n;
     458     1260270 :         ++m;
     459             :       }
     460      140030 :       force_indices.threadsafe_derivatives_end[i] = 0;
     461      140030 :       force_indices.tot_indices[i] = m;
     462             :     }
     463             :   }
     464       92638 : }
     465             : 
     466             : }
     467             : }
     468             : 
     469             : #endif

Generated by: LCOV version 1.16