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