1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 from copy import deepcopy
25 from numpy import array, arange, asarray, int32, float64, max, ones, pi
26 from unittest import TestCase
27
28
29 from lib.dispersion.cr72 import r2eff_CR72
30
31 """The 1H gyromagnetic ratio."""
32 g1H = 26.7522212 * 1e7
33
34 """The 15N gyromagnetic ratio."""
35 g15N = -2.7126 * 1e7
36
37
39 """Unit tests for the lib.dispersion.cr72 relax module."""
40
42 """Set up for all unit tests."""
43
44
45 self.r2 = None
46 self.r2a = 5.0
47 self.r2b = 10.0
48 self.dw = 3.0
49 self.pA = 0.9
50 self.kex = 1000.0
51 self.spins_params = ['r2a', 'r2b', 'dw', 'pA', 'kex']
52
53
54 self.model = "CR72 full"
55 self.num_spins = 2
56 self.fields = array([800. * 1E6])
57
58
59 self.exp_type = ['SQ CPMG']
60 self.offset = [0]
61
62
63 self.relax_times = self.fields / (100 * 100. *1E6 )
64 self.ncycs = []
65 self.points = []
66 self.value = []
67 self.error = []
68 self.num_disp_points = []
69
70 for i in range(len(self.fields)):
71 ncyc = arange(2, 1000. * self.relax_times[i], 4)
72
73 self.ncycs.append(ncyc)
74
75
76 cpmg_point = ncyc / self.relax_times[i]
77
78 nr_cpmg_points = len(cpmg_point)
79 self.num_disp_points.append(nr_cpmg_points)
80
81 self.points.append(list(cpmg_point))
82 self.value.append([2.0]*len(cpmg_point))
83 self.error.append([1.0]*len(cpmg_point))
84
85
87 """Calculate and check the R2eff values."""
88
89
90 self.params = self.assemble_param_vector(r2=self.r2, r2a=self.r2a, r2b=self.r2b, dw=self.dw, pA=self.pA, kex=self.kex, spins_params=self.spins_params)
91
92
93 values, errors, cpmg_frqs, missing, frqs, exp_types, relax_times, offsets = self.return_r2eff_arrays()
94
95
96
97 end_index = []
98
99 end_index.append(len(self.exp_type) * self.num_spins * len(self.fields))
100 if self.model in ["CR72 full"]:
101 end_index.append(2 * len(self.exp_type) * self.num_spins * len(self.fields))
102
103 end_index.append(end_index[-1] + self.num_spins)
104
105
106 R20 = self.params[:end_index[1]].reshape(self.num_spins*2, len(self.fields))
107 R20A = R20[::2].flatten()
108 R20B = R20[1::2].flatten()
109 dw = self.params[end_index[1]:end_index[2]]
110 pA = self.params[end_index[2]]
111 kex = self.params[end_index[2]+1]
112
113
114 self.back_calc = deepcopy(values)
115
116
117
118 back_calc_shape = list( asarray(self.back_calc).shape )[:4]
119
120
121
122 self.max_num_disp_points = max(self.num_disp_points)
123
124
125
126
127
128 self.R20A_a = ones(back_calc_shape + [self.max_num_disp_points])
129 self.R20B_a = ones(back_calc_shape + [self.max_num_disp_points])
130 self.dw_frq_a = ones(back_calc_shape + [self.max_num_disp_points])
131 self.cpmg_frqs_a = ones(back_calc_shape + [self.max_num_disp_points])
132 self.num_disp_points_a = ones(back_calc_shape + [self.max_num_disp_points])
133 self.back_calc_a = ones(back_calc_shape + [self.max_num_disp_points])
134
135
136 for si in range(self.num_spins):
137
138 for mi in range(len(self.fields)):
139
140 num_disp_points = self.num_disp_points[mi]
141
142
143 self.cpmg_frqs_a[0][si][mi][0][:num_disp_points] = cpmg_frqs[0][mi][0]
144 self.num_disp_points_a[0][si][mi][0][:num_disp_points] = self.num_disp_points[mi]
145
146
147
148
149 for si in range(self.num_spins):
150
151 for mi in range(len(self.fields)):
152
153 num_disp_points = len(cpmg_frqs[0][mi][0])
154
155
156 r20_index = mi + si*len(self.fields)
157
158
159 self.R20A_a[0][si][mi][0] = array( [R20A[r20_index]] * self.max_num_disp_points, float64)
160 self.R20B_a[0][si][mi][0] = array( [R20B[r20_index]] * self.max_num_disp_points, float64)
161
162
163 dw_frq = dw[si] * frqs[0][si][mi]
164
165
166 self.dw_frq_a[0][si][mi][0] = array( [dw_frq] * self.max_num_disp_points, float64)
167
168
169 r2eff_CR72(r20a_orig=self.R20A_a, r20b_orig=self.R20B_a, dw_orig=self.dw_frq_a, r20a=self.R20A_a, r20b=self.R20B_a, pA=pA, dw=self.dw_frq_a, kex=kex, cpmg_frqs=self.cpmg_frqs_a, back_calc=self.back_calc_a)
170
171
172
173
174 for si in range(self.num_spins):
175
176 for mi in range(len(self.fields)):
177
178 num_disp_points = self.num_disp_points[mi]
179
180
181 self.back_calc[0][si][mi][0][:] = self.back_calc_a[0][si][mi][0][:num_disp_points]
182
183
184 for di in range(num_disp_points):
185 self.assertAlmostEqual(self.back_calc[0][si][mi][0][di], self.R20A_a[0][si][mi][0][di])
186
187
189 """Return numpy arrays of the R2eff/R1rho values and errors.
190
191 @return: The numpy array structures of the R2eff/R1rho values, errors, missing data, and corresponding Larmor frequencies. For each structure, the first dimension corresponds to the experiment types, the second the spins of a spin block, the third to the spectrometer field strength, and the fourth is the dispersion points. For the Larmor frequency structure, the fourth dimension is omitted. For R1rho-type data, an offset dimension is inserted between the spectrometer field strength and the dispersion points.
192 @rtype: lists of numpy float arrays, lists of numpy float arrays, lists of numpy float arrays, numpy rank-2 int array
193 """
194
195
196 exp_types = []
197 values = []
198 errors = []
199 missing = []
200 frqs = []
201 frqs_H = []
202 relax_times = []
203 offsets = []
204 for ei in range(len(self.exp_type)):
205 values.append([])
206 errors.append([])
207 missing.append([])
208 frqs.append([])
209 frqs_H.append([])
210 relax_times.append([])
211 offsets.append([])
212 for si in range(self.num_spins):
213 values[ei].append([])
214 errors[ei].append([])
215 missing[ei].append([])
216 frqs[ei].append([])
217 frqs_H[ei].append([])
218 offsets[ei].append([])
219 for mi in range(len(self.fields)):
220 values[ei][si].append([])
221 errors[ei][si].append([])
222 missing[ei][si].append([])
223 frqs[ei][si].append(0.0)
224 frqs_H[ei][si].append(0.0)
225 offsets[ei][si].append([])
226 for oi in range(len(self.offset)):
227 values[ei][si][mi].append([])
228 errors[ei][si][mi].append([])
229 missing[ei][si][mi].append([])
230 offsets[ei][si][mi].append([])
231 for mi in range(len(self.fields)):
232 relax_times[ei].append(None)
233
234 cpmg_frqs = []
235 for ei in range(len(self.exp_type)):
236 cpmg_frqs.append([])
237 for mi in range(len(self.fields)):
238 cpmg_frqs[ei].append([])
239 for oi in range(len(self.offset)):
240
241 cpmg_frqs[ei][mi].append([])
242
243
244
245 si = 0
246 for spin_index in range(self.num_spins):
247 data_flag = True
248
249 for ei in range(len(self.exp_type)):
250 exp_type = self.exp_type[ei]
251
252 if exp_type not in exp_types:
253 exp_types.append(exp_type)
254
255 for mi in range(len(self.fields)):
256
257 frq = self.fields[mi]
258
259
260 frqs[ei][si][mi] = 2.0 * pi * frq / g1H * g15N * 1e-6
261
262
263 cpmg_frqs[ei][mi][oi] = self.points[mi]
264
265 for oi in range(len(self.offset)):
266 for di in range(len(self.points[mi])):
267
268 missing[ei][si][mi][oi].append(0)
269
270
271 values[ei][si][mi][oi].append(self.value[mi][di])
272
273 errors[ei][si][mi][oi].append(self.error[mi][di])
274
275
276
277 relax_time = self.relax_times[mi]
278
279
280 relax_times[ei][mi] = relax_time
281
282
283 si += 1
284
285
286 relax_times = array(relax_times, float64)
287 for ei in range(len(self.exp_type)):
288 for si in range(self.num_spins):
289 for mi in range(len(self.fields)):
290 for oi in range(len(self.offset)):
291 values[ei][si][mi][oi] = array(values[ei][si][mi][oi], float64)
292 errors[ei][si][mi][oi] = array(errors[ei][si][mi][oi], float64)
293 missing[ei][si][mi][oi] = array(missing[ei][si][mi][oi], int32)
294
295
296 return values, errors, cpmg_frqs, missing, frqs, exp_types, relax_times, offsets
297
298
299 - def assemble_param_vector(self, r2=None, r2a=None, r2b=None, dw=None, pA=None, kex=None, spins_params=None):
300 """Assemble the dispersion relaxation dispersion curve fitting parameter vector.
301
302 @keyword r2: The transversal relaxation rate.
303 @type r2: float
304 @keyword r2a: The transversal relaxation rate for state A in the absence of exchange.
305 @type r2a: float
306 @keyword r2b: The transversal relaxation rate for state B in the absence of exchange.
307 @type r2b: float
308 @keyword dw: The chemical exchange difference between states A and B in ppm.
309 @type dw: float
310 @keyword pA: The population of state A.
311 @type pA: float
312 @keyword kex: The rate of exchange.
313 @type kex: float
314 @keyword spins_params: List of parameter strings used in dispersion model.
315 @type spins_params: array of strings
316 @return: An array of the parameter values of the dispersion relaxation model.
317 @rtype: numpy float array
318 """
319
320
321 param_vector = []
322
323
324 for param_name, spin_index, mi in self.loop_parameters(spins_params=spins_params):
325 if param_name == 'r2':
326 value = r2
327 value = value + mi + spin_index*0.1
328 elif param_name == 'r2a':
329 value = r2a
330 value = value + mi+ spin_index*0.1
331 elif param_name == 'r2b':
332 value = r2b
333 value = value + mi + spin_index*0.1
334 elif param_name == 'dw':
335 value = dw
336 elif param_name == 'pA':
337 value = pA
338 elif param_name == 'kex':
339 value = kex
340
341
342 param_vector.append(value)
343
344
345 return array(param_vector, float64)
346
347
349 """Generator function for looping of the parameters of the cluster.
350
351 @keyword spins_params: List of parameter strings used in dispersion model.
352 @type spins_params: array of strings
353 @return: The parameter name.
354 @rtype: str
355 """
356
357
358
359 for spin_index in range(self.num_spins):
360
361
362 if 'r2' in spins_params:
363 for ei in range(len(self.exp_type)):
364 for mi in range(len(self.fields)):
365 yield 'r2', spin_index, mi
366
367
368 if 'r2a' in spins_params:
369 for ei in range(len(self.exp_type)):
370 for mi in range(len(self.fields)):
371 yield 'r2a', spin_index, mi
372
373
374
375 if 'r2b' in spins_params:
376 for ei in range(len(self.exp_type)):
377 for mi in range(len(self.fields)):
378 yield 'r2b', spin_index, mi
379
380
381
382 for spin_index in range(self.num_spins):
383
384 if 'dw' in spins_params:
385 yield 'dw', spin_index, 0
386
387
388 for param in spins_params:
389 if not param in ['r2', 'r2a', 'r2b', 'phi_ex', 'phi_ex_B', 'phi_ex_C', 'padw2', 'dw', 'dw_AB', 'dw_BC', 'dw_AB', 'dwH', 'dwH_AB', 'dwH_BC', 'dwH_AB']:
390 if param == 'pA':
391 yield 'pA', 0, 0
392 elif param == 'kex':
393 yield 'kex', 0, 0
394
395
397 """Test the r2eff_cr72() function for no exchange when dw = 0.0."""
398
399
400 self.dw = 0.0
401
402
403 self.calc_r2eff()
404
405
407 """Test the r2eff_cr72() function for no exchange when pA = 1.0."""
408
409
410 self.pA = 1.0
411
412
413 self.calc_r2eff()
414
415
417 """Test the r2eff_cr72() function for no exchange when kex = 0.0."""
418
419
420 self.kex = 0.0
421
422
423 self.calc_r2eff()
424
425
427 """Test the r2eff_cr72() function for no exchange when dw = 0.0 and pA = 1.0."""
428
429
430 self.pA = 1.0
431 self.dw = 0.0
432
433
434 self.calc_r2eff()
435
436
438 """Test the r2eff_cr72() function for no exchange when dw = 0.0 and kex = 0.0."""
439
440
441 self.dw = 0.0
442 self.kex = 0.0
443
444
445 self.calc_r2eff()
446
447
449 """Test the r2eff_cr72() function for no exchange when pA = 1.0 and kex = 0.0."""
450
451
452 self.pA = 1.0
453 self.kex = 0.0
454
455
456 self.calc_r2eff()
457
458
460 """Test the r2eff_cr72() function for no exchange when dw = 0.0, pA = 1.0, and kex = 0.0."""
461
462
463 self.dw = 0.0
464 self.pA = 1.0
465 self.kex = 0.0
466
467
468 self.calc_r2eff()
469
470
472 """Test the r2eff_cr72() function for no exchange when kex = 1e7."""
473
474
475 self.kex = 1e7
476
477
478 self.calc_r2eff()
479