1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Package containing the relax data storage object."""
24
25
26
27 from re import search
28 from sys import stderr
29 from time import asctime
30 import xml.dom.minidom
31
32
33 from data_store.gui import Gui
34 from data_store.pipe_container import PipeContainer
35 from data_store.seq_align import Sequence_alignments
36 import pipe_control
37 from lib.compat import builtins
38 from lib.errors import RelaxError, RelaxPipeError, RelaxNoPipeError
39 from lib.xml import fill_object_contents, xml_to_object
40 from status import Status; status = Status()
41 import version
42
43
44 __all__ = [ 'align_tensor',
45 'data_classes',
46 'diff_tensor',
47 'exp_info',
48 'gui',
49 'interatomic',
50 'mol_res_spin',
51 'pipe_container',
52 'prototype',
53 'seq_align'
54 ]
55
56
58 """The relax data storage object."""
59
60
61 current_pipe = None
62 builtins.cdp = None
63
64
65 instance = None
66
81
82
84 """The string representation of the object.
85
86 Rather than using the standard Python conventions (either the string representation of the
87 value or the "<...desc...>" notation), a rich-formatted description of the object is given.
88 """
89
90
91 text = "The relax data storage object.\n"
92
93
94 text = text + "\n"
95 text = text + "Data pipes:\n"
96 pipes = sorted(self.instance.keys())
97 if pipes:
98 for pipe in pipes:
99 text = text + " %s\n" % repr(pipe)
100 else:
101 text = text + " None\n"
102
103
104 text = text + "\n"
105 text = text + "Data store objects:\n"
106 names = sorted(self.__class__.__dict__.keys())
107 for name in names:
108
109 obj = getattr(self, name)
110
111
112 if obj == None or isinstance(obj, str):
113 text = text + " %s %s: %s\n" % (name, type(obj), obj)
114 else:
115 text = text + " %s %s: %s\n" % (name, type(obj), obj.__doc__.split('\n')[0])
116
117
118 text = text + "\n"
119 text = text + "Inherited dictionary methods:\n"
120 for name in dir(dict):
121
122 if search("^_", name):
123 continue
124
125
126 if name in self.__class__.__dict__:
127 continue
128
129
130 obj = getattr(self, name)
131
132
133 text = text + " %s %s: %s\n" % (name, type(obj), obj.__doc__.split('\n')[0])
134
135
136 text = text + "\n"
137 text = text + "All other objects:\n"
138 for name in dir(self):
139
140 if search("^_", name):
141 continue
142
143
144 if name in self.__class__.__dict__:
145 continue
146
147
148 if name in dir(dict):
149 continue
150
151
152 obj = getattr(self, name)
153
154
155 text = text + " %s %s: %s\n" % (name, type(obj), obj)
156
157
158 return text
159
160
162 """Delete all the data from the relax data storage object.
163
164 This method is to make the current single instance of the Data object identical to a newly
165 created instance of Data, hence resetting the relax program state.
166 """
167
168
169 keys = list(self.__dict__.keys())
170 for key in keys:
171
172 del self.__dict__[key]
173
174
175 self.instance.clear()
176
177
178 builtins.cdp = None
179
180
181 self.instance.pipe_bundles = {}
182
183
184 self.instance.relax_gui = Gui()
185
186
187 status.observers.reset.notify()
188 status.observers.pipe_alteration.notify()
189
190
192 """Method for converting the old data structures to the new ones.
193
194 @keyword file_version: The relax XML version of the XML file.
195 @type file_version: int
196 @keyword pipes: The list of new pipe names to update.
197 @type pipes: list of str
198 """
199
200
201 for pipe_name in pipes:
202
203 dp = self[pipe_name]
204
205
206 for mol in dp.mol:
207
208 for res in mol.res:
209
210 for spin in res.spin:
211
212 eliminate = []
213
214
215 spin_id = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin=spin)
216
217
218 if hasattr(spin, 'intensities'):
219 spin.peak_intensity = spin.intensities
220 eliminate.append('intensities')
221 if hasattr(spin, 'intensity_err'):
222 spin.peak_intensity_err = spin.intensity_err
223 eliminate.append('intensity_err')
224 if hasattr(spin, 'intensity_sim'):
225 spin.peak_intensity_sim = spin.intensity_sim
226 eliminate.append('intensity_sim')
227 if hasattr(spin, 'sim_intensity'):
228 spin.peak_intensity_sim = spin.sim_intensity
229 eliminate.append('sim_intensity')
230 if hasattr(spin, 'intensity_bc'):
231 spin.peak_intensity_bc = spin.intensity_bc
232 eliminate.append('intensity_bc')
233
234
235 if hasattr(spin, 'heteronuc_type') and hasattr(spin, 'element') and spin.element == 'H':
236
237 spin.isotope = spin.proton_type
238
239
240 eliminate.append('proton_type')
241
242
243 elif hasattr(spin, 'heteronuc_type'):
244
245 spin.isotope = spin.heteronuc_type
246
247
248 if spin.name == None:
249 if search('N', spin.isotope):
250 pipe_control.mol_res_spin.name_spin(spin_id=spin_id, name='N', pipe=pipe_name)
251 elif search('C', spin.isotope):
252 pipe_control.mol_res_spin.name_spin(spin_id=spin_id, name='C', pipe=pipe_name)
253
254
255 if (hasattr(spin, 'attached_proton') and spin.attached_proton != None) or (hasattr(spin, 'proton_type') and spin.proton_type != None):
256
257 if hasattr(spin, 'attached_proton') and spin.attached_proton != None:
258 proton_name = spin.attached_proton
259 else:
260 proton_name = 'H'
261
262
263 spin_id1 = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin=spin)
264 spin_id2 = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin_name=proton_name)
265
266
267 h_spin = pipe_control.mol_res_spin.return_spin(spin_id2, pipe=pipe_name)
268 if h_spin:
269 spin_id2 = pipe_control.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin_name=proton_name, spin_num=h_spin.num)
270
271
272 if not h_spin:
273 h_spin = pipe_control.mol_res_spin.create_spin(mol_name=mol.name, res_num=res.num, res_name=res.name, spin_name=proton_name, pipe=pipe_name)
274 h_spin.select = False
275
276
277 if not hasattr(h_spin, 'element'):
278 pipe_control.mol_res_spin.set_spin_element(spin_id=spin_id2, element='H', pipe=pipe_name)
279 if not hasattr(h_spin, 'isotope'):
280 pipe_control.mol_res_spin.set_spin_isotope(spin_id=spin_id2, isotope='1H', pipe=pipe_name)
281 pipe_control.interatomic.define(spin_id1, spin_id2, verbose=False, pipe=pipe_name)
282
283
284 interatom = pipe_control.interatomic.return_interatom(spin_id1=spin_id1, spin_id2=spin_id2, pipe=pipe_name)
285
286
287 if hasattr(spin, 'r'):
288 interatom.r = spin.r
289
290
291 if hasattr(spin, 'xh_vect'):
292 interatom.vector = spin.xh_vect
293
294
295 if hasattr(spin, 'rdc'):
296 interatom.rdc = spin.rdc
297 if hasattr(spin, 'rdc_err'):
298 interatom.rdc_err = spin.rdc_err
299 if hasattr(spin, 'rdc_sim'):
300 interatom.rdc_sim = spin.rdc_sim
301 if hasattr(spin, 'rdc_bc'):
302 interatom.rdc_bc = spin.rdc_bc
303
304
305 eliminate += ['heteronuc_type', 'proton_type', 'attached_proton', 'r', 'r_err', 'r_sim', 'rdc', 'rdc_err', 'rdc_sim', 'rdc_bc', 'xh_vect']
306
307
308 for name in eliminate:
309 if hasattr(spin, name):
310 delattr(spin, name)
311
312
313 if hasattr(dp, 'interatomic'):
314 for interatom in dp.interatomic:
315
316 if hasattr(interatom, 'rdc') and not hasattr(interatom, 'rdc_data_types'):
317
318 interatom.rdc_data_types = {}
319
320
321 for id in dp.rdc_ids:
322 interatom.rdc_data_types[id] = 'D'
323
324
325 if hasattr(dp, 'align_tensors'):
326 for i in range(len(dp.align_tensors)):
327
328 if not hasattr(dp.align_tensors[i], 'align_id'):
329 dp.align_tensors[i].set('align_id', dp.align_tensors[i].name)
330
331
332 if hasattr(dp, 'frq'):
333
334 dp.spectrometer_frq = dp.frq
335 del dp.frq
336
337
338 dp.spectrometer_frq_list = []
339 for frq in list(dp.spectrometer_frq.values()):
340 if frq not in dp.spectrometer_frq_list:
341 dp.spectrometer_frq_list.append(frq)
342
343
344 dp.spectrometer_frq_count = len(dp.spectrometer_frq_list)
345 dp.spectrometer_frq_list.sort()
346
347
348 if hasattr(dp, 'q_factors_pcs'):
349 dp.q_factors_pcs_norm_squared_sum = dp.q_factors_pcs
350 del dp.q_factors_pcs
351 if hasattr(dp, 'q_pcs'):
352 dp.q_pcs_norm_squared_sum = dp.q_pcs
353 del dp.q_pcs
354
355
356 if hasattr(dp, 'q_factors_rdc'):
357 dp.q_factors_rdc_norm_tensor_size = dp.q_factors_rdc
358 del dp.q_factors_rdc
359 if hasattr(dp, 'q_rdc'):
360 dp.q_rdc_norm_tensor_size = dp.q_rdc
361 del dp.q_rdc
362 if hasattr(dp, 'q_factors_rdc_norm2'):
363 dp.q_factors_rdc_norm_squared_sum = dp.q_factors_rdc_norm2
364 del dp.q_factors_rdc_norm2
365 if hasattr(dp, 'q_rdc_norm2'):
366 dp.q_rdc_norm_squared_sum = dp.q_rdc_norm2
367 del dp.q_rdc_norm2
368
369
370 - def add(self, pipe_name, pipe_type, bundle=None, switch=True):
371 """Method for adding a new data pipe container to the dictionary.
372
373 This method should be used rather than importing the PipeContainer class and using the statement 'D[pipe] = PipeContainer()', where D is the relax data storage object and pipe is the name of the data pipe.
374
375 @param pipe_name: The name of the new data pipe.
376 @type pipe_name: str
377 @param pipe_type: The data pipe type.
378 @type pipe_type: str
379 @keyword bundle: The optional data pipe bundle to associate the data pipe with.
380 @type bundle: str or None
381 @keyword switch: A flag which if True will cause the new data pipe to be set to the current data pipe.
382 @type switch: bool
383 """
384
385
386 if pipe_name in self.instance:
387 raise RelaxPipeError(pipe_name)
388
389
390 self[pipe_name] = PipeContainer()
391
392
393 self[pipe_name].pipe_type = pipe_type
394
395
396 if bundle:
397
398 if bundle not in self.pipe_bundles:
399 self.pipe_bundles[bundle] = []
400
401
402 self.pipe_bundles[bundle].append(pipe_name)
403
404
405 if switch:
406
407 self.instance.current_pipe = pipe_name
408 builtins.cdp = self[pipe_name]
409
410
411 status.observers.pipe_alteration.notify()
412
413
415 """Method for testing if the relax data store is empty.
416
417 @keyword verbosity: A flag which if True will cause messages to be printed to STDERR.
418 @type verbosity: bool
419 @return: True if the data store is empty, False otherwise.
420 @rtype: bool
421 """
422
423
424 if len(self):
425 if verbosity:
426 stderr.write("The relax data store contains the data pipes %s.\n" % sorted(self.keys()))
427 return False
428
429
430 blacklist = [
431 'pipe_bundles',
432 'relax_gui'
433 ]
434
435
436 for name in dir(self):
437
438 if name in self.__class__.__dict__:
439 continue
440
441
442 if name in dict.__dict__:
443 continue
444
445
446 if search("^__", name):
447 continue
448
449
450 if name in blacklist:
451 continue
452
453
454 if verbosity:
455 stderr.write("The relax data store contains the object %s.\n" % name)
456 return False
457
458
459 return True
460
461
462 - def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
463 """Parse a XML document representation of a data pipe, and load it into the relax data store.
464
465 @param file: The open file object.
466 @type file: file
467 @keyword dir: The name of the directory containing the results file (needed for loading external files).
468 @type dir: str
469 @keyword pipe_to: The data pipe to load the XML data pipe into (the file must only contain one data pipe).
470 @type pipe_to: str
471 @keyword verbosity: A flag specifying the amount of information to print. The higher the value, the greater the verbosity.
472 @type verbosity: int
473 @raises RelaxError: If pipe_to is given and the file contains multiple pipe elements; or if the data pipes in the XML file already exist in the relax data store; or if the data pipe type is invalid; or if the target data pipe is not empty.
474 @raises RelaxNoPipeError: If pipe_to is given but the data pipe does not exist.
475 @raises RelaxError: If the data pipes in the XML file already exist in the relax data store, or if the data pipe type is invalid.
476 @raises RelaxPipeError: If the data pipes of the XML file are already present in the relax data store.
477 """
478
479
480 doc = xml.dom.minidom.parse(file)
481
482
483 relax_node = doc.childNodes[0]
484
485
486 file_version = relax_node.getAttribute('file_version')
487 if file_version == '':
488 file_version = 1
489 else:
490 file_version = int(file_version)
491
492
493 pipe_nodes = relax_node.getElementsByTagName('pipe')
494
495
496 pipes = []
497
498
499 if pipe_to:
500
501 if len(pipe_nodes) > 1:
502 raise RelaxError("The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements." % pipe_to)
503
504
505 pipe_type = pipe_nodes[0].getAttribute('type')
506
507
508 if not pipe_to in self:
509 raise RelaxNoPipeError(pipe_to)
510
511
512 if pipe_type != self[pipe_to].pipe_type:
513 raise RelaxError("The XML file pipe type '%s' does not match the pipe type '%s'" % (pipe_type, self[pipe_to].pipe_type))
514
515
516 if not self[pipe_to].is_empty():
517 raise RelaxError("The data pipe '%s' is not empty." % pipe_to)
518
519
520 self[pipe_to].from_xml(pipe_nodes[0], dir=dir, file_version=file_version)
521
522
523 pipes.append(pipe_to)
524
525
526 else:
527
528 gui_nodes = relax_node.getElementsByTagName('relax_gui')
529 if gui_nodes:
530 self.relax_gui.from_xml(gui_nodes[0], file_version=file_version)
531
532
533 seq_align_nodes = relax_node.getElementsByTagName('sequence_alignments')
534 if seq_align_nodes:
535
536 self.sequence_alignments = Sequence_alignments()
537
538
539 self.sequence_alignments.from_xml(seq_align_nodes[0], file_version=file_version)
540
541
542 xml_to_object(relax_node, self, file_version=file_version, blacklist=['pipe', 'relax_gui', 'sequence_alignments'])
543
544
545 for pipe_node in pipe_nodes:
546
547 pipe_name = str(pipe_node.getAttribute('name'))
548 pipe_type = pipe_node.getAttribute('type')
549
550
551 if pipe_name in self:
552 raise RelaxPipeError(pipe_name)
553
554
555 if not pipe_type in pipe_control.pipes.VALID_TYPES:
556 raise RelaxError("The data pipe type '%s' is invalid and must be one of the strings in the list %s." % (pipe_type, pipe_control.pipes.VALID_TYPES))
557
558
559 for pipe_node in pipe_nodes:
560
561 pipe_name = str(pipe_node.getAttribute('name'))
562 pipe_type = pipe_node.getAttribute('type')
563
564
565 switch = False
566 if self.current_pipe == None:
567 switch = True
568 self.add(pipe_name, pipe_type, switch=switch)
569
570
571 self[pipe_name].from_xml(pipe_node, file_version=file_version, dir=dir)
572
573
574 pipes.append(pipe_name)
575
576
577 if self.current_pipe in self:
578 builtins.cdp = self[self.current_pipe]
579
580
581 for pipe in pipes:
582 pipe_control.mol_res_spin.metadata_update(pipe=pipe)
583
584
585 self._back_compat_hook(file_version, pipes=pipes)
586
587
588 - def to_xml(self, file, pipes=None):
589 """Create a XML document representation of the current data pipe.
590
591 This method creates the top level XML document including all the information needed
592 about relax, calls the PipeContainer.xml_write() method to fill in the document contents,
593 and writes the XML into the file object.
594
595 @param file: The open file object.
596 @type file: file
597 @param pipes: The name of the pipe, or list of pipes to place in the XML file.
598 @type pipes: str or list of str
599 """
600
601
602 all = False
603 if not pipes:
604 all = True
605 pipes = list(self.keys())
606 elif isinstance(pipes, str):
607 pipes = [pipes]
608
609
610 pipes.sort()
611
612
613 xmldoc = xml.dom.minidom.Document()
614
615
616 top_element = xmldoc.createElementNS('http://www.nmr-relax.com', 'relax')
617 top_element.setAttribute("xmlns", "http://www.nmr-relax.com")
618
619
620 xmldoc.appendChild(top_element)
621
622
623 top_element.setAttribute('version', version.version)
624 top_element.setAttribute('time', asctime())
625 top_element.setAttribute('file_version', "2")
626 if version.repo_revision:
627 top_element.setAttribute('revision', version.repo_revision)
628 if version.repo_url:
629 top_element.setAttribute('url', version.repo_url)
630
631
632 if all:
633 blacklist = list(self.__class__.__dict__.keys()) + list(dict.__dict__.keys())
634 for name in dir(self):
635
636 if name in blacklist:
637 continue
638
639
640 if search('^_', name):
641 continue
642
643
644 obj = getattr(self, name)
645 if hasattr(obj, 'to_xml'):
646 obj.to_xml(xmldoc, top_element)
647 blacklist = blacklist + [name]
648
649
650 blacklist.remove('current_pipe')
651
652
653 fill_object_contents(xmldoc, top_element, object=self, blacklist=blacklist)
654
655
656 for pipe in pipes:
657
658 pipe_element = xmldoc.createElement('pipe')
659 top_element.appendChild(pipe_element)
660
661
662 pipe_element.setAttribute('desc', 'The contents of a relax data pipe')
663 pipe_element.setAttribute('name', pipe)
664 pipe_element.setAttribute('type', self[pipe].pipe_type)
665
666
667 self[pipe].to_xml(xmldoc, pipe_element, pipe_type=self[pipe].pipe_type)
668
669
670 file.write(xmldoc.toprettyxml(indent=' '))
671