1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """Analysis specific code for the consistency testing of multi-field relaxation data."""
25
26
27 from re import search
28 from warnings import warn
29
30
31 from generic_fns.interatomic import return_interatom_list
32 from generic_fns.mol_res_spin import exists_mol_res_spin_data, return_spin, spin_loop
33 from generic_fns import pipes
34 from maths_fns.consistency_tests import Consistency
35 from physical_constants import N15_CSA, NH_BOND_LENGTH, h_bar, mu0, return_gyromagnetic_ratio
36 from relax_errors import RelaxError, RelaxFuncSetupError, RelaxNoSequenceError, RelaxNoValueError, RelaxSpinTypeError
37 from relax_warnings import RelaxDeselectWarning
38 import specific_fns
39 from specific_fns.api_base import API_base
40 from specific_fns.api_common import API_common
41 from user_functions.data import Uf_tables; uf_tables = Uf_tables()
42 from user_functions.objects import Desc_container
43
44
46 """Class containing functions specific to consistency testing."""
47
49 """Initialise the class by placing API_common methods into the API."""
50
51
52 super(Consistency_tests, self).__init__()
53
54
55 self.base_data_loop = self._base_data_loop_spin
56 self.create_mc_data = self._create_mc_relax_data
57 self.model_loop = self._model_loop_spin
58 self.return_conversion_factor = self._return_no_conversion_factor
59 self.return_error = self._return_error_relax_data
60 self.return_value = self._return_value_general
61 self.set_param_values = self._set_param_values_spin
62 self.set_selected_sim = self._set_selected_sim_spin
63 self.sim_pack_data = self._sim_pack_relax_data
64
65
66 self.PARAMS.add('j0', scope='spin', desc='Spectral density value at 0 MHz (from Farrow et al. (1995) JBNMR, 6: 153-162)', py_type=float, grace_string='\\qJ(0)\\Q', err=True, sim=True)
67 self.PARAMS.add('f_eta', scope='spin', desc='Eta-test (from Fushman et al. (1998) JACS, 120: 10947-10952)', py_type=float, grace_string='\\qF\\s\\xh\\Q', err=True, sim=True)
68 self.PARAMS.add('f_r2', scope='spin', desc='R2-test (from Fushman et al. (1998) JACS, 120: 10947-10952)', py_type=float, grace_string='\\qF\\sR2\\Q', err=True, sim=True)
69 self.PARAMS.add('csa', scope='spin', default=N15_CSA, units='ppm', desc='CSA value', py_type=float, grace_string='\\qCSA\\Q')
70 self.PARAMS.add('orientation', scope='spin', default=15.7, units='degrees', desc="Angle between the 15N-1H vector and the principal axis of the 15N chemical shift tensor", py_type=float, grace_string='\\q\\xq\\Q')
71 self.PARAMS.add('tc', scope='spin', default=13 * 1e-9, units='ns', desc="Correlation time", py_type=float, grace_string='\\q\\xt\\f{}c\\Q')
72
73
75 """Function for selecting which relaxation data to use in the consistency tests."""
76
77
78 pipes.test()
79
80
81 function_type = cdp.pipe_type
82 if function_type != 'ct':
83 raise RelaxFuncSetupError(specific_fns.setup.get_string(function_type))
84
85
86 if hasattr(cdp, 'ct_frq'):
87 raise RelaxError("The frequency for the run has already been set.")
88
89
90 if not hasattr(cdp, 'ct_frq'):
91 cdp.ct_frq = {}
92
93
94 cdp.ct_frq = frq
95
96
97 - def calculate(self, spin_id=None, verbosity=1, sim_index=None):
98 """Calculation of the consistency functions.
99
100 @keyword spin_id: The spin identification string.
101 @type spin_id: None or str
102 @keyword verbosity: The amount of information to print. The higher the value, the greater the verbosity.
103 @type verbosity: int
104 @keyword sim_index: The optional MC simulation index.
105 @type sim_index: None or int
106 """
107
108
109 if not hasattr(cdp, 'ct_frq') or not isinstance(cdp.ct_frq, float):
110 raise RelaxError("The frequency has not been set up.")
111
112
113 if not exists_mol_res_spin_data():
114 raise RelaxNoSequenceError
115
116
117 for spin, id in spin_loop(spin_id, return_id=True):
118
119 if not spin.select:
120 continue
121
122
123 if not hasattr(spin, 'isotope'):
124 raise RelaxSpinTypeError
125
126
127 if not hasattr(spin, 'csa') or spin.csa == None:
128 raise RelaxNoValueError("CSA")
129
130
131 if not hasattr(spin, 'orientation') or spin.orientation == None:
132 raise RelaxNoValueError("angle Theta")
133
134
135 if not hasattr(spin, 'tc') or spin.tc == None:
136 raise RelaxNoValueError("correlation time")
137
138
139 interatoms = return_interatom_list(id)
140 for interatom in interatoms:
141
142 if not interatom.dipole_pair:
143 continue
144
145
146 if id != interatom.spin_id1:
147 spin_id2 = interatom.spin_id1
148 else:
149 spin_id2 = interatom.spin_id2
150 spin2 = return_spin(spin_id2)
151
152
153 if not hasattr(spin2, 'isotope'):
154 raise RelaxSpinTypeError
155
156
157 if not hasattr(interatom, 'r') or interatom.r == None:
158 raise RelaxNoValueError("interatomic distance", spin_id=spin_id, spin_id2=spin_id2)
159
160
161 if cdp.ct_frq not in cdp.frq.values():
162 raise RelaxError("No relaxation data corresponding to the frequency %s has been loaded." % cdp.ct_frq)
163
164
165 for spin, id in spin_loop(spin_id, return_id=True):
166
167 if not spin.select:
168 continue
169
170
171 r1 = None
172 r2 = None
173 noe = None
174
175
176 for ri_id in cdp.ri_ids:
177
178 if cdp.frq[ri_id] != cdp.ct_frq:
179 continue
180
181
182 if cdp.ri_type[ri_id] == 'R1':
183 if sim_index == None:
184 r1 = spin.ri_data[ri_id]
185 else:
186 r1 = spin.ri_data_sim[ri_id][sim_index]
187
188
189 if cdp.ri_type[ri_id] == 'R2':
190 if sim_index == None:
191 r2 = spin.ri_data[ri_id]
192 else:
193 r2 = spin.ri_data_sim[ri_id][sim_index]
194
195
196 if cdp.ri_type[ri_id] == 'NOE':
197 if sim_index == None:
198 noe = spin.ri_data[ri_id]
199 else:
200 noe = spin.ri_data_sim[ri_id][sim_index]
201
202
203 if r1 == None or r2 == None or noe == None:
204 continue
205
206
207 interatoms = return_interatom_list(id)
208 for i in range(len(interatoms)):
209
210 if not interatoms[i].dipole_pair:
211 continue
212
213
214 if id != interatoms[i].spin_id1:
215 spin_id2 = interatoms[i].spin_id1
216 else:
217 spin_id2 = interatoms[i].spin_id2
218 spin2 = return_spin(spin_id2)
219
220
221 gx = return_gyromagnetic_ratio(spin.isotope)
222 gh = return_gyromagnetic_ratio(spin2.isotope)
223
224
225 r = interatoms[i].r
226
227
228 self.ct = Consistency(frq=cdp.ct_frq, gx=gx, gh=gh, mu0=mu0, h_bar=h_bar)
229
230
231 j0, f_eta, f_r2 = self.ct.func(orientation=spin.orientation, tc=spin.tc, r=r, csa=spin.csa, r1=r1, r2=r2, noe=noe)
232
233
234 if sim_index == None:
235 spin.j0 = j0
236 spin.f_eta = f_eta
237 spin.f_r2 = f_r2
238
239
240 else:
241
242 self.data_init(spin, sim=1)
243 if spin.j0_sim == None:
244 spin.j0_sim = []
245 spin.f_eta_sim = []
246 spin.f_r2_sim = []
247
248
249 spin.j0_sim.append(j0)
250 spin.f_eta_sim.append(f_eta)
251 spin.f_r2_sim.append(f_r2)
252
253
255 """Initialise the data structures.
256
257 @param data_cont: The data container.
258 @type data_cont: instance
259 @keyword sim: The Monte Carlo simulation flag, which if true will initialise the simulation data structure.
260 @type sim: bool
261 """
262
263
264 data_names = self.data_names()
265
266
267 for name in data_names:
268
269 if sim:
270
271 name = name + '_sim'
272
273
274 if not hasattr(data_cont, name):
275
276 setattr(data_cont, name, None)
277
278
279 default_value_doc = Desc_container("Consistency testing default values")
280 default_value_doc.add_paragraph("These default values are found in the file 'physical_constants.py'.")
281 _table = uf_tables.add_table(label="table: consistency testing default values", caption="Consistency testing default values.")
282 _table.add_headings(["Data type", "Object name", "Value"])
283 _table.add_row(["Bond length", "'r'", "1.02 * 1e-10"])
284 _table.add_row(["CSA", "'csa'", "-172 * 1e-6"])
285 _table.add_row(["Heteronucleus type", "'heteronuc_type'", "'15N'"])
286 _table.add_row(["Angle theta", "'proton_type'", "'1H'"])
287 _table.add_row(["Proton type", "'orientation'", "15.7"])
288 _table.add_row(["Correlation time", "'tc'", "13 * 1e-9"])
289 default_value_doc.add_table(_table.label)
290
291
293 """Deselect spins which have insufficient data to support calculation.
294
295 @keyword data_check: A flag to signal if the presence of base data is to be checked for.
296 @type data_check: bool
297 @keyword verbose: A flag which if True will allow printouts.
298 @type verbose: bool
299 """
300
301
302 if verbose:
303 print("\nOver-fit spin deselection:")
304
305
306 if not exists_mol_res_spin_data():
307 raise RelaxNoSequenceError
308
309
310 deselect_flag = False
311 for spin, spin_id in spin_loop(return_id=True):
312
313 if not spin.select:
314 continue
315
316
317 if not hasattr(spin, 'ri_data'):
318 warn(RelaxDeselectWarning(spin_id, 'missing relaxation data'))
319 spin.select = False
320 deselect_flag = True
321 continue
322
323
324 else:
325
326 data_points = 0
327 for id in cdp.ri_ids:
328 if id in spin.ri_data and spin.ri_data[id] != None:
329 data_points += 1
330
331
332 if data_points < 3:
333 warn(RelaxDeselectWarning(spin_id, 'insufficient relaxation data, 3 or more data points are required'))
334 spin.select = False
335 deselect_flag = True
336 continue
337
338
339 if verbose and not deselect_flag:
340 print("No spins have been deselected.")
341
342
343 return_data_name_doc = Desc_container("Consistency testing data type string matching patterns")
344 _table = uf_tables.add_table(label="table: Consistency testing data types", caption="Consistency testing data type string matching patterns.")
345 _table.add_headings(["Data type", "Object name"])
346 _table.add_row(["J(0)", "'j0'"])
347 _table.add_row(["F_eta", "'f_eta'"])
348 _table.add_row(["F_R2", "'f_r2'"])
349 _table.add_row(["Bond length", "'r'"])
350 _table.add_row(["CSA", "'csa'"])
351 _table.add_row(["Heteronucleus type", "'heteronuc_type'"])
352 _table.add_row(["Proton type", "'proton_type'"])
353 _table.add_row(["Angle theta", "'orientation'"])
354 _table.add_row(["Correlation time", "'tc'"])
355 return_data_name_doc.add_table(_table.label)
356
357
358 set_doc = Desc_container("Consistency testing set details")
359 set_doc.add_paragraph("In consistency testing, only four values can be set, the bond length, CSA, angle Theta ('orientation') and correlation time values. These must be set prior to the calculation of consistency functions.")
360
361
362 - def set_error(self, model_info, index, error):
363 """Set the parameter errors.
364
365 @param model_info: The spin container originating from model_loop().
366 @type model_info: SpinContainer instance
367 @param index: The index of the parameter to set the errors for.
368 @type index: int
369 @param error: The error value.
370 @type error: float
371 """
372
373
374 spin = model_info
375
376
377 if index == 0:
378 spin.j0_err = error
379
380
381 if index == 1:
382 spin.f_eta_err = error
383
384
385 if index == 2:
386 spin.f_r2_err = error
387
388
390 """Return the array of simulation parameter values.
391
392 @param model_info: The spin container originating from model_loop().
393 @type model_info: SpinContainer instance
394 @param index: The index of the parameter to return the array of values for.
395 @type index: int
396 @return: The array of simulation parameter values.
397 @rtype: list of float
398 """
399
400
401 spin = model_info
402
403
404 if not spin.select:
405 return
406
407
408 if index == 0:
409 return spin.j0_sim
410
411
412 if index == 1:
413 return spin.f_eta_sim
414
415
416 if index == 2:
417 return spin.f_r2_sim
418
419
421 """Return the array of selected simulation flags.
422
423 @param model_info: The spin container originating from model_loop().
424 @type model_info: SpinContainer instance
425 @return: The array of selected simulation flags.
426 @rtype: list of int
427 """
428
429
430 spin = model_info
431
432
433 return spin.select_sim
434