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