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