diffpy.structure 3.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffpy/Structure.py +35 -0
- diffpy/__init__.py +23 -0
- diffpy/structure/__init__.py +93 -0
- diffpy/structure/_legacy_importer.py +88 -0
- diffpy/structure/apps/__init__.py +17 -0
- diffpy/structure/apps/anyeye.py +284 -0
- diffpy/structure/apps/transtru.py +126 -0
- diffpy/structure/atom.py +544 -0
- diffpy/structure/expansion/__init__.py +27 -0
- diffpy/structure/expansion/makeellipsoid.py +129 -0
- diffpy/structure/expansion/shapeutils.py +44 -0
- diffpy/structure/expansion/supercell_mod.py +91 -0
- diffpy/structure/lattice.py +663 -0
- diffpy/structure/mmlibspacegroups.py +8154 -0
- diffpy/structure/parsers/__init__.py +83 -0
- diffpy/structure/parsers/p_auto.py +217 -0
- diffpy/structure/parsers/p_cif.py +876 -0
- diffpy/structure/parsers/p_discus.py +312 -0
- diffpy/structure/parsers/p_pdb.py +405 -0
- diffpy/structure/parsers/p_pdffit.py +290 -0
- diffpy/structure/parsers/p_rawxyz.py +149 -0
- diffpy/structure/parsers/p_xcfg.py +457 -0
- diffpy/structure/parsers/p_xyz.py +161 -0
- diffpy/structure/parsers/parser_index_mod.py +108 -0
- diffpy/structure/parsers/structureparser.py +80 -0
- diffpy/structure/pdffitstructure.py +109 -0
- diffpy/structure/sgtbxspacegroups.py +5198 -0
- diffpy/structure/spacegroupmod.py +329 -0
- diffpy/structure/spacegroups.py +1441 -0
- diffpy/structure/structure.py +866 -0
- diffpy/structure/structureerrors.py +35 -0
- diffpy/structure/symmetryutilities.py +1100 -0
- diffpy/structure/utils.py +126 -0
- diffpy/structure/version.py +26 -0
- diffpy.structure-3.2.0.dist-info/AUTHORS.rst +13 -0
- diffpy.structure-3.2.0.dist-info/LICENSE.rst +141 -0
- diffpy.structure-3.2.0.dist-info/LICENSE_DANSE.rst +50 -0
- diffpy.structure-3.2.0.dist-info/LICENSE_pymmlib.rst +203 -0
- diffpy.structure-3.2.0.dist-info/METADATA +197 -0
- diffpy.structure-3.2.0.dist-info/RECORD +42 -0
- diffpy.structure-3.2.0.dist-info/WHEEL +5 -0
- diffpy.structure-3.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.structure by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2008 trustees of the Michigan State University.
|
|
7
|
+
# All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# File coded by: Pavol Juhas
|
|
10
|
+
#
|
|
11
|
+
# See AUTHORS.txt for a list of people who contributed.
|
|
12
|
+
# See LICENSE_DANSE.txt for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
"""Basic parser for PDB structure format.
|
|
17
|
+
|
|
18
|
+
Note
|
|
19
|
+
----
|
|
20
|
+
References:
|
|
21
|
+
https://www.wwpdb.org/documentation/file-format-content/format23/v2.3.html
|
|
22
|
+
https://www.wwpdb.org/documentation/file-format-content/format30/index.html
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
import numpy
|
|
28
|
+
from numpy import pi
|
|
29
|
+
|
|
30
|
+
from diffpy.structure import Structure
|
|
31
|
+
from diffpy.structure.parsers import StructureParser
|
|
32
|
+
from diffpy.structure.structureerrors import StructureFormatError
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class P_pdb(StructureParser):
|
|
36
|
+
"""Simple parser for PDB format.
|
|
37
|
+
|
|
38
|
+
The parser understands following PDB records: `TITLE, CRYST1, SCALE1,
|
|
39
|
+
SCALE2, SCALE3, ATOM, SIGATM, ANISOU, SIGUIJ, TER, HETATM, END`.
|
|
40
|
+
|
|
41
|
+
Attributes
|
|
42
|
+
----------
|
|
43
|
+
format : str
|
|
44
|
+
Format name, default "pdb".
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Static data members
|
|
48
|
+
orderOfRecords = [
|
|
49
|
+
"HEADER",
|
|
50
|
+
"OBSLTE",
|
|
51
|
+
"TITLE",
|
|
52
|
+
"CAVEAT",
|
|
53
|
+
"COMPND",
|
|
54
|
+
"SOURCE",
|
|
55
|
+
"KEYWDS",
|
|
56
|
+
"EXPDTA",
|
|
57
|
+
"AUTHOR",
|
|
58
|
+
"REVDAT",
|
|
59
|
+
"SPRSDE",
|
|
60
|
+
"JRNL",
|
|
61
|
+
"REMARK",
|
|
62
|
+
"REMARK",
|
|
63
|
+
"REMARK",
|
|
64
|
+
"REMARK",
|
|
65
|
+
"DBREF",
|
|
66
|
+
"SEQADV",
|
|
67
|
+
"SEQRES",
|
|
68
|
+
"MODRES",
|
|
69
|
+
"HET",
|
|
70
|
+
"HETNAM",
|
|
71
|
+
"HETSYN",
|
|
72
|
+
"FORMUL",
|
|
73
|
+
"HELIX",
|
|
74
|
+
"SHEET",
|
|
75
|
+
"TURN",
|
|
76
|
+
"SSBOND",
|
|
77
|
+
"LINK",
|
|
78
|
+
"HYDBND",
|
|
79
|
+
"SLTBRG",
|
|
80
|
+
"CISPEP",
|
|
81
|
+
"SITE",
|
|
82
|
+
"CRYST1",
|
|
83
|
+
"ORIGX1",
|
|
84
|
+
"ORIGX2",
|
|
85
|
+
"ORIGX3",
|
|
86
|
+
"SCALE1",
|
|
87
|
+
"SCALE2",
|
|
88
|
+
"SCALE3",
|
|
89
|
+
"MTRIX1",
|
|
90
|
+
"MTRIX2",
|
|
91
|
+
"MTRIX3",
|
|
92
|
+
"TVECT",
|
|
93
|
+
"MODEL",
|
|
94
|
+
"ATOM",
|
|
95
|
+
"SIGATM",
|
|
96
|
+
"ANISOU",
|
|
97
|
+
"SIGUIJ",
|
|
98
|
+
"TER",
|
|
99
|
+
"HETATM",
|
|
100
|
+
"ENDMDL",
|
|
101
|
+
"CONECT",
|
|
102
|
+
"MASTER",
|
|
103
|
+
"END",
|
|
104
|
+
]
|
|
105
|
+
"""list: Ordered list of PDB record labels."""
|
|
106
|
+
|
|
107
|
+
validRecords = dict.fromkeys(orderOfRecords)
|
|
108
|
+
"""dict: Dictionary of PDB record labels."""
|
|
109
|
+
|
|
110
|
+
def __init__(self):
|
|
111
|
+
StructureParser.__init__(self)
|
|
112
|
+
self.format = "pdb"
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
def parseLines(self, lines):
|
|
116
|
+
"""Parse list of lines in PDB format.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
lines : list of str
|
|
121
|
+
List of lines in PDB format.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
Structure
|
|
126
|
+
Parsed structure instance.
|
|
127
|
+
|
|
128
|
+
Raises
|
|
129
|
+
------
|
|
130
|
+
StructureFormatError
|
|
131
|
+
Invalid PDB record.
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
stru = Structure()
|
|
135
|
+
scale = numpy.identity(3, dtype=float)
|
|
136
|
+
scaleU = numpy.zeros(3, dtype=float)
|
|
137
|
+
p_nl = 0
|
|
138
|
+
for line in lines:
|
|
139
|
+
p_nl += 1
|
|
140
|
+
# skip blank lines
|
|
141
|
+
if not line.strip():
|
|
142
|
+
continue
|
|
143
|
+
# make sure line has 80 characters
|
|
144
|
+
if len(line) < 80:
|
|
145
|
+
line = "%-80s" % line
|
|
146
|
+
words = line.split()
|
|
147
|
+
record = words[0]
|
|
148
|
+
if record == "TITLE":
|
|
149
|
+
continuation = line[8:10]
|
|
150
|
+
if continuation.strip():
|
|
151
|
+
stru.title += line[10:].rstrip()
|
|
152
|
+
else:
|
|
153
|
+
stru.title = line[10:].rstrip()
|
|
154
|
+
elif record == "CRYST1":
|
|
155
|
+
a = float(line[7:15])
|
|
156
|
+
b = float(line[15:24])
|
|
157
|
+
c = float(line[24:33])
|
|
158
|
+
alpha = float(line[33:40])
|
|
159
|
+
beta = float(line[40:47])
|
|
160
|
+
gamma = float(line[47:54])
|
|
161
|
+
stru.lattice.setLatPar(a, b, c, alpha, beta, gamma)
|
|
162
|
+
scale = numpy.transpose(stru.lattice.recbase)
|
|
163
|
+
elif record == "SCALE1":
|
|
164
|
+
sc = numpy.zeros((3, 3), dtype=float)
|
|
165
|
+
sc[0, :] = [float(x) for x in line[10:40].split()]
|
|
166
|
+
scaleU[0] = float(line[45:55])
|
|
167
|
+
elif record == "SCALE2":
|
|
168
|
+
sc[1, :] = [float(x) for x in line[10:40].split()]
|
|
169
|
+
scaleU[1] = float(line[45:55])
|
|
170
|
+
elif record == "SCALE3":
|
|
171
|
+
sc[2, :] = [float(x) for x in line[10:40].split()]
|
|
172
|
+
scaleU[2] = float(line[45:55])
|
|
173
|
+
base = numpy.transpose(numpy.linalg.inv(sc))
|
|
174
|
+
abcABGcryst = numpy.array(stru.lattice.abcABG())
|
|
175
|
+
stru.lattice.setLatBase(base)
|
|
176
|
+
abcABGscale = numpy.array(stru.lattice.abcABG())
|
|
177
|
+
reldiff = numpy.fabs(1.0 - abcABGscale / abcABGcryst)
|
|
178
|
+
if not numpy.all(reldiff < 1.0e-4):
|
|
179
|
+
emsg = "%d: " % p_nl + "SCALE and CRYST1 are not consistent."
|
|
180
|
+
raise StructureFormatError(emsg)
|
|
181
|
+
if numpy.any(scaleU != 0.0):
|
|
182
|
+
emsg = "Origin offset not yet implemented."
|
|
183
|
+
raise NotImplementedError(emsg)
|
|
184
|
+
elif record in ("ATOM", "HETATM"):
|
|
185
|
+
name = line[12:16].strip()
|
|
186
|
+
rc = [float(x) for x in line[30:54].split()]
|
|
187
|
+
try:
|
|
188
|
+
occupancy = float(line[54:60])
|
|
189
|
+
except ValueError:
|
|
190
|
+
occupancy = 1.0
|
|
191
|
+
try:
|
|
192
|
+
B = float(line[60:66])
|
|
193
|
+
uiso = B / (8 * pi**2)
|
|
194
|
+
except ValueError:
|
|
195
|
+
uiso = 0.0
|
|
196
|
+
element = line[76:78].strip()
|
|
197
|
+
if element == "":
|
|
198
|
+
# get element from the first 2 characters of name
|
|
199
|
+
element = line[12:14].strip()
|
|
200
|
+
element = element[0].upper() + element[1:].lower()
|
|
201
|
+
stru.addNewAtom(element, occupancy=occupancy, label=name)
|
|
202
|
+
last_atom = stru.getLastAtom()
|
|
203
|
+
last_atom.xyz_cartn = rc
|
|
204
|
+
last_atom.Uisoequiv = uiso
|
|
205
|
+
elif record == "SIGATM":
|
|
206
|
+
sigrc = [float(x) for x in line[30:54].split()]
|
|
207
|
+
sigxyz = numpy.dot(scale, sigrc)
|
|
208
|
+
try:
|
|
209
|
+
sigo = float(line[54:60])
|
|
210
|
+
except ValueError:
|
|
211
|
+
sigo = 0.0
|
|
212
|
+
try:
|
|
213
|
+
sigB = float(line[60:66])
|
|
214
|
+
sigU = numpy.identity(3) * sigB / (8 * pi**2)
|
|
215
|
+
except ValueError:
|
|
216
|
+
sigU = numpy.zeros((3, 3), dtype=float)
|
|
217
|
+
last_atom.sigxyz = sigxyz
|
|
218
|
+
last_atom.sigo = sigo
|
|
219
|
+
last_atom.sigU = sigU
|
|
220
|
+
elif record == "ANISOU":
|
|
221
|
+
last_atom.anisotropy = True
|
|
222
|
+
Uij = [float(x) * 1.0e-4 for x in line[28:70].split()]
|
|
223
|
+
Ua = last_atom.U
|
|
224
|
+
for i in range(3):
|
|
225
|
+
Ua[i, i] = Uij[i]
|
|
226
|
+
Ua[0, 1] = Ua[1, 0] = Uij[3]
|
|
227
|
+
Ua[0, 2] = Ua[2, 0] = Uij[4]
|
|
228
|
+
Ua[1, 2] = Ua[2, 1] = Uij[5]
|
|
229
|
+
elif record == "SIGUIJ":
|
|
230
|
+
sigUij = [float(x) * 1.0e-4 for x in line[28:70].split()]
|
|
231
|
+
for i in range(3):
|
|
232
|
+
last_atom.sigU[i, i] = sigUij[i]
|
|
233
|
+
last_atom.sigU[0, 1] = last_atom.sigU[1, 0] = sigUij[3]
|
|
234
|
+
last_atom.sigU[0, 2] = last_atom.sigU[2, 0] = sigUij[4]
|
|
235
|
+
last_atom.sigU[1, 2] = last_atom.sigU[2, 1] = sigUij[5]
|
|
236
|
+
elif record in P_pdb.validRecords:
|
|
237
|
+
pass
|
|
238
|
+
else:
|
|
239
|
+
emsg = "%d: invalid record name '%r'" % (p_nl, record)
|
|
240
|
+
raise StructureFormatError(emsg)
|
|
241
|
+
except (ValueError, IndexError):
|
|
242
|
+
emsg = "%d: invalid PDB record" % p_nl
|
|
243
|
+
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
244
|
+
e = StructureFormatError(emsg)
|
|
245
|
+
raise e.with_traceback(exc_traceback)
|
|
246
|
+
return stru
|
|
247
|
+
|
|
248
|
+
def titleLines(self, stru):
|
|
249
|
+
"""Build lines corresponding to `TITLE` record."""
|
|
250
|
+
lines = []
|
|
251
|
+
title = stru.title
|
|
252
|
+
while title != "":
|
|
253
|
+
stop = len(title)
|
|
254
|
+
# maximum length of title record is 60
|
|
255
|
+
if stop > 60:
|
|
256
|
+
stop = title.rfind(" ", 10, 60)
|
|
257
|
+
if stop < 0:
|
|
258
|
+
stop = 60
|
|
259
|
+
if len(lines) == 0:
|
|
260
|
+
continuation = " "
|
|
261
|
+
else:
|
|
262
|
+
continuation = "%2i" % (len(lines) + 1)
|
|
263
|
+
lines.append("%-80s" % ("TITLE " + continuation + title[0:stop]))
|
|
264
|
+
title = title[stop:]
|
|
265
|
+
return lines
|
|
266
|
+
|
|
267
|
+
def cryst1Lines(self, stru):
|
|
268
|
+
"""Build lines corresponding to `CRYST1` record."""
|
|
269
|
+
lines = []
|
|
270
|
+
latpar = (
|
|
271
|
+
stru.lattice.a,
|
|
272
|
+
stru.lattice.b,
|
|
273
|
+
stru.lattice.c,
|
|
274
|
+
stru.lattice.alpha,
|
|
275
|
+
stru.lattice.beta,
|
|
276
|
+
stru.lattice.gamma,
|
|
277
|
+
)
|
|
278
|
+
if latpar != (1.0, 1.0, 1.0, 90.0, 90.0, 90.0):
|
|
279
|
+
line = "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f" % latpar
|
|
280
|
+
lines.append("%-80s" % line)
|
|
281
|
+
return lines
|
|
282
|
+
|
|
283
|
+
def atomLines(self, stru, idx):
|
|
284
|
+
"""Build `ATOM` records and possibly `SIGATM`, `ANISOU` or `SIGUIJ` records
|
|
285
|
+
for `structure` stru `atom` number aidx.
|
|
286
|
+
"""
|
|
287
|
+
lines = []
|
|
288
|
+
a = stru[idx]
|
|
289
|
+
ad = a.__dict__
|
|
290
|
+
rc = a.xyz_cartn
|
|
291
|
+
B = a.Bisoequiv
|
|
292
|
+
atomline = (
|
|
293
|
+
"ATOM " # 1-6
|
|
294
|
+
+ "%(serial)5i " # 7-11, 12
|
|
295
|
+
+ "%(name)-4s" # 13-16
|
|
296
|
+
+ "%(altLoc)c" # 17
|
|
297
|
+
+ "%(resName)-3s " # 18-20, 21
|
|
298
|
+
+ "%(chainID)c" # 22
|
|
299
|
+
+ "%(resSeq)4i" # 23-26
|
|
300
|
+
+ "%(iCode)c " # 27, 28-30
|
|
301
|
+
+ "%(x)8.3f%(y)8.3f%(z)8.3f" # 31-54
|
|
302
|
+
+ "%(occupancy)6.2f" # 55-60
|
|
303
|
+
+ "%(tempFactor)6.2f " # 61-66, 67-72
|
|
304
|
+
+ "%(segID)-4s" # 73-76
|
|
305
|
+
+ "%(element)2s" # 77-78
|
|
306
|
+
+ "%(charge)-2s" # 79-80
|
|
307
|
+
) % {
|
|
308
|
+
"serial": idx + 1,
|
|
309
|
+
"name": a.label or a.element,
|
|
310
|
+
"altLoc": " ",
|
|
311
|
+
"resName": "",
|
|
312
|
+
"chainID": " ",
|
|
313
|
+
"resSeq": 1,
|
|
314
|
+
"iCode": " ",
|
|
315
|
+
"x": rc[0],
|
|
316
|
+
"y": rc[1],
|
|
317
|
+
"z": rc[2],
|
|
318
|
+
"occupancy": a.occupancy,
|
|
319
|
+
"tempFactor": B,
|
|
320
|
+
"segID": "",
|
|
321
|
+
"element": a.element,
|
|
322
|
+
"charge": "",
|
|
323
|
+
}
|
|
324
|
+
lines.append(atomline)
|
|
325
|
+
isotropic = numpy.all(a.U == a.U[0, 0] * numpy.identity(3))
|
|
326
|
+
if not isotropic:
|
|
327
|
+
mid = " %7i%7i%7i%7i%7i%7i " % tuple(
|
|
328
|
+
numpy.around(1e4 * numpy.array([a.U[0, 0], a.U[1, 1], a.U[2, 2], a.U[0, 1], a.U[0, 2], a.U[1, 2]]))
|
|
329
|
+
)
|
|
330
|
+
line = "ANISOU" + atomline[6:27] + mid + atomline[72:80]
|
|
331
|
+
lines.append(line)
|
|
332
|
+
# default values of standard deviations
|
|
333
|
+
d_sigxyz = numpy.zeros(3, dtype=float)
|
|
334
|
+
d_sigo = 0.0
|
|
335
|
+
d_sigU = numpy.zeros((3, 3), dtype=float)
|
|
336
|
+
sigxyz = ad.get("sigxyz", d_sigxyz)
|
|
337
|
+
sigo = [ad.get("sigo", d_sigo)]
|
|
338
|
+
sigU = ad.get("sigU", d_sigU)
|
|
339
|
+
sigB = [8 * pi**2 * numpy.average([sigU[i, i] for i in range(3)])]
|
|
340
|
+
sigmas = numpy.concatenate((sigxyz, sigo, sigB))
|
|
341
|
+
# no need to print sigmas if they all round to zero
|
|
342
|
+
hassigmas = numpy.any(numpy.fabs(sigmas) >= numpy.array(3 * [5e-4] + 2 * [5e-3])) or numpy.any(
|
|
343
|
+
numpy.fabs(sigU) > 5.0e-5
|
|
344
|
+
)
|
|
345
|
+
if hassigmas:
|
|
346
|
+
mid = " %8.3f%8.3f%8.3f%6.2f%6.2f " % tuple(sigmas)
|
|
347
|
+
line = "SIGATM" + atomline[6:27] + mid + atomline[72:80]
|
|
348
|
+
lines.append(line)
|
|
349
|
+
# do we need SIGUIJ record?
|
|
350
|
+
if not numpy.all(sigU == sigU[0, 0] * numpy.identity(3)):
|
|
351
|
+
mid = " %7i%7i%7i%7i%7i%7i " % tuple(
|
|
352
|
+
numpy.around(
|
|
353
|
+
1e4 * numpy.array([sigU[0, 0], sigU[1, 1], sigU[2, 2], sigU[0, 1], sigU[0, 2], sigU[1, 2]])
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
line = "SIGUIJ" + atomline[6:27] + mid + atomline[72:80]
|
|
357
|
+
lines.append(line)
|
|
358
|
+
return lines
|
|
359
|
+
|
|
360
|
+
def toLines(self, stru):
|
|
361
|
+
"""Convert `Structure` stru to a list of lines in PDB format.
|
|
362
|
+
|
|
363
|
+
Parameters
|
|
364
|
+
----------
|
|
365
|
+
stru : Structure
|
|
366
|
+
Structure to be converted.
|
|
367
|
+
|
|
368
|
+
Returns
|
|
369
|
+
-------
|
|
370
|
+
list of str
|
|
371
|
+
List of lines in PDB format.
|
|
372
|
+
"""
|
|
373
|
+
lines = []
|
|
374
|
+
lines.extend(self.titleLines(stru))
|
|
375
|
+
lines.extend(self.cryst1Lines(stru))
|
|
376
|
+
for idx in range(len(stru)):
|
|
377
|
+
lines.extend(self.atomLines(stru, idx))
|
|
378
|
+
line = (
|
|
379
|
+
"TER " # 1-6
|
|
380
|
+
+ "%(serial)5i " # 7-11, 12-17
|
|
381
|
+
+ "%(resName)-3s " # 18-20, 21
|
|
382
|
+
+ "%(chainID)c" # 22
|
|
383
|
+
+ "%(resSeq)4i" # 23-26
|
|
384
|
+
+ "%(iCode)c" # 27
|
|
385
|
+
+ "%(blank)53s" # 28-80
|
|
386
|
+
) % {"serial": len(stru) + 1, "resName": "", "chainID": " ", "resSeq": 1, "iCode": " ", "blank": " "}
|
|
387
|
+
lines.append(line)
|
|
388
|
+
lines.append("%-80s" % "END")
|
|
389
|
+
return lines
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# End of class P_pdb
|
|
393
|
+
|
|
394
|
+
# Routines -------------------------------------------------------------------
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def getParser():
|
|
398
|
+
"""Return new `parser` object for PDB format.
|
|
399
|
+
|
|
400
|
+
Returns
|
|
401
|
+
-------
|
|
402
|
+
P_pdb
|
|
403
|
+
Instance of `P_pdb`.
|
|
404
|
+
"""
|
|
405
|
+
return P_pdb()
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.structure by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2007 trustees of the Michigan State University.
|
|
7
|
+
# All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# File coded by: Pavol Juhas
|
|
10
|
+
#
|
|
11
|
+
# See AUTHORS.txt for a list of people who contributed.
|
|
12
|
+
# See LICENSE_DANSE.txt for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
"""Parser for PDFfit structure format
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
from functools import reduce
|
|
21
|
+
|
|
22
|
+
import numpy
|
|
23
|
+
|
|
24
|
+
from diffpy.structure import Lattice, PDFFitStructure
|
|
25
|
+
from diffpy.structure.parsers import StructureParser
|
|
26
|
+
from diffpy.structure.structureerrors import StructureFormatError
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class P_pdffit(StructureParser):
|
|
30
|
+
"""Parser for PDFfit structure format.
|
|
31
|
+
|
|
32
|
+
Attributes
|
|
33
|
+
----------
|
|
34
|
+
format : str
|
|
35
|
+
Format name, default "pdffit".
|
|
36
|
+
ignored_lines : list
|
|
37
|
+
List of lines ignored during parsing.
|
|
38
|
+
stru : PDFFitStructure
|
|
39
|
+
Structure instance used for cif input or output.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
StructureParser.__init__(self)
|
|
44
|
+
self.format = "pdffit"
|
|
45
|
+
self.ignored_lines = []
|
|
46
|
+
self.stru = None
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
def parseLines(self, lines):
|
|
50
|
+
"""Parse list of lines in PDFfit format.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
lines : list of str
|
|
55
|
+
List of lines in PDB format.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
Structure
|
|
60
|
+
Parsed structure instance.
|
|
61
|
+
|
|
62
|
+
Raises
|
|
63
|
+
------
|
|
64
|
+
StructureFormatError
|
|
65
|
+
File not in PDFfit format.
|
|
66
|
+
"""
|
|
67
|
+
p_nl = 0
|
|
68
|
+
try:
|
|
69
|
+
self.stru = PDFFitStructure()
|
|
70
|
+
stru = self.stru
|
|
71
|
+
cell_line_read = False
|
|
72
|
+
stop = len(lines)
|
|
73
|
+
while stop > 0 and lines[stop - 1].strip() == "":
|
|
74
|
+
stop -= 1
|
|
75
|
+
ilines = iter(lines[:stop])
|
|
76
|
+
# read header of PDFFit file
|
|
77
|
+
for line in ilines:
|
|
78
|
+
p_nl += 1
|
|
79
|
+
words = line.split()
|
|
80
|
+
if len(words) == 0 or words[0][0] == "#":
|
|
81
|
+
continue
|
|
82
|
+
elif words[0] == "title":
|
|
83
|
+
stru.title = line.lstrip()[5:].strip()
|
|
84
|
+
elif words[0] == "scale":
|
|
85
|
+
stru.pdffit["scale"] = float(words[1])
|
|
86
|
+
elif words[0] == "sharp":
|
|
87
|
+
l1 = line.replace(",", " ")
|
|
88
|
+
sharp_pars = [float(w) for w in l1.split()[1:]]
|
|
89
|
+
if len(sharp_pars) < 4:
|
|
90
|
+
stru.pdffit["delta2"] = sharp_pars[0]
|
|
91
|
+
stru.pdffit["sratio"] = sharp_pars[1]
|
|
92
|
+
stru.pdffit["rcut"] = sharp_pars[2]
|
|
93
|
+
else:
|
|
94
|
+
stru.pdffit["delta2"] = sharp_pars[0]
|
|
95
|
+
stru.pdffit["delta1"] = sharp_pars[1]
|
|
96
|
+
stru.pdffit["sratio"] = sharp_pars[2]
|
|
97
|
+
stru.pdffit["rcut"] = sharp_pars[3]
|
|
98
|
+
elif words[0] == "spcgr":
|
|
99
|
+
key = "spcgr"
|
|
100
|
+
start = line.find(key) + len(key)
|
|
101
|
+
value = line[start:].strip()
|
|
102
|
+
stru.pdffit["spcgr"] = value
|
|
103
|
+
elif words[0] == "shape":
|
|
104
|
+
self._parse_shape(line)
|
|
105
|
+
elif words[0] == "cell":
|
|
106
|
+
cell_line_read = True
|
|
107
|
+
l1 = line.replace(",", " ")
|
|
108
|
+
latpars = [float(w) for w in l1.split()[1:7]]
|
|
109
|
+
stru.lattice = Lattice(*latpars)
|
|
110
|
+
elif words[0] == "dcell":
|
|
111
|
+
l1 = line.replace(",", " ")
|
|
112
|
+
stru.pdffit["dcell"] = [float(w) for w in l1.split()[1:7]]
|
|
113
|
+
elif words[0] == "ncell":
|
|
114
|
+
l1 = line.replace(",", " ")
|
|
115
|
+
stru.pdffit["ncell"] = [int(w) for w in l1.split()[1:5]]
|
|
116
|
+
elif words[0] == "format":
|
|
117
|
+
if words[1] != "pdffit":
|
|
118
|
+
emsg = "%d: file is not in PDFfit format" % p_nl
|
|
119
|
+
raise StructureFormatError(emsg)
|
|
120
|
+
elif words[0] == "atoms" and cell_line_read:
|
|
121
|
+
break
|
|
122
|
+
else:
|
|
123
|
+
self.ignored_lines.append(line)
|
|
124
|
+
# Header reading finished, check if required lines were present.
|
|
125
|
+
if not cell_line_read:
|
|
126
|
+
emsg = "%d: file is not in PDFfit format" % p_nl
|
|
127
|
+
raise StructureFormatError(emsg)
|
|
128
|
+
# Load data from atom entries.
|
|
129
|
+
p_natoms = reduce(lambda x, y: x * y, stru.pdffit["ncell"])
|
|
130
|
+
# we are now inside data block
|
|
131
|
+
for line in ilines:
|
|
132
|
+
p_nl += 1
|
|
133
|
+
wl1 = line.split()
|
|
134
|
+
element = wl1[0][0].upper() + wl1[0][1:].lower()
|
|
135
|
+
xyz = [float(w) for w in wl1[1:4]]
|
|
136
|
+
occ = float(wl1[4])
|
|
137
|
+
stru.addNewAtom(element, xyz=xyz, occupancy=occ)
|
|
138
|
+
a = stru.getLastAtom()
|
|
139
|
+
p_nl += 1
|
|
140
|
+
wl2 = next(ilines).split()
|
|
141
|
+
a.sigxyz = [float(w) for w in wl2[0:3]]
|
|
142
|
+
a.sigo = float(wl2[3])
|
|
143
|
+
p_nl += 1
|
|
144
|
+
wl3 = next(ilines).split()
|
|
145
|
+
p_nl += 1
|
|
146
|
+
wl4 = next(ilines).split()
|
|
147
|
+
p_nl += 1
|
|
148
|
+
wl5 = next(ilines).split()
|
|
149
|
+
p_nl += 1
|
|
150
|
+
wl6 = next(ilines).split()
|
|
151
|
+
U = numpy.zeros((3, 3), dtype=float)
|
|
152
|
+
sigU = numpy.zeros((3, 3), dtype=float)
|
|
153
|
+
U[0, 0] = float(wl3[0])
|
|
154
|
+
U[1, 1] = float(wl3[1])
|
|
155
|
+
U[2, 2] = float(wl3[2])
|
|
156
|
+
sigU[0, 0] = float(wl4[0])
|
|
157
|
+
sigU[1, 1] = float(wl4[1])
|
|
158
|
+
sigU[2, 2] = float(wl4[2])
|
|
159
|
+
U[0, 1] = U[1, 0] = float(wl5[0])
|
|
160
|
+
U[0, 2] = U[2, 0] = float(wl5[1])
|
|
161
|
+
U[1, 2] = U[2, 1] = float(wl5[2])
|
|
162
|
+
sigU[0, 1] = sigU[1, 0] = float(wl6[0])
|
|
163
|
+
sigU[0, 2] = sigU[2, 0] = float(wl6[1])
|
|
164
|
+
sigU[1, 2] = sigU[2, 1] = float(wl6[2])
|
|
165
|
+
a.anisotropy = stru.lattice.isanisotropic(U)
|
|
166
|
+
a.U = U
|
|
167
|
+
a.sigU = sigU
|
|
168
|
+
if len(stru) != p_natoms:
|
|
169
|
+
emsg = "expected %d atoms, read %d" % (p_natoms, len(stru))
|
|
170
|
+
raise StructureFormatError(emsg)
|
|
171
|
+
if stru.pdffit["ncell"][:3] != [1, 1, 1]:
|
|
172
|
+
superlatpars = [latpars[i] * stru.pdffit["ncell"][i] for i in range(3)] + latpars[3:]
|
|
173
|
+
superlattice = Lattice(*superlatpars)
|
|
174
|
+
stru.placeInLattice(superlattice)
|
|
175
|
+
stru.pdffit["ncell"] = [1, 1, 1, p_natoms]
|
|
176
|
+
except (ValueError, IndexError):
|
|
177
|
+
emsg = "%d: file is not in PDFfit format" % p_nl
|
|
178
|
+
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
179
|
+
e = StructureFormatError(emsg)
|
|
180
|
+
raise e.with_traceback(exc_traceback)
|
|
181
|
+
return stru
|
|
182
|
+
|
|
183
|
+
def toLines(self, stru):
|
|
184
|
+
"""Convert `Structure` stru to a list of lines in PDFfit format.
|
|
185
|
+
|
|
186
|
+
Parameters
|
|
187
|
+
----------
|
|
188
|
+
stru : Structure
|
|
189
|
+
Structure to be converted.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
list of str
|
|
194
|
+
List of lines in PDFfit format.
|
|
195
|
+
"""
|
|
196
|
+
# build the stru_pdffit dictionary initialized from the defaults
|
|
197
|
+
# in PDFFitStructure
|
|
198
|
+
stru_pdffit = PDFFitStructure().pdffit
|
|
199
|
+
if stru.pdffit:
|
|
200
|
+
stru_pdffit.update(stru.pdffit)
|
|
201
|
+
lines = []
|
|
202
|
+
# default values of standard deviations
|
|
203
|
+
d_sigxyz = numpy.zeros(3, dtype=float)
|
|
204
|
+
d_sigo = 0.0
|
|
205
|
+
d_sigU = numpy.zeros((3, 3), dtype=float)
|
|
206
|
+
# here we can start
|
|
207
|
+
line = "title " + stru.title
|
|
208
|
+
lines.append(line.strip())
|
|
209
|
+
lines.append("format pdffit")
|
|
210
|
+
lines.append("scale %9.6f" % stru_pdffit["scale"])
|
|
211
|
+
lines.append(
|
|
212
|
+
"sharp %9.6f, %9.6f, %9.6f, %9.6f"
|
|
213
|
+
% (stru_pdffit["delta2"], stru_pdffit["delta1"], stru_pdffit["sratio"], stru_pdffit["rcut"])
|
|
214
|
+
)
|
|
215
|
+
lines.append("spcgr " + stru_pdffit["spcgr"])
|
|
216
|
+
if stru_pdffit.get("spdiameter", 0.0) > 0.0:
|
|
217
|
+
line = "shape sphere, %g" % stru_pdffit["spdiameter"]
|
|
218
|
+
lines.append(line)
|
|
219
|
+
if stru_pdffit.get("stepcut", 0.0) > 0.0:
|
|
220
|
+
line = "shape stepcut, %g" % stru_pdffit["stepcut"]
|
|
221
|
+
lines.append(line)
|
|
222
|
+
lat = stru.lattice
|
|
223
|
+
lines.append(
|
|
224
|
+
"cell %9.6f, %9.6f, %9.6f, %9.6f, %9.6f, %9.6f"
|
|
225
|
+
% (lat.a, lat.b, lat.c, lat.alpha, lat.beta, lat.gamma)
|
|
226
|
+
)
|
|
227
|
+
lines.append("dcell %9.6f, %9.6f, %9.6f, %9.6f, %9.6f, %9.6f" % tuple(stru_pdffit["dcell"]))
|
|
228
|
+
lines.append("ncell %9i, %9i, %9i, %9i" % (1, 1, 1, len(stru)))
|
|
229
|
+
lines.append("atoms")
|
|
230
|
+
for a in stru:
|
|
231
|
+
ad = a.__dict__
|
|
232
|
+
lines.append(
|
|
233
|
+
"%-4s %17.8f %17.8f %17.8f %12.4f" % (a.element.upper(), a.xyz[0], a.xyz[1], a.xyz[2], a.occupancy)
|
|
234
|
+
)
|
|
235
|
+
sigmas = numpy.concatenate((ad.get("sigxyz", d_sigxyz), [ad.get("sigo", d_sigo)]))
|
|
236
|
+
lines.append(" %18.8f %17.8f %17.8f %12.4f" % tuple(sigmas))
|
|
237
|
+
sigU = ad.get("sigU", d_sigU)
|
|
238
|
+
Uii = (a.U[0][0], a.U[1][1], a.U[2][2])
|
|
239
|
+
Uij = (a.U[0][1], a.U[0][2], a.U[1][2])
|
|
240
|
+
sigUii = (sigU[0][0], sigU[1][1], sigU[2][2])
|
|
241
|
+
sigUij = (sigU[0][1], sigU[0][2], sigU[1][2])
|
|
242
|
+
lines.append(" %18.8f %17.8f %17.8f" % Uii)
|
|
243
|
+
lines.append(" %18.8f %17.8f %17.8f" % sigUii)
|
|
244
|
+
lines.append(" %18.8f %17.8f %17.8f" % Uij)
|
|
245
|
+
lines.append(" %18.8f %17.8f %17.8f" % sigUij)
|
|
246
|
+
return lines
|
|
247
|
+
|
|
248
|
+
# Protected methods ------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
def _parse_shape(self, line):
|
|
251
|
+
"""Process shape line from PDFfit file and update self.stru.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
line : str
|
|
256
|
+
Line containing data for particle shape correction.
|
|
257
|
+
|
|
258
|
+
Raises
|
|
259
|
+
------
|
|
260
|
+
StructureFormatError
|
|
261
|
+
Invalid type of particle shape correction.
|
|
262
|
+
"""
|
|
263
|
+
line_nocommas = line.replace(",", " ")
|
|
264
|
+
words = line_nocommas.split()
|
|
265
|
+
assert words[0] == "shape"
|
|
266
|
+
shapetype = words[1]
|
|
267
|
+
if shapetype == "sphere":
|
|
268
|
+
self.stru.pdffit["spdiameter"] = float(words[2])
|
|
269
|
+
elif shapetype == "stepcut":
|
|
270
|
+
self.stru.pdffit["stepcut"] = float(words[2])
|
|
271
|
+
else:
|
|
272
|
+
emsg = "Invalid type of particle shape correction %r" % shapetype
|
|
273
|
+
raise StructureFormatError(emsg)
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# End of class P_pdffit
|
|
278
|
+
|
|
279
|
+
# Routines -------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def getParser():
|
|
283
|
+
"""Return new `parser` object for PDFfit format.
|
|
284
|
+
|
|
285
|
+
Returns
|
|
286
|
+
-------
|
|
287
|
+
P_pdffit
|
|
288
|
+
Instance of `P_pdffit`.
|
|
289
|
+
"""
|
|
290
|
+
return P_pdffit()
|