Package generic_fns :: Package structure :: Module api_base
[hide private]
[frames] | no frames]

Source Code for Module generic_fns.structure.api_base

   1  ############################################################################### 
   2  #                                                                             # 
   3  # Copyright (C) 2008-2012 Edward d'Auvergne                                   # 
   4  #                                                                             # 
   5  # This file is part of the program relax.                                     # 
   6  #                                                                             # 
   7  # relax 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 2 of the License, or           # 
  10  # (at your option) any later version.                                         # 
  11  #                                                                             # 
  12  # relax 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 relax; if not, write to the Free Software                        # 
  19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   # 
  20  #                                                                             # 
  21  ############################################################################### 
  22   
  23  # Module docstring. 
  24  """The API for accessing, creating, and modifying structural information. 
  25   
  26  The full API is documented within this base class object.  Each available API call is given as a 
  27  prototype method stub (or functional method) with all arguments, raised errors, and return values 
  28  documented. 
  29  """ 
  30   
  31  # Python module imports. 
  32  from numpy import float64 
  33  from os import sep 
  34  from re import match 
  35  from types import MethodType 
  36  from warnings import warn 
  37   
  38  # relax module import. 
  39  from data.relax_xml import fill_object_contents, object_to_xml, xml_to_object 
  40  from float import floatAsByteArray, packBytesAsPyFloat 
  41  from generic_fns.structure.superimpose import kabsch 
  42  from relax_errors import RelaxError, RelaxFileError, RelaxFromXMLNotEmptyError, RelaxImplementError 
  43  from relax_io import file_root 
  44  from relax_warnings import RelaxWarning 
  45   
  46   
47 -class Base_struct_API:
48 """The structural object base class. 49 50 All API methods are prototyped here as stub methods. 51 """ 52 53 # Identification string. 54 id = 'API' 55 56
57 - def __init__(self):
58 """Initialise the structural object.""" 59 60 # Initialise the empty model list. 61 self.structural_data = ModelList()
62 63
64 - def add_atom(self, mol_name=None, atom_name=None, res_name=None, res_num=None, pos=[None, None, None], element=None, atom_num=None, chain_id=None, segment_id=None, pdb_record=None):
65 """Add a new atom to the structural data object. 66 67 @keyword mol_name: The name of the molecule. 68 @type mol_name: str 69 @keyword atom_name: The atom name, e.g. 'H1'. 70 @type atom_name: str or None 71 @keyword res_name: The residue name. 72 @type res_name: str or None 73 @keyword res_num: The residue number. 74 @type res_num: int or None 75 @keyword pos: The position vector of coordinates. 76 @type pos: list (length = 3) 77 @keyword element: The element symbol. 78 @type element: str or None 79 @keyword atom_num: The atom number. 80 @type atom_num: int or None 81 @keyword chain_id: The chain identifier. 82 @type chain_id: str or None 83 @keyword segment_id: The segment identifier. 84 @type segment_id: str or None 85 @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'. 86 @type pdb_record: str or None 87 """ 88 89 # Raise the error. 90 raise RelaxImplementError
91 92
93 - def add_model(self, model=None, coords_from=None):
94 """Add a new model to the store. 95 96 The new model will be constructured with the structural information from the other models currently present. The coords_from argument allows the atomic positions to be taken from a certain model. If this argument is not set, then the atomic positions from the first model will be used. 97 98 @keyword model: The number of the model to create. 99 @type model: int or None 100 @keyword coords_from: The model number to take the coordinates from. 101 @type coords_from: int or None 102 @return: The model container. 103 @rtype: ModelContainer instance 104 """ 105 106 # Raise the error. 107 raise RelaxImplementError
108 109
110 - def add_molecule(self, name=None):
111 """Prototype method stub for adding the given molecule to the store. 112 113 @keyword name: The molecule identification string. 114 @type name: str 115 """ 116 117 # Raise the error. 118 raise RelaxImplementError
119 120
121 - def atom_loop(self, atom_id=None, str_id=None, model_num=None, model_num_flag=False, mol_name_flag=False, res_num_flag=False, res_name_flag=False, atom_num_flag=False, atom_name_flag=False, element_flag=False, pos_flag=False, ave=False):
122 """Prototype generator method stub for looping over all atoms in the structural data object. 123 124 This method should be designed as a generator (http://www.python.org/dev/peps/pep-0255/). 125 It should loop over all atoms of the system yielding the following atomic information, if 126 the corresponding flag is True, in tuple form: 127 128 1. Model number. 129 2. Molecule name. 130 3. Residue number. 131 4. Residue name. 132 5. Atom number. 133 6. Atom name. 134 7. The element name (its atomic symbol and optionally the isotope, e.g. 'N', 'Mg', 135 '17O', '13C', etc). 136 8. The position of the atom in Euclidean space. 137 138 139 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be yielded. 140 @type atom_id: str 141 @keyword str_id: The structure identifier. This can be the file name, model number, or structure number. If None, then all structures will be looped over. 142 @type str_id: str, int, or None 143 @keyword model_num: Only loop over a specific model. 144 @type model_num: int or None 145 @keyword model_num_flag: A flag which if True will cause the model number to be yielded. 146 @type model_num_flag: bool 147 @keyword mol_name_flag: A flag which if True will cause the molecule name to be yielded. 148 @type mol_name_flag: bool 149 @keyword res_num_flag: A flag which if True will cause the residue number to be yielded. 150 @type res_num_flag: bool 151 @keyword res_name_flag: A flag which if True will cause the residue name to be yielded. 152 @type res_name_flag: bool 153 @keyword atom_num_flag: A flag which if True will cause the atom number to be yielded. 154 @type atom_num_flag: bool 155 @keyword atom_name_flag: A flag which if True will cause the atom name to be yielded. 156 @type atom_name_flag: bool 157 @keyword element_flag: A flag which if True will cause the element name to be yielded. 158 @type element_flag: bool 159 @keyword pos_flag: A flag which if True will cause the atomic position to be yielded. 160 @type pos_flag: bool 161 @keyword ave: A flag which if True will result in this method returning the average atom properties across all loaded structures. 162 @type ave: bool 163 @return: A tuple of atomic information, as described in the docstring. 164 @rtype: tuple consisting of optional molecule name (str), residue number (int), residue name (str), atom number (int), atom name(str), element name (str), and atomic position (array of len 3). 165 """ 166 167 # Raise the error. 168 raise RelaxImplementError
169 170
171 - def attached_atom(self, atom_id=None, attached_atom=None, model=None):
172 """Prototype method stub for finding the atom 'attached_atom' bonded to the atom 'atom_id'. 173 174 @keyword atom_id: The molecule, residue, and atom identifier string. This must 175 correspond to a single atom in the system. 176 @type atom_id: str 177 @keyword attached_atom: The name of the attached atom to return. 178 @type attached_atom: str 179 @keyword model: The model to return the positional information from. If not 180 supplied and multiple models exist, then the returned atomic 181 position will be a list of the positions in each model. 182 @type model: None or int 183 @return: A tuple of information about the bonded atom. 184 @rtype: tuple consisting of the atom number (int), atom name (str), element 185 name (str), and atomic positions for each model (list of numpy 186 arrays) 187 """ 188 189 # Raise the error. 190 raise RelaxImplementError
191 192
193 - def bond_vectors(self, attached_atom=None, model_num=None, mol_name=None, res_num=None, res_name=None, spin_num=None, spin_name=None, return_name=False, return_warnings=False):
194 """Prototype method stub for finding the bond vectors between 'attached_atom' and 'atom_id'. 195 196 @keyword attached_atom: The name of the bonded atom. 197 @type attached_atom: str 198 @keyword model_num: The model of which to return the vectors from. If not supplied 199 and multiple models exist, then vectors from all models will be 200 returned. 201 @type model_num: None or int 202 @keyword mol_name: The name of the molecule that attached_atom belongs to. 203 @type mol_name: str 204 @keyword res_num: The number of the residue that attached_atom belongs to. 205 @type res_num: str 206 @keyword res_name: The name of the residue that attached_atom belongs to. 207 @type res_name: str 208 @keyword spin_num: The number of the spin that attached_atom is attached to. 209 @type spin_num: str 210 @keyword spin_name: The name of the spin that attached_atom is attached to. 211 @type spin_name: str 212 @keyword return_name: A flag which if True will cause the name of the attached atom to 213 be returned together with the bond vectors. 214 @type return_name: bool 215 @keyword return_warnings: A flag which if True will cause warning messages to be returned. 216 @type return_warnings: bool 217 @return: The list of bond vectors for each model. 218 @rtype: list of numpy arrays 219 """ 220 221 # Raise the error. 222 raise RelaxImplementError
223 224
225 - def connect_atom(self, mol_name=None, index1=None, index2=None):
226 """Connect two atoms in the structural data object. 227 228 @keyword mol_name: The name of the molecule. 229 @type mol_name: str 230 @keyword index1: The global index of the first atom. 231 @type index1: str 232 @keyword index2: The global index of the first atom. 233 @type index2: str 234 """ 235 236 # Raise the error. 237 raise RelaxImplementError
238 239
240 - def delete(self):
241 """Prototype method stub for deleting all structural data from the current data pipe.""" 242 243 # Raise the error. 244 raise RelaxImplementError
245 246
247 - def empty(self):
248 """Report if the structural data structure is empty or not. 249 250 @return: True if empty, False otherwise. 251 @rtype: bool 252 """ 253 254 # Check the ModelList structure. 255 if len(self.structural_data) == 0: 256 return True 257 else: 258 return False
259 260
261 - def from_xml(self, str_node, dir=None, id=None, file_version=1):
262 """Recreate the structural object from the XML structural object node. 263 264 @param str_node: The structural object XML node. 265 @type str_node: xml.dom.minicompat.Element instance 266 @keyword dir: The name of the directory containing the results file. 267 @type dir: str 268 @keyword id: The specific structural object ID string. This can be 'scientific', 'internal', etc. 269 @type id: str 270 @keyword file_version: The relax XML version of the XML file. 271 @type file_version: int 272 """ 273 274 # Recreate the model / molecule data structure. 275 model_nodes = str_node.getElementsByTagName('model') 276 self.structural_data.from_xml(model_nodes, id=id, file_version=file_version) 277 278 # The displacement structure. 279 disp_nodes = str_node.getElementsByTagName('displacements') 280 if len(disp_nodes): 281 # Initialise the object. 282 self.displacements = Displacements() 283 284 # Recreate the molecule data structures for the current model. 285 self.displacements.from_xml(disp_nodes[0], file_version=file_version)
286 287
288 - def get_model(self, model):
289 """Return or create the model. 290 291 @param model: The model number. 292 @type model: int or None 293 @return: The ModelContainer corresponding to the model number or that newly created. 294 @rtype: ModelContainer instance 295 """ 296 297 # Check if the target is a single model. 298 if model == None and self.num_models() > 1: 299 raise RelaxError("The target model cannot be determined as there are %s models already present." % self.num_modes()) 300 301 # No model specified. 302 if model == None: 303 # Create the first model, if necessary. 304 if self.num_models(): 305 self.structural_data.add_item() 306 307 # Alias the first model. 308 model_cont = self.structural_data[0] 309 310 # The model has been specified. 311 else: 312 # Get the preexisting model. 313 found = False 314 for model_cont in self.structural_data: 315 if model_cont.num == model: 316 found = True 317 break 318 319 # Add the model if it doesn't exist. 320 if not found: 321 self.structural_data.add_item(model) 322 model_cont = self.structural_data[-1]
323 324
325 - def get_molecule(self, molecule, model=None):
326 """Return the molecule. 327 328 @param molecule: The molecule name. 329 @type molecule: int or None 330 @keyword model: The model number. 331 @type model: int or None 332 @return: The MolContainer corresponding to the molecule name and model number. 333 @rtype: MolContainer instance or None 334 """ 335 336 # Raise the error. 337 raise RelaxImplementError
338 339
340 - def load_pdb(self, file_path, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, verbosity=False):
341 """Prototype method stub for loading structures from a PDB file. 342 343 This inherited prototype method is a stub which, if the functionality is desired, should be 344 overwritten by the derived class. 345 346 347 @param file_path: The full path of the PDB file. 348 @type file_path: str 349 @keyword read_mol: The molecule(s) to read from the file, independent of model. The 350 molecules are determined differently by the different parsers, but 351 are numbered consecutively from 1. If set to None, then all 352 molecules will be loaded. 353 @type read_mol: None, int, or list of int 354 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, 355 then the molecules will be automatically labelled based on the file 356 name or other information. 357 @type set_mol_name: None, str, or list of str 358 @keyword read_model: The PDB model to extract from the file. If set to None, then all 359 models will be loaded. 360 @type read_model: None, int, or list of int 361 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then 362 the PDB model numbers will be preserved, if they exist. 363 @type set_model_num: None, int, or list of int 364 @keyword verbosity: A flag which if True will cause messages to be printed. 365 @type verbosity: bool 366 @return: The status of the loading of the PDB file. 367 @rtype: bool 368 """ 369 370 # Raise the error. 371 raise RelaxImplementError
372 373
374 - def load_xyz(self, file_path, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, verbosity=False):
375 """Method for loading structures from a XYZ file. 376 377 @param file_path: The full path of the XYZ file. 378 @type file_path: str 379 @keyword read_mol: The molecule(s) to read from the file, independent of model. The 380 molecules are determined differently by the different parsers, but 381 are numbered consecutively from 1. If set to None, then all 382 molecules will be loaded. 383 @type read_mol: None, int, or list of int 384 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, 385 then the molecules will be automatically labelled based on the file 386 name or other information. 387 @type set_mol_name: None, str, or list of str 388 @keyword read_model: The XYZ model to extract from the file. If set to None, then all 389 models will be loaded. 390 @type read_model: None, int, or list of int 391 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then 392 the XYZ model numbers will be preserved, if they exist. 393 @type set_model_num: None, int, or list of int 394 @keyword verbosity: A flag which if True will cause messages to be printed. 395 @type verbosity: bool 396 @return: The status of the loading of the XYZ file. 397 @rtype: bool 398 """ 399 400 # Raise the error. 401 raise RelaxImplementError
402 403
404 - def model_loop(self, model=None):
405 """Generator method for looping over the models in numerical order. 406 407 @keyword model: Limit the loop to a single number. 408 @type model: int 409 @return: The model structural object. 410 @rtype: ModelContainer container 411 """ 412 413 # A single model. 414 if model: 415 for i in range(len(self.structural_data)): 416 if self.structural_data[i].num == model: 417 yield self.structural_data[i] 418 419 # All models. 420 else: 421 # The models. 422 model_nums = [] 423 for i in range(len(self.structural_data)): 424 if self.structural_data[i].num != None: 425 model_nums.append(self.structural_data[i].num) 426 427 # Sort. 428 if model_nums: 429 model_nums.sort() 430 431 # Loop over the models in order. 432 for model_num in model_nums: 433 # Find the model. 434 for i in range(len(self.structural_data)): 435 # Yield the model. 436 if self.structural_data[i].num == model_num: 437 yield self.structural_data[i] 438 439 # No models, so just yield the single container. 440 if not model_nums: 441 yield self.structural_data[0]
442 443
444 - def num_models(self):
445 """Method for returning the number of models. 446 447 @return: The number of models in the structural object. 448 @rtype: int 449 """ 450 451 return len(self.structural_data)
452 453
454 - def num_molecules(self):
455 """Method for returning the number of molecules. 456 457 @return: The number of molecules in the structural object. 458 @rtype: int 459 """ 460 461 # Validate the structural object. 462 self.validate() 463 464 # Return the number. 465 return len(self.structural_data[0].mol)
466 467
468 - def pack_structs(self, data_matrix, orig_model_num=None, set_model_num=None, orig_mol_num=None, set_mol_name=None, file_name=None, file_path=None):
469 """From the given structural data, expand the structural data data structure. 470 471 @param data_matrix: A matrix of structural objects. 472 @type data_matrix: list of lists of structural objects 473 @keyword orig_model_num: The original model numbers (for storage). 474 @type orig_model_num: list of int 475 @keyword set_model_num: The new model numbers (for model renumbering). 476 @type set_model_num: list of int 477 @keyword orig_mol_num: The original molecule numbers (for storage). 478 @type orig_mol_num: list of int 479 @keyword set_mol_name: The new molecule names. 480 @type set_mol_name: list of str 481 @keyword file_name: The name of the file from which the molecular data has been 482 extracted. 483 @type file_name: None or str 484 @keyword file_path: The full path to the file specified by 'file_name'. 485 @type file_path: None or str 486 """ 487 488 # Test the number of models. 489 if len(orig_model_num) != len(data_matrix): 490 raise RelaxError("Structural data mismatch, %s original models verses %s in the structural data." % (len(orig_model_num), len(data_matrix))) 491 492 # Test the number of molecules. 493 if len(orig_mol_num) != len(data_matrix[0]): 494 raise RelaxError("Structural data mismatch, %s original molecules verses %s in the structural data." % (len(orig_mol_num), len(data_matrix[0]))) 495 496 # Model numbers do not change. 497 if not set_model_num: 498 set_model_num = orig_model_num 499 500 # Test the model mapping. 501 if len(set_model_num) != len(data_matrix): 502 raise RelaxError("Failure of the mapping of new model numbers, %s new model numbers verses %s models in the structural data." % (len(set_model_num), len(data_matrix))) 503 504 # Test the molecule mapping. 505 if len(set_mol_name) != len(data_matrix[0]): 506 raise RelaxError("Failure of the mapping of new molecule names, %s new molecule names verses %s molecules in the structural data." % (len(set_mol_name), len(data_matrix[0]))) 507 508 # Test that the target models and structures are absent, and get the already existing model numbers. 509 current_models = [] 510 for i in range(len(self.structural_data)): 511 # Create a list of current models. 512 current_models.append(self.structural_data[i].num) 513 514 # Loop over the structures. 515 for j in range(len(self.structural_data[i].mol)): 516 if self.structural_data[i].num in set_model_num and self.structural_data[i].mol[j].mol_name in set_mol_name: 517 raise RelaxError("The molecule '%s' of model %s already exists." % (self.structural_data[i].mol[j].mol_name, self.structural_data[i].num)) 518 519 # Loop over the models. 520 for i in range(len(set_model_num)): 521 # The model doesn't currently exist. 522 if set_model_num[i] not in current_models: 523 # Create the model. 524 self.structural_data.add_item(set_model_num[i]) 525 526 # Add the model number to the current_models list. 527 current_models.append(set_model_num[i]) 528 529 # Get the model. 530 model = self.structural_data[-1] 531 532 # Otherwise get the pre-existing model. 533 else: 534 model = self.structural_data[current_models.index(set_model_num[i])] 535 536 # Loop over the molecules. 537 for j in range(len(set_mol_name)): 538 # Print out. 539 print(("Adding molecule '%s' to model %s (from the original molecule number %s of model %s)" % (set_mol_name[j], set_model_num[i], orig_mol_num[j], orig_model_num[i]))) 540 541 # Consistency check. 542 index = len(model.mol) 543 if model.num != self.structural_data[0].num and self.structural_data[0].mol[index].mol_name != set_mol_name[j]: 544 raise RelaxError("The new molecule name of '%s' in model %s does not match the corresponding molecule's name of '%s' in model %s." % (set_mol_name[j], set_model_num[i], self.structural_data[0].mol[index].mol_name, self.structural_data[0].num)) 545 546 # Pack the structures. 547 model.mol.add_item(mol_name=set_mol_name[j], mol_cont=data_matrix[i][j]) 548 549 # Set the molecule name and store the structure file info. 550 model.mol[-1].mol_name = set_mol_name[j] 551 model.mol[-1].file_name = file_name 552 model.mol[-1].file_path = file_path 553 model.mol[-1].file_mol_num = orig_mol_num[j] 554 model.mol[-1].file_model = orig_model_num[i]
555 556
557 - def rotate(self, R=None, origin=None, model=None, atom_id=None):
558 """Method stub for rotating a structure. 559 560 @keyword R: The forwards rotation matrix. 561 @type R: numpy 3D, rank-2 array 562 @keyword origin: The origin of the rotation. 563 @type origin: numpy 3D, rank-1 array 564 @keyword model: The model to rotate. If None, all models will be rotated. 565 @type model: int 566 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used. 567 @type atom_id: str or None 568 """ 569 570 # Raise the error. 571 raise RelaxImplementError
572 573
574 - def target_mol_name(self, set=None, target=None, index=None, mol_num=None, file=None):
575 """Add the new molecule name to the target data structure. 576 577 @keyword set: The list of new molecule names. If not supplied, the names will be 578 generated from the file name. 579 @type set: None or list of str 580 @keyword target: The target molecule name data structure to which the new name will be 581 appended. 582 @type target: list 583 @keyword index: The molecule index, matching the set argument. 584 @type index: int 585 @keyword mol_num: The molecule number. 586 @type mol_num: int 587 @keyword file: The name of the file, excluding all directories. 588 @type file: str 589 """ 590 591 # Set the target molecule name. 592 if set: 593 target.append(set[index]) 594 else: 595 # Set the name to the file name plus the structure number. 596 target.append(file_root(file) + '_mol' + repr(mol_num))
597 598
599 - def translate(self, T=None, model=None, atom_id=None):
600 """Method stub for displacing the structural information by the given translation vector. 601 602 @keyword T: The translation vector. 603 @type T: numpy 3D, rank-1 array 604 @keyword model: The model to rotate. If None, all models will be rotated. 605 @type model: int 606 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used. 607 @type atom_id: str or None 608 """ 609 610 # Raise the error. 611 raise RelaxImplementError
612 613
614 - def to_xml(self, doc, element):
615 """Prototype method for converting the structural object to an XML representation. 616 617 @param doc: The XML document object. 618 @type doc: xml.dom.minidom.Document instance 619 @param element: The element to add the alignment tensors XML element to. 620 @type element: XML element object 621 """ 622 623 # Create the structural element and add it to the higher level element. 624 str_element = doc.createElement('structure') 625 element.appendChild(str_element) 626 627 # Set the structural attributes. 628 str_element.setAttribute('desc', 'Structural information') 629 str_element.setAttribute('id', self.id) 630 631 # No contents to store, so pack up the structural containers. 632 if not self.structural_data.is_empty(): 633 self.structural_data.to_xml(doc, str_element) 634 635 # The displacement structure. 636 if hasattr(self, 'displacements'): 637 # Create an XML element. 638 disp_element = doc.createElement('displacements') 639 str_element.appendChild(disp_element) 640 641 # Set the attributes. 642 disp_element.setAttribute('desc', 'The rotational and translational displacements between models') 643 644 # Add the displacement data. 645 self.displacements.to_xml(doc, disp_element)
646 647
648 - def validate_models(self):
649 """Check that the models are consistent with each other. 650 651 This checks that the primary structure is identical between the models. 652 """ 653 654 # Raise the error. 655 raise RelaxImplementError
656 657
658 - def write_pdb(self, file, model_num=None):
659 """Prototype method stub for the creation of a PDB file from the structural data. 660 661 The PDB records 662 =============== 663 664 The following information about the PDB records has been taken from the "Protein Data Bank 665 Contents Guide: Atomic Coordinate Entry Format Description" version 3.1, February 11, 2008. 666 667 HET record 668 ---------- 669 670 The HET record describes non-standard residues. The format of the record is:: 671 __________________________________________________________________________________________ 672 | | | | | 673 | Columns | Data type | Field | Definition | 674 |_________|______________|______________|________________________________________________| 675 | | | | | 676 | 1 - 6 | Record name | "HET " | | 677 | 8 - 10 | LString(3) | hetID | Het identifier, right-justified. | 678 | 13 | Character | ChainID | Chain identifier. | 679 | 14 - 17 | Integer | seqNum | Sequence number. | 680 | 18 | AChar | iCode | Insertion code. | 681 | 21 - 25 | Integer | numHetAtoms | Number of HETATM records for the group present | 682 | | | | in the entry. | 683 | 31 - 70 | String | text | Text describing Het group. | 684 |_________|______________|______________|________________________________________________| 685 686 687 HETNAM record 688 ------------- 689 690 The HETNAM associates a chemical name with the hetID from the HET record. The format is of 691 the record is:: 692 __________________________________________________________________________________________ 693 | | | | | 694 | Columns | Data type | Field | Definition | 695 |_________|______________|______________|________________________________________________| 696 | | | | | 697 | 1 - 6 | Record name | "HETNAM" | | 698 | 9 - 10 | Continuation | continuation | Allows concatenation of multiple records. | 699 | 12 - 14 | LString(3) | hetID | Het identifier, right-justified. | 700 | 16 - 70 | String | text | Chemical name. | 701 |_________|______________|______________|________________________________________________| 702 703 704 FORMUL record 705 ------------- 706 707 The chemical formula for non-standard groups. The format of the record is:: 708 __________________________________________________________________________________________ 709 | | | | | 710 | Columns | Data type | Field | Definition | 711 |_________|______________|______________|________________________________________________| 712 | | | | | 713 | 1 - 6 | Record name | "FORMUL" | | 714 | 9 - 10 | Integer | compNum | Component number. | 715 | 13 - 15 | LString(3) | hetID | Het identifier. | 716 | 17 - 18 | Integer | continuation | Continuation number. | 717 | 19 | Character | asterisk | "*" for water. | 718 | 20 - 70 | String | text | Chemical formula. | 719 |_________|______________|______________|________________________________________________| 720 721 722 MODEL record 723 ------------ 724 725 The model number, for multiple structures. The format of the record is:: 726 __________________________________________________________________________________________ 727 | | | | | 728 | Columns | Data type | Field | Definition | 729 |_________|______________|______________|________________________________________________| 730 | | | | | 731 | 1 - 6 | Record name | "MODEL " | | 732 | 11 - 14 | Integer | serial | Model serial number. | 733 |_________|______________|______________|________________________________________________| 734 735 736 ATOM record 737 ----------- 738 739 The ATOM record contains the atomic coordinates for atoms belonging to standard residues. 740 The format of the record is:: 741 __________________________________________________________________________________________ 742 | | | | | 743 | Columns | Data type | Field | Definition | 744 |_________|______________|______________|________________________________________________| 745 | | | | | 746 | 1 - 6 | Record name | "ATOM" | | 747 | 7 - 11 | Integer | serial | Atom serial number. | 748 | 13 - 16 | Atom | name | Atom name. | 749 | 17 | Character | altLoc | Alternate location indicator. | 750 | 18 - 20 | Residue name | resName | Residue name. | 751 | 22 | Character | chainID | Chain identifier. | 752 | 23 - 26 | Integer | resSeq | Residue sequence number. | 753 | 27 | AChar | iCode | Code for insertion of residues. | 754 | 31 - 38 | Real(8.3) | x | Orthogonal coordinates for X in Angstroms. | 755 | 39 - 46 | Real(8.3) | y | Orthogonal coordinates for Y in Angstroms. | 756 | 47 - 54 | Real(8.3) | z | Orthogonal coordinates for Z in Angstroms. | 757 | 55 - 60 | Real(6.2) | occupancy | Occupancy. | 758 | 61 - 66 | Real(6.2) | tempFactor | Temperature factor. | 759 | 73 - 76 | LString(4) | segID | Segment identifier, left-justified. | 760 | 77 - 78 | LString(2) | element | Element symbol, right-justified. | 761 | 79 - 80 | LString(2) | charge | Charge on the atom. | 762 |_________|______________|______________|________________________________________________| 763 764 765 HETATM record 766 ------------- 767 768 The HETATM record contains the atomic coordinates for atoms belonging to non-standard 769 groups. The format of the record is:: 770 __________________________________________________________________________________________ 771 | | | | | 772 | Columns | Data type | Field | Definition | 773 |_________|______________|______________|________________________________________________| 774 | | | | | 775 | 1 - 6 | Record name | "HETATM" | | 776 | 7 - 11 | Integer | serial | Atom serial number. | 777 | 13 - 16 | Atom | name | Atom name. | 778 | 17 | Character | altLoc | Alternate location indicator. | 779 | 18 - 20 | Residue name | resName | Residue name. | 780 | 22 | Character | chainID | Chain identifier. | 781 | 23 - 26 | Integer | resSeq | Residue sequence number. | 782 | 27 | AChar | iCode | Code for insertion of residues. | 783 | 31 - 38 | Real(8.3) | x | Orthogonal coordinates for X. | 784 | 39 - 46 | Real(8.3) | y | Orthogonal coordinates for Y. | 785 | 47 - 54 | Real(8.3) | z | Orthogonal coordinates for Z. | 786 | 55 - 60 | Real(6.2) | occupancy | Occupancy. | 787 | 61 - 66 | Real(6.2) | tempFactor | Temperature factor. | 788 | 73 - 76 | LString(4) | segID | Segment identifier; left-justified. | 789 | 77 - 78 | LString(2) | element | Element symbol; right-justified. | 790 | 79 - 80 | LString(2) | charge | Charge on the atom. | 791 |_________|______________|______________|________________________________________________| 792 793 794 TER record 795 ---------- 796 797 The end of the ATOM and HETATM records for a chain. According to the draft atomic 798 coordinate entry format description: 799 800 I{"The TER record has the same residue name, chain identifier, sequence number and insertion 801 code as the terminal residue. The serial number of the TER record is one number greater than 802 the serial number of the ATOM/HETATM preceding the TER."} 803 804 The format of the record is:: 805 __________________________________________________________________________________________ 806 | | | | | 807 | Columns | Data type | Field | Definition | 808 |_________|______________|______________|________________________________________________| 809 | | | | | 810 | 1 - 6 | Record name | "TER " | | 811 | 7 - 11 | Integer | serial | Serial number. | 812 | 18 - 20 | Residue name | resName | Residue name. | 813 | 22 | Character | chainID | Chain identifier. | 814 | 23 - 26 | Integer | resSeq | Residue sequence number. | 815 | 27 | AChar | iCode | Insertion code. | 816 |_________|______________|______________|________________________________________________| 817 818 819 CONECT record 820 ------------- 821 822 The connectivity between atoms. This is required for all HET groups and for non-standard 823 bonds. The format of the record is:: 824 __________________________________________________________________________________________ 825 | | | | | 826 | Columns | Data type | Field | Definition | 827 |_________|______________|______________|________________________________________________| 828 | | | | | 829 | 1 - 6 | Record name | "CONECT" | | 830 | 7 - 11 | Integer | serial | Atom serial number | 831 | 12 - 16 | Integer | serial | Serial number of bonded atom | 832 | 17 - 21 | Integer | serial | Serial number of bonded atom | 833 | 22 - 26 | Integer | serial | Serial number of bonded atom | 834 | 27 - 31 | Integer | serial | Serial number of bonded atom | 835 | 32 - 36 | Integer | serial | Serial number of hydrogen bonded atom | 836 | 37 - 41 | Integer | serial | Serial number of hydrogen bonded atom | 837 | 42 - 46 | Integer | serial | Serial number of salt bridged atom | 838 | 47 - 51 | Integer | serial | Serial number of hydrogen bonded atom | 839 | 52 - 56 | Integer | serial | Serial number of hydrogen bonded atom | 840 | 57 - 61 | Integer | serial | Serial number of salt bridged atom | 841 |_________|______________|______________|________________________________________________| 842 843 844 ENDMDL record 845 ------------- 846 847 The end of model record, for multiple structures. The format of the record is:: 848 __________________________________________________________________________________________ 849 | | | | | 850 | Columns | Data type | Field | Definition | 851 |_________|______________|______________|________________________________________________| 852 | | | | | 853 | 1 - 6 | Record name | "ENDMDL" | | 854 |_________|______________|______________|________________________________________________| 855 856 857 858 MASTER record 859 ------------- 860 861 The control record for bookkeeping. The format of the record is:: 862 __________________________________________________________________________________________ 863 | | | | | 864 | Columns | Data type | Field | Definition | 865 |_________|______________|______________|________________________________________________| 866 | | | | | 867 | 1 - 6 | Record name | "MASTER" | | 868 | 11 - 15 | Integer | numRemark | Number of REMARK records | 869 | 16 - 20 | Integer | "0" | | 870 | 21 - 25 | Integer | numHet | Number of HET records | 871 | 26 - 30 | Integer | numHelix | Number of HELIX records | 872 | 31 - 35 | Integer | numSheet | Number of SHEET records | 873 | 36 - 40 | Integer | numTurn | Number of TURN records | 874 | 41 - 45 | Integer | numSite | Number of SITE records | 875 | 46 - 50 | Integer | numXform | Number of coordinate transformation records | 876 | | | | (ORIGX+SCALE+MTRIX) | 877 | 51 - 55 | Integer | numCoord | Number of atomic coordinate records | 878 | | | | (ATOM+HETATM) | 879 | 56 - 60 | Integer | numTer | Number of TER records | 880 | 61 - 65 | Integer | numConect | Number of CONECT records | 881 | 66 - 70 | Integer | numSeq | Number of SEQRES records | 882 |_________|______________|______________|________________________________________________| 883 884 885 END record 886 ---------- 887 888 The end of the PDB file. The format of the record is:: 889 __________________________________________________________________________________________ 890 | | | | | 891 | Columns | Data type | Field | Definition | 892 |_________|______________|______________|________________________________________________| 893 | | | | | 894 | 1 - 6 | Record name | "END " | | 895 |_________|______________|______________|________________________________________________| 896 897 898 @param file: The PDB file object. This object must be writable. 899 @type file: file object 900 @keyword model_num: The model to place into the PDB file. If not supplied, then all 901 models will be placed into the file. 902 @type model_num: None or int 903 """ 904 905 # Raise the error. 906 raise RelaxImplementError
907 908
909 - def validate(self):
910 """Check the integrity of the structural data. 911 912 The number of molecules must be the same in all models. 913 """ 914 915 # Reference number of molecules. 916 num_mols = len(self.structural_data[0].mol) 917 918 # Loop over all other models. 919 for i in range(1, len(self.structural_data)): 920 # Model alias. 921 model_i = self.structural_data[i] 922 923 # Size check. 924 if num_mols != len(model_i.mol): 925 raise RelaxError("The structural object is not valid - the number of molecules is not the same for all models.") 926 927 # Molecule name check. 928 for j in range(len(model_i.mol)): 929 if model_i.mol[j].mol_name != self.structural_data[0].mol[j].mol_name: 930 raise RelaxError("The molecule name '%s' of model %s does not match the corresponding molecule '%s' of model %s." % (model_i.mol[j].mol_name, model_i.num, self.structural_data[0].mol[j].mol_name, self.structural_data[0].num))
931 932 933
934 -class Displacements:
935 """A special object for representing rotational and translational displacements between models.""" 936
937 - def __init__(self):
938 """Initialise the storage objects.""" 939 940 # The displacement structures. 941 self._translation_vector = {} 942 self._translation_distance = {} 943 self._rotation_matrix = {} 944 self._rotation_axis = {} 945 self._rotation_angle = {}
946 947
948 - def _calculate(self, model_from=None, model_to=None, coord_from=None, coord_to=None, centroid=None):
949 """Calculate the rotational and translational displacements using the given coordinate sets. 950 951 This uses the Kabsch algorithm (http://en.wikipedia.org/wiki/Kabsch_algorithm). 952 953 954 @keyword model_from: The model number of the starting structure. 955 @type model_from: int 956 @keyword model_to: The model number of the ending structure. 957 @type model_to: int 958 @keyword coord_from: The list of atomic coordinates for the starting structure. 959 @type coord_from: numpy rank-2, Nx3 array 960 @keyword coord_to: The list of atomic coordinates for the ending structure. 961 @type coord_to: numpy rank-2, Nx3 array 962 @keyword centroid: An alternative position of the centroid, used for studying pivoted systems. 963 @type centroid: list of float or numpy rank-1, 3D array 964 """ 965 966 # Initialise structures if necessary. 967 if not self._translation_vector.has_key(model_from): 968 self._translation_vector[model_from] = {} 969 if not self._translation_distance.has_key(model_from): 970 self._translation_distance[model_from] = {} 971 if not self._rotation_matrix.has_key(model_from): 972 self._rotation_matrix[model_from] = {} 973 if not self._rotation_axis.has_key(model_from): 974 self._rotation_axis[model_from] = {} 975 if not self._rotation_angle.has_key(model_from): 976 self._rotation_angle[model_from] = {} 977 978 # The Kabsch algorithm. 979 trans_vect, trans_dist, R, axis, angle, pivot = kabsch(name_from='model %s'%model_from, name_to='model %s'%model_to, coord_from=coord_from, coord_to=coord_to, centroid=centroid) 980 981 # Store the data. 982 self._translation_vector[model_from][model_to] = trans_vect 983 self._translation_distance[model_from][model_to] = trans_dist 984 self._rotation_matrix[model_from][model_to] = R 985 self._rotation_axis[model_from][model_to] = axis 986 self._rotation_angle[model_from][model_to] = angle
987 988
989 - def from_xml(self, str_node, dir=None, id=None, file_version=1):
990 """Recreate the structural object from the XML structural object node. 991 992 @param str_node: The structural object XML node. 993 @type str_node: xml.dom.minicompat.Element instance 994 @keyword dir: The name of the directory containing the results file. 995 @type dir: str 996 @keyword id: The specific structural object ID string. This can be 'scientific', 'internal', etc. 997 @type id: str 998 @keyword file_version: The relax XML version of the XML file. 999 @type file_version: int 1000 """ 1001 1002 # Get the pairs of displacements. 1003 pair_nodes = str_node.getElementsByTagName('pair') 1004 1005 # Loop over the pairs. 1006 for pair_node in pair_nodes: 1007 # Get the two models. 1008 model_from = int(pair_node.getAttribute('model_from')) 1009 model_to = int(pair_node.getAttribute('model_to')) 1010 1011 # Initialise structures if necessary. 1012 if not self._translation_vector.has_key(model_from): 1013 self._translation_vector[model_from] = {} 1014 if not self._translation_distance.has_key(model_from): 1015 self._translation_distance[model_from] = {} 1016 if not self._rotation_matrix.has_key(model_from): 1017 self._rotation_matrix[model_from] = {} 1018 if not self._rotation_axis.has_key(model_from): 1019 self._rotation_axis[model_from] = {} 1020 if not self._rotation_angle.has_key(model_from): 1021 self._rotation_angle[model_from] = {} 1022 1023 # A temporary container to place the Python objects into. 1024 cont = ModelContainer() 1025 1026 # Recreate the Python objects. 1027 xml_to_object(pair_node, cont, file_version=file_version) 1028 1029 # Repackage the data. 1030 for name in ['translation_vector', 'translation_distance', 'rotation_matrix', 'rotation_axis', 'rotation_angle']: 1031 # The objects. 1032 obj = getattr(self, '_'+name) 1033 obj_temp = getattr(cont, name) 1034 1035 # Store. 1036 obj[model_from][model_to] = obj_temp
1037 1038
1039 - def to_xml(self, doc, element):
1040 """Create XML elements for each model. 1041 1042 @param doc: The XML document object. 1043 @type doc: xml.dom.minidom.Document instance 1044 @param element: The element to add the displacement XML elements to. 1045 @type element: XML element object 1046 """ 1047 1048 # Loop over the starting models. 1049 start_models = sorted(self._translation_vector.keys()) 1050 for model_from in start_models: 1051 # Loop over the ending models. 1052 end_models = sorted(self._translation_vector[model_from].keys()) 1053 for model_to in end_models: 1054 # Create an XML element for each pair. 1055 pair_element = doc.createElement('pair') 1056 element.appendChild(pair_element) 1057 1058 # Set the attributes. 1059 pair_element.setAttribute('desc', 'The displacement from model %s to model %s' % (model_from, model_to)) 1060 pair_element.setAttribute('model_from', str(model_from)) 1061 pair_element.setAttribute('model_to', str(model_to)) 1062 1063 # The objects to store. 1064 obj_names = [ 1065 '_translation_vector', 1066 '_translation_distance', 1067 '_rotation_matrix', 1068 '_rotation_axis', 1069 '_rotation_angle' 1070 ] 1071 1072 # Store the objects. 1073 for i in range(len(obj_names)): 1074 # Create a new element for this object, and add it to the main element. 1075 sub_elem = doc.createElement(obj_names[i][1:]) 1076 pair_element.appendChild(sub_elem) 1077 1078 # Get the sub-object. 1079 subobj = getattr(self, obj_names[i])[model_from][model_to] 1080 1081 # Add the value to the sub element. 1082 object_to_xml(doc, sub_elem, value=subobj)
1083 1084 1085
1086 -class ModelList(list):
1087 """List type data container for the different structural models. 1088 1089 Here different models are defined as the same molecule but with different conformations. 1090 """ 1091
1092 - def __repr__(self):
1093 """The string representation of the object. 1094 1095 Rather than using the standard Python conventions (either the string representation of the 1096 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 1097 """ 1098 1099 text = "Models.\n\n" 1100 text = text + "%-8s%-8s" % ("Index", "Model number") + "\n" 1101 for i in xrange(len(self)): 1102 text = text + "%-8i%-8s" % (i, self[i].num) + "\n" 1103 return text
1104 1105
1106 - def add_item(self, model_num=None):
1107 """Append an empty ModelContainer to the ModelList. 1108 1109 @keyword model_num: The model number. 1110 @type model_num: int 1111 """ 1112 1113 # If no model data exists, replace the empty first model with this model (just a renumbering). 1114 if len(self) and self.is_empty(): 1115 self[0].num = model_num 1116 1117 # Otherwise append an empty ModelContainer. 1118 else: 1119 # Test if the model number already exists. 1120 for i in xrange(len(self)): 1121 if self[i].num == model_num: 1122 raise RelaxError("The model '" + repr(model_num) + "' already exists.") 1123 1124 # Append an empty ModelContainer. 1125 self.append(ModelContainer(model_num))
1126 1127
1128 - def is_empty(self):
1129 """Method for testing if this ModelList object is empty. 1130 1131 @return: True if this list only has one ModelContainer and the model name has not 1132 been set, False otherwise. 1133 @rtype: bool 1134 """ 1135 1136 # No ModelContainers. 1137 if len(self) == 0: 1138 return True 1139 1140 # There is only one ModelContainer and it is empty. 1141 if len(self) == 1 and self[0].is_empty(): 1142 return True 1143 1144 # Otherwise. 1145 return False
1146 1147
1148 - def from_xml(self, model_nodes, id=None, file_version=1):
1149 """Recreate a model list data structure from the XML model nodes. 1150 1151 @param model_nodes: The model XML nodes. 1152 @type model_nodes: xml.dom.minicompat.NodeList instance 1153 @keyword id: The specific structural object ID string. This can be 'scientific', 'internal', etc. 1154 @type id: str 1155 @keyword file_version: The relax XML version of the XML file. 1156 @type file_version: int 1157 """ 1158 1159 # Test if empty. 1160 if not self.is_empty(): 1161 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 1162 1163 # Loop over the models. 1164 for model_node in model_nodes: 1165 # Get the model details and add the model to the ModelList structure. 1166 num = eval(model_node.getAttribute('num')) 1167 if num == 'None': 1168 num = None 1169 self.add_item(model_num=num) 1170 1171 # Get the molecule nodes. 1172 mol_nodes = model_node.getElementsByTagName('mol_cont') 1173 1174 # Recreate the molecule data structures for the current model. 1175 self[-1].mol.from_xml(mol_nodes, id=id, file_version=file_version)
1176 1177
1178 - def to_xml(self, doc, element):
1179 """Create XML elements for each model. 1180 1181 @param doc: The XML document object. 1182 @type doc: xml.dom.minidom.Document instance 1183 @param element: The element to add the model XML elements to. 1184 @type element: XML element object 1185 """ 1186 1187 # Loop over the models. 1188 for i in xrange(len(self)): 1189 # Create an XML element for this model and add it to the higher level element. 1190 model_element = doc.createElement('model') 1191 element.appendChild(model_element) 1192 1193 # Set the model attributes. 1194 model_element.setAttribute('desc', 'Model container') 1195 model_element.setAttribute('num', str(self[i].num)) 1196 1197 # Add all simple python objects within the ModelContainer to the XML element. 1198 fill_object_contents(doc, model_element, object=self[i], blacklist=['num', 'mol'] + list(self[i].__class__.__dict__.keys())) 1199 1200 # Add the molecule data. 1201 self[i].mol.to_xml(doc, model_element)
1202 1203 1204
1205 -class ModelContainer(object):
1206 """Class containing all the model specific data.""" 1207
1208 - def __init__(self, model_num=None):
1209 """Set up the default objects of the model data container.""" 1210 1211 # The model number. 1212 self.num = model_num 1213 1214 # The empty molecule list. 1215 self.mol = MolList()
1216 1217
1218 - def __repr__(self):
1219 """The string representation of the object. 1220 1221 Rather than using the standard Python conventions (either the string representation of the 1222 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 1223 """ 1224 1225 # Intro. 1226 text = "Class containing the data for model %s.\n" % self.num 1227 1228 # Objects. 1229 text = text + "\n" 1230 text = text + "Objects:\n" 1231 for name in dir(self): 1232 # Molecule list. 1233 if name == 'mol': 1234 text = text + " mol: The list of %s molecules within the model.\n" % len(self.mol) 1235 continue 1236 1237 # Skip the ModelContainer methods. 1238 if name == 'is_empty': 1239 continue 1240 1241 # Skip special objects. 1242 if match("^__", name): 1243 continue 1244 1245 # Add the object's attribute to the text string. 1246 text = text + " " + name + ": " + repr(getattr(self, name)) + "\n" 1247 1248 return text
1249 1250
1251 - def is_empty(self):
1252 """Method for testing if this ModelContainer object is empty. 1253 1254 @return: True if this container is empty and the model number has not been set, False 1255 otherwise. 1256 @rtype: bool 1257 """ 1258 1259 # The model num has been set. 1260 if self.num != None: 1261 return False 1262 1263 # An object has been added to the container. 1264 for name in dir(self): 1265 # Skip the objects initialised in __init__(). 1266 if name == 'num' or name == 'mol': 1267 continue 1268 1269 # Skip the ModelContainer methods. 1270 if name == 'is_empty': 1271 continue 1272 1273 # Skip special objects. 1274 if match("^__", name): 1275 continue 1276 1277 # An object has been added. 1278 return False 1279 1280 # The molecule list is not empty. 1281 if not self.mol.is_empty(): 1282 return False 1283 1284 # The ModelContainer is unmodified. 1285 return True
1286 1287 1288
1289 -class MolList(list):
1290 """List type data container for holding the different molecules of one model.""" 1291
1292 - def __repr__(self):
1293 """The string representation of the object. 1294 1295 Rather than using the standard Python conventions (either the string representation of the 1296 value or the "<...desc...>" notation), a rich-formatted description of the object is given. 1297 """ 1298 1299 text = "Molecules.\n\n" 1300 text = text + "%-8s%-8s" % ("Index", "Name") + "\n" 1301 for i in xrange(len(self)): 1302 text = text + "%-8i%-8s" % (i, self[i].mol_name) + "\n" 1303 return text
1304 1305
1306 - def add_item(self, mol_name=None, mol_cont=None):
1307 """Append the given MolContainer instance to the MolList. 1308 1309 @keyword mol_name: The molecule number. 1310 @type mol_name: int 1311 @keyword mol_cont: The data structure for the molecule. 1312 @type mol_cont: MolContainer instance 1313 """ 1314 1315 # If no molecule data exists, replace the empty first molecule with this molecule (just a renaming). 1316 if len(self) and self.is_empty(): 1317 self[0].mol_name = mol_name 1318 1319 # Otherwise append an empty MolContainer. 1320 else: 1321 # Test if the molecule already exists. 1322 for i in xrange(len(self)): 1323 if self[i].mol_name == mol_name: 1324 raise RelaxError("The molecule '%s' already exists." % mol_name) 1325 1326 # Append an empty MolContainer. 1327 self.append(mol_cont) 1328 1329 # Set the name. 1330 self[-1].mol_name = mol_name
1331 1332
1333 - def is_empty(self):
1334 """Method for testing if this MolList object is empty. 1335 1336 @return: True if this list only has one MolContainer and the molecule name has not 1337 been set, False otherwise. 1338 @rtype: bool 1339 """ 1340 1341 # No MolContainers. 1342 if len(self) == 0: 1343 return True 1344 1345 # There is only one MolContainer and it is empty. 1346 if len(self) == 1 and hasattr(self[0], 'is_empty') and self[0].is_empty(): 1347 return True 1348 1349 # Otherwise. 1350 return False
1351 1352
1353 - def from_xml(self, mol_nodes, id=None, file_version=1):
1354 """Recreate a molecule list data structure from the XML molecule nodes. 1355 1356 @param mol_nodes: The molecule XML nodes. 1357 @type mol_nodes: xml.dom.minicompat.NodeList instance 1358 @keyword id: The specific structural object ID string. This can be 'scientific', 'internal', etc. 1359 @type id: str 1360 @keyword file_version: The relax XML version of the XML file. 1361 @type file_version: int 1362 """ 1363 1364 # Test if empty. 1365 if not self.is_empty(): 1366 raise RelaxFromXMLNotEmptyError(self.__class__.__name__) 1367 1368 # Loop over the molecules. 1369 for mol_node in mol_nodes: 1370 # Some imports (here to break circular import issues). 1371 if id == 'internal': 1372 from internal import MolContainer 1373 elif id == 'scientific': 1374 from scientific import MolContainer 1375 1376 # Initialise a MolContainer instance. 1377 mol_cont = MolContainer() 1378 1379 # Get the molecule name. 1380 name = mol_node.getAttribute('name') 1381 if name == 'None': 1382 name = None 1383 1384 # Add the molecule to the MolList structure. 1385 self.add_item(mol_name=name, mol_cont=mol_cont) 1386 1387 # Execute the specific MolContainer from_xml() method. 1388 self[-1].from_xml(mol_node, file_version=file_version)
1389 1390
1391 - def to_xml(self, doc, element):
1392 """Create XML elements for each molecule. 1393 1394 @param doc: The XML document object. 1395 @type doc: xml.dom.minidom.Document instance 1396 @param element: The element to add the molecule XML elements to. 1397 @type element: XML element object 1398 """ 1399 1400 # Loop over the molecules. 1401 for i in xrange(len(self)): 1402 # Add the molecule data. 1403 self[i].to_xml(doc, element)
1404