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> ¢eredreference, 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 ¢er_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