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, 'num_int_pts'):
349
350 dp.sobol_max_points = dp.num_int_pts
351 del dp.num_int_pts
352
353
354 cdp.sobol_oversample = 1
355
356
357 if hasattr(dp, 'q_factors_pcs'):
358 dp.q_factors_pcs_norm_squared_sum = dp.q_factors_pcs
359 del dp.q_factors_pcs
360 if hasattr(dp, 'q_pcs'):
361 dp.q_pcs_norm_squared_sum = dp.q_pcs
362 del dp.q_pcs
363
364
365 if hasattr(dp, 'q_factors_rdc'):
366 dp.q_factors_rdc_norm_tensor_size = dp.q_factors_rdc
367 del dp.q_factors_rdc
368 if hasattr(dp, 'q_rdc'):
369 dp.q_rdc_norm_tensor_size = dp.q_rdc
370 del dp.q_rdc
371 if hasattr(dp, 'q_factors_rdc_norm2'):
372 dp.q_factors_rdc_norm_squared_sum = dp.q_factors_rdc_norm2
373 del dp.q_factors_rdc_norm2
374 if hasattr(dp, 'q_rdc_norm2'):
375 dp.q_rdc_norm_squared_sum = dp.q_rdc_norm2
376 del dp.q_rdc_norm2
377
378
379 - def add(self, pipe_name, pipe_type, bundle=None, switch=True):
380 """Method for adding a new data pipe container to the dictionary.
381
382 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.
383
384 @param pipe_name: The name of the new data pipe.
385 @type pipe_name: str
386 @param pipe_type: The data pipe type.
387 @type pipe_type: str
388 @keyword bundle: The optional data pipe bundle to associate the data pipe with.
389 @type bundle: str or None
390 @keyword switch: A flag which if True will cause the new data pipe to be set to the current data pipe.
391 @type switch: bool
392 """
393
394
395 if pipe_name in self.instance:
396 raise RelaxPipeError(pipe_name)
397
398
399 self[pipe_name] = PipeContainer()
400
401
402 self[pipe_name].pipe_type = pipe_type
403
404
405 if bundle:
406
407 if bundle not in self.pipe_bundles:
408 self.pipe_bundles[bundle] = []
409
410
411 self.pipe_bundles[bundle].append(pipe_name)
412
413
414 if switch:
415
416 self.instance.current_pipe = pipe_name
417 builtins.cdp = self[pipe_name]
418
419
420 status.observers.pipe_alteration.notify()
421
422
424 """Method for testing if the relax data store is empty.
425
426 @keyword verbosity: A flag which if True will cause messages to be printed to STDERR.
427 @type verbosity: bool
428 @return: True if the data store is empty, False otherwise.
429 @rtype: bool
430 """
431
432
433 if len(self):
434 if verbosity:
435 stderr.write("The relax data store contains the data pipes %s.\n" % sorted(self.keys()))
436 return False
437
438
439 blacklist = [
440 'pipe_bundles',
441 'relax_gui'
442 ]
443
444
445 for name in dir(self):
446
447 if name in self.__class__.__dict__:
448 continue
449
450
451 if name in dict.__dict__:
452 continue
453
454
455 if search("^__", name):
456 continue
457
458
459 if name in blacklist:
460 continue
461
462
463 if verbosity:
464 stderr.write("The relax data store contains the object %s.\n" % name)
465 return False
466
467
468 return True
469
470
471 - def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
472 """Parse a XML document representation of a data pipe, and load it into the relax data store.
473
474 @param file: The open file object.
475 @type file: file
476 @keyword dir: The name of the directory containing the results file (needed for loading external files).
477 @type dir: str
478 @keyword pipe_to: The data pipe to load the XML data pipe into (the file must only contain one data pipe).
479 @type pipe_to: str
480 @keyword verbosity: A flag specifying the amount of information to print. The higher the value, the greater the verbosity.
481 @type verbosity: int
482 @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.
483 @raises RelaxNoPipeError: If pipe_to is given but the data pipe does not exist.
484 @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.
485 @raises RelaxPipeError: If the data pipes of the XML file are already present in the relax data store.
486 """
487
488
489 doc = xml.dom.minidom.parse(file)
490
491
492 relax_node = doc.childNodes[0]
493
494
495 file_version = relax_node.getAttribute('file_version')
496 if file_version == '':
497 file_version = 1
498 else:
499 file_version = int(file_version)
500
501
502 pipe_nodes = relax_node.getElementsByTagName('pipe')
503
504
505 pipes = []
506
507
508 if pipe_to:
509
510 if len(pipe_nodes) > 1:
511 raise RelaxError("The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements." % pipe_to)
512
513
514 pipe_type = pipe_nodes[0].getAttribute('type')
515
516
517 if not pipe_to in self:
518 raise RelaxNoPipeError(pipe_to)
519
520
521 if pipe_type != self[pipe_to].pipe_type:
522 raise RelaxError("The XML file pipe type '%s' does not match the pipe type '%s'" % (pipe_type, self[pipe_to].pipe_type))
523
524
525 if not self[pipe_to].is_empty():
526 raise RelaxError("The data pipe '%s' is not empty." % pipe_to)
527
528
529 self[pipe_to].from_xml(pipe_nodes[0], dir=dir, file_version=file_version)
530
531
532 pipes.append(pipe_to)
533
534
535 else:
536
537 gui_nodes = relax_node.getElementsByTagName('relax_gui')
538 if gui_nodes:
539 self.relax_gui.from_xml(gui_nodes[0], file_version=file_version)
540
541
542 seq_align_nodes = relax_node.getElementsByTagName('sequence_alignments')
543 if seq_align_nodes:
544
545 self.sequence_alignments = Sequence_alignments()
546
547
548 self.sequence_alignments.from_xml(seq_align_nodes[0], file_version=file_version)
549
550
551 xml_to_object(relax_node, self, file_version=file_version, blacklist=['pipe', 'relax_gui', 'sequence_alignments'])
552
553
554 for pipe_node in pipe_nodes:
555
556 pipe_name = str(pipe_node.getAttribute('name'))
557 pipe_type = pipe_node.getAttribute('type')
558
559
560 if pipe_name in self:
561 raise RelaxPipeError(pipe_name)
562
563
564 if not pipe_type in pipe_control.pipes.VALID_TYPES:
565 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))
566
567
568 for pipe_node in pipe_nodes:
569
570 pipe_name = str(pipe_node.getAttribute('name'))
571 pipe_type = pipe_node.getAttribute('type')
572
573
574 switch = False
575 if self.current_pipe == None:
576 switch = True
577 self.add(pipe_name, pipe_type, switch=switch)
578
579
580 self[pipe_name].from_xml(pipe_node, file_version=file_version, dir=dir)
581
582
583 pipes.append(pipe_name)
584
585
586 if self.current_pipe in self:
587 builtins.cdp = self[self.current_pipe]
588
589
590 for pipe in pipes:
591 pipe_control.mol_res_spin.metadata_update(pipe=pipe)
592
593
594 self._back_compat_hook(file_version, pipes=pipes)
595
596
597 - def to_xml(self, file, pipes=None):
598 """Create a XML document representation of the current data pipe.
599
600 This method creates the top level XML document including all the information needed
601 about relax, calls the PipeContainer.xml_write() method to fill in the document contents,
602 and writes the XML into the file object.
603
604 @param file: The open file object.
605 @type file: file
606 @param pipes: The name of the pipe, or list of pipes to place in the XML file.
607 @type pipes: str or list of str
608 """
609
610
611 all = False
612 if not pipes:
613 all = True
614 pipes = list(self.keys())
615 elif isinstance(pipes, str):
616 pipes = [pipes]
617
618
619 pipes.sort()
620
621
622 xmldoc = xml.dom.minidom.Document()
623
624
625 top_element = xmldoc.createElementNS('http://www.nmr-relax.com', 'relax')
626 top_element.setAttribute("xmlns", "http://www.nmr-relax.com")
627
628
629 xmldoc.appendChild(top_element)
630
631
632 top_element.setAttribute('version', version.version)
633 top_element.setAttribute('time', asctime())
634 top_element.setAttribute('file_version', "2")
635 if version.repo_revision:
636 top_element.setAttribute('revision', version.repo_revision)
637 if version.repo_url:
638 top_element.setAttribute('url', version.repo_url)
639
640
641 if all:
642 blacklist = list(self.__class__.__dict__.keys()) + list(dict.__dict__.keys())
643 for name in dir(self):
644
645 if name in blacklist:
646 continue
647
648
649 if search('^_', name):
650 continue
651
652
653 obj = getattr(self, name)
654 if hasattr(obj, 'to_xml'):
655 obj.to_xml(xmldoc, top_element)
656 blacklist = blacklist + [name]
657
658
659 blacklist.remove('current_pipe')
660
661
662 fill_object_contents(xmldoc, top_element, object=self, blacklist=blacklist)
663
664
665 for pipe in pipes:
666
667 pipe_element = xmldoc.createElement('pipe')
668 top_element.appendChild(pipe_element)
669
670
671 pipe_element.setAttribute('desc', 'The contents of a relax data pipe')
672 pipe_element.setAttribute('name', pipe)
673 pipe_element.setAttribute('type', self[pipe].pipe_type)
674
675
676 self[pipe].to_xml(xmldoc, pipe_element, pipe_type=self[pipe].pipe_type)
677
678
679 file.write(xmldoc.toprettyxml(indent=' '))
680