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 manipulation of RDC data."""
24
25
26 from copy import deepcopy
27 from math import pi, sqrt
28 from numpy import float64, ones, zeros
29 from numpy.linalg import norm
30 import sys
31 from warnings import warn
32
33
34 from arg_check import is_float
35 from float import nan
36 from generic_fns import grace, pipes
37 from generic_fns.align_tensor import get_tensor_index
38 from generic_fns.interatomic import create_interatom, interatomic_loop, return_interatom
39 from generic_fns.mol_res_spin import exists_mol_res_spin_data, generate_spin_id, return_spin, spin_loop
40 from maths_fns.rdc import ave_rdc_tensor
41 from physical_constants import dipolar_constant, return_gyromagnetic_ratio
42 from relax_errors import RelaxError, RelaxNoRDCError, RelaxNoSequenceError, RelaxSpinTypeError
43 from relax_io import extract_data, open_write_file, strip, write_data
44 from relax_warnings import RelaxWarning
45
46
48 """Back calculate the RDC from the alignment tensor and unit bond vectors.
49
50 @keyword align_id: The alignment tensor ID string.
51 @type align_id: str
52 """
53
54
55 if align_id and align_id not in cdp.align_ids:
56 raise RelaxError("The alignment ID '%s' is not in the alignment ID list %s." % (align_id, cdp.align_ids))
57
58
59 if align_id:
60 align_ids = [align_id]
61 else:
62 align_ids = cdp.align_ids
63
64
65 for align_id in align_ids:
66
67 if not hasattr(cdp, 'rdc_ids'):
68 cdp.rdc_ids = []
69
70
71 if align_id not in cdp.rdc_ids:
72 cdp.rdc_ids.append(align_id)
73
74
75 weights = ones(cdp.N, float64) / cdp.N
76
77
78 unit_vect = zeros((cdp.N, 3), float64)
79
80
81 for interatom in interatomic_loop():
82
83 if not hasattr(interatom, 'vector'):
84 continue
85
86
87 spin1 = return_spin(interatom.spin_id1)
88 spin2 = return_spin(interatom.spin_id2)
89
90
91 if not hasattr(spin1, 'isotope'):
92 raise RelaxSpinTypeError(interatom.spin_id1)
93 if not hasattr(spin2, 'isotope'):
94 raise RelaxSpinTypeError(interatom.spin_id2)
95
96
97 if is_float(interatom.vector[0], raise_error=False):
98 vectors = [interatom.vector]
99 else:
100 vectors = interatom.vector
101
102
103 g1 = return_gyromagnetic_ratio(spin1.isotope)
104 g2 = return_gyromagnetic_ratio(spin2.isotope)
105
106
107 dj = 3.0/(2.0*pi) * dipolar_constant(g1, g2, interatom.r)
108
109
110 for c in range(cdp.N):
111 unit_vect[c] = vectors[c] / norm(vectors[c])
112
113
114 if not hasattr(interatom, 'rdc_bc'):
115 interatom.rdc_bc = {}
116
117
118 for id in align_ids:
119 interatom.rdc_bc[id] = ave_rdc_tensor(dj, unit_vect, cdp.N, cdp.align_tensors[get_tensor_index(id)].A, weights=weights)
120
121
122 -def convert(value, align_id, to_intern=False):
123 """Convert the RDC based on the 'D' or '2D' data type.
124
125 @param value: The value or error to convert.
126 @type value: float or None
127 @param align_id: The alignment tensor ID string.
128 @type align_id: str
129 @keyword to_intern: A flag which if True will convert to the internal D notation if needed, or if False will convert from the internal D notation to the external D or 2D format.
130 @type to_intern: bool
131 @return: The converted value.
132 @rtype: float or None
133 """
134
135
136 if value == None:
137 return None
138
139
140 factor = 1.0
141 if hasattr(cdp, 'rdc_data_types') and align_id in cdp.rdc_data_types and cdp.rdc_data_types[align_id] == '2D':
142
143 if to_intern:
144 factor = 2.0
145
146
147 else:
148 factor = 0.5
149
150
151 return value * factor
152
153
154 -def corr_plot(format=None, file=None, dir=None, force=False):
155 """Generate a correlation plot of the measured vs. back-calculated RDCs.
156
157 @keyword format: The format for the plot file. The following values are accepted: 'grace', a Grace plot; None, a plain text file.
158 @type format: str or None
159 @keyword file: The file name or object to write to.
160 @type file: str or file object
161 @keyword dir: The name of the directory to place the file into (defaults to the current directory).
162 @type dir: str
163 @keyword force: A flag which if True will cause any pre-existing file to be overwritten.
164 @type force: bool
165 """
166
167
168 pipes.test()
169
170
171 if not exists_mol_res_spin_data():
172 raise RelaxNoSequenceError
173
174
175 if not hasattr(cdp, 'rdc_ids') or not cdp.rdc_ids:
176 warn(RelaxWarning("No RDC data exists, skipping file creation."))
177 return
178
179
180 file = open_write_file(file, dir, force)
181
182
183 data = []
184
185
186 data.append([[-100, -100, 0], [100, 100, 0]])
187
188
189 for align_id in cdp.rdc_ids:
190
191 data.append([])
192
193
194 err_flag = False
195 for interatom in interatomic_loop():
196
197 if hasattr(interatom, 'rdc_err') and align_id in interatom.rdc_err.keys():
198 err_flag = True
199 break
200
201
202 for interatom in interatomic_loop():
203
204 if not hasattr(interatom, 'rdc') or not hasattr(interatom, 'rdc_bc') or not align_id in interatom.rdc.keys() or not align_id in interatom.rdc_bc.keys():
205 continue
206
207
208 data[-1].append([convert(interatom.rdc_bc[align_id], align_id), convert(interatom.rdc[align_id], align_id)])
209
210
211 if err_flag:
212 if hasattr(interatom, 'rdc_err') and align_id in interatom.rdc_err.keys():
213 data[-1][-1].append(convert(interatom.rdc_err[align_id], align_id))
214 else:
215 data[-1][-1].append(None)
216
217
218 data[-1][-1].append("%s-%s" % (interatom.spin_id1, interatom.spin_id2))
219
220
221 size = len(data)
222
223
224 data = [data]
225
226
227 if err_flag:
228 graph_type = 'xydy'
229 else:
230 graph_type = 'xy'
231
232
233 if format == 'grace':
234
235 grace.write_xy_header(file=file, title="RDC correlation plot", sets=size, set_names=[None]+cdp.rdc_ids, linestyle=[2]+[0]*size, data_type=['rdc_bc', 'rdc'], axis_min=[-10, -10], axis_max=[10, 10], legend_pos=[1, 0.5])
236
237
238 grace.write_xy_data(data=data, file=file, graph_type=graph_type)
239
240
242 """Delete the RDC data corresponding to the alignment ID.
243
244 @keyword align_id: The alignment tensor ID string. If not specified, all data will be deleted.
245 @type align_id: str or None
246 """
247
248
249 pipes.test()
250
251
252 if not exists_mol_res_spin_data():
253 raise RelaxNoSequenceError
254
255
256 if align_id and not align_id in cdp.rdc_ids:
257 raise RelaxError("The RDC alignment id '%s' does not exist" % align_id)
258
259
260 if not align_id:
261 ids = deepcopy(cdp.rdc_ids)
262 else:
263 ids = [align_id]
264
265
266 for id in ids:
267
268 cdp.rdc_ids.pop(cdp.rdc_ids.index(id))
269
270
271 if hasattr(cdp, 'rdc_data_types') and id in cdp.rdc_data_types:
272 cdp.rdc_data_types.pop(id)
273
274
275 for interatom in interatomic_loop():
276
277 if hasattr(interatom, 'rdc') and id in interatom.rdc:
278 interatom.rdc.pop(id)
279
280
281 if hasattr(interatom, 'rdc_err') and id in interatom.rdc_err:
282 interatom.rdc_err.pop(id)
283
284
285 if not hasattr(cdp, 'pcs_ids') or id not in cdp.pcs_ids:
286 cdp.align_ids.pop(id)
287
288
289 -def display(align_id=None, bc=False):
290 """Display the RDC data corresponding to the alignment ID.
291
292 @keyword align_id: The alignment tensor ID string.
293 @type align_id: str
294 @keyword bc: The back-calculation flag which if True will cause the back-calculated rather than measured data to be displayed.
295 @type bc: bool
296 """
297
298
299 write(align_id=align_id, file=sys.stdout, bc=bc)
300
301
303 """Calculate the Q-factors for the RDC data.
304
305 @keyword spin_id: The spin ID string used to restrict the Q-factor calculation to a subset of all spins.
306 @type spin_id: None or str
307 """
308
309
310 if not hasattr(cdp, 'rdc_ids') or not len(cdp.rdc_ids):
311 warn(RelaxWarning("No RDC data exists, Q factors cannot be calculated."))
312 return
313
314
315 cdp.q_factors_rdc = {}
316 cdp.q_factors_rdc_norm2 = {}
317
318
319 for align_id in cdp.rdc_ids:
320
321 D2_sum = 0.0
322 sse = 0.0
323
324
325 dj = None
326 N = 0
327 interatom_count = 0
328 rdc_data = False
329 rdc_bc_data = False
330 norm2_flag = True
331 for interatom in interatomic_loop():
332
333 interatom_count += 1
334
335
336 if hasattr(interatom, 'rdc') and align_id in interatom.rdc:
337 rdc_data = True
338 if hasattr(interatom, 'rdc_bc') and align_id in interatom.rdc_bc:
339 rdc_bc_data = True
340
341
342 if not hasattr(interatom, 'rdc') or not hasattr(interatom, 'rdc_bc') or not align_id in interatom.rdc or interatom.rdc[align_id] == None or not align_id in interatom.rdc_bc or interatom.rdc_bc[align_id] == None:
343 continue
344
345
346 spin1 = return_spin(interatom.spin_id1)
347 spin2 = return_spin(interatom.spin_id2)
348
349
350 sse = sse + (interatom.rdc[align_id] - interatom.rdc_bc[align_id])**2
351
352
353 D2_sum = D2_sum + interatom.rdc[align_id]**2
354
355
356 g1 = return_gyromagnetic_ratio(spin1.isotope)
357 g2 = return_gyromagnetic_ratio(spin2.isotope)
358
359
360 dj_new = 3.0/(2.0*pi) * dipolar_constant(g1, g2, interatom.r)
361 if norm2_flag and dj != None and dj_new != dj:
362 warn(RelaxWarning("The dipolar constant is not the same for all RDCs, skipping the Q factor normalised with 2Da^2(4 + 3R)/5."))
363 norm2_flag = False
364 else:
365 dj = dj_new
366
367
368 N = N + 1
369
370
371 if not interatom_count:
372 warn(RelaxWarning("No interatomic data containers have been used in the calculation."))
373 return
374 if not rdc_data:
375 warn(RelaxWarning("No RDC data can be found."))
376 return
377 if not rdc_bc_data:
378 warn(RelaxWarning("No back-calculated RDC data can be found."))
379 return
380
381
382 if norm2_flag:
383 D = dj * cdp.align_tensors[cdp.align_ids.index(align_id)].A_diag
384 Da = 1.0/3.0 * (D[2, 2] - (D[0, 0]+D[1, 1])/2.0)
385 Dr = 1.0/3.0 * (D[0, 0] - D[1, 1])
386 if Da == 0:
387 R = nan
388 else:
389 R = Dr / Da
390 norm = 2.0 * (Da)**2 * (4.0 + 3.0*R**2)/5.0
391 if Da == 0.0:
392 norm = 1e-15
393
394
395 cdp.q_factors_rdc[align_id] = sqrt(sse / N / norm)
396
397 else:
398 cdp.q_factors_rdc[align_id] = 0.0
399
400
401 cdp.q_factors_rdc_norm2[align_id] = sqrt(sse / D2_sum)
402
403
404 cdp.q_rdc = 0.0
405 cdp.q_rdc_norm2 = 0.0
406 for id in cdp.q_factors_rdc:
407 cdp.q_rdc = cdp.q_rdc + cdp.q_factors_rdc[id]**2
408 for id in cdp.q_factors_rdc_norm2:
409 cdp.q_rdc_norm2 = cdp.q_rdc_norm2 + cdp.q_factors_rdc_norm2[id]**2
410 cdp.q_rdc = sqrt(cdp.q_rdc / len(cdp.q_factors_rdc))
411 cdp.q_rdc_norm2 = sqrt(cdp.q_rdc_norm2 / len(cdp.q_factors_rdc_norm2))
412
413
414 -def read(align_id=None, file=None, dir=None, file_data=None, data_type='D', spin_id1_col=None, spin_id2_col=None, data_col=None, error_col=None, sep=None, neg_g_corr=False, absolute=False):
415 """Read the RDC data from file.
416
417 @keyword align_id: The alignment tensor ID string.
418 @type align_id: str
419 @keyword file: The name of the file to open.
420 @type file: str
421 @keyword dir: The directory containing the file (defaults to the current directory if None).
422 @type dir: str or None
423 @keyword file_data: An alternative to opening a file, if the data already exists in the correct format. The format is a list of lists where the first index corresponds to the row and the second the column.
424 @type file_data: list of lists
425 @keyword data_type: A string which is set to 'D' means that the splitting in the aligned sample was assumed to be J + D, or if set to '2D' then the splitting was taken as J + 2D.
426 @keyword spin_id1_col: The column containing the spin ID strings of the first spin.
427 @type spin_id1_col: int
428 @keyword spin_id2_col: The column containing the spin ID strings of the second spin.
429 @type spin_id2_col: int
430 @keyword data_col: The column containing the RDC data in Hz.
431 @type data_col: int or None
432 @keyword error_col: The column containing the RDC errors.
433 @type error_col: int or None
434 @keyword sep: The column separator which, if None, defaults to whitespace.
435 @type sep: str or None
436 @keyword neg_g_corr: A flag which is used to correct for the negative gyromagnetic ratio of 15N. If True, a sign inversion will be applied to all RDC values to be loaded.
437 @type neg_g_corr: bool
438 @keyword absolute: A flag which if True indicates that the RDCs to load are signless. All RDCs will then be converted to positive values.
439 @type absolute: bool
440 """
441
442
443 pipes.test()
444
445
446 if not exists_mol_res_spin_data():
447 raise RelaxNoSequenceError
448
449
450 if data_col == None and error_col == None:
451 raise RelaxError("One of either the data or error column must be supplied.")
452
453
454 if not hasattr(cdp, 'rdc_data_types'):
455 cdp.rdc_data_types = {}
456 if not align_id in cdp.rdc_data_types:
457 cdp.rdc_data_types[align_id] = data_type
458
459
460
461
462
463 file_data = extract_data(file, dir, sep=sep)
464
465
466 data = []
467 for line in file_data:
468
469 if spin_id1_col > len(line):
470 warn(RelaxWarning("The data %s is invalid, no first spin ID column can be found." % line))
471 continue
472 if spin_id2_col > len(line):
473 warn(RelaxWarning("The data %s is invalid, no second spin ID column can be found." % line))
474 continue
475 if data_col and data_col > len(line):
476 warn(RelaxWarning("The data %s is invalid, no data column can be found." % line))
477 continue
478 if error_col and error_col > len(line):
479 warn(RelaxWarning("The data %s is invalid, no error column can be found." % line))
480 continue
481
482
483 spin_id1 = line[spin_id1_col-1]
484 spin_id2 = line[spin_id2_col-1]
485 value = None
486 if data_col:
487 value = line[data_col-1]
488 error = None
489 if error_col:
490 error = line[error_col-1]
491
492
493 if spin_id1[0] in ["\"", "\'"]:
494 spin_id1 = eval(spin_id1)
495 if spin_id2[0] in ["\"", "\'"]:
496 spin_id2 = eval(spin_id2)
497
498
499 if value == 'None':
500 value = None
501 if value != None:
502 try:
503 value = float(value)
504 except ValueError:
505 warn(RelaxWarning("The RDC value of the line %s is invalid." % line))
506 continue
507
508
509 if error == 'None':
510 error = None
511 if error != None:
512 try:
513 error = float(error)
514 except ValueError:
515 warn(RelaxWarning("The error value of the line %s is invalid." % line))
516 continue
517
518
519 spin1 = return_spin(spin_id1)
520 spin2 = return_spin(spin_id2)
521
522
523 if not spin1:
524 warn(RelaxWarning("The spin ID '%s' cannot be found in the current data pipe, skipping the data %s." % (spin_id1, line)))
525 continue
526 if not spin2:
527 warn(RelaxWarning("The spin ID '%s' cannot be found in the current data pipe, skipping the data %s." % (spin_id2, line)))
528 continue
529
530
531 if error == 0.0:
532 raise RelaxError("An invalid error value of zero has been encountered.")
533
534
535 spin_id1 = spin1._spin_ids[0]
536 spin_id2 = spin2._spin_ids[0]
537
538
539 interatom = return_interatom(spin_id1, spin_id2)
540
541
542 if interatom == None:
543 interatom = create_interatom(spin_id1=spin_id1, spin_id2=spin_id2)
544
545
546 if data_col:
547
548 value = convert(value, align_id, to_intern=True)
549
550
551 if neg_g_corr and value != None:
552 value = -value
553
554
555 if absolute:
556
557 value = abs(value)
558
559
560 if not hasattr(interatom, 'rdc'):
561 interatom.rdc = {}
562
563
564 interatom.rdc[align_id] = value
565
566
567 if not hasattr(interatom, 'absolute_rdc'):
568 interatom.absolute_rdc = {}
569 interatom.absolute_rdc[align_id] = absolute
570
571
572 if error_col:
573
574 error = convert(error, align_id, to_intern=True)
575
576
577 if not hasattr(interatom, 'rdc_err'):
578 interatom.rdc_err = {}
579
580
581 interatom.rdc_err[align_id] = error
582
583
584 data.append([spin_id1, spin_id2, repr(value), repr(error)])
585
586
587 if not len(data):
588 raise RelaxError("No RDC data could be extracted.")
589
590
591 print("The following RDCs have been loaded into the relax data store:\n")
592 write_data(out=sys.stdout, headings=["Spin_ID1", "Spin_ID2", "Value", "Error"], data=data)
593
594
595 if not hasattr(cdp, 'align_ids'):
596 cdp.align_ids = []
597 if not hasattr(cdp, 'rdc_ids'):
598 cdp.rdc_ids = []
599
600
601 if align_id not in cdp.align_ids:
602 cdp.align_ids.append(align_id)
603 if align_id not in cdp.rdc_ids:
604 cdp.rdc_ids.append(align_id)
605
606
607 -def weight(align_id=None, spin_id=None, weight=1.0):
608 """Set optimisation weights on the RDC data.
609
610 @keyword align_id: The alignment tensor ID string.
611 @type align_id: str
612 @keyword spin_id: The spin ID string.
613 @type spin_id: None or str
614 @keyword weight: The optimisation weight. The higher the value, the more importance the RDC will have.
615 @type weight: float or int.
616 """
617
618
619 if not exists_mol_res_spin_data():
620 raise RelaxNoSequenceError
621
622
623 if not hasattr(cdp, 'rdc_ids') or align_id not in cdp.rdc_ids:
624 raise RelaxNoRDCError(align_id)
625
626
627 for interatom in interatomic_loop():
628
629 if not hasattr(interatom, 'rdc_weight'):
630 interatom.rdc_weight = {}
631
632
633 interatom.rdc_weight[align_id] = weight
634
635
636 -def write(align_id=None, file=None, dir=None, bc=False, force=False):
637 """Display the RDC data corresponding to the alignment ID.
638
639 @keyword align_id: The alignment tensor ID string.
640 @type align_id: str
641 @keyword file: The file name or object to write to.
642 @type file: str or file object
643 @keyword dir: The name of the directory to place the file into (defaults to the current directory).
644 @type dir: str
645 @keyword bc: The back-calculation flag which if True will cause the back-calculated rather than measured data to be written.
646 @type bc: bool
647 @keyword force: A flag which if True will cause any pre-existing file to be overwritten.
648 @type force: bool
649 """
650
651
652 pipes.test()
653
654
655 if not exists_mol_res_spin_data():
656 raise RelaxNoSequenceError
657
658
659 if not hasattr(cdp, 'rdc_ids') or align_id not in cdp.rdc_ids:
660 raise RelaxNoRDCError(align_id)
661
662
663 file = open_write_file(file, dir, force)
664
665
666 data = []
667 for interatom in interatomic_loop():
668
669 if not bc and (not hasattr(interatom, 'rdc') or align_id not in interatom.rdc.keys()):
670 continue
671 elif bc and (not hasattr(interatom, 'rdc_bc') or align_id not in interatom.rdc_bc.keys()):
672 continue
673
674
675 data.append([])
676 data[-1].append(interatom.spin_id1)
677 data[-1].append(interatom.spin_id2)
678
679
680 if bc:
681 data[-1].append(repr(convert(interatom.rdc_bc[align_id], align_id)))
682 else:
683 data[-1].append(repr(convert(interatom.rdc[align_id], align_id)))
684
685
686 if hasattr(interatom, 'rdc_err') and align_id in interatom.rdc_err.keys():
687 data[-1].append(repr(convert(interatom.rdc_err[align_id], align_id)))
688 else:
689 data[-1].append(repr(None))
690
691
692 write_data(out=file, headings=["Spin_ID1", "Spin_ID2", "RDCs", "RDC_error"], data=data)
693