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 the reading of Bruker Dynamics Centre (DC) files."""
24
25
26 from re import search, split
27 from warnings import warn
28
29
30 from lib.errors import RelaxError
31 from lib.io import open_read_file
32 from lib.physical_constants import element_from_isotope
33 from lib.warnings import RelaxWarning
34
35
37 """Determine the relaxation data from the given DC data.
38
39 @param data: The list of Tx, Tx error, and scaling factor for a given residue from the DC file.
40 @type data: list of str
41 """
42
43
44 rx = 1.0 / float(data[0])
45
46
47 rx_err = float(data[1]) / float(data[2])
48
49
50 rx_err = rx**2 * rx_err
51
52
53 return rx, rx_err
54
55
57 """Parse the DC data file and create and return an object representation of the data.
58
59 @keyword file: The name of the file to open.
60 @type file: str
61 @keyword dir: The directory containing the file (defaults to the current directory if None).
62 @type dir: str or None
63 @return: The object representation of the Bruker DC file.
64 @rtype: DCObject instance
65 """
66
67
68 file_handle = open_read_file(file, dir)
69 lines = file_handle.readlines()
70 file_handle.close()
71
72
73 obj = DCObject()
74 obj.populate(lines)
75 obj.process()
76
77
78 return obj
79
80
82 """Determine the residue number from the given DC data.
83
84 @param data: The list of residue info, split by whitespace, from the DC file.
85 @type data: list of str
86 """
87
88
89 res_num = None
90
91
92 row = split('([0-9]+)', data)
93
94
95 for j in range(len(row)):
96 try:
97 res_num = int(row[j])
98 except ValueError:
99 pass
100
101
102 return ":%s" % res_num
103
104
105
107 """An object representation of the Bruker DC file data."""
108
110 """Initialise the object."""
111
112
113 self._header = {}
114
115
116 self._sections = []
117
118
119 self.ri_type = None
120 self.version = None
121
122
124 """Populate the object with the file data.
125
126 @param lines: The Bruker DC file data with each list element being a line of the data file.
127 @type lines: list of str
128 """
129
130
131 in_sections = False
132 for i in range(len(lines)):
133
134 if i == 0:
135 if lines[0].strip() != "$##1.0":
136 raise RelaxError("Unknown file format, Bruker DC files must start with $##1.0 on the first line.")
137 else:
138 continue
139
140
141 row = split("\t", lines[i])
142
143
144 for j in range(len(row)):
145 row[j] = row[j].strip()
146
147
148 if len(row) == 0 or row == ['']:
149 continue
150
151
152 if row[0] == "SECTION:":
153
154 in_sections = True
155
156
157 if row[1] == "sample information":
158 self.sample_information = DCSampleInfo()
159 self._sections.append(self.sample_information)
160 elif row[1] == "relevant parameters":
161 self.parameters = DCParams()
162 self._sections.append(self.parameters)
163 elif row[1] == "integrals":
164 self.intensities = DCIntegrals(err=False, bc=False)
165 self._sections.append(self.intensities)
166 elif row[1] == "integral errors":
167 self.intensity_errors = DCIntegrals(err=True, bc=False)
168 self._sections.append(self.intensity_errors)
169 elif row[1] == "integrals back calculated from fit":
170 self.intensities_bc = DCIntegrals(err=True, bc=True)
171 self._sections.append(self.intensities_bc)
172 elif row[1] == "details":
173 self.details = DCDetails()
174 self._sections.append(self.details)
175 elif row[1] == "results":
176 self.results = DCResults()
177 self._sections.append(self.results)
178
179
180 else:
181 warn(RelaxWarning("The Bruker DC file section \"%s\" is unknown." % row[1]))
182
183
184 if not in_sections:
185 self._header[row[0]] = row[1]
186
187
188 else:
189 self._sections[-1].add(row)
190
191
193 """Process the Bruker DC data already present in the object."""
194
195
196 if search('T1', self._header['Project:']):
197 self.ri_type = 'R1'
198 elif search('T2', self._header['Project:']):
199 self.ri_type = 'R2'
200 elif search('NOE', self._header['Project:']):
201 self.ri_type = 'NOE'
202
203
204 if 'generated by:' in self._header:
205 self.version = self._header['generated by:']
206
207
208 for section in self._sections:
209 section.process()
210
211
212
214 """Base class for the various Bruker DC sections."""
215
217 """Initialise the Bruker DC section object."""
218
219
220 self._data = []
221
222
223 - def add(self, elements):
224 """Store the data.
225
226 @param elements: The Bruker DC file line split by tabs, with whitespace removed.
227 @type elements: list of str
228 """
229
230
231 if elements[0] == "SECTION:":
232 return
233
234
235 self._data.append(elements)
236
237
238
240 """Class for the Bruker DC analysis information."""
241
243 """Initialise the Bruker DC section object."""
244
245
246 super(DCDetails, self).__init__()
247
248
249 self.int_type = None
250
251
253 """Process the Bruker DC data already present in the section object."""
254
255
256 for i in range(len(self._data)):
257
258 if self._data[i][0] == 'Systematic error estimation of data:':
259
260 if self._data[i][1] == 'worst case per peak scenario':
261 raise RelaxError("The errors estimation method \"worst case per peak scenario\" is not suitable for model-free analysis. Please go back to the DC and switch to \"average variance calculation\".")
262
263
264 if self._data[i][0] == 'Used integrals:':
265
266 if self._data[i][1] == 'peak intensities':
267 self.int_type = 'height'
268
269
270 if self._data[i][1] == 'area integral':
271 self.int_type = 'volume'
272
273
274
276 """Class for the Bruker DC peak intensity information."""
277
278 - def __init__(self, err=False, bc=False):
279 """Initialise the Bruker DC section object.
280
281 @keyword err: A flag which if True means that the data if for the peak intensity errors.
282 @type err: bool
283 @keyword bc: A flag which if True means that this is the back-calculated peak intensity data.
284 @type bc: bool
285 """
286
287
288 super(DCIntegrals, self).__init__()
289
290
291 self.err = err
292 self.bc = bc
293
294
295 self.ids = []
296 self.relaxation_time = []
297 self.peak_intensity = {}
298
299
301 """Process the Bruker DC data already present in the section object."""
302
303
304 for i in range(len(self._data)):
305
306 if self._data[i][0] == 'Mixing time [s]:':
307 for j in range(1, len(self._data[i])):
308 self.relaxation_time.append(float(self._data[i][j]))
309
310
311 elif self._data[i][0] == 'Peak name':
312 for j in range(1, len(self._data[i])):
313 self.ids.append(self._data[i][j])
314
315
316 else:
317
318 res_num = get_res_num(self._data[i][0])
319 if res_num not in self.peak_intensity:
320 self.peak_intensity[res_num] = []
321
322
323 for j in range(1, len(self._data[i])):
324 self.peak_intensity[res_num].append(float(self._data[i][j]))
325
326
327
329 """Class for the Bruker DC parameter information."""
330
332 """Process the Bruker DC data already present in the section object."""
333
334
335 for i in range(len(self._data)):
336
337 if self._data[i][0] == 'Proton frequency[MHz]:':
338 self.frq = float(self._data[i][1]) * 1e6
339
340
341
343 """Class for the Bruker DC results."""
344
346 """Initialise the Bruker DC section object."""
347
348
349 super(DCResults, self).__init__()
350
351
352 self.sequence = []
353 self.f1 = {}
354 self.f2 = {}
355 self.I0 = {}
356 self.I0_err = {}
357 self.Tx = {}
358 self.Tx_err = {}
359 self.Tx_err_scale = {}
360 self.Rx = {}
361 self.Rx_err = {}
362 self.fit_info = {}
363
364
365 self.indices = {
366 'f1': None,
367 'f2': None,
368 'I0': None,
369 'I0_err': None,
370 'Tx': None,
371 'Tx_err': None,
372 'Tx_err_scale': None,
373 'Rx': None,
374 'Rx_err': None,
375 'fit_info': None
376 }
377
378
380 """Process the Bruker DC data already present in the section object."""
381
382
383 for i in range(len(self._data)):
384
385 if self._data[i][0] == 'Peak name':
386 for j in range(1, len(self._data[i])):
387 if self._data[i][j] == 'F1 [ppm]':
388 self.indices['f1'] = j
389 elif self._data[i][j] == 'F2 [ppm]':
390 self.indices['f2'] = j
391 elif self._data[i][j] == 'Io':
392 self.indices['I0'] = j
393 elif self._data[i][j] == 'error' and self._data[i][j-1] == 'Io':
394 self.indices['I0_err'] = j
395 elif self._data[i][j] in ['T1 [s]', 'T2 [s]']:
396 self.indices['Tx'] = j
397 elif self._data[i][j] == 'error' and self._data[i][j-1] in ['T1 [s]', 'T2 [s]']:
398 self.indices['Tx_err'] = j
399 elif self._data[i][j] == 'errorScale' and self._data[i][j-2] in ['T1 [s]', 'T2 [s]']:
400 self.indices['Tx_err_scale'] = j
401 elif self._data[i][j] in ['R1 [rad/s]', 'R2 [rad/s]', 'NOE', 'NOE [ ]', 'NOE [none]']:
402 self.indices['Rx'] = j
403 elif self._data[i][j] in ['R1 sd [rad/s]', 'R2 sd [rad/s]']:
404 self.indices['Rx_err'] = j
405 elif self._data[i][j] == 'error' and self._data[i][j-1] in ['NOE', 'NOE [ ]', 'NOE [none]']:
406 self.indices['Rx_err'] = j
407 elif self._data[i][j] == 'fitInfo':
408 self.indices['fit_info'] = j
409
410
411 if self.indices['Rx'] == None:
412 raise RelaxError("The old Protein Dynamics Center (PDC) files with relaxation times but no relaxation rates are not supported.")
413
414
415 else:
416
417 res_id = get_res_num(self._data[i][0])
418 self.sequence.append(res_id)
419
420
421 if self.indices['f1'] != None:
422 self.f1[res_id] = float(self._data[i][self.indices['f1']])
423 if self.indices['f2'] != None:
424 self.f2[res_id] = float(self._data[i][self.indices['f2']])
425 if self.indices['I0'] != None:
426 self.I0[res_id] = float(self._data[i][self.indices['I0']])
427 if self.indices['I0_err'] != None:
428 self.I0_err[res_id] = float(self._data[i][self.indices['I0_err']])
429 if self.indices['Tx'] != None:
430 self.Tx[res_id] = float(self._data[i][self.indices['Tx']])
431 if self.indices['Tx_err'] != None:
432 self.Tx_err[res_id] = float(self._data[i][self.indices['Tx_err']])
433 if self.indices['Tx_err_scale'] != None:
434 self.Tx_err_scale[res_id] = float(self._data[i][self.indices['Tx_err_scale']])
435 if self.indices['Rx'] != None:
436 self.Rx[res_id] = float(self._data[i][self.indices['Rx']])
437 if self.indices['Rx_err'] != None:
438 self.Rx_err[res_id] = float(self._data[i][self.indices['Rx_err']])
439 if self.indices['fit_info'] != None:
440 self.fit_info[res_id] = self._data[i][self.indices['fit_info']]
441
442
443
445 """Class for the Bruker DC sample information."""
446
448 """Process the Bruker DC data already present in the section object."""
449
450
451 for i in range(len(self._data)):
452
453 if self._data[i][0] == 'Labelling:':
454
455 self.isotope = self._data[i][1]
456
457
458 self.spin_name = split('([A-Z]+)', self._data[i][1])[1]
459
460
461 self.atom_name = element_from_isotope(self.isotope)
462