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