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