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

Source Code for Module data_store.mol_res_spin

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2007-2014 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  from re import match 
 27   
 28  # relax module imports. 
 29  from data_store.prototype import Prototype 
 30  from lib.errors import RelaxError, RelaxFromXMLNotEmptyError, RelaxImplementError 
 31  from lib.xml import fill_object_contents, object_to_xml, xml_to_object 
 32  import specific_analyses 
 33   
 34   
 35  # The spin system data. 
 36  ####################### 
 37   
38 -class SpinContainer(Prototype):
39 """Class containing all the spin system specific data.""" 40
41 - def __init__(self, spin_name=None, spin_num=None, select=True):
42 """Set up the default objects of the spin system data container.""" 43 44 # The spin system name and number. 45 self.name = spin_name 46 self.num = spin_num 47 self.select = select 48 49 # The private metadata. 50 self._mol_name = None 51 self._mol_index = None 52 self._res_name = None 53 self._res_num = None 54 self._res_index = None 55 self._spin_index = None 56 self._spin_ids = []
57 58
59 - def __repr__(self):
60 """The string representation of the object. 61 62 Rather than using the standard Python conventions (either the string representation of the 63 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 64 """ 65 66 # Intro. 67 text = "Class containing all the spin system specific data.\n\n" 68 69 # Objects. 70 text = text + "\n" 71 text = text + "Objects:\n" 72 for name in dir(self): 73 # Skip the SpinContainer methods. 74 if name == 'is_empty': 75 continue 76 77 # Skip special objects. 78 if match("^_", name): 79 continue 80 81 # Add the object's attribute to the text string. 82 text = text + " " + name + ": " + repr(getattr(self, name)) + "\n" 83 84 return text
85 86
87 - def _back_compat_hook(self, file_version=None):
88 """Method for converting old spin data structures to the new ones. 89 90 @keyword file_version: The relax XML version of the XML file. 91 @type file_version: int 92 """ 93 94 # Model-free parameters. 95 if hasattr(self, 'equation') and self.equation in ['mf_orig', 'mf_ext', 'mf_ext2']: 96 self._back_compat_hook_mf_data() 97 98 # Relaxation data. 99 self._back_compat_hook_ri_data()
100 101
103 """Converting the old model-free parameter vector to the new one.""" 104 105 # Nothing to do. 106 if not hasattr(self, 'params'): 107 return 108 109 # Loop over the parameters, converting them to lowercase. 110 for i in range(len(self.params)): 111 self.params[i] = self.params[i].lower()
112 113
115 """Converting the old spin relaxation data structures to the new ones.""" 116 117 # Nothing to do. 118 if not (hasattr(self, 'frq_labels') and hasattr(self, 'noe_r1_table') and hasattr(self, 'remap_table')): 119 return 120 121 # Initialise the new structures. 122 self.ri_data = {} 123 self.ri_data_err = {} 124 sims = False 125 if hasattr(self, 'relax_sim_data'): 126 sims = True 127 self.ri_data_sim = {} 128 129 # Generate the new structures. 130 for i in range(self.num_ri): 131 # The ID. 132 ri_id = "%s_%s" % (self.ri_labels[i], self.frq_labels[self.remap_table[i]]) 133 134 # The relaxation data. 135 self.ri_data[ri_id] = self.relax_data[i] 136 self.ri_data_err[ri_id] = self.relax_error[i] 137 138 # Simulation data. 139 if sims: 140 self.ri_data_sim[ri_id] = [] 141 for j in range(cdp.sim_number): 142 self.ri_data_sim[ri_id].append(self.relax_sim_data[j][i]) 143 144 # Delete the old structures. 145 del self.frq 146 del self.frq_labels 147 del self.noe_r1_table 148 del self.num_frq 149 del self.num_ri 150 del self.ri_labels 151 del self.remap_table 152 del self.relax_data 153 del self.relax_error 154 if sims: 155 del self.relax_sim_data
156 157
158 - def is_empty(self):
159 """Method for testing if this SpinContainer object is empty. 160 161 @return: True if this container is empty and the spin number and name have not been set, 162 False otherwise. 163 @rtype: bool 164 """ 165 166 # The spin number or spin name has been set. 167 if self.num != None or self.name != None: 168 return False 169 170 # An object has been added to the container. 171 for name in dir(self): 172 # Skip the objects initialised in __init__(). 173 if name == 'num' or name == 'name' or name == 'select': 174 continue 175 176 # Skip the SpinContainer methods. 177 if name == 'is_empty': 178 continue 179 180 # Skip special objects. 181 if match("^_", name): 182 continue 183 184 # An object has been added. 185 return False 186 187 # The SpinContainer is unmodified. 188 return True
189 190
191 -class SpinList(list):
192 """List type data container for spin system specific data.""" 193
194 - def __init__(self):
195 """Set up the first spin system data container.""" 196 197 # Add the initial spin system container at index 0. 198 self.append(SpinContainer())
199 200
201 - def __repr__(self):
202 """The string representation of the object. 203 204 Rather than using the standard Python conventions (either the string representation of the 205 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 206 """ 207 208 # Intro. 209 text = "Spin systems.\n\n" 210 211 # Residue data. 212 text = text + "%-8s%-8s%-8s%-10s" % ("Index", "Number", "Name", "Selected") + "\n" 213 for i in range(len(self)): 214 text = text + "%-8i%-8s%-8s%-10s" % (i, repr(self[i].num), self[i].name, self[i].select) + "\n" 215 text = text + "\nThese can be accessed by typing 'D.mol[i].res[j].spin[k]', where D is the relax data storage object.\n" 216 217 return text
218 219
220 - def add_item(self, spin_name=None, spin_num=None, select=True):
221 """Appending an empty container to the list. 222 223 @keyword spin_name: The name of the new spin. 224 @type spin_name: str or None 225 @keyword spin_num: The number of the new spin. 226 @type spin_num: str or None 227 @keyword select: The selection flag. 228 @type select: bool 229 @return: The new container. 230 @rtype: SpinContainer instance 231 """ 232 233 # If no spin data exists, replace the empty first spin with this spin. 234 if self.is_empty(): 235 self[0].num = spin_num 236 self[0].name = spin_name 237 self[0].select = select 238 239 # Return the container. 240 return self[0] 241 242 # Otherwise append a new SpinContainer. 243 else: 244 # Test if the spin number (or name if unnumbered) already exists. 245 for i in range(len(self)): 246 # Spin number has been supplied. 247 if spin_num != None and spin_name != None: 248 if self[i].num == spin_num and self[i].name == spin_name: 249 raise RelaxError("The spin with name '%s' and number '%s' already exists." % (spin_name, spin_num)) 250 251 # No spin numbers. 252 if spin_num == None and self[i].name == spin_name: 253 raise RelaxError("The unnumbered spin name '%s' already exists." % spin_name) 254 255 # Append a new SpinContainer. 256 self.append(SpinContainer(spin_name, spin_num, select)) 257 258 # Return the container. 259 return self[-1]
260 261
262 - def is_empty(self):
263 """Method for testing if this SpinList object is empty. 264 265 @return: True if this list only has one SpinContainer and the spin number and name have 266 not been set, False otherwise. 267 @rtype: bool 268 """ 269 270 # There is only one SpinContainer and it is empty. 271 if len(self) == 1 and self[0].is_empty(): 272 return True 273 274 # Otherwise. 275 return False
276 277
278 - def from_xml(self, spin_nodes, file_version=None):
279 """Recreate a spin list data structure from the XML spin nodes. 280 281 @param spin_nodes: The spin XML nodes. 282 @type spin_nodes: xml.dom.minicompat.NodeList instance 283 @keyword file_version: The relax XML version of the XML file. 284 @type file_version: int 285 """ 286 287 # Test if empty. 288 if not self.is_empty(): 289 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 290 291 # Loop over the spins. 292 for spin_node in spin_nodes: 293 # Get the spin details and add the spin to the SpinList structure. 294 name = str(spin_node.getAttribute('name')) 295 if name == 'None': 296 name = None 297 num = eval(spin_node.getAttribute('num')) 298 self.add_item(spin_name=name, spin_num=num) 299 300 # Recreate the current spin container. 301 xml_to_object(spin_node, self[-1], file_version=file_version) 302 303 # Backwards compatibility transformations. 304 self[-1]._back_compat_hook(file_version)
305 306
307 - def to_xml(self, doc, element, pipe_type=None):
308 """Create XML elements for each spin. 309 310 @param doc: The XML document object. 311 @type doc: xml.dom.minidom.Document instance 312 @param element: The element to add the spin XML elements to. 313 @type element: XML element object 314 @keyword pipe_type: The type of the pipe being converted to XML. 315 @type pipe_type: str 316 """ 317 318 # The specific analysis API object. 319 api = specific_analyses.api.return_api(analysis_type=pipe_type) 320 321 # Loop over the spins. 322 for i in range(len(self)): 323 # Create an XML element for this spin and add it to the higher level element. 324 spin_element = doc.createElement('spin') 325 element.appendChild(spin_element) 326 327 # Set the spin attributes. 328 spin_element.setAttribute('desc', 'Spin container') 329 spin_element.setAttribute('name', str(self[i].name)) 330 spin_element.setAttribute('num', str(self[i].num)) 331 332 # Get the spin specific object names and loop over them to get their descriptions. 333 object_info = [] 334 try: 335 for name in api.data_names(error_names=True, sim_names=True): 336 # Get the description. 337 if hasattr(api, 'return_data_desc'): 338 desc = api.return_data_desc(name) 339 else: 340 desc = None 341 342 # Append the two. 343 object_info.append([name, desc]) 344 except RelaxImplementError: 345 pass 346 347 # Add the ordered objects. 348 blacklist = [] 349 for name, desc in object_info: 350 # Add the name to the blacklist. 351 blacklist.append(name) 352 353 # Skip the object if it is missing from the SpinContainer. 354 if not hasattr(self[i], name): 355 continue 356 357 # Create a new element for this object, and add it to the main element. 358 sub_element = doc.createElement(name) 359 spin_element.appendChild(sub_element) 360 361 # Add the object description. 362 if desc: 363 sub_element.setAttribute('desc', desc) 364 365 # Get the object. 366 object = getattr(self[i], name) 367 368 # Convert to XML. 369 object_to_xml(doc, sub_element, value=object) 370 371 # Add all simple python objects within the SpinContainer to the XML element. 372 fill_object_contents(doc, spin_element, object=self[i], blacklist=['name', 'num', 'spin'] + blacklist + list(self[i].__class__.__dict__.keys()))
373 374 375 376 # The residue data. 377 ################### 378
379 -class ResidueContainer(Prototype):
380 """Class containing all the residue specific data.""" 381
382 - def __init__(self, res_name=None, res_num=None):
383 """Set up the default objects of the residue data container.""" 384 385 # The residue name and number. 386 self.name = res_name 387 self.num = res_num 388 389 # The private metadata. 390 self._mol_name = None 391 self._mol_index = None 392 self._res_index = None 393 394 # The empty spin system list. 395 self.spin = SpinList()
396 397
398 - def __repr__(self):
399 """The string representation of the object. 400 401 Rather than using the standard Python conventions (either the string representation of the 402 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 403 """ 404 405 # Intro. 406 text = "Class containing all the residue specific data.\n" 407 408 # Objects. 409 text = text + "\n" 410 text = text + "Objects:\n" 411 for name in dir(self): 412 # Spin systems. 413 if name == 'spin': 414 text = text + " spin: The list of spin systems of the residues\n" 415 continue 416 417 # Skip the ResidueContainer methods. 418 if name == 'is_empty': 419 continue 420 421 # Skip special objects. 422 if match("^_", name): 423 continue 424 425 # Add the object's attribute to the text string. 426 text = text + " " + name + ": " + repr(getattr(self, name)) + "\n" 427 428 return text
429 430
431 - def is_empty(self):
432 """Method for testing if this ResidueContainer object is empty. 433 434 @return: True if this container is empty and the residue number and name have not been 435 set, False otherwise. 436 @rtype: bool 437 """ 438 439 # The residue number or residue name have been set. 440 if self.num != None or self.name != None: 441 return False 442 443 # An object has been added to the container. 444 for name in dir(self): 445 # Skip the objects initialised in __init__(). 446 if name == 'num' or name == 'name' or name == 'spin': 447 continue 448 449 # Skip the ResidueContainer methods. 450 if name == 'is_empty': 451 continue 452 453 # Skip special objects. 454 if match("^_", name): 455 continue 456 457 # An object has been added. 458 return False 459 460 # The spin list is not empty. 461 if not self.spin.is_empty(): 462 return False 463 464 # The ResidueContainer is unmodified. 465 return True
466 467
468 -class ResidueList(list):
469 """List type data container for residue specific data.""" 470
471 - def __init__(self):
472 """Set up the first residue data container.""" 473 474 # Add the initial residue container at index 0. 475 self.append(ResidueContainer())
476 477
478 - def __repr__(self):
479 """The string representation of the object. 480 481 Rather than using the standard Python conventions (either the string representation of the 482 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 483 """ 484 485 # Intro. 486 text = "Residues.\n\n" 487 488 # Residue data. 489 text = text + "%-8s%-8s%-8s" % ("Index", "Number", "Name") + "\n" 490 for i in range(len(self)): 491 text = text + "%-8i%-8s%-8s" % (i, repr(self[i].num), self[i].name) + "\n" 492 text = text + "\nThese can be accessed by typing 'D.mol[i].res[j]', where D is the relax data storage object.\n" 493 494 return text
495 496
497 - def add_item(self, res_name=None, res_num=None):
498 """Append an empty ResidueContainer to the ResidueList.""" 499 500 # If no residue data exists, replace the empty first residue with this residue. 501 if self.is_empty(): 502 self[0].num = res_num 503 self[0].name = res_name 504 505 # Otherwise append a new ResidueContainer. 506 else: 507 # Test if the residue number (or name if unnumbered) already exists. 508 for i in range(len(self)): 509 # Residue number has been supplied. 510 if res_num != None: 511 if self[i].num == res_num: 512 raise RelaxError("The residue number '" + repr(res_num) + "' already exists in the sequence.") 513 514 # No residue numbers. 515 else: 516 if self[i].name == res_name: 517 raise RelaxError("The unnumbered residue name '" + repr(res_name) + "' already exists.") 518 519 # Append a new ResidueContainer. 520 self.append(ResidueContainer(res_name, res_num))
521 522
523 - def is_empty(self):
524 """Method for testing if this ResidueList object is empty. 525 526 @return: True if this list only has one ResidueContainer and the residue number and name 527 have not been set, False otherwise. 528 @rtype: bool 529 """ 530 531 # There is only one ResidueContainer and it is empty. 532 if len(self) == 1 and self[0].is_empty(): 533 return True 534 535 # Otherwise. 536 return False
537 538
539 - def from_xml(self, res_nodes, file_version=None):
540 """Recreate a residue list data structure from the XML residue nodes. 541 542 @param res_nodes: The residue XML nodes. 543 @type res_nodes: xml.dom.minicompat.NodeList instance 544 @keyword file_version: The relax XML version of the XML file. 545 @type file_version: int 546 """ 547 548 # Test if empty. 549 if not self.is_empty(): 550 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 551 552 # Loop over the residues. 553 for res_node in res_nodes: 554 # Get the residue details and add the residue to the ResidueList structure. 555 name = str(res_node.getAttribute('name')) 556 if name == 'None': 557 name = None 558 num = eval(res_node.getAttribute('num')) 559 self.add_item(res_name=name, res_num=num) 560 561 # Get the spin nodes. 562 spin_nodes = res_node.getElementsByTagName('spin') 563 564 # Recreate the spin data structures for the current residue. 565 self[-1].spin.from_xml(spin_nodes, file_version=file_version)
566 567
568 - def to_xml(self, doc, element, pipe_type=None):
569 """Create XML elements for each residue. 570 571 @param doc: The XML document object. 572 @type doc: xml.dom.minidom.Document instance 573 @param element: The element to add the residue XML elements to. 574 @type element: XML element object 575 @keyword pipe_type: The type of the pipe being converted to XML. 576 @type pipe_type: str 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, pipe_type=pipe_type)
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, pipe_type=None):
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 @keyword pipe_type: The type of the pipe being converted to XML. 792 @type pipe_type: str 793 """ 794 795 # Loop over the molecules. 796 for i in range(len(self)): 797 # Create an XML element for this molecule and add it to the higher level element. 798 mol_element = doc.createElement('mol') 799 element.appendChild(mol_element) 800 801 # Set the molecule attributes. 802 mol_element.setAttribute('desc', 'Molecule container') 803 mol_element.setAttribute('name', str(self[i].name)) 804 mol_element.setAttribute('type', str(self[i].type)) 805 806 # Add all simple python objects within the MoleculeContainer to the XML element. 807 fill_object_contents(doc, mol_element, object=self[i], blacklist=['name', 'res', 'type'] + list(self[i].__class__.__dict__.keys())) 808 809 # Add the residue data. 810 self[i].res.to_xml(doc, mol_element, pipe_type=pipe_type)
811