1 | #!/usr/bin/env python |
---|
2 | # -*- coding: utf-8 -*- |
---|
3 | ########### SVN repository information ################### |
---|
4 | # $Date: 2021-11-18 22:39:00 +0000 (Thu, 18 Nov 2021) $ |
---|
5 | # $Author: vondreele $ |
---|
6 | # $Revision: 5090 $ |
---|
7 | # $URL: trunk/exports/G2export_PDB.py $ |
---|
8 | # $Id: G2export_PDB.py 5090 2021-11-18 22:39:00Z vondreele $ |
---|
9 | ########### SVN repository information ################### |
---|
10 | ''' |
---|
11 | *Module G2export_PDB: Macromolecular export* |
---|
12 | -------------------------------------------- |
---|
13 | Code to export a phase into the venerated/obsolete (pick one) |
---|
14 | ASCII PDB format. Also defines exporter :class:`ExportPhaseCartXYZ` |
---|
15 | which writes atom positions in orthogonal coordinates for a phase. |
---|
16 | |
---|
17 | ''' |
---|
18 | from __future__ import division, print_function |
---|
19 | import numpy as np |
---|
20 | import os.path |
---|
21 | import GSASIIpath |
---|
22 | GSASIIpath.SetVersionNumber("$Revision: 5090 $") |
---|
23 | import GSASIIIO as G2IO |
---|
24 | import GSASIIlattice as G2lat |
---|
25 | |
---|
26 | class ExportPhasePDB(G2IO.ExportBaseclass): |
---|
27 | '''Used to create a PDB file for a phase |
---|
28 | |
---|
29 | :param wx.Frame G2frame: reference to main GSAS-II frame |
---|
30 | ''' |
---|
31 | def __init__(self,G2frame): |
---|
32 | super(self.__class__,self).__init__( # fancy way to say <parentclass>.__init__ |
---|
33 | G2frame=G2frame, |
---|
34 | formatName = 'PDB', |
---|
35 | extension='.PDB', |
---|
36 | longFormatName = 'Export phase as .PDB file' |
---|
37 | ) |
---|
38 | self.exporttype = ['phase'] |
---|
39 | self.multiple = True |
---|
40 | |
---|
41 | def Exporter(self,event=None): |
---|
42 | '''Export as a PDB file |
---|
43 | ''' |
---|
44 | |
---|
45 | def PDBheader(): |
---|
46 | self.Write("HEADER phase "+str(phasenam)+" from "+str(self.G2frame.GSASprojectfile)) |
---|
47 | self.Write("TITLE") |
---|
48 | self.Write("COMPND") |
---|
49 | self.Write("SOURCE") |
---|
50 | self.Write("KEYWDS") |
---|
51 | self.Write("EXPDTA X-RAY POWDER DIFFRACTION") |
---|
52 | self.Write("REVDAT") |
---|
53 | self.Write("JRNL") |
---|
54 | self.Write("REMARK 1") |
---|
55 | self.Write("REMARK 2") |
---|
56 | self.Write("REMARK 2 RESOLUTION. 2.66 ANGSTROMS.") |
---|
57 | self.Write("REMARK 2 POWDER DIFFRACTION MINIMUM D-SPACING.") |
---|
58 | |
---|
59 | def PDBremark250(): |
---|
60 | self.Write('REMARK 250') |
---|
61 | self.Write('REMARK 250 REFINEMENT.') |
---|
62 | self.Write('REMARK 250 PROGRAM : GSAS-II') |
---|
63 | self.Write('REMARK 250 AUTHORS : TOBY & VON DREELE') |
---|
64 | self.Write('REMARK 250 REFRENCE : J. APPL. CRYST. 46, 544-549(2013)') |
---|
65 | self.Write('REMARK 250') |
---|
66 | self.Write('REMARK 250 DATA USED IN REFINEMENT') |
---|
67 | self.Write('REMARK 250 RESOLUTION RANGE HIGH (ANGSTROMS) : x.xx') |
---|
68 | self.Write('REMARK 250 RESOLUTION RANGE LOW (ANGSTROMS) : xx.xx') |
---|
69 | self.Write('REMARK 250 POWDER DIFFRACTION DATA.') |
---|
70 | self.Write('REMARK 250') |
---|
71 | self.Write('REMARK 250 FIT TO DATA USED IN REFINEMENT') |
---|
72 | self.Write('REMARK 250 NUMBER OF POWDER PATTERNS : x') |
---|
73 | self.Write('REMARK 250 PROFILE R VALUES (%) : x.xx') |
---|
74 | self.Write('REMARK 250 WEIGHTED PROFILE R VALUES (%) : x.xx') |
---|
75 | self.Write('REMARK 250 F**2 R VALUES (%) : xx.xx') |
---|
76 | self.Write('REMARK 250 NUMBERS OF POWDER PATTERN POINTS : xxxx') |
---|
77 | self.Write('REMARK 250 NUMBERS OF REFLECTIONS : xxxx') |
---|
78 | self.Write('REMARK 250 TOTAL NUMBER OF POWDER POINTS : xxxx') |
---|
79 | self.Write('REMARK 250') |
---|
80 | self.Write('REMARK 250 NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT.') |
---|
81 | self.Write('REMARK 250 PROTEIN ATOMS : xxxx') |
---|
82 | self.Write('REMARK 250 NUCLEIC ACID ATOMS : xxxx') |
---|
83 | self.Write('REMARK 250 HETEROGEN ATOMS : xxxx') |
---|
84 | self.Write('REMARK 250 SOLVENT ATOMS : xxxx') |
---|
85 | self.Write('REMARK 250') |
---|
86 | self.Write('REMARK 250 MODEL REFINEMENT.') |
---|
87 | self.Write('REMARK 250 NUMBER OF LEAST-SQUARES PARAMETERS : xxxx') |
---|
88 | self.Write('REMARK 250 NUMBER OF RESTRAINTS : xxxx') |
---|
89 | self.Write('REMARK 250') |
---|
90 | self.Write('REMARK 250 RMS DEVIATIONS FROM RESTRAINT TARGET VALUES. NUMBER.') |
---|
91 | self.Write('REMARK 250 BOND ANGLES (DEG) : x.xx xxx') |
---|
92 | # self.Write('REMARK 250 ANTI-BUMPING DISTANCE RESTRAINTS (A) :x.xxx xxx') |
---|
93 | # self.Write('REMARK 250 HYDROGEN BOND DISTANCE RESTRAINTS (A) :x.xxx xxx') |
---|
94 | self.Write('REMARK 250 INTERATOMIC DISTANCES (A) :x.xxx xxx') |
---|
95 | self.Write('REMARK 250 DISTANCES FROM RESTRAINT PLANES (A) :x.xxx xxx') |
---|
96 | self.Write('REMARK 250 TORSION PSEUDOPOTENTIAL RESTRAINTS (E) : x.xx xxx') |
---|
97 | self.Write('REMARK 250 TORSION ANGLE RESTRAINTS (E) : x.xx xxx') |
---|
98 | self.Write('REMARK 250') |
---|
99 | self.Write('REMARK 200') |
---|
100 | self.Write('DBREF') |
---|
101 | |
---|
102 | def PDBseqres(seqList): |
---|
103 | chains = list(seqList.keys()) |
---|
104 | chains.sort() |
---|
105 | nSeq = 0 |
---|
106 | for chain in chains: |
---|
107 | nres = len(seqList[chain]) |
---|
108 | nrec = (nres-1)//13+1 |
---|
109 | iB = 0 |
---|
110 | for irec in range(nrec): |
---|
111 | iF = min(iB+13,nres) |
---|
112 | text = 'SEQRES {:3d}{:2s}{:5d} '+(iF-iB)*'{:4s}' |
---|
113 | self.Write(text.format(irec+1,chain,nres,*seqList[chain][iB:iF])) |
---|
114 | nSeq += 1 |
---|
115 | iB += 13 |
---|
116 | return nSeq |
---|
117 | |
---|
118 | # the export process starts here |
---|
119 | self.InitExport(event) |
---|
120 | # load all of the tree into a set of dicts |
---|
121 | self.loadTree() |
---|
122 | # create a dict with refined values and their uncertainties |
---|
123 | self.loadParmDict() |
---|
124 | if self.ExportSelect(): # set export parameters; ask for file name |
---|
125 | return |
---|
126 | filename = self.filename |
---|
127 | for phasenam in self.phasenam: |
---|
128 | phasedict = self.Phases[phasenam] # pointer to current phase info |
---|
129 | General = phasedict['General'] |
---|
130 | if General['Type'] != 'macromolecular': |
---|
131 | print ('phase '+phasenam+' not macromolecular, skipping') |
---|
132 | continue |
---|
133 | i = self.Phases[phasenam]['pId'] |
---|
134 | if len(self.phasenam) > 1: # if more than one filename is included, add a phase # |
---|
135 | self.filename = os.path.splitext(filename)[1] + "_" + str(i) + self.extension |
---|
136 | fp = self.OpenFile() |
---|
137 | Atoms = phasedict['Atoms'] |
---|
138 | cx,ct,cs,cia = General['AtomPtrs'] |
---|
139 | seqList = {} |
---|
140 | AA3letter = ['ALA','ARG','ASN','ASP','CYS','GLN','GLU','GLY','HIS','ILE', |
---|
141 | 'LEU','LYS','MET','PHE','PRO','SER','THR','TRP','TYR','VAL','MSE'] |
---|
142 | seq = 0 |
---|
143 | notProt = True |
---|
144 | for atom in Atoms: |
---|
145 | if atom[ct-3] in AA3letter and int(atom[ct-4]) != seq: |
---|
146 | notProt = False |
---|
147 | if atom[ct-2] not in seqList: |
---|
148 | seqList[atom[ct-2]] = [] |
---|
149 | seqList[atom[ct-2]].append(atom[ct-3]) |
---|
150 | seq = int(atom[ct-4]) |
---|
151 | PDBheader() |
---|
152 | PDBremark250() |
---|
153 | nSeq = PDBseqres(seqList) |
---|
154 | |
---|
155 | # get cell parameters |
---|
156 | Cell = General['Cell'][1:7] |
---|
157 | line = "CRYST1 {:8.3f} {:8.3f} {:8.3f} {:6.2f} {:6.2f} {:6.2f} ".format(*Cell) |
---|
158 | line += General['SGData']['SpGrp'].ljust(13) |
---|
159 | line += '%2d'%(len(General['SGData']['SGOps'])*len(General['SGData']['SGCen'])) |
---|
160 | self.Write(line) |
---|
161 | self.Write('ORIGX1 1.000000 0.000000 0.000000 0.00000') |
---|
162 | self.Write('ORIGX2 0.000000 1.000000 0.000000 0.00000') |
---|
163 | self.Write('ORIGX3 0.000000 0.000000 1.000000 0.00000') |
---|
164 | A,B = G2lat.cell2AB(Cell) |
---|
165 | self.Write('SCALE1 {:9.6f} {:9.6f} {:9.6f} 0.00000'.format(*B[0])) |
---|
166 | self.Write('SCALE2 {:9.6f} {:9.6f} {:9.6f} 0.00000'.format(*B[1])) |
---|
167 | self.Write('SCALE3 {:9.6f} {:9.6f} {:9.6f} 0.00000'.format(*B[2])) |
---|
168 | iatom = 1 |
---|
169 | nHet = 0 |
---|
170 | nTer = 0 |
---|
171 | fmt = '{:6s}{:5d} {:4s}{:3s} {:1s}{:4s} '+3*'{:8.3f}'+2*'{:6.2f}'+'{:s}' |
---|
172 | for atom in Atoms: |
---|
173 | if atom[cia] == 'I': #need to deal with aniso thermals for proteins = "ANISOU" records |
---|
174 | Biso = atom[cia+1]*8.*np.pi**2 |
---|
175 | xyz = np.inner(A,np.array(atom[cx:cx+3])) |
---|
176 | if atom[ct-3] in AA3letter or notProt: |
---|
177 | self.Write(fmt.format('ATOM ',iatom,atom[ct-1],atom[ct-3].strip(), \ |
---|
178 | atom[ct-2].strip(),atom[ct-4].rjust(4),xyz[0],xyz[1],xyz[2],atom[cx+3], \ |
---|
179 | Biso,atom[ct].rjust(12))) |
---|
180 | if atom[ct-1] == 'OXT': |
---|
181 | iatom += 1 |
---|
182 | self.Write('{:6s}{:5d} {:4s}{:3s}'.format('TER ',iatom,atom[ct-1],atom[ct-3].strip())) |
---|
183 | nTer += 1 |
---|
184 | else: |
---|
185 | nHet += 1 |
---|
186 | self.Write(fmt.format('HETATM',iatom,atom[ct-1],atom[ct-3].strip(), \ |
---|
187 | atom[ct-2].strip(),atom[ct-4].rjust(4),xyz[0],xyz[1],xyz[2],atom[cx+3], \ |
---|
188 | Biso,atom[ct].rjust(12))) |
---|
189 | #if atim[cia] == 'a': |
---|
190 | # put in 'ANISOU' record |
---|
191 | #'ANISOU 1 N ALA A 340 4392 4159 4615 249 -189 73 N' |
---|
192 | iatom += 1 |
---|
193 | |
---|
194 | vals = [3,0,nHet,0,0,0,6,len(Atoms),nTer,0,nSeq] |
---|
195 | fmt = 'MASTER'+11*'{:5d}' |
---|
196 | self.Write(fmt.format(*vals)) |
---|
197 | self.Write('END') |
---|
198 | self.CloseFile() |
---|
199 | print('Phase '+phasenam+' written to PDB file '+self.fullpath) |
---|
200 | |
---|
201 | class ExportPhaseCartXYZ(G2IO.ExportBaseclass): |
---|
202 | '''Used to create a Cartesian XYZ file for a phase |
---|
203 | |
---|
204 | :param wx.Frame G2frame: reference to main GSAS-II frame |
---|
205 | ''' |
---|
206 | def __init__(self,G2frame): |
---|
207 | super(self.__class__,self).__init__( # fancy way to say <parentclass>.__init__ |
---|
208 | G2frame=G2frame, |
---|
209 | formatName = 'Cartesian XYZ', |
---|
210 | extension='.XYZ', |
---|
211 | longFormatName = 'Export phase with Cartesian coordinates as .XYZ file' |
---|
212 | ) |
---|
213 | self.exporttype = ['phase'] |
---|
214 | self.multiple = True |
---|
215 | |
---|
216 | def Exporter(self,event=None): |
---|
217 | '''Export as a XYZ file |
---|
218 | ''' |
---|
219 | # the export process starts here |
---|
220 | self.InitExport(event) |
---|
221 | # load all of the tree into a set of dicts |
---|
222 | self.loadTree() |
---|
223 | # create a dict with refined values and their uncertainties |
---|
224 | self.loadParmDict() |
---|
225 | if self.ExportSelect(): # set export parameters; ask for file name |
---|
226 | return |
---|
227 | filename = self.filename |
---|
228 | self.OpenFile() |
---|
229 | for phasenam in self.phasenam: |
---|
230 | phasedict = self.Phases[phasenam] # pointer to current phase info |
---|
231 | General = phasedict['General'] |
---|
232 | i = self.Phases[phasenam]['pId'] |
---|
233 | Atoms = phasedict['Atoms'] |
---|
234 | if not len(Atoms): |
---|
235 | print('**** ERROR - Phase '+phasenam+' has no atoms! ****') |
---|
236 | continue |
---|
237 | if len(self.phasenam) > 1: # if more than one filename is included, add a phase # |
---|
238 | self.filename = os.path.splitext(filename)[1] + "_" + str(i) + self.extension |
---|
239 | cx,ct,cs,cia = General['AtomPtrs'] |
---|
240 | Cell = General['Cell'][1:7] |
---|
241 | A,B = G2lat.cell2AB(Cell) |
---|
242 | fmt = '{:4s}'+3*'{:12.4f}' |
---|
243 | self.Write('{:6d}'.format(len(Atoms))) |
---|
244 | self.Write(' ') |
---|
245 | for atom in Atoms: |
---|
246 | xyz = np.inner(A,np.array(atom[cx:cx+3])) |
---|
247 | self.Write(fmt.format(atom[ct],*xyz)) |
---|
248 | self.CloseFile() |
---|
249 | print('Phase '+phasenam+' written to XYZ file '+self.fullpath) |
---|
250 | |
---|