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