1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module containing the internal relax structural object."""
24
25
26 from copy import deepcopy
27 from numpy import array, dot, float64, linalg, zeros
28 import os
29 from os import F_OK, access
30 from string import digits, ascii_uppercase
31 from warnings import warn
32
33
34 from check_types import is_float
35 from data.relax_xml import fill_object_contents, xml_to_object
36 from generic_fns import pipes, relax_re
37 from generic_fns.mol_res_spin import spin_loop
38 from generic_fns.mol_res_spin import Selection
39 from generic_fns.structure import pdb_read, pdb_write
40 from generic_fns.structure.api_base import Base_struct_API, ModelList, Displacements
41 from relax_errors import RelaxError, RelaxNoneIntError, RelaxNoPdbError
42 from relax_io import file_root, open_read_file
43 from relax_warnings import RelaxWarning
44
45
46
48 """The internal relax structural data object.
49
50 The structural data object for this class is a container possessing a number of different arrays
51 corresponding to different structural information. These objects are described in the
52 structural container docstring.
53 """
54
55
56 id = 'internal'
57
58
60 """Find the atom named attached_atom directly bonded to the atom located at the index.
61
62 @param attached_atom: The name of the attached atom to return.
63 @type attached_atom: str
64 @param index: The index of the atom which the attached atom is attached to.
65 @type index: int
66 @param mol: The molecule container.
67 @type mol: MolContainer instance
68 @return: A tuple of information about the bonded atom.
69 @rtype: tuple consisting of the atom number (int), atom name (str), element
70 name (str), and atomic position (Numeric array of len 3)
71 """
72
73
74 bonded_found = False
75
76
77 if not mol.bonded[index]:
78
79 if not hasattr(mol, 'type'):
80 self._mol_type(mol)
81
82
83 if mol.type == 'protein':
84 self._protein_connect(mol)
85
86
87 else:
88 self._find_bonded_atoms(index, mol, radius=2)
89
90
91 matching_list = []
92 for bonded_index in mol.bonded[index]:
93 if relax_re.search(mol.atom_name[bonded_index], attached_atom):
94 matching_list.append(bonded_index)
95 num_attached = len(matching_list)
96
97
98 if num_attached > 1:
99
100 matching_names = []
101 for i in matching_list:
102 matching_names.append(mol.atom_name[i])
103
104
105 return None, None, None, None, None, 'More than one attached atom found: ' + repr(matching_names)
106
107
108 if num_attached == 0:
109 if relax_re.search('@*', attached_atom):
110 matching_list = []
111 bonded_num=[]
112 bonded_name=[]
113 element=[]
114 pos=[]
115 for spin, mol_name, res_num, res_name in spin_loop(selection=attached_atom, full_info=True):
116 bonded_num.append(spin.num)
117 bonded_name.append(spin.name)
118 element.append(spin.element)
119 pos.append(spin.pos)
120 if len(bonded_num) == 1:
121 return bonded_num[0], bonded_name[0], element[0], pos[0], attached_atom, None
122 elif len(bonded_num) > 1:
123
124 return None, None, None, None, None, 'More than one attached atom found: ' + repr(matching_names)
125 elif len(bonded_num) > 1:
126
127 return None, None, None, None, None, "No attached atom could be found"
128 else:
129 return None, None, None, None, None, "No attached atom could be found"
130
131
132 index = matching_list[0]
133 bonded_num = mol.atom_num[index]
134 bonded_name = mol.atom_name[index]
135 element = mol.element[index]
136 pos = [mol.x[index], mol.y[index], mol.z[index]]
137 attached_name = mol.atom_name[index]
138
139
140 return bonded_num, bonded_name, element, pos, attached_name, None
141
142
144 """Find all atoms within a sphere and say that they are attached to the central atom.
145
146 The found atoms will be added to the 'bonded' data structure.
147
148
149 @param index: The index of the central atom.
150 @type index: int
151 @param mol: The molecule container.
152 @type mol: MolContainer instance
153 """
154
155
156 centre = array([mol.x[index], mol.y[index], mol.z[index]], float64)
157
158
159 dist_list = []
160 connect_list = {}
161 element_list = {}
162 for i in range(len(mol.atom_num)):
163
164 if mol.element[index] == 'H' and mol.element[i] == 'H':
165 continue
166
167
168 pos = array([mol.x[i], mol.y[i], mol.z[i]], float64)
169
170
171 dist = linalg.norm(centre-pos)
172
173
174 if dist < radius:
175
176 dist_list.append(dist)
177
178
179 connect_list[dist] = i
180
181
182 element_list[dist] = mol.element[i]
183
184
185 max_conn = 1000
186 if mol.element[index] == 'H':
187 max_conn = 1
188 elif mol.element[index] == 'O':
189 max_conn = 2
190 elif mol.element[index] == 'N':
191 max_conn = 3
192 elif mol.element[index] == 'C':
193 max_conn = 4
194
195
196 dist_list.sort()
197
198
199 for i in range(min(max_conn, len(dist_list))):
200 mol.atom_connect(index, connect_list[dist_list[i]])
201
202
204 """Return the chemical name corresponding to the given residue ID.
205
206 The following names are currently returned::
207 ________________________________________________
208 | | |
209 | hetID | Chemical name |
210 |________|_____________________________________|
211 | | |
212 | TNS | Tensor |
213 | COM | Centre of mass |
214 | AXS | Tensor axes |
215 | SIM | Monte Carlo simulation tensor axes |
216 | PIV | Pivot point |
217 | CON | Cone object |
218 | AVE | Average vector |
219 |________|_____________________________________|
220
221 For any other residues, no description is returned.
222
223 @param hetID: The residue ID.
224 @type hetID: str
225 @return: The chemical name.
226 @rtype: str or None
227 """
228
229
230 if hetID == 'TNS':
231 return 'Tensor'
232
233
234 if hetID == 'COM':
235 return 'Centre of mass'
236
237
238 if hetID == 'AXS':
239 return 'Tensor axes'
240
241
242 if hetID == 'SIM':
243 return 'Monte Carlo simulation tensor axes'
244
245
246 if hetID == 'PIV':
247 return 'Pivot point'
248
249
250 if hetID == 'CON':
251 return 'Cone'
252
253
254 if hetID == 'AVE':
255 return 'Average vector'
256
257
259 """Loop over and parse the PDB connectivity annotation records.
260
261 These are the records identified in the U{PDB version 3.30 documentation<http://www.wwpdb.org/documentation/format33/sect6.html>}.
262
263
264 @param lines: The lines of the PDB file excluding the sections prior to the connectivity annotation section.
265 @type lines: list of str
266 @return: The remaining PDB lines with the connectivity annotation records stripped.
267 @rtype: list of str
268 """
269
270
271 records = [
272 'SSBOND',
273 'LINK ',
274 'CISPEP'
275 ]
276
277
278 for i in range(len(lines)):
279
280 if lines[i][:6] not in records:
281 break
282
283
284 return lines[i:]
285
286
288 """Generator function for looping over the models in the PDB file.
289
290 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/format33/sect9.html}.
291
292
293 @param lines: The lines of the coordinate section.
294 @type lines: list of str
295 @return: The model number and all the records for that model.
296 @rtype: tuple of int and array of str
297 """
298
299
300 model = None
301 records = []
302
303
304 for i in range(len(lines)):
305
306 if lines[i][:5] == 'MODEL':
307 try:
308 model = int(lines[i].split()[1])
309 except:
310 raise RelaxError("The MODEL record " + repr(lines[i]) + " is corrupt, cannot read the PDB file.")
311
312
313 if not (lines[i][:4] == 'ATOM' or lines[i][:6] == 'HETATM') and not len(records):
314 continue
315
316
317 if lines[i][:6] == 'ENDMDL':
318
319 yield model, records
320
321
322 records = []
323
324
325 continue
326
327
328 records.append(lines[i])
329
330
331 if len(records):
332 yield model, records
333
334
336 """Loop over and parse the PDB hetrogen records.
337
338 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/format33/sect4.html}.
339
340
341 @param lines: The lines of the PDB file excluding the sections prior to the hetrogen section.
342 @type lines: list of str
343 @return: The remaining PDB lines with the hetrogen records stripped.
344 @rtype: list of str
345 """
346
347
348 records = [
349 'HET ',
350 'FORMUL',
351 'HETNAM',
352 'HETSYN'
353 ]
354
355
356 for i in range(len(lines)):
357
358 if lines[i][:6] not in records:
359 break
360
361
362 return lines[i:]
363
364
366 """Loop over and parse the PDB miscellaneous records.
367
368 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/format33/sect7.html}.
369
370
371 @param lines: The lines of the PDB file excluding the sections prior to the miscellaneous section.
372 @type lines: list of str
373 @return: The remaining PDB lines with the miscellaneous records stripped.
374 @rtype: list of str
375 """
376
377
378 records = [
379 'SITE '
380 ]
381
382
383 for i in range(len(lines)):
384
385 if lines[i][:6] not in records:
386 break
387
388
389 return lines[i:]
390
391
393 """Loop over and parse the PDB primary structure records.
394
395 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/format33/sect3.html}.
396
397
398 @param lines: The lines of the PDB file excluding the title section.
399 @type lines: list of str
400 @return: The remaining PDB lines with the primary structure records stripped.
401 @rtype: list of str
402 """
403
404
405 records = [
406 'DBREF ',
407 'DBREF1',
408 'DBREF2',
409 'SEQADV',
410 'SEQRES',
411 'MODRES'
412 ]
413
414
415 for i in range(len(lines)):
416
417 if lines[i][:6] not in records:
418 break
419
420
421 return lines[i:]
422
423
425 """Loop over and parse the PDB secondary structure records.
426
427 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/format33/sect5.html}.
428
429
430 @param lines: The lines of the PDB file excluding the sections prior to the secondary structure section.
431 @type lines: list of str
432 @return: The remaining PDB lines with the secondary structure records stripped.
433 @rtype: list of str
434 """
435
436
437 records = [
438 'HELIX ',
439 'SHEET ',
440 'TURN '
441 ]
442
443
444 for i in range(len(lines)):
445
446 if lines[i][:6] not in records:
447 break
448
449
450 if lines[i][:5] == 'HELIX':
451
452 record_type, ser_num, helix_id, init_res_name, init_chain_id, init_seq_num, init_icode, end_res_name, end_chain_id, end_seq_num, end_icode, helix_class, comment, length = pdb_read.helix(lines[i])
453
454
455 if not hasattr(self, 'helices'):
456 self.helices = []
457 self.helices.append([helix_id, init_chain_id, init_res_name, init_seq_num, end_chain_id, end_res_name, end_seq_num, helix_class, length])
458
459
460 if lines[i][:5] == 'SHEET':
461
462 record_type, strand, sheet_id, num_strands, init_res_name, init_chain_id, init_seq_num, init_icode, end_res_name, end_chain_id, end_seq_num, end_icode, sense, cur_atom, cur_res_name, cur_chain_id, cur_res_seq, cur_icode, prev_atom, prev_res_name, prev_chain_id, prev_res_seq, prev_icode = pdb_read.sheet(lines[i])
463
464
465 if not hasattr(self, 'sheets'):
466 self.sheets = []
467 self.sheets.append([strand, sheet_id, num_strands, init_res_name, init_chain_id, init_seq_num, init_icode, end_res_name, end_chain_id, end_seq_num, end_icode, sense, cur_atom, cur_res_name, cur_chain_id, cur_res_seq, cur_icode, prev_atom, prev_res_name, prev_chain_id, prev_res_seq, prev_icode])
468
469
470 return lines[i:]
471
472
474 """Loop over and parse the PDB title records.
475
476 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/format33/sect2.html}.
477
478
479 @param lines: All lines of the PDB file.
480 @type lines: list of str
481 @return: The remaining PDB lines with the title records stripped.
482 @rtype: list of str
483 """
484
485
486 records = [
487 'HEADER',
488 'OBSLTE',
489 'TITLE ',
490 'SPLT ',
491 'CAVEAT',
492 'COMPND',
493 'SOURCE',
494 'KEYWDS',
495 'EXPDTA',
496 'NUMMDL',
497 'MDLTYP',
498 'AUTHOR',
499 'REVDAT',
500 'SPRSDE',
501 'JRNL ',
502 'REMARK'
503 ]
504
505
506 for i in range(len(lines)):
507
508 if lines[i][:6] not in records:
509 break
510
511
512 return lines[i:]
513
514
543
544
546 """Generator function for looping over the models in the XYZ file.
547
548 @param file_path: The full path of the XYZ file.
549 @type file_path: str
550 @return: The model number and all the records for that model.
551 @rtype: tuple of int and array of str
552 """
553
554
555 file = open_read_file(file_path)
556 lines = file.readlines()
557 file.close()
558
559
560 if lines == []:
561 raise RelaxError("The XYZ file is empty.")
562
563
564 total_atom = 0
565 model = 0
566 records = []
567
568
569 for i in range(len(lines)):
570 num=0
571 word = lines[i].split()
572
573 if (i==0) and (len(word)==1):
574 try:
575 total_atom = int(word[0])
576 num = 1
577 except:
578 raise RelaxError("The MODEL record " + repr(lines[i]) + " is corrupt, cannot read the XYZ file.")
579
580
581 if (len(records) == total_atom):
582
583 yield records
584
585
586 records = []
587
588
589 if (len(word) != 4):
590 continue
591
592
593 records.append(lines[i])
594
595
596 if len(records):
597 yield records
598
599
601 """Generator function for looping over the molecules in the PDB records of a model.
602
603 @param records: The list of PDB records for the model, or if no models exist the entire
604 PDB file.
605 @type records: list of str
606 @return: The molecule number and all the records for that molecule.
607 @rtype: tuple of int and list of str
608 """
609
610
611 if records == []:
612 raise RelaxError("There are no PDB records for this model.")
613
614
615 mol_num = 1
616 mol_records = []
617 end = False
618
619
620 for i in range(len(records)):
621
622 if records[i][:3] == 'END':
623 break
624
625
626 if records[i][:6] == 'MASTER':
627 break
628
629
630 if records[i][:6] == 'ENDMDL':
631 end = True
632
633
634 elif i < len(records)-1 and records[i][:3] == 'TER' and not records[i+1][:6] == 'HETATM':
635 end = True
636
637
638 elif i < len(records)-1 and records[i][:6] == 'HETATM' and records[i+1][:4] == 'ATOM':
639 end = True
640
641
642 if end:
643
644 yield mol_num, mol_records
645
646
647 mol_records = []
648
649
650 mol_num = mol_num + 1
651
652
653 end = False
654
655
656 continue
657
658
659 mol_records.append(records[i])
660
661
662 if len(mol_records):
663 yield mol_num, mol_records
664
665
667 """Convert the PDB chain ID into the molecule index in a regular way.
668
669 @keyword chain_id: The PDB chain ID string.
670 @type chain_id: str
671 @return: The corresponding molecule index.
672 @rtype: int
673 """
674
675
676 mol_index = 0
677
678
679 if chain_id:
680 mol_index = ascii_uppercase.index(chain_id)
681
682
683 return mol_index
684
685
687 """Convert the residue info into a dictionary of unique residues with numbers as keys.
688
689 @keyword res_nums: The list of residue numbers.
690 @type res_nums: list of int
691 @keyword res_names: The list of residue names matching the numbers.
692 @type res_names: list of str
693 @return: The dictionary of residue names with residue numbers as keys.
694 @rtype: dict of str
695 """
696
697
698 data = {}
699
700
701 for i in range(len(res_nums)):
702
703 if res_nums[i] in data:
704 continue
705
706
707 data[res_nums[i]] = res_names[i]
708
709
710 return data
711
712
714 """Check the validity of the data arrays in the given structure object.
715
716 @param struct: The structural object.
717 @type struct: Structure_container instance
718 """
719
720
721 num = len(struct.atom_name)
722
723
724 if len(struct.bonded) != num and len(struct.chain_id) != num and len(struct.element) != num and len(struct.pdb_record) != num and len(struct.res_name) != num and len(struct.res_num) != num and len(struct.seg_id) != num and len(struct.x) != num and len(struct.y) != num and len(struct.z) != num:
725 raise RelaxError("The structural data is invalid.")
726
727
729 """Determine the type of molecule.
730
731 @param mol: The molecule data container.
732 @type mol: MolContainer instance
733 """
734
735
736 aa = ['ALA', 'ARG', 'ASN', 'ASP', 'CYS', 'GLU', 'GLN', 'GLY', 'HIS', 'ILE', 'LEU', 'LYS', 'MET', 'PHE', 'PRO', 'SER', 'THR', 'TRP', 'TYR', 'VAL']
737
738
739 mol.type = 'other'
740
741
742 for res in mol.res_name:
743
744 if res in aa:
745
746 mol.type = 'protein'
747 return
748
749
751 """Set up the connectivities for the protein.
752
753 @param mol: The molecule data container.
754 @type mol: MolContainer instance
755 """
756
757
758 curr_res_num = None
759 res_atoms = []
760
761
762 for i in range(len(mol.atom_num)):
763
764 if mol.res_num[i] != curr_res_num:
765
766 if len(res_atoms):
767 self._protein_intra_connect(mol, res_atoms)
768
769
770 curr_res_num = mol.res_num[i]
771
772
773 res_atoms = []
774
775
776 res_atoms.append(i)
777
778
779 if i == len(mol.atom_num) - 1 and len(res_atoms):
780 self._protein_intra_connect(mol, res_atoms)
781
782
784 """Set up the connectivities for the protein.
785
786 @param mol: The molecule data container.
787 @type mol: MolContainer instance
788 @param res_atoms: The list of atom indices corresponding to the residue.
789 @type res_atoms: list of int
790 """
791
792
793 indices = {
794 'N': None,
795 'C': None,
796 'O': None,
797 'CA': None,
798 'HN': None,
799 'H': None,
800 'HA': None
801 }
802
803
804 for index in res_atoms:
805 if mol.atom_name[index] in indices:
806 indices[mol.atom_name[index]] = index
807
808
809 pairs = [
810 ['N', 'HN'],
811 ['N', 'H'],
812 ['N', 'CA'],
813 ['CA', 'HA'],
814 ['CA', 'C'],
815 ['C', 'O']
816 ]
817
818
819 for pair in pairs:
820 if indices[pair[0]] != None and indices[pair[1]] != None:
821 mol.atom_connect(indices[pair[0]], indices[pair[1]])
822
823
825 """Convert the data into a format for writing to file.
826
827 @param data: The data to convert to the required format.
828 @type data: anything
829 @keyword format: The format to convert to. This can be 'str', 'float', or 'int'.
830 @type format: str
831 @return: The converted version of the data.
832 @rtype: str
833 """
834
835
836 if format == 'str':
837
838 if data == None:
839 data = ''
840
841
842 if not isinstance(data, str):
843 data = repr(data)
844
845
846 if format == 'float':
847
848 if data == None:
849 data = 0.0
850
851
852 if not isinstance(data, float):
853 data = float(data)
854
855
856 return data
857
858
859 - def _trim_helix(self, helix=None, trim_res_list=[], res_data=None):
860 """Trim the given helix based on the list of deleted residue numbers.
861
862 @keyword helix: The single helix metadata structure.
863 @type helix: list
864 @keyword trim_res_list: The list of residue numbers which no longer exist.
865 @type trim_res_list: list of int
866 @keyword res_data: The dictionary of residue names with residue numbers as keys.
867 @type res_data: dict of str
868 @return: The trimmed helix metadata structure, or None if the whole helix is to be deleted.
869 @rtype: list or None
870 """
871
872
873 start_res = helix[3]
874 end_res = helix[6]
875
876
877 trim_res_list_rev = deepcopy(trim_res_list)
878 trim_res_list_rev.reverse()
879
880
881 helix_res = list(range(start_res, end_res+1))
882
883
884 for res_num in trim_res_list:
885 if res_num == start_res:
886
887 helix_res.pop(0)
888
889
890 if len(helix_res) == 0:
891 break
892
893
894 start_res = helix_res[0]
895
896
897 if len(helix_res) == 0:
898 return None
899
900
901 for res_num in trim_res_list_rev:
902 if res_num == end_res:
903 helix_res.pop(-1)
904 end_res = helix_res[-1]
905
906
907 if start_res != helix[3]:
908 helix[3] = start_res
909 helix[2] = res_data[start_res]
910 if end_res != helix[6]:
911 helix[6] = end_res
912 helix[5] = res_data[end_res]
913
914
915 helix[-1] = len(helix_res)
916
917
918 return helix
919
920
921 - def _trim_sheet(self, sheet=None, trim_res_list=[], res_data=None):
922 """Trim the given sheet based on the list of deleted residue numbers.
923
924 @keyword sheet: The single sheet metadata structure.
925 @type sheet: list
926 @keyword trim_res_list: The list of residue numbers which no longer exist.
927 @type trim_res_list: list of int
928 @keyword res_data: The dictionary of residue names with residue numbers as keys.
929 @type res_data: dict of str
930 @return: The trimmed sheet metadata structure, or None if the whole sheet is to be deleted.
931 @rtype: list or None
932 """
933
934
935 start_res = sheet[5]
936 end_res = sheet[9]
937
938
939 trim_res_list_rev = deepcopy(trim_res_list)
940 trim_res_list_rev.reverse()
941
942
943 sheet_res = list(range(start_res, end_res+1))
944
945
946 for res_num in trim_res_list:
947 if res_num == start_res:
948
949 sheet_res.pop(0)
950
951
952 if len(sheet_res) == 0:
953 break
954
955
956 start_res = sheet_res[0]
957
958
959 if len(sheet_res) == 0:
960 return None
961
962
963 for res_num in trim_res_list_rev:
964 if res_num == end_res:
965 sheet_res.pop(-1)
966 end_res = sheet_res[-1]
967
968
969 if start_res != sheet[5]:
970 sheet[5] = start_res
971 sheet[3] = res_data[start_res]
972 if end_res != sheet[9]:
973 sheet[9] = end_res
974 sheet[7] = res_data[end_res]
975
976
977 return sheet
978
979
980 - 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):
981 """Add a new atom to the structural data object.
982
983 @keyword mol_name: The name of the molecule.
984 @type mol_name: str
985 @keyword atom_name: The atom name, e.g. 'H1'.
986 @type atom_name: str or None
987 @keyword res_name: The residue name.
988 @type res_name: str or None
989 @keyword res_num: The residue number.
990 @type res_num: int or None
991 @keyword pos: The position vector of coordinates. If a rank-2 array is supplied, the length of the first dimension must match the number of models.
992 @type pos: rank-1 or rank-2 array or list of float
993 @keyword element: The element symbol.
994 @type element: str or None
995 @keyword atom_num: The atom number.
996 @type atom_num: int or None
997 @keyword chain_id: The chain identifier.
998 @type chain_id: str or None
999 @keyword segment_id: The segment identifier.
1000 @type segment_id: str or None
1001 @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'.
1002 @type pdb_record: str or None
1003 """
1004
1005
1006 pipes.test()
1007
1008
1009 if len(self.structural_data) == 0:
1010 self.add_model()
1011
1012
1013 if is_float(pos[0]):
1014 if len(pos) != 3:
1015 raise RelaxError("The single atomic position %s must be a 3D list." % pos)
1016 else:
1017 if len(pos) != len(self.structural_data):
1018 raise RelaxError("The %s atomic positions does not match the %s models present." % (len(pos), len(self.structural_data)))
1019
1020
1021 for i in range(len(self.structural_data)):
1022
1023 model = self.structural_data[i]
1024
1025
1026 mol = self.get_molecule(mol_name, model=model.num)
1027
1028
1029 if mol == None:
1030 self.add_molecule(name=mol_name)
1031 mol = self.get_molecule(mol_name, model=model.num)
1032
1033
1034 if is_float(pos[0]):
1035 model_pos = pos
1036 else:
1037 model_pos = pos[i]
1038
1039
1040 mol.atom_add(atom_name=atom_name, res_name=res_name, res_num=res_num, pos=model_pos, element=element, atom_num=atom_num, chain_id=chain_id, segment_id=segment_id, pdb_record=pdb_record)
1041
1042
1043 - def add_model(self, model=None, coords_from=None):
1044 """Add a new model to the store.
1045
1046 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.
1047
1048 @keyword model: The number of the model to create.
1049 @type model: int or None
1050 @keyword coords_from: The model number to take the coordinates from.
1051 @type coords_from: int or None
1052 @return: The model container.
1053 @rtype: ModelContainer instance
1054 """
1055
1056
1057 if model != None:
1058 for i in range(len(self.structural_data)):
1059 if model == self.structural_data[i].num:
1060 raise RelaxError("The model '%s' already exists." % model)
1061
1062
1063 self.structural_data.add_item(model_num=model)
1064
1065
1066 if coords_from == None:
1067 coords_from = self.structural_data[0].num
1068
1069
1070 for mol_name, res_num, res_name, atom_num, atom_name, element, pos in self.atom_loop(model_num=coords_from, mol_name_flag=True, res_num_flag=True, res_name_flag=True, atom_num_flag=True, atom_name_flag=True, element_flag=True, pos_flag=True):
1071
1072 self.add_atom(self, mol_name=mol_name, atom_name=atom_name, res_name=res_name, res_num=res_num, pos=pos, element=element, atom_num=atom_num)
1073
1074
1075 return self.structural_data[-1]
1076
1077
1079 """Add a new molecule to the store.
1080
1081 @keyword name: The molecule identifier string.
1082 @type name: str
1083 """
1084
1085
1086 if len(self.structural_data) == 0:
1087 self.add_model()
1088
1089
1090 for i in range(len(self.structural_data)):
1091
1092 self.structural_data[i].mol.add_item(mol_name=name, mol_cont=MolContainer())
1093
1094
1095 - def are_bonded(self, atom_id1=None, atom_id2=None):
1096 """Determine if two atoms are directly bonded to each other.
1097
1098 @keyword atom_id1: The molecule, residue, and atom identifier string of the first atom.
1099 @type atom_id1: str
1100 @keyword atom_id2: The molecule, residue, and atom identifier string of the second atom.
1101 @type atom_id2: str
1102 @return: True if the atoms are directly bonded.
1103 @rtype: bool
1104 """
1105
1106
1107 sel_obj1 = Selection(atom_id1)
1108 sel_obj2 = Selection(atom_id2)
1109
1110
1111 for mol in self.structural_data[0].mol:
1112 for i in range(len(mol.atom_num)):
1113 if not len(mol.bonded[i]):
1114 self._find_bonded_atoms(i, mol, radius=2)
1115
1116
1117 for mol in self.structural_data[0].mol:
1118
1119 if not sel_obj1.contains_mol(mol.mol_name):
1120 continue
1121 if not sel_obj2.contains_mol(mol.mol_name):
1122 continue
1123
1124
1125 index1 = None
1126 for i in range(len(mol.atom_num)):
1127
1128 if sel_obj1.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1129 index1 = i
1130 break
1131
1132
1133 index2 = None
1134 for i in range(len(mol.atom_num)):
1135
1136 if sel_obj2.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1137 index2 = i
1138 break
1139
1140
1141 if index1 < len(mol.bonded):
1142 if index2 in mol.bonded[index1]:
1143 return True
1144 else:
1145 return False
1146
1147
1148 - def atom_loop(self, atom_id=None, str_id=None, model_num=None, model_num_flag=False, mol_name_flag=False, res_num_flag=False, res_name_flag=False, atom_num_flag=False, atom_name_flag=False, element_flag=False, pos_flag=False, index_flag=False, ave=False):
1149 """Generator function for looping over all atoms in the internal relax structural object.
1150
1151 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be yielded.
1152 @type atom_id: str
1153 @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.
1154 @type str_id: str, int, or None
1155 @keyword model_num: Only loop over a specific model.
1156 @type model_num: int or None
1157 @keyword model_num_flag: A flag which if True will cause the model number to be yielded.
1158 @type model_num_flag: bool
1159 @keyword mol_name_flag: A flag which if True will cause the molecule name to be yielded.
1160 @type mol_name_flag: bool
1161 @keyword res_num_flag: A flag which if True will cause the residue number to be yielded.
1162 @type res_num_flag: bool
1163 @keyword res_name_flag: A flag which if True will cause the residue name to be yielded.
1164 @type res_name_flag: bool
1165 @keyword atom_num_flag: A flag which if True will cause the atom number to be yielded.
1166 @type atom_num_flag: bool
1167 @keyword atom_name_flag: A flag which if True will cause the atom name to be yielded.
1168 @type atom_name_flag: bool
1169 @keyword element_flag: A flag which if True will cause the element name to be yielded.
1170 @type element_flag: bool
1171 @keyword pos_flag: A flag which if True will cause the atomic position to be yielded.
1172 @type pos_flag: bool
1173 @keyword index_flag: A flag which if True will cause the atomic index to be yielded.
1174 @type index_flag: bool
1175 @keyword ave: A flag which if True will result in this method returning the average atom properties across all loaded structures.
1176 @type ave: bool
1177 @return: A tuple of atomic information, as described in the docstring.
1178 @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).
1179 """
1180
1181
1182 if not len(self.structural_data):
1183 raise RelaxNoPdbError
1184
1185
1186 sel_obj = None
1187 if atom_id:
1188 sel_obj = Selection(atom_id)
1189
1190
1191 for model in self.model_loop(model_num):
1192
1193 for mol_index in range(len(model.mol)):
1194 mol = model.mol[mol_index]
1195
1196
1197 if sel_obj and not sel_obj.contains_mol(mol.mol_name):
1198 continue
1199
1200
1201 for i in range(len(mol.atom_name)):
1202
1203 if sel_obj and not sel_obj.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1204 continue
1205
1206
1207 res_num = mol.res_num[i]
1208 res_name = mol.res_name[i]
1209 atom_num = mol.atom_num[i]
1210 atom_name = mol.atom_name[i]
1211 element = mol.element[i]
1212 pos = zeros(3, float64)
1213
1214
1215 if ave:
1216
1217 for model in self.model_loop():
1218
1219 mol = model.mol[mol_index]
1220
1221
1222 if mol.atom_num[i] != atom_num:
1223 raise RelaxError("The loaded structures do not contain the same atoms. The average structural properties can not be calculated.")
1224
1225
1226 pos = pos + array([mol.x[i], mol.y[i], mol.z[i]], float64)
1227
1228
1229 pos = pos / len(self.structural_data)
1230 else:
1231 pos = array([mol.x[i], mol.y[i], mol.z[i]], float64)
1232
1233
1234 mol_name = mol.mol_name
1235
1236
1237 atomic_tuple = ()
1238 if model_num_flag:
1239 if ave:
1240 atomic_tuple = atomic_tuple + (None,)
1241 else:
1242 atomic_tuple = atomic_tuple + (model.num,)
1243 if mol_name_flag:
1244 atomic_tuple = atomic_tuple + (mol_name,)
1245 if res_num_flag:
1246 atomic_tuple = atomic_tuple + (res_num,)
1247 if res_name_flag:
1248 atomic_tuple = atomic_tuple + (res_name,)
1249 if atom_num_flag:
1250 atomic_tuple = atomic_tuple + (atom_num,)
1251 if atom_name_flag:
1252 atomic_tuple = atomic_tuple + (atom_name,)
1253 if element_flag:
1254 atomic_tuple = atomic_tuple + (element,)
1255 if pos_flag:
1256 atomic_tuple = atomic_tuple + (pos,)
1257 if index_flag:
1258 atomic_tuple += (i,)
1259
1260
1261 if len(atomic_tuple) == 1:
1262 atomic_tuple = atomic_tuple[0]
1263 yield atomic_tuple
1264
1265
1266 if ave:
1267 break
1268
1269
1270 - 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):
1271 """Find the bond vector between the atoms of 'attached_atom' and 'atom_id'.
1272
1273 @keyword attached_atom: The name of the bonded atom.
1274 @type attached_atom: str
1275 @keyword model_num: The model of which to return the vectors from. If not supplied and multiple models exist, then vectors from all models will be returned.
1276 @type model_num: None or int
1277 @keyword mol_name: The name of the molecule that attached_atom belongs to.
1278 @type mol_name: str
1279 @keyword res_num: The number of the residue that attached_atom belongs to.
1280 @type res_num: str
1281 @keyword res_name: The name of the residue that attached_atom belongs to.
1282 @type res_name: str
1283 @keyword spin_num: The number of the spin that attached_atom is attached to.
1284 @type spin_num: str
1285 @keyword spin_name: The name of the spin that attached_atom is attached to.
1286 @type spin_name: str
1287 @keyword return_name: A flag which if True will cause the name of the attached atom to be returned together with the bond vectors.
1288 @type return_name: bool
1289 @keyword return_warnings: A flag which if True will cause warning messages to be returned.
1290 @type return_warnings: bool
1291 @return: The list of bond vectors for each model.
1292 @rtype: list of numpy arrays (or a tuple if return_name or return_warnings are set)
1293 """
1294
1295
1296 vectors = []
1297 attached_name = None
1298 warnings = None
1299
1300
1301 for model in self.model_loop(model_num):
1302
1303 for mol in model.mol:
1304
1305 if mol_name and mol_name != mol.mol_name:
1306 continue
1307
1308
1309 index = None
1310 for i in range(len(mol.atom_name)):
1311
1312 if (res_num != None and mol.res_num[i] != res_num) or (res_name != None and mol.res_name[i] != res_name):
1313 continue
1314
1315
1316 if (spin_num != None and mol.atom_num[i] != spin_num) or (spin_name != None and mol.atom_name[i] != spin_name):
1317 continue
1318
1319
1320 index = i
1321 break
1322
1323
1324 if index != None:
1325
1326 bonded_num, bonded_name, element, pos, attached_name, warnings = self._bonded_atom(attached_atom, index, mol)
1327
1328
1329 if (bonded_num, bonded_name, element) == (None, None, None):
1330 continue
1331
1332
1333 vector = array(pos, float64) - array([mol.x[index], mol.y[index], mol.z[index]], float64)
1334
1335
1336 vectors.append(vector)
1337
1338
1339 else:
1340 warnings = "Cannot find the atom in the structure"
1341
1342
1343 data = (vectors,)
1344 if return_name:
1345 data = data + (attached_name,)
1346 if return_warnings:
1347 data = data + (warnings,)
1348
1349
1350 return data
1351
1352
1353 - def connect_atom(self, mol_name=None, index1=None, index2=None):
1354 """Connect two atoms in the structural data object.
1355
1356 @keyword mol_name: The name of the molecule.
1357 @type mol_name: str
1358 @keyword index1: The global index of the first atom.
1359 @type index1: str
1360 @keyword index2: The global index of the first atom.
1361 @type index2: str
1362 """
1363
1364
1365 pipes.test()
1366
1367
1368 if self.get_molecule(mol_name) == None:
1369 self.add_molecule(name=mol_name)
1370
1371
1372 for model in self.structural_data:
1373
1374 mol = self.get_molecule(mol_name)
1375
1376
1377 mol.atom_connect(index1=index1, index2=index2)
1378
1379
1380 - def delete(self, atom_id=None):
1381 """Deletion of structural information.
1382
1383 @keyword atom_id: The molecule, residue, and atom identifier string. This matches the spin ID string format. If not given, then all structural data will be deleted.
1384 @type atom_id: str or None
1385 """
1386
1387
1388 if atom_id == None:
1389
1390 print("Deleting the following structural data:\n")
1391 print(self.structural_data)
1392
1393
1394 del self.structural_data
1395
1396
1397 self.structural_data = ModelList()
1398
1399
1400 else:
1401
1402 sel_obj = None
1403 if atom_id:
1404 sel_obj = Selection(atom_id)
1405
1406
1407 del_res_nums = []
1408 for model in self.model_loop():
1409
1410 for mol_index in range(len(model.mol)):
1411 mol = model.mol[mol_index]
1412
1413
1414 if sel_obj and not sel_obj.contains_mol(mol.mol_name):
1415 continue
1416
1417
1418 indices = []
1419 for i in self.atom_loop(atom_id=atom_id, model_num=model.num, index_flag=True):
1420 indices.append(i)
1421
1422
1423 res_data = self._residue_data(res_nums=mol.res_num, res_names=mol.res_name)
1424
1425
1426 indices.reverse()
1427 for i in indices:
1428 mol.atom_num.pop(i)
1429 mol.atom_name.pop(i)
1430 mol.bonded.pop(i)
1431 mol.chain_id.pop(i)
1432 mol.element.pop(i)
1433 mol.pdb_record.pop(i)
1434 mol.res_name.pop(i)
1435 res_num = mol.res_num.pop(i)
1436 mol.seg_id.pop(i)
1437 mol.x.pop(i)
1438 mol.y.pop(i)
1439 mol.z.pop(i)
1440
1441
1442 if res_num not in mol.res_num and res_num not in del_res_nums:
1443 del_res_nums.append(res_num)
1444
1445
1446 if not len(del_res_nums):
1447 return
1448
1449
1450 del_res_nums.reverse()
1451
1452
1453 del_helix_indices = []
1454 for i in range(len(self.helices)):
1455
1456 helix = self._trim_helix(helix=self.helices[i], trim_res_list=del_res_nums, res_data=res_data)
1457
1458
1459 if helix != None:
1460 self.helices[i] = helix
1461
1462
1463 else:
1464 del_helix_indices.append(i)
1465
1466
1467 del_helix_indices.reverse()
1468 for i in del_helix_indices:
1469 self.helices.pop(i)
1470
1471
1472 del_sheet_indices = []
1473 for i in range(len(self.sheets)):
1474
1475 sheet = self._trim_sheet(sheet=self.sheets[i], trim_res_list=del_res_nums, res_data=res_data)
1476
1477
1478 if sheet != None:
1479 self.sheets[i] = sheet
1480
1481
1482 else:
1483 del_sheet_indices.append(i)
1484
1485
1486 del_sheet_indices.reverse()
1487 for i in del_sheet_indices:
1488 self.sheets.pop(i)
1489
1490
1492 """Return the molecule.
1493
1494 Only one model can be specified.
1495
1496
1497 @param molecule: The molecule name.
1498 @type molecule: int or None
1499 @keyword model: The model number.
1500 @type model: int or None
1501 @raises RelaxError: If the model is not specified and there is more than one model loaded.
1502 @return: The MolContainer corresponding to the molecule name and model number.
1503 @rtype: MolContainer instance or None
1504 """
1505
1506
1507 if model == None and self.num_models() > 1:
1508 raise RelaxError("The target molecule cannot be determined as there are %s models already present." % self.num_models())
1509
1510
1511 if not isinstance(model, int) and not model == None:
1512 raise RelaxNoneIntError
1513
1514
1515 if not len(self.structural_data):
1516 return
1517
1518
1519 for model_cont in self.model_loop(model):
1520
1521 for mol in model_cont.mol:
1522
1523 if mol.mol_name == molecule:
1524 return mol
1525
1526
1527 - def load_pdb(self, file_path, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, alt_loc=None, verbosity=False, merge=False):
1528 """Method for loading structures from a PDB file.
1529
1530 @param file_path: The full path of the PDB file.
1531 @type file_path: str
1532 @keyword read_mol: The molecule(s) to read from the file, independent of model. The molecules are determined differently by the different parsers, but are numbered consecutively from 1. If set to None, then all molecules will be loaded.
1533 @type read_mol: None, int, or list of int
1534 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, then the molecules will be automatically labelled based on the file name or other information.
1535 @type set_mol_name: None, str, or list of str
1536 @keyword read_model: The PDB model to extract from the file. If set to None, then all models will be loaded.
1537 @type read_model: None, int, or list of int
1538 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then the PDB model numbers will be preserved, if they exist.
1539 @type set_model_num: None, int, or list of int
1540 @keyword alt_loc: The PDB ATOM record 'Alternate location indicator' field value to select which coordinates to use.
1541 @type alt_loc: str or None
1542 @keyword verbosity: A flag which if True will cause messages to be printed.
1543 @type verbosity: bool
1544 @keyword merge: A flag which if set to True will try to merge the PDB structure into the currently loaded structures.
1545 @type merge: bool
1546 @return: The status of the loading of the PDB file.
1547 @rtype: bool
1548 """
1549
1550
1551 if verbosity:
1552 print("\nInternal relax PDB parser.")
1553
1554
1555 if not access(file_path, F_OK):
1556
1557 return False
1558
1559
1560 path, file = os.path.split(file_path)
1561
1562
1563 if read_mol and not isinstance(read_mol, list):
1564 read_mol = [read_mol]
1565 if set_mol_name and not isinstance(set_mol_name, list):
1566 set_mol_name = [set_mol_name]
1567 if read_model and not isinstance(read_model, list):
1568 read_model = [read_model]
1569 if set_model_num and not isinstance(set_model_num, list):
1570 set_model_num = [set_model_num]
1571
1572
1573 pdb_file = open_read_file(file_path)
1574 pdb_lines = pdb_file.readlines()
1575 pdb_file.close()
1576
1577
1578 if pdb_lines == []:
1579 raise RelaxError("The PDB file is empty.")
1580
1581
1582 pdb_lines = self._parse_pdb_title(pdb_lines)
1583 pdb_lines = self._parse_pdb_prim_struct(pdb_lines)
1584 pdb_lines = self._parse_pdb_hetrogen(pdb_lines)
1585 pdb_lines = self._parse_pdb_ss(pdb_lines)
1586 pdb_lines = self._parse_pdb_connectivity_annotation(pdb_lines)
1587 pdb_lines = self._parse_pdb_misc(pdb_lines)
1588 pdb_lines = self._parse_pdb_transform(pdb_lines)
1589
1590
1591 model_index = 0
1592 orig_model_num = []
1593 mol_conts = []
1594 for model_num, model_records in self._parse_pdb_coord(pdb_lines):
1595
1596 if read_model and model_num not in read_model:
1597 continue
1598
1599
1600 orig_model_num.append(model_num)
1601
1602
1603 mol_conts.append([])
1604 mol_index = 0
1605 orig_mol_num = []
1606 new_mol_name = []
1607 for mol_num, mol_records in self._parse_mols(model_records):
1608
1609 if read_mol and mol_num not in read_mol:
1610 continue
1611
1612
1613 if set_mol_name:
1614 new_mol_name.append(set_mol_name[mol_index])
1615 else:
1616
1617 num_struct = 0
1618 for model in self.structural_data:
1619 if not set_model_num or (model_index <= len(set_model_num) and set_model_num[model_index] == model.num):
1620 num_struct = len(model.mol)
1621
1622
1623 new_mol_name.append(file_root(file) + '_mol' + repr(mol_num+num_struct))
1624
1625
1626 orig_mol_num.append(mol_num)
1627
1628
1629 mol = MolContainer()
1630
1631
1632 mol.fill_object_from_pdb(mol_records, alt_loc_select=alt_loc)
1633
1634
1635 mol_conts[model_index].append(mol)
1636
1637
1638 mol_index = mol_index + 1
1639
1640
1641 model_index = model_index + 1
1642
1643
1644 if not len(mol_conts):
1645 warn(RelaxWarning("No structural data could be read from the file '%s'." % file_path))
1646 return False
1647
1648
1649 self.pack_structs(mol_conts, orig_model_num=orig_model_num, set_model_num=set_model_num, orig_mol_num=orig_mol_num, set_mol_name=new_mol_name, file_name=file, file_path=path, merge=merge)
1650
1651
1652 return True
1653
1654
1655 - def load_xyz(self, file_path, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, verbosity=False):
1656 """Method for loading structures from a XYZ file.
1657
1658 @param file_path: The full path of the XYZ file.
1659 @type file_path: str
1660 @keyword read_mol: The molecule(s) to read from the file, independent of model. The
1661 molecules are determined differently by the different parsers, but
1662 are numbered consecutively from 1. If set to None, then all
1663 molecules will be loaded.
1664 @type read_mol: None, int, or list of int
1665 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None,
1666 then the molecules will be automatically labelled based on the file
1667 name or other information.
1668 @type set_mol_name: None, str, or list of str
1669 @keyword read_model: The XYZ model to extract from the file. If set to None, then all
1670 models will be loaded.
1671 @type read_model: None, int, or list of int
1672 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then
1673 the XYZ model numbers will be preserved, if they exist.
1674 @type set_model_num: None, int, or list of int
1675 @keyword verbosity: A flag which if True will cause messages to be printed.
1676 @type verbosity: bool
1677 @return: The status of the loading of the XYZ file.
1678 @rtype: bool
1679 """
1680
1681
1682 if verbosity:
1683 print("\nInternal relax XYZ parser.")
1684
1685
1686 if not access(file_path, F_OK):
1687
1688 return False
1689
1690
1691 path, file = os.path.split(file_path)
1692
1693
1694 if read_mol and not isinstance(read_mol, list):
1695 read_mol = [read_mol]
1696 if set_mol_name and not isinstance(set_mol_name, list):
1697 set_mol_name = [set_mol_name]
1698 if read_model and not isinstance(read_model, list):
1699 read_model = [read_model]
1700 if set_model_num and not isinstance(set_model_num, list):
1701 set_model_num = [set_model_num]
1702
1703
1704 mol_index=0
1705 model_index = 0
1706 xyz_model_increment = 0
1707 orig_model_num = []
1708 mol_conts = []
1709 orig_mol_num = []
1710 new_mol_name = []
1711 for model_records in self._parse_models_xyz(file_path):
1712
1713 xyz_model_increment = xyz_model_increment +1
1714
1715
1716 if read_model and xyz_model_increment not in read_model:
1717 continue
1718
1719
1720 orig_model_num.append(model_index)
1721
1722
1723 if read_mol and mol_index not in read_mol:
1724 continue
1725
1726
1727 if set_mol_name:
1728 new_mol_name.append(set_mol_name[mol_index])
1729 else:
1730 if mol_index==0:
1731
1732 new_mol_name.append(file_root(file) + '_mol' + repr(mol_index+1))
1733
1734
1735 orig_mol_num.append(mol_index)
1736
1737
1738 mol = MolContainer()
1739
1740
1741 mol.fill_object_from_xyz(model_records)
1742
1743
1744 mol_conts.append([])
1745 mol_conts[model_index].append(mol)
1746
1747
1748 mol_index = mol_index + 1
1749
1750
1751 model_index = model_index + 1
1752
1753 orig_mol_num=[0]
1754
1755 self.pack_structs(mol_conts, orig_model_num=orig_model_num, set_model_num=set_model_num, orig_mol_num=orig_mol_num, set_mol_name=new_mol_name, file_name=file, file_path=path)
1756
1757
1758 return True
1759
1760
1761 - def rotate(self, R=None, origin=None, model=None, atom_id=None):
1762 """Rotate the structural information about the given origin.
1763
1764 @keyword R: The forwards rotation matrix.
1765 @type R: numpy 3D, rank-2 array
1766 @keyword origin: The origin of the rotation.
1767 @type origin: numpy 3D, rank-1 array
1768 @keyword model: The model to rotate. If None, all models will be rotated.
1769 @type model: int
1770 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
1771 @type atom_id: str or None
1772 """
1773
1774
1775 sel_obj = None
1776 if atom_id:
1777 sel_obj = Selection(atom_id)
1778
1779
1780 for model_cont in self.model_loop(model):
1781
1782 for mol in model_cont.mol:
1783
1784 if sel_obj and not sel_obj.contains_mol(mol.mol_name):
1785 continue
1786
1787
1788 for i in range(len(mol.atom_num)):
1789
1790 if sel_obj and not sel_obj.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1791 continue
1792
1793
1794 vect = array([mol.x[i], mol.y[i], mol.z[i]], float64) - origin
1795
1796
1797 rot_vect = dot(R, vect)
1798
1799
1800 pos = rot_vect + origin
1801 mol.x[i] = pos[0]
1802 mol.y[i] = pos[1]
1803 mol.z[i] = pos[2]
1804
1805
1806 - def translate(self, T=None, model=None, atom_id=None):
1807 """Displace the structural information by the given translation vector.
1808
1809 @keyword T: The translation vector.
1810 @type T: numpy 3D, rank-1 array
1811 @keyword model: The model to rotate. If None, all models will be rotated.
1812 @type model: int
1813 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
1814 @type atom_id: str or None
1815 """
1816
1817
1818 sel_obj = None
1819 if atom_id:
1820 sel_obj = Selection(atom_id)
1821
1822
1823 for model_cont in self.model_loop(model):
1824
1825 for mol in model_cont.mol:
1826
1827 if sel_obj and not sel_obj.contains_mol(mol.mol_name):
1828 continue
1829
1830
1831 for i in range(len(mol.atom_num)):
1832
1833 if sel_obj and not sel_obj.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1834 continue
1835
1836
1837 mol.x[i] = mol.x[i] + T[0]
1838 mol.y[i] = mol.y[i] + T[1]
1839 mol.z[i] = mol.z[i] + T[2]
1840
1841
1843 """Check that the models are consistent with each other.
1844
1845 This checks that the primary structure is identical between the models.
1846 """
1847
1848
1849 print("Validating models:")
1850
1851
1852 for i in range(len(self.structural_data)):
1853
1854 if len(self.structural_data[0].mol) != len(self.structural_data[i].mol):
1855 raise RelaxError("The number of molecules, %i, in model %i does not match the %i molecules of the first model." % (len(self.structural_data[i].mol), self.structural_data[i].num, len(self.structural_data[0].mol)))
1856
1857
1858 for j in range(len(self.structural_data[i].mol)):
1859
1860 mol = self.structural_data[i].mol[j]
1861 mol_ref = self.structural_data[0].mol[j]
1862
1863
1864 if mol.mol_name != mol_ref.mol_name:
1865 raise RelaxError("The molecule name '%s' of model %i does not match the name '%s' of the first model." % (mol.mol_name, self.structural_data[i].num, mol_ref.mol_name))
1866
1867
1868 for k in range(len(mol.atom_name)):
1869
1870 atom = "%-6s%5s %4s%1s%3s %1s%4s%1s %8s%8s%8s%6.2f%6.2f %4s%2s%2s" % ('ATOM', mol.atom_num[k], self._translate(mol.atom_name[k]), '', self._translate(mol.res_name[k]), self._translate(mol.chain_id[k]), self._translate(mol.res_num[k]), '', '#', '#', '#', 1.0, 0, self._translate(mol.seg_id[k]), self._translate(mol.element[k]), '')
1871 atom_ref = "%-6s%5s %4s%1s%3s %1s%4s%1s %8s%8s%8s%6.2f%6.2f %4s%2s%2s" % ('ATOM', mol_ref.atom_num[k], self._translate(mol_ref.atom_name[k]), '', self._translate(mol_ref.res_name[k]), self._translate(mol_ref.chain_id[k]), self._translate(mol_ref.res_num[k]), '', '#', '#', '#', 1.0, 0, self._translate(mol_ref.seg_id[k]), self._translate(mol_ref.element[k]), '')
1872
1873
1874 if atom != atom_ref:
1875 print(atom)
1876 print(atom_ref)
1877 raise RelaxError("The atoms of model %i do not match the first model." % self.structural_data[i].num)
1878
1879
1880 print("\tAll models are consistent")
1881
1882
1884 """Method for the creation of a PDB file from the structural data.
1885
1886 A number of PDB records including HET, HETNAM, FORMUL, HETATM, TER, CONECT, MASTER, and END
1887 are created. To create the non-standard residue records HET, HETNAM, and FORMUL, the data
1888 structure 'het_data' is created. It is an array of arrays where the first dimension
1889 corresponds to a different residue and the second dimension has the elements:
1890
1891 0. Residue number.
1892 1. Residue name.
1893 2. Chain ID.
1894 3. Total number of atoms in the residue.
1895 4. Number of H atoms in the residue.
1896 5. Number of C atoms in the residue.
1897
1898
1899 @param file: The PDB file object. This object must be writable.
1900 @type file: file object
1901 @keyword model_num: The model to place into the PDB file. If not supplied, then all
1902 models will be placed into the file.
1903 @type model_num: None or int
1904 """
1905
1906
1907 self.validate()
1908
1909
1910 num_hetatm = 0
1911 num_atom = 0
1912 num_ter = 0
1913 num_conect = 0
1914
1915
1916 print("\nCreating the PDB records\n")
1917
1918
1919 print("REMARK")
1920 pdb_write.remark(file, num=4, remark="This file complies with format v. 3.30, Jul-2011.")
1921 pdb_write.remark(file, num=40, remark="Created by relax (http://nmr-relax.com).")
1922 num_remark = 2
1923
1924
1925 model_records = False
1926 for model in self.model_loop():
1927 if hasattr(model, 'num') and model.num != None:
1928 model_records = True
1929
1930
1931
1932
1933
1934
1935
1936 het_data = []
1937 het_data_coll = []
1938
1939
1940 index = 0
1941 for mol in self.structural_data[0].mol:
1942
1943 self._validate_data_arrays(mol)
1944
1945
1946 het_data.append([])
1947
1948
1949 for i in range(len(mol.atom_name)):
1950
1951 if mol.pdb_record[i] != 'HETATM' or mol.res_name[i] == None:
1952 continue
1953
1954
1955
1956 if not het_data[index] or not mol.res_num[i] == het_data[index][-1][0]:
1957 het_data[index].append([mol.res_num[i], mol.res_name[i], mol.chain_id[i], 0, []])
1958
1959
1960 if het_data[index][-1][2] == None:
1961 het_data[index][-1][2] = ''
1962
1963
1964 het_data[index][-1][3] = het_data[index][-1][3] + 1
1965
1966
1967 entry = False
1968 for j in range(len(het_data[index][-1][4])):
1969 if mol.element[i] == het_data[index][-1][4][j][0]:
1970 entry = True
1971
1972
1973 if not entry:
1974 het_data[index][-1][4].append([mol.element[i], 0])
1975
1976
1977 for j in range(len(het_data[index][-1][4])):
1978 if mol.element[i] == het_data[index][-1][4][j][0]:
1979 het_data[index][-1][4][j][1] = het_data[index][-1][4][j][1] + 1
1980
1981
1982 for i in range(len(het_data[index])):
1983
1984 found = False
1985 for j in range(len(het_data_coll)):
1986
1987 if het_data[index][i][0] == het_data_coll[j][0]:
1988
1989 found = True
1990
1991
1992 if het_data_coll[j][1] != het_data[index][i][1]:
1993 raise RelaxError("The " + repr(het_data[index][i][1]) + " residue name of hetrogen " + repr(het_data[index][i][0]) + " " + het_data[index][i][1] + " of structure " + repr(index) + " does not match the " + repr(het_data_coll[j][1]) + " name of the previous structures.")
1994
1995 elif het_data_coll[j][2] != het_data[index][i][2]:
1996 raise RelaxError("The hetrogen chain id " + repr(het_data[index][i][2]) + " does not match " + repr(het_data_coll[j][2]) + " of residue " + repr(het_data_coll[j][0]) + " " + het_data_coll[j][1] + " of the previous structures.")
1997
1998 elif het_data_coll[j][3] != het_data[index][i][3]:
1999 raise RelaxError("The " + repr(het_data[index][i][3]) + " atoms of hetrogen " + repr(het_data_coll[j][0]) + " " + het_data_coll[j][1] + " of structure " + repr(index) + " does not match the " + repr(het_data_coll[j][3]) + " of the previous structures.")
2000
2001 elif het_data_coll[j][4] != het_data[index][i][4]:
2002 raise RelaxError("The atom counts " + repr(het_data[index][i][4]) + " for the hetrogen residue " + repr(het_data_coll[j][0]) + " " + het_data_coll[j][1] + " of structure " + repr(index) + " do not match the counts " + repr(het_data_coll[j][4]) + " of the previous structures.")
2003
2004
2005 if not found:
2006 het_data_coll.append(het_data[index][i])
2007
2008
2009 index = index + 1
2010
2011
2012
2013
2014
2015
2016 print("HET")
2017
2018
2019 for het in het_data_coll:
2020 pdb_write.het(file, het_id=het[1], chain_id=het[2], seq_num=het[0], num_het_atoms=het[3])
2021
2022
2023
2024
2025
2026
2027 print("HETNAM")
2028
2029
2030 residues = []
2031 for het in het_data_coll:
2032
2033 if het[1] in residues:
2034 continue
2035 else:
2036 residues.append(het[1])
2037
2038
2039 chemical_name = self._get_chemical_name(het[1])
2040 if not chemical_name:
2041 chemical_name = 'Unknown'
2042
2043
2044 pdb_write.hetnam(file, het_id=het[1], text=chemical_name)
2045
2046
2047
2048
2049
2050
2051 print("FORMUL")
2052
2053
2054 residues = []
2055 for i in range(len(het_data_coll)):
2056
2057 het = het_data_coll[i]
2058
2059
2060 if het[1] in residues:
2061 continue
2062 else:
2063 residues.append(het[1])
2064
2065
2066 formula = ''
2067
2068
2069 for atom_count in het[4]:
2070 formula = formula + atom_count[0] + repr(atom_count[1])
2071
2072
2073 pdb_write.formul(file, comp_num=i+1, het_id=het[1], text=formula)
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083 if hasattr(self, 'helices') and len(self.helices):
2084
2085 print("HELIX")
2086
2087
2088 index = 1
2089 for helix_id, init_chain_id, init_res_name, init_seq_num, end_chain_id, end_res_name, end_seq_num, helix_class, length in self.helices:
2090 pdb_write.helix(file, ser_num=index, helix_id=helix_id, init_chain_id=init_chain_id, init_res_name=init_res_name, init_seq_num=init_seq_num, end_chain_id=end_chain_id, end_res_name=end_res_name, end_seq_num=end_seq_num, helix_class=helix_class, length=length)
2091 index += 1
2092
2093
2094
2095
2096 if hasattr(self, 'sheets') and len(self.sheets):
2097
2098 print("SHEET")
2099
2100
2101 index = 1
2102 for strand, sheet_id, num_strands, init_res_name, init_chain_id, init_seq_num, init_icode, end_res_name, end_chain_id, end_seq_num, end_icode, sense, cur_atom, cur_res_name, cur_chain_id, cur_res_seq, cur_icode, prev_atom, prev_res_name, prev_chain_id, prev_res_seq, prev_icode in self.sheets:
2103 pdb_write.sheet(file, strand=strand, sheet_id=sheet_id, num_strands=num_strands, init_res_name=init_res_name, init_chain_id=init_chain_id, init_seq_num=init_seq_num, init_icode=init_icode, end_res_name=end_res_name, end_chain_id=end_chain_id, end_seq_num=end_seq_num, end_icode=end_icode, sense=sense, cur_atom=cur_atom, cur_res_name=cur_res_name, cur_chain_id=cur_chain_id, cur_res_seq=cur_res_seq, cur_icode=cur_icode, prev_atom=prev_atom, prev_res_name=prev_res_name, prev_chain_id=prev_chain_id, prev_res_seq=prev_res_seq, prev_icode=prev_icode)
2104 index += 1
2105
2106
2107
2108
2109
2110
2111
2112 for model in self.model_loop(model_num):
2113
2114
2115
2116 if model_records:
2117
2118 print("\nMODEL %s" % model.num)
2119
2120
2121 pdb_write.model(file, serial=model.num)
2122
2123
2124
2125
2126
2127
2128 for mol in model.mol:
2129
2130 print("ATOM, HETATM, TER")
2131
2132
2133 atom_record = False
2134 for i in range(len(mol.atom_name)):
2135
2136 if mol.pdb_record[i] in [None, 'ATOM']:
2137 atom_record = True
2138
2139
2140 atom_num = mol.atom_num[i]
2141 if atom_num == None:
2142 atom_num = i + 1
2143
2144
2145
2146 if len(mol.atom_name[i]) == 1:
2147 atom_name = " %s" % mol.atom_name[i]
2148 else:
2149 atom_name = "%s" % mol.atom_name[i]
2150
2151
2152 pdb_write.atom(file, serial=atom_num, name=atom_name, res_name=mol.res_name[i], chain_id=self._translate(mol.chain_id[i]), res_seq=mol.res_num[i], x=mol.x[i], y=mol.y[i], z=mol.z[i], occupancy=1.0, temp_factor=0, element=mol.element[i])
2153 num_atom = num_atom + 1
2154
2155
2156 ter_num = atom_num + 1
2157 ter_name = mol.res_name[i]
2158 ter_chain_id = mol.chain_id[i]
2159 ter_res_num = mol.res_num[i]
2160
2161
2162 if atom_record:
2163 pdb_write.ter(file, serial=ter_num, res_name=ter_name, chain_id=self._translate(ter_chain_id), res_seq=ter_res_num)
2164 num_ter = num_ter + 1
2165
2166
2167 count_shift = False
2168 for i in range(len(mol.atom_name)):
2169
2170 if mol.pdb_record[i] == 'HETATM':
2171
2172 atom_num = mol.atom_num[i]
2173 if atom_num == None:
2174 atom_num = i + 1
2175
2176
2177 if atom_record and atom_num == ter_num:
2178 count_shift = True
2179 if atom_record and count_shift:
2180 atom_num += 1
2181
2182
2183 pdb_write.hetatm(file, serial=atom_num, name=self._translate(mol.atom_name[i]), res_name=mol.res_name[i], chain_id=self._translate(mol.chain_id[i]), res_seq=mol.res_num[i], x=mol.x[i], y=mol.y[i], z=mol.z[i], occupancy=1.0, temp_factor=0.0, element=mol.element[i])
2184 num_hetatm = num_hetatm + 1
2185
2186
2187
2188
2189
2190 if model_records:
2191 print("ENDMDL")
2192 pdb_write.endmdl(file)
2193
2194
2195
2196
2197
2198
2199 print("CONECT")
2200
2201
2202 for mol in self.structural_data[0].mol:
2203
2204 for i in range(len(mol.atom_name)):
2205
2206 if not len(mol.bonded[i]):
2207 continue
2208
2209
2210 flush = 0
2211 bonded_index = 0
2212 bonded = ['', '', '', '']
2213
2214
2215 for j in range(len(mol.bonded[i])):
2216
2217 if j == len(mol.bonded[i])-1:
2218 flush = True
2219
2220
2221 if bonded_index == 3:
2222 flush = True
2223
2224
2225 bonded[bonded_index] = mol.bonded[i][j]
2226
2227
2228 bonded_index = bonded_index + 1
2229
2230
2231 if flush:
2232
2233 for k in range(4):
2234 if bonded[k] != '':
2235 if mol.atom_num[bonded[k]] != None:
2236 bonded[k] = mol.atom_num[bonded[k]]
2237 else:
2238 bonded[k] = bonded[k] + 1
2239
2240
2241 pdb_write.conect(file, serial=i+1, bonded1=bonded[0], bonded2=bonded[1], bonded3=bonded[2], bonded4=bonded[3])
2242
2243
2244 flush = False
2245 bonded_index = 0
2246 bonded = ['', '', '', '']
2247
2248
2249 num_conect = num_conect + 1
2250
2251
2252
2253
2254
2255
2256 print("\nMASTER")
2257 pdb_write.master(file, num_het=len(het_data_coll), num_coord=num_atom+num_hetatm, num_ter=num_ter, num_conect=num_conect)
2258
2259
2260
2261
2262
2263 print("END")
2264 pdb_write.end(file)
2265
2266
2268 """The container for the molecular information.
2269
2270 The structural data object for this class is a container possessing a number of different arrays
2271 corresponding to different structural information. These objects include:
2272
2273 - atom_num: The atom name.
2274 - atom_name: The atom name.
2275 - bonded: Each element an array of bonded atom indices.
2276 - chain_id: The chain ID.
2277 - element: The element symbol.
2278 - pdb_record: The optional PDB record name (one of ATOM, HETATM, or TER).
2279 - res_name: The residue name.
2280 - res_num: The residue number.
2281 - seg_id: The segment ID.
2282 - x: The x coordinate of the atom.
2283 - y: The y coordinate of the atom.
2284 - z: The z coordinate of the atom.
2285
2286 All arrays should be of equal length so that an atom index can retrieve all the corresponding
2287 data. Only the atom identification string is compulsory, all other arrays can contain None.
2288 """
2289
2290
2292 """Initialise the molecular container."""
2293
2294
2295 self.atom_num = []
2296
2297
2298 self.atom_name = []
2299
2300
2301 self.bonded = []
2302
2303
2304 self.chain_id = []
2305
2306
2307 self.element = []
2308
2309
2310 self.pdb_record = []
2311
2312
2313 self.res_name = []
2314
2315
2316 self.res_num = []
2317
2318
2319 self.seg_id = []
2320
2321
2322 self.x = []
2323
2324
2325 self.y = []
2326
2327
2328 self.z = []
2329
2330
2332 """Find the atom index corresponding to the given atom number.
2333
2334 @param atom_num: The atom number to find the index of.
2335 @type atom_num: int
2336 @return: The atom index corresponding to the atom.
2337 @rtype: int
2338 """
2339
2340
2341 for j in range(len(self.atom_num)):
2342
2343 if self.atom_num[j] == atom_num:
2344 return j
2345
2346
2347 warn(RelaxWarning("The atom number " + repr(atom_num) + " from the CONECT record cannot be found within the ATOM and HETATM records."))
2348
2349
2351 """Try to determine the element from the PDB atom name.
2352
2353 @param atom_name: The PDB atom name.
2354 @type atom_name: str
2355 @return: The element name, or None if unsuccessful.
2356 @rtype: str or None
2357 """
2358
2359
2360 element = atom_name.strip("'")
2361
2362
2363 element = element.strip(digits)
2364
2365
2366 table = {'C': ['CA', 'CB', 'CG', 'CD', 'CE', 'CH', 'CZ'],
2367 'N': ['ND', 'NE', 'NH', 'NZ'],
2368 'H': ['HA', 'HB', 'HG', 'HD', 'HE', 'HH', 'HT', 'HZ'],
2369 'O': ['OG', 'OD', 'OE', 'OH', 'OT'],
2370 'S': ['SD', 'SG']
2371 }
2372
2373
2374 for key in list(table.keys()):
2375 if element in table[key]:
2376 element = key
2377 break
2378
2379
2380 elements = ['H', 'C', 'N', 'O', 'F', 'P', 'S']
2381
2382
2383 if element in elements:
2384 return element
2385
2386
2387 warn(RelaxWarning("Cannot determine the element associated with atom '%s'." % atom_name))
2388
2389
2391 """Parse the XYZ record string and return an array of the corresponding atomic information.
2392
2393 The format of the XYZ records is::
2394 __________________________________________________________________________________________
2395 | | | | |
2396 | Columns | Data type | Field | Definition |
2397 |_________|______________|______________|________________________________________________|
2398 | | | | |
2399 | 1 | String | element | |
2400 | 2 | Real | x | Orthogonal coordinates for X in Angstroms |
2401 | 3 | Real | y | Orthogonal coordinates for Y in Angstroms |
2402 | 4 | Real | z | Orthogonal coordinates for Z in Angstroms |
2403 |_________|______________|______________|________________________________________________|
2404
2405
2406 @param record: The single line PDB record.
2407 @type record: str
2408 @return: The list of atomic information
2409 @rtype: list of str
2410 """
2411
2412
2413 fields = []
2414 word = record.split()
2415
2416
2417 if len(word)==4:
2418
2419 fields.append(word[0])
2420 fields.append(word[1])
2421 fields.append(word[2])
2422 fields.append(word[3])
2423
2424
2425 for i in range(len(fields)):
2426
2427 fields[i] = fields[i].strip()
2428
2429
2430 if fields[i] == '':
2431 fields[i] = None
2432
2433
2434 if fields[1]:
2435 fields[1] = float(fields[1])
2436 if fields[2]:
2437 fields[2] = float(fields[2])
2438 if fields[3]:
2439 fields[3] = float(fields[3])
2440
2441
2442 return fields
2443
2444
2445 - def atom_add(self, 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):
2446 """Method for adding an atom to the structural data object.
2447
2448 This method will create the key-value pair for the given atom.
2449
2450
2451 @keyword atom_name: The atom name, e.g. 'H1'.
2452 @type atom_name: str or None
2453 @keyword res_name: The residue name.
2454 @type res_name: str or None
2455 @keyword res_num: The residue number.
2456 @type res_num: int or None
2457 @keyword pos: The position vector of coordinates.
2458 @type pos: list (length = 3)
2459 @keyword element: The element symbol.
2460 @type element: str or None
2461 @keyword atom_num: The atom number.
2462 @type atom_num: int or None
2463 @keyword chain_id: The chain identifier.
2464 @type chain_id: str or None
2465 @keyword segment_id: The segment identifier.
2466 @type segment_id: str or None
2467 @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'.
2468 @type pdb_record: str or None
2469 @return: The index of the added atom.
2470 @rtype: int
2471 """
2472
2473
2474 self.atom_num.append(atom_num)
2475 self.atom_name.append(atom_name)
2476 self.bonded.append([])
2477 self.chain_id.append(chain_id)
2478 self.element.append(element)
2479 self.pdb_record.append(pdb_record)
2480 self.res_name.append(res_name)
2481 self.res_num.append(res_num)
2482 self.seg_id.append(segment_id)
2483 self.x.append(pos[0])
2484 self.y.append(pos[1])
2485 self.z.append(pos[2])
2486
2487
2488 return len(self.atom_num) - 1
2489
2490
2492 """Method for connecting two atoms within the data structure object.
2493
2494 This method will append index2 to the array at bonded[index1] and vice versa.
2495
2496
2497 @keyword index1: The index of the first atom.
2498 @type index1: int
2499 @keyword index2: The index of the second atom.
2500 @type index2: int
2501 """
2502
2503
2504 if index2 not in self.bonded[index1]:
2505 self.bonded[index1].append(index2)
2506 if index1 not in self.bonded[index2]:
2507 self.bonded[index2].append(index1)
2508
2509
2511 """Method for generating a complete Structure_container object from the given PDB records.
2512
2513 @param records: A list of structural PDB records.
2514 @type records: list of str
2515 @keyword alt_loc_select: The PDB ATOM record 'Alternate location indicator' field value to select which coordinates to use.
2516 @type alt_loc_select: str or None
2517 """
2518
2519
2520 for record in records:
2521
2522 if not record or record == '\n':
2523 continue
2524
2525
2526 if record[:4] == 'ATOM' or record[:6] == 'HETATM':
2527
2528 if record[:4] == 'ATOM':
2529 record_type, serial, name, alt_loc, res_name, chain_id, res_seq, icode, x, y, z, occupancy, temp_factor, element, charge = pdb_read.atom(record)
2530 if record[:6] == 'HETATM':
2531 record_type, serial, name, alt_loc, res_name, chain_id, res_seq, icode, x, y, z, occupancy, temp_factor, element, charge = pdb_read.hetatm(record)
2532
2533
2534 if alt_loc != None:
2535
2536 if alt_loc_select == None:
2537 raise RelaxError("Multiple alternate location indicators are present in the PDB file, but the desired coordinate set has not been specified.")
2538
2539
2540 if alt_loc != alt_loc_select:
2541 continue
2542
2543
2544 if not element:
2545 element = self._det_pdb_element(name)
2546
2547
2548 self.atom_add(pdb_record=record_type, atom_num=serial, atom_name=name, res_name=res_name, chain_id=chain_id, res_num=res_seq, pos=[x, y, z], element=element)
2549
2550
2551 if record[:6] == 'CONECT':
2552
2553 record_type, serial, bonded1, bonded2, bonded3, bonded4 = pdb_read.conect(record)
2554
2555
2556 for bonded in [bonded1, bonded2, bonded3, bonded4]:
2557
2558 if not bonded:
2559 continue
2560
2561
2562 if self._atom_index(serial) == None or self._atom_index(bonded) == None:
2563 continue
2564
2565
2566 self.atom_connect(index1=self._atom_index(serial), index2=self._atom_index(bonded))
2567
2568
2570 """Method for generating a complete Structure_container object from the given xyz records.
2571
2572 @param records: A list of structural xyz records.
2573 @type records: list of str
2574 """
2575
2576
2577 atom_number = 1
2578
2579
2580 for record in records:
2581
2582 record = self._parse_xyz_record(record)
2583
2584
2585 if not record:
2586 continue
2587
2588
2589 if len(record) == 4:
2590
2591 self.atom_add(atom_name=record[0], atom_num=atom_number, pos=[record[1], record[2], record[3]], element=record[0])
2592
2593
2594 atom_number = atom_number + 1
2595
2596
2597 - def from_xml(self, mol_node, file_version=1):
2598 """Recreate the MolContainer from the XML molecule node.
2599
2600 @param mol_node: The molecule XML node.
2601 @type mol_node: xml.dom.minicompat.NodeList instance
2602 @keyword file_version: The relax XML version of the XML file.
2603 @type file_version: int
2604 """
2605
2606
2607 xml_to_object(mol_node, self, file_version=file_version)
2608
2609
2611 """Check if the container is empty."""
2612
2613
2614 if hasattr(self, 'mol_name'): return False
2615 if hasattr(self, 'file_name'): return False
2616 if hasattr(self, 'file_path'): return False
2617 if hasattr(self, 'file_mol_num'): return False
2618 if hasattr(self, 'file_model'): return False
2619
2620
2621 if not self.atom_num == []: return False
2622 if not self.atom_name == []: return False
2623 if not self.bonded == []: return False
2624 if not self.chain_id == []: return False
2625 if not self.element == []: return False
2626 if not self.pdb_record == []: return False
2627 if not self.res_name == []: return False
2628 if not self.res_num == []: return False
2629 if not self.seg_id == []: return False
2630 if not self.x == []: return False
2631 if not self.y == []: return False
2632 if not self.z == []: return False
2633
2634
2635 return True
2636
2637
2639 """Return the number of the last residue.
2640
2641 @return: The last residue number.
2642 @rtype: int
2643 """
2644
2645
2646 return self.res_num[-1]
2647
2648
2649 - def merge(self, mol_cont=None):
2650 """Merge the contents of the given molecule container into here.
2651
2652 @keyword mol_cont: The data structure for the molecule to merge.
2653 @type mol_cont: MolContainer instance
2654 """
2655
2656
2657 curr_index = len(self.atom_num)
2658
2659
2660 for i in range(len(mol_cont.atom_num)):
2661
2662 self.atom_add(atom_num=curr_index+i+1, atom_name=mol_cont.atom_name[i], res_name=mol_cont.res_name[i], res_num=mol_cont.res_num[i], pos=[mol_cont.x[i], mol_cont.y[i], mol_cont.z[i]], element=mol_cont.element[i], chain_id=mol_cont.chain_id[i], pdb_record=mol_cont.pdb_record[i])
2663
2664
2665 for j in range(len(mol_cont.bonded[i])):
2666 self.atom_connect(index1=i+curr_index+1, index2=mol_cont.bonded[i][j]+curr_index+1)
2667
2668
2669 - def to_xml(self, doc, element):
2670 """Create XML elements for the contents of this molecule container.
2671
2672 @param doc: The XML document object.
2673 @type doc: xml.dom.minidom.Document instance
2674 @param element: The element to add the molecule XML elements to.
2675 @type element: XML element object
2676 """
2677
2678
2679 mol_element = doc.createElement('mol_cont')
2680 element.appendChild(mol_element)
2681
2682
2683 mol_element.setAttribute('desc', 'Molecule container')
2684 mol_element.setAttribute('name', str(self.mol_name))
2685
2686
2687 fill_object_contents(doc, mol_element, object=self, blacklist=list(self.__class__.__dict__.keys()))
2688