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