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,149 @@
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 raw XYZ file format.
17
+
18
+ Raw XYZ is a 3 or 4 column text file with cartesian coordinates
19
+ of atoms and an optional first column for atom types.
20
+ """
21
+
22
+ import sys
23
+
24
+ from diffpy.structure import Structure
25
+ from diffpy.structure.parsers import StructureParser
26
+ from diffpy.structure.structureerrors import StructureFormatError
27
+ from diffpy.structure.utils import isfloat
28
+
29
+
30
+ class P_rawxyz(StructureParser):
31
+ """Parser --> StructureParser subclass for RAWXYZ format.
32
+
33
+ Attributes
34
+ ----------
35
+ format : str
36
+ Format name, default "rawxyz".
37
+ """
38
+
39
+ def __init__(self):
40
+ StructureParser.__init__(self)
41
+ self.format = "rawxyz"
42
+ return
43
+
44
+ def parseLines(self, lines):
45
+ """Parse list of lines in RAWXYZ format.
46
+
47
+ Parameters
48
+ ----------
49
+ lines : list of str
50
+ List of lines in RAWXYZ format.
51
+
52
+ Returns
53
+ -------
54
+ Structure
55
+ Parsed structure instance.
56
+
57
+ Raises
58
+ ------
59
+ StructureFormatError
60
+ Invalid RAWXYZ format.
61
+ """
62
+ linefields = [line.split() for line in lines]
63
+ # prepare output structure
64
+ stru = Structure()
65
+ # find first valid record
66
+ start = 0
67
+ for field in linefields:
68
+ if len(field) == 0 or field[0] == "#":
69
+ start += 1
70
+ else:
71
+ break
72
+ # find the last valid record
73
+ stop = len(lines)
74
+ while stop > start and len(linefields[stop - 1]) == 0:
75
+ stop -= 1
76
+ # get out for empty structure
77
+ if start >= stop:
78
+ return stru
79
+ # here we have at least one valid record line
80
+ # figure out xyz layout from the first line for plain and raw formats
81
+ floatfields = [isfloat(f) for f in linefields[start]]
82
+ nfields = len(linefields[start])
83
+ if nfields not in (3, 4):
84
+ emsg = "%d: invalid RAWXYZ format, expected 3 or 4 columns" % (start + 1)
85
+ raise StructureFormatError(emsg)
86
+ if floatfields[:3] == [True, True, True]:
87
+ el_idx, x_idx = (None, 0)
88
+ elif floatfields[:4] == [False, True, True, True]:
89
+ el_idx, x_idx = (0, 1)
90
+ else:
91
+ emsg = "%d: invalid RAWXYZ format" % (start + 1)
92
+ raise StructureFormatError(emsg)
93
+ # now try to read all record lines
94
+ try:
95
+ p_nl = start
96
+ for fields in linefields[start:]:
97
+ p_nl += 1
98
+ if fields == []:
99
+ continue
100
+ elif len(fields) != nfields:
101
+ emsg = ("%d: all lines must have " + "the same number of columns") % p_nl
102
+ raise StructureFormatError(emsg)
103
+ element = el_idx is not None and fields[el_idx] or ""
104
+ xyz = [float(f) for f in fields[x_idx : x_idx + 3]]
105
+ if len(xyz) == 2:
106
+ xyz.append(0.0)
107
+ stru.addNewAtom(element, xyz=xyz)
108
+ except ValueError:
109
+ emsg = "%d: invalid number" % p_nl
110
+ exc_type, exc_value, exc_traceback = sys.exc_info()
111
+ e = StructureFormatError(emsg)
112
+ raise e.with_traceback(exc_traceback)
113
+ return stru
114
+
115
+ def toLines(self, stru):
116
+ """Convert Structure stru to a list of lines in RAWXYZ format.
117
+
118
+ Parameters
119
+ ----------
120
+ stru : Structure
121
+ Structure to be converted.
122
+
123
+ Returns
124
+ -------
125
+ list of str
126
+ List of lines in RAWXYZ format.
127
+ """
128
+ lines = []
129
+ for a in stru:
130
+ rc = a.xyz_cartn
131
+ s = "%s %g %g %g" % (a.element, rc[0], rc[1], rc[2])
132
+ lines.append(s.lstrip())
133
+ return lines
134
+
135
+
136
+ # End of class P_rawxyz
137
+
138
+ # Routines -------------------------------------------------------------------
139
+
140
+
141
+ def getParser():
142
+ """Return new `parser` object for RAWXYZ format.
143
+
144
+ Returns
145
+ -------
146
+ P_rawxyz
147
+ Instance of `P_rawxyz`.
148
+ """
149
+ return P_rawxyz()
@@ -0,0 +1,457 @@
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 extended CFG format used by atomeye.
17
+
18
+ Attributes
19
+ ----------
20
+ AtomicMass : dict
21
+ Dictionary of atomic masses for elements.
22
+ """
23
+
24
+ import re
25
+ import sys
26
+
27
+ import numpy
28
+
29
+ from diffpy.structure import Structure
30
+ from diffpy.structure.parsers import StructureParser
31
+ from diffpy.structure.structureerrors import StructureFormatError
32
+ from diffpy.structure.utils import isfloat
33
+
34
+ # Constants ------------------------------------------------------------------
35
+
36
+ # Atomic Mass of elements
37
+ # This can be later when PeriodicTable package becomes available.
38
+
39
+ AtomicMass = {
40
+ "H": 1.007947, # 1 H hydrogen 1.007947
41
+ "He": 4.0026022, # 2 He helium 4.0026022
42
+ "Li": 6.9412, # 3 Li lithium 6.9412
43
+ "Be": 9.0121823, # 4 Be beryllium 9.0121823
44
+ "B": 10.8117, # 5 B boron 10.8117
45
+ "C": 12.01078, # 6 C carbon 12.01078
46
+ "N": 14.00672, # 7 N nitrogen 14.00672
47
+ "O": 15.99943, # 8 O oxygen 15.99943
48
+ "F": 18.99840325, # 9 F fluorine 18.99840325
49
+ "Ne": 20.17976, # 10 Ne neon 20.17976
50
+ "Na": 22.9897702, # 11 Na sodium 22.9897702
51
+ "Mg": 24.30506, # 12 Mg magnesium 24.30506
52
+ "Al": 26.9815382, # 13 Al aluminium 26.9815382
53
+ "Si": 28.08553, # 14 Si silicon 28.08553
54
+ "P": 30.9737612, # 15 P phosphorus 30.9737612
55
+ "S": 32.0655, # 16 S sulfur 32.0655
56
+ "Cl": 35.4532, # 17 Cl chlorine 35.4532
57
+ "Ar": 39.9481, # 18 Ar argon 39.9481
58
+ "K": 39.09831, # 19 K potassium 39.09831
59
+ "Ca": 40.0784, # 20 Ca calcium 40.0784
60
+ "Sc": 44.9559108, # 21 Sc scandium 44.9559108
61
+ "Ti": 47.8671, # 22 Ti titanium 47.8671
62
+ "V": 50.94151, # 23 V vanadium 50.94151
63
+ "Cr": 51.99616, # 24 Cr chromium 51.99616
64
+ "Mn": 54.9380499, # 25 Mn manganese 54.9380499
65
+ "Fe": 55.8452, # 26 Fe iron 55.8452
66
+ "Co": 58.9332009, # 27 Co cobalt 58.9332009
67
+ "Ni": 58.69342, # 28 Ni nickel 58.69342
68
+ "Cu": 63.5463, # 29 Cu copper 63.5463
69
+ "Zn": 65.4094, # 30 Zn zinc 65.4094
70
+ "Ga": 69.7231, # 31 Ga gallium 69.7231
71
+ "Ge": 72.641, # 32 Ge germanium 72.641
72
+ "As": 74.921602, # 33 As arsenic 74.921602
73
+ "Se": 78.963, # 34 Se selenium 78.963
74
+ "Br": 79.9041, # 35 Br bromine 79.9041
75
+ "Kr": 83.7982, # 36 Kr krypton 83.7982
76
+ "Rb": 85.46783, # 37 Rb rubidium 85.46783
77
+ "Sr": 87.621, # 38 Sr strontium 87.621
78
+ "Y": 88.905852, # 39 Y yttrium 88.905852
79
+ "Zr": 91.2242, # 40 Zr zirconium 91.2242
80
+ "Nb": 92.906382, # 41 Nb niobium 92.906382
81
+ "Mo": 95.942, # 42 Mo molybdenum 95.942
82
+ "Tc": 98.0, # 43 Tc technetium 98
83
+ "Ru": 101.072, # 44 Ru ruthenium 101.072
84
+ "Rh": 102.905502, # 45 Rh rhodium 102.905502
85
+ "Pd": 106.421, # 46 Pd palladium 106.421
86
+ "Ag": 107.86822, # 47 Ag silver 107.86822
87
+ "Cd": 112.4118, # 48 Cd cadmium 112.4118
88
+ "In": 114.8183, # 49 In indium 114.8183
89
+ "Sn": 118.7107, # 50 Sn tin 118.7107
90
+ "Sb": 121.7601, # 51 Sb antimony 121.7601
91
+ "Te": 127.603, # 52 Te tellurium 127.603
92
+ "I": 126.904473, # 53 I iodine 126.904473
93
+ "Xe": 131.2936, # 54 Xe xenon 131.2936
94
+ "Cs": 132.905452, # 55 Cs caesium 132.905452
95
+ "Ba": 137.3277, # 56 Ba barium 137.3277
96
+ "La": 138.90552, # 57 La lanthanum 138.90552
97
+ "Ce": 140.1161, # 58 Ce cerium 140.1161
98
+ "Pr": 140.907652, # 59 Pr praseodymium 140.907652
99
+ "Nd": 144.243, # 60 Nd neodymium 144.243
100
+ "Pm": 145.0, # 61 Pm promethium 145
101
+ "Sm": 150.363, # 62 Sm samarium 150.363
102
+ "Eu": 151.9641, # 63 Eu europium 151.9641
103
+ "Gd": 157.253, # 64 Gd gadolinium 157.253
104
+ "Tb": 158.925342, # 65 Tb terbium 158.925342
105
+ "Dy": 162.5001, # 66 Dy dysprosium 162.5001
106
+ "Ho": 164.930322, # 67 Ho holmium 164.930322
107
+ "Er": 167.2593, # 68 Er erbium 167.2593
108
+ "Tm": 168.934212, # 69 Tm thulium 168.934212
109
+ "Yb": 173.043, # 70 Yb ytterbium 173.043
110
+ "Lu": 174.9671, # 71 Lu lutetium 174.9671
111
+ "Hf": 178.492, # 72 Hf hafnium 178.492
112
+ "Ta": 180.94791, # 73 Ta tantalum 180.94791
113
+ "W": 183.841, # 74 W tungsten 183.841
114
+ "Re": 186.2071, # 75 Re rhenium 186.2071
115
+ "Os": 190.233, # 76 Os osmium 190.233
116
+ "Ir": 192.2173, # 77 Ir iridium 192.2173
117
+ "Pt": 195.0782, # 78 Pt platinum 195.0782
118
+ "Au": 196.966552, # 79 Au gold 196.966552
119
+ "Hg": 200.592, # 80 Hg mercury 200.592
120
+ "Tl": 204.38332, # 81 Tl thallium 204.38332
121
+ "Pb": 207.21, # 82 Pb lead 207.21
122
+ "Bi": 208.980382, # 83 Bi bismuth 208.980382
123
+ "Po": 209.0, # 84 Po polonium 209
124
+ "At": 210.0, # 85 At astatine 210
125
+ "Rn": 222.0, # 86 Rn radon 222
126
+ "Fr": 223.0, # 87 Fr francium 223
127
+ "Ra": 226.0, # 88 Ra radium 226
128
+ "Ac": 227.0, # 89 Ac actinium 227
129
+ "Th": 232.03811, # 90 Th thorium 232.03811
130
+ "Pa": 231.035882, # 91 Pa protactinium 231.035882
131
+ "U": 238.028913, # 92 U uranium 238.028913
132
+ "Np": 237.0, # 93 Np neptunium 237
133
+ "Pu": 244.0, # 94 Pu plutonium 244
134
+ "Am": 243.0, # 95 Am americium 243
135
+ "Cm": 247.0, # 96 Cm curium 247
136
+ "Bk": 247.0, # 97 Bk berkelium 247
137
+ "Cf": 251.0, # 98 Cf californium 251
138
+ "Es": 252.0, # 99 Es einsteinium 252
139
+ "Fm": 257.0, # 100 Fm fermium 257
140
+ "Md": 258.0, # 101 Md mendelevium 258
141
+ "No": 259.0, # 102 No nobelium 259
142
+ "Lr": 262.0, # 103 Lr lawrencium 262
143
+ "Rf": 261.0, # 104 Rf rutherfordium 261
144
+ "Db": 262.0, # 105 Db dubnium 262
145
+ "Sg": 266.0, # 106 Sg seaborgium 266
146
+ "Bh": 264.0, # 107 Bh bohrium 264
147
+ "Hs": 277.0, # 108 Hs hassium 277
148
+ "Mt": 268.0, # 109 Mt meitnerium 268
149
+ "Ds": 281.0, # 110 Ds darmstadtium 281
150
+ "Rg": 272.0, # 111 Rg roentgenium 272
151
+ }
152
+
153
+ # ----------------------------------------------------------------------------
154
+
155
+
156
+ class P_xcfg(StructureParser):
157
+ """Parser for AtomEye extended CFG format.
158
+
159
+ Attributes
160
+ ----------
161
+ format : str
162
+ Format name, default "xcfg".
163
+ """
164
+
165
+ cluster_boundary = 2
166
+ """int: Width of boundary around corners of non-periodic
167
+ cluster to avoid PBC effects in atomeye.
168
+ """
169
+
170
+ def __init__(self):
171
+ StructureParser.__init__(self)
172
+ self.format = "xcfg"
173
+ return
174
+
175
+ def parseLines(self, lines):
176
+ """Parse list of lines in XCFG format.
177
+
178
+ Parameters
179
+ ----------
180
+ lines : list of str
181
+ List of lines in XCFG format.
182
+
183
+ Returns
184
+ -------
185
+ Structure
186
+ Parsed structure instance.
187
+
188
+ Raises
189
+ ------
190
+ StructureFormatError
191
+ Invalid XCFG format.
192
+ """
193
+ xcfg_Number_of_particles = None
194
+ xcfg_A = None
195
+ xcfg_H0 = numpy.zeros((3, 3), dtype=float)
196
+ xcfg_H0_set = numpy.zeros((3, 3), dtype=bool)
197
+ xcfg_NO_VELOCITY = False
198
+ xcfg_entry_count = None
199
+ p_nl = 0
200
+ p_auxiliary_re = re.compile(r"^auxiliary\[(\d+)\] =")
201
+ p_auxiliary = {}
202
+ stru = Structure()
203
+ # ignore trailing blank lines
204
+ stop = len(lines)
205
+ for line in reversed(lines):
206
+ if line.strip():
207
+ break
208
+ stop -= 1
209
+ # iterator over the valid data lines
210
+ ilines = iter(lines[:stop])
211
+ try:
212
+ # read XCFG header
213
+ for line in ilines:
214
+ p_nl += 1
215
+ stripped_line = line.strip()
216
+ # blank lines and lines starting with # are ignored
217
+ if stripped_line == "" or line[0] == "#":
218
+ continue
219
+ elif xcfg_Number_of_particles is None:
220
+ if line.find("Number of particles =") != 0:
221
+ emsg = ("%d: first line must " + "contain 'Number of particles ='") % p_nl
222
+ raise StructureFormatError(emsg)
223
+ xcfg_Number_of_particles = int(line[21:].split(None, 1)[0])
224
+ p_natoms = xcfg_Number_of_particles
225
+ elif line.find("A =") == 0:
226
+ xcfg_A = float(line[3:].split(None, 1)[0])
227
+ elif line.find("H0(") == 0:
228
+ i, j = (int(line[3]) - 1, int(line[5]) - 1)
229
+ xcfg_H0[i, j] = float(line[10:].split(None, 1)[0])
230
+ xcfg_H0_set[i, j] = True
231
+ elif line.find(".NO_VELOCITY.") == 0:
232
+ xcfg_NO_VELOCITY = True
233
+ elif line.find("entry_count =") == 0:
234
+ xcfg_entry_count = int(line[13:].split(None, 1)[0])
235
+ elif p_auxiliary_re.match(line):
236
+ m = p_auxiliary_re.match(line)
237
+ idx = int(m.group(1))
238
+ p_auxiliary[idx] = line[m.end() :].split(None, 1)[0]
239
+ else:
240
+ break
241
+ # check header for consistency
242
+ if not numpy.all(xcfg_H0_set):
243
+ emsg = "H0 tensor is not properly defined"
244
+ raise StructureFormatError(emsg)
245
+ p_auxnum = len(p_auxiliary) and max(p_auxiliary.keys()) + 1
246
+ for i in range(p_auxnum):
247
+ if i not in p_auxiliary:
248
+ p_auxiliary[i] = "aux%d" % i
249
+ sorted_aux_keys = sorted(p_auxiliary.keys())
250
+ if p_auxnum != 0:
251
+ stru.xcfg = {"auxiliaries": [p_auxiliary[k] for k in sorted_aux_keys]}
252
+ ecnt = len(p_auxiliary) + (3 if xcfg_NO_VELOCITY else 6)
253
+ if ecnt != xcfg_entry_count:
254
+ emsg = ("%d: auxiliary fields are " "not consistent with entry_count") % p_nl
255
+ raise StructureFormatError(emsg)
256
+ # define proper lattice
257
+ stru.lattice.setLatBase(xcfg_H0)
258
+ # here we are inside the data block
259
+ p_element = None
260
+ for line in ilines:
261
+ p_nl += 1
262
+ words = line.split()
263
+ # ignore atom mass
264
+ if len(words) == 1 and isfloat(words[0]):
265
+ continue
266
+ # parse element allowing empty symbol
267
+ elif len(words) <= 1:
268
+ w = line.strip()
269
+ p_element = w[:1].upper() + w[1:].lower()
270
+ elif len(words) == xcfg_entry_count and p_element is not None:
271
+ fields = [float(w) for w in words]
272
+ xyz = [xcfg_A * xi for xi in fields[:3]]
273
+ stru.addNewAtom(p_element, xyz=xyz)
274
+ a = stru[-1]
275
+ _assign_auxiliaries(a, fields, auxiliaries=p_auxiliary, no_velocity=xcfg_NO_VELOCITY)
276
+ else:
277
+ emsg = "%d: invalid record" % p_nl
278
+ raise StructureFormatError(emsg)
279
+ if len(stru) != p_natoms:
280
+ emsg = "expected %d atoms, read %d" % (p_natoms, len(stru))
281
+ raise StructureFormatError(emsg)
282
+ except (ValueError, IndexError):
283
+ emsg = "%d: file is not in XCFG format" % p_nl
284
+ exc_type, exc_value, exc_traceback = sys.exc_info()
285
+ e = StructureFormatError(emsg)
286
+ raise e.with_traceback(exc_traceback)
287
+ return stru
288
+
289
+ def toLines(self, stru):
290
+ """Convert Structure stru to a list of lines in XCFG atomeye format.
291
+
292
+ Parameters
293
+ ----------
294
+ stru : Structure
295
+ Structure to be converted.
296
+
297
+ Returns
298
+ -------
299
+ list of str
300
+ List of lines in XCFG format.
301
+
302
+ Raises
303
+ ------
304
+ StructureFormatError
305
+ Cannot convert empty structure to XCFG format.
306
+ """
307
+ if len(stru) == 0:
308
+ emsg = "cannot convert empty structure to XCFG format"
309
+ raise StructureFormatError(emsg)
310
+ lines = []
311
+ lines.append("Number of particles = %i" % len(stru))
312
+ # figure out length unit A
313
+ allxyz = numpy.array([a.xyz for a in stru])
314
+ lo_xyz = allxyz.min(axis=0)
315
+ hi_xyz = allxyz.max(axis=0)
316
+ max_range_xyz = (hi_xyz - lo_xyz).max()
317
+ if numpy.allclose(stru.lattice.abcABG(), (1, 1, 1, 90, 90, 90)):
318
+ max_range_xyz += self.cluster_boundary
319
+ # range of CFG coordinates must be less than 1
320
+ p_A = numpy.ceil(max_range_xyz + 1.0e-13)
321
+ # atomeye draws rubbish when boxsize is less than 3.5
322
+ hi_ucvect = max([numpy.sqrt(numpy.dot(v, v)) for v in stru.lattice.base])
323
+ if hi_ucvect * p_A < 3.5:
324
+ p_A = numpy.ceil(3.5 / hi_ucvect)
325
+ lines.append("A = %.8g Angstrom" % p_A)
326
+ # how much do we need to shift the coordinates?
327
+ p_dxyz = numpy.zeros(3, dtype=float)
328
+ for i in range(3):
329
+ if lo_xyz[i] / p_A < 0.0 or hi_xyz[i] / p_A >= 1.0 or (lo_xyz[i] == hi_xyz[i] and lo_xyz[i] == 0.0):
330
+ p_dxyz[i] = 0.5 - (hi_xyz[i] + lo_xyz[i]) / 2.0 / p_A
331
+ # H0 tensor
332
+ for i in range(3):
333
+ for j in range(3):
334
+ lines.append("H0(%i,%i) = %.8g A" % (i + 1, j + 1, stru.lattice.base[i, j]))
335
+ # get out for empty structure
336
+ if len(stru) == 0:
337
+ return lines
338
+ a_first = stru[0]
339
+ p_NO_VELOCITY = "v" not in a_first.__dict__
340
+ if p_NO_VELOCITY:
341
+ lines.append(".NO_VELOCITY.")
342
+ # build a p_auxiliaries list of (aux_name,atom_expression) tuples
343
+ # if stru came from xcfg file, it would store original auxiliaries in
344
+ # xcfg dictionary
345
+ try:
346
+ p_auxiliaries = [(aux, "a." + aux) for aux in stru.xcfg["auxiliaries"]]
347
+ except AttributeError:
348
+ p_auxiliaries = []
349
+ # add occupancy if any atom has nonunit occupancy
350
+ for a in stru:
351
+ if a.occupancy != 1.0:
352
+ p_auxiliaries.append(("occupancy", "a.occupancy"))
353
+ break
354
+ # add temperature factor with as many terms as needed
355
+ # check whether all temperature factors are zero or isotropic
356
+ p_allUzero = True
357
+ p_allUiso = True
358
+ for a in stru:
359
+ if p_allUzero and numpy.any(a.U != 0.0):
360
+ p_allUzero = False
361
+ if not numpy.all(a.U == a.U[0, 0] * numpy.identity(3)):
362
+ p_allUiso = False
363
+ # here p_allUzero must be false
364
+ break
365
+ if p_allUzero:
366
+ pass
367
+ elif p_allUiso:
368
+ p_auxiliaries.append(("Uiso", "uflat[0]"))
369
+ else:
370
+ p_auxiliaries.extend([("U11", "uflat[0]"), ("U22", "uflat[4]"), ("U33", "uflat[8]")])
371
+ # check if there are off-diagonal elements
372
+ allU = numpy.array([a.U for a in stru])
373
+ if numpy.any(allU[:, 0, 1] != 0.0):
374
+ p_auxiliaries.append(("U12", "uflat[1]"))
375
+ if numpy.any(allU[:, 0, 2] != 0.0):
376
+ p_auxiliaries.append(("U13", "uflat[2]"))
377
+ if numpy.any(allU[:, 1, 2] != 0.0):
378
+ p_auxiliaries.append(("U23", "uflat[5]"))
379
+ # count entries
380
+ p_entry_count = (3 if p_NO_VELOCITY else 6) + len(p_auxiliaries)
381
+ lines.append("entry_count = %d" % p_entry_count)
382
+ # add auxiliaries
383
+ for i in range(len(p_auxiliaries)):
384
+ lines.append("auxiliary[%d] = %s [au]" % (i, p_auxiliaries[i][0]))
385
+ # now define entry format efmt for representing atom properties
386
+ fmwords = ["{pos[0]:.8g}", "{pos[1]:.8g}", "{pos[2]:.8g}"]
387
+ if not p_NO_VELOCITY:
388
+ fmwords += ["{v[0]:.8g}", "{v[1]:.8g}", "{v[2]:.8g}"]
389
+ fmwords += (("{" + e + ":.8g}") for p, e in p_auxiliaries)
390
+ efmt = " ".join(fmwords)
391
+ # we are ready to output atoms:
392
+ lines.append("")
393
+ p_element = None
394
+ for a in stru:
395
+ if a.element != p_element:
396
+ p_element = a.element
397
+ lines.append("%.4f" % AtomicMass.get(p_element, 0.0))
398
+ lines.append(p_element)
399
+ pos = a.xyz / p_A + p_dxyz
400
+ v = None if p_NO_VELOCITY else a.v
401
+ uflat = numpy.ravel(a.U)
402
+ entry = efmt.format(pos=pos, v=v, uflat=uflat, a=a)
403
+ lines.append(entry)
404
+ return lines
405
+
406
+
407
+ # End of class P_xcfg
408
+
409
+ # Routines -------------------------------------------------------------------
410
+
411
+
412
+ def getParser():
413
+ """Return new `parser` object for XCFG format.
414
+
415
+ Returns
416
+ -------
417
+ P_xcfg
418
+ Instance of `P_xcfg`.
419
+ """
420
+ return P_xcfg()
421
+
422
+
423
+ # Local Helpers --------------------------------------------------------------
424
+
425
+
426
+ def _assign_auxiliaries(a, fields, auxiliaries, no_velocity):
427
+ """Assing auxiliary properties for `Atom` object when reading CFG format.
428
+
429
+ Parameters
430
+ ----------
431
+ a : Atom
432
+ The `Atom` instance for which the auxiliary properties need to be set.
433
+ fields : list
434
+ Floating point values for the current row of the processed CFG file.
435
+ auxiliaries : dict
436
+ Dictionary of zero-based indices and names of auxiliary properties
437
+ defined in the CFG format.
438
+ no_velocity : bool
439
+ When `False` set atom velocity `a.v` to `fields[3:6]`.
440
+ Use `fields[3:6]` for auxiliary values otherwise.
441
+ """
442
+ if not no_velocity:
443
+ a.v = numpy.asarray(fields[3:6], dtype=float)
444
+ auxfirst = 3 if no_velocity else 6
445
+ for i, prop in auxiliaries.items():
446
+ value = fields[auxfirst + i]
447
+ if prop == "Uiso":
448
+ a.Uisoequiv = value
449
+ elif prop == "Biso":
450
+ a.Bisoequiv = value
451
+ elif prop[0] in "BU" and all(d in "123" for d in prop[1:]):
452
+ nm = prop if prop[1] <= prop[2] else prop[0] + prop[2] + prop[1]
453
+ a.anisotropy = True
454
+ setattr(a, nm, value)
455
+ else:
456
+ setattr(a, prop, value)
457
+ return