LCOV - code coverage report
Current view: top level - tools - RMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 576 810 71.1 %
Date: 2026-11-05 10:19:04 Functions: 46 82 56.1 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-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 "RMSD.h"
      23             : #include "PDB.h"
      24             : #include "Exception.h"
      25             : #include <cmath>
      26             : #include "Tools.h"
      27             : 
      28             : namespace PLMD {
      29             : 
      30             : // this is ugly, I am sorry
      31             : template <typename T>
      32      780344 : View<T> make_view(const std::vector<T>& d) {
      33      780344 :   return View<T> {const_cast<T*>(d.data()),d.size()};
      34             : }
      35             : 
      36       47141 : RMSD::RMSD() =default;
      37             : 
      38             : ///
      39             : /// general method to set all the rmsd property at once by using a pdb where occupancy column sets the weights for the atoms involved in the
      40             : /// alignment and beta sets the weight that are used for calculating the displacement.
      41             : ///
      42        1191 : void RMSD::set(const PDB&pdb,
      43             :                const std::string & mytype,
      44             :                const bool remove_center,
      45             :                const bool normalize_weights ) {
      46             : 
      47        1191 :   set(pdb.getOccupancy(),
      48             :       pdb.getBeta(),
      49             :       pdb.getPositions(),
      50             :       mytype,
      51             :       remove_center,
      52             :       normalize_weights);
      53             : 
      54        1191 : }
      55       28872 : void RMSD::set(const std::vector<double>& alignTo,
      56             :                const std::vector<double>& displaceFrom,
      57             :                const std::vector<Vector>& ref,
      58             :                const std::string & mytype,
      59             :                const bool remove_center,
      60             :                const bool normalize_weights ) {
      61             : 
      62       28872 :   setReference(ref); // this by default remove the com and assumes uniform weights
      63       28872 :   setAlign(alignTo, normalize_weights, remove_center); // this recalculates the com with weights. If remove_center=false then it restore the center back
      64       28872 :   setDisplace(displaceFrom, normalize_weights);  // this is does not affect any calculation of the weights
      65       28872 :   setType(mytype);
      66             : 
      67       28872 : }
      68             : 
      69       74064 : void RMSD::setType(const std::string & mytype) {
      70             : 
      71       74064 :   alignmentMethod=SIMPLE; // initialize with the simplest case: no rotation
      72       74064 :   if (mytype=="SIMPLE") {
      73             :     alignmentMethod=SIMPLE;
      74       72885 :   } else if (mytype=="OPTIMAL") {
      75       59298 :     alignmentMethod=OPTIMAL;
      76       13587 :   } else if (mytype=="OPTIMAL-FAST") {
      77       13587 :     alignmentMethod=OPTIMAL_FAST;
      78             :   } else {
      79           0 :     plumed_merror("unknown RMSD type" + mytype);
      80             :   }
      81             : 
      82       74064 : }
      83             : 
      84       27684 : void RMSD::clear() {
      85       27684 :   reference.clear();
      86             :   reference_center.zero();
      87       27684 :   reference_center_is_calculated=false;
      88       27684 :   reference_center_is_removed=false;
      89       27684 :   align.clear();
      90       27684 :   displace.clear();
      91             :   positions_center.zero();
      92       27684 :   positions_center_is_calculated=false;
      93       27684 :   positions_center_is_removed=false;
      94       27684 : }
      95             : 
      96          52 : std::string RMSD::getMethod() const {
      97             :   std::string mystring;
      98          52 :   switch(alignmentMethod) {
      99           2 :   case SIMPLE:
     100           2 :     mystring.assign("SIMPLE");
     101             :     break;
     102          32 :   case OPTIMAL:
     103          32 :     mystring.assign("OPTIMAL");
     104             :     break;
     105          18 :   case OPTIMAL_FAST:
     106          18 :     mystring.assign("OPTIMAL-FAST");
     107             :     break;
     108             :   }
     109          52 :   return mystring;
     110             : }
     111             : ///
     112             : /// this calculates the center of mass for the reference and removes it from the reference itself
     113             : /// considering uniform weights for alignment
     114             : ///
     115       74080 : void RMSD::setReference(const std::vector<Vector>& ref) {
     116       74080 :   reference=ref;
     117       74080 :   unsigned n=reference.size();
     118       74080 :   plumed_massert(align.empty(),"you should first clear() an RMSD object, then set a new reference");
     119       74080 :   plumed_massert(displace.empty(),"you should first clear() an RMSD object, then set a new reference");
     120       74080 :   align.resize(n,1.0/n);
     121       74080 :   displace.resize(n,1.0/n);
     122     1063284 :   for(unsigned i=0; i<n; i++) {
     123      989204 :     reference_center+=reference[i]*align[i];
     124             :   }
     125             :   #pragma omp simd
     126             :   for(unsigned i=0; i<n; i++) {
     127      989204 :     reference[i]-=reference_center;
     128             :   }
     129       74080 :   reference_center_is_calculated=true;
     130       74080 :   reference_center_is_removed=true;
     131       74080 : }
     132      216081 : const std::vector<Vector>& RMSD::getReference() const {
     133      216081 :   return reference;
     134             : }
     135             : ///
     136             : /// the alignment weights are here normalized to 1 and  the center of the reference is removed accordingly
     137             : ///
     138       28872 : void RMSD::setAlign(const std::vector<double>& alignTo,
     139             :                     const bool normalize_weights,
     140             :                     const bool remove_center) {
     141       28872 :   const unsigned n=reference.size();
     142             :   //NOTE:QUESTION HERE: I am looping on the n from the reference, but I am checking that alignTo and align have the same size, not that reference and align have the same size!!!!
     143       28872 :   plumed_massert(align.size()==alignTo.size(),"mismatch in dimension of align arrays");
     144       28872 :   align=alignTo;
     145       28872 :   if(normalize_weights) {
     146             :     double w=0.0;
     147       57656 :     #pragma omp simd reduction(+:w)
     148             :     for(unsigned i=0; i<n; i++) {
     149      400968 :       w+=align[i];
     150             :     }
     151       28828 :     if(w>epsilon) {
     152       28826 :       double inv=1.0/w;
     153             :       #pragma omp simd
     154             :       for(unsigned i=0; i<n; i++) {
     155      400938 :         align[i]*=inv;
     156             :       }
     157             :     } else {
     158           2 :       double inv=1.0/n;
     159             :       #pragma omp simd
     160             :       for(unsigned i=0; i<n; i++) {
     161          30 :         align[i]=inv;
     162             :       }
     163             :     }
     164             :   }
     165             :   // recalculate the center anyway
     166             :   // just remove the center if that is asked
     167             :   // if the center was removed before, then add it and store the new one
     168       28872 :   if(reference_center_is_removed) {
     169       28872 :     plumed_massert(reference_center_is_calculated," seems that the reference center has been removed but not calculated and stored!");
     170       28872 :     addCenter(reference,reference_center);
     171             :   }
     172       28872 :   reference_center=calculateCenter(reference,align);
     173       28872 :   reference_center_is_calculated=true;
     174       28872 :   if(remove_center) {
     175       28867 :     removeCenter(reference,reference_center);
     176       28867 :     reference_center_is_removed=true;
     177             :   } else {
     178           5 :     reference_center_is_removed=false;
     179             :   }
     180       28872 : }
     181           0 : std::vector<double> RMSD::getAlign() {
     182           0 :   return align;
     183             : }
     184             : ///
     185             : /// here the weigth for normalized weighths are normalized and set
     186             : ///
     187       28872 : void RMSD::setDisplace(const std::vector<double>& displaceFrom,
     188             :                        const bool normalize_weights) {
     189       28872 :   const unsigned n=reference.size();
     190             :   //NOTE:QUESTION HERE: I am looping on the n from the reference, but I am checking that displaceFrom and displace have the same size, not that reference and displace have the same size!!!!
     191       28872 :   plumed_massert(displace.size()==displaceFrom.size(),"mismatch in dimension of displace arrays");
     192       28872 :   displace=displaceFrom;
     193       28872 :   if(normalize_weights) {
     194             :     double w=0.0;
     195       57656 :     #pragma omp simd reduction(+:w)
     196             :     for(unsigned i=0; i<n; i++) {
     197      400968 :       w+=displace[i];
     198             :     }
     199       28828 :     if(w>epsilon) {
     200       28826 :       double inv=1.0/w;
     201             :       #pragma omp simd
     202             :       for(unsigned i=0; i<n; i++) {
     203      400938 :         displace[i]*=inv;
     204             :       }
     205             :     } else {
     206           2 :       double inv=1.0/n;
     207             :       #pragma omp simd
     208             :       for(unsigned i=0; i<n; i++) {
     209          30 :         displace[i]=inv;
     210             :       }
     211             :     }
     212             :   }
     213       28872 : }
     214           0 : std::vector<double> RMSD::getDisplace() {
     215           0 :   return displace;
     216             : }
     217             : ///
     218             : /// This is the main workhorse for rmsd that decides to use specific optimal alignment versions
     219             : ///
     220      776921 : double RMSD::calculate(const View<Vector> positions,
     221             :                        std::vector<Vector>&derivatives,
     222             :                        const bool squared)const {
     223             : 
     224             :   double ret=0.;
     225             : 
     226      776921 :   switch(alignmentMethod) {
     227             :   case SIMPLE : {
     228             :     //  do a simple alignment without rotation
     229         277 :     std::vector<Vector> displacement( derivatives.size() );
     230         277 :     ret=simpleAlignment(align,displace,positions,reference,derivatives,displacement,squared);
     231             :     break;
     232         277 :   }
     233      333674 :   case OPTIMAL_FAST : {
     234             :     // this is calling the fastest option:
     235      333674 :     if(align==displace) {
     236      333672 :       ret=optimalAlignment<false,true>(align,displace,positions,reference,derivatives,squared);
     237             :     } else {
     238           2 :       ret=optimalAlignment<false,false>(align,displace,positions,reference,derivatives,squared);
     239             :     }
     240             :     break;
     241             : 
     242             :   }
     243      442970 :   case OPTIMAL : {
     244             :     // this is the fast routine but in the "safe" mode, which gives less numerical error:
     245      442970 :     if(align==displace) {
     246      442858 :       ret=optimalAlignment<true,true>(align,displace,positions,reference,derivatives,squared);
     247             :     } else {
     248         112 :       ret=optimalAlignment<true,false>(align,displace,positions,reference,derivatives,squared);
     249             :     }
     250             :     break;
     251             :   }
     252             :   }
     253             : 
     254      776921 :   return ret;
     255             : 
     256             : }
     257             : 
     258      585289 : double RMSD::calculate(const std::vector<Vector>& positions,
     259             :                        std::vector<Vector>&derivatives,
     260             :                        const bool squared)const {
     261      585289 :   return calculate(make_view(positions),derivatives,squared);
     262             : }
     263             : 
     264             : /// convenience method for calculating the standard derivatives and the derivative of the rmsd respect to the reference position
     265           1 : double RMSD::calc_DDistDRef(
     266             :   const std::vector<Vector>& positions,
     267             :   std::vector<Vector>&derivatives,
     268             :   std::vector<Vector>& DDistDRef,
     269             :   const bool squared  ) {
     270             :   double ret=0.;
     271           1 :   switch(alignmentMethod) {
     272           0 :   case SIMPLE:
     273           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     274             :     break;
     275           0 :   case OPTIMAL_FAST:
     276           0 :     if(align==displace) {
     277           0 :       ret=optimalAlignment_DDistDRef<false,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef, squared);
     278             :     } else {
     279           0 :       ret=optimalAlignment_DDistDRef<false,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef,squared);
     280             :     }
     281             :     break;
     282           1 :   case OPTIMAL:
     283           1 :     if(align==displace) {
     284           0 :       ret=optimalAlignment_DDistDRef<true,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef,squared);
     285             :     } else {
     286           1 :       ret=optimalAlignment_DDistDRef<true,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef,squared);
     287             :     }
     288             :     break;
     289             :   }
     290           1 :   return ret;
     291             : 
     292             : }
     293             : 
     294             : /// convenience method for calculating the standard derivatives and the derivative of the rmsd respect to the reference position without the matrix contribution
     295             : /// as required by SOMA
     296           0 : double RMSD::calc_SOMA(const std::vector<Vector>& positions,
     297             :                        std::vector<Vector>&derivatives,
     298             :                        std::vector<Vector>& DDistDRef,
     299             :                        const bool squared) {
     300             :   double ret=0.;
     301           0 :   switch(alignmentMethod) {
     302           0 :   case SIMPLE:
     303           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     304             :     break;
     305           0 :   case OPTIMAL_FAST:
     306           0 :     if(align==displace) {
     307           0 :       ret=optimalAlignment_SOMA<false,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef, squared);
     308             :     } else {
     309           0 :       ret=optimalAlignment_SOMA<false,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef,squared);
     310             :     }
     311             :     break;
     312           0 :   case OPTIMAL:
     313           0 :     if(align==displace) {
     314           0 :       ret=optimalAlignment_SOMA<true,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef,squared);
     315             :     } else {
     316           0 :       ret=optimalAlignment_SOMA<true,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef,squared);
     317             :     }
     318             :     break;
     319             :   }
     320           0 :   return ret;
     321             : 
     322             : }
     323             : 
     324           0 : double RMSD::calc_DDistDRef_Rot_DRotDPos(
     325             :   const std::vector<Vector>& positions,
     326             :   std::vector<Vector>&derivatives,
     327             :   std::vector<Vector>& DDistDRef,
     328             :   Tensor & Rot,
     329             :   Matrix<std::vector<Vector> >&DRotDPos,
     330             :   const bool squared) {
     331             :   double ret=0.;
     332           0 :   switch(alignmentMethod) {
     333           0 :   case SIMPLE:
     334           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     335             :     break;
     336           0 :   case OPTIMAL_FAST:
     337           0 :     if(align==displace) {
     338           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos<false,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos,  squared);
     339             :     } else {
     340           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos<false,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, squared);
     341             :     }
     342             :     break;
     343           0 :   case OPTIMAL:
     344           0 :     if(align==displace) {
     345           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos<true,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, squared);
     346             :     } else {
     347           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos<true,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, squared);
     348             :     }
     349             :     break;
     350             :   }
     351           0 :   return ret;
     352             : }
     353             : 
     354           0 : double RMSD::calc_DDistDRef_Rot_DRotDPos_DRotDRef(
     355             :   const std::vector<Vector>& positions,
     356             :   std::vector<Vector>&derivatives,
     357             :   std::vector<Vector>& DDistDRef,
     358             :   Tensor & Rot,
     359             :   Matrix<std::vector<Vector> >&DRotDPos,
     360             :   Matrix<std::vector<Vector> >&DRotDRef,
     361             :   const bool squared) {
     362             :   double ret=0.;
     363           0 :   switch(alignmentMethod) {
     364           0 :   case SIMPLE:
     365           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     366             :     break;
     367           0 :   case OPTIMAL_FAST:
     368           0 :     if(align==displace) {
     369           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<false,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef,   squared);
     370             :     } else {
     371           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<false,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef,  squared);
     372             :     }
     373             :     break;
     374           0 :   case OPTIMAL:
     375           0 :     if(align==displace) {
     376           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<true,true>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef, squared);
     377             :     } else {
     378           0 :       ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<true,false>(align,displace,make_view(positions),reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef, squared);
     379             :     }
     380             :     break;
     381             :   }
     382           0 :   return ret;
     383             : }
     384             : 
     385        1092 : double RMSD::calc_Rot_DRotDRr01( const std::vector<Vector>& positions,
     386             :                                  Tensor & Rotation,
     387             :                                  std::array<std::array<Tensor,3>,3>& DRotDRr01,
     388             :                                  const bool squared) {
     389             :   double ret=0.;
     390        1092 :   switch(alignmentMethod) {
     391           0 :   case SIMPLE:
     392           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     393             :     break;
     394           0 :   case OPTIMAL_FAST:
     395           0 :     if(align==displace) {
     396           0 :       ret=optimalAlignment_Rot_DRotDRr01<false,true>(align,displace,make_view(positions),reference, Rotation, DRotDRr01,   squared);
     397             :     } else {
     398           0 :       ret=optimalAlignment_Rot_DRotDRr01<false,false>(align,displace,make_view(positions),reference, Rotation, DRotDRr01,  squared);
     399             :     }
     400             :     break;
     401        1092 :   case OPTIMAL:
     402        1092 :     if(align==displace) {
     403        1092 :       ret=optimalAlignment_Rot_DRotDRr01<true,true>(align,displace,make_view(positions),reference, Rotation, DRotDRr01, squared);
     404             :     } else {
     405           0 :       ret=optimalAlignment_Rot_DRotDRr01<true,false>(align,displace,make_view(positions),reference, Rotation, DRotDRr01, squared);
     406             :     }
     407             :     break;
     408             :   }
     409        1092 :   return ret;
     410             : }
     411             : 
     412         672 : double RMSD::calc_Rot( const std::vector<Vector>& positions,
     413             :                        std::vector<Vector>&derivatives,
     414             :                        Tensor & Rotation,
     415             :                        const bool squared) {
     416             :   double ret=0.;
     417         672 :   switch(alignmentMethod) {
     418           0 :   case SIMPLE:
     419           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     420             :     break;
     421           0 :   case OPTIMAL_FAST:
     422           0 :     if(align==displace) {
     423           0 :       ret=optimalAlignment_Rot<false,true>(align,displace,make_view(positions),reference,derivatives, Rotation, squared);
     424             :     } else {
     425           0 :       ret=optimalAlignment_Rot<false,false>(align,displace,make_view(positions),reference,derivatives, Rotation, squared);
     426             :     }
     427             :     break;
     428         672 :   case OPTIMAL:
     429         672 :     if(align==displace) {
     430         672 :       ret=optimalAlignment_Rot<true,true>(align,displace,make_view(positions),reference,derivatives, Rotation, squared);
     431             :     } else {
     432           0 :       ret=optimalAlignment_Rot<true,false>(align,displace,make_view(positions),reference,derivatives, Rotation, squared);
     433             :     }
     434             :     break;
     435             :   }
     436         672 :   return ret;
     437             : }
     438             : 
     439       45192 : double RMSD::calculateWithCloseStructure( const std::vector<Vector>& positions,
     440             :     std::vector<Vector>&derivatives,
     441             :     const Tensor & rotationPosClose,
     442             :     const Tensor & rotationRefClose,
     443             :     std::array<std::array<Tensor,3>,3>& drotationPosCloseDrr01,
     444             :     const bool squared) {
     445             :   double ret=0.;
     446       45192 :   switch(alignmentMethod) {
     447           0 :   case SIMPLE:
     448           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     449             :     break;
     450           0 :   case OPTIMAL_FAST:
     451           0 :     if(align==displace) {
     452           0 :       ret=optimalAlignmentWithCloseStructure<false,true>(align,displace,make_view(positions),reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     453             :     } else {
     454           0 :       ret=optimalAlignmentWithCloseStructure<false,false>(align,displace,make_view(positions),reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     455             :     }
     456             :     break;
     457       45192 :   case OPTIMAL:
     458       45192 :     if(align==displace) {
     459       45192 :       ret=optimalAlignmentWithCloseStructure<true,true>(align,displace,make_view(positions),reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     460             :     } else {
     461           0 :       ret=optimalAlignmentWithCloseStructure<true,false>(align,displace,make_view(positions),reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     462             :     }
     463             :     break;
     464             :   }
     465       45192 :   return ret;
     466             : }
     467             : 
     468      147888 : double RMSD::calc_PCAelements(const std::vector<Vector>& positions,
     469             :                               std::vector<Vector>&DDistDPos,
     470             :                               Tensor & Rotation,
     471             :                               Matrix<std::vector<Vector> >& DRotDPos,std::vector<Vector>& alignedpositions,
     472             :                               std::vector<Vector>& centeredpositions,
     473             :                               std::vector<Vector>&centeredreference,
     474             :                               const bool squared) const {
     475             :   double ret=0.;
     476      147888 :   switch(alignmentMethod) {
     477           0 :   case SIMPLE:
     478           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     479             :     break;
     480      123667 :   case OPTIMAL_FAST:
     481      123667 :     if(align==displace) {
     482      123667 :       ret=optimalAlignment_PCA<false,true>(align,displace,make_view(positions),reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     483             :     } else {
     484           0 :       ret=optimalAlignment_PCA<false,false>(align,displace,make_view(positions),reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     485             :     }
     486             :     break;
     487       24221 :   case OPTIMAL:
     488       24221 :     if(align==displace) {
     489       24182 :       ret=optimalAlignment_PCA<true,true>(align,displace,make_view(positions),reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     490             :     } else {
     491          39 :       ret=optimalAlignment_PCA<true,false>(align,displace,make_view(positions),reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     492             :     }
     493             :     break;
     494             :   }
     495      147888 :   return ret;
     496             : }
     497             : 
     498             : 
     499         210 : double RMSD::calc_FitElements(const std::vector<Vector>& positions,
     500             :                               Tensor & Rotation,
     501             :                               Matrix<std::vector<Vector> >& DRotDPos,
     502             :                               std::vector<Vector>& centeredpositions,
     503             :                               Vector &center_positions,
     504             :                               const bool squared) {
     505             :   double ret=0.;
     506         210 :   switch(alignmentMethod) {
     507           0 :   case SIMPLE:
     508           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     509             :     break;
     510           0 :   case OPTIMAL_FAST:
     511           0 :     if(align==displace) {
     512           0 :       ret=optimalAlignment_Fit<false,true>(align,displace,make_view(positions),reference, Rotation,DRotDPos,centeredpositions,center_positions,squared);
     513             :     } else {
     514           0 :       ret=optimalAlignment_Fit<false,false>(align,displace,make_view(positions),reference, Rotation,DRotDPos,centeredpositions,center_positions,squared);
     515             :     }
     516             :     break;
     517         210 :   case OPTIMAL:
     518         210 :     if(align==displace) {
     519         150 :       ret=optimalAlignment_Fit<true,true>(align,displace,make_view(positions),reference,Rotation,DRotDPos,centeredpositions,center_positions,squared);
     520             :     } else {
     521          60 :       ret=optimalAlignment_Fit<true,false>(align,displace,make_view(positions),reference,Rotation,DRotDPos,centeredpositions,center_positions,squared);
     522             :     }
     523             :     break;
     524             :   }
     525         210 :   return ret;
     526             : }
     527             : 
     528       47846 : double RMSD::simpleAlignment(const std::vector<double>& alignTo,
     529             :                              const std::vector<double>& displaceFrom,
     530             :                              const View<Vector> positions,
     531             :                              const std::vector<Vector>& ref,
     532             :                              std::vector<Vector>& derivatives,
     533             :                              std::vector<Vector>& displacement,
     534             :                              const bool squared)const {
     535             : 
     536             :   double dist(0);
     537       47846 :   unsigned n=ref.size();
     538             : 
     539             :   Vector apositions;
     540             :   Vector areference;
     541             :   Vector dpositions;
     542             :   Vector dreference;
     543             : 
     544      669651 :   for(unsigned i=0; i<n; i++) {
     545      621805 :     double aw=alignTo[i];
     546      621805 :     double dw=displaceFrom[i];
     547             :     apositions+=positions[i]*aw;
     548             :     areference+=ref[i]*aw;
     549             :     dpositions+=positions[i]*dw;
     550             :     dreference+=ref[i]*dw;
     551             :   }
     552             : 
     553             :   Vector shift=((apositions-areference)-(dpositions-dreference));
     554      669651 :   for(unsigned i=0; i<n; i++) {
     555      621805 :     displacement[i]=(positions[i]-apositions)-(ref[i]-areference);
     556      621805 :     dist+=displaceFrom[i]*displacement[i].modulo2();
     557      621805 :     derivatives[i]=2*(displaceFrom[i]*displacement[i]+alignTo[i]*shift);
     558             :   }
     559             : 
     560       47846 :   if(!squared) {
     561             :     // sqrt
     562        1382 :     dist=std::sqrt(dist);
     563             :     ///// sqrt on derivatives
     564       20097 :     for(unsigned i=0; i<n; i++) {
     565       18715 :       derivatives[i]*=(0.5/dist);
     566             :     }
     567             :   }
     568       47846 :   return dist;
     569             : }
     570             : 
     571             : // this below enable the standard case for rmsd where the rmsd is calculated and the derivative of rmsd respect to positions is retrieved
     572             : // additionally this assumes that the com of the reference is already subtracted.
     573             : #define OLDRMSD
     574             : #ifdef OLDRMSD
     575             : // notice that in the current implementation the safe argument only makes sense for
     576             : // align==displace
     577             : template <bool safe,bool alEqDis>
     578      776644 : double RMSD::optimalAlignment(const std::vector<double>& alignTo,
     579             :                               const std::vector<double>& displaceFrom,
     580             :                               const View<Vector> positions,
     581             :                               const std::vector<Vector>& ref,
     582             :                               std::vector<Vector>& derivatives, const bool squared)const {
     583      776644 :   const unsigned n=ref.size();
     584             : // This is the trace of positions*positions + ref*reference
     585             :   double rr00(0);
     586             :   double rr11(0);
     587             : // This is positions*ref
     588      776644 :   Tensor rr01;
     589             : 
     590      776644 :   derivatives.resize(n);
     591             : 
     592             :   Vector cpositions;
     593             : 
     594             : // first expensive loop: compute centers
     595    28165818 :   for(unsigned iat=0; iat<n; iat++) {
     596    27389174 :     double w=alignTo[iat];
     597             :     cpositions+=positions[iat]*w;
     598             :   }
     599             : 
     600             : // second expensive loop: compute second moments wrt centers
     601    28165818 :   for(unsigned iat=0; iat<n; iat++) {
     602    27389174 :     double w=alignTo[iat];
     603    27386998 :     rr00+=dotProduct(positions[iat]-cpositions,positions[iat]-cpositions)*w;
     604    27386998 :     rr11+=dotProduct(ref[iat],reference[iat])*w;
     605    54778348 :     rr01+=Tensor(positions[iat]-cpositions,ref[iat])*w;
     606             :   }
     607             : 
     608             :   Tensor4d m;
     609             : 
     610      776644 :   m[0][0]=2.0*(-rr01[0][0]-rr01[1][1]-rr01[2][2]);
     611      776644 :   m[1][1]=2.0*(-rr01[0][0]+rr01[1][1]+rr01[2][2]);
     612      776644 :   m[2][2]=2.0*(+rr01[0][0]-rr01[1][1]+rr01[2][2]);
     613      776644 :   m[3][3]=2.0*(+rr01[0][0]+rr01[1][1]-rr01[2][2]);
     614      776644 :   m[0][1]=2.0*(-rr01[1][2]+rr01[2][1]);
     615      776644 :   m[0][2]=2.0*(+rr01[0][2]-rr01[2][0]);
     616      776644 :   m[0][3]=2.0*(-rr01[0][1]+rr01[1][0]);
     617      776644 :   m[1][2]=2.0*(-rr01[0][1]-rr01[1][0]);
     618      776644 :   m[1][3]=2.0*(-rr01[0][2]-rr01[2][0]);
     619      776644 :   m[2][3]=2.0*(-rr01[1][2]-rr01[2][1]);
     620      776644 :   m[1][0] = m[0][1];
     621      776644 :   m[2][0] = m[0][2];
     622      776644 :   m[2][1] = m[1][2];
     623      776644 :   m[3][0] = m[0][3];
     624      776644 :   m[3][1] = m[1][3];
     625      776644 :   m[3][2] = m[2][3];
     626             : 
     627         114 :   Tensor dm_drr01[4][4];
     628             :   if(!alEqDis) {
     629         114 :     dm_drr01[0][0] = 2.0*Tensor(-1.0, 0.0, 0.0,  0.0,-1.0, 0.0,  0.0, 0.0,-1.0);
     630         114 :     dm_drr01[1][1] = 2.0*Tensor(-1.0, 0.0, 0.0,  0.0,+1.0, 0.0,  0.0, 0.0,+1.0);
     631         114 :     dm_drr01[2][2] = 2.0*Tensor(+1.0, 0.0, 0.0,  0.0,-1.0, 0.0,  0.0, 0.0,+1.0);
     632         114 :     dm_drr01[3][3] = 2.0*Tensor(+1.0, 0.0, 0.0,  0.0,+1.0, 0.0,  0.0, 0.0,-1.0);
     633         114 :     dm_drr01[0][1] = 2.0*Tensor( 0.0, 0.0, 0.0,  0.0, 0.0,-1.0,  0.0,+1.0, 0.0);
     634         114 :     dm_drr01[0][2] = 2.0*Tensor( 0.0, 0.0,+1.0,  0.0, 0.0, 0.0, -1.0, 0.0, 0.0);
     635         114 :     dm_drr01[0][3] = 2.0*Tensor( 0.0,-1.0, 0.0, +1.0, 0.0, 0.0,  0.0, 0.0, 0.0);
     636         114 :     dm_drr01[1][2] = 2.0*Tensor( 0.0,-1.0, 0.0, -1.0, 0.0, 0.0,  0.0, 0.0, 0.0);
     637         114 :     dm_drr01[1][3] = 2.0*Tensor( 0.0, 0.0,-1.0,  0.0, 0.0, 0.0, -1.0, 0.0, 0.0);
     638         114 :     dm_drr01[2][3] = 2.0*Tensor( 0.0, 0.0, 0.0,  0.0, 0.0,-1.0,  0.0,-1.0, 0.0);
     639         114 :     dm_drr01[1][0] = dm_drr01[0][1];
     640         114 :     dm_drr01[2][0] = dm_drr01[0][2];
     641         114 :     dm_drr01[2][1] = dm_drr01[1][2];
     642         114 :     dm_drr01[3][0] = dm_drr01[0][3];
     643         114 :     dm_drr01[3][1] = dm_drr01[1][3];
     644         114 :     dm_drr01[3][2] = dm_drr01[2][3];
     645             :   }
     646             : 
     647             :   double dist=0.0;
     648      776530 :   Vector4d q;
     649             : 
     650         114 :   Tensor dq_drr01[4];
     651             :   if(!alEqDis) {
     652         114 :     Vector4d eigenvals;
     653         114 :     Tensor4d eigenvecs;
     654         114 :     diagMatSym(m, eigenvals, eigenvecs );
     655         114 :     dist=eigenvals[0]+rr00+rr11;
     656         114 :     q=Vector4d(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]);
     657             :     double dq_dm[4][4][4];
     658         570 :     for(unsigned i=0; i<4; i++)
     659        2280 :       for(unsigned j=0; j<4; j++)
     660        9120 :         for(unsigned k=0; k<4; k++) {
     661             :           double tmp=0.0;
     662             : // perturbation theory for matrix m
     663       29184 :           for(unsigned l=1; l<4; l++) {
     664       21888 :             tmp+=eigenvecs[l][j]*eigenvecs[l][i]/(eigenvals[0]-eigenvals[l])*eigenvecs[0][k];
     665             :           }
     666        7296 :           dq_dm[i][j][k]=tmp;
     667             :         }
     668             : // propagation to _drr01
     669         570 :     for(unsigned i=0; i<4; i++) {
     670         456 :       Tensor tmp;
     671        2280 :       for(unsigned j=0; j<4; j++)
     672        9120 :         for(unsigned k=0; k<4; k++) {
     673       14592 :           tmp+=dq_dm[i][j][k]*dm_drr01[j][k];
     674             :         }
     675         456 :       dq_drr01[i]=tmp;
     676             :     }
     677             :   } else {
     678      776530 :     dist = lowestEigenpairSym(m, q) + rr00 + rr11;
     679             :   }
     680             : 
     681             : 
     682             : // This is the rotation matrix that brings ref to positions
     683             : // i.e. matmul(rotation,ref[iat])+shift is fitted to positions[iat]
     684             : 
     685             :   Tensor rotation;
     686      776644 :   rotation[0][0]=q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3];
     687      776644 :   rotation[1][1]=q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3];
     688      776644 :   rotation[2][2]=q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3];
     689      776644 :   rotation[0][1]=2*(+q[0]*q[3]+q[1]*q[2]);
     690      776644 :   rotation[0][2]=2*(-q[0]*q[2]+q[1]*q[3]);
     691      776644 :   rotation[1][2]=2*(+q[0]*q[1]+q[2]*q[3]);
     692      776644 :   rotation[1][0]=2*(-q[0]*q[3]+q[1]*q[2]);
     693      776644 :   rotation[2][0]=2*(+q[0]*q[2]+q[1]*q[3]);
     694      776644 :   rotation[2][1]=2*(-q[0]*q[1]+q[2]*q[3]);
     695             : 
     696             : 
     697         114 :   std::array<std::array<Tensor,3>,3> drotation_drr01;
     698             :   if(!alEqDis) {
     699         228 :     drotation_drr01[0][0]=2*q[0]*dq_drr01[0]+2*q[1]*dq_drr01[1]-2*q[2]*dq_drr01[2]-2*q[3]*dq_drr01[3];
     700         114 :     drotation_drr01[1][1]=2*q[0]*dq_drr01[0]-2*q[1]*dq_drr01[1]+2*q[2]*dq_drr01[2]-2*q[3]*dq_drr01[3];
     701         114 :     drotation_drr01[2][2]=2*q[0]*dq_drr01[0]-2*q[1]*dq_drr01[1]-2*q[2]*dq_drr01[2]+2*q[3]*dq_drr01[3];
     702         114 :     drotation_drr01[0][1]=2*(+(q[0]*dq_drr01[3]+dq_drr01[0]*q[3])+(q[1]*dq_drr01[2]+dq_drr01[1]*q[2]));
     703         114 :     drotation_drr01[0][2]=2*(-(q[0]*dq_drr01[2]+dq_drr01[0]*q[2])+(q[1]*dq_drr01[3]+dq_drr01[1]*q[3]));
     704         114 :     drotation_drr01[1][2]=2*(+(q[0]*dq_drr01[1]+dq_drr01[0]*q[1])+(q[2]*dq_drr01[3]+dq_drr01[2]*q[3]));
     705         114 :     drotation_drr01[1][0]=2*(-(q[0]*dq_drr01[3]+dq_drr01[0]*q[3])+(q[1]*dq_drr01[2]+dq_drr01[1]*q[2]));
     706         114 :     drotation_drr01[2][0]=2*(+(q[0]*dq_drr01[2]+dq_drr01[0]*q[2])+(q[1]*dq_drr01[3]+dq_drr01[1]*q[3]));
     707         114 :     drotation_drr01[2][1]=2*(-(q[0]*dq_drr01[1]+dq_drr01[0]*q[1])+(q[2]*dq_drr01[3]+dq_drr01[2]*q[3]));
     708             :   }
     709             : 
     710             :   double prefactor=2.0;
     711             : 
     712      776530 :   if(!squared && alEqDis) {
     713      233414 :     prefactor*=0.5/std::sqrt(dist);
     714             :   }
     715             : 
     716             : // if "safe", recompute dist here to a better accuracy
     717             :   if(safe || !alEqDis) {
     718             :     dist=0.0;
     719             :   }
     720             : 
     721             : // If safe is set to "false", MSD is taken from the eigenvalue of the M matrix
     722             : // If safe is set to "true", MSD is recomputed from the rotational matrix
     723             : // For some reason, this last approach leads to less numerical noise but adds an overhead
     724             : 
     725         114 :   Tensor ddist_drotation;
     726             :   Vector ddist_dcpositions;
     727             : 
     728             : // third expensive loop: derivatives
     729    28165818 :   for(unsigned iat=0; iat<n; iat++) {
     730    27389174 :     Vector d(positions[iat]-cpositions - matmul(rotation,ref[iat]));
     731             :     if(alEqDis) {
     732             : // there is no need for derivatives of rotation and shift here as it is by construction zero
     733             : // (similar to Hellman-Feynman forces)
     734    27386998 :       derivatives[iat]= prefactor*alignTo[iat]*d;
     735             :       if(safe) {
     736     7454522 :         dist+=alignTo[iat]*modulo2(d);
     737             :       }
     738             :     } else {
     739             : // the case for alignTo != displaceFrom is different, sob:
     740        2176 :       dist+=displaceFrom[iat]*modulo2(d);
     741             : // these are the derivatives assuming the roto-translation as frozen
     742        2176 :       derivatives[iat]=2*displaceFrom[iat]*d;
     743             : // here I accumulate derivatives wrt rotation matrix ..
     744        4352 :       ddist_drotation+=-2*displaceFrom[iat]*extProduct(d,ref[iat]);
     745             : // .. and cpositions
     746             :       ddist_dcpositions+=-2*displaceFrom[iat]*d;
     747             :     }
     748             :   }
     749             : 
     750             :   if(!alEqDis) {
     751         114 :     Tensor ddist_drr01;
     752         456 :     for(unsigned i=0; i<3; i++)
     753        1368 :       for(unsigned j=0; j<3; j++) {
     754        2052 :         ddist_drr01+=ddist_drotation[i][j]*drotation_drr01[i][j];
     755             :       }
     756        2290 :     for(unsigned iat=0; iat<n; iat++) {
     757             : // this is propagating to positions.
     758             : // I am implicitly using the derivative of rr01 wrt positions here
     759        2176 :       derivatives[iat]+=matmul(ddist_drr01,ref[iat])*alignTo[iat];
     760        2176 :       derivatives[iat]+=ddist_dcpositions*alignTo[iat];
     761             :     }
     762             :   }
     763      776644 :   if(!squared) {
     764      233528 :     dist=std::sqrt(dist);
     765             :     if(!alEqDis) {
     766         114 :       double xx=0.5/dist;
     767        2290 :       for(unsigned iat=0; iat<n; iat++) {
     768        2176 :         derivatives[iat]*=xx;
     769             :       }
     770             :     }
     771             :   }
     772             : 
     773      776644 :   return dist;
     774             : }
     775             : #else
     776             : /// note that this method is intended to be repeatedly invoked
     777             : /// when the reference does already have the center subtracted
     778             : /// but the position has not calculated center and not subtracted
     779             : template <bool safe,bool alEqDis>
     780             : double RMSD::optimalAlignment(const std::vector<double>& align,
     781             :                               const std::vector<double>& displace,
     782             :                               const View<Vector> positions,
     783             :                               const std::vector<Vector>& reference,
     784             :                               std::vector<Vector>& derivatives,
     785             :                               const bool squared) const {
     786             :   RMSDCoreData cd(align,displace,make_view(positions),reference);
     787             : 
     788             :   // transfer the settings for the center to let the CoreCalc deal with it
     789             :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     790             :   if(positions_center_is_calculated) {
     791             :     cd.setPositionsCenter(positions_center);
     792             :   } else {
     793             :     cd.calcPositionsCenter();
     794             :   };
     795             : 
     796             :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     797             :   if(!reference_center_is_calculated) {
     798             :     cd.calcReferenceCenter();
     799             :   } else {
     800             :     cd.setReferenceCenter(reference_center);
     801             :   }
     802             : 
     803             :   // Perform the diagonalization and all the needed stuff
     804             :   cd.doCoreCalc(safe,alEqDis);
     805             :   // make the core calc distance
     806             :   double dist=cd.getDistance(squared);
     807             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     808             :   derivatives=cd.getDDistanceDPositions();
     809             :   return dist;
     810             : }
     811             : #endif
     812             : template <bool safe,bool alEqDis>
     813           1 : double RMSD::optimalAlignment_DDistDRef(const std::vector<double>& alignTo,
     814             :                                         const std::vector<double>& displaceFrom,
     815             :                                         const View<Vector> positions,
     816             :                                         const std::vector<Vector>& ref,
     817             :                                         std::vector<Vector>& derivatives,
     818             :                                         std::vector<Vector>& ddistdref,
     819             :                                         const bool squared) const {
     820             :   //initialize the data into the structure
     821             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     822           1 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
     823             :   // transfer the settings for the center to let the CoreCalc deal with it
     824             :   // transfer the settings for the center to let the CoreCalc deal with it
     825           1 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     826           1 :   if(positions_center_is_calculated) {
     827           0 :     cd.setPositionsCenter(positions_center);
     828             :   } else {
     829           1 :     cd.calcPositionsCenter();
     830             :   };
     831             : 
     832           1 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     833           1 :   if(!reference_center_is_calculated) {
     834           0 :     cd.calcReferenceCenter();
     835             :   } else {
     836           1 :     cd.setReferenceCenter(reference_center);
     837             :   }
     838             : 
     839             :   // Perform the diagonalization and all the needed stuff
     840           1 :   cd.doCoreCalc(safe,alEqDis);
     841             :   // make the core calc distance
     842           1 :   double dist=cd.getDistance(squared);
     843             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     844           2 :   derivatives=cd.getDDistanceDPositions();
     845           2 :   ddistdref=cd.getDDistanceDReference();
     846           1 :   return dist;
     847             : }
     848             : 
     849             : template <bool safe,bool alEqDis>
     850           0 : double RMSD::optimalAlignment_SOMA(const std::vector<double>& alignTo,
     851             :                                    const std::vector<double>& displaceFrom,
     852             :                                    const View<Vector> positions,
     853             :                                    const std::vector<Vector>& ref,
     854             :                                    std::vector<Vector>& derivatives,
     855             :                                    std::vector<Vector>& ddistdref,
     856             :                                    const bool squared) const {
     857             :   //initialize the data into the structure
     858             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     859           0 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
     860             :   // transfer the settings for the center to let the CoreCalc deal with it
     861             :   // transfer the settings for the center to let the CoreCalc deal with it
     862           0 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     863           0 :   if(positions_center_is_calculated) {
     864           0 :     cd.setPositionsCenter(positions_center);
     865             :   } else {
     866           0 :     cd.calcPositionsCenter();
     867             :   };
     868             : 
     869           0 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     870           0 :   if(!reference_center_is_calculated) {
     871           0 :     cd.calcReferenceCenter();
     872             :   } else {
     873           0 :     cd.setReferenceCenter(reference_center);
     874             :   }
     875             : 
     876             :   // Perform the diagonalization and all the needed stuff
     877           0 :   cd.doCoreCalc(safe,alEqDis);
     878             :   // make the core calc distance
     879           0 :   double dist=cd.getDistance(squared);
     880             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     881           0 :   derivatives=cd.getDDistanceDPositions();
     882           0 :   ddistdref=cd.getDDistanceDReferenceSOMA();
     883           0 :   return dist;
     884             : }
     885             : 
     886             : 
     887             : template <bool safe,bool alEqDis>
     888           0 : double RMSD::optimalAlignment_DDistDRef_Rot_DRotDPos(
     889             :   const std::vector<double>& alignTo,
     890             :   const std::vector<double>& displaceFrom,
     891             :   const View<Vector> positions,
     892             :   const std::vector<Vector>& ref,
     893             :   std::vector<Vector>& derivatives,
     894             :   std::vector<Vector>& ddistdref,
     895             :   Tensor & Rotation,
     896             :   Matrix<std::vector<Vector> >&DRotDPos,
     897             :   const bool squared) const {
     898             :   //initialize the data into the structure
     899             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     900           0 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
     901             :   // transfer the settings for the center to let the CoreCalc deal with it
     902             :   // transfer the settings for the center to let the CoreCalc deal with it
     903           0 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     904           0 :   if(positions_center_is_calculated) {
     905           0 :     cd.setPositionsCenter(positions_center);
     906             :   } else {
     907           0 :     cd.calcPositionsCenter();
     908             :   };
     909             : 
     910           0 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     911           0 :   if(!reference_center_is_calculated) {
     912           0 :     cd.calcReferenceCenter();
     913             :   } else {
     914           0 :     cd.setReferenceCenter(reference_center);
     915             :   }
     916             : 
     917             :   // Perform the diagonalization and all the needed stuff
     918           0 :   cd.doCoreCalc(safe,alEqDis);
     919             :   // make the core calc distance
     920           0 :   double dist=cd.getDistance(squared);
     921             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     922           0 :   derivatives=cd.getDDistanceDPositions();
     923           0 :   ddistdref=cd.getDDistanceDReference();
     924             :   // get the rotation matrix
     925           0 :   Rotation=cd.getRotationMatrixReferenceToPositions();
     926             :   // get its derivative
     927           0 :   DRotDPos=cd.getDRotationDPositions();
     928           0 :   return dist;
     929             : }
     930             : 
     931             : template <bool safe,bool alEqDis>
     932           0 : double RMSD::optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef(
     933             :   const std::vector<double>& alignTo,
     934             :   const std::vector<double>& displaceFrom,
     935             :   const View<Vector> positions,
     936             :   const std::vector<Vector>& ref,
     937             :   std::vector<Vector>& derivatives,
     938             :   std::vector<Vector>& ddistdref,
     939             :   Tensor & Rotation,
     940             :   Matrix<std::vector<Vector> >&DRotDPos,
     941             :   Matrix<std::vector<Vector> >&DRotDRef,
     942             :   const bool squared) const {
     943             :   //initialize the data into the structure
     944             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     945           0 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
     946             :   // transfer the settings for the center to let the CoreCalc deal with it
     947             :   // transfer the settings for the center to let the CoreCalc deal with it
     948           0 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     949           0 :   if(positions_center_is_calculated) {
     950           0 :     cd.setPositionsCenter(positions_center);
     951             :   } else {
     952           0 :     cd.calcPositionsCenter();
     953             :   };
     954             : 
     955           0 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     956           0 :   if(!reference_center_is_calculated) {
     957           0 :     cd.calcReferenceCenter();
     958             :   } else {
     959           0 :     cd.setReferenceCenter(reference_center);
     960             :   }
     961             : 
     962             :   // Perform the diagonalization and all the needed stuff
     963           0 :   cd.doCoreCalc(safe,alEqDis);
     964             :   // make the core calc distance
     965           0 :   double dist=cd.getDistance(squared);
     966             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     967           0 :   derivatives=cd.getDDistanceDPositions();
     968           0 :   ddistdref=cd.getDDistanceDReference();
     969             :   // get the rotation matrix
     970           0 :   Rotation=cd.getRotationMatrixReferenceToPositions();
     971             :   // get its derivative
     972           0 :   DRotDPos=cd.getDRotationDPositions();
     973           0 :   DRotDRef=cd.getDRotationDReference();
     974           0 :   return dist;
     975             : }
     976             : 
     977             : template <bool safe,bool alEqDis>
     978        1092 : double RMSD::optimalAlignment_Rot_DRotDRr01(const std::vector<double>& alignTo,
     979             :     const std::vector<double>& displaceFrom,
     980             :     const View<Vector> positions,
     981             :     const std::vector<Vector>& ref,
     982             :     Tensor & Rotation,
     983             :     std::array<std::array<Tensor,3>,3>& DRotDRr01,
     984             :     const bool squared) const {
     985             :   //initialize the data into the structure
     986             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     987        1092 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
     988             :   // transfer the settings for the center to let the CoreCalc deal with it
     989        1092 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     990        1092 :   if(positions_center_is_calculated) {
     991           0 :     cd.setPositionsCenter(positions_center);
     992             :   } else {
     993        1092 :     cd.calcPositionsCenter();
     994             :   };
     995             : 
     996        1092 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     997        1092 :   if(!reference_center_is_calculated) {
     998           0 :     cd.calcReferenceCenter();
     999             :   } else {
    1000        1092 :     cd.setReferenceCenter(reference_center);
    1001             :   }
    1002             : 
    1003             :   // Perform the diagonalization and all the needed stuff
    1004        1092 :   cd.doCoreCalc(safe,alEqDis);
    1005             :   // make the core calc distance
    1006        1092 :   double dist=cd.getDistance(squared);
    1007             :   // get the rotation matrix
    1008        1092 :   Rotation=cd.getRotationMatrixReferenceToPositions();
    1009             :   //get detivative w.r.t. rr01
    1010        1092 :   DRotDRr01=cd.getDRotationDRr01();
    1011        1092 :   return dist;
    1012             : }
    1013             : 
    1014             : template <bool safe,bool alEqDis>
    1015         672 : double RMSD::optimalAlignment_Rot(const std::vector<double>& alignTo,
    1016             :                                   const std::vector<double>& displaceFrom,
    1017             :                                   const View<Vector> positions,
    1018             :                                   const std::vector<Vector>& ref,
    1019             :                                   std::vector<Vector>& derivatives,
    1020             :                                   Tensor & Rotation,
    1021             :                                   const bool squared) const {
    1022             :   //initialize the data into the structure
    1023             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
    1024         672 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
    1025             :   // transfer the settings for the center to let the CoreCalc deal with it
    1026         672 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
    1027         672 :   if(positions_center_is_calculated) {
    1028           0 :     cd.setPositionsCenter(positions_center);
    1029             :   } else {
    1030         672 :     cd.calcPositionsCenter();
    1031             :   };
    1032             : 
    1033         672 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
    1034         672 :   if(!reference_center_is_calculated) {
    1035           0 :     cd.calcReferenceCenter();
    1036             :   } else {
    1037         672 :     cd.setReferenceCenter(reference_center);
    1038             :   }
    1039             : 
    1040             :   // Perform the diagonalization and all the needed stuff
    1041         672 :   cd.doCoreCalc(safe,alEqDis);
    1042             :   // make the core calc distance
    1043         672 :   double dist=cd.getDistance(squared);
    1044             :   //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
    1045        1344 :   derivatives=cd.getDDistanceDPositions();
    1046             :   // get the rotation matrix
    1047         672 :   Rotation=cd.getRotationMatrixReferenceToPositions();
    1048         672 :   return dist;
    1049             : }
    1050             : 
    1051             : template <bool safe,bool alEqDis>
    1052       45192 : double RMSD::optimalAlignmentWithCloseStructure(const std::vector<double>& alignTo,
    1053             :     const std::vector<double>& displaceFrom,
    1054             :     const View<Vector> positions,
    1055             :     const std::vector<Vector>& ref,
    1056             :     std::vector<Vector>& derivatives,
    1057             :     const Tensor & rotationPosClose,
    1058             :     const Tensor & rotationRefClose,
    1059             :     std::array<std::array<Tensor,3>,3>& drotationPosCloseDrr01,
    1060             :     const bool squared) const {
    1061             :   //initialize the data into the structure
    1062             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
    1063       45192 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
    1064             :   // transfer the settings for the center to let the CoreCalc deal with it
    1065       45192 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
    1066       45192 :   if(positions_center_is_calculated) {
    1067           0 :     cd.setPositionsCenter(positions_center);
    1068             :   } else {
    1069       45192 :     cd.calcPositionsCenter();
    1070             :   };
    1071             : 
    1072       45192 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
    1073       45192 :   if(!reference_center_is_calculated) {
    1074           0 :     cd.calcReferenceCenter();
    1075             :   } else {
    1076       45192 :     cd.setReferenceCenter(reference_center);
    1077             :   }
    1078             : 
    1079             :   // instead of diagonalization, approximate with saved rotation matrix
    1080       45192 :   cd.doCoreCalcWithCloseStructure(safe,alEqDis, rotationPosClose, rotationRefClose, drotationPosCloseDrr01);
    1081             :   // make the core calc distance
    1082       45192 :   double dist=cd.getDistance(squared);
    1083             :   //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
    1084       90384 :   derivatives=cd.getDDistanceDPositions();
    1085       45192 :   return dist;
    1086             : }
    1087             : 
    1088             : 
    1089             : template <bool safe,bool alEqDis>
    1090      147888 : double RMSD::optimalAlignment_PCA(const std::vector<double>& alignTo,
    1091             :                                   const std::vector<double>& displaceFrom,
    1092             :                                   const View<Vector> positions,
    1093             :                                   const std::vector<Vector>& ref,
    1094             :                                   std::vector<Vector>& alignedpositions,
    1095             :                                   std::vector<Vector>& centeredpositions,
    1096             :                                   std::vector<Vector>& centeredreference,
    1097             :                                   Tensor & Rotation,
    1098             :                                   std::vector<Vector>& DDistDPos,
    1099             :                                   Matrix<std::vector<Vector> >& DRotDPos,
    1100             :                                   const bool squared) const {
    1101             :   //initialize the data into the structure
    1102             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
    1103      147888 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
    1104             :   // transfer the settings for the center to let the CoreCalc deal with it
    1105      147888 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
    1106      147888 :   if(positions_center_is_calculated) {
    1107           0 :     cd.setPositionsCenter(positions_center);
    1108             :   } else {
    1109      147888 :     cd.calcPositionsCenter();
    1110             :   };
    1111             : 
    1112      147888 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
    1113      147888 :   if(!reference_center_is_calculated) {
    1114           0 :     cd.calcReferenceCenter();
    1115             :   } else {
    1116      147888 :     cd.setReferenceCenter(reference_center);
    1117             :   }
    1118             : 
    1119             :   // Perform the diagonalization and all the needed stuff
    1120      147888 :   cd.doCoreCalc(safe,alEqDis);
    1121             :   // make the core calc distance
    1122      147888 :   double dist=cd.getDistance(squared);
    1123             :   // make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
    1124      295776 :   DDistDPos=cd.getDDistanceDPositions();
    1125             :   // get the rotation matrix
    1126      147888 :   Rotation=cd.getRotationMatrixPositionsToReference();
    1127             :   // get its derivative
    1128      147888 :   DRotDPos=cd.getDRotationDPositions(true); // this gives back the inverse
    1129             :   // get aligned positions
    1130      295776 :   alignedpositions=cd.getAlignedPositionsToReference();
    1131             :   // get centered positions
    1132      295776 :   centeredpositions=cd.getCenteredPositions();
    1133             :   // get centered ref
    1134      295776 :   centeredreference=cd.getCenteredReference();
    1135      147888 :   return dist;
    1136             : }
    1137             : 
    1138             : 
    1139             : template <bool safe,bool alEqDis>
    1140         210 : double RMSD::optimalAlignment_Fit(const std::vector<double>& alignTo,
    1141             :                                   const std::vector<double>& displaceFrom,
    1142             :                                   const View<Vector> positions,
    1143             :                                   const std::vector<Vector>& ref,
    1144             :                                   Tensor& Rotation,
    1145             :                                   Matrix<std::vector<Vector>>& DRotDPos,
    1146             :                                   std::vector<Vector>& centeredpositions,
    1147             :                                   Vector& center_positions,
    1148             :                                   const bool squared) {
    1149             :   //initialize the data into the structure
    1150             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
    1151         210 :   RMSDCoreData cd(alignTo,displaceFrom,positions,ref);
    1152             :   // transfer the settings for the center to let the CoreCalc deal with it
    1153         210 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
    1154         210 :   if(positions_center_is_calculated) {
    1155           0 :     cd.setPositionsCenter(positions_center);
    1156             :   } else {
    1157         210 :     cd.calcPositionsCenter();
    1158             :   };
    1159             : 
    1160         210 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
    1161         210 :   if(!reference_center_is_calculated) {
    1162           0 :     cd.calcReferenceCenter();
    1163             :   } else {
    1164         210 :     cd.setReferenceCenter(reference_center);
    1165             :   }
    1166             : 
    1167             :   // Perform the diagonalization and all the needed stuff
    1168         210 :   cd.doCoreCalc(safe,alEqDis);
    1169             :   // make the core calc distance
    1170         210 :   double dist=cd.getDistance(squared);
    1171             :   // get the rotation matrix
    1172         210 :   Rotation=cd.getRotationMatrixPositionsToReference();
    1173             :   // get its derivative
    1174         210 :   DRotDPos=cd.getDRotationDPositions(true); // this gives back the inverse
    1175             :   // get centered positions
    1176         420 :   centeredpositions=cd.getCenteredPositions();
    1177             :   // get center
    1178         210 :   center_positions=cd.getPositionsCenter();
    1179         210 :   return dist;
    1180             : }
    1181             : 
    1182             : /// This calculates the elements needed by the quaternion to calculate everything that is needed
    1183             : /// additional calls retrieve different components
    1184             : /// note that this considers that the centers of both reference and positions are already set
    1185             : /// but automatically should properly account for non removed components: if not removed then it
    1186             : /// removes prior to calculation of the alignment
    1187      149863 : void RMSDCoreData::doCoreCalc(const bool setSafe,
    1188             :                               const bool setAlEqDis) {
    1189             : 
    1190             :   // transfer this bools to the cd so that this settings will be reflected in the other calls
    1191      149863 :   alEqDis=setAlEqDis;
    1192      149863 :   safe=setSafe;
    1193      149863 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1194             : 
    1195      149863 :   plumed_massert(creference_is_calculated,"the center of the reference frame must be already provided at this stage");
    1196      149863 :   plumed_massert(cpositions_is_calculated,"the center of the positions frame must be already provided at this stage");
    1197             : 
    1198             : // This is the trace of positions*positions + reference*reference
    1199      149863 :   rr00=0.;
    1200      149863 :   rr11=0.;
    1201             : // This is positions*reference
    1202      149863 :   Tensor rr01;
    1203             : // center of mass managing: must subtract the center from the position or not?
    1204             :   Vector cp;
    1205             :   cp.zero();
    1206      149863 :   if(!cpositions_is_removed) {
    1207      149863 :     cp=cpositions;
    1208             :   }
    1209             :   Vector cr;
    1210             :   cr.zero();
    1211      149863 :   if(!creference_is_removed) {
    1212          60 :     cr=creference;
    1213             :   }
    1214             : // second expensive loop: compute second moments wrt centers
    1215     2583539 :   for(unsigned iat=0; iat<n; iat++) {
    1216     2433676 :     double w=align[iat];
    1217     2433676 :     rr00+=dotProduct(positions[iat]-cp,positions[iat]-cp)*w;
    1218     2433676 :     rr11+=dotProduct(reference[iat]-cr,reference[iat]-cr)*w;
    1219     4867352 :     rr01+=Tensor(positions[iat]-cp,reference[iat]-cr)*w;
    1220             :   }
    1221             : 
    1222             : // the quaternion matrix: this is internal
    1223             :   Tensor4d m;
    1224             : 
    1225      149863 :   m[0][0]=2.0*(-rr01[0][0]-rr01[1][1]-rr01[2][2]);
    1226      149863 :   m[1][1]=2.0*(-rr01[0][0]+rr01[1][1]+rr01[2][2]);
    1227      149863 :   m[2][2]=2.0*(+rr01[0][0]-rr01[1][1]+rr01[2][2]);
    1228      149863 :   m[3][3]=2.0*(+rr01[0][0]+rr01[1][1]-rr01[2][2]);
    1229      149863 :   m[0][1]=2.0*(-rr01[1][2]+rr01[2][1]);
    1230      149863 :   m[0][2]=2.0*(+rr01[0][2]-rr01[2][0]);
    1231      149863 :   m[0][3]=2.0*(-rr01[0][1]+rr01[1][0]);
    1232      149863 :   m[1][2]=2.0*(-rr01[0][1]-rr01[1][0]);
    1233      149863 :   m[1][3]=2.0*(-rr01[0][2]-rr01[2][0]);
    1234      149863 :   m[2][3]=2.0*(-rr01[1][2]-rr01[2][1]);
    1235      149863 :   m[1][0] = m[0][1];
    1236      149863 :   m[2][0] = m[0][2];
    1237      149863 :   m[2][1] = m[1][2];
    1238      149863 :   m[3][0] = m[0][3];
    1239      149863 :   m[3][1] = m[1][3];
    1240      149863 :   m[3][2] = m[2][3];
    1241             : 
    1242             : 
    1243      149863 :   Tensor dm_drr01[4][4];
    1244      149863 :   dm_drr01[0][0] = 2.0*Tensor(-1.0, 0.0, 0.0,  0.0,-1.0, 0.0,  0.0, 0.0,-1.0);
    1245      149863 :   dm_drr01[1][1] = 2.0*Tensor(-1.0, 0.0, 0.0,  0.0,+1.0, 0.0,  0.0, 0.0,+1.0);
    1246      149863 :   dm_drr01[2][2] = 2.0*Tensor(+1.0, 0.0, 0.0,  0.0,-1.0, 0.0,  0.0, 0.0,+1.0);
    1247      149863 :   dm_drr01[3][3] = 2.0*Tensor(+1.0, 0.0, 0.0,  0.0,+1.0, 0.0,  0.0, 0.0,-1.0);
    1248      149863 :   dm_drr01[0][1] = 2.0*Tensor( 0.0, 0.0, 0.0,  0.0, 0.0,-1.0,  0.0,+1.0, 0.0);
    1249      149863 :   dm_drr01[0][2] = 2.0*Tensor( 0.0, 0.0,+1.0,  0.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    1250      149863 :   dm_drr01[0][3] = 2.0*Tensor( 0.0,-1.0, 0.0, +1.0, 0.0, 0.0,  0.0, 0.0, 0.0);
    1251      149863 :   dm_drr01[1][2] = 2.0*Tensor( 0.0,-1.0, 0.0, -1.0, 0.0, 0.0,  0.0, 0.0, 0.0);
    1252      149863 :   dm_drr01[1][3] = 2.0*Tensor( 0.0, 0.0,-1.0,  0.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    1253      149863 :   dm_drr01[2][3] = 2.0*Tensor( 0.0, 0.0, 0.0,  0.0, 0.0,-1.0,  0.0,-1.0, 0.0);
    1254      149863 :   dm_drr01[1][0] = dm_drr01[0][1];
    1255      149863 :   dm_drr01[2][0] = dm_drr01[0][2];
    1256      149863 :   dm_drr01[2][1] = dm_drr01[1][2];
    1257      149863 :   dm_drr01[3][0] = dm_drr01[0][3];
    1258      149863 :   dm_drr01[3][1] = dm_drr01[1][3];
    1259      149863 :   dm_drr01[3][2] = dm_drr01[2][3];
    1260             : 
    1261             : 
    1262             :   Vector4d q;
    1263             : 
    1264      149863 :   Tensor dq_drr01[4];
    1265      149863 :   diagMatSym(m, eigenvals, eigenvecs );
    1266      149863 :   q=Vector4d(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]);
    1267             :   double dq_dm[4][4][4];
    1268      749315 :   for(unsigned i=0; i<4; i++)
    1269     2997260 :     for(unsigned j=0; j<4; j++)
    1270    11989040 :       for(unsigned k=0; k<4; k++) {
    1271             :         double tmp=0.0;
    1272             : // perturbation theory for matrix m
    1273    38364928 :         for(unsigned l=1; l<4; l++) {
    1274    28773696 :           tmp+=eigenvecs[l][j]*eigenvecs[l][i]/(eigenvals[0]-eigenvals[l])*eigenvecs[0][k];
    1275             :         }
    1276     9591232 :         dq_dm[i][j][k]=tmp;
    1277             :       }
    1278             : // propagation to _drr01
    1279      749315 :   for(unsigned i=0; i<4; i++) {
    1280      599452 :     Tensor tmp;
    1281     2997260 :     for(unsigned j=0; j<4; j++)
    1282    11989040 :       for(unsigned k=0; k<4; k++) {
    1283    19182464 :         tmp+=dq_dm[i][j][k]*dm_drr01[j][k];
    1284             :       }
    1285      599452 :     dq_drr01[i]=tmp;
    1286             :   }
    1287             : 
    1288             : // This is the rotation matrix that brings reference to positions
    1289             : // i.e. matmul(rotation,reference[iat])+shift is fitted to positions[iat]
    1290             : 
    1291      149863 :   rotation[0][0]=q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3];
    1292      149863 :   rotation[1][1]=q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3];
    1293      149863 :   rotation[2][2]=q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3];
    1294      149863 :   rotation[0][1]=2*(+q[0]*q[3]+q[1]*q[2]);
    1295      149863 :   rotation[0][2]=2*(-q[0]*q[2]+q[1]*q[3]);
    1296      149863 :   rotation[1][2]=2*(+q[0]*q[1]+q[2]*q[3]);
    1297      149863 :   rotation[1][0]=2*(-q[0]*q[3]+q[1]*q[2]);
    1298      149863 :   rotation[2][0]=2*(+q[0]*q[2]+q[1]*q[3]);
    1299      149863 :   rotation[2][1]=2*(-q[0]*q[1]+q[2]*q[3]);
    1300             : 
    1301             : 
    1302      299726 :   drotation_drr01[0][0]=2*q[0]*dq_drr01[0]+2*q[1]*dq_drr01[1]-2*q[2]*dq_drr01[2]-2*q[3]*dq_drr01[3];
    1303      149863 :   drotation_drr01[1][1]=2*q[0]*dq_drr01[0]-2*q[1]*dq_drr01[1]+2*q[2]*dq_drr01[2]-2*q[3]*dq_drr01[3];
    1304      149863 :   drotation_drr01[2][2]=2*q[0]*dq_drr01[0]-2*q[1]*dq_drr01[1]-2*q[2]*dq_drr01[2]+2*q[3]*dq_drr01[3];
    1305      149863 :   drotation_drr01[0][1]=2*(+(q[0]*dq_drr01[3]+dq_drr01[0]*q[3])+(q[1]*dq_drr01[2]+dq_drr01[1]*q[2]));
    1306      149863 :   drotation_drr01[0][2]=2*(-(q[0]*dq_drr01[2]+dq_drr01[0]*q[2])+(q[1]*dq_drr01[3]+dq_drr01[1]*q[3]));
    1307      149863 :   drotation_drr01[1][2]=2*(+(q[0]*dq_drr01[1]+dq_drr01[0]*q[1])+(q[2]*dq_drr01[3]+dq_drr01[2]*q[3]));
    1308      149863 :   drotation_drr01[1][0]=2*(-(q[0]*dq_drr01[3]+dq_drr01[0]*q[3])+(q[1]*dq_drr01[2]+dq_drr01[1]*q[2]));
    1309      149863 :   drotation_drr01[2][0]=2*(+(q[0]*dq_drr01[2]+dq_drr01[0]*q[2])+(q[1]*dq_drr01[3]+dq_drr01[1]*q[3]));
    1310      149863 :   drotation_drr01[2][1]=2*(-(q[0]*dq_drr01[1]+dq_drr01[0]*q[1])+(q[2]*dq_drr01[3]+dq_drr01[2]*q[3]));
    1311             : 
    1312      149863 :   d.resize(n);
    1313             : 
    1314             :   // calculate rotation matrix derivatives and components distances needed for components only when align!=displacement
    1315      149863 :   if(!alEqDis) {
    1316             :     ddist_drotation.zero();
    1317             :   }
    1318             :   // This pragma leads to incorrect results with INTEL compiler.
    1319             :   // Failures are seen in rt65-rmsd2, rt-close-structure, rt64-pca, and others.
    1320             :   // Not really clear why. GB
    1321             :   // #pragma omp simd
    1322     2583539 :   for(unsigned iat=0; iat<n; iat++) {
    1323             :     // components differences: this is useful externally
    1324     2433676 :     d[iat]=positions[iat]-cp - matmul(rotation,reference[iat]-cr);
    1325             :   }
    1326             :   // ddist_drotation if needed
    1327     2583539 :   for (unsigned iat=0; iat<n; iat++) {
    1328     4867352 :     ddist_drotation+=-2*displace[iat]*extProduct(d[iat],reference[iat]-cr);
    1329             :   }
    1330             : 
    1331             :   ddist_drr01.zero();
    1332      599452 :   for(unsigned i=0; i<3; i++)
    1333     1798356 :     for(unsigned j=0; j<3; j++) {
    1334     2697534 :       ddist_drr01+=ddist_drotation[i][j]*drotation_drr01[i][j];
    1335             :     }
    1336      149863 :   isInitialized=true;
    1337             : 
    1338      149863 : }
    1339             : /// just retrieve the distance already calculated
    1340      195055 : double RMSDCoreData::getDistance( const bool squared) {
    1341             : 
    1342      195055 :   if(!isInitialized) {
    1343           0 :     plumed_merror("getDistance cannot calculate the distance without being initialized first by doCoreCalc ");
    1344             :   }
    1345             : 
    1346             :   double localDist=0.0;
    1347      195055 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1348      195055 :   if(safe || !alEqDis) {
    1349             :     localDist=0.0;
    1350             :   } else {
    1351      123667 :     localDist=eigenvals[0]+rr00+rr11;
    1352             :   }
    1353      195055 :   if(alEqDis) {
    1354      194955 :     if(safe) {
    1355      142576 :       #pragma omp simd reduction(+:localDist)
    1356             :       for(unsigned iat=0; iat<n; iat++) {
    1357     1411839 :         localDist+=align[iat]*modulo2(d[iat]);
    1358             :       }
    1359             :     }
    1360             :   } else {
    1361         200 :     #pragma omp simd reduction(+:localDist)
    1362             :     for(unsigned iat=0; iat<n; iat++) {
    1363        1662 :       localDist+=displace[iat]*modulo2(d[iat]);
    1364             :     }
    1365             :   }
    1366      195055 :   if(!squared) {
    1367       23679 :     dist=std::sqrt(localDist);
    1368       23679 :     distanceIsMSD=false;
    1369             :   } else {
    1370      171376 :     dist=localDist;
    1371      171376 :     distanceIsMSD=true;
    1372             :   }
    1373      195055 :   hasDistance=true;
    1374      195055 :   return dist;
    1375             : }
    1376             : 
    1377       45192 : void RMSDCoreData::doCoreCalcWithCloseStructure(const bool setSafe,
    1378             :     const bool setAlEqDis,
    1379             :     const Tensor & rotationPosClose,
    1380             :     const Tensor & rotationRefClose,
    1381             :     std::array<std::array<Tensor,3>,3>& drotationPosCloseDrr01) {
    1382             : 
    1383       45192 :   alEqDis=setAlEqDis;
    1384       45192 :   safe=setSafe;
    1385       45192 :   unsigned natoms = reference.size();
    1386       45192 :   Tensor ddist_drxy;
    1387             :   ddist_drr01.zero();
    1388       45192 :   d.resize(natoms);
    1389             : 
    1390             :   // center of mass managing: must subtract the center from the position or not?
    1391             :   Vector cp;
    1392             :   cp.zero();
    1393       45192 :   if(!cpositions_is_removed) {
    1394       45192 :     cp=cpositions;
    1395             :   }
    1396             :   Vector cr;
    1397             :   cr.zero();
    1398       45192 :   if(!creference_is_removed) {
    1399           0 :     cr=creference;
    1400             :   }
    1401             :   //distance = \sum_{n=0}^{N} w_n(x_n-cpos-R_{XY} R_{AY} a_n)^2
    1402             : 
    1403       45192 :   Tensor rot = matmul(rotationPosClose, rotationRefClose);
    1404             : 
    1405             :   // This pragma leads to incorrect results with INTEL compiler.
    1406             :   // Failures are seen in rt65-rmsd2, rt-close-structure, rt64-pca, and others.
    1407             :   // Not really clear why. GB
    1408             :   // #pragma omp simd
    1409      632688 :   for (unsigned iat=0; iat<natoms; iat++) {
    1410      587496 :     d[iat] = positions[iat] - cp - matmul(rot, reference[iat]-cr);
    1411             :   }
    1412       45192 :   if (!alEqDis) {
    1413           0 :     for (unsigned iat=0; iat<natoms; iat++) {
    1414             :       //dist = \sum w_i(x_i - cpos - R_xy * R_ay * a_i)
    1415           0 :       ddist_drxy += -2*displace[iat]*extProduct(matmul(d[iat], rotationRefClose), reference[iat]-cr);
    1416             :     }
    1417           0 :     for(unsigned i=0; i<3; i++)
    1418           0 :       for(unsigned j=0; j<3; j++) {
    1419           0 :         ddist_drr01+=ddist_drxy[i][j]*drotationPosCloseDrr01[i][j];
    1420             :       }
    1421             :   }
    1422       45192 :   isInitialized=true;
    1423       45192 : }
    1424             : 
    1425      193753 : std::vector<Vector> RMSDCoreData::getDDistanceDPositions() {
    1426      193753 :   std::vector<Vector> derivatives;
    1427      193753 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1428             :   Vector ddist_dcpositions;
    1429      193753 :   derivatives.resize(n);
    1430             :   double prefactor=1.0;
    1431      193753 :   if(!distanceIsMSD) {
    1432       23619 :     prefactor*=0.5/dist;
    1433             :   }
    1434      193753 :   if(!hasDistance) {
    1435           0 :     plumed_merror("getDPositionsDerivatives needs to calculate the distance via getDistance first !");
    1436             :   }
    1437      193753 :   if(!isInitialized) {
    1438           0 :     plumed_merror("getDPositionsDerivatives needs to initialize the coreData first!");
    1439             :   }
    1440             :   Vector csum;
    1441     2714729 :   for(unsigned iat=0; iat<n; iat++) {
    1442     2520976 :     if(alEqDis) {
    1443             : // there is no need for derivatives of rotation and shift here as it is by construction zero
    1444             : // (similar to Hellman-Feynman forces)
    1445     2519614 :       derivatives[iat]= 2*prefactor*align[iat]*d[iat];
    1446             :     } else {
    1447             : // these are the derivatives assuming the roto-translation as frozen
    1448        1362 :       Vector tmp1=2*displace[iat]*d[iat];
    1449        1362 :       derivatives[iat]=tmp1;
    1450             : // derivative of cpositions
    1451             :       ddist_dcpositions+=-tmp1;
    1452             :       // these needed for com corrections
    1453        1362 :       Vector tmp2=matmul(ddist_drr01,reference[iat]-creference)*align[iat];
    1454             :       derivatives[iat]+=tmp2;
    1455             :       csum+=tmp2;
    1456             :     }
    1457             :   }
    1458             : 
    1459      193753 :   if(!alEqDis) {
    1460             :     const Vector shift=ddist_dcpositions-csum;
    1461             :     #pragma omp simd
    1462             :     for(unsigned iat=0; iat<n; iat++) {
    1463        1362 :       const double a=align[iat];
    1464        1362 :       derivatives[iat][0]=prefactor*(derivatives[iat][0]+shift[0]*a);
    1465        1362 :       derivatives[iat][1]=prefactor*(derivatives[iat][1]+shift[1]*a);
    1466        1362 :       derivatives[iat][2]=prefactor*(derivatives[iat][2]+shift[2]*a);
    1467             :     }
    1468             :   }
    1469             : 
    1470      193753 :   return derivatives;
    1471           0 : }
    1472             : 
    1473           1 : std::vector<Vector> RMSDCoreData::getDDistanceDReference() {
    1474           1 :   std::vector<Vector> derivatives;
    1475           1 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1476             :   Vector ddist_dcreference;
    1477           1 :   derivatives.resize(n);
    1478             :   double prefactor=1.0;
    1479           1 :   if(!distanceIsMSD) {
    1480           1 :     prefactor*=0.5/dist;
    1481             :   }
    1482             :   Vector csum;
    1483             : 
    1484           1 :   if(!hasDistance) {
    1485           0 :     plumed_merror("getDDistanceDReference needs to calculate the distance via getDistance first !");
    1486             :   }
    1487           1 :   if(!isInitialized) {
    1488           0 :     plumed_merror("getDDistanceDReference to initialize the coreData first!");
    1489             :   }
    1490             :   // get the transpose rotation
    1491           1 :   Tensor t_rotation=rotation.transpose();
    1492           1 :   Tensor t_ddist_drr01=ddist_drr01.transpose();
    1493             : 
    1494             : // third expensive loop: derivatives
    1495         856 :   for(unsigned iat=0; iat<n; iat++) {
    1496         855 :     if(alEqDis) {
    1497             : // there is no need for derivatives of rotation and shift here as it is by construction zero
    1498             : // (similar to Hellman-Feynman forces)
    1499             :       //TODO: check this derivative down here
    1500           0 :       derivatives[iat]= -2*prefactor*align[iat]*matmul(t_rotation,d[iat]);
    1501             :     } else {
    1502             : // these are the derivatives assuming the roto-translation as frozen
    1503         855 :       Vector tmp1=2*displace[iat]*matmul(t_rotation,d[iat]);
    1504         855 :       derivatives[iat]= -tmp1;
    1505             : // derivative of cpositions
    1506             :       ddist_dcreference+=tmp1;
    1507             :       // these below are needed for com correction
    1508         855 :       Vector tmp2=matmul(t_ddist_drr01,positions[iat]-cpositions)*align[iat];
    1509             :       derivatives[iat]+=tmp2;
    1510             :       csum+=tmp2;
    1511             :     }
    1512             :   }
    1513             : 
    1514           1 :   if(!alEqDis) {
    1515             :     const Vector shift=ddist_dcreference-csum;
    1516             :     #pragma omp simd
    1517             :     for(unsigned iat=0; iat<n; iat++) {
    1518         855 :       const double a=align[iat];
    1519         855 :       derivatives[iat][0]=prefactor*(derivatives[iat][0]+shift[0]*a);
    1520         855 :       derivatives[iat][1]=prefactor*(derivatives[iat][1]+shift[1]*a);
    1521         855 :       derivatives[iat][2]=prefactor*(derivatives[iat][2]+shift[2]*a);
    1522             :     }
    1523             :   }
    1524             : 
    1525           1 :   return derivatives;
    1526           0 : }
    1527             : 
    1528             : /// this version does not calculate the derivative of rotation matrix as needed for SOMA
    1529           0 : std::vector<Vector> RMSDCoreData::getDDistanceDReferenceSOMA() {
    1530           0 :   std::vector<Vector> derivatives;
    1531           0 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1532             :   Vector ddist_dcreference;
    1533           0 :   derivatives.resize(n);
    1534             :   double prefactor=1.0;
    1535           0 :   if(!distanceIsMSD) {
    1536           0 :     prefactor*=0.5/dist;
    1537             :   }
    1538             :   Vector tmp1;
    1539             : 
    1540           0 :   if(!hasDistance) {
    1541           0 :     plumed_merror("getDDistanceDReference needs to calculate the distance via getDistance first !");
    1542             :   }
    1543           0 :   if(!isInitialized) {
    1544           0 :     plumed_merror("getDDistanceDReference to initialize the coreData first!");
    1545             :   }
    1546             :   // get the transpose rotation
    1547           0 :   Tensor t_rotation=rotation.transpose();
    1548             : 
    1549             : // third expensive loop: derivatives
    1550           0 :   for(unsigned iat=0; iat<n; iat++) {
    1551           0 :     if(alEqDis) {
    1552             : // there is no need for derivatives of rotation and shift here as it is by construction zero
    1553             : // (similar to Hellman-Feynman forces)
    1554             :       //TODO: check this derivative down here
    1555           0 :       derivatives[iat]= -2*prefactor*align[iat]*matmul(t_rotation,d[iat]);
    1556             :     } else {
    1557             : // these are the derivatives assuming the roto-translation as frozen
    1558           0 :       tmp1=2*displace[iat]*matmul(t_rotation,d[iat]);
    1559           0 :       derivatives[iat]= -tmp1;
    1560             : // derivative of cpositions
    1561             :       ddist_dcreference+=tmp1;
    1562             :     }
    1563             :   }
    1564             : 
    1565           0 :   if(!alEqDis)
    1566           0 :     for(unsigned iat=0; iat<n; iat++) {
    1567           0 :       derivatives[iat]=prefactor*(derivatives[iat]+ddist_dcreference*align[iat]);
    1568             :     }
    1569             : 
    1570           0 :   return derivatives;
    1571           0 : }
    1572             : 
    1573             : 
    1574             : 
    1575             : /*
    1576             : This below is the derivative of the rotation matrix that aligns the reference onto the positions
    1577             : respect to positions
    1578             : note that the this transformation overlap the  reference onto position
    1579             : if inverseTransform=true then aligns the positions onto reference
    1580             : */
    1581      148098 : Matrix<std::vector<Vector> >  RMSDCoreData::getDRotationDPositions( const bool inverseTransform ) {
    1582      148098 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1583      148098 :   if(!isInitialized) {
    1584           0 :     plumed_merror("getDRotationDPosition to initialize the coreData first!");
    1585             :   }
    1586      148098 :   Matrix<std::vector<Vector> > DRotDPos=Matrix<std::vector<Vector> >(3,3);
    1587             :   // remember drotation_drr01 is Tensor drotation_drr01[3][3]
    1588             :   //           (3x3 rot) (3x3 components of rr01)
    1589      148098 :   std::vector<Vector> v(n);
    1590             :   Vector csum;
    1591             :   // these below could probably be calculated in the main routine
    1592             :   Vector cp;
    1593             :   cp.zero();
    1594      148098 :   if(!cpositions_is_removed) {
    1595             :     cp=cpositions;
    1596             :   }
    1597             :   Vector cr;
    1598             :   cr.zero();
    1599      148098 :   if(!creference_is_removed) {
    1600          60 :     cr=creference;
    1601             :   }
    1602     2557987 :   for(unsigned iat=0; iat<n; iat++) {
    1603     2409889 :     csum+=(reference[iat]-cr)*align[iat];
    1604             :   }
    1605     2557987 :   for(unsigned iat=0; iat<n; iat++) {
    1606     2409889 :     v[iat]=(reference[iat]-cr-csum)*align[iat];
    1607             :   }
    1608      592392 :   for(unsigned a=0; a<3; a++) {
    1609     1777176 :     for(unsigned b=0; b<3; b++) {
    1610     1332882 :       if(inverseTransform) {
    1611     1332882 :         DRotDPos[b][a].resize(n);
    1612    23021883 :         for(unsigned iat=0; iat<n; iat++) {
    1613    21689001 :           DRotDPos[b][a][iat]=matmul(drotation_drr01[a][b],v[iat]);
    1614             :         }
    1615             :       } else {
    1616           0 :         DRotDPos[a][b].resize(n);
    1617           0 :         for(unsigned iat=0; iat<n; iat++) {
    1618           0 :           DRotDPos[a][b][iat]=matmul(drotation_drr01[a][b],v[iat]);
    1619             :         }
    1620             :       }
    1621             :     }
    1622             :   }
    1623      148098 :   return DRotDPos;
    1624      148098 : }
    1625             : 
    1626             : /*
    1627             : This below is the derivative of the rotation matrix that aligns the reference onto the positions
    1628             : respect to reference
    1629             : note that the this transformation overlap the  reference onto position
    1630             : if inverseTransform=true then aligns the positions onto reference
    1631             : */
    1632           0 : Matrix<std::vector<Vector> >  RMSDCoreData::getDRotationDReference( const bool inverseTransform ) {
    1633           0 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1634           0 :   if(!isInitialized) {
    1635           0 :     plumed_merror("getDRotationDPositions to initialize the coreData first!");
    1636             :   }
    1637           0 :   Matrix<std::vector<Vector> > DRotDRef=Matrix<std::vector<Vector> >(3,3);
    1638             :   // remember drotation_drr01 is Tensor drotation_drr01[3][3]
    1639             :   //           (3x3 rot) (3x3 components of rr01)
    1640           0 :   std::vector<Vector> v(n);
    1641             :   Vector csum;
    1642             :   // these below could probably be calculated in the main routine
    1643             :   Vector cp;
    1644             :   cp.zero();
    1645           0 :   if(!cpositions_is_removed) {
    1646           0 :     cp=cpositions;
    1647             :   }
    1648             :   Vector cr;
    1649             :   cr.zero();
    1650           0 :   if(!creference_is_removed) {
    1651             :     cr=creference;
    1652             :   }
    1653           0 :   for(unsigned iat=0; iat<n; iat++) {
    1654           0 :     csum+=(positions[iat]-cp)*align[iat];
    1655             :   }
    1656           0 :   for(unsigned iat=0; iat<n; iat++) {
    1657           0 :     v[iat]=(positions[iat]-cp-csum)*align[iat];
    1658             :   }
    1659             : 
    1660           0 :   for(unsigned a=0; a<3; a++) {
    1661           0 :     for(unsigned b=0; b<3; b++) {
    1662           0 :       Tensor t_drotation_drr01=drotation_drr01[a][b].transpose();
    1663           0 :       if(inverseTransform) {
    1664           0 :         DRotDRef[b][a].resize(n);
    1665           0 :         for(unsigned iat=0; iat<n; iat++) {
    1666           0 :           DRotDRef[b][a][iat]=matmul(t_drotation_drr01,v[iat]);
    1667             :         }
    1668             :       } else {
    1669           0 :         DRotDRef[a][b].resize(n);
    1670           0 :         for(unsigned iat=0; iat<n; iat++) {
    1671           0 :           DRotDRef[a][b][iat]=matmul(t_drotation_drr01,v[iat]);
    1672             :         }
    1673             :       }
    1674             :     }
    1675             :   }
    1676           0 :   return DRotDRef;
    1677           0 : }
    1678             : 
    1679             : 
    1680           0 : std::vector<Vector> RMSDCoreData::getAlignedReferenceToPositions() {
    1681           0 :   std::vector<Vector> alignedref;
    1682           0 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1683           0 :   alignedref.resize(n);
    1684           0 :   if(!isInitialized) {
    1685           0 :     plumed_merror("getAlignedReferenceToPostions needs to initialize the coreData first!");
    1686             :   }
    1687             :   // avoid to calculate matrix element but use the sum of what you have
    1688             :   Vector cp;
    1689             :   cp.zero();
    1690           0 :   if(!cpositions_is_removed) {
    1691           0 :     cp=cpositions;
    1692             :   }
    1693           0 :   for(unsigned iat=0; iat<n; iat++) {
    1694           0 :     alignedref[iat]=-d[iat]+positions[iat]-cp;
    1695             :   }
    1696           0 :   return alignedref;
    1697           0 : }
    1698      147888 : std::vector<Vector> RMSDCoreData::getAlignedPositionsToReference() {
    1699      147888 :   std::vector<Vector> alignedpos;
    1700      147888 :   if(!isInitialized) {
    1701           0 :     plumed_merror("getAlignedPostionsToReference needs to initialize the coreData first!");
    1702             :   }
    1703      147888 :   const unsigned n=static_cast<unsigned int>(positions.size());
    1704      147888 :   alignedpos.resize(n);
    1705             :   Vector cp;
    1706             :   cp.zero();
    1707      147888 :   if(!cpositions_is_removed) {
    1708      147888 :     cp=cpositions;
    1709             :   }
    1710             :   // avoid to calculate matrix element but use the sum of what you have
    1711     2071777 :   for(unsigned iat=0; iat<n; iat++) {
    1712     1923889 :     alignedpos[iat]=matmul(rotation.transpose(),positions[iat]-cp);
    1713             :   }
    1714      147888 :   return alignedpos;
    1715           0 : }
    1716             : 
    1717             : 
    1718      148098 : std::vector<Vector> RMSDCoreData::getCenteredPositions() {
    1719      148098 :   std::vector<Vector> centeredpos;
    1720      148098 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1721      148098 :   centeredpos.resize(n);
    1722      148098 :   if(!isInitialized) {
    1723           0 :     plumed_merror("getCenteredPositions needs to initialize the coreData first!");
    1724             :   }
    1725             :   // avoid to calculate matrix element but use the sum of what you have
    1726     2557987 :   for(unsigned iat=0; iat<n; iat++) {
    1727     2409889 :     centeredpos[iat]=positions[iat]-cpositions;
    1728             :   }
    1729      148098 :   return centeredpos;
    1730           0 : }
    1731             : 
    1732      147888 : std::vector<Vector> RMSDCoreData::getCenteredReference() {
    1733      147888 :   std::vector<Vector> centeredref;
    1734      147888 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1735      147888 :   centeredref.resize(n);
    1736      147888 :   if(!isInitialized) {
    1737           0 :     plumed_merror("getCenteredReference needs to initialize the coreData first!");
    1738             :   }
    1739             :   // avoid to calculate matrix element but use the sum of what you have
    1740             :   Vector cr;
    1741             :   cr.zero();
    1742      147888 :   if(!creference_is_removed) {
    1743           0 :     cr=creference;
    1744             :   }
    1745     2071777 :   for(unsigned iat=0; iat<n; iat++) {
    1746     1923889 :     centeredref[iat]=reference[iat]-cr;
    1747             :   }
    1748      147888 :   return centeredref;
    1749           0 : }
    1750             : 
    1751             : 
    1752         210 : Vector RMSDCoreData::getPositionsCenter() {
    1753         210 :   if(!isInitialized) {
    1754           0 :     plumed_merror("getCenteredPositions needs to initialize the coreData first!");
    1755             :   }
    1756         210 :   return cpositions;
    1757             : }
    1758             : 
    1759           0 : Vector RMSDCoreData::getReferenceCenter() {
    1760           0 :   if(!isInitialized) {
    1761           0 :     plumed_merror("getCenteredPositions needs to initialize the coreData first!");
    1762             :   }
    1763           0 :   return creference;
    1764             : }
    1765             : 
    1766        1764 : Tensor RMSDCoreData::getRotationMatrixReferenceToPositions() {
    1767        1764 :   if(!isInitialized) {
    1768           0 :     plumed_merror("getRotationMatrixReferenceToPositions needs to initialize the coreData first!");
    1769             :   }
    1770        1764 :   return rotation;
    1771             : }
    1772             : 
    1773      148098 : Tensor RMSDCoreData::getRotationMatrixPositionsToReference() {
    1774      148098 :   if(!isInitialized) {
    1775           0 :     plumed_merror("getRotationMatrixReferenceToPositions needs to initialize the coreData first!");
    1776             :   }
    1777      148098 :   return rotation.transpose();
    1778             : }
    1779             : 
    1780        1092 : const std::array<std::array<Tensor,3>,3>&  RMSDCoreData::getDRotationDRr01() const {
    1781        1092 :   if(!isInitialized) {
    1782           0 :     plumed_merror("getDRotationDRr01 needs to initialize the coreData first!");
    1783             :   }
    1784        1092 :   return drotation_drr01;
    1785             : }
    1786             : 
    1787             : template double RMSD::optimalAlignment<true,true>(
    1788             :   const std::vector<double>& align,
    1789             :   const std::vector<double>& displaceFrom,
    1790             :   const View<Vector> positions,
    1791             :   const std::vector<Vector>& reference,
    1792             :   std::vector<Vector>& derivatives, bool squared)const;
    1793             : 
    1794             : template double RMSD::optimalAlignment<true,false>(
    1795             :   const std::vector<double>& align,
    1796             :   const std::vector<double>& displaceFrom,
    1797             :   const View<Vector> positions,
    1798             :   const std::vector<Vector>& reference,
    1799             :   std::vector<Vector>& derivatives, bool squared)const;
    1800             : 
    1801             : template double RMSD::optimalAlignment<false,true>(
    1802             :   const std::vector<double>& align,
    1803             :   const std::vector<double>& displaceFrom,
    1804             :   const View<Vector> positions,
    1805             :   const std::vector<Vector>& reference,
    1806             :   std::vector<Vector>& derivatives, bool squared)const;
    1807             : 
    1808             : template double RMSD::optimalAlignment<false,false>(
    1809             :   const std::vector<double>& align,
    1810             :   const std::vector<double>& displaceFrom,
    1811             :   const View<Vector> positions,
    1812             :   const std::vector<Vector>& reference,
    1813             :   std::vector<Vector>& derivatives, bool squared)const;
    1814             : }

Generated by: LCOV version 1.16