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