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