1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module for handling all types of structural superimpositions."""
24
25
26 from copy import deepcopy
27 from math import pi
28 from numpy import diag, dot, eye, float64, outer, sign, transpose, zeros
29 from numpy.linalg import det, norm, svd
30
31
32 from lib.structure.statistics import calc_mean_structure
33 from lib.geometry.rotations import R_to_axis_angle, R_to_euler_zyz
34
35
37 """Calculate the centroid of the structural coordinates.
38
39 @keyword coord: The atomic coordinates.
40 @type coord: numpy rank-2, Nx3 array
41 @return: The centroid.
42 @rtype: numpy rank-1, 3D array
43 """
44
45
46 centroid = coords.sum(0) / coords.shape[0]
47
48
49 return centroid
50
51
53 """Superimpose a set of structural models using the fit to first algorithm.
54
55 @keyword models: The list of models to superimpose.
56 @type models: list of int
57 @keyword coord: The list of coordinates of all models to superimpose. The first index is the models, the second is the atomic positions, and the third is the xyz coordinates.
58 @type coord: list of numpy rank-2, Nx3 arrays
59 @keyword centroid: An alternative position of the centroid to allow for different superpositions, for example of pivot point motions.
60 @type centroid: list of float or numpy rank-1, 3D array
61 @return: The lists of translation vectors, rotation matrices, and rotation pivots.
62 @rtype: list of numpy rank-1 3D arrays, list of numpy rank-2 3D arrays, list of numpy rank-1 3D arrays
63 """
64
65
66 print("\nSuperimposition of structural models %s using the 'fit to first' algorithm." % models)
67
68
69 T_list = [zeros(3, float64)]
70 R_list = [eye(3, dtype=float64)]
71 pivot_list = [zeros(3, float64)]
72
73
74 for i in range(1, len(models)):
75
76 trans_vect, trans_dist, R, axis, angle, pivot = kabsch(name_from='model %s'%models[0], name_to='model %s'%models[i], coord_from=coord[i], coord_to=coord[0], centroid=centroid)
77
78
79 T_list.append(trans_vect)
80 R_list.append(R)
81 pivot_list.append(pivot)
82
83
84 return T_list, R_list, pivot_list
85
86
87 -def fit_to_mean(models=None, coord=None, centroid=None, verbosity=1):
88 """Superimpose a set of structural models using the fit to first algorithm.
89
90 @keyword models: The list of models to superimpose.
91 @type models: list of int
92 @keyword coord: The list of coordinates of all models to superimpose. The first index is the models, the second is the atomic positions, and the third is the xyz coordinates.
93 @type coord: list of numpy rank-2, Nx3 arrays
94 @keyword centroid: An alternative position of the centroid to allow for different superpositions, for example of pivot point motions.
95 @type centroid: list of float or numpy rank-1, 3D array
96 @keyword verbosity: The amount of information to print out. If 0, nothing will be printed.
97 @type verbosity: int
98 @return: The lists of translation vectors, rotation matrices, and rotation pivots.
99 @rtype: list of numpy rank-1 3D arrays, list of numpy rank-2 3D arrays, list of numpy rank-1 3D arrays
100 """
101
102
103 if verbosity:
104 print("\nSuperimposition of structural models %s using the 'fit to mean' algorithm." % models)
105
106
107 orig_coord = deepcopy(coord)
108
109
110 T_list = []
111 R_list = []
112 pivot_list = []
113
114
115 N = len(coord[0])
116 mean = zeros((N, 3), float64)
117
118
119 converged = False
120 iter = 0
121 while not converged:
122
123 if verbosity:
124 print("\nIteration %i of the algorithm." % iter)
125 print("%-10s%-25s%-25s" % ("Model", "Translation (Angstrom)", "Rotation (deg)"))
126
127
128 calc_mean_structure(coord, mean)
129
130
131 converged = True
132 for i in range(len(models)):
133
134 trans_vect, trans_dist, R, axis, angle, pivot = kabsch(name_from='model %s'%models[0], name_to='mean', coord_from=coord[i], coord_to=mean, centroid=centroid, verbosity=0)
135
136
137 if verbosity:
138 print("%-10i%25.3g%25.3g" % (i, trans_dist, (angle / 2.0 / pi * 360.0)))
139
140
141 for j in range(N):
142
143 coord[i][j] += trans_vect
144
145
146 coord[i][j] -= pivot
147
148
149 coord[i][j] = dot(R, coord[i][j])
150
151
152 coord[i][j] += pivot
153
154
155 if trans_dist > 1e-10 or angle > 1e-10:
156 converged = False
157
158
159 iter += 1
160
161
162 for i in range(len(models)):
163
164 trans_vect, trans_dist, R, axis, angle, pivot = kabsch(name_from='model %s'%models[i], name_to='the mean structure', coord_from=orig_coord[i], coord_to=mean, centroid=centroid, verbosity=0)
165
166
167 T_list.append(trans_vect)
168 R_list.append(R)
169 pivot_list.append(pivot)
170
171
172 return T_list, R_list, pivot_list
173
174
175 -def kabsch(name_from=None, name_to=None, coord_from=None, coord_to=None, centroid=None, verbosity=1):
176 """Calculate the rotational and translational displacements between the two given coordinate sets.
177
178 This uses the U{Kabsch algorithm<http://en.wikipedia.org/wiki/Kabsch_algorithm>}.
179
180
181 @keyword name_from: The name of the starting structure, used for the printouts.
182 @type name_from: str
183 @keyword name_to: The name of the ending structure, used for the printouts.
184 @type name_to: str
185 @keyword coord_from: The list of atomic coordinates for the starting structure.
186 @type coord_from: numpy rank-2, Nx3 array
187 @keyword coord_to: The list of atomic coordinates for the ending structure.
188 @type coord_to: numpy rank-2, Nx3 array
189 @keyword centroid: An alternative position of the centroid, used for studying pivoted systems.
190 @type centroid: list of float or numpy rank-1, 3D array
191 @return: The translation vector T, translation distance d, rotation matrix R, rotation axis r, rotation angle theta, and the rotational pivot defined as the centroid of the ending structure.
192 @rtype: numpy rank-1 3D array, float, numpy rank-2 3D array, numpy rank-1 3D array, float, numpy rank-1 3D array
193 """
194
195
196 if centroid != None:
197 centroid_from = centroid
198 centroid_to = centroid
199 else:
200 centroid_from = find_centroid(coord_from)
201 centroid_to = find_centroid(coord_to)
202
203
204 trans_vect = centroid_to - centroid_from
205 trans_dist = norm(trans_vect)
206
207
208 R = kabsch_rotation(coord_from=coord_from, coord_to=coord_to, centroid_from=centroid_from, centroid_to=centroid_to)
209 axis, angle = R_to_axis_angle(R)
210 a, b, g = R_to_euler_zyz(R)
211
212
213 if verbosity >= 1:
214 print("\n\nCalculating the rotational and translational displacements from %s to %s using the Kabsch algorithm.\n" % (name_from, name_to))
215 print("Start centroid: [%20.15f, %20.15f, %20.15f]" % (centroid_from[0], centroid_from[1], centroid_from[2]))
216 print("End centroid: [%20.15f, %20.15f, %20.15f]" % (centroid_to[0], centroid_to[1], centroid_to[2]))
217 print("Translation vector: [%20.15f, %20.15f, %20.15f]" % (trans_vect[0], trans_vect[1], trans_vect[2]))
218 print("Translation distance: %.15f" % trans_dist)
219 print("Rotation matrix:")
220 print(" [[%20.15f, %20.15f, %20.15f]" % (R[0, 0], R[0, 1], R[0, 2]))
221 print(" [%20.15f, %20.15f, %20.15f]" % (R[1, 0], R[1, 1], R[1, 2]))
222 print(" [%20.15f, %20.15f, %20.15f]]" % (R[2, 0], R[2, 1], R[2, 2]))
223 print("Rotation axis: [%20.15f, %20.15f, %20.15f]" % (axis[0], axis[1], axis[2]))
224 print("Rotation euler angles: [%20.15f, %20.15f, %20.15f]" % (a, b, g))
225 print("Rotation angle (deg): %.15f" % (angle / 2.0 / pi * 360.0))
226
227
228 return trans_vect, trans_dist, R, axis, angle, centroid_to
229
230
231 -def kabsch_rotation(coord_from=None, coord_to=None, centroid_from=None, centroid_to=None):
232 """Calculate the rotation via SVD.
233
234 @keyword coord_from: The list of atomic coordinates for the starting structure.
235 @type coord_from: numpy rank-2, Nx3 array
236 @keyword coord_to: The list of atomic coordinates for the ending structure.
237 @type coord_to: numpy rank-2, Nx3 array
238 @keyword centroid_from: The starting centroid.
239 @type centroid_from: numpy rank-1, 3D array
240 @keyword centroid_to: The ending centroid.
241 @type centroid_to: numpy rank-1, 3D array
242 @return: The rotation matrix.
243 @rtype: numpy rank-2, 3D array
244 """
245
246
247 A = zeros((3, 3), float64)
248
249
250 for i in range(coord_from.shape[0]):
251
252 orig_from = coord_from[i] - centroid_from
253 orig_to = coord_to[i] - centroid_to
254
255
256 A += outer(orig_from, orig_to)
257
258
259 U, S, V = svd(A)
260
261
262 d = sign(det(A))
263 D = diag([1, 1, d])
264
265
266 R = dot(transpose(V), dot(D, transpose(U)))
267
268
269 return R
270