Gemmi C++ API
Loading...
Searching...
No Matches
qcp.hpp
Go to the documentation of this file.
1// Structural superposition, the QCP method.
2//
3// This is modified code from qcprot.c from https://theobald.brandeis.edu/qcp/
4// The original qcprot.c file startsd with the following info and license:
5
6/*******************************************************************************
7 * -/_|:|_|_\-
8 *
9 * File: qcprot.c
10 * Version: 1.5
11 *
12 * Function: Rapid calculation of the least-squares rotation using a
13 * quaternion-based characteristic polynomial and
14 * a cofactor matrix
15 *
16 * Author(s): Douglas L. Theobald
17 * Department of Biochemistry
18 * MS 009
19 * Brandeis University
20 * 415 South St
21 * Waltham, MA 02453
22 * USA
23 *
24 * dtheobald@brandeis.edu
25 *
26 * Pu Liu
27 * Johnson & Johnson Pharmaceutical Research and Development, L.L.C.
28 * 665 Stockton Drive
29 * Exton, PA 19341
30 * USA
31 *
32 * pliu24@its.jnj.com
33 *
34 *
35 * If you use this QCP rotation calculation method in a publication, please
36 * reference:
37 *
38 * Douglas L. Theobald (2005)
39 * "Rapid calculation of RMSD using a quaternion-based characteristic
40 * polynomial."
41 * Acta Crystallographica A 61(4):478-480.
42 *
43 * Pu Liu, Dmitris K. Agrafiotis, and Douglas L. Theobald (2009)
44 * "Fast determination of the optimal rotational matrix for macromolecular
45 * superpositions."
46 * Journal of Computational Chemistry 31(7):1561-1563.
47 *
48 * Copyright (c) 2009-2016 Pu Liu and Douglas L. Theobald
49 * All rights reserved.
50 *
51 * Redistribution and use in source and binary forms, with or without modification, are permitted
52 * provided that the following conditions are met:
53 *
54 * * Redistributions of source code must retain the above copyright notice, this list of
55 * conditions and the following disclaimer.
56 * * Redistributions in binary form must reproduce the above copyright notice, this list
57 * of conditions and the following disclaimer in the documentation and/or other materials
58 * provided with the distribution.
59 * * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to
60 * endorse or promote products derived from this software without specific prior written
61 * permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
64 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
65 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
66 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
67 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
68 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
69 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
70 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
71 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
72 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
73 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
74 */
75
76#ifndef GEMMI_QCP_HPP_
77#define GEMMI_QCP_HPP_
78
79#include <cmath> // for fabs, sqrt
80#include <cstdio> // for fprintf (it's temporary)
81#include "math.hpp" // for Mat33
82#include "unitcell.hpp" // for Position
83
84namespace gemmi {
85
92
93// helper function
94inline double qcp_inner_product(Mat33& mat,
95 const Position* pos1, const Position& ctr1,
96 const Position* pos2, const Position& ctr2,
97 size_t len, const double* weight) {
98 double G1 = 0.0, G2 = 0.0;
99 for (size_t i = 0; i < len; ++i) {
100 Position f1 = pos1[i] - ctr1;
101 Position f2 = pos2[i] - ctr2;
102 double w = (weight != nullptr ? weight[i] : 1.);
103 Vec3 v1 = w * f1;
104 G1 += v1.dot(f1);
105 G2 += w * f2.length_sq();
106 mat[0][0] += v1.x * f2.x;
107 mat[0][1] += v1.x * f2.y;
108 mat[0][2] += v1.x * f2.z;
109 mat[1][0] += v1.y * f2.x;
110 mat[1][1] += v1.y * f2.y;
111 mat[1][2] += v1.y * f2.z;
112 mat[2][0] += v1.z * f2.x;
113 mat[2][1] += v1.z * f2.y;
114 mat[2][2] += v1.z * f2.z;
115 }
116 return (G1 + G2) * 0.5;
117}
118
119// helper function
120inline int fast_calc_rmsd_and_rotation(Mat33* rot, const Mat33& A, double *rmsd,
121 double E0, double len, double min_score) {
122 const double evecprec = 1e-6;
123 const double evalprec = 1e-11;
124
125 double Sxx, Sxy, Sxz, Syx, Syy, Syz, Szx, Szy, Szz;
126 Sxx = A[0][0]; Sxy = A[0][1]; Sxz = A[0][2];
127 Syx = A[1][0]; Syy = A[1][1]; Syz = A[1][2];
128 Szx = A[2][0]; Szy = A[2][1]; Szz = A[2][2];
129
130 double Sxx2 = Sxx * Sxx;
131 double Syy2 = Syy * Syy;
132 double Szz2 = Szz * Szz;
133
134 double Sxy2 = Sxy * Sxy;
135 double Syz2 = Syz * Syz;
136 double Sxz2 = Sxz * Sxz;
137
138 double Syx2 = Syx * Syx;
139 double Szy2 = Szy * Szy;
140 double Szx2 = Szx * Szx;
141
142 double SyzSzymSyySzz2 = 2.0 * (Syz*Szy - Syy*Szz);
143 double Sxx2Syy2Szz2Syz2Szy2 = Syy2 + Szz2 - Sxx2 + Syz2 + Szy2;
144
145 double C[4];
146 C[2] = -2.0 * (Sxx2 + Syy2 + Szz2 + Sxy2 + Syx2 + Sxz2 + Szx2 + Syz2 + Szy2);
147 C[1] = 8.0 * (Sxx*Syz*Szy + Syy*Szx*Sxz + Szz*Sxy*Syx - Sxx*Syy*Szz - Syz*Szx*Sxy - Szy*Syx*Sxz);
148
149 double SxzpSzx = Sxz + Szx;
150 double SyzpSzy = Syz + Szy;
151 double SxypSyx = Sxy + Syx;
152 double SyzmSzy = Syz - Szy;
153 double SxzmSzx = Sxz - Szx;
154 double SxymSyx = Sxy - Syx;
155 double SxxpSyy = Sxx + Syy;
156 double SxxmSyy = Sxx - Syy;
157 double Sxy2Sxz2Syx2Szx2 = Sxy2 + Sxz2 - Syx2 - Szx2;
158
165
166 /* Newton-Raphson */
167 double mxEigenV = E0;
168 int i;
169 double oldg = 0.0;
170 for (i = 0; i < 50; ++i) {
171 oldg = mxEigenV;
172 double x2 = mxEigenV * mxEigenV;
173 double b = (x2 + C[2]) * mxEigenV;
174 double a = b + C[1];
175 double delta = (a * mxEigenV + C[0]) / (2.0 * x2 * mxEigenV + b + a);
176 mxEigenV -= delta;
177 // printf("\n diff[%3d]: %16g %16g %16g", i, mxEigenV - oldg, evalprec*mxEigenV, mxEigenV);
178 if (std::fabs(mxEigenV - oldg) < std::fabs(evalprec * mxEigenV))
179 break;
180 }
181 if (i == 50)
182 std::fprintf(stderr,"\nMore than %d iterations needed!\n", i);
183
184 // the fabs() is to guard against extremely small, but *negative* numbers
185 // due to floating point error
186 double rms = std::sqrt(std::fabs(2.0 * (E0 - mxEigenV) / len));
187 (*rmsd) = rms;
188 // printf("\n\n %16g %16g %16g \n", rms, E0, 2.0 * (E0 - mxEigenV)/len);
189
190 if (rot == nullptr)
191 return -1;
192 if (min_score > 0)
193 if (rms < min_score)
194 return -1; // Don't bother with rotation.
195
196 double a11, a12, a13, a14, a21, a22, a23, a24;
197 a11 = SxxpSyy + Szz-mxEigenV; a12 = SyzmSzy; a13 = - SxzmSzx; a14 = SxymSyx;
198 a21 = SyzmSzy; a22 = SxxmSyy - Szz-mxEigenV; a23 = SxypSyx; a24= SxzpSzx;
199 double a31, a32, a33, a34, a41, a42, a43, a44;
200 a31 = a13; a32 = a23; a33 = Syy-Sxx-Szz - mxEigenV; a34 = SyzpSzy;
201 a41 = a14; a42 = a24; a43 = a34; a44 = Szz - SxxpSyy - mxEigenV;
203 a3344_4334 = a33 * a44 - a43 * a34; a3244_4234 = a32 * a44-a42*a34;
204 a3243_4233 = a32 * a43 - a42 * a33; a3143_4133 = a31 * a43-a41*a33;
206 double q1 = a22*a3344_4334-a23*a3244_4234+a24*a3243_4233;
209 double q4 = -a21*a3243_4233+a22*a3143_4133-a23*a3142_4132;
210
211 double qsqr = q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4;
212
213 /* The following code tries to calculate another column in the adjoint matrix
214 when the norm of the current column is too small.
215 Usually this block will never be activated. To be absolutely safe this should be
216 uncommented, but it is most likely unnecessary.
217 */
218 if (qsqr < evecprec) {
219 q1 = a12*a3344_4334 - a13*a3244_4234 + a14*a3243_4233;
220 q2 = -a11*a3344_4334 + a13*a3144_4134 - a14*a3143_4133;
221 q3 = a11*a3244_4234 - a12*a3144_4134 + a14*a3142_4132;
222 q4 = -a11*a3243_4233 + a12*a3143_4133 - a13*a3142_4132;
223 qsqr = q1*q1 + q2 *q2 + q3*q3+q4*q4;
224
225 if (qsqr < evecprec) {
226 double a1324_1423 = a13 * a24 - a14 * a23, a1224_1422 = a12 * a24 - a14 * a22;
227 double a1223_1322 = a12 * a23 - a13 * a22, a1124_1421 = a11 * a24 - a14 * a21;
228 double a1123_1321 = a11 * a23 - a13 * a21, a1122_1221 = a11 * a22 - a12 * a21;
229
234 qsqr = q1*q1 + q2 *q2 + q3*q3+q4*q4;
235
236 if (qsqr < evecprec) {
237 q1 = a32 * a1324_1423 - a33 * a1224_1422 + a34 * a1223_1322;
238 q2 = -a31 * a1324_1423 + a33 * a1124_1421 - a34 * a1123_1321;
240 q4 = -a31 * a1223_1322 + a32 * a1123_1321 - a33 * a1122_1221;
241 qsqr = q1*q1 + q2 *q2 + q3*q3 + q4*q4;
242
243 if (qsqr < evecprec) {
244 /* if qsqr is still too small, return the identity matrix. */
245 *rot = Mat33();
246 return 0;
247 }
248 }
249 }
250 }
251
252 double normq = std::sqrt(qsqr);
253 q1 /= normq;
254 q2 /= normq;
255 q3 /= normq;
256 q4 /= normq;
257
258 double a2 = q1 * q1;
259 double x2 = q2 * q2;
260 double y2 = q3 * q3;
261 double z2 = q4 * q4;
262
263 double xy = q2 * q3;
264 double az = q1 * q4;
265 double zx = q4 * q2;
266 double ay = q1 * q3;
267 double yz = q3 * q4;
268 double ax = q1 * q2;
269
270 rot->a[0][0] = a2 + x2 - y2 - z2;
271 rot->a[0][1] = 2 * (xy + az);
272 rot->a[0][2] = 2 * (zx - ay);
273 rot->a[1][0] = 2 * (xy - az);
274 rot->a[1][1] = a2 - x2 + y2 - z2;
275 rot->a[1][2] = 2 * (yz + ax);
276 rot->a[2][0] = 2 * (zx + ay);
277 rot->a[2][1] = 2 * (yz - ax);
278 rot->a[2][2] = a2 - x2 - y2 + z2;
279
280 return 1;
281}
282
283// helper function
284inline Position qcp_calculate_center(const Position* pos, size_t len, const double *weight) {
285 double wsum = 0.0;
287 for (size_t i = 0; i < len; ++i) {
288 double w = (weight != nullptr ? weight[i] : 1.);
289 ctr += w * pos[i];
290 wsum += w;
291 }
292 return ctr / wsum;
293}
294
295// Calculate superposition of pos2 onto pos1 -- pos2 is movable.
296// Does not perform the superposition, only returns the operation to be used.
298 size_t len, const double* weight) {
300 result.count = len;
301
302 /* center the structures -- if precentered you can omit this step */
303 result.center1 = qcp_calculate_center(pos1, len, weight);
304 result.center2 = qcp_calculate_center(pos2, len, weight);
305
306 double wsum = 0.0;
307 if (weight == nullptr)
308 wsum = (double) len;
309 else
310 for (size_t i = 0; i < len; ++i)
311 wsum += weight[i];
312
313 Mat33 A(0);
314 /* calculate the (weighted) inner product of two structures */
315 double E0 = qcp_inner_product(A, pos1, result.center1, pos2, result.center2, len, weight);
316
317 /* calculate the RMSD & rotational matrix */
318 fast_calc_rmsd_and_rotation(&result.transform.mat, A, &result.rmsd, E0, wsum, -1);
319 result.transform.vec = Vec3(result.center1) - result.transform.mat.multiply(result.center2);
320
321 return result;
322}
323
324// Similar to superpose_positions(), but calculates RMSD only.
326 const Position* pos2,
327 size_t len, const double* weight) {
328 double result;
329
330 // center the structures
331 Position ctr1 = qcp_calculate_center(pos1, len, weight);
332 Position ctr2 = qcp_calculate_center(pos2, len, weight);
333
334 double wsum = 0.0;
335 if (weight == nullptr)
336 wsum = (double) len;
337 else
338 for (size_t i = 0; i < len; ++i)
339 wsum += weight[i];
340
341 // calculate the (weighted) inner product of two structures
342 Mat33 A(0);
343 double E0 = qcp_inner_product(A, pos1, ctr1, pos2, ctr2, len, weight);
344
345 // calculate the RMSD
346 fast_calc_rmsd_and_rotation(nullptr, A, &result, E0, wsum, -1);
347 return result;
348}
349
350} // namespace gemmi
351#endif
Position qcp_calculate_center(const Position *pos, size_t len, const double *weight)
Definition qcp.hpp:284
SupResult superpose_positions(const Position *pos1, const Position *pos2, size_t len, const double *weight)
Definition qcp.hpp:297
int fast_calc_rmsd_and_rotation(Mat33 *rot, const Mat33 &A, double *rmsd, double E0, double len, double min_score)
Definition qcp.hpp:120
double calculate_rmsd_of_superposed_positions(const Position *pos1, const Position *pos2, size_t len, const double *weight)
Definition qcp.hpp:325
Vec3_< double > Vec3
Definition math.hpp:101
double qcp_inner_product(Mat33 &mat, const Position *pos1, const Position &ctr1, const Position *pos2, const Position &ctr2, size_t len, const double *weight)
Definition qcp.hpp:94
double a[3][3]
Definition math.hpp:116
Coordinates in Angstroms - orthogonal (Cartesian) coordinates.
Definition unitcell.hpp:32
Transform transform
Definition qcp.hpp:90
double rmsd
Definition qcp.hpp:87
Position center2
Definition qcp.hpp:89
Position center1
Definition qcp.hpp:89
size_t count
Definition qcp.hpp:88