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,876 @@
|
|
|
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 basic CIF file format.
|
|
17
|
+
|
|
18
|
+
Attributes
|
|
19
|
+
----------
|
|
20
|
+
rx_float : re.Pattern
|
|
21
|
+
Constant regular expression for `leading_float()`.
|
|
22
|
+
symvec : dict
|
|
23
|
+
Helper dictionary for `getSymOp()`.
|
|
24
|
+
|
|
25
|
+
Note
|
|
26
|
+
----
|
|
27
|
+
References: https://www.iucr.org/resources/cif
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import io
|
|
31
|
+
import re
|
|
32
|
+
import sys
|
|
33
|
+
from contextlib import contextmanager
|
|
34
|
+
|
|
35
|
+
import numpy
|
|
36
|
+
|
|
37
|
+
from diffpy.structure import Atom, Lattice, Structure
|
|
38
|
+
from diffpy.structure.parsers import StructureParser
|
|
39
|
+
from diffpy.structure.structureerrors import StructureFormatError
|
|
40
|
+
|
|
41
|
+
# ----------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class P_cif(StructureParser):
|
|
45
|
+
"""Simple parser for CIF structure format.
|
|
46
|
+
|
|
47
|
+
Reads Structure from the first block containing _atom_site_label key.
|
|
48
|
+
Following blocks, if any, are ignored.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
eps : float, Optional
|
|
53
|
+
Fractional coordinates cutoff for duplicate positions.
|
|
54
|
+
When ``None`` use the default for `ExpandAsymmetricUnit`: ``1.0e-5``.
|
|
55
|
+
|
|
56
|
+
Attributes
|
|
57
|
+
----------
|
|
58
|
+
format : str
|
|
59
|
+
Structure format name.
|
|
60
|
+
ciffile : CifFile
|
|
61
|
+
Instance of `CifFile` from `PyCifRW`.
|
|
62
|
+
stru : Structure
|
|
63
|
+
`Structure` instance used for CIF input or output.
|
|
64
|
+
spacegroup : SpaceGroup
|
|
65
|
+
Instance of `SpaceGroup` used for symmetry expansion.
|
|
66
|
+
eps : float
|
|
67
|
+
Resolution in fractional coordinates for non-equal positions.
|
|
68
|
+
Used for expansion of asymmetric unit.
|
|
69
|
+
eau : ExpandAsymmetricUnit
|
|
70
|
+
Instance of `ExpandAsymmetricUnit` from `SymmetryUtilities`.
|
|
71
|
+
asymmetric_unit : list
|
|
72
|
+
List of `Atom` instances for the original asymmetric unit in the CIF file.
|
|
73
|
+
labelindex : dict
|
|
74
|
+
Dictionary mapping unique atom label to index of `Atom` in `self.asymmetric_unit`.
|
|
75
|
+
anisotropy : dict
|
|
76
|
+
Dictionary mapping unique atom label to displacement anisotropy resolved at that site.
|
|
77
|
+
cif_sgname : str or None
|
|
78
|
+
Space group name obtained by looking up the value of
|
|
79
|
+
`_space_group_name_Hall`,
|
|
80
|
+
`_symmetry_space_group_name_Hall`,
|
|
81
|
+
`_space_group_name_H-M_alt`,
|
|
82
|
+
`_symmetry_space_group_name_H-M`
|
|
83
|
+
items. ``None`` when neither is defined.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
# static data and methods ------------------------------------------------
|
|
87
|
+
|
|
88
|
+
# dictionary set of class methods for translating CIF values
|
|
89
|
+
# to Atom attributes
|
|
90
|
+
|
|
91
|
+
# static data and methods ------------------------------------------------
|
|
92
|
+
|
|
93
|
+
# dictionary set of class methods for translating CIF values
|
|
94
|
+
# to Atom attributes
|
|
95
|
+
|
|
96
|
+
_atom_setters = dict.fromkeys(
|
|
97
|
+
(
|
|
98
|
+
"_tr_ignore",
|
|
99
|
+
"_tr_atom_site_label",
|
|
100
|
+
"_tr_atom_site_type_symbol",
|
|
101
|
+
"_tr_atom_site_fract_x",
|
|
102
|
+
"_tr_atom_site_fract_y",
|
|
103
|
+
"_tr_atom_site_fract_z",
|
|
104
|
+
"_tr_atom_site_cartn_x",
|
|
105
|
+
"_tr_atom_site_cartn_y",
|
|
106
|
+
"_tr_atom_site_cartn_z",
|
|
107
|
+
"_tr_atom_site_U_iso_or_equiv",
|
|
108
|
+
"_tr_atom_site_B_iso_or_equiv",
|
|
109
|
+
"_tr_atom_site_adp_type",
|
|
110
|
+
"_tr_atom_site_thermal_displace_type",
|
|
111
|
+
"_tr_atom_site_occupancy",
|
|
112
|
+
"_tr_atom_site_aniso_U_11",
|
|
113
|
+
"_tr_atom_site_aniso_U_22",
|
|
114
|
+
"_tr_atom_site_aniso_U_33",
|
|
115
|
+
"_tr_atom_site_aniso_U_12",
|
|
116
|
+
"_tr_atom_site_aniso_U_13",
|
|
117
|
+
"_tr_atom_site_aniso_U_23",
|
|
118
|
+
"_tr_atom_site_aniso_B_11",
|
|
119
|
+
"_tr_atom_site_aniso_B_22",
|
|
120
|
+
"_tr_atom_site_aniso_B_33",
|
|
121
|
+
"_tr_atom_site_aniso_B_12",
|
|
122
|
+
"_tr_atom_site_aniso_B_13",
|
|
123
|
+
"_tr_atom_site_aniso_B_23",
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
# make _atom_setters case insensitive
|
|
127
|
+
for k in list(_atom_setters.keys()):
|
|
128
|
+
_atom_setters[k] = _atom_setters[k.lower()] = k
|
|
129
|
+
del k
|
|
130
|
+
|
|
131
|
+
BtoU = 1.0 / (8 * numpy.pi**2)
|
|
132
|
+
"""float: Conversion factor from B values to U values."""
|
|
133
|
+
|
|
134
|
+
def _tr_ignore(a, value):
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
_tr_ignore = staticmethod(_tr_ignore)
|
|
138
|
+
|
|
139
|
+
def _tr_atom_site_label(a, value):
|
|
140
|
+
a.label = str(value)
|
|
141
|
+
# set element when not specified by _atom_site_type_symbol
|
|
142
|
+
if not a.element:
|
|
143
|
+
P_cif._tr_atom_site_type_symbol(a, value)
|
|
144
|
+
|
|
145
|
+
_tr_atom_site_label = staticmethod(_tr_atom_site_label)
|
|
146
|
+
|
|
147
|
+
# 3 regexp groups for nucleon number, atom symbol, and oxidation state
|
|
148
|
+
_psymb = re.compile(r"(\d+-)?([a-zA-Z]+)(\d[+-])?")
|
|
149
|
+
|
|
150
|
+
def _tr_atom_site_type_symbol(a, value):
|
|
151
|
+
rx = P_cif._psymb.match(value)
|
|
152
|
+
smbl = rx and rx.group(0) or value
|
|
153
|
+
smbl = str(smbl)
|
|
154
|
+
a.element = smbl[:1].upper() + smbl[1:].lower()
|
|
155
|
+
|
|
156
|
+
_tr_atom_site_type_symbol = staticmethod(_tr_atom_site_type_symbol)
|
|
157
|
+
|
|
158
|
+
def _tr_atom_site_fract_x(a, value):
|
|
159
|
+
a.xyz[0] = leading_float(value)
|
|
160
|
+
|
|
161
|
+
_tr_atom_site_fract_x = staticmethod(_tr_atom_site_fract_x)
|
|
162
|
+
|
|
163
|
+
def _tr_atom_site_fract_y(a, value):
|
|
164
|
+
a.xyz[1] = leading_float(value)
|
|
165
|
+
|
|
166
|
+
_tr_atom_site_fract_y = staticmethod(_tr_atom_site_fract_y)
|
|
167
|
+
|
|
168
|
+
def _tr_atom_site_fract_z(a, value):
|
|
169
|
+
a.xyz[2] = leading_float(value)
|
|
170
|
+
|
|
171
|
+
_tr_atom_site_fract_z = staticmethod(_tr_atom_site_fract_z)
|
|
172
|
+
|
|
173
|
+
def _tr_atom_site_cartn_x(a, value):
|
|
174
|
+
a.xyz_cartn[0] = leading_float(value)
|
|
175
|
+
|
|
176
|
+
_tr_atom_site_cartn_x = staticmethod(_tr_atom_site_cartn_x)
|
|
177
|
+
|
|
178
|
+
def _tr_atom_site_cartn_y(a, value):
|
|
179
|
+
a.xyz_cartn[1] = leading_float(value)
|
|
180
|
+
|
|
181
|
+
_tr_atom_site_cartn_y = staticmethod(_tr_atom_site_cartn_y)
|
|
182
|
+
|
|
183
|
+
def _tr_atom_site_cartn_z(a, value):
|
|
184
|
+
a.xyz_cartn[2] = leading_float(value)
|
|
185
|
+
|
|
186
|
+
_tr_atom_site_cartn_z = staticmethod(_tr_atom_site_cartn_z)
|
|
187
|
+
|
|
188
|
+
def _tr_atom_site_U_iso_or_equiv(a, value):
|
|
189
|
+
a.Uisoequiv = leading_float(value)
|
|
190
|
+
|
|
191
|
+
_tr_atom_site_U_iso_or_equiv = staticmethod(_tr_atom_site_U_iso_or_equiv)
|
|
192
|
+
|
|
193
|
+
def _tr_atom_site_B_iso_or_equiv(a, value):
|
|
194
|
+
a.Uisoequiv = P_cif.BtoU * leading_float(value)
|
|
195
|
+
|
|
196
|
+
_tr_atom_site_B_iso_or_equiv = staticmethod(_tr_atom_site_B_iso_or_equiv)
|
|
197
|
+
|
|
198
|
+
def _tr_atom_site_adp_type(a, value):
|
|
199
|
+
a.anisotropy = value not in ("Uiso", "Biso")
|
|
200
|
+
|
|
201
|
+
_tr_atom_site_adp_type = staticmethod(_tr_atom_site_adp_type)
|
|
202
|
+
_tr_atom_site_thermal_displace_type = _tr_atom_site_adp_type
|
|
203
|
+
|
|
204
|
+
def _tr_atom_site_occupancy(a, value):
|
|
205
|
+
a.occupancy = leading_float(value, 1.0)
|
|
206
|
+
|
|
207
|
+
_tr_atom_site_occupancy = staticmethod(_tr_atom_site_occupancy)
|
|
208
|
+
|
|
209
|
+
def _tr_atom_site_aniso_U_11(a, value):
|
|
210
|
+
a.U11 = leading_float(value)
|
|
211
|
+
|
|
212
|
+
_tr_atom_site_aniso_U_11 = staticmethod(_tr_atom_site_aniso_U_11)
|
|
213
|
+
|
|
214
|
+
def _tr_atom_site_aniso_U_22(a, value):
|
|
215
|
+
a.U22 = leading_float(value)
|
|
216
|
+
|
|
217
|
+
_tr_atom_site_aniso_U_22 = staticmethod(_tr_atom_site_aniso_U_22)
|
|
218
|
+
|
|
219
|
+
def _tr_atom_site_aniso_U_33(a, value):
|
|
220
|
+
a.U33 = leading_float(value)
|
|
221
|
+
|
|
222
|
+
_tr_atom_site_aniso_U_33 = staticmethod(_tr_atom_site_aniso_U_33)
|
|
223
|
+
|
|
224
|
+
def _tr_atom_site_aniso_U_12(a, value):
|
|
225
|
+
a.U12 = leading_float(value)
|
|
226
|
+
|
|
227
|
+
_tr_atom_site_aniso_U_12 = staticmethod(_tr_atom_site_aniso_U_12)
|
|
228
|
+
|
|
229
|
+
def _tr_atom_site_aniso_U_13(a, value):
|
|
230
|
+
a.U13 = leading_float(value)
|
|
231
|
+
|
|
232
|
+
_tr_atom_site_aniso_U_13 = staticmethod(_tr_atom_site_aniso_U_13)
|
|
233
|
+
|
|
234
|
+
def _tr_atom_site_aniso_U_23(a, value):
|
|
235
|
+
a.U23 = leading_float(value)
|
|
236
|
+
|
|
237
|
+
_tr_atom_site_aniso_U_23 = staticmethod(_tr_atom_site_aniso_U_23)
|
|
238
|
+
|
|
239
|
+
def _tr_atom_site_aniso_B_11(a, value):
|
|
240
|
+
a.U11 = P_cif.BtoU * leading_float(value)
|
|
241
|
+
|
|
242
|
+
_tr_atom_site_aniso_B_11 = staticmethod(_tr_atom_site_aniso_B_11)
|
|
243
|
+
|
|
244
|
+
def _tr_atom_site_aniso_B_22(a, value):
|
|
245
|
+
a.U22 = P_cif.BtoU * leading_float(value)
|
|
246
|
+
|
|
247
|
+
_tr_atom_site_aniso_B_22 = staticmethod(_tr_atom_site_aniso_B_22)
|
|
248
|
+
|
|
249
|
+
def _tr_atom_site_aniso_B_33(a, value):
|
|
250
|
+
a.U33 = P_cif.BtoU * leading_float(value)
|
|
251
|
+
|
|
252
|
+
_tr_atom_site_aniso_B_33 = staticmethod(_tr_atom_site_aniso_B_33)
|
|
253
|
+
|
|
254
|
+
def _tr_atom_site_aniso_B_12(a, value):
|
|
255
|
+
a.U12 = P_cif.BtoU * leading_float(value)
|
|
256
|
+
|
|
257
|
+
_tr_atom_site_aniso_B_12 = staticmethod(_tr_atom_site_aniso_B_12)
|
|
258
|
+
|
|
259
|
+
def _tr_atom_site_aniso_B_13(a, value):
|
|
260
|
+
a.U13 = P_cif.BtoU * leading_float(value)
|
|
261
|
+
|
|
262
|
+
_tr_atom_site_aniso_B_13 = staticmethod(_tr_atom_site_aniso_B_13)
|
|
263
|
+
|
|
264
|
+
def _tr_atom_site_aniso_B_23(a, value):
|
|
265
|
+
a.U23 = P_cif.BtoU * leading_float(value)
|
|
266
|
+
|
|
267
|
+
_tr_atom_site_aniso_B_23 = staticmethod(_tr_atom_site_aniso_B_23)
|
|
268
|
+
|
|
269
|
+
def _get_atom_setters(cifloop):
|
|
270
|
+
"""Static method for finding translators of CifLoop items to data in `Atom` instance.
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
cifloop : CifLoop
|
|
275
|
+
Instance of `CifLoop`.
|
|
276
|
+
|
|
277
|
+
Returns
|
|
278
|
+
-------
|
|
279
|
+
list
|
|
280
|
+
List of setter functions in the order of `cifloop.keys()`.
|
|
281
|
+
"""
|
|
282
|
+
rv = []
|
|
283
|
+
for p in cifloop.keys():
|
|
284
|
+
lcname = "_tr" + p.lower()
|
|
285
|
+
fncname = P_cif._atom_setters.get(lcname, "_tr_ignore")
|
|
286
|
+
f = getattr(P_cif, fncname)
|
|
287
|
+
rv.append(f)
|
|
288
|
+
return rv
|
|
289
|
+
|
|
290
|
+
_get_atom_setters = staticmethod(_get_atom_setters)
|
|
291
|
+
|
|
292
|
+
# normal methods ---------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
def __init__(self, eps=None):
|
|
295
|
+
StructureParser.__init__(self)
|
|
296
|
+
self.format = "cif"
|
|
297
|
+
self.ciffile = None
|
|
298
|
+
self.stru = None
|
|
299
|
+
self.spacegroup = None
|
|
300
|
+
self.eps = eps
|
|
301
|
+
self.eau = None
|
|
302
|
+
self.asymmetric_unit = None
|
|
303
|
+
self.labelindex = {}
|
|
304
|
+
self.anisotropy = {}
|
|
305
|
+
self.cif_sgname = None
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
def parse(self, s):
|
|
309
|
+
"""Create `Structure` instance from a string in CIF format.
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
s : str
|
|
314
|
+
A string in CIF format.
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
Structure
|
|
319
|
+
`Structure` instance.
|
|
320
|
+
|
|
321
|
+
Raises
|
|
322
|
+
------
|
|
323
|
+
StructureFormatError
|
|
324
|
+
When the data do not constitute a valid CIF format.
|
|
325
|
+
"""
|
|
326
|
+
self.ciffile = None
|
|
327
|
+
self.filename = ""
|
|
328
|
+
fp = io.StringIO(s)
|
|
329
|
+
rv = self._parseCifDataSource(fp)
|
|
330
|
+
return rv
|
|
331
|
+
|
|
332
|
+
def parseLines(self, lines):
|
|
333
|
+
"""Parse list of lines in CIF format.
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
lines : list
|
|
338
|
+
List of strings stripped of line terminator.
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
Structure
|
|
343
|
+
`Structure` instance.
|
|
344
|
+
|
|
345
|
+
Raises
|
|
346
|
+
------
|
|
347
|
+
StructureFormatError
|
|
348
|
+
When the data do not constitute a valid CIF format.
|
|
349
|
+
"""
|
|
350
|
+
s = "\n".join(lines) + "\n"
|
|
351
|
+
return self.parse(s)
|
|
352
|
+
|
|
353
|
+
def parseFile(self, filename):
|
|
354
|
+
"""Create Structure from an existing CIF file.
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
filename : str
|
|
359
|
+
Path to structure file.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
Structure
|
|
364
|
+
`Structure` instance.
|
|
365
|
+
|
|
366
|
+
Raises
|
|
367
|
+
------
|
|
368
|
+
StructureFormatError
|
|
369
|
+
When the data do not constitute a valid CIF format.
|
|
370
|
+
IOError
|
|
371
|
+
When the file cannot be opened.
|
|
372
|
+
"""
|
|
373
|
+
self.ciffile = None
|
|
374
|
+
self.filename = filename
|
|
375
|
+
rv = self._parseCifDataSource(filename)
|
|
376
|
+
# all good here
|
|
377
|
+
return rv
|
|
378
|
+
|
|
379
|
+
def _parseCifDataSource(self, datasource):
|
|
380
|
+
"""Open and process CIF data from the specified `datasource`.
|
|
381
|
+
|
|
382
|
+
Parameters
|
|
383
|
+
----------
|
|
384
|
+
datasource : str or a file-like object
|
|
385
|
+
This is used as an argument to the `CifFile` class. The `CifFile`
|
|
386
|
+
instance is stored in `ciffile` attribute of this Parser.
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
Structure
|
|
391
|
+
The `Structure` object loaded from the specified data source.
|
|
392
|
+
|
|
393
|
+
Raises
|
|
394
|
+
------
|
|
395
|
+
StructureFormatError
|
|
396
|
+
When the data do not constitute a valid CIF format.
|
|
397
|
+
"""
|
|
398
|
+
from CifFile import CifFile, StarError
|
|
399
|
+
|
|
400
|
+
self.stru = None
|
|
401
|
+
try:
|
|
402
|
+
with _suppressCifParserOutput():
|
|
403
|
+
# Use `grammar` option to digest values with curly-brackets.
|
|
404
|
+
# Ref: https://bitbucket.org/jamesrhester/pycifrw/issues/19
|
|
405
|
+
self.ciffile = CifFile(datasource, grammar="auto")
|
|
406
|
+
for blockname in self.ciffile.keys():
|
|
407
|
+
self._parseCifBlock(blockname)
|
|
408
|
+
# stop after reading the first structure
|
|
409
|
+
if self.stru is not None:
|
|
410
|
+
break
|
|
411
|
+
except (StarError, ValueError, IndexError) as err:
|
|
412
|
+
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
413
|
+
emsg = str(err).strip()
|
|
414
|
+
e = StructureFormatError(emsg)
|
|
415
|
+
raise e.with_traceback(exc_traceback)
|
|
416
|
+
return self.stru
|
|
417
|
+
|
|
418
|
+
def _parseCifBlock(self, blockname):
|
|
419
|
+
"""Translate CIF file block, skip blocks without `_atom_site_label`.
|
|
420
|
+
Updates data members `stru`, `eau`.
|
|
421
|
+
|
|
422
|
+
Parameters
|
|
423
|
+
----------
|
|
424
|
+
blockname : str
|
|
425
|
+
Name of top level block in `self.ciffile`.
|
|
426
|
+
"""
|
|
427
|
+
block = self.ciffile[blockname]
|
|
428
|
+
if "_atom_site_label" not in block:
|
|
429
|
+
return
|
|
430
|
+
# here block contains structure, initialize output data
|
|
431
|
+
self.stru = Structure()
|
|
432
|
+
self.labelindex.clear()
|
|
433
|
+
self.anisotropy.clear()
|
|
434
|
+
# execute specialized block parsers
|
|
435
|
+
self._parse_lattice(block)
|
|
436
|
+
self._parse_atom_site_label(block)
|
|
437
|
+
self._parse_atom_site_aniso_label(block)
|
|
438
|
+
self._parse_space_group_symop_operation_xyz(block)
|
|
439
|
+
return
|
|
440
|
+
|
|
441
|
+
def _parse_lattice(self, block):
|
|
442
|
+
"""Obtain `lattice` parameters from a `CifBlock`.
|
|
443
|
+
|
|
444
|
+
This method updates `self.stru.lattic`e.
|
|
445
|
+
|
|
446
|
+
Parameters
|
|
447
|
+
----------
|
|
448
|
+
block : CifBlock
|
|
449
|
+
Instance of CifBlock.
|
|
450
|
+
"""
|
|
451
|
+
if "_cell_length_a" not in block:
|
|
452
|
+
return
|
|
453
|
+
# obtain lattice parameters
|
|
454
|
+
try:
|
|
455
|
+
latpars = (
|
|
456
|
+
leading_float(block["_cell_length_a"]),
|
|
457
|
+
leading_float(block["_cell_length_b"]),
|
|
458
|
+
leading_float(block["_cell_length_c"]),
|
|
459
|
+
leading_float(block["_cell_angle_alpha"]),
|
|
460
|
+
leading_float(block["_cell_angle_beta"]),
|
|
461
|
+
leading_float(block["_cell_angle_gamma"]),
|
|
462
|
+
)
|
|
463
|
+
except KeyError as err:
|
|
464
|
+
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
465
|
+
emsg = str(err)
|
|
466
|
+
e = StructureFormatError(emsg)
|
|
467
|
+
raise e.with_traceback(exc_traceback)
|
|
468
|
+
self.stru.lattice = Lattice(*latpars)
|
|
469
|
+
return
|
|
470
|
+
|
|
471
|
+
def _parse_atom_site_label(self, block):
|
|
472
|
+
"""Obtain atoms in asymmetric unit from a `CifBlock`.
|
|
473
|
+
|
|
474
|
+
This method inserts `Atom` instances to `self.stru` and
|
|
475
|
+
updates `labelindex` dictionary.
|
|
476
|
+
|
|
477
|
+
Parameters
|
|
478
|
+
----------
|
|
479
|
+
block : CifBlock
|
|
480
|
+
Instance of `CifBlock`.
|
|
481
|
+
"""
|
|
482
|
+
# process _atom_site_label
|
|
483
|
+
atom_site_loop = block.GetLoop("_atom_site_label")
|
|
484
|
+
does_adp_type = (
|
|
485
|
+
"_atom_site_adp_type" in atom_site_loop or "_atom_site_thermal_displace_type" in atom_site_loop
|
|
486
|
+
)
|
|
487
|
+
# get a list of setters for atom_site values
|
|
488
|
+
prop_setters = P_cif._get_atom_setters(atom_site_loop)
|
|
489
|
+
# index of the _atom_site_label item for the labelindex dictionary
|
|
490
|
+
ilb = atom_site_loop.keys().index("_atom_site_label")
|
|
491
|
+
# loop through the values and pass them to the setters
|
|
492
|
+
sitedatalist = zip(*atom_site_loop.values())
|
|
493
|
+
for values in sitedatalist:
|
|
494
|
+
curlabel = values[ilb]
|
|
495
|
+
# skip entries that have invalid label
|
|
496
|
+
if curlabel == "?":
|
|
497
|
+
continue
|
|
498
|
+
self.labelindex[curlabel] = len(self.stru)
|
|
499
|
+
self.stru.addNewAtom()
|
|
500
|
+
a = self.stru.getLastAtom()
|
|
501
|
+
for fset, val in zip(prop_setters, values):
|
|
502
|
+
fset(a, val)
|
|
503
|
+
if does_adp_type:
|
|
504
|
+
self.anisotropy[curlabel] = a.anisotropy
|
|
505
|
+
return
|
|
506
|
+
|
|
507
|
+
def _parse_atom_site_aniso_label(self, block):
|
|
508
|
+
"""Obtain value of anisotropic thermal displacements from a `CifBlock`.
|
|
509
|
+
|
|
510
|
+
This method updates `U` members of `Atom` instances in `self.stru`.
|
|
511
|
+
The `labelindex` dictionary has to be defined beforehand.
|
|
512
|
+
|
|
513
|
+
Parameters
|
|
514
|
+
----------
|
|
515
|
+
block : CifBlock
|
|
516
|
+
Instance of `CifBlock`.
|
|
517
|
+
"""
|
|
518
|
+
if "_atom_site_aniso_label" not in block:
|
|
519
|
+
return
|
|
520
|
+
# something to do here:
|
|
521
|
+
adp_loop = block.GetLoop("_atom_site_aniso_label")
|
|
522
|
+
# index of the _atom_site_label column
|
|
523
|
+
ilb = adp_loop.keys().index("_atom_site_aniso_label")
|
|
524
|
+
# get a list of setters for this loop
|
|
525
|
+
prop_setters = P_cif._get_atom_setters(adp_loop)
|
|
526
|
+
sitedatalist = zip(*adp_loop.values())
|
|
527
|
+
for values in sitedatalist:
|
|
528
|
+
lb = values[ilb]
|
|
529
|
+
if lb == "?":
|
|
530
|
+
break
|
|
531
|
+
idx = self.labelindex[lb]
|
|
532
|
+
a = self.stru[idx]
|
|
533
|
+
if lb not in self.anisotropy:
|
|
534
|
+
a.anisotropy = True
|
|
535
|
+
self.anisotropy[lb] = True
|
|
536
|
+
for fset, val in zip(prop_setters, values):
|
|
537
|
+
fset(a, val)
|
|
538
|
+
return
|
|
539
|
+
|
|
540
|
+
def _parse_space_group_symop_operation_xyz(self, block):
|
|
541
|
+
"""Process symmetry operations from a CifBlock.
|
|
542
|
+
|
|
543
|
+
The method updates `spacegroup` and `eau` data according to symmetry
|
|
544
|
+
operations defined in `_space_group_symop_operation_xyz` or
|
|
545
|
+
`_symmetry_equiv_pos_as_xyz` items in `CifBlock`.
|
|
546
|
+
|
|
547
|
+
Parameters
|
|
548
|
+
----------
|
|
549
|
+
block : CifBlock
|
|
550
|
+
Instance of `CifBlock`.
|
|
551
|
+
"""
|
|
552
|
+
from diffpy.structure.spacegroups import FindSpaceGroup, GetSpaceGroup, IsSpaceGroupIdentifier, SpaceGroup
|
|
553
|
+
|
|
554
|
+
self.asymmetric_unit = list(self.stru)
|
|
555
|
+
sym_synonyms = ("_space_group_symop_operation_xyz", "_symmetry_equiv_pos_as_xyz")
|
|
556
|
+
sym_loop_name = [n for n in sym_synonyms if n in block]
|
|
557
|
+
# recover explicit list of symmetry operations
|
|
558
|
+
symop_list = []
|
|
559
|
+
if sym_loop_name:
|
|
560
|
+
# sym_loop exists here and we know its cif name
|
|
561
|
+
sym_loop_name = sym_loop_name[0]
|
|
562
|
+
sym_loop = block.GetLoop(sym_loop_name)
|
|
563
|
+
for eqxyz in sym_loop[sym_loop_name]:
|
|
564
|
+
opcif = getSymOp(eqxyz)
|
|
565
|
+
symop_list.append(opcif)
|
|
566
|
+
# determine space group number
|
|
567
|
+
sg_nameHall = block.get("_space_group_name_Hall", "") or block.get("_symmetry_space_group_name_Hall", "")
|
|
568
|
+
sg_nameHM = (
|
|
569
|
+
block.get("_space_group_name_H-M_alt", "")
|
|
570
|
+
or block.get("_space_group_name_H-M_ref", "")
|
|
571
|
+
or block.get("_symmetry_space_group_name_H-M", "")
|
|
572
|
+
)
|
|
573
|
+
self.cif_sgname = sg_nameHall or sg_nameHM or None
|
|
574
|
+
sgid = block.get("_space_group_IT_number", "") or block.get("_symmetry_Int_Tables_number", "") or sg_nameHM
|
|
575
|
+
self.spacegroup = None
|
|
576
|
+
# try to reuse existing space group from symmetry operations
|
|
577
|
+
if symop_list:
|
|
578
|
+
try:
|
|
579
|
+
self.spacegroup = FindSpaceGroup(symop_list)
|
|
580
|
+
except ValueError:
|
|
581
|
+
pass
|
|
582
|
+
# otherwise lookup the space group from its identifier
|
|
583
|
+
if self.spacegroup is None and sgid and IsSpaceGroupIdentifier(sgid):
|
|
584
|
+
self.spacegroup = GetSpaceGroup(sgid)
|
|
585
|
+
# define new spacegroup when symmetry operations were listed, but
|
|
586
|
+
# there is no match to an existing definition
|
|
587
|
+
if symop_list and self.spacegroup is None:
|
|
588
|
+
new_short_name = "CIF " + (sg_nameHall or "data")
|
|
589
|
+
new_crystal_system = (
|
|
590
|
+
block.get("_space_group_crystal_system") or block.get("_symmetry_cell_setting") or "TRICLINIC"
|
|
591
|
+
).upper()
|
|
592
|
+
self.spacegroup = SpaceGroup(
|
|
593
|
+
short_name=new_short_name, crystal_system=new_crystal_system, symop_list=symop_list
|
|
594
|
+
)
|
|
595
|
+
if self.spacegroup is None:
|
|
596
|
+
emsg = "CIF file has unknown space group identifier {!r}."
|
|
597
|
+
raise StructureFormatError(emsg.format(sgid))
|
|
598
|
+
self._expandAsymmetricUnit(block)
|
|
599
|
+
return
|
|
600
|
+
|
|
601
|
+
def _expandAsymmetricUnit(self, block):
|
|
602
|
+
"""Perform symmetry expansion of `self.stru` using `self.spacegroup`.
|
|
603
|
+
|
|
604
|
+
This method updates data in `stru` and `eau`.
|
|
605
|
+
|
|
606
|
+
Parameters
|
|
607
|
+
----------
|
|
608
|
+
block : CifBlock
|
|
609
|
+
The top-level block containing crystal structure data.
|
|
610
|
+
"""
|
|
611
|
+
from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit
|
|
612
|
+
|
|
613
|
+
corepos = [a.xyz for a in self.stru]
|
|
614
|
+
coreUijs = [a.U for a in self.stru]
|
|
615
|
+
self.eau = ExpandAsymmetricUnit(self.spacegroup, corepos, coreUijs, eps=self.eps)
|
|
616
|
+
# setup anisotropy according to symmetry requirements
|
|
617
|
+
# unless it was already explicitly set
|
|
618
|
+
for ca, uisotropy in zip(self.stru, self.eau.Uisotropy):
|
|
619
|
+
if ca.label not in self.anisotropy:
|
|
620
|
+
ca.anisotropy = not uisotropy
|
|
621
|
+
self.anisotropy[ca.label] = ca.anisotropy
|
|
622
|
+
# build a nested list of new atoms:
|
|
623
|
+
newatoms = []
|
|
624
|
+
for i, ca in enumerate(self.stru):
|
|
625
|
+
eca = [] # expanded core atom
|
|
626
|
+
for j in range(self.eau.multiplicity[i]):
|
|
627
|
+
a = Atom(ca)
|
|
628
|
+
a.xyz = self.eau.expandedpos[i][j]
|
|
629
|
+
if j > 0:
|
|
630
|
+
a.label += "_" + str(j + 1)
|
|
631
|
+
if a.anisotropy:
|
|
632
|
+
a.U = self.eau.expandedUijs[i][j]
|
|
633
|
+
eca.append(a)
|
|
634
|
+
newatoms.append(eca)
|
|
635
|
+
# insert new atoms where they belong
|
|
636
|
+
self.stru[:] = sum(newatoms, [])
|
|
637
|
+
return
|
|
638
|
+
|
|
639
|
+
# conversion to CIF ------------------------------------------------------
|
|
640
|
+
|
|
641
|
+
def toLines(self, stru):
|
|
642
|
+
"""Convert `Structure` to a list of lines in basic CIF format.
|
|
643
|
+
|
|
644
|
+
Parameters
|
|
645
|
+
----------
|
|
646
|
+
stru : Structure
|
|
647
|
+
The structure to be converted.
|
|
648
|
+
|
|
649
|
+
Returns
|
|
650
|
+
-------
|
|
651
|
+
list
|
|
652
|
+
List of lines in basic CIF format.
|
|
653
|
+
"""
|
|
654
|
+
import time
|
|
655
|
+
|
|
656
|
+
lines = []
|
|
657
|
+
# may be replaced with filtered Structure.title
|
|
658
|
+
# for now, we can add the title as a comment
|
|
659
|
+
if stru.title.strip() != "":
|
|
660
|
+
title_lines = stru.title.split("\n")
|
|
661
|
+
lines.extend(["# " + line.strip() for line in title_lines])
|
|
662
|
+
lines.append("")
|
|
663
|
+
lines.append("data_3D")
|
|
664
|
+
iso_date = "%04i-%02i-%02i" % time.gmtime()[:3]
|
|
665
|
+
lines.extend(
|
|
666
|
+
[
|
|
667
|
+
"%-31s %s" % ("_audit_creation_date", iso_date),
|
|
668
|
+
"%-31s %s" % ("_audit_creation_method", "P_cif.py"),
|
|
669
|
+
"",
|
|
670
|
+
"%-31s %s" % ("_symmetry_space_group_name_H-M", "'P1'"),
|
|
671
|
+
"%-31s %s" % ("_symmetry_Int_Tables_number", "1"),
|
|
672
|
+
"%-31s %s" % ("_symmetry_cell_setting", "triclinic"),
|
|
673
|
+
"",
|
|
674
|
+
]
|
|
675
|
+
)
|
|
676
|
+
# there should be no need to specify equivalent positions for P1
|
|
677
|
+
# _symmetry_equiv_posi_as_xyz x,y,z
|
|
678
|
+
lines.extend(
|
|
679
|
+
[
|
|
680
|
+
"%-31s %.6g" % ("_cell_length_a", stru.lattice.a),
|
|
681
|
+
"%-31s %.6g" % ("_cell_length_b", stru.lattice.b),
|
|
682
|
+
"%-31s %.6g" % ("_cell_length_c", stru.lattice.c),
|
|
683
|
+
"%-31s %.6g" % ("_cell_angle_alpha", stru.lattice.alpha),
|
|
684
|
+
"%-31s %.6g" % ("_cell_angle_beta", stru.lattice.beta),
|
|
685
|
+
"%-31s %.6g" % ("_cell_angle_gamma", stru.lattice.gamma),
|
|
686
|
+
"",
|
|
687
|
+
]
|
|
688
|
+
)
|
|
689
|
+
# build a list of site labels and adp (displacement factor) types
|
|
690
|
+
element_count = {}
|
|
691
|
+
a_site_label = []
|
|
692
|
+
a_adp_type = []
|
|
693
|
+
for a in stru:
|
|
694
|
+
cnt = element_count[a.element] = element_count.get(a.element, 0) + 1
|
|
695
|
+
a_site_label.append("%s%i" % (a.element, cnt))
|
|
696
|
+
if numpy.all(a.U == a.U[0, 0] * numpy.identity(3)):
|
|
697
|
+
a_adp_type.append("Uiso")
|
|
698
|
+
else:
|
|
699
|
+
a_adp_type.append("Uani")
|
|
700
|
+
# list all atoms
|
|
701
|
+
lines.extend(
|
|
702
|
+
[
|
|
703
|
+
"loop_",
|
|
704
|
+
" _atom_site_label",
|
|
705
|
+
" _atom_site_type_symbol",
|
|
706
|
+
" _atom_site_fract_x",
|
|
707
|
+
" _atom_site_fract_y",
|
|
708
|
+
" _atom_site_fract_z",
|
|
709
|
+
" _atom_site_U_iso_or_equiv",
|
|
710
|
+
" _atom_site_adp_type",
|
|
711
|
+
" _atom_site_occupancy",
|
|
712
|
+
]
|
|
713
|
+
)
|
|
714
|
+
for i in range(len(stru)):
|
|
715
|
+
a = stru[i]
|
|
716
|
+
line = " %-5s %-3s %11.6f %11.6f %11.6f %11.6f %-5s %.4f" % (
|
|
717
|
+
a_site_label[i],
|
|
718
|
+
a.element,
|
|
719
|
+
a.xyz[0],
|
|
720
|
+
a.xyz[1],
|
|
721
|
+
a.xyz[2],
|
|
722
|
+
a.Uisoequiv,
|
|
723
|
+
a_adp_type[i],
|
|
724
|
+
a.occupancy,
|
|
725
|
+
)
|
|
726
|
+
lines.append(line)
|
|
727
|
+
# find anisotropic atoms
|
|
728
|
+
idx_aniso = [i for i in range(len(stru)) if a_adp_type[i] != "Uiso"]
|
|
729
|
+
if idx_aniso != []:
|
|
730
|
+
lines.extend(
|
|
731
|
+
[
|
|
732
|
+
"loop_",
|
|
733
|
+
" _atom_site_aniso_label",
|
|
734
|
+
" _atom_site_aniso_U_11",
|
|
735
|
+
" _atom_site_aniso_U_22",
|
|
736
|
+
" _atom_site_aniso_U_33",
|
|
737
|
+
" _atom_site_aniso_U_12",
|
|
738
|
+
" _atom_site_aniso_U_13",
|
|
739
|
+
" _atom_site_aniso_U_23",
|
|
740
|
+
]
|
|
741
|
+
)
|
|
742
|
+
for i in idx_aniso:
|
|
743
|
+
a = stru[i]
|
|
744
|
+
line = " %-5s %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f" % (
|
|
745
|
+
a_site_label[i],
|
|
746
|
+
a.U[0, 0],
|
|
747
|
+
a.U[1, 1],
|
|
748
|
+
a.U[2, 2],
|
|
749
|
+
a.U[0, 1],
|
|
750
|
+
a.U[0, 2],
|
|
751
|
+
a.U[1, 2],
|
|
752
|
+
)
|
|
753
|
+
lines.append(line)
|
|
754
|
+
return lines
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# End of class P_cif
|
|
758
|
+
|
|
759
|
+
# Routines -------------------------------------------------------------------
|
|
760
|
+
|
|
761
|
+
# constant regular expression for leading_float()
|
|
762
|
+
rx_float = re.compile(r"[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?")
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def leading_float(s, d=0.0):
|
|
766
|
+
"""Extract the first float from a string and ignore trailing characters.
|
|
767
|
+
|
|
768
|
+
Useful for extracting values from "value(std)" syntax.
|
|
769
|
+
|
|
770
|
+
Parameters
|
|
771
|
+
----------
|
|
772
|
+
s : str
|
|
773
|
+
The string to be scanned for floating point value.
|
|
774
|
+
d : float, Optional
|
|
775
|
+
The default value when `s` is "." or "?", which in CIF
|
|
776
|
+
format stands for inapplicable and unknown, respectively.
|
|
777
|
+
|
|
778
|
+
Returns
|
|
779
|
+
-------
|
|
780
|
+
float
|
|
781
|
+
The extracted floating point value.
|
|
782
|
+
|
|
783
|
+
Raises
|
|
784
|
+
------
|
|
785
|
+
ValueError
|
|
786
|
+
When string does not start with a float.
|
|
787
|
+
"""
|
|
788
|
+
sbare = s.strip()
|
|
789
|
+
mx = rx_float.match(sbare)
|
|
790
|
+
if mx:
|
|
791
|
+
rv = float(mx.group())
|
|
792
|
+
elif sbare == "." or sbare == "?":
|
|
793
|
+
# CIF files may contain "." or "?" for unknown values
|
|
794
|
+
rv = d
|
|
795
|
+
else:
|
|
796
|
+
rv = float(sbare)
|
|
797
|
+
return rv
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
# helper dictionary for getSymOp()
|
|
801
|
+
symvec = {
|
|
802
|
+
"x": numpy.array([1, 0, 0], dtype=float),
|
|
803
|
+
"y": numpy.array([0, 1, 0], dtype=float),
|
|
804
|
+
"z": numpy.array([0, 0, 1], dtype=float),
|
|
805
|
+
"-x": numpy.array([-1, 0, 0], dtype=float),
|
|
806
|
+
"-y": numpy.array([0, -1, 0], dtype=float),
|
|
807
|
+
"-z": numpy.array([0, 0, -1], dtype=float),
|
|
808
|
+
}
|
|
809
|
+
symvec["+x"] = symvec["x"]
|
|
810
|
+
symvec["+y"] = symvec["y"]
|
|
811
|
+
symvec["+z"] = symvec["z"]
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def getSymOp(s):
|
|
815
|
+
"""Create `SpaceGroups.SymOp` instance from a string.
|
|
816
|
+
|
|
817
|
+
Parameters
|
|
818
|
+
----------
|
|
819
|
+
s : str
|
|
820
|
+
Formula for equivalent coordinates, for example ``'x,1/2-y,1/2+z'``.
|
|
821
|
+
|
|
822
|
+
Returns
|
|
823
|
+
-------
|
|
824
|
+
SymOp
|
|
825
|
+
Instance of `SymOp`.
|
|
826
|
+
"""
|
|
827
|
+
from diffpy.structure.spacegroups import SymOp
|
|
828
|
+
|
|
829
|
+
snoblanks = s.replace(" ", "")
|
|
830
|
+
eqlist = snoblanks.split(",")
|
|
831
|
+
R = numpy.zeros((3, 3), dtype=float)
|
|
832
|
+
t = numpy.zeros(3, dtype=float)
|
|
833
|
+
for i in (0, 1, 2):
|
|
834
|
+
eqparts = re.split("(?i)([+-]?[xyz])", eqlist[i])
|
|
835
|
+
for Rpart in eqparts[1::2]:
|
|
836
|
+
R[i, :] += symvec[Rpart.lower()]
|
|
837
|
+
for tpart in eqparts[::2]:
|
|
838
|
+
t[i] += eval("1.0*%s+0" % tpart)
|
|
839
|
+
t -= numpy.floor(t)
|
|
840
|
+
rv = SymOp(R, t)
|
|
841
|
+
return rv
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def getParser(eps=None):
|
|
845
|
+
"""Return new `parser` object for CIF format.
|
|
846
|
+
|
|
847
|
+
Parameters
|
|
848
|
+
----------
|
|
849
|
+
eps : float, Optional
|
|
850
|
+
fractional coordinates cutoff for duplicate positions.
|
|
851
|
+
When ``None`` use the default for `ExpandAsymmetricUnit`: ``1.0e-5``.
|
|
852
|
+
|
|
853
|
+
Returns
|
|
854
|
+
-------
|
|
855
|
+
P_cif
|
|
856
|
+
Instance of `P_cif`.
|
|
857
|
+
"""
|
|
858
|
+
return P_cif(eps=eps)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
# Local Helpers --------------------------------------------------------------
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
@contextmanager
|
|
865
|
+
def _suppressCifParserOutput():
|
|
866
|
+
"""Context manager which suppresses diagnostic messages from CIF parser."""
|
|
867
|
+
from CifFile import yapps3_compiled_rt
|
|
868
|
+
|
|
869
|
+
print_error = yapps3_compiled_rt.print_error
|
|
870
|
+
# replace the print_error function with no-operation
|
|
871
|
+
yapps3_compiled_rt.print_error = lambda *a, **kw: None
|
|
872
|
+
try:
|
|
873
|
+
yield print_error
|
|
874
|
+
finally:
|
|
875
|
+
yapps3_compiled_rt.print_error = print_error
|
|
876
|
+
pass
|