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