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.gui import Gui
35 from data.pipe_container import PipeContainer
36 from data.relax_xml import fill_object_contents, xml_to_object
37 import generic_fns
38 from relax_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 = generic_fns.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 generic_fns.mol_res_spin.name_spin(spin_id=spin_id, name='N', pipe=pipe_name)
226 elif search('C', spin.isotope):
227 generic_fns.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 = generic_fns.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin=spin)
239 spin_id2 = generic_fns.mol_res_spin.generate_spin_id_unique(pipe_cont=dp, mol=mol, res=res, spin_name=proton_name)
240
241
242 h_spin = generic_fns.mol_res_spin.return_spin(spin_id2, pipe=pipe_name)
243 if h_spin:
244 spin_id2 = generic_fns.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 = generic_fns.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 generic_fns.mol_res_spin.set_spin_element(spin_id=spin_id2, element='H', pipe=pipe_name)
254 if not hasattr(h_spin, 'isotope'):
255 generic_fns.mol_res_spin.set_spin_isotope(spin_id=spin_id2, isotope='1H', pipe=pipe_name)
256 generic_fns.dipole_pair.define(spin_id1, spin_id2, verbose=False, pipe=pipe_name)
257
258
259 interatom = generic_fns.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 - def add(self, pipe_name, pipe_type, bundle=None, switch=True):
287 """Method for adding a new data pipe container to the dictionary.
288
289 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.
290
291 @param pipe_name: The name of the new data pipe.
292 @type pipe_name: str
293 @param pipe_type: The data pipe type.
294 @type pipe_type: str
295 @keyword bundle: The optional data pipe bundle to associate the data pipe with.
296 @type bundle: str or None
297 @keyword switch: A flag which if True will cause the new data pipe to be set to the current data pipe.
298 @type switch: bool
299 """
300
301
302 if pipe_name in list(self.instance.keys()):
303 raise RelaxPipeError(pipe_name)
304
305
306 self[pipe_name] = PipeContainer()
307
308
309 self[pipe_name].pipe_type = pipe_type
310
311
312 if bundle:
313
314 if bundle not in list(self.pipe_bundles.keys()):
315 self.pipe_bundles[bundle] = []
316
317
318 self.pipe_bundles[bundle].append(pipe_name)
319
320
321 if switch:
322
323 self.instance.current_pipe = pipe_name
324 builtins.cdp = self[pipe_name]
325
326
327 status.observers.pipe_alteration.notify()
328
329
331 """Method for testing if the relax data store is empty.
332
333 @keyword verbosity: A flag which if True will cause messages to be printed to STDERR.
334 @type verbosity: bool
335 @return: True if the data store is empty, False otherwise.
336 @rtype: bool
337 """
338
339
340 if not list(self.keys()) == []:
341 if verbosity:
342 stderr.write("The relax data store contains the data pipes %s.\n" % list(self.keys()))
343 return False
344
345
346 blacklist = [
347 'pipe_bundles',
348 'relax_gui'
349 ]
350
351
352 for name in dir(self):
353
354 if name in list(self.__class__.__dict__.keys()):
355 continue
356
357
358 if name in list(dict.__dict__.keys()):
359 continue
360
361
362 if search("^__", name):
363 continue
364
365
366 if name in blacklist:
367 continue
368
369
370 if verbosity:
371 stderr.write("The relax data store contains the object %s.\n" % name)
372 return False
373
374
375 return True
376
377
378 - def from_xml(self, file, dir=None, pipe_to=None, verbosity=1):
379 """Parse a XML document representation of a data pipe, and load it into the relax data store.
380
381 @param file: The open file object.
382 @type file: file
383 @keyword dir: The name of the directory containing the results file (needed
384 for loading external files).
385 @type dir: str
386 @keyword pipe_to: The data pipe to load the XML data pipe into (the file must only
387 contain one data pipe).
388 @type pipe_to: str
389 @keyword verbosity: A flag specifying the amount of information to print. The
390 higher the value, the greater the verbosity.
391 @type verbosity: int
392 @raises RelaxError: If pipe_to is given and the file contains multiple pipe
393 elements; or if the data pipes in the XML file already exist in
394 the relax data store; or if the data pipe type is invalid; or
395 if the target data pipe is not empty.
396 @raises RelaxNoPipeError: If pipe_to is given but the data pipe does not exist.
397 @raises RelaxError: If the data pipes in the XML file already exist in the relax
398 data store, or if the data pipe type is invalid.
399 @raises RelaxPipeError: If the data pipes of the XML file are already present in the
400 relax data store.
401 """
402
403
404 doc = xml.dom.minidom.parse(file)
405
406
407 relax_node = doc.childNodes[0]
408
409
410 file_version = relax_node.getAttribute('file_version')
411 if file_version == '':
412 file_version = 1
413 else:
414 file_version = int(file_version)
415
416
417 gui_nodes = relax_node.getElementsByTagName('relax_gui')
418 if gui_nodes:
419 self.relax_gui.from_xml(gui_nodes[0], file_version=file_version)
420
421
422 xml_to_object(relax_node, self, file_version=file_version, blacklist=['pipe', 'relax_gui'])
423
424
425 pipe_nodes = relax_node.getElementsByTagName('pipe')
426
427
428 pipes = []
429
430
431 if pipe_to:
432
433 if len(pipe_nodes) > 1:
434 raise RelaxError("The pipe_to target pipe argument '%s' cannot be given as the file contains multiple pipe elements." % pipe_to)
435
436
437 pipe_type = pipe_nodes[0].getAttribute('type')
438
439
440 if not pipe_to in self:
441 raise RelaxNoPipeError(pipe_to)
442
443
444 if pipe_type != self[pipe_to].pipe_type:
445 raise RelaxError("The XML file pipe type '%s' does not match the pipe type '%s'" % (pipe_type, self[pipe_to].pipe_type))
446
447
448 if not self[pipe_to].is_empty():
449 raise RelaxError("The data pipe '%s' is not empty." % pipe_to)
450
451
452 self[pipe_to].from_xml(pipe_nodes[0], dir=dir, file_version=file_version)
453
454
455 pipes.append(pipe_to)
456
457
458 else:
459
460 for pipe_node in pipe_nodes:
461
462 pipe_name = str(pipe_node.getAttribute('name'))
463 pipe_type = pipe_node.getAttribute('type')
464
465
466 if pipe_name in self:
467 raise RelaxPipeError(pipe_name)
468
469
470 if not pipe_type in generic_fns.pipes.VALID_TYPES:
471 raise RelaxError("The data pipe type '%s' is invalid and must be one of the strings in the list %s." % (pipe_type, generic_fns.pipes.VALID_TYPES))
472
473
474 for pipe_node in pipe_nodes:
475
476 pipe_name = str(pipe_node.getAttribute('name'))
477 pipe_type = pipe_node.getAttribute('type')
478
479
480 switch = False
481 if self.current_pipe == None:
482 switch = True
483 self.add(pipe_name, pipe_type, switch=switch)
484
485
486 self[pipe_name].from_xml(pipe_node, file_version=file_version, dir=dir)
487
488
489 pipes.append(pipe_name)
490
491
492 if self.current_pipe in list(self.keys()):
493 builtins.cdp = self[self.current_pipe]
494
495
496 for pipe in pipes:
497 generic_fns.mol_res_spin.metadata_update(pipe=pipe)
498
499
500 self._back_compat_hook(file_version, pipes=pipes)
501
502
503 - def to_xml(self, file, pipes=None):
504 """Create a XML document representation of the current data pipe.
505
506 This method creates the top level XML document including all the information needed
507 about relax, calls the PipeContainer.xml_write() method to fill in the document contents,
508 and writes the XML into the file object.
509
510 @param file: The open file object.
511 @type file: file
512 @param pipes: The name of the pipe, or list of pipes to place in the XML file.
513 @type pipes: str or list of str
514 """
515
516
517 all = False
518 if not pipes:
519 all = True
520 pipes = list(self.keys())
521 elif isinstance(pipes, str):
522 pipes = [pipes]
523
524
525 pipes.sort()
526
527
528 xmldoc = xml.dom.minidom.Document()
529
530
531 top_element = xmldoc.createElementNS('http://www.nmr-relax.com', 'relax')
532 top_element.setAttribute("xmlns", "http://www.nmr-relax.com")
533
534
535 xmldoc.appendChild(top_element)
536
537
538 top_element.setAttribute('version', version.version)
539 top_element.setAttribute('time', asctime())
540 top_element.setAttribute('file_version', "2")
541 rev = version.revision()
542 if rev:
543 top_element.setAttribute('revision', rev)
544 url = version.url()
545 if url:
546 top_element.setAttribute('url', url)
547
548
549 if all:
550 blacklist = list(list(self.__class__.__dict__.keys()) + list(dict.__dict__.keys()))
551 for name in dir(self):
552
553 if name in blacklist:
554 continue
555
556
557 if search('^_', name):
558 continue
559
560
561 obj = getattr(self, name)
562 if hasattr(obj, 'to_xml'):
563 obj.to_xml(xmldoc, top_element)
564 blacklist = blacklist + [name]
565
566
567 blacklist.remove('current_pipe')
568
569
570 fill_object_contents(xmldoc, top_element, object=self, blacklist=blacklist)
571
572
573 for pipe in pipes:
574
575 pipe_element = xmldoc.createElement('pipe')
576 top_element.appendChild(pipe_element)
577
578
579 pipe_element.setAttribute('desc', 'The contents of a relax data pipe')
580 pipe_element.setAttribute('name', pipe)
581 pipe_element.setAttribute('type', self[pipe].pipe_type)
582
583
584 self[pipe].to_xml(xmldoc, pipe_element)
585
586
587 file.write(xmldoc.toprettyxml(indent=' '))
588