LCOV - code coverage report
Current view: top level - tools - RMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 541 732 73.9 %
Date: 2021-11-18 15:22:58 Functions: 43 82 52.4 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2020 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "RMSD.h"
      23             : #include "PDB.h"
      24             : #include "Log.h"
      25             : #include "Exception.h"
      26             : #include <cmath>
      27             : #include <iostream>
      28             : #include "Tools.h"
      29             : using namespace std;
      30             : namespace PLMD {
      31             : 
      32       93604 : RMSD::RMSD() : alignmentMethod(SIMPLE),reference_center_is_calculated(false),reference_center_is_removed(false),positions_center_is_calculated(false),positions_center_is_removed(false) {}
      33             : 
      34             : ///
      35             : /// 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
      36             : /// alignment and beta sets the weight that are used for calculating the displacement.
      37             : ///
      38        1108 : void RMSD::set(const PDB&pdb, const string & mytype, bool remove_center, bool normalize_weights ) {
      39             : 
      40        1108 :   set(pdb.getOccupancy(),pdb.getBeta(),pdb.getPositions(),mytype,remove_center,normalize_weights);
      41             : 
      42        1108 : }
      43        1570 : void RMSD::set(const std::vector<double> & align, const std::vector<double> & displace, const std::vector<Vector> & reference, const string & mytype, bool remove_center, bool normalize_weights ) {
      44             : 
      45        1570 :   setReference(reference); // this by default remove the com and assumes uniform weights
      46        1570 :   setAlign(align, normalize_weights, remove_center); // this recalculates the com with weights. If remove_center=false then it restore the center back
      47        1570 :   setDisplace(displace, normalize_weights);  // this is does not affect any calculation of the weights
      48        1570 :   setType(mytype);
      49             : 
      50        1570 : }
      51             : 
      52       46762 : void RMSD::setType(const string & mytype) {
      53             : 
      54       46762 :   alignmentMethod=SIMPLE; // initialize with the simplest case: no rotation
      55       46762 :   if (mytype=="SIMPLE") {
      56           0 :     alignmentMethod=SIMPLE;
      57             :   }
      58       46762 :   else if (mytype=="OPTIMAL") {
      59       46762 :     alignmentMethod=OPTIMAL;
      60             :   }
      61           0 :   else if (mytype=="OPTIMAL-FAST") {
      62           0 :     alignmentMethod=OPTIMAL_FAST;
      63             :   }
      64           0 :   else plumed_merror("unknown RMSD type" + mytype);
      65             : 
      66       46762 : }
      67             : 
      68         471 : void RMSD::clear() {
      69             :   reference.clear();
      70         471 :   reference_center.zero();
      71         471 :   reference_center_is_calculated=false;
      72         471 :   reference_center_is_removed=false;
      73             :   align.clear();
      74             :   displace.clear();
      75         471 :   positions_center.zero();
      76         471 :   positions_center_is_calculated=false;
      77         471 :   positions_center_is_removed=false;
      78         471 : }
      79             : 
      80           5 : string RMSD::getMethod() {
      81             :   string mystring;
      82           5 :   switch(alignmentMethod) {
      83           0 :   case SIMPLE: mystring.assign("SIMPLE"); break;
      84           5 :   case OPTIMAL: mystring.assign("OPTIMAL"); break;
      85           0 :   case OPTIMAL_FAST: mystring.assign("OPTIMAL-FAST"); break;
      86             :   }
      87           5 :   return mystring;
      88             : }
      89             : ///
      90             : /// this calculates the center of mass for the reference and removes it from the reference itself
      91             : /// considering uniform weights for alignment
      92             : ///
      93       46778 : void RMSD::setReference(const vector<Vector> & reference) {
      94       46778 :   unsigned n=reference.size();
      95       46778 :   this->reference=reference;
      96       46778 :   plumed_massert(align.empty(),"you should first clear() an RMSD object, then set a new reference");
      97       46778 :   plumed_massert(displace.empty(),"you should first clear() an RMSD object, then set a new reference");
      98       46778 :   align.resize(n,1.0/n);
      99       46778 :   displace.resize(n,1.0/n);
     100     1879394 :   for(unsigned i=0; i<n; i++) reference_center+=this->reference[i]*align[i];
     101             :   #pragma omp simd
     102     1221744 :   for(unsigned i=0; i<n; i++) this->reference[i]-=reference_center;
     103       46778 :   reference_center_is_calculated=true;
     104       46778 :   reference_center_is_removed=true;
     105       46778 : }
     106       45192 : std::vector<Vector> RMSD::getReference() {
     107       45192 :   return reference;
     108             : }
     109             : ///
     110             : /// the alignment weights are here normalized to 1 and  the center of the reference is removed accordingly
     111             : ///
     112        1570 : void RMSD::setAlign(const vector<double> & align, bool normalize_weights, bool remove_center) {
     113        1570 :   unsigned n=reference.size();
     114        1570 :   plumed_massert(this->align.size()==align.size(),"mismatch in dimension of align/displace arrays");
     115        1570 :   this->align=align;
     116        1570 :   if(normalize_weights) {
     117             :     double w=0.0;
     118        3130 :     #pragma omp simd reduction(+:w)
     119       46286 :     for(unsigned i=0; i<n; i++) w+=this->align[i];
     120        1565 :     plumed_massert(w>epsilon,"It looks like weights used for alignment are zero. Check your reference PDB file.");
     121        1565 :     double inv=1.0/w;
     122             :     #pragma omp simd
     123       46286 :     for(unsigned i=0; i<n; i++) this->align[i]*=inv;
     124             :   }
     125             :   // recalculate the center anyway
     126             :   // just remove the center if that is asked
     127             :   // if the center was removed before, then add it and store the new one
     128        1570 :   if(reference_center_is_removed) {
     129        1570 :     plumed_massert(reference_center_is_calculated," seems that the reference center has been removed but not calculated and stored!");
     130        1570 :     addCenter(reference,reference_center);
     131             :   }
     132        1570 :   reference_center=calculateCenter(reference,this->align);
     133        1570 :   reference_center_is_calculated=true;
     134        1570 :   if(remove_center) {
     135        1565 :     removeCenter(reference,reference_center);
     136        1565 :     reference_center_is_removed=true;
     137             :   } else {
     138           5 :     reference_center_is_removed=false;
     139             :   }
     140        1570 : }
     141           0 : std::vector<double> RMSD::getAlign() {
     142           0 :   return align;
     143             : }
     144             : ///
     145             : /// here the weigth for normalized weighths are normalized and set
     146             : ///
     147        1570 : void RMSD::setDisplace(const vector<double> & displace, bool normalize_weights) {
     148        1570 :   unsigned n=reference.size();
     149        1570 :   plumed_massert(this->displace.size()==displace.size(),"mismatch in dimension of align/displace arrays");
     150        1570 :   this->displace=displace;
     151             :   double w=0.0;
     152        3140 :   #pragma omp simd reduction(+:w)
     153       46336 :   for(unsigned i=0; i<n; i++) w+=this->displace[i];
     154        1570 :   plumed_massert(w>epsilon,"It looks like weights used for displacement are zero. Check your reference PDB file.");
     155        1570 :   double inv=1.0/w;
     156        1570 :   if(normalize_weights) {
     157             :     #pragma omp simd
     158       46286 :     for(unsigned i=0; i<n; i++) this->displace[i]*=inv;
     159             :   }
     160        1570 : }
     161           0 : std::vector<double> RMSD::getDisplace() {
     162           0 :   return displace;
     163             : }
     164             : ///
     165             : /// This is the main workhorse for rmsd that decides to use specific optimal alignment versions
     166             : ///
     167      332887 : double RMSD::calculate(const std::vector<Vector> & positions,std::vector<Vector> &derivatives, bool squared)const {
     168             : 
     169             :   double ret=0.;
     170             : 
     171      332887 :   switch(alignmentMethod) {
     172             :   case SIMPLE : {
     173             :     //  do a simple alignment without rotation
     174           0 :     std::vector<Vector> displacement( derivatives.size() );
     175           0 :     ret=simpleAlignment(align,displace,positions,reference,derivatives,displacement,squared);
     176             :     break;
     177           0 :   } case OPTIMAL_FAST : {
     178             :     // this is calling the fastest option:
     179           0 :     if(align==displace) ret=optimalAlignment<false,true>(align,displace,positions,reference,derivatives,squared);
     180           0 :     else                ret=optimalAlignment<false,false>(align,displace,positions,reference,derivatives,squared);
     181             :     break;
     182             : 
     183      332887 :   } case OPTIMAL : {
     184             :     // this is the fast routine but in the "safe" mode, which gives less numerical error:
     185      332887 :     if(align==displace) ret=optimalAlignment<true,true>(align,displace,positions,reference,derivatives,squared);
     186           1 :     else ret=optimalAlignment<true,false>(align,displace,positions,reference,derivatives,squared);
     187             :     break;
     188             :   }
     189             :   }
     190             : 
     191      332887 :   return ret;
     192             : 
     193             : }
     194             : 
     195             : 
     196             : /// convenience method for calculating the standard derivatives and the derivative of the rmsd respect to the reference position
     197           1 : double RMSD::calc_DDistDRef( const std::vector<Vector>& positions, std::vector<Vector> &derivatives, std::vector<Vector>& DDistDRef, const bool squared  ) {
     198             :   double ret=0.;
     199           1 :   switch(alignmentMethod) {
     200           0 :   case SIMPLE:
     201           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     202             :     break;
     203           0 :   case OPTIMAL_FAST:
     204           0 :     if(align==displace) ret=optimalAlignment_DDistDRef<false,true>(align,displace,positions,reference,derivatives,DDistDRef, squared);
     205           0 :     else                ret=optimalAlignment_DDistDRef<false,false>(align,displace,positions,reference,derivatives,DDistDRef,squared);
     206             :     break;
     207           1 :   case OPTIMAL:
     208           1 :     if(align==displace) ret=optimalAlignment_DDistDRef<true,true>(align,displace,positions,reference,derivatives,DDistDRef,squared);
     209           1 :     else                ret=optimalAlignment_DDistDRef<true,false>(align,displace,positions,reference,derivatives,DDistDRef,squared);
     210             :     break;
     211             :   }
     212           1 :   return ret;
     213             : 
     214             : }
     215             : 
     216             : /// convenience method for calculating the standard derivatives and the derivative of the rmsd respect to the reference position without the matrix contribution
     217             : /// as required by SOMA
     218           0 : double RMSD::calc_SOMA( const std::vector<Vector>& positions, std::vector<Vector> &derivatives, std::vector<Vector>& DDistDRef, const bool squared  ) {
     219             :   double ret=0.;
     220           0 :   switch(alignmentMethod) {
     221           0 :   case SIMPLE:
     222           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     223             :     break;
     224           0 :   case OPTIMAL_FAST:
     225           0 :     if(align==displace) ret=optimalAlignment_SOMA<false,true>(align,displace,positions,reference,derivatives,DDistDRef, squared);
     226           0 :     else                ret=optimalAlignment_SOMA<false,false>(align,displace,positions,reference,derivatives,DDistDRef,squared);
     227             :     break;
     228           0 :   case OPTIMAL:
     229           0 :     if(align==displace) ret=optimalAlignment_SOMA<true,true>(align,displace,positions,reference,derivatives,DDistDRef,squared);
     230           0 :     else                ret=optimalAlignment_SOMA<true,false>(align,displace,positions,reference,derivatives,DDistDRef,squared);
     231             :     break;
     232             :   }
     233           0 :   return ret;
     234             : 
     235             : }
     236             : 
     237           0 : double RMSD::calc_DDistDRef_Rot_DRotDPos( const std::vector<Vector>& positions, std::vector<Vector> &derivatives, std::vector<Vector>& DDistDRef, Tensor & Rot, Matrix<std::vector<Vector> > &DRotDPos, const bool squared  ) {
     238             :   double ret=0.;
     239           0 :   switch(alignmentMethod) {
     240           0 :   case SIMPLE:
     241           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     242             :     break;
     243           0 :   case OPTIMAL_FAST:
     244           0 :     if(align==displace) ret=optimalAlignment_DDistDRef_Rot_DRotDPos<false,true>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos,  squared);
     245           0 :     else                ret=optimalAlignment_DDistDRef_Rot_DRotDPos<false,false>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, squared);
     246             :     break;
     247           0 :   case OPTIMAL:
     248           0 :     if(align==displace) ret=optimalAlignment_DDistDRef_Rot_DRotDPos<true,true>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, squared);
     249           0 :     else                ret=optimalAlignment_DDistDRef_Rot_DRotDPos<true,false>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, squared);
     250             :     break;
     251             :   }
     252           0 :   return ret;
     253             : }
     254             : 
     255           0 : double RMSD::calc_DDistDRef_Rot_DRotDPos_DRotDRef( const std::vector<Vector>& positions, std::vector<Vector> &derivatives, std::vector<Vector>& DDistDRef, Tensor & Rot, Matrix<std::vector<Vector> > &DRotDPos,  Matrix<std::vector<Vector> > &DRotDRef, const bool squared  ) {
     256             :   double ret=0.;
     257           0 :   switch(alignmentMethod) {
     258           0 :   case SIMPLE:
     259           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     260             :     break;
     261           0 :   case OPTIMAL_FAST:
     262           0 :     if(align==displace) ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<false,true>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef,   squared);
     263           0 :     else                ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<false,false>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef,  squared);
     264             :     break;
     265           0 :   case OPTIMAL:
     266           0 :     if(align==displace) ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<true,true>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef, squared);
     267           0 :     else                ret=optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef<true,false>(align,displace,positions,reference,derivatives,DDistDRef, Rot, DRotDPos, DRotDRef, squared);
     268             :     break;
     269             :   }
     270           0 :   return ret;
     271             : }
     272             : 
     273        1092 : double RMSD::calc_Rot_DRotDRr01( const std::vector<Vector>& positions, Tensor & Rotation, std::array<std::array<Tensor,3>,3> & DRotDRr01, const bool squared) {
     274             :   double ret=0.;
     275        1092 :   switch(alignmentMethod) {
     276           0 :   case SIMPLE:
     277           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     278             :     break;
     279           0 :   case OPTIMAL_FAST:
     280           0 :     if(align==displace) ret=optimalAlignment_Rot_DRotDRr01<false,true>(align,displace,positions,reference, Rotation, DRotDRr01,   squared);
     281           0 :     else                ret=optimalAlignment_Rot_DRotDRr01<false,false>(align,displace,positions,reference, Rotation, DRotDRr01,  squared);
     282             :     break;
     283        1092 :   case OPTIMAL:
     284        1092 :     if(align==displace) ret=optimalAlignment_Rot_DRotDRr01<true,true>(align,displace,positions,reference, Rotation, DRotDRr01, squared);
     285           0 :     else                ret=optimalAlignment_Rot_DRotDRr01<true,false>(align,displace,positions,reference, Rotation, DRotDRr01, squared);
     286             :     break;
     287             :   }
     288        1092 :   return ret;
     289             : }
     290             : 
     291         672 : double RMSD::calc_Rot( const std::vector<Vector>& positions, std::vector<Vector> &derivatives, Tensor & Rotation, const bool squared) {
     292             :   double ret=0.;
     293         672 :   switch(alignmentMethod) {
     294           0 :   case SIMPLE:
     295           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     296             :     break;
     297           0 :   case OPTIMAL_FAST:
     298           0 :     if(align==displace) ret=optimalAlignment_Rot<false,true>(align,displace,positions,reference,derivatives, Rotation, squared);
     299           0 :     else                ret=optimalAlignment_Rot<false,false>(align,displace,positions,reference,derivatives, Rotation, squared);
     300             :     break;
     301         672 :   case OPTIMAL:
     302         672 :     if(align==displace) ret=optimalAlignment_Rot<true,true>(align,displace,positions,reference,derivatives, Rotation, squared);
     303           0 :     else                ret=optimalAlignment_Rot<true,false>(align,displace,positions,reference,derivatives, Rotation, squared);
     304             :     break;
     305             :   }
     306         672 :   return ret;
     307             : }
     308             : 
     309       45192 : double RMSD::calculateWithCloseStructure( const std::vector<Vector>& positions, std::vector<Vector> &derivatives, Tensor & rotationPosClose, Tensor & rotationRefClose, std::array<std::array<Tensor,3>,3> & drotationPosCloseDrr01, const bool squared) {
     310             :   double ret=0.;
     311       45192 :   switch(alignmentMethod) {
     312           0 :   case SIMPLE:
     313           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     314             :     break;
     315           0 :   case OPTIMAL_FAST:
     316           0 :     if(align==displace) ret=optimalAlignmentWithCloseStructure<false,true>(align,displace,positions,reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     317           0 :     else                ret=optimalAlignmentWithCloseStructure<false,false>(align,displace,positions,reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     318             :     break;
     319       45192 :   case OPTIMAL:
     320       45192 :     if(align==displace) ret=optimalAlignmentWithCloseStructure<true,true>(align,displace,positions,reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     321           0 :     else                ret=optimalAlignmentWithCloseStructure<true,false>(align,displace,positions,reference,derivatives, rotationPosClose, rotationRefClose, drotationPosCloseDrr01, squared);
     322             :     break;
     323             :   }
     324       45192 :   return ret;
     325             : }
     326             : 
     327        4985 : double RMSD::calc_PCAelements( const std::vector<Vector>& positions, std::vector<Vector> &DDistDPos, Tensor & Rotation, Matrix<std::vector<Vector> > & DRotDPos,std::vector<Vector>  & alignedpositions, std::vector<Vector> & centeredpositions, std::vector<Vector> &centeredreference, const bool& squared  ) const {
     328             :   double ret=0.;
     329        4985 :   switch(alignmentMethod) {
     330           0 :   case SIMPLE:
     331           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     332             :     break;
     333           0 :   case OPTIMAL_FAST:
     334           0 :     if(align==displace) ret=optimalAlignment_PCA<false,true>(align,displace,positions,reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     335           0 :     else                ret=optimalAlignment_PCA<false,false>(align,displace,positions,reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     336             :     break;
     337        4985 :   case OPTIMAL:
     338        4985 :     if(align==displace) ret=optimalAlignment_PCA<true,true>(align,displace,positions,reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     339           0 :     else                ret=optimalAlignment_PCA<true,false>(align,displace,positions,reference, alignedpositions, centeredpositions,centeredreference,Rotation,DDistDPos,DRotDPos,squared);
     340             :     break;
     341             :   }
     342        4985 :   return ret;
     343             : }
     344             : 
     345             : 
     346          60 : double RMSD::calc_FitElements( const std::vector<Vector>& positions, Tensor & Rotation, Matrix<std::vector<Vector> > & DRotDPos, std::vector<Vector> & centeredpositions, Vector &center_positions, const bool& squared  ) {
     347             :   double ret=0.;
     348          60 :   switch(alignmentMethod) {
     349           0 :   case SIMPLE:
     350           0 :     plumed_merror("derivative of the refreence frame not implemented for SIMPLE alignmentMethod \n");
     351             :     break;
     352           0 :   case OPTIMAL_FAST:
     353           0 :     if(align==displace)ret=optimalAlignment_Fit<false,true>(align,displace,positions,reference, Rotation,DRotDPos,centeredpositions,center_positions,squared);
     354           0 :     else               ret=optimalAlignment_Fit<false,false>(align,displace,positions,reference, Rotation,DRotDPos,centeredpositions,center_positions,squared);
     355             :     break;
     356          60 :   case OPTIMAL:
     357          60 :     if(align==displace)ret=optimalAlignment_Fit<true,true>(align,displace,positions,reference,Rotation,DRotDPos,centeredpositions,center_positions,squared);
     358          60 :     else               ret=optimalAlignment_Fit<true,false>(align,displace,positions,reference,Rotation,DRotDPos,centeredpositions,center_positions,squared);
     359             :     break;
     360             :   }
     361          60 :   return ret;
     362             : }
     363             : 
     364             : 
     365             : 
     366             : 
     367             : 
     368             : 
     369         160 : double RMSD::simpleAlignment(const  std::vector<double>  & align,
     370             :                              const  std::vector<double>  & displace,
     371             :                              const std::vector<Vector> & positions,
     372             :                              const std::vector<Vector> & reference,
     373             :                              std::vector<Vector>  & derivatives,
     374             :                              std::vector<Vector>  & displacement,
     375             :                              bool squared)const {
     376             : 
     377             :   double dist(0);
     378         160 :   unsigned n=reference.size();
     379             : 
     380         160 :   Vector apositions;
     381         160 :   Vector areference;
     382         160 :   Vector dpositions;
     383         160 :   Vector dreference;
     384             : 
     385        4858 :   for(unsigned i=0; i<n; i++) {
     386        4698 :     double aw=align[i];
     387        2349 :     double dw=displace[i];
     388        2349 :     apositions+=positions[i]*aw;
     389        2349 :     areference+=reference[i]*aw;
     390        2349 :     dpositions+=positions[i]*dw;
     391        2349 :     dreference+=reference[i]*dw;
     392             :   }
     393             : 
     394         160 :   Vector shift=((apositions-areference)-(dpositions-dreference));
     395        4858 :   for(unsigned i=0; i<n; i++) {
     396        9396 :     displacement[i]=(positions[i]-apositions)-(reference[i]-areference);
     397        4698 :     dist+=displace[i]*displacement[i].modulo2();
     398        7047 :     derivatives[i]=2*(displace[i]*displacement[i]+align[i]*shift);
     399             :   }
     400             : 
     401         160 :   if(!squared) {
     402             :     // sqrt
     403         106 :     dist=sqrt(dist);
     404             :     ///// sqrt on derivatives
     405        4192 :     for(unsigned i=0; i<n; i++) {derivatives[i]*=(0.5/dist);}
     406             :   }
     407         160 :   return dist;
     408             : }
     409             : 
     410             : // this below enable the standard case for rmsd where the rmsd is calculated and the derivative of rmsd respect to positions is retrieved
     411             : // additionally this assumes that the com of the reference is already subtracted.
     412             : #define OLDRMSD
     413             : #ifdef OLDRMSD
     414             : // notice that in the current implementation the safe argument only makes sense for
     415             : // align==displace
     416             : template <bool safe,bool alEqDis>
     417      549034 : double RMSD::optimalAlignment(const  std::vector<double>  & align,
     418             :                               const  std::vector<double>  & displace,
     419             :                               const std::vector<Vector> & positions,
     420             :                               const std::vector<Vector> & reference,
     421             :                               std::vector<Vector>  & derivatives, bool squared)const {
     422      549034 :   const unsigned n=reference.size();
     423             : // This is the trace of positions*positions + reference*reference
     424             :   double rr00(0);
     425             :   double rr11(0);
     426             : // This is positions*reference
     427      549034 :   Tensor rr01;
     428             : 
     429      549034 :   derivatives.resize(n);
     430             : 
     431      549034 :   Vector cpositions;
     432             : 
     433             : // first expensive loop: compute centers
     434    44181112 :   for(unsigned iat=0; iat<n; iat++) {
     435    43632078 :     double w=align[iat];
     436    21816039 :     cpositions+=positions[iat]*w;
     437             :   }
     438             : 
     439             : // second expensive loop: compute second moments wrt centers
     440    44181112 :   for(unsigned iat=0; iat<n; iat++) {
     441    43632078 :     double w=align[iat];
     442    43632078 :     rr00+=dotProduct(positions[iat]-cpositions,positions[iat]-cpositions)*w;
     443    21813863 :     rr11+=dotProduct(reference[iat],reference[iat])*w;
     444    21816039 :     rr01+=Tensor(positions[iat]-cpositions,reference[iat])*w;
     445             :   }
     446             : 
     447      549034 :   Tensor4d m;
     448             : 
     449      549034 :   m[0][0]=2.0*(-rr01[0][0]-rr01[1][1]-rr01[2][2]);
     450      549034 :   m[1][1]=2.0*(-rr01[0][0]+rr01[1][1]+rr01[2][2]);
     451      549034 :   m[2][2]=2.0*(+rr01[0][0]-rr01[1][1]+rr01[2][2]);
     452      549034 :   m[3][3]=2.0*(+rr01[0][0]+rr01[1][1]-rr01[2][2]);
     453      549034 :   m[0][1]=2.0*(-rr01[1][2]+rr01[2][1]);
     454      549034 :   m[0][2]=2.0*(+rr01[0][2]-rr01[2][0]);
     455      549034 :   m[0][3]=2.0*(-rr01[0][1]+rr01[1][0]);
     456      549034 :   m[1][2]=2.0*(-rr01[0][1]-rr01[1][0]);
     457      549034 :   m[1][3]=2.0*(-rr01[0][2]-rr01[2][0]);
     458      549034 :   m[2][3]=2.0*(-rr01[1][2]-rr01[2][1]);
     459      549034 :   m[1][0] = m[0][1];
     460      549034 :   m[2][0] = m[0][2];
     461      549034 :   m[2][1] = m[1][2];
     462      549034 :   m[3][0] = m[0][3];
     463      549034 :   m[3][1] = m[1][3];
     464      549034 :   m[3][2] = m[2][3];
     465             : 
     466     2745170 :   Tensor dm_drr01[4][4];
     467             :   if(!alEqDis) {
     468         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);
     469         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);
     470         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);
     471         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);
     472         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);
     473         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);
     474         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);
     475         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);
     476         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);
     477         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);
     478         114 :     dm_drr01[1][0] = dm_drr01[0][1];
     479         114 :     dm_drr01[2][0] = dm_drr01[0][2];
     480         114 :     dm_drr01[2][1] = dm_drr01[1][2];
     481         114 :     dm_drr01[3][0] = dm_drr01[0][3];
     482         114 :     dm_drr01[3][1] = dm_drr01[1][3];
     483         114 :     dm_drr01[3][2] = dm_drr01[2][3];
     484             :   }
     485             : 
     486             :   double dist=0.0;
     487      549034 :   Vector4d q;
     488             : 
     489     2745170 :   Tensor dq_drr01[4];
     490             :   if(!alEqDis) {
     491         114 :     Vector4d eigenvals;
     492         114 :     Tensor4d eigenvecs;
     493         114 :     diagMatSym(m, eigenvals, eigenvecs );
     494             :     dist=eigenvals[0]+rr00+rr11;
     495         114 :     q=Vector4d(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]);
     496             :     double dq_dm[4][4][4];
     497        7866 :     for(unsigned i=0; i<4; i++) for(unsigned j=0; j<4; j++) for(unsigned k=0; k<4; k++) {
     498             :           double tmp=0.0;
     499             : // perturbation theory for matrix m
     500       29184 :           for(unsigned l=1; l<4; l++) tmp+=eigenvecs[l][j]*eigenvecs[l][i]/(eigenvals[0]-eigenvals[l])*eigenvecs[0][k];
     501        7296 :           dq_dm[i][j][k]=tmp;
     502             :         }
     503             : // propagation to _drr01
     504        1026 :     for(unsigned i=0; i<4; i++) {
     505         456 :       Tensor tmp;
     506        9576 :       for(unsigned j=0; j<4; j++) for(unsigned k=0; k<4; k++) {
     507        7296 :           tmp+=dq_dm[i][j][k]*dm_drr01[j][k];
     508             :         }
     509         456 :       dq_drr01[i]=tmp;
     510             :     }
     511             :   } else {
     512      548920 :     VectorGeneric<1> eigenvals;
     513      548920 :     TensorGeneric<1,4> eigenvecs;
     514      548920 :     diagMatSym(m, eigenvals, eigenvecs );
     515      548920 :     dist=eigenvals[0]+rr00+rr11;
     516      548920 :     q=Vector4d(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]);
     517             :   }
     518             : 
     519             : 
     520             : // This is the rotation matrix that brings reference to positions
     521             : // i.e. matmul(rotation,reference[iat])+shift is fitted to positions[iat]
     522             : 
     523      549034 :   Tensor rotation;
     524      549034 :   rotation[0][0]=q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3];
     525      549034 :   rotation[1][1]=q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3];
     526      549034 :   rotation[2][2]=q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3];
     527      549034 :   rotation[0][1]=2*(+q[0]*q[3]+q[1]*q[2]);
     528      549034 :   rotation[0][2]=2*(-q[0]*q[2]+q[1]*q[3]);
     529      549034 :   rotation[1][2]=2*(+q[0]*q[1]+q[2]*q[3]);
     530      549034 :   rotation[1][0]=2*(-q[0]*q[3]+q[1]*q[2]);
     531      549034 :   rotation[2][0]=2*(+q[0]*q[2]+q[1]*q[3]);
     532      549034 :   rotation[2][1]=2*(-q[0]*q[1]+q[2]*q[3]);
     533             : 
     534             : 
     535      549034 :   std::array<std::array<Tensor,3>,3> drotation_drr01;
     536             :   if(!alEqDis) {
     537         114 :     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];
     538         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];
     539         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];
     540         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]));
     541         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]));
     542         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]));
     543         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]));
     544         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]));
     545         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]));
     546             :   }
     547             : 
     548             :   double prefactor=2.0;
     549             : 
     550      548920 :   if(!squared && alEqDis) prefactor*=0.5/sqrt(dist);
     551             : 
     552             : // if "safe", recompute dist here to a better accuracy
     553             :   if(safe || !alEqDis) dist=0.0;
     554             : 
     555             : // If safe is set to "false", MSD is taken from the eigenvalue of the M matrix
     556             : // If safe is set to "true", MSD is recomputed from the rotational matrix
     557             : // For some reason, this last approach leads to less numerical noise but adds an overhead
     558             : 
     559      549034 :   Tensor ddist_drotation;
     560      549034 :   Vector ddist_dcpositions;
     561             : 
     562             : // third expensive loop: derivatives
     563    44181112 :   for(unsigned iat=0; iat<n; iat++) {
     564    65448117 :     Vector d(positions[iat]-cpositions - matmul(rotation,reference[iat]));
     565             :     if(alEqDis) {
     566             : // there is no need for derivatives of rotation and shift here as it is by construction zero
     567             : // (similar to Hellman-Feynman forces)
     568    43627726 :       derivatives[iat]= prefactor*align[iat]*d;
     569     4952157 :       if(safe) dist+=align[iat]*modulo2(d);
     570             :     } else {
     571             : // the case for align != displace is different, sob:
     572        2176 :       dist+=displace[iat]*modulo2(d);
     573             : // these are the derivatives assuming the roto-translation as frozen
     574        4352 :       derivatives[iat]=2*displace[iat]*d;
     575             : // here I accumulate derivatives wrt rotation matrix ..
     576        4352 :       ddist_drotation+=-2*displace[iat]*extProduct(d,reference[iat]);
     577             : // .. and cpositions
     578        2176 :       ddist_dcpositions+=-2*displace[iat]*d;
     579             :     }
     580             :   }
     581             : 
     582             :   if(!alEqDis) {
     583         114 :     Tensor ddist_drr01;
     584        1482 :     for(unsigned i=0; i<3; i++) for(unsigned j=0; j<3; j++) ddist_drr01+=ddist_drotation[i][j]*drotation_drr01[i][j];
     585        4466 :     for(unsigned iat=0; iat<n; iat++) {
     586             : // this is propagating to positions.
     587             : // I am implicitly using the derivative of rr01 wrt positions here
     588        8704 :       derivatives[iat]+=matmul(ddist_drr01,reference[iat])*align[iat];
     589        4352 :       derivatives[iat]+=ddist_dcpositions*align[iat];
     590             :     }
     591             :   }
     592      549034 :   if(!squared) {
     593       74171 :     dist=sqrt(dist);
     594             :     if(!alEqDis) {
     595         114 :       double xx=0.5/dist;
     596        4466 :       for(unsigned iat=0; iat<n; iat++) derivatives[iat]*=xx;
     597             :     }
     598             :   }
     599             : 
     600      549034 :   return dist;
     601             : }
     602             : #else
     603             : /// note that this method is intended to be repeatedly invoked
     604             : /// when the reference does already have the center subtracted
     605             : /// but the position has not calculated center and not subtracted
     606             : template <bool safe,bool alEqDis>
     607             : double RMSD::optimalAlignment(const  std::vector<double>  & align,
     608             :                               const  std::vector<double>  & displace,
     609             :                               const std::vector<Vector> & positions,
     610             :                               const std::vector<Vector> & reference,
     611             :                               std::vector<Vector>  & derivatives,
     612             :                               bool squared) const {
     613             :   //std::cerr<<"setting up the core data \n";
     614             :   RMSDCoreData cd(align,displace,positions,reference);
     615             : 
     616             :   // transfer the settings for the center to let the CoreCalc deal with it
     617             :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     618             :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     619             :   else {cd.calcPositionsCenter();};
     620             : 
     621             :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     622             :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     623             :   else {cd.setReferenceCenter(reference_center);}
     624             : 
     625             :   // Perform the diagonalization and all the needed stuff
     626             :   cd.doCoreCalc(safe,alEqDis);
     627             :   // make the core calc distance
     628             :   double dist=cd.getDistance(squared);
     629             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     630             :   derivatives=cd.getDDistanceDPositions();
     631             :   return dist;
     632             : }
     633             : #endif
     634             : template <bool safe,bool alEqDis>
     635           1 : double RMSD::optimalAlignment_DDistDRef(const  std::vector<double>  & align,
     636             :                                         const  std::vector<double>  & displace,
     637             :                                         const std::vector<Vector> & positions,
     638             :                                         const std::vector<Vector> & reference,
     639             :                                         std::vector<Vector>  & derivatives,
     640             :                                         std::vector<Vector> & ddistdref,
     641             :                                         bool squared) const {
     642             :   //initialize the data into the structure
     643             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     644           1 :   RMSDCoreData cd(align,displace,positions,reference);
     645             :   // transfer the settings for the center to let the CoreCalc deal with it
     646             :   // transfer the settings for the center to let the CoreCalc deal with it
     647           1 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     648           1 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     649           1 :   else {cd.calcPositionsCenter();};
     650             : 
     651           1 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     652           1 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     653           1 :   else {cd.setReferenceCenter(reference_center);}
     654             : 
     655             :   // Perform the diagonalization and all the needed stuff
     656           1 :   cd.doCoreCalc(safe,alEqDis);
     657             :   // make the core calc distance
     658           1 :   double dist=cd.getDistance(squared);
     659             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     660           2 :   derivatives=cd.getDDistanceDPositions();
     661           2 :   ddistdref=cd.getDDistanceDReference();
     662           1 :   return dist;
     663             : }
     664             : 
     665             : template <bool safe,bool alEqDis>
     666           0 : double RMSD::optimalAlignment_SOMA(const  std::vector<double>  & align,
     667             :                                    const  std::vector<double>  & displace,
     668             :                                    const std::vector<Vector> & positions,
     669             :                                    const std::vector<Vector> & reference,
     670             :                                    std::vector<Vector>  & derivatives,
     671             :                                    std::vector<Vector> & ddistdref,
     672             :                                    bool squared) const {
     673             :   //initialize the data into the structure
     674             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     675           0 :   RMSDCoreData cd(align,displace,positions,reference);
     676             :   // transfer the settings for the center to let the CoreCalc deal with it
     677             :   // transfer the settings for the center to let the CoreCalc deal with it
     678           0 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     679           0 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     680           0 :   else {cd.calcPositionsCenter();};
     681             : 
     682           0 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     683           0 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     684           0 :   else {cd.setReferenceCenter(reference_center);}
     685             : 
     686             :   // Perform the diagonalization and all the needed stuff
     687           0 :   cd.doCoreCalc(safe,alEqDis);
     688             :   // make the core calc distance
     689           0 :   double dist=cd.getDistance(squared);
     690             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     691           0 :   derivatives=cd.getDDistanceDPositions();
     692           0 :   ddistdref=cd.getDDistanceDReferenceSOMA();
     693           0 :   return dist;
     694             : }
     695             : 
     696             : 
     697             : template <bool safe,bool alEqDis>
     698           0 : double RMSD::optimalAlignment_DDistDRef_Rot_DRotDPos(const  std::vector<double>  & align,
     699             :     const  std::vector<double>  & displace,
     700             :     const std::vector<Vector> & positions,
     701             :     const std::vector<Vector> & reference,
     702             :     std::vector<Vector>  & derivatives,
     703             :     std::vector<Vector> & ddistdref,
     704             :     Tensor & Rotation,
     705             :     Matrix<std::vector<Vector> > &DRotDPos,
     706             :     bool squared) const {
     707             :   //initialize the data into the structure
     708             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     709           0 :   RMSDCoreData cd(align,displace,positions,reference);
     710             :   // transfer the settings for the center to let the CoreCalc deal with it
     711             :   // transfer the settings for the center to let the CoreCalc deal with it
     712           0 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     713           0 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     714           0 :   else {cd.calcPositionsCenter();};
     715             : 
     716           0 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     717           0 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     718           0 :   else {cd.setReferenceCenter(reference_center);}
     719             : 
     720             :   // Perform the diagonalization and all the needed stuff
     721           0 :   cd.doCoreCalc(safe,alEqDis);
     722             :   // make the core calc distance
     723           0 :   double dist=cd.getDistance(squared);
     724             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     725           0 :   derivatives=cd.getDDistanceDPositions();
     726           0 :   ddistdref=cd.getDDistanceDReference();
     727             :   // get the rotation matrix
     728           0 :   Rotation=cd.getRotationMatrixReferenceToPositions();
     729             :   // get its derivative
     730           0 :   DRotDPos=cd.getDRotationDPositions();
     731           0 :   return dist;
     732             : }
     733             : 
     734             : template <bool safe,bool alEqDis>
     735           0 : double RMSD::optimalAlignment_DDistDRef_Rot_DRotDPos_DRotDRef(const  std::vector<double>  & align,
     736             :     const  std::vector<double>  & displace,
     737             :     const std::vector<Vector> & positions,
     738             :     const std::vector<Vector> & reference,
     739             :     std::vector<Vector>  & derivatives,
     740             :     std::vector<Vector> & ddistdref,
     741             :     Tensor & Rotation,
     742             :     Matrix<std::vector<Vector> > &DRotDPos,
     743             :     Matrix<std::vector<Vector> > &DRotDRef,
     744             :     bool squared) const {
     745             :   //initialize the data into the structure
     746             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     747           0 :   RMSDCoreData cd(align,displace,positions,reference);
     748             :   // transfer the settings for the center to let the CoreCalc deal with it
     749             :   // transfer the settings for the center to let the CoreCalc deal with it
     750           0 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     751           0 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     752           0 :   else {cd.calcPositionsCenter();};
     753             : 
     754           0 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     755           0 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     756           0 :   else {cd.setReferenceCenter(reference_center);}
     757             : 
     758             :   // Perform the diagonalization and all the needed stuff
     759           0 :   cd.doCoreCalc(safe,alEqDis);
     760             :   // make the core calc distance
     761           0 :   double dist=cd.getDistance(squared);
     762             : //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     763           0 :   derivatives=cd.getDDistanceDPositions();
     764           0 :   ddistdref=cd.getDDistanceDReference();
     765             :   // get the rotation matrix
     766           0 :   Rotation=cd.getRotationMatrixReferenceToPositions();
     767             :   // get its derivative
     768           0 :   DRotDPos=cd.getDRotationDPositions();
     769           0 :   DRotDRef=cd.getDRotationDReference();
     770           0 :   return dist;
     771             : }
     772             : 
     773             : template <bool safe,bool alEqDis>
     774        1092 : double RMSD::optimalAlignment_Rot_DRotDRr01(const  std::vector<double>  & align,
     775             :     const  std::vector<double>  & displace,
     776             :     const std::vector<Vector> & positions,
     777             :     const std::vector<Vector> & reference,
     778             :     Tensor & Rotation,
     779             :     std::array<std::array<Tensor,3>,3> & DRotDRr01,
     780             :     bool squared) const {
     781             :   //initialize the data into the structure
     782             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     783        1092 :   RMSDCoreData cd(align,displace,positions,reference);
     784             :   // transfer the settings for the center to let the CoreCalc deal with it
     785        1092 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     786        1092 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     787        1092 :   else {cd.calcPositionsCenter();};
     788             : 
     789        1092 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     790        1092 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     791        1092 :   else {cd.setReferenceCenter(reference_center);}
     792             : 
     793             :   // Perform the diagonalization and all the needed stuff
     794        1092 :   cd.doCoreCalc(safe,alEqDis);
     795             :   // make the core calc distance
     796        1092 :   double dist=cd.getDistance(squared);
     797             :   // get the rotation matrix
     798        1092 :   Rotation=cd.getRotationMatrixReferenceToPositions();
     799             :   //get detivative w.r.t. rr01
     800        1092 :   DRotDRr01=cd.getDRotationDRr01();
     801        1092 :   return dist;
     802             : }
     803             : 
     804             : template <bool safe,bool alEqDis>
     805         672 : double RMSD::optimalAlignment_Rot(const  std::vector<double>  & align,
     806             :                                   const  std::vector<double>  & displace,
     807             :                                   const std::vector<Vector> & positions,
     808             :                                   const std::vector<Vector> & reference,
     809             :                                   std::vector<Vector>  & derivatives,
     810             :                                   Tensor & Rotation,
     811             :                                   bool squared) const {
     812             :   //initialize the data into the structure
     813             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     814         672 :   RMSDCoreData cd(align,displace,positions,reference);
     815             :   // transfer the settings for the center to let the CoreCalc deal with it
     816         672 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     817         672 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     818         672 :   else {cd.calcPositionsCenter();};
     819             : 
     820         672 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     821         672 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     822         672 :   else {cd.setReferenceCenter(reference_center);}
     823             : 
     824             :   // Perform the diagonalization and all the needed stuff
     825         672 :   cd.doCoreCalc(safe,alEqDis);
     826             :   // make the core calc distance
     827         672 :   double dist=cd.getDistance(squared);
     828             :   //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     829        1344 :   derivatives=cd.getDDistanceDPositions();
     830             :   // get the rotation matrix
     831         672 :   Rotation=cd.getRotationMatrixReferenceToPositions();
     832         672 :   return dist;
     833             : }
     834             : 
     835             : template <bool safe,bool alEqDis>
     836       45192 : double RMSD::optimalAlignmentWithCloseStructure(const  std::vector<double>  & align,
     837             :     const  std::vector<double>  & displace,
     838             :     const std::vector<Vector> & positions,
     839             :     const std::vector<Vector> & reference,
     840             :     std::vector<Vector>  & derivatives,
     841             :     Tensor & rotationPosClose,
     842             :     Tensor & rotationRefClose,
     843             :     std::array<std::array<Tensor,3>,3> & drotationPosCloseDrr01,
     844             :     bool squared) const {
     845             :   //initialize the data into the structure
     846             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     847       45192 :   RMSDCoreData cd(align,displace,positions,reference);
     848             :   // transfer the settings for the center to let the CoreCalc deal with it
     849       45192 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     850       45192 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     851       45192 :   else {cd.calcPositionsCenter();};
     852             : 
     853       45192 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     854       45192 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     855       45192 :   else {cd.setReferenceCenter(reference_center);}
     856             : 
     857             :   // instead of diagonalization, approximate with saved rotation matrix
     858       45192 :   cd.doCoreCalcWithCloseStructure(safe,alEqDis, rotationPosClose, rotationRefClose, drotationPosCloseDrr01);
     859             :   // make the core calc distance
     860       45192 :   double dist=cd.getDistance(squared);
     861             :   //  make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     862       90384 :   derivatives=cd.getDDistanceDPositions();
     863       45192 :   return dist;
     864             : }
     865             : 
     866             : 
     867             : template <bool safe,bool alEqDis>
     868        4985 : double RMSD::optimalAlignment_PCA(const  std::vector<double>  & align,
     869             :                                   const  std::vector<double>  & displace,
     870             :                                   const std::vector<Vector> & positions,
     871             :                                   const std::vector<Vector> & reference,
     872             :                                   std::vector<Vector> & alignedpositions,
     873             :                                   std::vector<Vector> & centeredpositions,
     874             :                                   std::vector<Vector> & centeredreference,
     875             :                                   Tensor & Rotation,
     876             :                                   std::vector<Vector> & DDistDPos,
     877             :                                   Matrix<std::vector<Vector> > & DRotDPos,
     878             :                                   bool squared) const {
     879             :   //initialize the data into the structure
     880             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     881        4985 :   RMSDCoreData cd(align,displace,positions,reference);
     882             :   // transfer the settings for the center to let the CoreCalc deal with it
     883        4985 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     884        4985 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     885        4985 :   else {cd.calcPositionsCenter();};
     886             : 
     887        4985 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     888        4985 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     889        4985 :   else {cd.setReferenceCenter(reference_center);}
     890             : 
     891             :   // Perform the diagonalization and all the needed stuff
     892        4985 :   cd.doCoreCalc(safe,alEqDis);
     893             :   // make the core calc distance
     894        4985 :   double dist=cd.getDistance(squared);
     895             :   // make the derivatives by using pieces calculated in coreCalc (probably the best is just to copy the vector...)
     896        9970 :   DDistDPos=cd.getDDistanceDPositions();
     897             :   // get the rotation matrix
     898        4985 :   Rotation=cd.getRotationMatrixPositionsToReference();
     899             :   // get its derivative
     900        9970 :   DRotDPos=cd.getDRotationDPositions(true); // this gives back the inverse
     901             :   // get aligned positions
     902        9970 :   alignedpositions=cd.getAlignedPositionsToReference();
     903             :   // get centered positions
     904        9970 :   centeredpositions=cd.getCenteredPositions();
     905             :   // get centered reference
     906        9970 :   centeredreference=cd.getCenteredReference();
     907        4985 :   return dist;
     908             : }
     909             : 
     910             : 
     911             : template <bool safe,bool alEqDis>
     912          60 : double RMSD::optimalAlignment_Fit(const  std::vector<double>  & align,
     913             :                                   const  std::vector<double>  & displace,
     914             :                                   const std::vector<Vector> & positions,
     915             :                                   const std::vector<Vector> & reference,
     916             :                                   Tensor & Rotation,
     917             :                                   Matrix<std::vector<Vector> > & DRotDPos,
     918             :                                   std::vector<Vector> & centeredpositions,
     919             :                                   Vector & center_positions,
     920             :                                   bool squared) {
     921             :   //initialize the data into the structure
     922             :   // typically the positions do not have the com neither calculated nor subtracted. This layer takes care of this business
     923          60 :   RMSDCoreData cd(align,displace,positions,reference);
     924             :   // transfer the settings for the center to let the CoreCalc deal with it
     925          60 :   cd.setPositionsCenterIsRemoved(positions_center_is_removed);
     926          60 :   if(positions_center_is_calculated) {cd.setPositionsCenter(positions_center);}
     927          60 :   else {cd.calcPositionsCenter();};
     928             : 
     929          60 :   cd.setReferenceCenterIsRemoved(reference_center_is_removed);
     930          60 :   if(!reference_center_is_calculated) {cd.calcReferenceCenter();}
     931          60 :   else {cd.setReferenceCenter(reference_center);}
     932             : 
     933             :   // Perform the diagonalization and all the needed stuff
     934          60 :   cd.doCoreCalc(safe,alEqDis);
     935             :   // make the core calc distance
     936          60 :   double dist=cd.getDistance(squared);
     937             :   // get the rotation matrix
     938          60 :   Rotation=cd.getRotationMatrixPositionsToReference();
     939             :   // get its derivative
     940         120 :   DRotDPos=cd.getDRotationDPositions(true); // this gives back the inverse
     941             :   // get centered positions
     942         120 :   centeredpositions=cd.getCenteredPositions();
     943             :   // get center
     944          60 :   center_positions=cd.getPositionsCenter();
     945          60 :   return dist;
     946             : }
     947             : 
     948             : 
     949             : 
     950             : 
     951             : 
     952             : 
     953             : /// This calculates the elements needed by the quaternion to calculate everything that is needed
     954             : /// additional calls retrieve different components
     955             : /// note that this considers that the centers of both reference and positions are already setted
     956             : /// but automatically should properly account for non removed components: if not removed then it
     957             : /// removes prior to calculation of the alignment
     958        6810 : void RMSDCoreData::doCoreCalc(bool safe,bool alEqDis, bool only_rotation) {
     959             : 
     960        6810 :   retrieve_only_rotation=only_rotation;
     961       13620 :   const unsigned n=static_cast<unsigned int>(reference.size());
     962             : 
     963        6810 :   plumed_massert(creference_is_calculated,"the center of the reference frame must be already provided at this stage");
     964        6810 :   plumed_massert(cpositions_is_calculated,"the center of the positions frame must be already provided at this stage");
     965             : 
     966             : // This is the trace of positions*positions + reference*reference
     967        6810 :   rr00=0.;
     968        6810 :   rr11=0.;
     969             : // This is positions*reference
     970        6810 :   Tensor rr01;
     971             : // center of mass managing: must subtract the center from the position or not?
     972        6810 :   Vector cp; cp.zero(); if(!cpositions_is_removed)cp=cpositions;
     973        6810 :   Vector cr; cr.zero(); if(!creference_is_removed)cr=creference;
     974             : // second expensive loop: compute second moments wrt centers
     975      199314 :   for(unsigned iat=0; iat<n; iat++) {
     976      192504 :     double w=align[iat];
     977      288756 :     rr00+=dotProduct(positions[iat]-cp,positions[iat]-cp)*w;
     978      288756 :     rr11+=dotProduct(reference[iat]-cr,reference[iat]-cr)*w;
     979      288756 :     rr01+=Tensor(positions[iat]-cp,reference[iat]-cr)*w;
     980             :   }
     981             : 
     982             : // the quaternion matrix: this is internal
     983        6810 :   Tensor4d m;
     984             : 
     985        6810 :   m[0][0]=2.0*(-rr01[0][0]-rr01[1][1]-rr01[2][2]);
     986        6810 :   m[1][1]=2.0*(-rr01[0][0]+rr01[1][1]+rr01[2][2]);
     987        6810 :   m[2][2]=2.0*(+rr01[0][0]-rr01[1][1]+rr01[2][2]);
     988        6810 :   m[3][3]=2.0*(+rr01[0][0]+rr01[1][1]-rr01[2][2]);
     989        6810 :   m[0][1]=2.0*(-rr01[1][2]+rr01[2][1]);
     990        6810 :   m[0][2]=2.0*(+rr01[0][2]-rr01[2][0]);
     991        6810 :   m[0][3]=2.0*(-rr01[0][1]+rr01[1][0]);
     992        6810 :   m[1][2]=2.0*(-rr01[0][1]-rr01[1][0]);
     993        6810 :   m[1][3]=2.0*(-rr01[0][2]-rr01[2][0]);
     994        6810 :   m[2][3]=2.0*(-rr01[1][2]-rr01[2][1]);
     995        6810 :   m[1][0] = m[0][1];
     996        6810 :   m[2][0] = m[0][2];
     997        6810 :   m[2][1] = m[1][2];
     998        6810 :   m[3][0] = m[0][3];
     999        6810 :   m[3][1] = m[1][3];
    1000        6810 :   m[3][2] = m[2][3];
    1001             : 
    1002             : 
    1003       34050 :   Tensor dm_drr01[4][4];
    1004        6810 :   if(!alEqDis or !retrieve_only_rotation) {
    1005        6810 :     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);
    1006        6810 :     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);
    1007        6810 :     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);
    1008        6810 :     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);
    1009        6810 :     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);
    1010        6810 :     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);
    1011        6810 :     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);
    1012        6810 :     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);
    1013        6810 :     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);
    1014        6810 :     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);
    1015        6810 :     dm_drr01[1][0] = dm_drr01[0][1];
    1016        6810 :     dm_drr01[2][0] = dm_drr01[0][2];
    1017        6810 :     dm_drr01[2][1] = dm_drr01[1][2];
    1018        6810 :     dm_drr01[3][0] = dm_drr01[0][3];
    1019        6810 :     dm_drr01[3][1] = dm_drr01[1][3];
    1020        6810 :     dm_drr01[3][2] = dm_drr01[2][3];
    1021             :   }
    1022             : 
    1023             : 
    1024        6810 :   Vector4d q;
    1025             : 
    1026       34050 :   Tensor dq_drr01[4];
    1027        6810 :   if(!alEqDis or !only_rotation) {
    1028        6810 :     diagMatSym(m, eigenvals, eigenvecs );
    1029        6810 :     q=Vector4d(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]);
    1030             :     double dq_dm[4][4][4];
    1031      469890 :     for(unsigned i=0; i<4; i++) for(unsigned j=0; j<4; j++) for(unsigned k=0; k<4; k++) {
    1032             :           double tmp=0.0;
    1033             : // perturbation theory for matrix m
    1034     1743360 :           for(unsigned l=1; l<4; l++) tmp+=eigenvecs[l][j]*eigenvecs[l][i]/(eigenvals[0]-eigenvals[l])*eigenvecs[0][k];
    1035      435840 :           dq_dm[i][j][k]=tmp;
    1036             :         }
    1037             : // propagation to _drr01
    1038       61290 :     for(unsigned i=0; i<4; i++) {
    1039       27240 :       Tensor tmp;
    1040      572040 :       for(unsigned j=0; j<4; j++) for(unsigned k=0; k<4; k++) {
    1041      435840 :           tmp+=dq_dm[i][j][k]*dm_drr01[j][k];
    1042             :         }
    1043       27240 :       dq_drr01[i]=tmp;
    1044             :     }
    1045             :   } else {
    1046           0 :     TensorGeneric<1,4> here_eigenvecs;
    1047           0 :     VectorGeneric<1> here_eigenvals;
    1048           0 :     diagMatSym(m, here_eigenvals, here_eigenvecs );
    1049           0 :     for(unsigned i=0; i<4; i++) eigenvecs[0][i]=here_eigenvecs[0][i];
    1050           0 :     eigenvals[0]=here_eigenvals[0];
    1051           0 :     q=Vector4d(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]);
    1052             :   }
    1053             : 
    1054             : // This is the rotation matrix that brings reference to positions
    1055             : // i.e. matmul(rotation,reference[iat])+shift is fitted to positions[iat]
    1056             : 
    1057        6810 :   rotation[0][0]=q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3];
    1058        6810 :   rotation[1][1]=q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3];
    1059        6810 :   rotation[2][2]=q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3];
    1060        6810 :   rotation[0][1]=2*(+q[0]*q[3]+q[1]*q[2]);
    1061        6810 :   rotation[0][2]=2*(-q[0]*q[2]+q[1]*q[3]);
    1062        6810 :   rotation[1][2]=2*(+q[0]*q[1]+q[2]*q[3]);
    1063        6810 :   rotation[1][0]=2*(-q[0]*q[3]+q[1]*q[2]);
    1064        6810 :   rotation[2][0]=2*(+q[0]*q[2]+q[1]*q[3]);
    1065        6810 :   rotation[2][1]=2*(-q[0]*q[1]+q[2]*q[3]);
    1066             : 
    1067             : 
    1068        6810 :   if(!alEqDis or !only_rotation) {
    1069       13620 :     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];
    1070       13620 :     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];
    1071       13620 :     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];
    1072       13620 :     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]));
    1073       13620 :     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]));
    1074       13620 :     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]));
    1075       13620 :     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]));
    1076       13620 :     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]));
    1077       13620 :     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]));
    1078             :   }
    1079             : 
    1080        6810 :   d.resize(n);
    1081             : 
    1082             :   // calculate rotation matrix derivatives and components distances needed for components only when align!=displacement
    1083        6810 :   if(!alEqDis)ddist_drotation.zero();
    1084             :   #pragma omp simd
    1085             :   for(unsigned iat=0; iat<n; iat++) {
    1086             :     // components differences: this is useful externally
    1087      385008 :     d[iat]=positions[iat]-cp - matmul(rotation,reference[iat]-cr);
    1088             :     //cerr<<"D "<<iat<<" "<<d[iat][0]<<" "<<d[iat][1]<<" "<<d[iat][2]<<"\n";
    1089             :   }
    1090             :   // ddist_drotation if needed
    1091        6810 :   if(!alEqDis or !only_rotation)
    1092      199314 :     for (unsigned iat=0; iat<n; iat++)
    1093      385008 :       ddist_drotation+=-2*displace[iat]*extProduct(d[iat],reference[iat]-cr);
    1094             : 
    1095        6810 :   if(!alEqDis or !only_rotation) {
    1096        6810 :     ddist_drr01.zero();
    1097       88530 :     for(unsigned i=0; i<3; i++) for(unsigned j=0; j<3; j++) ddist_drr01+=ddist_drotation[i][j]*drotation_drr01[i][j];
    1098             :   }
    1099             :   // transfer this bools to the cd so that this settings will be reflected in the other calls
    1100        6810 :   this->alEqDis=alEqDis;
    1101        6810 :   this->safe=safe;
    1102        6810 :   isInitialized=true;
    1103             : 
    1104        6810 : }
    1105             : /// just retrieve the distance already calculated
    1106       52002 : double RMSDCoreData::getDistance( bool squared) {
    1107             : 
    1108       52002 :   if(!isInitialized)plumed_merror("getDistance cannot calculate the distance without being initialized first by doCoreCalc ");
    1109             : 
    1110             :   double localDist=0.0;
    1111      104004 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1112       52002 :   if(safe || !alEqDis) localDist=0.0;
    1113             :   else
    1114           0 :     localDist=eigenvals[0]+rr00+rr11;
    1115      104004 :   #pragma omp simd reduction(+:localDist)
    1116             :   for(unsigned iat=0; iat<n; iat++) {
    1117      683748 :     if(alEqDis) {
    1118     2047779 :       if(safe) localDist+=align[iat]*modulo2(d[iat]);
    1119             :     } else {
    1120        3465 :       localDist+=displace[iat]*modulo2(d[iat]);
    1121             :     }
    1122             :   }
    1123       52002 :   if(!squared) {
    1124          85 :     dist=sqrt(localDist);
    1125          85 :     distanceIsMSD=false;
    1126             :   } else {
    1127       51917 :     dist=localDist;
    1128       51917 :     distanceIsMSD=true;
    1129             :   }
    1130       52002 :   hasDistance=true;
    1131       52002 :   return dist;
    1132             : }
    1133             : 
    1134       45192 : void RMSDCoreData::doCoreCalcWithCloseStructure(bool safe,bool alEqDis, Tensor & rotationPosClose, Tensor & rotationRefClose, std::array<std::array<Tensor,3>,3> & drotationPosCloseDrr01) {
    1135             : 
    1136       90384 :   unsigned natoms = reference.size();
    1137       45192 :   Tensor ddist_drxy;
    1138       45192 :   ddist_drr01.zero();
    1139       45192 :   d.resize(natoms);
    1140             : 
    1141             :   // center of mass managing: must subtract the center from the position or not?
    1142       45192 :   Vector cp; cp.zero(); if(!cpositions_is_removed)cp=cpositions;
    1143       45192 :   Vector cr; cr.zero(); if(!creference_is_removed)cr=creference;
    1144             :   //distance = \sum_{n=0}^{N} w_n(x_n-cpos-R_{XY} R_{AY} a_n)^2
    1145             : 
    1146       45192 :   Tensor rotation = matmul(rotationPosClose, rotationRefClose);
    1147             : 
    1148             :   #pragma omp simd
    1149             :   for (unsigned iat=0; iat<natoms; iat++) {
    1150     2349984 :     d[iat] = positions[iat] - cp - matmul(rotation, reference[iat]-cr);
    1151             :   }
    1152       45192 :   if (!alEqDis) {
    1153           0 :     for (unsigned iat=0; iat<natoms; iat++) {
    1154             :       //dist = \sum w_i(x_i - cpos - R_xy * R_ay * a_i)
    1155           0 :       ddist_drxy += -2*displace[iat]*extProduct(matmul(d[iat], rotationRefClose), reference[iat]-cr);
    1156             :     }
    1157             :   }
    1158             : 
    1159       45192 :   if (!alEqDis) {
    1160           0 :     for(unsigned i=0; i<3; i++)
    1161           0 :       for(unsigned j=0; j<3; j++)
    1162           0 :         ddist_drr01+=ddist_drxy[i][j]*drotationPosCloseDrr01[i][j];
    1163             :   }
    1164       45192 :   this->alEqDis=alEqDis;
    1165       45192 :   this->safe=safe;
    1166       45192 :   isInitialized=true;
    1167       45192 : }
    1168             : 
    1169       50850 : std::vector<Vector> RMSDCoreData::getDDistanceDPositions() {
    1170             :   std::vector<Vector>  derivatives;
    1171      101700 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1172       50850 :   Vector ddist_dcpositions;
    1173       50850 :   derivatives.resize(n);
    1174             :   double prefactor=1.0;
    1175       50850 :   if(!distanceIsMSD) prefactor*=0.5/dist;
    1176       50850 :   plumed_massert(!retrieve_only_rotation,"You used  only_rotation=true in doCoreCalc therefore you cannot retrieve this information now");
    1177       50850 :   if(!hasDistance)plumed_merror("getDPositionsDerivatives needs to calculate the distance via getDistance first !");
    1178       50850 :   if(!isInitialized)plumed_merror("getDPositionsDerivatives needs to initialize the coreData first!");
    1179       50850 :   Vector csum;
    1180     1389354 :   for(unsigned iat=0; iat<n; iat++) {
    1181      669252 :     if(alEqDis) {
    1182             : // there is no need for derivatives of rotation and shift here as it is by construction zero
    1183             : // (similar to Hellman-Feynman forces)
    1184     2673588 :       derivatives[iat]= 2*prefactor*align[iat]*d[iat];
    1185             :     } else {
    1186             : // these are the derivatives assuming the roto-translation as frozen
    1187        2565 :       Vector tmp1=2*displace[iat]*d[iat];
    1188         855 :       derivatives[iat]=tmp1;
    1189             : // derivative of cpositions
    1190         855 :       ddist_dcpositions+=-tmp1;
    1191             :       // these needed for com corrections
    1192        2565 :       Vector tmp2=matmul(ddist_drr01,reference[iat]-creference)*align[iat];
    1193         855 :       derivatives[iat]+=tmp2;
    1194         855 :       csum+=tmp2;
    1195             :     }
    1196             :   }
    1197             : 
    1198       50850 :   if(!alEqDis)
    1199             :     #pragma omp simd
    1200        3420 :     for(unsigned iat=0; iat<n; iat++) {derivatives[iat]= prefactor*(derivatives[iat]+(ddist_dcpositions-csum)*align[iat]); }
    1201             : 
    1202       50850 :   return derivatives;
    1203             : }
    1204             : 
    1205           1 : std::vector<Vector>  RMSDCoreData::getDDistanceDReference() {
    1206             :   std::vector<Vector>  derivatives;
    1207           2 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1208           1 :   Vector ddist_dcreference;
    1209           1 :   derivatives.resize(n);
    1210             :   double prefactor=1.0;
    1211           1 :   if(!distanceIsMSD) prefactor*=0.5/dist;
    1212           1 :   Vector csum;
    1213             : 
    1214           1 :   plumed_massert(!retrieve_only_rotation,"You used  only_rotation=true in doCoreCalc therefore you cannot retrieve this information now");
    1215           1 :   if(!hasDistance)plumed_merror("getDDistanceDReference needs to calculate the distance via getDistance first !");
    1216           1 :   if(!isInitialized)plumed_merror("getDDistanceDReference to initialize the coreData first!");
    1217             :   // get the transpose rotation
    1218           1 :   Tensor t_rotation=rotation.transpose();
    1219           1 :   Tensor t_ddist_drr01=ddist_drr01.transpose();
    1220             : 
    1221             : // third expensive loop: derivatives
    1222        1711 :   for(unsigned iat=0; iat<n; iat++) {
    1223         855 :     if(alEqDis) {
    1224             : // there is no need for derivatives of rotation and shift here as it is by construction zero
    1225             : // (similar to Hellman-Feynman forces)
    1226             :       //TODO: check this derivative down here
    1227           0 :       derivatives[iat]= -2*prefactor*align[iat]*matmul(t_rotation,d[iat]);
    1228             :     } else {
    1229             : // these are the derivatives assuming the roto-translation as frozen
    1230        2565 :       Vector tmp1=2*displace[iat]*matmul(t_rotation,d[iat]);
    1231         855 :       derivatives[iat]= -tmp1;
    1232             : // derivative of cpositions
    1233         855 :       ddist_dcreference+=tmp1;
    1234             :       // these below are needed for com correction
    1235        2565 :       Vector tmp2=matmul(t_ddist_drr01,positions[iat]-cpositions)*align[iat];
    1236         855 :       derivatives[iat]+=tmp2;
    1237         855 :       csum+=tmp2;
    1238             :     }
    1239             :   }
    1240             : 
    1241           1 :   if(!alEqDis)
    1242             :     #pragma omp simd
    1243        3420 :     for(unsigned iat=0; iat<n; iat++) {derivatives[iat]= prefactor*(derivatives[iat]+(ddist_dcreference-csum)*align[iat]);}
    1244             : 
    1245           1 :   return derivatives;
    1246             : }
    1247             : 
    1248             : /// this version does not calculate the derivative of rotation matrix as needed for SOMA
    1249           0 : std::vector<Vector>  RMSDCoreData::getDDistanceDReferenceSOMA() {
    1250             :   std::vector<Vector>  derivatives;
    1251           0 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1252           0 :   Vector ddist_dcreference;
    1253           0 :   derivatives.resize(n);
    1254             :   double prefactor=1.0;
    1255           0 :   if(!distanceIsMSD) prefactor*=0.5/dist;
    1256           0 :   Vector csum,tmp1,tmp2;
    1257             : 
    1258           0 :   plumed_massert(!retrieve_only_rotation,"You used  only_rotation=true in doCoreCalc therefore you cannot retrieve this information now");
    1259           0 :   if(!hasDistance)plumed_merror("getDDistanceDReference needs to calculate the distance via getDistance first !");
    1260           0 :   if(!isInitialized)plumed_merror("getDDistanceDReference to initialize the coreData first!");
    1261             :   // get the transpose rotation
    1262           0 :   Tensor t_rotation=rotation.transpose();
    1263             : 
    1264             : // third expensive loop: derivatives
    1265           0 :   for(unsigned iat=0; iat<n; iat++) {
    1266           0 :     if(alEqDis) {
    1267             : // there is no need for derivatives of rotation and shift here as it is by construction zero
    1268             : // (similar to Hellman-Feynman forces)
    1269             :       //TODO: check this derivative down here
    1270           0 :       derivatives[iat]= -2*prefactor*align[iat]*matmul(t_rotation,d[iat]);
    1271             :     } else {
    1272             : // these are the derivatives assuming the roto-translation as frozen
    1273           0 :       tmp1=2*displace[iat]*matmul(t_rotation,d[iat]);
    1274           0 :       derivatives[iat]= -tmp1;
    1275             : // derivative of cpositions
    1276           0 :       ddist_dcreference+=tmp1;
    1277             :     }
    1278             :   }
    1279             : 
    1280           0 :   if(!alEqDis) for(unsigned iat=0; iat<n; iat++)derivatives[iat]=prefactor*(derivatives[iat]+ddist_dcreference*align[iat]);
    1281             : 
    1282           0 :   return derivatives;
    1283             : }
    1284             : 
    1285             : 
    1286             : 
    1287             : /*
    1288             : This below is the derivative of the rotation matrix that aligns the reference onto the positions
    1289             : respect to positions
    1290             : note that the this transformation overlap the  reference onto position
    1291             : if inverseTransform=true then aligns the positions onto reference
    1292             : */
    1293        5045 : Matrix<std::vector<Vector> >  RMSDCoreData::getDRotationDPositions( bool inverseTransform ) {
    1294       10090 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1295        5045 :   plumed_massert(!retrieve_only_rotation,"You used  only_rotation=true in doCoreCalc therefore you cannot retrieve this information now");
    1296        5045 :   if(!isInitialized)plumed_merror("getDRotationDPosition to initialize the coreData first!");
    1297             :   Matrix<std::vector<Vector> > DRotDPos=Matrix<std::vector<Vector> >(3,3);
    1298             :   // remember drotation_drr01 is Tensor drotation_drr01[3][3]
    1299             :   //           (3x3 rot) (3x3 components of rr01)
    1300        5045 :   std::vector<Vector> v(n);
    1301        5045 :   Vector csum;
    1302             :   // these below could probably be calculated in the main routine
    1303        5045 :   Vector cp; cp.zero(); if(!cpositions_is_removed)cp=cpositions;
    1304        5045 :   Vector cr; cr.zero(); if(!creference_is_removed)cr=creference;
    1305      222440 :   for(unsigned iat=0; iat<n; iat++) csum+=(reference[iat]-cr)*align[iat];
    1306      294905 :   for(unsigned iat=0; iat<n; iat++) v[iat]=(reference[iat]-cr-csum)*align[iat];
    1307       35315 :   for(unsigned a=0; a<3; a++) {
    1308      105945 :     for(unsigned b=0; b<3; b++) {
    1309       45405 :       if(inverseTransform) {
    1310       45405 :         DRotDPos[b][a].resize(n);
    1311     1349775 :         for(unsigned iat=0; iat<n; iat++) {
    1312     2608740 :           DRotDPos[b][a][iat]=matmul(drotation_drr01[a][b],v[iat]);
    1313             :         }
    1314             :       } else {
    1315           0 :         DRotDPos[a][b].resize(n);
    1316           0 :         for(unsigned iat=0; iat<n; iat++) {
    1317           0 :           DRotDPos[a][b][iat]=matmul(drotation_drr01[a][b],v[iat]);
    1318             :         }
    1319             :       }
    1320             :     }
    1321             :   }
    1322        5045 :   return DRotDPos;
    1323             : }
    1324             : 
    1325             : /*
    1326             : This below is the derivative of the rotation matrix that aligns the reference onto the positions
    1327             : respect to reference
    1328             : note that the this transformation overlap the  reference onto position
    1329             : if inverseTransform=true then aligns the positions onto reference
    1330             : */
    1331           0 : Matrix<std::vector<Vector> >  RMSDCoreData::getDRotationDReference( bool inverseTransform ) {
    1332           0 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1333           0 :   plumed_massert(!retrieve_only_rotation,"You used  only_rotation=true in doCoreCalc therefore you cannot retrieve this information now");
    1334           0 :   if(!isInitialized)plumed_merror("getDRotationDPositions to initialize the coreData first!");
    1335             :   Matrix<std::vector<Vector> > DRotDRef=Matrix<std::vector<Vector> >(3,3);
    1336             :   // remember drotation_drr01 is Tensor drotation_drr01[3][3]
    1337             :   //           (3x3 rot) (3x3 components of rr01)
    1338           0 :   std::vector<Vector> v(n);
    1339           0 :   Vector csum;
    1340             :   // these below could probably be calculated in the main routine
    1341           0 :   Vector cp; cp.zero(); if(!cpositions_is_removed)cp=cpositions;
    1342           0 :   Vector cr; cr.zero(); if(!creference_is_removed)cr=creference;
    1343           0 :   for(unsigned iat=0; iat<n; iat++) csum+=(positions[iat]-cp)*align[iat];
    1344           0 :   for(unsigned iat=0; iat<n; iat++) v[iat]=(positions[iat]-cp-csum)*align[iat];
    1345             : 
    1346           0 :   for(unsigned a=0; a<3; a++) {
    1347           0 :     for(unsigned b=0; b<3; b++) {
    1348           0 :       Tensor t_drotation_drr01=drotation_drr01[a][b].transpose();
    1349           0 :       if(inverseTransform) {
    1350           0 :         DRotDRef[b][a].resize(n);
    1351           0 :         for(unsigned iat=0; iat<n; iat++) {
    1352           0 :           DRotDRef[b][a][iat]=matmul(t_drotation_drr01,v[iat]);
    1353             :         }
    1354             :       } else {
    1355           0 :         DRotDRef[a][b].resize(n);
    1356           0 :         for(unsigned iat=0; iat<n; iat++) {
    1357           0 :           DRotDRef[a][b][iat]=matmul(t_drotation_drr01,v[iat]);
    1358             :         }
    1359             :       }
    1360             :     }
    1361             :   }
    1362           0 :   return DRotDRef;
    1363             : }
    1364             : 
    1365             : 
    1366           0 : std::vector<Vector> RMSDCoreData::getAlignedReferenceToPositions() {
    1367             :   std::vector<Vector> alignedref;
    1368           0 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1369           0 :   alignedref.resize(n);
    1370           0 :   if(!isInitialized)plumed_merror("getAlignedReferenceToPostions needs to initialize the coreData first!");
    1371             :   // avoid to calculate matrix element but use the sum of what you have
    1372           0 :   Vector cp; cp.zero(); if(!cpositions_is_removed)cp=cpositions;
    1373           0 :   for(unsigned iat=0; iat<n; iat++)alignedref[iat]=-d[iat]+positions[iat]-cp;
    1374           0 :   return alignedref;
    1375             : }
    1376        4985 : std::vector<Vector> RMSDCoreData::getAlignedPositionsToReference() {
    1377             :   std::vector<Vector> alignedpos;
    1378        4985 :   if(!isInitialized)plumed_merror("getAlignedPostionsToReference needs to initialize the coreData first!");
    1379        9970 :   const unsigned n=static_cast<unsigned int>(positions.size());
    1380        4985 :   alignedpos.resize(n);
    1381        4985 :   Vector cp; cp.zero(); if(!cpositions_is_removed)cp=cpositions;
    1382             :   // avoid to calculate matrix element but use the sum of what you have
    1383      221480 :   for(unsigned iat=0; iat<n; iat++)alignedpos[iat]=matmul(rotation.transpose(),positions[iat]-cp);
    1384        4985 :   return alignedpos;
    1385             : }
    1386             : 
    1387             : 
    1388        5045 : std::vector<Vector> RMSDCoreData::getCenteredPositions() {
    1389             :   std::vector<Vector> centeredpos;
    1390       10090 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1391        5045 :   centeredpos.resize(n);
    1392        5045 :   if(!isInitialized)plumed_merror("getCenteredPositions needs to initialize the coreData first!");
    1393             :   // avoid to calculate matrix element but use the sum of what you have
    1394      149975 :   for(unsigned iat=0; iat<n; iat++)centeredpos[iat]=positions[iat]-cpositions;
    1395        5045 :   return centeredpos;
    1396             : }
    1397             : 
    1398        4985 : std::vector<Vector> RMSDCoreData::getCenteredReference() {
    1399             :   std::vector<Vector> centeredref;
    1400        9970 :   const unsigned n=static_cast<unsigned int>(reference.size());
    1401        4985 :   centeredref.resize(n);
    1402        4985 :   if(!isInitialized)plumed_merror("getCenteredReference needs to initialize the coreData first!");
    1403             :   // avoid to calculate matrix element but use the sum of what you have
    1404        4985 :   Vector cr; cr.zero(); if(!creference_is_removed)cr=creference;
    1405      149315 :   for(unsigned iat=0; iat<n; iat++)centeredref[iat]=reference[iat]-cr;
    1406        4985 :   return centeredref;
    1407             : }
    1408             : 
    1409             : 
    1410          60 : Vector RMSDCoreData::getPositionsCenter() {
    1411          60 :   if(!isInitialized)plumed_merror("getCenteredPositions needs to initialize the coreData first!");
    1412          60 :   return cpositions;
    1413             : }
    1414             : 
    1415           0 : Vector RMSDCoreData::getReferenceCenter() {
    1416           0 :   if(!isInitialized)plumed_merror("getCenteredPositions needs to initialize the coreData first!");
    1417           0 :   return creference;
    1418             : }
    1419             : 
    1420        1764 : Tensor RMSDCoreData::getRotationMatrixReferenceToPositions() {
    1421        1764 :   if(!isInitialized)plumed_merror("getRotationMatrixReferenceToPositions needs to initialize the coreData first!");
    1422        1764 :   return rotation;
    1423             : }
    1424             : 
    1425        5045 : Tensor RMSDCoreData::getRotationMatrixPositionsToReference() {
    1426        5045 :   if(!isInitialized)plumed_merror("getRotationMatrixReferenceToPositions needs to initialize the coreData first!");
    1427        5045 :   return rotation.transpose();
    1428             : }
    1429             : 
    1430        1092 : const std::array<std::array<Tensor,3>,3> &  RMSDCoreData::getDRotationDRr01() const {
    1431        1092 :   if(!isInitialized)plumed_merror("getDRotationDRr01 needs to initialize the coreData first!");
    1432        1092 :   return drotation_drr01;
    1433             : }
    1434             : 
    1435             : 
    1436             : 
    1437             : template double RMSD::optimalAlignment<true,true>(const  std::vector<double>  & align,
    1438             :     const  std::vector<double>  & displace,
    1439             :     const std::vector<Vector> & positions,
    1440             :     const std::vector<Vector> & reference,
    1441             :     std::vector<Vector>  & derivatives, bool squared)const;
    1442             : template double RMSD::optimalAlignment<true,false>(const  std::vector<double>  & align,
    1443             :     const  std::vector<double>  & displace,
    1444             :     const std::vector<Vector> & positions,
    1445             :     const std::vector<Vector> & reference,
    1446             :     std::vector<Vector>  & derivatives, bool squared)const;
    1447             : template double RMSD::optimalAlignment<false,true>(const  std::vector<double>  & align,
    1448             :     const  std::vector<double>  & displace,
    1449             :     const std::vector<Vector> & positions,
    1450             :     const std::vector<Vector> & reference,
    1451             :     std::vector<Vector>  & derivatives, bool squared)const;
    1452             : template double RMSD::optimalAlignment<false,false>(const  std::vector<double>  & align,
    1453             :     const  std::vector<double>  & displace,
    1454             :     const std::vector<Vector> & positions,
    1455             :     const std::vector<Vector> & reference,
    1456             :     std::vector<Vector>  & derivatives, bool squared)const;
    1457             : 
    1458             : 
    1459             : 
    1460        5517 : }

Generated by: LCOV version 1.14