Package data :: Module mol_res_spin
[hide private]
[frames] | no frames]

Source Code for Module data.mol_res_spin

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2007-2013 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """The molecule-residue-spin containers of the relax data store.""" 
 24   
 25  # Python module imports. 
 26  import numpy 
 27  from re import match, search 
 28   
 29  # relax module imports. 
 30  from data.prototype import Prototype 
 31  from data.relax_xml import fill_object_contents, object_to_xml, xml_to_object 
 32  from float import floatAsByteArray 
 33  import generic_fns 
 34  from relax_errors import RelaxError, RelaxFromXMLNotEmptyError, RelaxImplementError 
 35  import specific_fns 
 36   
 37   
 38  # The spin system data. 
 39  ####################### 
 40   
41 -class SpinContainer(Prototype):
42 """Class containing all the spin system specific data.""" 43
44 - def __init__(self, spin_name=None, spin_num=None, select=True):
45 """Set up the default objects of the spin system data container.""" 46 47 # The spin system name and number. 48 self.name = spin_name 49 self.num = spin_num 50 self.select = select 51 52 # The private metadata. 53 self._mol_name = None 54 self._mol_index = None 55 self._res_name = None 56 self._res_num = None 57 self._res_index = None 58 self._spin_index = None 59 self._spin_ids = []
60 61
62 - def __repr__(self):
63 """The string representation of the object. 64 65 Rather than using the standard Python conventions (either the string representation of the 66 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 67 """ 68 69 # Intro. 70 text = "Class containing all the spin system specific data.\n\n" 71 72 # Objects. 73 text = text + "\n" 74 text = text + "Objects:\n" 75 for name in dir(self): 76 # Skip the SpinContainer methods. 77 if name == 'is_empty': 78 continue 79 80 # Skip special objects. 81 if match("^_", name): 82 continue 83 84 # Add the object's attribute to the text string. 85 text = text + " " + name + ": " + repr(getattr(self, name)) + "\n" 86 87 return text
88 89
90 - def _back_compat_hook(self, file_version=None):
91 """Method for converting old spin data structures to the new ones. 92 93 @keyword file_version: The relax XML version of the XML file. 94 @type file_version: int 95 """ 96 97 # Model-free parameters. 98 self._back_compat_hook_mf_data() 99 100 # Relaxation data. 101 self._back_compat_hook_ri_data()
102 103
105 """Converting the old model-free parameter vector to the new one.""" 106 107 # Nothing to do. 108 if not hasattr(self, 'params'): 109 return 110 111 # Loop over the parameters, converting them to lowercase. 112 for i in range(len(self.params)): 113 self.params[i] = self.params[i].lower()
114 115
117 """Converting the old spin relaxation data structures to the new ones.""" 118 119 # Nothing to do. 120 if not (hasattr(self, 'frq_labels') and hasattr(self, 'noe_r1_table') and hasattr(self, 'remap_table')): 121 return 122 123 # Initialise the new structures. 124 self.ri_data = {} 125 self.ri_data_err = {} 126 sims = False 127 if hasattr(self, 'relax_sim_data'): 128 sims = True 129 self.ri_data_sim = {} 130 131 # Generate the new structures. 132 for i in range(self.num_ri): 133 # The ID. 134 ri_id = "%s_%s" % (self.ri_labels[i], self.frq_labels[self.remap_table[i]]) 135 136 # The relaxation data. 137 self.ri_data[ri_id] = self.relax_data[i] 138 self.ri_data_err[ri_id] = self.relax_error[i] 139 140 # Simulation data. 141 if sims: 142 self.ri_data_sim[ri_id] = [] 143 for j in range(cdp.sim_number): 144 self.ri_data_sim[ri_id].append(self.relax_sim_data[j][i]) 145 146 # Delete the old structures. 147 del self.frq 148 del self.frq_labels 149 del self.noe_r1_table 150 del self.num_frq 151 del self.num_ri 152 del self.ri_labels 153 del self.remap_table 154 del self.relax_data 155 del self.relax_error 156 if sims: 157 del self.relax_sim_data
158 159
160 - def is_empty(self):
161 """Method for testing if this SpinContainer object is empty. 162 163 @return: True if this container is empty and the spin number and name have not been set, 164 False otherwise. 165 @rtype: bool 166 """ 167 168 # The spin number or spin name has been set. 169 if self.num != None or self.name != None: 170 return False 171 172 # An object has been added to the container. 173 for name in dir(self): 174 # Skip the objects initialised in __init__(). 175 if name == 'num' or name == 'name' or name == 'select': 176 continue 177 178 # Skip the SpinContainer methods. 179 if name == 'is_empty': 180 continue 181 182 # Skip special objects. 183 if match("^_", name): 184 continue 185 186 # An object has been added. 187 return False 188 189 # The SpinContainer is unmodified. 190 return True
191 192
193 -class SpinList(list):
194 """List type data container for spin system specific data.""" 195
196 - def __init__(self):
197 """Set up the first spin system data container.""" 198 199 # Add the initial spin system container at index 0. 200 self.append(SpinContainer())
201 202
203 - def __repr__(self):
204 """The string representation of the object. 205 206 Rather than using the standard Python conventions (either the string representation of the 207 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 208 """ 209 210 # Intro. 211 text = "Spin systems.\n\n" 212 213 # Residue data. 214 text = text + "%-8s%-8s%-8s%-10s" % ("Index", "Number", "Name", "Selected") + "\n" 215 for i in range(len(self)): 216 text = text + "%-8i%-8s%-8s%-10s" % (i, repr(self[i].num), self[i].name, self[i].select) + "\n" 217 text = text + "\nThese can be accessed by typing 'D.mol[i].res[j].spin[k]', where D is the relax data storage object.\n" 218 219 return text
220 221
222 - def add_item(self, spin_name=None, spin_num=None, select=True):
223 """Appending an empty container to the list. 224 225 @keyword spin_name: The name of the new spin. 226 @type spin_name: str or None 227 @keyword spin_num: The number of the new spin. 228 @type spin_num: str or None 229 @keyword select: The selection flag. 230 @type select: bool 231 @return: The new container. 232 @rtype: SpinContainer instance 233 """ 234 235 # If no spin data exists, replace the empty first spin with this spin. 236 if self.is_empty(): 237 self[0].num = spin_num 238 self[0].name = spin_name 239 self[0].select = select 240 241 # Return the container. 242 return self[0] 243 244 # Otherwise append a new SpinContainer. 245 else: 246 # Test if the spin number (or name if unnumbered) already exists. 247 for i in range(len(self)): 248 # Spin number has been supplied. 249 if spin_num != None: 250 if self[i].num == spin_num: 251 raise RelaxError("The spin number '%s' already exists." % spin_num) 252 253 # No spin numbers. 254 else: 255 if self[i].name == spin_name: 256 raise RelaxError("The unnumbered spin name '%s' already exists." % spin_name) 257 258 # Append a new SpinContainer. 259 self.append(SpinContainer(spin_name, spin_num, select)) 260 261 # Return the container. 262 return self[-1]
263 264
265 - def is_empty(self):
266 """Method for testing if this SpinList object is empty. 267 268 @return: True if this list only has one SpinContainer and the spin number and name have 269 not been set, False otherwise. 270 @rtype: bool 271 """ 272 273 # There is only one SpinContainer and it is empty. 274 if len(self) == 1 and self[0].is_empty(): 275 return True 276 277 # Otherwise. 278 return False
279 280
281 - def from_xml(self, spin_nodes, file_version=None):
282 """Recreate a spin list data structure from the XML spin nodes. 283 284 @param spin_nodes: The spin XML nodes. 285 @type spin_nodes: xml.dom.minicompat.NodeList instance 286 @keyword file_version: The relax XML version of the XML file. 287 @type file_version: int 288 """ 289 290 # Test if empty. 291 if not self.is_empty(): 292 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 293 294 # Loop over the spins. 295 for spin_node in spin_nodes: 296 # Get the spin details and add the spin to the SpinList structure. 297 name = str(spin_node.getAttribute('name')) 298 if name == 'None': 299 name = None 300 num = eval(spin_node.getAttribute('num')) 301 self.add_item(spin_name=name, spin_num=num) 302 303 # Recreate the current spin container. 304 xml_to_object(spin_node, self[-1], file_version=file_version) 305 306 # Backwards compatibility transformations. 307 self[-1]._back_compat_hook(file_version)
308 309
310 - def to_xml(self, doc, element):
311 """Create XML elements for each spin. 312 313 @param doc: The XML document object. 314 @type doc: xml.dom.minidom.Document instance 315 @param element: The element to add the spin XML elements to. 316 @type element: XML element object 317 """ 318 319 # Get the specific functions. 320 data_names = specific_fns.setup.get_specific_fn('data_names', generic_fns.pipes.get_type(), raise_error=False) 321 return_data_desc = specific_fns.setup.get_specific_fn('return_data_desc', generic_fns.pipes.get_type(), raise_error=False) 322 323 # Loop over the spins. 324 for i in range(len(self)): 325 # Create an XML element for this spin and add it to the higher level element. 326 spin_element = doc.createElement('spin') 327 element.appendChild(spin_element) 328 329 # Set the spin attributes. 330 spin_element.setAttribute('desc', 'Spin container') 331 spin_element.setAttribute('name', str(self[i].name)) 332 spin_element.setAttribute('num', str(self[i].num)) 333 334 # Get the spin specific object names and loop over them to get their descriptions. 335 object_info = [] 336 try: 337 for name in data_names(error_names=True, sim_names=True): 338 # Get the description. 339 if return_data_desc: 340 desc = return_data_desc(name) 341 else: 342 desc = None 343 344 # Append the two. 345 object_info.append([name, desc]) 346 except RelaxImplementError: 347 pass 348 349 # Add the ordered objects. 350 blacklist = [] 351 for name, desc in object_info: 352 # Add the name to the blacklist. 353 blacklist.append(name) 354 355 # Skip the object if it is missing from the SpinContainer. 356 if not hasattr(self[i], name): 357 continue 358 359 # Create a new element for this object, and add it to the main element. 360 sub_element = doc.createElement(name) 361 spin_element.appendChild(sub_element) 362 363 # Add the object description. 364 if desc: 365 sub_element.setAttribute('desc', desc) 366 367 # Get the object. 368 object = getattr(self[i], name) 369 370 # Convert to XML. 371 object_to_xml(doc, sub_element, value=object) 372 373 # Add all simple python objects within the SpinContainer to the XML element. 374 fill_object_contents(doc, spin_element, object=self[i], blacklist=['name', 'num', 'spin'] + blacklist + list(self[i].__class__.__dict__.keys()))
375 376 377 378 # The residue data. 379 ################### 380
381 -class ResidueContainer(Prototype):
382 """Class containing all the residue specific data.""" 383
384 - def __init__(self, res_name=None, res_num=None):
385 """Set up the default objects of the residue data container.""" 386 387 # The residue name and number. 388 self.name = res_name 389 self.num = res_num 390 391 # The private metadata. 392 self._mol_name = None 393 self._mol_index = None 394 self._res_index = None 395 396 # The empty spin system list. 397 self.spin = SpinList()
398 399
400 - def __repr__(self):
401 """The string representation of the object. 402 403 Rather than using the standard Python conventions (either the string representation of the 404 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 405 """ 406 407 # Intro. 408 text = "Class containing all the residue specific data.\n" 409 410 # Objects. 411 text = text + "\n" 412 text = text + "Objects:\n" 413 for name in dir(self): 414 # Spin systems. 415 if name == 'spin': 416 text = text + " spin: The list of spin systems of the residues\n" 417 continue 418 419 # Skip the ResidueContainer methods. 420 if name == 'is_empty': 421 continue 422 423 # Skip special objects. 424 if match("^_", name): 425 continue 426 427 # Add the object's attribute to the text string. 428 text = text + " " + name + ": " + repr(getattr(self, name)) + "\n" 429 430 return text
431 432
433 - def is_empty(self):
434 """Method for testing if this ResidueContainer object is empty. 435 436 @return: True if this container is empty and the residue number and name have not been 437 set, False otherwise. 438 @rtype: bool 439 """ 440 441 # The residue number or residue name have been set. 442 if self.num != None or self.name != None: 443 return False 444 445 # An object has been added to the container. 446 for name in dir(self): 447 # Skip the objects initialised in __init__(). 448 if name == 'num' or name == 'name' or name == 'spin': 449 continue 450 451 # Skip the ResidueContainer methods. 452 if name == 'is_empty': 453 continue 454 455 # Skip special objects. 456 if match("^_", name): 457 continue 458 459 # An object has been added. 460 return False 461 462 # The spin list is not empty. 463 if not self.spin.is_empty(): 464 return False 465 466 # The ResidueContainer is unmodified. 467 return True
468 469
470 -class ResidueList(list):
471 """List type data container for residue specific data.""" 472
473 - def __init__(self):
474 """Set up the first residue data container.""" 475 476 # Add the initial residue container at index 0. 477 self.append(ResidueContainer())
478 479
480 - def __repr__(self):
481 """The string representation of the object. 482 483 Rather than using the standard Python conventions (either the string representation of the 484 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 485 """ 486 487 # Intro. 488 text = "Residues.\n\n" 489 490 # Residue data. 491 text = text + "%-8s%-8s%-8s" % ("Index", "Number", "Name") + "\n" 492 for i in range(len(self)): 493 text = text + "%-8i%-8s%-8s" % (i, repr(self[i].num), self[i].name) + "\n" 494 text = text + "\nThese can be accessed by typing 'D.mol[i].res[j]', where D is the relax data storage object.\n" 495 496 return text
497 498
499 - def add_item(self, res_name=None, res_num=None):
500 """Append an empty ResidueContainer to the ResidueList.""" 501 502 # If no residue data exists, replace the empty first residue with this residue. 503 if self.is_empty(): 504 self[0].num = res_num 505 self[0].name = res_name 506 507 # Otherwise append a new ResidueContainer. 508 else: 509 # Test if the residue number (or name if unnumbered) already exists. 510 for i in range(len(self)): 511 # Residue number has been supplied. 512 if res_num != None: 513 if self[i].num == res_num: 514 raise RelaxError("The residue number '" + repr(res_num) + "' already exists in the sequence.") 515 516 # No residue numbers. 517 else: 518 if self[i].name == res_name: 519 raise RelaxError("The unnumbered residue name '" + repr(res_name) + "' already exists.") 520 521 # Append a new ResidueContainer. 522 self.append(ResidueContainer(res_name, res_num))
523 524
525 - def is_empty(self):
526 """Method for testing if this ResidueList object is empty. 527 528 @return: True if this list only has one ResidueContainer and the residue number and name 529 have not been set, False otherwise. 530 @rtype: bool 531 """ 532 533 # There is only one ResidueContainer and it is empty. 534 if len(self) == 1 and self[0].is_empty(): 535 return True 536 537 # Otherwise. 538 return False
539 540
541 - def from_xml(self, res_nodes, file_version=None):
542 """Recreate a residue list data structure from the XML residue nodes. 543 544 @param res_nodes: The residue XML nodes. 545 @type res_nodes: xml.dom.minicompat.NodeList instance 546 @keyword file_version: The relax XML version of the XML file. 547 @type file_version: int 548 """ 549 550 # Test if empty. 551 if not self.is_empty(): 552 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 553 554 # Loop over the residues. 555 for res_node in res_nodes: 556 # Get the residue details and add the residue to the ResidueList structure. 557 name = str(res_node.getAttribute('name')) 558 if name == 'None': 559 name = None 560 num = eval(res_node.getAttribute('num')) 561 self.add_item(res_name=name, res_num=num) 562 563 # Get the spin nodes. 564 spin_nodes = res_node.getElementsByTagName('spin') 565 566 # Recreate the spin data structures for the current residue. 567 self[-1].spin.from_xml(spin_nodes, file_version=file_version)
568 569
570 - def to_xml(self, doc, element):
571 """Create XML elements for each residue. 572 573 @param doc: The XML document object. 574 @type doc: xml.dom.minidom.Document instance 575 @param element: The element to add the residue XML elements to. 576 @type element: XML element object 577 """ 578 579 # Loop over the residues. 580 for i in range(len(self)): 581 # Create an XML element for this residue and add it to the higher level element. 582 res_element = doc.createElement('res') 583 element.appendChild(res_element) 584 585 # Set the residue attributes. 586 res_element.setAttribute('desc', 'Residue container') 587 res_element.setAttribute('name', str(self[i].name)) 588 res_element.setAttribute('num', str(self[i].num)) 589 590 # Add all simple python objects within the ResidueContainer to the XML element. 591 fill_object_contents(doc, res_element, object=self[i], blacklist=['name', 'num', 'spin'] + list(self[i].__class__.__dict__.keys())) 592 593 # Add the residue data. 594 self[i].spin.to_xml(doc, res_element)
595 596 597 598 # The molecule data. 599 ################### 600
601 -class MoleculeContainer(Prototype):
602 """Class containing all the molecule specific data.""" 603
604 - def __init__(self, mol_name=None, mol_type=None):
605 """Set up the default objects of the molecule data container.""" 606 607 # The name of the molecule, corresponding to that of the structure file if specified. 608 self.name = mol_name 609 610 # The type of molecule. 611 self.type = mol_type 612 613 # The private metadata. 614 self._mol_index = None 615 616 # The empty residue list. 617 self.res = ResidueList()
618 619
620 - def __repr__(self):
621 """The string representation of the object. 622 623 Rather than using the standard Python conventions (either the string representation of the 624 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 625 """ 626 627 # Intro. 628 text = "Class containing all the molecule specific data.\n" 629 630 # Objects. 631 text = text + "\n" 632 text = text + "Objects:\n" 633 for name in dir(self): 634 # Residue list. 635 if name == 'res': 636 text = text + " res: The list of the residues of the molecule\n" 637 continue 638 639 # Skip the MoleculeContainer methods. 640 if name == 'is_empty': 641 continue 642 643 # Skip special objects. 644 if match("^_", name): 645 continue 646 647 # Add the object's attribute to the text string. 648 text = text + " " + name + ": " + repr(getattr(self, name)) + "\n" 649 650 return text
651 652
653 - def is_empty(self):
654 """Method for testing if this MoleculeContainer object is empty. 655 656 @return: True if this container is empty and the molecule name has not been set, False 657 otherwise. 658 @rtype: bool 659 """ 660 661 # The molecule name has been set. 662 if self.name != None: 663 return False 664 665 # An object has been added to the container. 666 for name in dir(self): 667 # Skip the objects initialised in __init__(). 668 if name in ['name', 'res', 'type']: 669 continue 670 671 # Skip the MoleculeContainer methods. 672 if name == 'is_empty': 673 continue 674 675 # Skip special objects. 676 if match("^_", name): 677 continue 678 679 # An object has been added. 680 return False 681 682 # The residue list is not empty. 683 if not self.res.is_empty(): 684 return False 685 686 # The MoleculeContainer is unmodified. 687 return True
688 689
690 -class MoleculeList(list):
691 """List type data container for the molecule specific data.""" 692
693 - def __init__(self):
694 """Set up the first molecule data container.""" 695 696 # Add the initial molecule container at index 0. 697 self.append(MoleculeContainer()) 698 699 # Create a special private lookup table for fast spin accesses. 700 self._spin_id_lookup = {}
701 702
703 - def __repr__(self):
704 """The string representation of the object. 705 706 Rather than using the standard Python conventions (either the string representation of the 707 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 708 """ 709 710 text = "Molecules.\n\n" 711 text = text + "%-8s%-8s" % ("Index", "Name") + "\n" 712 for i in range(len(self)): 713 text = text + "%-8i%-8s" % (i, self[i].name) + "\n" 714 text = text + "\nThese can be accessed by typing 'D.mol[i]', where D is the relax data storage object.\n" 715 return text
716 717
718 - def add_item(self, mol_name=None, mol_type=None):
719 """Append an empty MoleculeContainer to the MoleculeList.""" 720 721 # If no molecule data exists, replace the empty first molecule with this molecule (just a renaming). 722 if self.is_empty(): 723 self[0].name = mol_name 724 self[0].type = mol_type 725 726 # Otherwise append an empty MoleculeContainer. 727 else: 728 # Test if the molecule name already exists. 729 for i in range(len(self)): 730 if self[i].name == mol_name: 731 raise RelaxError("The molecule '%s' already exists in the sequence." % mol_name) 732 733 # Append an empty MoleculeContainer. 734 self.append(MoleculeContainer(mol_name, mol_type))
735 736
737 - def is_empty(self):
738 """Method for testing if this MoleculeList object is empty. 739 740 @return: True if this list only has one MoleculeContainer and the molecule name has not 741 been set, False otherwise. 742 @rtype: bool 743 """ 744 745 # There is only one MoleculeContainer and it is empty. 746 if len(self) == 1 and self[0].is_empty(): 747 return True 748 749 # Otherwise. 750 return False
751 752
753 - def from_xml(self, mol_nodes, file_version=None):
754 """Recreate a molecule list data structure from the XML molecule nodes. 755 756 @param mol_nodes: The molecule XML nodes. 757 @type mol_nodes: xml.dom.minicompat.NodeList instance 758 @keyword file_version: The relax XML version of the XML file. 759 @type file_version: int 760 """ 761 762 # Test if empty. 763 if not self.is_empty(): 764 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 765 766 # Loop over the molecules. 767 for mol_node in mol_nodes: 768 # Get the molecule details and add the molecule to the MoleculeList structure. 769 name = str(mol_node.getAttribute('name')) 770 if name == 'None': 771 name = None 772 type = str(mol_node.getAttribute('type')) 773 if type == 'None': 774 type = None 775 self.add_item(mol_name=name, mol_type=type) 776 777 # Get the residue nodes. 778 res_nodes = mol_node.getElementsByTagName('res') 779 780 # Recreate the residue data structures for the current molecule. 781 self[-1].res.from_xml(res_nodes, file_version=file_version)
782 783
784 - def to_xml(self, doc, element):
785 """Create XML elements for each molecule. 786 787 @param doc: The XML document object. 788 @type doc: xml.dom.minidom.Document instance 789 @param element: The element to add the molecule XML elements to. 790 @type element: XML element object 791 """ 792 793 # Loop over the molecules. 794 for i in range(len(self)): 795 # Create an XML element for this molecule and add it to the higher level element. 796 mol_element = doc.createElement('mol') 797 element.appendChild(mol_element) 798 799 # Set the molecule attributes. 800 mol_element.setAttribute('desc', 'Molecule container') 801 mol_element.setAttribute('name', str(self[i].name)) 802 mol_element.setAttribute('type', str(self[i].type)) 803 804 # Add all simple python objects within the MoleculeContainer to the XML element. 805 fill_object_contents(doc, mol_element, object=self[i], blacklist=['name', 'res', 'type'] + list(self[i].__class__.__dict__.keys())) 806 807 # Add the residue data. 808 self[i].res.to_xml(doc, mol_element)
809