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