1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 """The numeric solution for the 2-site Bloch-McConnell equations for MMQ CPMG data, the U{NS MMQ 2-site<http://wiki.nmr-relax.com/NS_MMQ_2-site>} model.
27
28 Description
29 ===========
30
31 This handles proton-heteronuclear SQ, ZQ, DQ and MQ CPMG data.
32
33
34 References
35 ==========
36
37 It uses the equations of:
38
39 - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005). Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant. I{J. Am. Chem. Soc.}, B{127}, 15602-15611. (U{DOI: 10.1021/ja054550e<http://dx.doi.org/10.1021/ja054550e>}).
40
41
42 Links
43 =====
44
45 More information on the NS MMQ 2-site model can be found in the:
46
47 - U{relax wiki<http://wiki.nmr-relax.com/NS_MMQ_2-site>},
48 - U{relax manual<http://www.nmr-relax.com/manual/The_NS_MMQ_2_site_model.html>},
49 - U{relaxation dispersion page of the relax website<http://www.nmr-relax.com/analyses/relaxation_dispersion.html#NS_MMQ_2-site>}.
50 """
51
52
53 from math import floor
54 from numpy import array, conj, complex128, dot, einsum, float64, log, multiply
55 from numpy.linalg import matrix_power
56
57
58 from lib.float import isNaN
59 from lib.dispersion.matrix_exponential import matrix_exponential
60
61
62 m_r20a = array([
63 [-1, 0],
64 [ 0, 0]], float64)
65
66 m_r20b = array([
67 [ 0, 0],
68 [ 0, -1]], float64)
69
70 m_k_AB = array([
71 [-1, 0],
72 [ 1, 0]], float64)
73
74 m_k_BA = array([
75 [ 0, 1],
76 [ 0, -1]], float64)
77
78 m_dw = array([
79 [ 0, 0],
80 [ 0, 1]], float64)
81
82
83 -def rmmq_2site_rankN(R20A=None, R20B=None, dw=None, k_AB=None, k_BA=None, tcp=None):
84 """The Bloch-McConnell matrix for 2-site exchange, for rank [NE][NS][NM][NO][ND][2][2].
85
86 @keyword R20A: The transverse, spin-spin relaxation rate for state A.
87 @type R20A: numpy float array of rank [NE][NS][NM][NO][ND]
88 @keyword R20B: The transverse, spin-spin relaxation rate for state B.
89 @type R20B: numpy float array of rank [NE][NS][NM][NO][ND]
90 @keyword dw: The combined chemical exchange difference parameters between states A and B in rad/s. This can be any combination of dw and dwH.
91 @type dw: numpy float array of rank [NE][NS][NM][NO][ND]
92 @keyword k_AB: The rate of exchange from site A to B (rad/s).
93 @type k_AB: float
94 @keyword k_BA: The rate of exchange from site B to A (rad/s).
95 @type k_BA: float
96 @keyword tcp: The tau_CPMG times (1 / 4.nu1).
97 @type tcp: numpy float array of rank [NE][NS][NM][NO][ND]
98 @return: The relaxation matrix.
99 @rtype: numpy float array of rank [NE][NS][NM][NO][ND][2][2]
100 """
101
102
103 r20a_tcp = R20A * tcp
104 r20b_tcp = R20B * tcp
105 k_AB_tcp = k_AB * tcp
106 k_BA_tcp = k_BA * tcp
107
108 dw_tcp_C = dw * tcp * 1j
109
110
111
112
113
114
115
116
117 m_r20a_tcp = multiply.outer( r20a_tcp, m_r20a )
118 m_r20b_tcp = multiply.outer( r20b_tcp, m_r20b )
119
120
121 m_k_AB_tcp = multiply.outer( k_AB_tcp, m_k_AB )
122 m_k_BA_tcp = multiply.outer( k_BA_tcp, m_k_BA )
123
124
125 m_dw_tcp_C = multiply.outer( dw_tcp_C, m_dw )
126
127
128 matrix = (m_r20a_tcp + m_r20b_tcp + m_k_AB_tcp + m_k_BA_tcp + m_dw_tcp_C)
129
130 return matrix
131
132
133 -def r2eff_ns_mmq_2site_mq(M0=None, F_vector=array([1, 0], float64), R20A=None, R20B=None, pA=None, dw=None, dwH=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
134 """The 2-site numerical solution to the Bloch-McConnell equation for MQ data.
135
136 The notation used here comes from:
137
138 - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005). Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant. J. Am. Chem. Soc., 127, 15602-15611. (doi: http://dx.doi.org/10.1021/ja054550e).
139
140 and:
141
142 - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005). Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant. J. Am. Chem. Soc., 127, 15602-15611. (doi: http://dx.doi.org/10.1021/ja054550e).
143
144 This function calculates and stores the R2eff values.
145
146
147 @keyword M0: This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
148 @type M0: numpy float64, rank-1, 7D array
149 @keyword F_vector: The observable magnitisation vector. This defaults to [1, 0] for X observable magnitisation.
150 @type F_vector: numpy rank-1, 2D float64 array
151 @keyword R20A: The transverse, spin-spin relaxation rate for state A.
152 @type R20A: numpy float array of rank [NS][NM][NO][ND]
153 @keyword R20B: The transverse, spin-spin relaxation rate for state B.
154 @type R20B: numpy float array of rank [NS][NM][NO][ND]
155 @keyword pA: The population of state A.
156 @type pA: float
157 @keyword dw: The chemical exchange difference between states A and B in rad/s.
158 @type dw: numpy float array of rank [NS][NM][NO][ND]
159 @keyword dwH: The proton chemical exchange difference between states A and B in rad/s.
160 @type dwH: numpy float array of rank [NS][NM][NO][ND]
161 @keyword kex: The kex parameter value (the exchange rate in rad/s).
162 @type kex: float
163 @keyword inv_tcpmg: The inverse of the total duration of the CPMG element (in inverse seconds).
164 @type inv_tcpmg: numpy float array of rank [NS][NM][NO][ND]
165 @keyword tcp: The tau_CPMG times (1 / 4.nu1).
166 @type tcp: numpy float array of rank [NS][NM][NO][ND]
167 @keyword back_calc: The array for holding the back calculated R2eff values. Each element corresponds to one of the CPMG nu1 frequencies.
168 @type back_calc: numpy float array of rank [NS][NM][NO][ND]
169 @keyword num_points: The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
170 @type num_points: numpy int array of rank [NS][NM][NO]
171 @keyword power: The matrix exponential power array.
172 @type power: numpy int array of rank [NS][NM][NO][ND]
173 """
174
175
176 pB = 1.0 - pA
177 k_BA = pA * kex
178 k_AB = pB * kex
179
180
181 M0[0] = pA
182 M0[1] = pB
183
184
185 NS, NM, NO = num_points.shape
186
187
188
189 m1_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=-dw - dwH, k_AB=k_AB, k_BA=k_BA, tcp=tcp)
190
191 m2_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=dw - dwH, k_AB=k_AB, k_BA=k_BA, tcp=tcp)
192
193
194
195 M1_mat = matrix_exponential(m1_mat, dtype=complex128)
196
197 M2_mat = matrix_exponential(m2_mat, dtype=complex128)
198
199
200
201 M1_star_mat = conj(M1_mat)
202
203 M2_star_mat = conj(M2_mat)
204
205
206 M1_M2_mat = einsum('...ij, ...jk', M1_mat, M2_mat)
207 M2_M1_mat = einsum('...ij, ...jk', M2_mat, M1_mat)
208 M1_M2_M2_M1_mat = einsum('...ij, ...jk', M1_M2_mat, M2_M1_mat)
209 M2_M1_M1_M2_mat = einsum('...ij, ...jk', M2_M1_mat, M1_M2_mat)
210 M1_M2_star_mat = einsum('...ij, ...jk', M1_star_mat, M2_star_mat)
211 M2_M1_star_mat = einsum('...ij, ...jk', M2_star_mat, M1_star_mat)
212 M1_M2_M2_M1_star_mat = einsum('...ij, ...jk', M1_M2_star_mat, M2_M1_star_mat)
213 M2_M1_M1_M2_star_mat = einsum('...ij, ...jk', M2_M1_star_mat, M1_M2_star_mat)
214
215
216 for si in range(NS):
217
218 for mi in range(NM):
219
220 for oi in range(NO):
221 num_points_i = num_points[si, mi, oi]
222
223
224 for i in range(num_points_i):
225
226 power_i = int(power[si, mi, oi, i])
227 M1_M2_i = M1_M2_mat[si, mi, oi, i]
228 M1_M2_star_i = M1_M2_star_mat[si, mi, oi, i]
229 M2_M1_i = M2_M1_mat[si, mi, oi, i]
230 M2_M1_star_i = M2_M1_star_mat[si, mi, oi, i]
231 M1_M2_M2_M1_i = M1_M2_M2_M1_mat[si, mi, oi, i]
232 M2_M1_M1_M2_star_i = M2_M1_M1_M2_star_mat[si, mi, oi, i]
233 M2_M1_M1_M2_i = M2_M1_M1_M2_mat[si, mi, oi, i]
234 M1_M2_M2_M1_star_i = M1_M2_M2_M1_star_mat[si, mi, oi, i]
235
236
237 if power_i == 1:
238
239 A = M1_M2_i
240
241
242 B = M1_M2_star_i
243
244
245 C = M2_M1_i
246
247
248 D = M2_M1_star_i
249
250
251 elif power_i % 2 == 0:
252
253 fact = int(floor(power_i / 2))
254
255
256 A = matrix_power(M1_M2_M2_M1_i, fact)
257
258
259 B = matrix_power(M2_M1_M1_M2_star_i, fact)
260
261
262 C = matrix_power(M2_M1_M1_M2_i, fact)
263
264
265 D = matrix_power(M1_M2_M2_M1_star_i, fact)
266
267
268 else:
269
270 fact = int(floor((power_i - 1) / 2))
271
272
273 A = matrix_power(M1_M2_M2_M1_i, fact)
274 A = dot(A, M1_M2_i)
275
276
277 B = matrix_power(M1_M2_M2_M1_star_i, fact)
278 B = dot(B, M1_M2_star_i)
279
280
281 C = matrix_power(M2_M1_M1_M2_i, fact)
282 C = dot(C, M2_M1_i)
283
284
285 D = matrix_power(M2_M1_M1_M2_star_i, fact)
286 D = dot(D, M2_M1_star_i)
287
288
289 A_B = dot(A, B)
290 C_D = dot(C, D)
291 Mx = dot(dot(F_vector, (A_B + C_D)), M0)
292 Mx = Mx.real / 2.0
293 if Mx <= 0.0 or isNaN(Mx):
294 back_calc[si, mi, oi, i] = 1e99
295 else:
296 back_calc[si, mi, oi, i]= -inv_tcpmg[si, mi, oi, i] * log(Mx / pA)
297
298
299 -def r2eff_ns_mmq_2site_sq_dq_zq(M0=None, F_vector=array([1, 0], float64), R20A=None, R20B=None, pA=None, dw=None, dwH=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
300 """The 2-site numerical solution to the Bloch-McConnell equation for SQ, ZQ, and DQ data.
301
302 The notation used here comes from:
303
304 - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005). Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant. J. Am. Chem. Soc., 127, 15602-15611. (doi: http://dx.doi.org/10.1021/ja054550e).
305
306 This function calculates and stores the R2eff values.
307
308
309 @keyword M0: This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
310 @type M0: numpy float64, rank-1, 7D array
311 @keyword F_vector: The observable magnitisation vector. This defaults to [1, 0] for X observable magnitisation.
312 @type F_vector: numpy rank-1, 2D float64 array
313 @keyword R20A: The transverse, spin-spin relaxation rate for state A.
314 @type R20A: numpy float array of rank [NS][NM][NO][ND]
315 @keyword R20B: The transverse, spin-spin relaxation rate for state B.
316 @type R20B: numpy float array of rank [NS][NM][NO][ND]
317 @keyword pA: The population of state A.
318 @type pA: float
319 @keyword dw: The combined chemical exchange difference between states A and B in rad/s. It should be set to dwH for 1H SQ data, dw for heteronuclear SQ data, dwH-dw for ZQ data, and dwH+dw for DQ data.
320 @type dw: numpy float array of rank [NS][NM][NO][ND]
321 @keyword dwH: Unused - this is simply to match the r2eff_ns_mmq_2site_mq() function arguments.
322 @type dwH: numpy float array of rank [NS][NM][NO][ND]
323 @keyword kex: The kex parameter value (the exchange rate in rad/s).
324 @type kex: float
325 @keyword inv_tcpmg: The inverse of the total duration of the CPMG element (in inverse seconds).
326 @type inv_tcpmg: numpy float array of rank [NS][NM][NO][ND]
327 @keyword tcp: The tau_CPMG times (1 / 4.nu1).
328 @type tcp: numpy float array of rank [NS][NM][NO][ND]
329 @keyword back_calc: The array for holding the back calculated R2eff values. Each element corresponds to one of the CPMG nu1 frequencies.
330 @type back_calc: numpy float array of rank [NS][NM][NO][ND]
331 @keyword num_points: The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
332 @type num_points: numpy int array of rank [NS][NM][NO]
333 @keyword power: The matrix exponential power array.
334 @type power: numpy int array of rank [NS][NM][NO][ND]
335 """
336
337
338 pB = 1.0 - pA
339 k_BA = pA * kex
340 k_AB = pB * kex
341
342
343 M0[0] = pA
344 M0[1] = pB
345
346
347 NS, NM, NO = num_points.shape
348
349
350 m1_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)
351 m2_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=-dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)
352
353
354 A_pos_mat = matrix_exponential(m1_mat, dtype=complex128)
355 A_neg_mat = matrix_exponential(m2_mat, dtype=complex128)
356
357
358 evol_block_mat = einsum('...ij, ...jk', A_neg_mat, A_pos_mat)
359 evol_block_mat = einsum('...ij, ...jk', A_neg_mat, evol_block_mat)
360 evol_block_mat = einsum('...ij, ...jk', A_pos_mat, evol_block_mat)
361
362
363 for si in range(NS):
364
365 for mi in range(NM):
366
367 for oi in range(NO):
368
369 num_points_i = num_points[si, mi, oi]
370
371
372 for i in range(num_points_i):
373
374 power_i = int(power[si, mi, oi, i])
375 evol_block_i = evol_block_mat[si, mi, oi, i]
376
377
378 evol = matrix_power(evol_block_i, power_i)
379
380
381 Mx = dot(F_vector, dot(evol, M0))
382 Mx = Mx.real
383 if Mx <= 0.0 or isNaN(Mx):
384 back_calc[si, mi, oi, i] = 1e99
385 else:
386 back_calc[si, mi, oi, i] = -inv_tcpmg[si, mi, oi, i] * log(Mx / pA)
387