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, curdir, sep
30 from os.path import abspath
31 from re import search
32 import sys
33 from time import asctime
34 from warnings import warn
35
36
37 from lib import regex
38 from lib.check_types import is_float
39 from lib.errors import RelaxError, RelaxFault, RelaxNoneIntError, RelaxNoPdbError
40 from lib.io import file_root, open_read_file
41 from lib.selection import Selection, tokenise
42 from lib.sequence import aa_codes_three_to_one
43 from lib.structure import pdb_read, pdb_write
44 from lib.structure.internal.displacements import Displacements
45 from lib.structure.internal.models import ModelList
46 from lib.structure.internal.molecules import MolContainer
47 from lib.structure.internal.selection import Internal_selection
48 from lib.warnings import RelaxWarning
49 from lib.xml import fill_object_contents, object_to_xml, xml_to_object
50
51
52
53 CHAIN_ID_LIST = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'
54 RELAX_VERSION = None
55
56
58 """The internal relax structural data object.
59
60 The structural data object for this class is a container possessing a number of different arrays corresponding to different structural information. These objects are described in the structural container docstring.
61 """
62
64 """Initialise the structural object."""
65
66
67 self.structural_data = ModelList()
68
69
71 """Find the atom named attached_atom directly bonded to the atom located at the index.
72
73 @param attached_atom: The name of the attached atom to return.
74 @type attached_atom: str
75 @param index: The index of the atom which the attached atom is attached to.
76 @type index: int
77 @param mol: The molecule container.
78 @type mol: MolContainer instance
79 @return: A tuple of information about the bonded atom.
80 @rtype: tuple consisting of the atom number (int), atom name (str), element name (str), and atomic position (Numeric array of len 3)
81 """
82
83
84 bonded_found = False
85
86
87 if not mol.bonded[index]:
88
89 if not hasattr(mol, 'type'):
90 self._mol_type(mol)
91
92
93 if mol.type == 'protein':
94 self._protein_connect(mol)
95
96
97 else:
98 self._find_bonded_atoms(index, mol, radius=2)
99
100
101 matching_list = []
102 for bonded_index in mol.bonded[index]:
103 if regex.search(mol.atom_name[bonded_index], attached_atom):
104 matching_list.append(bonded_index)
105 num_attached = len(matching_list)
106
107
108 if num_attached > 1:
109
110 matching_names = []
111 for i in matching_list:
112 matching_names.append(mol.atom_name[i])
113
114
115 return None, None, None, None, None, 'More than one attached atom found: ' + repr(matching_names)
116
117
118 if num_attached == 0:
119 return None, None, None, None, None, "No attached atom could be found"
120
121
122 index = matching_list[0]
123 bonded_num = mol.atom_num[index]
124 bonded_name = mol.atom_name[index]
125 element = mol.element[index]
126 pos = [mol.x[index], mol.y[index], mol.z[index]]
127 attached_name = mol.atom_name[index]
128
129
130 return bonded_num, bonded_name, element, pos, attached_name, None
131
132
134 """Find all atoms within a sphere and say that they are attached to the central atom.
135
136 The found atoms will be added to the 'bonded' data structure.
137
138
139 @param index: The index of the central atom.
140 @type index: int
141 @param mol: The molecule container.
142 @type mol: MolContainer instance
143 """
144
145
146 centre = array([mol.x[index], mol.y[index], mol.z[index]], float64)
147
148
149 dist_list = []
150 connect_list = {}
151 element_list = {}
152 for i in range(len(mol.atom_num)):
153
154 if mol.element[index] == 'H' and mol.element[i] == 'H':
155 continue
156
157
158 pos = array([mol.x[i], mol.y[i], mol.z[i]], float64)
159
160
161 dist = linalg.norm(centre-pos)
162
163
164 if dist < radius:
165
166 dist_list.append(dist)
167
168
169 connect_list[dist] = i
170
171
172 element_list[dist] = mol.element[i]
173
174
175 max_conn = 1000
176 if mol.element[index] == 'H':
177 max_conn = 1
178 elif mol.element[index] == 'O':
179 max_conn = 2
180 elif mol.element[index] == 'N':
181 max_conn = 3
182 elif mol.element[index] == 'C':
183 max_conn = 4
184
185
186 dist_list.sort()
187
188
189 for i in range(min(max_conn, len(dist_list))):
190 mol.atom_connect(index, connect_list[dist_list[i]])
191
192
194 """Return the chemical name corresponding to the given residue ID.
195
196 The following names are currently returned::
197 ___________________________________________________________
198 | | |
199 | hetID | Chemical name |
200 |________|________________________________________________|
201 | | |
202 | TNS | Tensor |
203 | COM | Centre of mass |
204 | AXS | Tensor axes |
205 | SIM | Monte Carlo simulation tensor axes |
206 | PIV | Pivot point |
207 | AVE | Average vector |
208 | RTX | Axis of the rotor geometric object |
209 | RTB | Propeller blades of the rotor geometric object |
210 | RTL | Labels for the rotor geometric object |
211 | CON | Cone object |
212 | CNC | Apex or centre of the cone geometric object |
213 | CNX | Axis of the cone geometric object |
214 | CNE | Edge of the cone geometric object |
215 | AXE | The axis geometric object |
216 | TLE | The title for the object |
217 |________|________________________________________________|
218
219 For any other residues, no description is returned.
220
221 @param hetID: The residue ID.
222 @type hetID: str
223 @return: The chemical name.
224 @rtype: str or None
225 """
226
227
228 table = {
229 "TNS": "Tensor",
230 "COM": "Centre of mass",
231 "AXS": "Tensor axes",
232 "SIM": "Monte Carlo simulation tensor axes",
233 "PIV": "Pivot point",
234 "AVE": "Average vector",
235 "RTX": "Axis of the rotor geometric object",
236 "RTB": "Propeller blades of the rotor geometric object",
237 "RTL": "Labels for the rotor geometric object",
238 "CON": "Cone object",
239 "CNC": "Apex or centre of the cone geometric object",
240 "CNX": "Axis of the cone geometric object",
241 "CNE": "Edge of the cone geometric object",
242 "AXE": "The axis geometric object",
243 "TLE": "The title for the object",
244 }
245
246
247 if hetID in table:
248 return table[hetID]
249
250
252 """Generator function for looping over the models in the Gaussian log file.
253
254 @param file_path: The full path of the Gaussian log file.
255 @type file_path: str
256 @return: The model number and all the records for that model.
257 @rtype: tuple of int and array of str
258 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
259 @type verbosity: int
260 """
261
262
263 file = open_read_file(file_path, verbosity=verbosity)
264 lines = file.readlines()
265 file.close()
266
267
268 if lines == []:
269 raise RelaxError("The Gaussian log file is empty.")
270
271
272 found = False
273 str_index = 0
274 total_atom = 0
275 model = 0
276 records = []
277
278
279 for i in range(len(lines)):
280
281 if search("Standard orientation", lines[i]):
282 found = True
283 str_index = 0
284 continue
285
286
287 if found and str_index > 4 and search("---------", lines[i]):
288
289 yield records
290
291
292 records = []
293 found = False
294
295
296 if not found:
297 continue
298
299
300 records.append(lines[i])
301
302
303 str_index += 1
304
305
307 """Loop over and parse the PDB connectivity annotation records.
308
309 These are the records identified in the U{PDB version 3.30 documentation<http://www.wwpdb.org/documentation/file-format/format33/sect6.html>}.
310
311
312 @param lines: The lines of the PDB file excluding the sections prior to the connectivity annotation section.
313 @type lines: list of str
314 @return: The remaining PDB lines with the connectivity annotation records stripped.
315 @rtype: list of str
316 """
317
318
319 records = [
320 'SSBOND',
321 'LINK ',
322 'CISPEP'
323 ]
324
325
326 for i in range(len(lines)):
327
328 if lines[i][:6] not in records:
329 break
330
331
332 return lines[i:]
333
334
336 """Generator function for looping over the models in the PDB file.
337
338 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/file-format/format33/sect9.html}.
339
340
341 @param lines: The lines of the coordinate section.
342 @type lines: list of str
343 @return: The model number and all the records for that model.
344 @rtype: tuple of int and array of str
345 """
346
347
348 model = None
349 records = []
350
351
352 for i in range(len(lines)):
353
354 if lines[i][:5] == 'MODEL':
355 try:
356 model = int(lines[i].split()[1])
357 except:
358 raise RelaxError("The MODEL record " + repr(lines[i]) + " is corrupt, cannot read the PDB file.")
359
360
361 if not (lines[i][:4] == 'ATOM' or lines[i][:6] == 'HETATM') and not len(records):
362 continue
363
364
365 if lines[i][:6] == 'ENDMDL':
366
367 yield model, records
368
369
370 records = []
371
372
373 continue
374
375
376 records.append(lines[i])
377
378
379 if len(records):
380 yield model, records
381
382
384 """Loop over and parse the PDB hetrogen records.
385
386 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/file-format/format33/sect4.html}.
387
388
389 @param lines: The lines of the PDB file excluding the sections prior to the hetrogen section.
390 @type lines: list of str
391 @return: The remaining PDB lines with the hetrogen records stripped.
392 @rtype: list of str
393 """
394
395
396 records = [
397 'HET ',
398 'FORMUL',
399 'HETNAM',
400 'HETSYN'
401 ]
402
403
404 for i in range(len(lines)):
405
406 if lines[i][:6] not in records:
407 break
408
409
410 return lines[i:]
411
412
414 """Loop over and parse the PDB miscellaneous records.
415
416 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/file-format/format33/sect7.html}.
417
418
419 @param lines: The lines of the PDB file excluding the sections prior to the miscellaneous section.
420 @type lines: list of str
421 @return: The remaining PDB lines with the miscellaneous records stripped.
422 @rtype: list of str
423 """
424
425
426 records = [
427 'SITE '
428 ]
429
430
431 for i in range(len(lines)):
432
433 if lines[i][:6] not in records:
434 break
435
436
437 return lines[i:]
438
439
441 """Loop over and parse the PDB primary structure records.
442
443 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/file-format/format33/sect3.html}.
444
445
446 @param lines: The lines of the PDB file excluding the title section.
447 @type lines: list of str
448 @return: The remaining PDB lines with the primary structure records stripped.
449 @rtype: list of str
450 """
451
452
453 records = [
454 'DBREF ',
455 'DBREF1',
456 'DBREF2',
457 'SEQADV',
458 'SEQRES',
459 'MODRES'
460 ]
461
462
463 for i in range(len(lines)):
464
465 if lines[i][:6] not in records:
466 break
467
468
469 return lines[i:]
470
471
472 - def _parse_pdb_ss(self, lines, read_mol=None, helices=None, sheets=None):
473 """Loop over and parse the PDB secondary structure records.
474
475 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/file-format/format33/sect5.html}.
476
477
478 @param lines: The lines of the PDB file excluding the sections prior to the secondary structure section.
479 @type lines: list of str
480 @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.
481 @type read_mol: None, int, or list of int
482 @keyword helices: The list of helix data to append to.
483 @type helices: list
484 @keyword sheets: The list of sheet data to append to.
485 @type sheets: list
486 @return: The remaining PDB lines with the secondary structure records stripped.
487 @rtype: list of str
488 """
489
490
491 records = [
492 'HELIX ',
493 'SHEET ',
494 'TURN '
495 ]
496
497
498 if not len(self.structural_data):
499 mol_num = 0
500 else:
501 mol_num = len(self.structural_data[0].mol)
502
503
504 for i in range(len(lines)):
505
506 if lines[i][:6] not in records:
507 break
508
509
510 if lines[i][:5] == 'HELIX':
511
512 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])
513
514
515 mol_init_index = self._pdb_chain_id_to_mol_index(init_chain_id)
516 mol_end_index = self._pdb_chain_id_to_mol_index(end_chain_id)
517
518
519 if read_mol != None:
520 if mol_init_index + 1 not in read_mol:
521 continue
522 if mol_end_index + 1 not in read_mol:
523 continue
524
525
526 helices.append([helix_id, mol_init_index, init_res_name, init_seq_num, mol_end_index, end_res_name, end_seq_num, helix_class, length])
527
528
529 if lines[i][:5] == 'SHEET':
530
531 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])
532
533
534 mol_init_index = self._pdb_chain_id_to_mol_index(init_chain_id)
535 mol_end_index = self._pdb_chain_id_to_mol_index(end_chain_id)
536
537
538 if read_mol != None:
539 if mol_init_index + 1 not in read_mol:
540 continue
541 if mol_end_index + 1 not in read_mol:
542 continue
543
544
545 mol_cur_index = None
546 if cur_chain_id:
547 mol_cur_index = self._pdb_chain_id_to_mol_index(cur_chain_id)
548 mol_prev_index = None
549 if prev_chain_id:
550 mol_prev_index = self._pdb_chain_id_to_mol_index(prev_chain_id)
551
552
553 sheets.append([strand, sheet_id, num_strands, init_res_name, mol_init_index, init_seq_num, init_icode, end_res_name, mol_end_index, end_seq_num, end_icode, sense, cur_atom, cur_res_name, mol_cur_index, cur_res_seq, cur_icode, prev_atom, prev_res_name, mol_prev_index, prev_res_seq, prev_icode])
554
555
556 return lines[i:]
557
558
560 """Loop over and parse the PDB title records.
561
562 These are the records identified in the PDB version 3.30 documentation at U{http://www.wwpdb.org/documentation/file-format/format33/sect2.html}.
563
564
565 @param lines: All lines of the PDB file.
566 @type lines: list of str
567 @return: The remaining PDB lines with the title records stripped.
568 @rtype: list of str
569 """
570
571
572 records = [
573 'HEADER',
574 'OBSLTE',
575 'TITLE ',
576 'SPLT ',
577 'CAVEAT',
578 'COMPND',
579 'SOURCE',
580 'KEYWDS',
581 'EXPDTA',
582 'NUMMDL',
583 'MDLTYP',
584 'AUTHOR',
585 'REVDAT',
586 'SPRSDE',
587 'JRNL ',
588 'REMARK'
589 ]
590
591
592 for i in range(len(lines)):
593
594 if lines[i][:6] not in records:
595 break
596
597
598 return lines[i:]
599
600
629
630
632 """Generator function for looping over the models in the XYZ file.
633
634 @param file_path: The full path of the XYZ file.
635 @type file_path: str
636 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
637 @type verbosity: int
638 @return: The model number and all the records for that model.
639 @rtype: tuple of int and array of str
640 """
641
642
643 file = open_read_file(file_path, verbosity=verbosity)
644 lines = file.readlines()
645 file.close()
646
647
648 if lines == []:
649 raise RelaxError("The XYZ file is empty.")
650
651
652 total_atom = 0
653 model = 0
654 records = []
655
656
657 for i in range(len(lines)):
658 num=0
659 word = lines[i].split()
660
661 if (i==0) and (len(word)==1):
662 try:
663 total_atom = int(word[0])
664 num = 1
665 except:
666 raise RelaxError("The MODEL record " + repr(lines[i]) + " is corrupt, cannot read the XYZ file.")
667
668
669 if (len(records) == total_atom):
670
671 yield records
672
673
674 records = []
675
676
677 if (len(word) != 4):
678 continue
679
680
681 records.append(lines[i])
682
683
684 if len(records):
685 yield records
686
687
689 """Generator function for looping over the molecules in the PDB records of a model.
690
691 @param records: The list of PDB records for the model, or if no models exist the entire PDB file.
692 @type records: list of str
693 @return: The molecule number and all the records for that molecule.
694 @rtype: tuple of int and list of str
695 """
696
697
698 if records == []:
699 raise RelaxError("There are no PDB records for this model.")
700
701
702 mol_count = 1
703 mol_records = [[]]
704 end = False
705
706
707 for i in range(len(records)):
708
709 if records[i][:3] == 'END':
710 break
711
712
713 if records[i][:6] == 'MASTER':
714 break
715
716
717 if records[i][:6] == 'ENDMDL':
718 end = True
719
720
721 elif i < len(records)-1 and records[i][:3] == 'TER' and not records[i+1][:6] == 'HETATM' and not records[i+1][:6] == 'CONECT':
722 end = True
723
724
725 elif i < len(records)-1 and records[i][:6] == 'HETATM' and records[i+1][:4] == 'ATOM':
726 end = True
727
728
729 if end:
730
731 mol_count = mol_count + 1
732
733
734 end = False
735
736
737 chain_id = records[i][21]
738 if chain_id == ' ':
739 mol_index = mol_count - 1
740 else:
741 mol_index = self._pdb_chain_id_to_mol_index(chain_id)
742
743
744 while True:
745 if len(mol_records) <= mol_index:
746 mol_records.append([])
747 else:
748 break
749
750
751 mol_records[mol_index].append(records[i])
752
753
754 for i in range(len(mol_records)):
755 if mol_records[i] != []:
756 yield i+1, mol_records[i]
757
758
760 """Convert the PDB chain ID into the molecule index in a regular way.
761
762 @keyword chain_id: The PDB chain ID string.
763 @type chain_id: str
764 @return: The corresponding molecule index.
765 @rtype: int
766 """
767
768
769 mol_index = 0
770
771
772 if chain_id:
773 mol_index = CHAIN_ID_LIST.index(chain_id)
774
775
776 return mol_index
777
778
780 """Convert the residue info into a dictionary of unique residues with numbers as keys.
781
782 @keyword res_nums: The list of residue numbers.
783 @type res_nums: list of int
784 @keyword res_names: The list of residue names matching the numbers.
785 @type res_names: list of str
786 @return: The dictionary of residue names with residue numbers as keys.
787 @rtype: dict of str
788 """
789
790
791 data = {}
792
793
794 for i in range(len(res_nums)):
795
796 if res_nums[i] in data:
797 continue
798
799
800 data[res_nums[i]] = res_names[i]
801
802
803 return data
804
805
807 """Check the validity of the data arrays in the given structure object.
808
809 @param struct: The structural object.
810 @type struct: Structure_container instance
811 """
812
813
814 num = len(struct.atom_name)
815
816
817 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:
818 raise RelaxError("The structural data is invalid.")
819
820
822 """Make sure all PDB records are 80 char in length, padding with whitespace when needed.
823
824 All newline characters are stripped from the records as well.
825
826
827 @param lines: All lines of the PDB file.
828 @type lines: list of str
829 @return: The padded PDB lines.
830 @rtype: list of str
831 """
832
833
834 for i in range(len(lines)):
835
836 lines[i] = lines[i].rstrip('\r\n')
837
838
839 if len(lines[i]) != 80:
840 lines[i] = "%-80s" % lines[i]
841
842
843 return lines
844
845
847 """Determine the type of molecule.
848
849 @param mol: The molecule data container.
850 @type mol: MolContainer instance
851 """
852
853
854 aa = ['ALA', 'ARG', 'ASN', 'ASP', 'CYS', 'GLU', 'GLN', 'GLY', 'HIS', 'ILE', 'LEU', 'LYS', 'MET', 'PHE', 'PRO', 'SER', 'THR', 'TRP', 'TYR', 'VAL']
855
856
857 mol.type = 'other'
858
859
860 for res in mol.res_name:
861
862 if res in aa:
863
864 mol.type = 'protein'
865 return
866
867
869 """Set up the connectivities for the protein.
870
871 @param mol: The molecule data container.
872 @type mol: MolContainer instance
873 """
874
875
876 curr_res_num = None
877 res_atoms = []
878
879
880 for i in range(len(mol.atom_num)):
881
882 if mol.res_num[i] != curr_res_num:
883
884 if len(res_atoms):
885 self._protein_intra_connect(mol, res_atoms)
886
887
888 curr_res_num = mol.res_num[i]
889
890
891 res_atoms = []
892
893
894 res_atoms.append(i)
895
896
897 if i == len(mol.atom_num) - 1 and len(res_atoms):
898 self._protein_intra_connect(mol, res_atoms)
899
900
902 """Set up the connectivities for the protein.
903
904 @param mol: The molecule data container.
905 @type mol: MolContainer instance
906 @param res_atoms: The list of atom indices corresponding to the residue.
907 @type res_atoms: list of int
908 """
909
910
911 indices = {
912 'N': None,
913 'C': None,
914 'O': None,
915 'CA': None,
916 'HN': None,
917 'H': None,
918 'HA': None
919 }
920
921
922 for index in res_atoms:
923 if mol.atom_name[index] in indices:
924 indices[mol.atom_name[index]] = index
925
926
927 pairs = [
928 ['N', 'HN'],
929 ['N', 'H'],
930 ['N', 'CA'],
931 ['CA', 'HA'],
932 ['CA', 'C'],
933 ['C', 'O']
934 ]
935
936
937 for pair in pairs:
938 if indices[pair[0]] != None and indices[pair[1]] != None:
939 mol.atom_connect(indices[pair[0]], indices[pair[1]])
940
941
943 """Convert the data into a format for writing to file.
944
945 @param data: The data to convert to the required format.
946 @type data: anything
947 @keyword format: The format to convert to. This can be 'str', 'float', or 'int'.
948 @type format: str
949 @return: The converted version of the data.
950 @rtype: str
951 """
952
953
954 if format == 'str':
955
956 if data == None:
957 data = ''
958
959
960 if not isinstance(data, str):
961 data = repr(data)
962
963
964 if format == 'float':
965
966 if data == None:
967 data = 0.0
968
969
970 if not isinstance(data, float):
971 data = float(data)
972
973
974 return data
975
976
977 - def _trim_helix(self, helix=None, trim_res_list=[], res_data=None):
978 """Trim the given helix based on the list of deleted residue numbers.
979
980 @keyword helix: The single helix metadata structure.
981 @type helix: list
982 @keyword trim_res_list: The list of residue numbers which no longer exist.
983 @type trim_res_list: list of int
984 @keyword res_data: The dictionary of residue names with residue numbers as keys.
985 @type res_data: dict of str
986 @return: The trimmed helix metadata structure, or None if the whole helix is to be deleted.
987 @rtype: list or None
988 """
989
990
991 start_res = helix[3]
992 end_res = helix[6]
993
994
995 trim_res_list_rev = deepcopy(trim_res_list)
996 trim_res_list_rev.reverse()
997
998
999 helix_res = list(range(start_res, end_res+1))
1000
1001
1002 for res_num in trim_res_list:
1003 if res_num == start_res:
1004
1005 helix_res.pop(0)
1006
1007
1008 if len(helix_res) == 0:
1009 break
1010
1011
1012 start_res = helix_res[0]
1013
1014
1015 if len(helix_res) == 0:
1016 return None
1017
1018
1019 for res_num in trim_res_list_rev:
1020 if res_num == end_res:
1021 helix_res.pop(-1)
1022 end_res = helix_res[-1]
1023
1024
1025 if start_res != helix[3]:
1026 helix[3] = start_res
1027 helix[2] = res_data[start_res]
1028 if end_res != helix[6]:
1029 helix[6] = end_res
1030 helix[5] = res_data[end_res]
1031
1032
1033 helix[-1] = len(helix_res)
1034
1035
1036 return helix
1037
1038
1039 - def _trim_sheet(self, sheet=None, trim_res_list=[], res_data=None):
1040 """Trim the given sheet based on the list of deleted residue numbers.
1041
1042 @keyword sheet: The single sheet metadata structure.
1043 @type sheet: list
1044 @keyword trim_res_list: The list of residue numbers which no longer exist.
1045 @type trim_res_list: list of int
1046 @keyword res_data: The dictionary of residue names with residue numbers as keys.
1047 @type res_data: dict of str
1048 @return: The trimmed sheet metadata structure, or None if the whole sheet is to be deleted.
1049 @rtype: list or None
1050 """
1051
1052
1053 start_res = sheet[5]
1054 end_res = sheet[9]
1055
1056
1057 trim_res_list_rev = deepcopy(trim_res_list)
1058 trim_res_list_rev.reverse()
1059
1060
1061 sheet_res = list(range(start_res, end_res+1))
1062
1063
1064 for res_num in trim_res_list:
1065 if res_num == start_res:
1066
1067 sheet_res.pop(0)
1068
1069
1070 if len(sheet_res) == 0:
1071 break
1072
1073
1074 start_res = sheet_res[0]
1075
1076
1077 if len(sheet_res) == 0:
1078 return None
1079
1080
1081 for res_num in trim_res_list_rev:
1082 if res_num == end_res:
1083 sheet_res.pop(-1)
1084 end_res = sheet_res[-1]
1085
1086
1087 if start_res != sheet[5]:
1088 sheet[5] = start_res
1089 sheet[3] = res_data[start_res]
1090 if end_res != sheet[9]:
1091 sheet[9] = end_res
1092 sheet[7] = res_data[end_res]
1093
1094
1095 return sheet
1096
1097
1098 - 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, sort=False):
1099 """Add a new atom to the structural data object.
1100
1101 @keyword mol_name: The name of the molecule.
1102 @type mol_name: str
1103 @keyword atom_name: The atom name, e.g. 'H1'.
1104 @type atom_name: str or None
1105 @keyword res_name: The residue name.
1106 @type res_name: str or None
1107 @keyword res_num: The residue number.
1108 @type res_num: int or None
1109 @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.
1110 @type pos: rank-1 or rank-2 array or list of float
1111 @keyword element: The element symbol.
1112 @type element: str or None
1113 @keyword atom_num: The atom number.
1114 @type atom_num: int or None
1115 @keyword chain_id: The chain identifier.
1116 @type chain_id: str or None
1117 @keyword segment_id: The segment identifier.
1118 @type segment_id: str or None
1119 @keyword pdb_record: The optional PDB record name, e.g. 'ATOM' or 'HETATM'.
1120 @type pdb_record: str or None
1121 @keyword sort: A flag which if True will cause the structural data to be sorted after adding the atom.
1122 @type sort: bool
1123 """
1124
1125
1126 if len(self.structural_data) == 0:
1127 self.add_model()
1128
1129
1130 if is_float(pos[0]):
1131 if len(pos) != 3:
1132 raise RelaxError("The single atomic position %s must be a 3D list." % pos)
1133 else:
1134 if len(pos) != len(self.structural_data):
1135 raise RelaxError("The %s atomic positions does not match the %s models present." % (len(pos), len(self.structural_data)))
1136
1137
1138 for i in range(len(self.structural_data)):
1139
1140 model = self.structural_data[i]
1141
1142
1143 mol = self.get_molecule(mol_name, model=model.num)
1144
1145
1146 if mol == None:
1147 self.add_molecule(name=mol_name)
1148 mol = self.get_molecule(mol_name, model=model.num)
1149
1150
1151 if is_float(pos[0]):
1152 model_pos = pos
1153 else:
1154 model_pos = pos[i]
1155
1156
1157 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)
1158
1159
1160 if sort:
1161 mol._sort()
1162
1163
1164 - def add_coordinates(self, coord=None, mol_names=None, res_names=None, res_nums=None, atom_names=None, elements=None, set_mol_name=None, set_model_num=None):
1165 """Add a set of coordinates to the structural object.
1166
1167 @keyword coord: The array of atomic coordinates (first dimension is the atoms and the second are the coordinates).
1168 @type coord: numpy rank-2 float64 array
1169 @keyword mol_names: The list of molecule names corresponding to the first dimension of the coordinate array.
1170 @type mol_names: list of str
1171 @keyword res_names: The list of residue names corresponding to the first dimension of the coordinate array.
1172 @type res_names: list of str
1173 @keyword res_nums: The list of residue numbers corresponding to the first dimension of the coordinate array.
1174 @type res_nums: list of str
1175 @keyword atom_names: The list of atom names corresponding to the first dimension of the coordinate array.
1176 @type atom_names: list of str
1177 @keyword elements: The list of elements corresponding to the first dimension of the coordinate array.
1178 @type elements: list of str
1179 @keyword set_mol_name: Set the names of the molecules which are loaded. If set to None, then all molecule names must be identical and the new molecule will have the same name.
1180 @type set_mol_name: None or str
1181 @keyword set_model_num: Set the model number for the coordinates.
1182 @type set_model_num: None or int
1183 """
1184
1185
1186 if not set_mol_name:
1187 set_mol_name = mol_names[0]
1188 for name in mol_names:
1189 if set_mol_name != name:
1190 raise RelaxError("No unique molecule name can be found in the list %s for storing the new molecule structure.")
1191
1192
1193 if set_mol_name == None and set_model_num == None:
1194 print("Deleting all structural data so that only the new molecule will be present.")
1195 self.delete(verbosity=0)
1196
1197
1198 mol = MolContainer()
1199
1200
1201 for i in range(len(coord)):
1202 mol.atom_add(atom_name=atom_names[i], res_name=res_names[i], res_num=res_nums[i], pos=coord[i], element=elements[i])
1203
1204
1205 self.pack_structs([[mol]], orig_model_num=[None], set_model_num=[set_model_num], orig_mol_num=[[None]], set_mol_name=[set_mol_name])
1206
1207
1208 - def add_model(self, model=None, coords_from=None):
1209 """Add a new model to the store.
1210
1211 The new model will be constructed 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.
1212
1213 @keyword model: The number of the model to create.
1214 @type model: int or None
1215 @keyword coords_from: The model number to take the coordinates from.
1216 @type coords_from: int or None
1217 @return: The model container.
1218 @rtype: ModelContainer instance
1219 """
1220
1221
1222 if model != None:
1223 for i in range(len(self.structural_data)):
1224 if model == self.structural_data[i].num:
1225 raise RelaxError("The model '%s' already exists." % model)
1226
1227
1228 model = self.structural_data.add_item(model_num=model)
1229
1230
1231 if coords_from == None:
1232 model_from = self.structural_data[0]
1233 else:
1234 for i in range(len(self.structural_data)):
1235 if self.structural_data[i].num == coords_from:
1236 model_from = self.structural_data[i]
1237 break
1238
1239
1240 for mol_index in range(len(model_from.mol)):
1241
1242 model.mol.add_item(mol_name=model_from.mol[mol_index].mol_name, mol_cont=MolContainer())
1243 mol = model.mol[mol_index]
1244 mol_from = model_from.mol[mol_index]
1245
1246
1247 for i in range(len(mol_from.atom_num)):
1248 mol.atom_num.append(mol_from.atom_num[i])
1249 mol.atom_name.append(mol_from.atom_name[i])
1250 mol.bonded.append(mol_from.bonded[i])
1251 mol.chain_id.append(mol_from.chain_id[i])
1252 mol.element.append(mol_from.element[i])
1253 mol.pdb_record.append(mol_from.pdb_record[i])
1254 mol.res_name.append(mol_from.res_name[i])
1255 mol.res_num.append(mol_from.res_num[i])
1256 mol.seg_id.append(mol_from.seg_id[i])
1257 mol.x.append(mol_from.x[i])
1258 mol.y.append(mol_from.y[i])
1259 mol.z.append(mol_from.z[i])
1260
1261
1262 return self.structural_data[-1]
1263
1264
1266 """Add a new molecule to the store.
1267
1268 @keyword model_num: The optional model to add the molecule to. If not supplied, the molecule will be added to all models.
1269 @type model_num: None or int
1270 @keyword name: The molecule identifier string.
1271 @type name: str
1272 """
1273
1274
1275 if len(self.structural_data) == 0:
1276 self.add_model()
1277
1278
1279 for model in self.model_loop(model=model_num):
1280 model.mol.add_item(mol_name=name, mol_cont=MolContainer())
1281
1282
1283 - def are_bonded(self, atom_id1=None, atom_id2=None):
1284 """Determine if two atoms are directly bonded to each other.
1285
1286 @keyword atom_id1: The molecule, residue, and atom identifier string of the first atom.
1287 @type atom_id1: str
1288 @keyword atom_id2: The molecule, residue, and atom identifier string of the second atom.
1289 @type atom_id2: str
1290 @return: True if the atoms are directly bonded.
1291 @rtype: bool
1292 """
1293
1294
1295 sel_obj1 = Selection(atom_id1)
1296 sel_obj2 = Selection(atom_id2)
1297
1298
1299 for mol in self.structural_data[0].mol:
1300 for i in range(len(mol.atom_num)):
1301 if not len(mol.bonded[i]):
1302 self._find_bonded_atoms(i, mol, radius=2)
1303
1304
1305 for mol in self.structural_data[0].mol:
1306
1307 if not sel_obj1.contains_mol(mol.mol_name):
1308 continue
1309 if not sel_obj2.contains_mol(mol.mol_name):
1310 continue
1311
1312
1313 index1 = None
1314 index2 = None
1315 for i in range(len(mol.atom_num)):
1316
1317 if index1 == None and sel_obj1.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1318 index1 = i
1319
1320
1321 if index2 == None and sel_obj2.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
1322 index2 = i
1323
1324
1325 if index1 != None and index2 != None:
1326 break
1327
1328
1329 if index1 < len(mol.bonded):
1330 if index2 in mol.bonded[index1]:
1331 return True
1332 else:
1333 return False
1334
1335
1336 - def are_bonded_index(self, mol_index1=None, atom_index1=None, mol_index2=None, atom_index2=None):
1337 """Determine if two atoms, given as indices, are directly bonded to each other.
1338
1339 @keyword mol_index1: The molecule index of the first atom.
1340 @type mol_index1: int
1341 @keyword atom_index1: The index of the first atom.
1342 @type atom_index1: int
1343 @keyword mol_index2: The molecule index of the second atom.
1344 @type mol_index2: int
1345 @keyword atom_index2: The index of the second atom.
1346 @type atom_index2: int
1347 @return: True if the atoms are directly bonded.
1348 @rtype: bool
1349 """
1350
1351
1352 mol1 = self.structural_data[0].mol[mol_index1]
1353 mol2 = self.structural_data[0].mol[mol_index2]
1354
1355
1356 if not len(mol1.bonded[atom_index1]):
1357 self._find_bonded_atoms(atom_index1, mol1, radius=2)
1358 if not len(mol2.bonded[atom_index2]):
1359 self._find_bonded_atoms(atom_index2, mol2, radius=2)
1360
1361
1362 if atom_index2 in mol1.bonded[atom_index1]:
1363 return True
1364
1365
1366 return False
1367
1368
1369 - def atom_loop(self, selection=None, str_id=None, model_num=None, 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, mol_index_flag=False, index_flag=False, ave=False):
1370 """Generator function for looping over all atoms in the internal relax structural object.
1371
1372 This method should be designed as a U{generator<http://www.python.org/dev/peps/pep-0255/>}. It should loop over all atoms of the system yielding the following atomic information, if the corresponding flag is True, in tuple form:
1373
1374 1. Model number.
1375 2. Molecule name.
1376 3. Residue number.
1377 4. Residue name.
1378 5. Atom number.
1379 6. Atom name.
1380 7. The element name (its atomic symbol and optionally the isotope, e.g. 'N', 'Mg', '17O', '13C', etc).
1381 8. The position of the atom in Euclidean space.
1382
1383
1384 @keyword selection: The internal structural selection object. This is obtained by calling the selection() method with the atom ID string.
1385 @type selection: lib.structure.internal.Internal_selection instance
1386 @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.
1387 @type str_id: str, int, or None
1388 @keyword model_num: Only loop over a specific model.
1389 @type model_num: int or None
1390 @keyword mol_name_flag: A flag which if True will cause the molecule name to be yielded.
1391 @type mol_name_flag: bool
1392 @keyword res_num_flag: A flag which if True will cause the residue number to be yielded.
1393 @type res_num_flag: bool
1394 @keyword res_name_flag: A flag which if True will cause the residue name to be yielded.
1395 @type res_name_flag: bool
1396 @keyword atom_num_flag: A flag which if True will cause the atom number to be yielded.
1397 @type atom_num_flag: bool
1398 @keyword atom_name_flag: A flag which if True will cause the atom name to be yielded.
1399 @type atom_name_flag: bool
1400 @keyword element_flag: A flag which if True will cause the element name to be yielded.
1401 @type element_flag: bool
1402 @keyword pos_flag: A flag which if True will cause the atomic position to be yielded.
1403 @type pos_flag: bool
1404 @keyword mol_index_flag: A flag which if True will cause the molecule index to be yielded.
1405 @type mol_index_flag: bool
1406 @keyword index_flag: A flag which if True will cause the atomic index to be yielded.
1407 @type index_flag: bool
1408 @keyword ave: A flag which if True will result in this method returning the average atom properties across all loaded structures.
1409 @type ave: bool
1410 @return: A tuple of atomic information, as described in the docstring.
1411 @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).
1412 """
1413
1414
1415 if not len(self.structural_data):
1416 raise RelaxNoPdbError
1417
1418
1419 model = self.structural_data[0]
1420
1421
1422 for mol_index, i in selection.loop():
1423 mol = model.mol[mol_index]
1424
1425
1426 res_num = mol.res_num[i]
1427 res_name = mol.res_name[i]
1428 atom_num = mol.atom_num[i]
1429 atom_name = mol.atom_name[i]
1430 element = mol.element[i]
1431
1432
1433 if pos_flag:
1434
1435 if ave:
1436
1437 pos = zeros(3, float64)
1438
1439
1440 for model in self.model_loop(model=model_num):
1441
1442 mol2 = model.mol[mol_index]
1443
1444
1445 if mol2.atom_num[i] != atom_num:
1446 raise RelaxError("The loaded structures do not contain the same atoms. The average structural properties can not be calculated.")
1447
1448
1449 pos = pos + array([mol2.x[i], mol2.y[i], mol2.z[i]], float64)
1450
1451
1452 pos = pos / len(self.structural_data)
1453
1454
1455 else:
1456
1457 pos = []
1458
1459
1460 for model in self.model_loop(model=model_num):
1461
1462 mol2 = model.mol[mol_index]
1463
1464
1465 pos.append([mol2.x[i], mol2.y[i], mol2.z[i]])
1466
1467
1468 pos = array(pos, float64)
1469
1470
1471 mol_name = mol.mol_name
1472
1473
1474 atomic_tuple = ()
1475 if mol_name_flag:
1476 atomic_tuple = atomic_tuple + (mol_name,)
1477 if res_num_flag:
1478 atomic_tuple = atomic_tuple + (res_num,)
1479 if res_name_flag:
1480 atomic_tuple = atomic_tuple + (res_name,)
1481 if atom_num_flag:
1482 atomic_tuple = atomic_tuple + (atom_num,)
1483 if atom_name_flag:
1484 atomic_tuple = atomic_tuple + (atom_name,)
1485 if element_flag:
1486 atomic_tuple = atomic_tuple + (element,)
1487 if pos_flag:
1488 atomic_tuple = atomic_tuple + (pos,)
1489 if mol_index_flag:
1490 atomic_tuple += (mol_index,)
1491 if index_flag:
1492 atomic_tuple += (i,)
1493
1494
1495 if len(atomic_tuple) == 1:
1496 atomic_tuple = atomic_tuple[0]
1497 yield atomic_tuple
1498
1499
1500 - 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):
1501 """Find the bond vector between the atoms of 'attached_atom' and 'atom_id'.
1502
1503 @keyword attached_atom: The name of the bonded atom.
1504 @type attached_atom: str
1505 @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.
1506 @type model_num: None or int
1507 @keyword mol_name: The name of the molecule that attached_atom belongs to.
1508 @type mol_name: str
1509 @keyword res_num: The number of the residue that attached_atom belongs to.
1510 @type res_num: str
1511 @keyword res_name: The name of the residue that attached_atom belongs to.
1512 @type res_name: str
1513 @keyword spin_num: The number of the spin that attached_atom is attached to.
1514 @type spin_num: str
1515 @keyword spin_name: The name of the spin that attached_atom is attached to.
1516 @type spin_name: str
1517 @keyword return_name: A flag which if True will cause the name of the attached atom to be returned together with the bond vectors.
1518 @type return_name: bool
1519 @keyword return_warnings: A flag which if True will cause warning messages to be returned.
1520 @type return_warnings: bool
1521 @return: The list of bond vectors for each model.
1522 @rtype: list of numpy arrays (or a tuple if return_name or return_warnings are set)
1523 """
1524
1525
1526 vectors = []
1527 attached_name = None
1528 warnings = None
1529
1530
1531 model = self.structural_data[0]
1532
1533
1534 for mol_index in range(len(model.mol)):
1535
1536 mol = model.mol[mol_index]
1537
1538
1539 if mol_name and mol_name != mol.mol_name:
1540 continue
1541
1542
1543 index = None
1544 for i in range(len(mol.atom_name)):
1545
1546 if (res_num != None and mol.res_num[i] != res_num) or (res_name != None and mol.res_name[i] != res_name):
1547 continue
1548
1549
1550 if (spin_num != None and mol.atom_num[i] != spin_num) or (spin_name != None and mol.atom_name[i] != spin_name):
1551 continue
1552
1553
1554 index = i
1555 break
1556
1557
1558 if index != None:
1559
1560 for model in self.model_loop(model=model_num):
1561
1562 mol = model.mol[mol_index]
1563
1564
1565 bonded_num, bonded_name, element, pos, attached_name, warnings = self._bonded_atom(attached_atom, index, mol)
1566
1567
1568 if (bonded_num, bonded_name, element) == (None, None, None):
1569 continue
1570
1571
1572 vector = array(pos, float64) - array([mol.x[index], mol.y[index], mol.z[index]], float64)
1573
1574
1575 vectors.append(vector)
1576
1577
1578 else:
1579 warnings = "Cannot find the atom in the structure"
1580
1581
1582 data = (vectors,)
1583 if return_name:
1584 data = data + (attached_name,)
1585 if return_warnings:
1586 data = data + (warnings,)
1587
1588
1589 return data
1590
1591
1593 """Collapse the ensemble into a single model.
1594
1595 @keyword model_num: The number of the model to keep. All other models will be removed.
1596 @type model_num: int
1597 @keyword model_to: The model number for the sole remaining model.
1598 @type model_to: int
1599 """
1600
1601
1602 models = []
1603 for model_cont in self.model_loop():
1604 if model_cont.num != model_num:
1605 models.append(model_cont.num)
1606
1607
1608 for model in models:
1609 self.delete(model)
1610
1611
1612 self.set_model(model_orig=model_num, model_new=model_to)
1613
1614
1615 - def connect_atom(self, mol_name=None, index1=None, index2=None):
1616 """Connect two atoms in the structural data object.
1617
1618 @keyword mol_name: The name of the molecule.
1619 @type mol_name: str
1620 @keyword index1: The global index of the first atom.
1621 @type index1: str
1622 @keyword index2: The global index of the first atom.
1623 @type index2: str
1624 """
1625
1626
1627 if self.get_molecule(mol_name) == None:
1628 self.add_molecule(name=mol_name)
1629
1630
1631 for model in self.structural_data:
1632
1633 mol = self.get_molecule(mol_name)
1634
1635
1636 mol.atom_connect(index1=index1, index2=index2)
1637
1638
1639 - def delete(self, model=None, selection=None, verbosity=1):
1640 """Deletion of structural information.
1641
1642 @keyword model: Individual structural models from a loaded ensemble can be deleted by specifying the model number.
1643 @type model: None or int
1644 @keyword selection: The internal structural selection object. This is obtained by calling the selection() method with the atom ID string.
1645 @type selection: lib.structure.internal.Internal_selection instance
1646 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
1647 @type verbosity: int
1648 """
1649
1650
1651 if model == None and selection == None:
1652
1653 if verbosity:
1654 print("Deleting the following structural data:\n")
1655 print(self.structural_data)
1656
1657
1658 del self.structural_data
1659
1660
1661 self.structural_data = ModelList()
1662
1663
1664 elif selection == None:
1665 self.structural_data.delete_model(model_num=model)
1666
1667
1668 else:
1669
1670 indices = {}
1671 for mol_index, i in selection.loop():
1672 if mol_index not in indices:
1673 indices[mol_index] = []
1674 indices[mol_index].append(i)
1675 for mol_index in indices:
1676 indices[mol_index].reverse()
1677
1678
1679 del_res_nums = []
1680 for model_cont in self.model_loop():
1681
1682 if model != None and model_cont.num == model:
1683 continue
1684
1685
1686 for mol_index in indices:
1687 mol = model_cont.mol[mol_index]
1688
1689
1690 res_data = self._residue_data(res_nums=mol.res_num, res_names=mol.res_name)
1691
1692
1693 for i in indices[mol_index]:
1694 mol.atom_num.pop(i)
1695 mol.atom_name.pop(i)
1696 mol.bonded.pop(i)
1697 mol.chain_id.pop(i)
1698 mol.element.pop(i)
1699 mol.pdb_record.pop(i)
1700 mol.res_name.pop(i)
1701 res_num = mol.res_num.pop(i)
1702 mol.seg_id.pop(i)
1703 mol.x.pop(i)
1704 mol.y.pop(i)
1705 mol.z.pop(i)
1706
1707
1708 if res_num not in mol.res_num and res_num not in del_res_nums:
1709 del_res_nums.append(res_num)
1710
1711
1712 for j in range(len(mol.bonded)):
1713 if i in mol.bonded[j]:
1714 mol.bonded[j].pop(mol.bonded[j].index(i))
1715
1716
1717 for j in range(i, len(mol.bonded)):
1718 for k in range(len(mol.bonded[j])):
1719 mol.bonded[j][k] -= 1
1720
1721
1722 if mol.atom_num == []:
1723 if hasattr(mol, 'file_name'):
1724 del mol.file_name
1725 if hasattr(mol, 'file_path'):
1726 del mol.file_path
1727 if hasattr(mol, 'file_mol_num'):
1728 del mol.file_mol_num
1729 if hasattr(mol, 'file_model'):
1730 del mol.file_model
1731
1732
1733 if not len(del_res_nums):
1734 return
1735 if model != None and len(self.structural_data) > 1:
1736 return
1737
1738
1739 del_res_nums.reverse()
1740
1741
1742 if hasattr(self, 'helices'):
1743 del_helix_indices = []
1744 for i in range(len(self.helices)):
1745
1746 helix = self._trim_helix(helix=self.helices[i], trim_res_list=del_res_nums, res_data=res_data)
1747
1748
1749 if helix != None:
1750 self.helices[i] = helix
1751
1752
1753 else:
1754 del_helix_indices.append(i)
1755
1756
1757 del_helix_indices.reverse()
1758 for i in del_helix_indices:
1759 self.helices.pop(i)
1760
1761
1762 if hasattr(self, 'sheets'):
1763 del_sheet_indices = []
1764 for i in range(len(self.sheets)):
1765
1766 sheet = self._trim_sheet(sheet=self.sheets[i], trim_res_list=del_res_nums, res_data=res_data)
1767
1768
1769 if sheet != None:
1770 self.sheets[i] = sheet
1771
1772
1773 else:
1774 del_sheet_indices.append(i)
1775
1776
1777 del_sheet_indices.reverse()
1778 for i in del_sheet_indices:
1779 self.sheets.pop(i)
1780
1781
1783 """Deletion of all secondary structure information.
1784
1785 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
1786 @type verbosity: int
1787 """
1788
1789
1790 self.helices = []
1791 self.sheets = []
1792
1793
1795 """Report if the structural data structure is empty or not.
1796
1797 @return: True if empty, False otherwise.
1798 @rtype: bool
1799 """
1800
1801
1802 if len(self.structural_data) == 0:
1803 return True
1804 else:
1805 return False
1806
1807
1808 - def from_xml(self, str_node, dir=None, file_version=1):
1809 """Recreate the structural object from the XML structural object node.
1810
1811 @param str_node: The structural object XML node.
1812 @type str_node: xml.dom.minicompat.Element instance
1813 @keyword dir: The name of the directory containing the results file.
1814 @type dir: str
1815 @keyword file_version: The relax XML version of the XML file.
1816 @type file_version: int
1817 """
1818
1819
1820 xml_to_object(str_node, self, file_version=file_version, blacklist=['model', 'displacements'])
1821
1822
1823 model_nodes = str_node.getElementsByTagName('model')
1824 self.structural_data.from_xml(model_nodes, file_version=file_version)
1825
1826
1827 disp_nodes = str_node.getElementsByTagName('displacements')
1828 if len(disp_nodes):
1829
1830 self.displacements = Displacements()
1831
1832
1833 self.displacements.from_xml(disp_nodes[0], file_version=file_version)
1834
1835
1837 """Return or create the model.
1838
1839 @param model: The model number.
1840 @type model: int or None
1841 @return: The ModelContainer corresponding to the model number or that newly created.
1842 @rtype: ModelContainer instance
1843 """
1844
1845
1846 if model == None and self.num_models() > 1:
1847 raise RelaxError("The target model cannot be determined as there are %s models already present." % self.num_modes())
1848
1849
1850 if model == None:
1851
1852 if self.num_models():
1853 self.structural_data.add_item()
1854
1855
1856 model_cont = self.structural_data[0]
1857
1858
1859 else:
1860
1861 found = False
1862 for model_cont in self.structural_data:
1863 if model_cont.num == model:
1864 found = True
1865 break
1866
1867
1868 if not found:
1869 self.structural_data.add_item(model)
1870 model_cont = self.structural_data[-1]
1871
1872
1873 return model_cont
1874
1875
1877 """Return the molecule.
1878
1879 Only one model can be specified.
1880
1881
1882 @param molecule: The molecule name.
1883 @type molecule: int or None
1884 @keyword model: The model number.
1885 @type model: int or None
1886 @raises RelaxError: If the model is not specified and there is more than one model loaded.
1887 @return: The MolContainer corresponding to the molecule name and model number.
1888 @rtype: MolContainer instance or None
1889 """
1890
1891
1892 if model == None and self.num_models() > 1:
1893 raise RelaxError("The target molecule cannot be determined as there are %s models already present." % self.num_models())
1894
1895
1896 if not isinstance(model, int) and not model == None:
1897 raise RelaxNoneIntError
1898
1899
1900 if not len(self.structural_data):
1901 return
1902
1903
1904 for model_cont in self.model_loop(model):
1905
1906 for mol in model_cont.mol:
1907
1908 if mol.mol_name == molecule:
1909 return mol
1910
1911
1913 """Check if the molecule name exists.
1914
1915 @keyword model_num: The optional model to check. If not supplied, the molecule will be searched for across all models.
1916 @type model_num: None or int
1917 @param name: The molecule name.
1918 @type name: str
1919 @return: True if the molecule exists, False otherwise.
1920 @rtype: bool
1921 """
1922
1923
1924 if not len(self.structural_data):
1925 return False
1926
1927
1928 for model_cont in self.model_loop(model=model_num):
1929
1930 for mol in model_cont.mol:
1931
1932 if mol.mol_name == name:
1933 return True
1934
1935
1936 return False
1937
1938
1939 - def load_gaussian(self, file_path, set_mol_name=None, set_model_num=None, verbosity=False):
1940 """Method for loading structures from a Gaussian log file.
1941
1942 @param file_path: The full path of the Gaussian log file.
1943 @type file_path: str
1944 @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.
1945 @type set_mol_name: None, str, or list of str
1946 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then the Gaussian log model numbers will be preserved, if they exist.
1947 @type set_model_num: None, int, or list of int
1948 @keyword verbosity: A flag which if True will cause messages to be printed.
1949 @type verbosity: bool
1950 @return: The status of the loading of the Gaussian log file.
1951 @rtype: bool
1952 """
1953
1954
1955 if verbosity:
1956 print("\nInternal relax Gaussian log parser.")
1957
1958
1959 if not access(file_path, F_OK):
1960
1961 return False
1962
1963
1964 path, file_name = os.path.split(file_path)
1965
1966
1967 path_abs = abspath(curdir) + sep + path
1968
1969
1970 if set_mol_name:
1971 if not isinstance(set_mol_name, list):
1972 set_mol_name = [set_mol_name]
1973 else:
1974 set_mol_name = [file_root(file_name) + '_mol1']
1975
1976
1977 if set_model_num:
1978 if not isinstance(set_model_num, list):
1979 set_model_num = [set_model_num]
1980 else:
1981 set_model_num = [1]
1982
1983
1984 for model_records in self._parse_models_gaussian(file_path, verbosity=verbosity):
1985 pass
1986
1987
1988 mol = MolContainer()
1989
1990
1991 mol.fill_object_from_gaussian(model_records)
1992
1993
1994 self.pack_structs([[mol]], orig_model_num=[1], set_model_num=set_model_num, orig_mol_num=[[0]], set_mol_name=set_mol_name, file_name=file_name, file_path=path, file_path_abs=path_abs, verbosity=verbosity)
1995
1996
1997 return True
1998
1999
2000 - 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):
2001 """Method for loading structures from a PDB file.
2002
2003 @param file_path: The full path of the PDB file.
2004 @type file_path: str
2005 @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.
2006 @type read_mol: None, int, or list of int
2007 @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.
2008 @type set_mol_name: None, str, or list of str
2009 @keyword read_model: The PDB model to extract from the file. If set to None, then all models will be loaded.
2010 @type read_model: None, int, or list of int
2011 @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.
2012 @type set_model_num: None, int, or list of int
2013 @keyword alt_loc: The PDB ATOM record 'Alternate location indicator' field value to select which coordinates to use.
2014 @type alt_loc: str or None
2015 @keyword verbosity: A flag which if True will cause messages to be printed.
2016 @type verbosity: bool
2017 @keyword merge: A flag which if set to True will try to merge the PDB structure into the currently loaded structures.
2018 @type merge: bool
2019 @return: The status of the loading of the PDB file.
2020 @rtype: bool
2021 """
2022
2023
2024 if verbosity:
2025 print("\nInternal relax PDB parser.")
2026
2027
2028 if not access(file_path, F_OK):
2029
2030 return False
2031
2032
2033 path, file = os.path.split(file_path)
2034
2035
2036 path_abs = abspath(curdir) + sep + path
2037
2038
2039 if read_mol and not isinstance(read_mol, list):
2040 read_mol = [read_mol]
2041 if set_mol_name and not isinstance(set_mol_name, list):
2042 set_mol_name = [set_mol_name]
2043 if read_mol:
2044 set_mol_name *= len(read_mol)
2045 if read_model and not isinstance(read_model, list):
2046 read_model = [read_model]
2047 if set_model_num and not isinstance(set_model_num, list):
2048 set_model_num = [set_model_num]
2049 if read_model:
2050 set_model_num *= len(read_model)
2051
2052
2053 pdb_file = open_read_file(file_path, verbosity=verbosity)
2054 pdb_lines = pdb_file.readlines()
2055 pdb_file.close()
2056
2057
2058 if pdb_lines == []:
2059 raise RelaxError("The PDB file is empty.")
2060
2061
2062 pdb_lines = self._validate_records(pdb_lines)
2063
2064
2065 helices = []
2066 sheets = []
2067
2068
2069 pdb_lines = self._parse_pdb_title(pdb_lines)
2070 pdb_lines = self._parse_pdb_prim_struct(pdb_lines)
2071 pdb_lines = self._parse_pdb_hetrogen(pdb_lines)
2072 pdb_lines = self._parse_pdb_ss(pdb_lines, read_mol=read_mol, helices=helices, sheets=sheets)
2073 pdb_lines = self._parse_pdb_connectivity_annotation(pdb_lines)
2074 pdb_lines = self._parse_pdb_misc(pdb_lines)
2075 pdb_lines = self._parse_pdb_transform(pdb_lines)
2076
2077
2078 model_index = 0
2079 orig_model_num = []
2080 mol_conts = []
2081 orig_mol_num = []
2082 for model_num, model_records in self._parse_pdb_coord(pdb_lines):
2083
2084 if read_model and model_num not in read_model:
2085 continue
2086
2087
2088 orig_model_num.append(model_num)
2089
2090
2091 mol_conts.append([])
2092 orig_mol_num.append([])
2093 mol_index = 0
2094 new_mol_name = []
2095 reset_serial = True
2096 for mol_num, mol_records in self._parse_mols_pdb(model_records):
2097
2098 if read_mol and mol_num not in read_mol:
2099 continue
2100
2101
2102 if set_mol_name:
2103
2104 if len(set_mol_name) == 1:
2105 new_mol_name.append(set_mol_name[0])
2106
2107
2108 else:
2109 new_mol_name.append(set_mol_name[mol_index])
2110
2111
2112 else:
2113
2114 num_struct = 0
2115 if self.structural_data != None and len(self.structural_data) and (not set_model_num or (model_index <= len(set_model_num) and set_model_num[model_index] == self.structural_data[0].num)):
2116 num_struct = len(self.structural_data[0].mol)
2117
2118
2119 new_mol_name.append(file_root(file) + '_mol' + repr(mol_num+num_struct))
2120
2121
2122 mol = MolContainer()
2123
2124
2125 mol.fill_object_from_pdb(mol_records, alt_loc_select=alt_loc, reset_serial=reset_serial)
2126
2127
2128 mol_conts[model_index].append(mol)
2129
2130
2131 orig_mol_num[model_index].append(mol_num)
2132
2133
2134 mol_index = mol_index + 1
2135
2136
2137 reset_serial = False
2138
2139
2140 model_index = model_index + 1
2141
2142
2143 if not len(mol_conts):
2144 warn(RelaxWarning("No structural data could be read from the file '%s'." % file_path))
2145 return False
2146
2147
2148 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, file_path_abs=path_abs, merge=merge, helices=helices, sheets=sheets, verbosity=verbosity)
2149
2150
2151 return True
2152
2153
2154 - def load_xyz(self, file_path, read_mol=None, set_mol_name=None, read_model=None, set_model_num=None, verbosity=False):
2155 """Method for loading structures from a XYZ file.
2156
2157 @param file_path: The full path of the XYZ file.
2158 @type file_path: str
2159 @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.
2160 @type read_mol: None, int, or list of int
2161 @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.
2162 @type set_mol_name: None, str, or list of str
2163 @keyword read_model: The XYZ model to extract from the file. If set to None, then all models will be loaded.
2164 @type read_model: None, int, or list of int
2165 @keyword set_model_num: Set the model number of the loaded molecule. If set to None, then the XYZ model numbers will be preserved, if they exist.
2166 @type set_model_num: None, int, or list of int
2167 @keyword verbosity: A flag which if True will cause messages to be printed.
2168 @type verbosity: bool
2169 @return: The status of the loading of the XYZ file.
2170 @rtype: bool
2171 """
2172
2173
2174 if verbosity:
2175 print("\nInternal relax XYZ parser.")
2176
2177
2178 if not access(file_path, F_OK):
2179
2180 return False
2181
2182
2183 path, file = os.path.split(file_path)
2184
2185
2186 path_abs = abspath(curdir) + sep + path
2187
2188
2189 if read_mol and not isinstance(read_mol, list):
2190 read_mol = [read_mol]
2191 if set_mol_name and not isinstance(set_mol_name, list):
2192 set_mol_name = [set_mol_name]
2193 if read_model and not isinstance(read_model, list):
2194 read_model = [read_model]
2195 if set_model_num and not isinstance(set_model_num, list):
2196 set_model_num = [set_model_num]
2197
2198
2199 mol_index = 0
2200 model_index = 0
2201 xyz_model_increment = 0
2202 orig_model_num = []
2203 mol_conts = []
2204 orig_mol_num = []
2205 new_mol_name = []
2206 for model_records in self._parse_models_xyz(file_path, verbosity=verbosity):
2207
2208 xyz_model_increment = xyz_model_increment +1
2209
2210
2211 if read_model and xyz_model_increment not in read_model:
2212 continue
2213
2214
2215 orig_model_num.append(model_index)
2216
2217
2218 if read_mol and mol_index not in read_mol:
2219 continue
2220
2221
2222 if set_mol_name:
2223 new_mol_name.append(set_mol_name[mol_index])
2224 else:
2225 if mol_index == 0:
2226
2227 new_mol_name.append(file_root(file) + '_mol' + repr(mol_index+1))
2228
2229
2230 orig_mol_num.append(mol_index)
2231
2232
2233 mol = MolContainer()
2234
2235
2236 mol.fill_object_from_xyz(model_records)
2237
2238
2239 mol_conts.append([])
2240 mol_conts[model_index].append(mol)
2241
2242
2243 mol_index = mol_index + 1
2244
2245
2246 model_index = model_index + 1
2247
2248
2249 orig_mol_num = [[0]]
2250 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, file_path_abs=path_abs, verbosity=verbosity)
2251
2252
2253 return True
2254
2255
2257 """Calculate the mean structure from all models in the structural data object."""
2258
2259
2260 num = self.num_models()
2261 self.add_model()
2262 mean_model = self.structural_data[-1]
2263
2264
2265 selection = self.selection()
2266
2267
2268 for mol_index, i in selection.loop():
2269
2270 mean_model.mol[mol_index].x[i] = 0.0
2271 mean_model.mol[mol_index].y[i] = 0.0
2272 mean_model.mol[mol_index].z[i] = 0.0
2273
2274
2275 for model_index in range(num):
2276 model_cont = self.structural_data[model_index]
2277 mean_model.mol[mol_index].x[i] += model_cont.mol[mol_index].x[i]
2278 mean_model.mol[mol_index].y[i] += model_cont.mol[mol_index].y[i]
2279 mean_model.mol[mol_index].z[i] += model_cont.mol[mol_index].z[i]
2280
2281
2282 mean_model.mol[mol_index].x[i] /= num
2283 mean_model.mol[mol_index].y[i] /= num
2284 mean_model.mol[mol_index].z[i] /= num
2285
2286
2287 for model_index in reversed(list(range(num))):
2288 self.delete(model=self.structural_data[model_index].num)
2289
2290
2292 """Create a list of all models.
2293
2294 @return: The list of all models.
2295 @rtype: list of int
2296 """
2297
2298
2299 models = []
2300 for model in self.model_loop():
2301 models.append(model.num)
2302
2303
2304 return models
2305
2306
2308 """Generator method for looping over the models in numerical order.
2309
2310 @keyword model: Limit the loop to a single number.
2311 @type model: int
2312 @return: The model structural object.
2313 @rtype: ModelContainer container
2314 """
2315
2316
2317 if model:
2318 for i in range(len(self.structural_data)):
2319 if self.structural_data[i].num == model:
2320 yield self.structural_data[i]
2321
2322
2323 else:
2324
2325 model_nums = []
2326 for i in range(len(self.structural_data)):
2327 if self.structural_data[i].num != None:
2328 model_nums.append(self.structural_data[i].num)
2329
2330
2331 if model_nums:
2332 model_nums.sort()
2333
2334
2335 for model_num in model_nums:
2336
2337 for i in range(len(self.structural_data)):
2338
2339 if self.structural_data[i].num == model_num:
2340 yield self.structural_data[i]
2341
2342
2343 if not model_nums:
2344 yield self.structural_data[0]
2345
2346
2348 """Method for returning the number of models.
2349
2350 @return: The number of models in the structural object.
2351 @rtype: int
2352 """
2353
2354 return len(self.structural_data)
2355
2356
2358 """Method for returning the number of molecules.
2359
2360 @return: The number of molecules in the structural object.
2361 @rtype: int
2362 """
2363
2364
2365 if self.empty():
2366 return 0
2367
2368
2369 self.validate()
2370
2371
2372 return len(self.structural_data[0].mol)
2373
2374
2376 """Generate and return the one letter code sequence for the given molecule.
2377
2378 @keyword mol_name: The name of the molecule to return the one letter codes for.
2379 @type mol_name: str
2380 @keyword selection: The internal structural selection object. This is obtained by calling the selection() method with the atom ID string.
2381 @type selection: lib.structure.internal.Internal_selection instance
2382 @return: The one letter code sequence for the given molecule.
2383 @rtype: str
2384 """
2385
2386
2387 codes = ''
2388
2389
2390 model = self.structural_data[0]
2391
2392
2393 res_nums = []
2394
2395
2396 for mol_index, i in selection.loop():
2397
2398 mol = model.mol[mol_index]
2399
2400
2401 if mol_name and mol_name != mol.mol_name:
2402 continue
2403
2404
2405 if mol.res_num[i] in res_nums:
2406 continue
2407
2408
2409 codes += aa_codes_three_to_one(mol.res_name[i])
2410 res_nums.append(mol.res_num[i])
2411
2412
2413 return codes
2414
2415
2416 - 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, file_path_abs=None, helices=None, sheets=None, verbosity=1, merge=False):
2417 """From the given structural data, expand the structural data data structure.
2418
2419 @param data_matrix: A matrix of structural objects. The first dimension is the models, the second is the molecules.
2420 @type data_matrix: list of lists of structural objects
2421 @keyword orig_model_num: The original model numbers (for storage).
2422 @type orig_model_num: list of int
2423 @keyword set_model_num: The new model numbers (for model renumbering).
2424 @type set_model_num: list of int
2425 @keyword orig_mol_num: The original molecule numbers (for storage). The dimensions match the data matrix.
2426 @type orig_mol_num: list of list of int
2427 @keyword set_mol_name: The new molecule names.
2428 @type set_mol_name: list of str
2429 @keyword file_name: The name of the file from which the molecular data has been extracted.
2430 @type file_name: None or str
2431 @keyword file_path: The full path to the file specified by 'file_name'.
2432 @type file_path: None or str
2433 @keyword file_path_abs: The absolute path to the file specified by 'file_name'. This is a fallback mechanism in case results or save files are located somewhere other than the working directory.
2434 @type file_path_abs: None or str
2435 @keyword helices: The helix secondary structure information to store. The first dimension corresponds to each helix element. The second dimension consists of the helix_id, mol_init_index, init_res_name, init_seq_num, mol_end_index, end_res_name, end_seq_num, helix_class, and length.
2436 @type helices: list of lists
2437 @keyword sheets: The sheet secondary structure information to store.
2438 @type sheets: list of lists
2439 @keyword verbosity: The amount of information to print to screen. Zero corresponds to minimal output while higher values increase the amount of output. The default value is 1.
2440 @type verbosity: int
2441 @keyword merge: A flag which if set to True will try to merge the structure into the currently loaded structures.
2442 @type merge: bool
2443 """
2444
2445
2446 if len(orig_model_num) != len(data_matrix):
2447 raise RelaxError("Structural data mismatch, %s original models verses %s in the structural data." % (len(orig_model_num), len(data_matrix)))
2448
2449
2450 for i in range(len(data_matrix)):
2451 if len(orig_mol_num[i]) != len(data_matrix[i]):
2452 raise RelaxError("Structural data mismatch, %s original molecules verses %s in the structural data." % (len(orig_mol_num[i]), len(data_matrix[i])))
2453
2454
2455 if not set_model_num:
2456 set_model_num = orig_model_num
2457
2458
2459 if len(set_model_num) != len(data_matrix):
2460 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)))
2461
2462
2463 if len(set_mol_name) != len(data_matrix[0]):
2464 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])))
2465
2466
2467 for i in range(len(set_model_num)):
2468
2469 if merge:
2470 continue
2471
2472
2473 if not set_model_num[i] in self.structural_data.current_models:
2474 continue
2475
2476
2477 index = self.structural_data.current_models.index(set_model_num[i])
2478 for j in range(len(self.structural_data[index].mol)):
2479 if self.structural_data[index].mol[j].mol_name in set_mol_name:
2480 raise RelaxError("The molecule '%s' of model %s already exists." % (self.structural_data[i].mol[j].mol_name, self.structural_data[i].num))
2481
2482
2483 store_mol_num_orig = []
2484 store_mol_num_new = []
2485 for i in range(len(set_model_num)):
2486 store_mol_num_orig.append([])
2487 store_mol_num_new.append([])
2488
2489
2490 if set_model_num[i] not in self.structural_data.current_models:
2491
2492 self.structural_data.add_item(set_model_num[i])
2493
2494
2495 model = self.structural_data[-1]
2496
2497
2498 else:
2499 model = self.structural_data[self.structural_data.current_models.index(set_model_num[i])]
2500
2501
2502 for j in range(len(set_mol_name)):
2503
2504 found = False
2505 merge_new = True
2506 for k in range(len(model.mol)):
2507 if model.mol[k].mol_name == set_mol_name[j]:
2508 found = True
2509 index = k
2510 if not found:
2511 merge_new = False
2512
2513
2514 if verbosity:
2515
2516 orig_model_text = ''
2517 if orig_model_num[i] != None:
2518 orig_model_text = " of model %s" % orig_model_num[i]
2519 new_model_text = ''
2520 if set_model_num[i] != None:
2521 if merge_new:
2522 new_model_text += ' of'
2523 else:
2524 new_model_text += ' to'
2525 new_model_text += ' model %s' % set_model_num[i]
2526
2527
2528 if merge_new:
2529 print("Merging with molecule '%s'%s (from the original molecule number %s%s)." % (set_mol_name[j], new_model_text, orig_mol_num[i][j], orig_model_text))
2530 else:
2531 print("Adding molecule '%s'%s (from the original molecule number %s%s)." % (set_mol_name[j], new_model_text, orig_mol_num[i][j], orig_model_text))
2532
2533
2534 if not merge_new:
2535 index = len(model.mol)
2536
2537
2538 store_mol_num_new[i].append(index+1)
2539 store_mol_num_orig[i].append(orig_mol_num[i][j])
2540
2541
2542 if model.num != self.structural_data[0].num and self.structural_data[0].mol[index].mol_name != set_mol_name[j]:
2543 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))
2544
2545
2546 if merge_new:
2547 mol = model.mol.merge_item(mol_name=set_mol_name[j], mol_cont=data_matrix[i][j])
2548 else:
2549 mol = model.mol.add_item(mol_name=set_mol_name[j], mol_cont=data_matrix[i][j])
2550
2551
2552 mol.mol_name = set_mol_name[j]
2553 mol.file_name = file_name
2554 mol.file_path = file_path
2555 mol.file_path_abs = file_path_abs
2556 mol.file_mol_num = orig_mol_num[i][j]
2557 mol.file_model = orig_model_num[i]
2558
2559
2560 mol._sort()
2561
2562
2563 for model_index in range(len(store_mol_num_new)):
2564 if len(store_mol_num_new[model_index]) != len(store_mol_num_orig[model_index]):
2565 raise RelaxFault
2566
2567
2568 if helices != None:
2569
2570 if not hasattr(self, 'helices'):
2571 self.helices = []
2572
2573
2574 for i in range(len(helices)):
2575
2576 self.helices.append(helices[i])
2577
2578
2579 mol_init_index = store_mol_num_new[0][store_mol_num_orig[0].index(helices[i][1]+1)] - 1
2580 self.helices[-1][1] = mol_init_index
2581
2582
2583 mol_end_index = store_mol_num_new[0][store_mol_num_orig[0].index(helices[i][4]+1)] - 1
2584 self.helices[-1][4] = mol_end_index
2585
2586
2587 if sheets != None:
2588
2589 if not hasattr(self, 'sheets'):
2590 self.sheets = []
2591
2592
2593 for i in range(len(sheets)):
2594
2595 self.sheets.append(sheets[i])
2596
2597
2598 mol_init_index = store_mol_num_new[0][store_mol_num_orig[0].index(sheets[i][4]+1)] - 1
2599 self.sheets[-1][4] = mol_init_index
2600
2601
2602
2603 mol_end_index = store_mol_num_new[0][store_mol_num_orig[0].index(sheets[i][8]+1)] - 1
2604 self.sheets[-1][8] = mol_end_index
2605
2606
2607 if sheets[i][14] != None:
2608 mol_cur_index = store_mol_num_new[0][store_mol_num_orig[0].index(sheets[i][14]+1)] - 1
2609 self.sheets[-1][14] = mol_cur_index
2610
2611
2612 if sheets[i][19] != None:
2613 mol_prev_index = store_mol_num_new[0][store_mol_num_orig[0].index(sheets[i][19]+1)] - 1
2614 self.sheets[-1][19] = mol_prev_index
2615
2616
2617 - def rotate(self, R=None, origin=None, model=None, selection=None):
2618 """Rotate the structural information about the given origin.
2619
2620 @keyword R: The forwards rotation matrix.
2621 @type R: numpy 3D, rank-2 array
2622 @keyword origin: The origin of the rotation.
2623 @type origin: numpy 3D, rank-1 array
2624 @keyword model: The model to rotate. If None, all models will be rotated.
2625 @type model: int
2626 @keyword selection: The internal structural selection object. This is obtained by calling the selection() method with the atom ID string.
2627 @type selection: lib.structure.internal.Internal_selection instance
2628 """
2629
2630
2631 for model_cont in self.model_loop(model):
2632
2633 for mol_index, i in selection.loop():
2634 mol = model_cont.mol[mol_index]
2635
2636
2637 vect = array([mol.x[i], mol.y[i], mol.z[i]], float64) - origin
2638
2639
2640 rot_vect = dot(R, vect)
2641
2642
2643 pos = rot_vect + origin
2644 mol.x[i] = pos[0]
2645 mol.y[i] = pos[1]
2646 mol.z[i] = pos[2]
2647
2648
2649 - def selection(self, atom_id=None, inv=False):
2650 """Convert the atom ID string into a special internal selection object for speed.
2651
2652 @keyword atom_id: The molecule, residue, and atom identifier string. Only atoms matching this selection will be used.
2653 @type atom_id: str or None
2654 @keyword inv: A flag which if True will cause the selection to be inverted.
2655 @type inv: bool
2656 @return: The internal structural selection object.
2657 @rtype: Internal_selection instance
2658 """
2659
2660
2661 selection = Internal_selection()
2662
2663
2664 mol_token, res_token, spin_token = tokenise(atom_id)
2665
2666
2667 sel_obj = None
2668 if atom_id:
2669 sel_obj = Selection(atom_id)
2670
2671
2672 model = self.structural_data[0]
2673
2674
2675 for mol_index in range(len(model.mol)):
2676 mol = model.mol[mol_index]
2677
2678
2679 if not inv:
2680 if sel_obj and not sel_obj.contains_mol(mol.mol_name):
2681 continue
2682
2683
2684 else:
2685 if (not sel_obj) or (mol_token != None and sel_obj.contains_mol(mol.mol_name)):
2686 continue
2687
2688
2689 selection.add_mol(mol_index=mol_index)
2690
2691
2692 for i in range(len(mol.atom_num)):
2693
2694 if not inv:
2695 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):
2696 continue
2697
2698
2699 else:
2700 if (not sel_obj) or sel_obj.contains_spin(mol.atom_num[i], mol.atom_name[i], mol.res_num[i], mol.res_name[i], mol.mol_name):
2701 continue
2702
2703
2704 selection.add_atom(mol_index=mol_index, atom_index=i)
2705
2706
2707 return selection
2708
2709
2710 - def set_model(self, model_orig=None, model_new=None):
2711 """Set or reset the model number.
2712 @keyword model_orig: The original model number. Leave as None if no models are currently present.
2713 @type model_orig: None or int
2714 @keyword model_new: The new model number to set the model to.
2715 @type model_new: int
2716 """
2717
2718
2719 if model_orig == None and self.num_models() != 1:
2720 raise RelaxError("If the original model number is not supplied, only one model in the current structural object is allowed, but %s were found." % self.num_models())
2721
2722
2723 if model_orig == None:
2724 self.structural_data[0].num = model_new
2725 return
2726
2727
2728 set = False
2729 for i in range(len(self.structural_data)):
2730 if model_orig == self.structural_data[i].num:
2731 self.structural_data[i].num = model_new
2732 set = True
2733
2734
2735 if not set:
2736 raise RelaxError("The original model number %s could not be found in the structural object." % model_orig)
2737
2738
2739 - def target_mol_name(self, set=None, target=None, index=None, mol_num=None, file=None):
2740 """Add the new molecule name to the target data structure.
2741
2742 @keyword set: The list of new molecule names. If not supplied, the names will be generated from the file name.
2743 @type set: None or list of str
2744 @keyword target: The target molecule name data structure to which the new name will be appended.
2745 @type target: list
2746 @keyword index: The molecule index, matching the set argument.
2747 @type index: int
2748 @keyword mol_num: The molecule number.
2749 @type mol_num: int
2750 @keyword file: The name of the file, excluding all directories.
2751 @type file: str
2752 """
2753
2754
2755 if set:
2756 target.append(set[index])
2757 else:
2758
2759 target.append(file_root(file) + '_mol' + repr(mol_num))
2760
2761
2762 - def translate(self, T=None, model=None, selection=None):
2763 """Displace the structural information by the given translation vector.
2764
2765 @keyword T: The translation vector.
2766 @type T: numpy 3D, rank-1 array
2767 @keyword model: The model to rotate. If None, all models will be rotated.
2768 @type model: int
2769 @keyword selection: The internal structural selection object. This is obtained by calling the selection() method with the atom ID string.
2770 @type selection: lib.structure.internal.Internal_selection instance
2771 """
2772
2773
2774 for model_cont in self.model_loop(model):
2775
2776 for mol_index, i in selection.loop():
2777 mol = model_cont.mol[mol_index]
2778
2779
2780 mol.x[i] = mol.x[i] + T[0]
2781 mol.y[i] = mol.y[i] + T[1]
2782 mol.z[i] = mol.z[i] + T[2]
2783
2784
2785 - def to_xml(self, doc, element):
2786 """Prototype method for converting the structural object to an XML representation.
2787
2788 @param doc: The XML document object.
2789 @type doc: xml.dom.minidom.Document instance
2790 @param element: The element to add the alignment tensors XML element to.
2791 @type element: XML element object
2792 """
2793
2794
2795 str_element = doc.createElement('structure')
2796 element.appendChild(str_element)
2797
2798
2799 str_element.setAttribute('desc', 'Structural information')
2800
2801
2802 if not self.structural_data.is_empty():
2803 self.structural_data.to_xml(doc, str_element)
2804
2805
2806 metadata = ['helices', 'sheets']
2807 for name in metadata:
2808
2809 if not hasattr(self, name):
2810 continue
2811
2812
2813 obj = getattr(self, name)
2814
2815
2816 sub_elem = doc.createElement(name)
2817 str_element.appendChild(sub_elem)
2818
2819
2820 object_to_xml(doc, sub_elem, value=obj)
2821
2822
2823 if hasattr(self, 'displacements'):
2824
2825 disp_element = doc.createElement('displacements')
2826 str_element.appendChild(disp_element)
2827
2828
2829 disp_element.setAttribute('desc', 'The rotational and translational displacements between models')
2830
2831
2832 self.displacements.to_xml(doc, disp_element)
2833
2834
2835 blacklist = ['structural_data', 'displacements'] + metadata + list(Internal.__dict__.keys()) + list(self.__class__.__dict__.keys()) + list(object.__dict__.keys())
2836
2837
2838 fill_object_contents(doc, str_element, object=self, blacklist=blacklist)
2839
2840
2842 """Check that the models are consistent with each other.
2843
2844 This checks that the primary structure is identical between the models.
2845
2846
2847 @keyword verbosity: If 0, then all printouts will be silenced.
2848 @type verbosity: int
2849 """
2850
2851
2852 if verbosity:
2853 print("Validating models:")
2854
2855
2856 for i in range(len(self.structural_data)):
2857
2858 if len(self.structural_data[0].mol) != len(self.structural_data[i].mol):
2859 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)))
2860
2861
2862 for j in range(len(self.structural_data[i].mol)):
2863
2864 mol = self.structural_data[i].mol[j]
2865 mol_ref = self.structural_data[0].mol[j]
2866
2867
2868 if mol.mol_name != mol_ref.mol_name:
2869 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))
2870
2871
2872 for k in range(len(mol.atom_name)):
2873
2874 same = True
2875 if mol.chain_id[k] != mol_ref.chain_id[k]:
2876 same = False
2877 elif mol.atom_num[k] != mol_ref.atom_num[k]:
2878 same = False
2879 elif mol.atom_name[k] != mol_ref.atom_name[k]:
2880 same = False
2881 elif mol.res_name[k] != mol_ref.res_name[k]:
2882 same = False
2883 elif mol.res_num[k] != mol_ref.res_num[k]:
2884 same = False
2885 elif mol.seg_id[k] != mol_ref.seg_id[k]:
2886 same = False
2887 elif mol.element[k] != mol_ref.element[k]:
2888 same = False
2889
2890
2891 if not same:
2892 print("%-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]), ''))
2893 print("%-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]), ''))
2894 raise RelaxError("The atoms of model %s do not match the first model." % self.structural_data[i].num)
2895
2896
2897 if verbosity:
2898 print("\tAll models are consistent")
2899
2900
2902 """Method for the creation of a PDB file from the structural data.
2903
2904 A number of PDB records including HET, HETNAM, FORMUL, HELIX, SHEET, HETATM, TER, CONECT, MASTER, and END are created. To create the non-standard residue records HET, HETNAM, and FORMUL, the data structure 'het_data' is created. It is an array of arrays where the first dimension corresponds to a different residue and the second dimension has the elements:
2905
2906 0. Residue number.
2907 1. Residue name.
2908 2. Chain ID.
2909 3. Total number of atoms in the residue.
2910 4. Number of H atoms in the residue.
2911 5. Number of C atoms in the residue.
2912
2913
2914 @param file: The PDB file object. This object must be writable.
2915 @type file: file object
2916 @keyword model_num: The model to place into the PDB file. If not supplied, then all models will be placed into the file.
2917 @type model_num: None or int
2918 """
2919
2920
2921 self.validate()
2922
2923
2924 print("\nCreating the PDB records\n")
2925
2926
2927 print("REMARK")
2928 pdb_write.remark(file, num=4, remark="This file complies with format v. 3.30, Jul-2011.")
2929 pdb_write.remark(file, num=40, remark=None)
2930 pdb_write.remark(file, num=40, remark="Created using relax (http://www.nmr-relax.com).")
2931 pdb_write.remark(file, num=40, remark=None)
2932 if RELAX_VERSION:
2933 pdb_write.remark(file, num=40, remark="relax version %s." % RELAX_VERSION)
2934 pdb_write.remark(file, num=40, remark="Created on %s." % asctime())
2935 num_remark = 2
2936
2937
2938 model_records = False
2939 for model in self.model_loop():
2940 if hasattr(model, 'num') and model.num != None:
2941 model_records = True
2942
2943
2944
2945
2946
2947
2948
2949 het_data = []
2950 het_data_coll = []
2951
2952
2953 index = 0
2954 for mol in self.structural_data[0].mol_loop():
2955
2956 self._validate_data_arrays(mol)
2957
2958
2959 het_data.append([])
2960
2961
2962 for i in range(len(mol.atom_name)):
2963
2964 if mol.pdb_record[i] != 'HETATM' or mol.res_name[i] == None:
2965 continue
2966
2967
2968 if mol.res_name[i] == 'HOH':
2969 continue
2970
2971
2972
2973 if not het_data[index] or not mol.res_num[i] == het_data[index][-1][0]:
2974 het_data[index].append([mol.res_num[i], mol.res_name[i], CHAIN_ID_LIST[index], 0, []])
2975
2976
2977 if het_data[index][-1][2] == None:
2978 het_data[index][-1][2] = ''
2979
2980
2981 het_data[index][-1][3] = het_data[index][-1][3] + 1
2982
2983
2984 entry = False
2985 for j in range(len(het_data[index][-1][4])):
2986 if mol.element[i] == het_data[index][-1][4][j][0]:
2987 entry = True
2988
2989
2990 if not entry:
2991 het_data[index][-1][4].append([mol.element[i], 0])
2992
2993
2994 for j in range(len(het_data[index][-1][4])):
2995 if mol.element[i] == het_data[index][-1][4][j][0]:
2996 het_data[index][-1][4][j][1] = het_data[index][-1][4][j][1] + 1
2997
2998
2999 for i in range(len(het_data[index])):
3000
3001 found = False
3002 for j in range(len(het_data_coll)):
3003
3004 if het_data[index][i][0] != het_data_coll[j][0]:
3005 continue
3006
3007
3008 if het_data_coll[j][2] != het_data[index][i][2]:
3009 continue
3010
3011
3012 found = True
3013
3014
3015 if not found:
3016 het_data_coll.append(het_data[index][i])
3017
3018
3019 index = index + 1
3020
3021
3022
3023
3024
3025
3026 print("HET")
3027
3028
3029 for het in het_data_coll:
3030 pdb_write.het(file, het_id=het[1], chain_id=het[2], seq_num=het[0], num_het_atoms=het[3])
3031
3032
3033
3034
3035
3036
3037 print("HETNAM")
3038
3039
3040 residues = []
3041 for het in het_data_coll:
3042
3043 if het[1] in residues:
3044 continue
3045 else:
3046 residues.append(het[1])
3047
3048
3049 chemical_name = self._get_chemical_name(het[1])
3050 if not chemical_name:
3051 chemical_name = 'Unknown'
3052
3053
3054 pdb_write.hetnam(file, het_id=het[1], text=chemical_name)
3055
3056
3057
3058
3059
3060
3061 print("FORMUL")
3062
3063
3064 residues = []
3065 for i in range(len(het_data_coll)):
3066
3067 het = het_data_coll[i]
3068
3069
3070 if het[1] in residues:
3071 continue
3072 else:
3073 residues.append(het[1])
3074
3075
3076 formula = ''
3077
3078
3079 for atom_count in het[4]:
3080 formula = formula + atom_count[0] + repr(atom_count[1])
3081
3082
3083 pdb_write.formul(file, comp_num=i+1, het_id=het[1], text=formula)
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093 if hasattr(self, 'helices') and len(self.helices):
3094
3095 print("HELIX")
3096
3097
3098 index = 1
3099 for helix_id, mol_init_index, init_res_name, init_seq_num, mol_end_index, end_res_name, end_seq_num, helix_class, length in self.helices:
3100
3101 if mol_init_index == None:
3102 mol_init_index = 0
3103 if mol_end_index == None:
3104 mol_end_index = 0
3105
3106 pdb_write.helix(file, ser_num=index, helix_id=helix_id, init_chain_id=CHAIN_ID_LIST[mol_init_index], init_res_name=init_res_name, init_seq_num=init_seq_num, end_chain_id=CHAIN_ID_LIST[mol_end_index], end_res_name=end_res_name, end_seq_num=end_seq_num, helix_class=helix_class, length=length)
3107 index += 1
3108
3109
3110
3111
3112 if hasattr(self, 'sheets') and len(self.sheets):
3113
3114 print("SHEET")
3115
3116
3117 index = 1
3118 for strand, sheet_id, num_strands, init_res_name, mol_init_index, init_seq_num, init_icode, end_res_name, mol_end_index, end_seq_num, end_icode, sense, cur_atom, cur_res_name, mol_cur_index, cur_res_seq, cur_icode, prev_atom, prev_res_name, mol_prev_index, prev_res_seq, prev_icode in self.sheets:
3119
3120 if mol_init_index == None:
3121 mol_init_index = 0
3122 if mol_end_index == None:
3123 mol_end_index = 0
3124 init_chain_id = CHAIN_ID_LIST[mol_init_index]
3125 end_chain_id = CHAIN_ID_LIST[mol_end_index]
3126 cur_chain_id = None
3127 if mol_cur_index != None:
3128 cur_chain_id = CHAIN_ID_LIST[mol_cur_index]
3129 prev_chain_id = None
3130 if mol_prev_index != None:
3131 prev_chain_id = CHAIN_ID_LIST[mol_prev_index]
3132
3133
3134 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)
3135 index += 1
3136
3137
3138
3139
3140
3141
3142
3143 if model_records:
3144 print("\nMODEL records:")
3145
3146
3147 for model in self.model_loop(model_num):
3148
3149 num_hetatm = 0
3150 num_atom = 0
3151 num_ter = 0
3152 ser_num = 1
3153
3154
3155
3156
3157 if model_records:
3158
3159 sys.stdout.write('.')
3160
3161
3162 pdb_write.model(file, serial=model.num)
3163
3164
3165
3166
3167
3168
3169 index = 0
3170 for mol in model.mol_loop():
3171
3172 if not model_records:
3173 print("ATOM, HETATM, TER")
3174
3175
3176 atom_record = False
3177 for i in range(len(mol.atom_name)):
3178
3179 if mol.pdb_record[i] in [None, 'ATOM']:
3180 atom_record = True
3181
3182
3183 pdb_write.atom(file, serial=ser_num, name=mol.atom_name[i], res_name=mol.res_name[i], chain_id=CHAIN_ID_LIST[index], 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])
3184 num_atom += 1
3185 ser_num += 1
3186
3187
3188 ter_num = ser_num + 1
3189 ter_name = mol.res_name[i]
3190 ter_chain_id = CHAIN_ID_LIST[index]
3191 ter_res_num = mol.res_num[i]
3192
3193
3194 if atom_record:
3195 pdb_write.ter(file, serial=ser_num, res_name=ter_name, chain_id=CHAIN_ID_LIST[index], res_seq=ter_res_num)
3196 num_ter += 1
3197 ser_num += 1
3198
3199
3200 count_shift = False
3201 for i in range(len(mol.atom_name)):
3202
3203 if mol.pdb_record[i] == 'HETATM':
3204
3205 if atom_record and ser_num == ter_num:
3206 count_shift = True
3207
3208
3209 pdb_write.hetatm(file, serial=ser_num, name=self._translate(mol.atom_name[i]), res_name=mol.res_name[i], chain_id=CHAIN_ID_LIST[index], 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])
3210 num_hetatm += 1
3211 ser_num += 1
3212
3213
3214 index += 1
3215
3216
3217
3218
3219
3220 if model_records:
3221 if not model_records:
3222 print("ENDMDL")
3223 pdb_write.endmdl(file)
3224
3225
3226
3227
3228
3229
3230 if model_records:
3231 sys.stdout.write('\n')
3232 print("CONECT")
3233
3234
3235 num_conect = 0
3236
3237
3238 atom_counts = [0]
3239 index = 0
3240 for mol in self.structural_data[0].mol_loop():
3241 if index == 0:
3242 atom_counts.append(len(mol.atom_name))
3243 else:
3244 atom_counts.append(atom_counts[index] + len(mol.atom_name))
3245 index += 1
3246
3247
3248 index = 0
3249 for mol in self.structural_data[0].mol_loop():
3250
3251 for i in range(len(mol.atom_name)):
3252
3253 if not len(mol.bonded[i]):
3254 continue
3255
3256
3257 flush = 0
3258 bonded_index = 0
3259 bonded = ['', '', '', '']
3260 bonded_shifted = ['', '', '', '']
3261
3262
3263 for j in range(len(mol.bonded[i])):
3264
3265 if j == len(mol.bonded[i])-1:
3266 flush = True
3267
3268
3269 if bonded_index == 3:
3270 flush = True
3271
3272
3273 bonded[bonded_index] = mol.bonded[i][j]
3274
3275
3276 bonded_index = bonded_index + 1
3277
3278
3279 if flush:
3280
3281 for k in range(4):
3282 if bonded[k] != '':
3283 bonded_shifted[k] = bonded[k] + 1 + atom_counts[index]
3284
3285
3286 pdb_write.conect(file, serial=i+1+atom_counts[index], bonded1=bonded_shifted[0], bonded2=bonded_shifted[1], bonded3=bonded_shifted[2], bonded4=bonded_shifted[3])
3287
3288
3289 flush = False
3290 bonded_index = 0
3291 bonded = ['', '', '', '']
3292
3293
3294 num_conect = num_conect + 1
3295
3296
3297 index += 1
3298
3299
3300
3301
3302
3303 print("\nMASTER")
3304 pdb_write.master(file, num_het=len(het_data_coll), num_coord=num_atom+num_hetatm, num_ter=num_ter, num_conect=num_conect)
3305
3306
3307
3308
3309
3310 print("END")
3311 pdb_write.end(file)
3312
3313
3315 """Check the integrity of the structural data.
3316
3317 The number of molecules must be the same in all models.
3318 """
3319
3320
3321 num_mols = len(self.structural_data[0].mol)
3322
3323
3324 for i in range(1, len(self.structural_data)):
3325
3326 model_i = self.structural_data[i]
3327
3328
3329 if num_mols != len(model_i.mol):
3330 raise RelaxError("The structural object is not valid - the number of molecules is not the same for all models.")
3331
3332
3333 for j in range(len(model_i.mol)):
3334 if model_i.mol[j].mol_name != self.structural_data[0].mol[j].mol_name:
3335 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))
3336