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.
Files changed (42) hide show
  1. diffpy/Structure.py +35 -0
  2. diffpy/__init__.py +23 -0
  3. diffpy/structure/__init__.py +93 -0
  4. diffpy/structure/_legacy_importer.py +88 -0
  5. diffpy/structure/apps/__init__.py +17 -0
  6. diffpy/structure/apps/anyeye.py +284 -0
  7. diffpy/structure/apps/transtru.py +126 -0
  8. diffpy/structure/atom.py +544 -0
  9. diffpy/structure/expansion/__init__.py +27 -0
  10. diffpy/structure/expansion/makeellipsoid.py +129 -0
  11. diffpy/structure/expansion/shapeutils.py +44 -0
  12. diffpy/structure/expansion/supercell_mod.py +91 -0
  13. diffpy/structure/lattice.py +663 -0
  14. diffpy/structure/mmlibspacegroups.py +8154 -0
  15. diffpy/structure/parsers/__init__.py +83 -0
  16. diffpy/structure/parsers/p_auto.py +217 -0
  17. diffpy/structure/parsers/p_cif.py +876 -0
  18. diffpy/structure/parsers/p_discus.py +312 -0
  19. diffpy/structure/parsers/p_pdb.py +405 -0
  20. diffpy/structure/parsers/p_pdffit.py +290 -0
  21. diffpy/structure/parsers/p_rawxyz.py +149 -0
  22. diffpy/structure/parsers/p_xcfg.py +457 -0
  23. diffpy/structure/parsers/p_xyz.py +161 -0
  24. diffpy/structure/parsers/parser_index_mod.py +108 -0
  25. diffpy/structure/parsers/structureparser.py +80 -0
  26. diffpy/structure/pdffitstructure.py +109 -0
  27. diffpy/structure/sgtbxspacegroups.py +5198 -0
  28. diffpy/structure/spacegroupmod.py +329 -0
  29. diffpy/structure/spacegroups.py +1441 -0
  30. diffpy/structure/structure.py +866 -0
  31. diffpy/structure/structureerrors.py +35 -0
  32. diffpy/structure/symmetryutilities.py +1100 -0
  33. diffpy/structure/utils.py +126 -0
  34. diffpy/structure/version.py +26 -0
  35. diffpy.structure-3.2.0.dist-info/AUTHORS.rst +13 -0
  36. diffpy.structure-3.2.0.dist-info/LICENSE.rst +141 -0
  37. diffpy.structure-3.2.0.dist-info/LICENSE_DANSE.rst +50 -0
  38. diffpy.structure-3.2.0.dist-info/LICENSE_pymmlib.rst +203 -0
  39. diffpy.structure-3.2.0.dist-info/METADATA +197 -0
  40. diffpy.structure-3.2.0.dist-info/RECORD +42 -0
  41. diffpy.structure-3.2.0.dist-info/WHEEL +5 -0
  42. 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()