pyemaps 2.0.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.
- pyemaps/COPYING +798 -0
- pyemaps/CifFile/CifFile_module.py +3117 -0
- pyemaps/CifFile/StarFile.py +2496 -0
- pyemaps/CifFile/TypeContentsParser.py +82 -0
- pyemaps/CifFile/YappsStarParser_1_0.py +289 -0
- pyemaps/CifFile/YappsStarParser_1_1.py +288 -0
- pyemaps/CifFile/YappsStarParser_2_0.py +369 -0
- pyemaps/CifFile/YappsStarParser_STAR2.py +382 -0
- pyemaps/CifFile/__init__.py +7 -0
- pyemaps/CifFile/drel/__init__.py +1 -0
- pyemaps/CifFile/drel/drel_ast_yacc.py +582 -0
- pyemaps/CifFile/drel/drel_lex.py +228 -0
- pyemaps/CifFile/drel/drel_runtime.py +126 -0
- pyemaps/CifFile/drel/parsetab.py +185 -0
- pyemaps/CifFile/drel/py_from_ast.py +628 -0
- pyemaps/CifFile/parsetab.py +185 -0
- pyemaps/CifFile/yapps3_compiled_rt.py +390 -0
- pyemaps/README.md +201 -0
- pyemaps/__config__.py +91 -0
- pyemaps/__init__.py +233 -0
- pyemaps/__main__.py +219 -0
- pyemaps/__version__.py +1 -0
- pyemaps/cdata/Aluminium.xtl +4 -0
- pyemaps/cdata/AluminiumOxide.xtl +5 -0
- pyemaps/cdata/Aluminium_FCC.xtl +4 -0
- pyemaps/cdata/BariumTitanate_180k.xtl +6 -0
- pyemaps/cdata/BariumTitanate_270k.xtl +7 -0
- pyemaps/cdata/BariumTitanate_Tetra.xtl +7 -0
- pyemaps/cdata/BiMnO3.xtl +27 -0
- pyemaps/cdata/Boron_Tetra.xtl +8 -0
- pyemaps/cdata/CadmiumSelenide_Hex.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Cubic.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Hex.xtl +5 -0
- pyemaps/cdata/Chromium_BCC.xtl +4 -0
- pyemaps/cdata/CoSb3_Skutterudite.xtl +5 -0
- pyemaps/cdata/CopperOxide.xtl +5 -0
- pyemaps/cdata/Copper_FCC.xtl +4 -0
- pyemaps/cdata/Cu2O_Cuprite.xtl +5 -0
- pyemaps/cdata/Diamond.xtl +4 -0
- pyemaps/cdata/ErbiumPyrogermanate.xtl +9 -0
- pyemaps/cdata/FePd_Tetra.xtl +5 -0
- pyemaps/cdata/FeS2_Pyrite.xtl +15 -0
- pyemaps/cdata/GalliumAntimonide.xtl +5 -0
- pyemaps/cdata/GalliumArsenide.xtl +5 -0
- pyemaps/cdata/GalliumNitride.xtl +5 -0
- pyemaps/cdata/Germanium.xtl +4 -0
- pyemaps/cdata/Gold_FCC.xtl +4 -0
- pyemaps/cdata/IndiumArsenide.xtl +5 -0
- pyemaps/cdata/LaMnO3.xtl +7 -0
- pyemaps/cdata/LeadZirconateTitanate.xtl +8 -0
- pyemaps/cdata/Li2MnO3.xtl +13 -0
- pyemaps/cdata/NaFeO2.xtl +6 -0
- pyemaps/cdata/Nb3Sn.xtl +5 -0
- pyemaps/cdata/Pentacene.xtl +39 -0
- pyemaps/cdata/Silicon.xtl +4 -0
- pyemaps/cdata/StrontiumTitanate.xtl +6 -0
- pyemaps/cdata/TelluriumDioxide.xtl +5 -0
- pyemaps/cdata/TinDioxide_RT.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Anatase.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Rutile.xtl +5 -0
- pyemaps/cdata/TungstenDiselenide.xtl +5 -0
- pyemaps/cdata/VanadiumDioxide_RT.xtl +6 -0
- pyemaps/cdata/ZincOxide.xtl +5 -0
- pyemaps/cdata/Zinc_HCP.xtl +4 -0
- pyemaps/cdata/ZirconiumNitride.xtl +5 -0
- pyemaps/cdata/limno2.xtl +6 -0
- pyemaps/crystals.py +1415 -0
- pyemaps/ddiffs.py +147 -0
- pyemaps/diffract/__init__.py +70 -0
- pyemaps/diffract/bloch_dec.py +779 -0
- pyemaps/diffract/csf_dec.py +242 -0
- pyemaps/diffract/dif_dec.py +519 -0
- pyemaps/diffract/dpgen_dec.py +175 -0
- pyemaps/diffract/mxtal_dec.py +201 -0
- pyemaps/diffract/powder_dec.py +137 -0
- pyemaps/diffract/stereo_dec.py +111 -0
- pyemaps/display.py +791 -0
- pyemaps/emcontrols.py +1017 -0
- pyemaps/errors.py +266 -0
- pyemaps/fileutils.py +1001 -0
- pyemaps/kdiffs.py +1221 -0
- pyemaps/license.txt +42 -0
- pyemaps/samples/adf.py +137 -0
- pyemaps/samples/al.img +0 -0
- pyemaps/samples/al_dpgen.py +57 -0
- pyemaps/samples/al_ediom.py +94 -0
- pyemaps/samples/convert_image.py +88 -0
- pyemaps/samples/dm_diff.py +225 -0
- pyemaps/samples/mask.im3 +0 -0
- pyemaps/samples/powder.py +51 -0
- pyemaps/samples/raw.img +0 -0
- pyemaps/samples/si_bloch.py +120 -0
- pyemaps/samples/si_constructor.py +51 -0
- pyemaps/samples/si_csf.py +59 -0
- pyemaps/samples/si_dif.py +134 -0
- pyemaps/samples/si_lacbed.py +59 -0
- pyemaps/samples/si_pyemaps.py +80 -0
- pyemaps/samples/si_scm.py +156 -0
- pyemaps/samples/si_stereo.py +116 -0
- pyemaps/scattering/__init__.py +26 -0
- pyemaps/scattering/sct_dec.py +31 -0
- pyemaps/spg/__init__.py +25 -0
- pyemaps/spg/spg_dec.py +60 -0
- pyemaps/stackimg.py +1031 -0
- pyemaps/xtal.py +222 -0
- pyemaps-2.0.0.dist-info/METADATA +231 -0
- pyemaps-2.0.0.dist-info/RECORD +112 -0
- pyemaps-2.0.0.dist-info/WHEEL +5 -0
- pyemaps-2.0.0.dist-info/entry_points.txt +2 -0
- pyemaps-2.0.0.dist-info/licenses/COPYING +798 -0
- pyemaps-2.0.0.dist-info/licenses/license.txt +42 -0
- pyemaps-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Yapps 2 Runtime, part of Yapps 2 - yet another python parser system
|
|
3
|
+
# Copyright 1999-2003 by Amit J. Patel <amitp@cs.stanford.edu>
|
|
4
|
+
#
|
|
5
|
+
# This version of the Yapps 2 Runtime can be distributed under the
|
|
6
|
+
# terms of the MIT open source license, either found in the LICENSE file
|
|
7
|
+
# included with the Yapps distribution
|
|
8
|
+
# <http://theory.stanford.edu/~amitp/yapps/> or at
|
|
9
|
+
# <http://www.opensource.org/licenses/mit-license.php>
|
|
10
|
+
#
|
|
11
|
+
# Modified for PyCIFRW by JRH to allow external scanner
|
|
12
|
+
#
|
|
13
|
+
# To maximize python3/python2 compatibility
|
|
14
|
+
from __future__ import print_function
|
|
15
|
+
from __future__ import unicode_literals
|
|
16
|
+
from __future__ import division
|
|
17
|
+
from __future__ import absolute_import
|
|
18
|
+
|
|
19
|
+
""" Detail of JRH modifications.
|
|
20
|
+
|
|
21
|
+
The compiled module handles all token administration by itself, but
|
|
22
|
+
does not deal with restrictions. It also effectively removes the
|
|
23
|
+
context-sensitivity of Yapps, as it ignores restrictions, but
|
|
24
|
+
these restrictions turn out to be unnecessary for CIF.
|
|
25
|
+
|
|
26
|
+
Interestingly, the module scan function is never called directly
|
|
27
|
+
from python.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
"""Run time libraries needed to run parsers generated by Yapps.
|
|
32
|
+
|
|
33
|
+
This module defines parse-time exception classes, a scanner class, a
|
|
34
|
+
base class for parsers produced by Yapps, and a context class that
|
|
35
|
+
keeps track of the parse stack.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# TODO: it should be possible to embed yappsrt into the generated
|
|
40
|
+
# grammar to make a standalone module.
|
|
41
|
+
|
|
42
|
+
import sys, re
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# For normal installation this module is "emapsCifFile.yapps3_compiled_rt"
|
|
46
|
+
# and StarScan is an extension module within the parent CifFile module.
|
|
47
|
+
if __name__.startswith('pyemaps.CifFile.'):
|
|
48
|
+
try:
|
|
49
|
+
from . import StarScan
|
|
50
|
+
have_star_scan = True
|
|
51
|
+
except ImportError:
|
|
52
|
+
have_star_scan = False
|
|
53
|
+
# Otherwise assume this is imported from the yapps3/yapps2.py script
|
|
54
|
+
# that is executed from Makefile to generate YappsStarParser sources.
|
|
55
|
+
else:
|
|
56
|
+
assert __name__ == 'yapps3_compiled_rt', "Unexpected module name."
|
|
57
|
+
assert sys.argv[0].endswith('yapps2.py'), (
|
|
58
|
+
"This should be reached only when running yapps2.py in Makefile.")
|
|
59
|
+
have_star_scan = False
|
|
60
|
+
|
|
61
|
+
class YappsSyntaxError(Exception):
|
|
62
|
+
"""When we run into an unexpected token, this is the exception to use"""
|
|
63
|
+
def __init__(self, charpos=-1, msg="Bad Token", context=None):
|
|
64
|
+
Exception.__init__(self)
|
|
65
|
+
self.charpos = charpos
|
|
66
|
+
self.msg = msg
|
|
67
|
+
self.context = context
|
|
68
|
+
|
|
69
|
+
def __str__(self):
|
|
70
|
+
if self.charpos < 0: return 'SyntaxError'
|
|
71
|
+
else: return 'SyntaxError@char%s(%s)' % (repr(self.charpos), self.msg)
|
|
72
|
+
|
|
73
|
+
class NoMoreTokens(Exception):
|
|
74
|
+
"""Another exception object, for when we run out of tokens"""
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
class Scanner:
|
|
78
|
+
"""Yapps scanner.
|
|
79
|
+
|
|
80
|
+
The Yapps scanner can work in context sensitive or context
|
|
81
|
+
insensitive modes. The token(i) method is used to retrieve the
|
|
82
|
+
i-th token. It takes a restrict set that limits the set of tokens
|
|
83
|
+
it is allowed to return. In context sensitive mode, this restrict
|
|
84
|
+
set guides the scanner. In context insensitive mode, there is no
|
|
85
|
+
restriction (the set is always the full set of tokens).
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, patterns, ignore, input, scantype="standard"):
|
|
90
|
+
"""Initialize the scanner.
|
|
91
|
+
|
|
92
|
+
Parameters:
|
|
93
|
+
patterns : [(terminal, uncompiled regex), ...] or None
|
|
94
|
+
ignore : [terminal,...]
|
|
95
|
+
input : string
|
|
96
|
+
|
|
97
|
+
If patterns is None, we assume that the subclass has
|
|
98
|
+
defined self.patterns : [(terminal, compiled regex), ...].
|
|
99
|
+
Note that the patterns parameter expects uncompiled regexes,
|
|
100
|
+
whereas the self.patterns field expects compiled regexes.
|
|
101
|
+
"""
|
|
102
|
+
self.tokens = [] # [(begin char pos, end char pos, token name, matched text), ...]
|
|
103
|
+
self.restrictions = []
|
|
104
|
+
self.input = input
|
|
105
|
+
self.pos = 0
|
|
106
|
+
self.ignore = ignore
|
|
107
|
+
self.scantype = scantype
|
|
108
|
+
self.first_line_number = 1
|
|
109
|
+
if self.scantype == "flex" and have_star_scan:
|
|
110
|
+
StarScan.prepare(input)
|
|
111
|
+
self.scan = self.compiled_scan
|
|
112
|
+
self.token = self.compiled_token
|
|
113
|
+
self.__del__ = StarScan.cleanup
|
|
114
|
+
elif self.scantype == "flex":
|
|
115
|
+
print("WARNING: using Python scanner although C scanner requested")
|
|
116
|
+
self.scantype = "standard"
|
|
117
|
+
if self.scantype != "flex":
|
|
118
|
+
self.scan = self.interp_scan
|
|
119
|
+
self.token = self.interp_token
|
|
120
|
+
|
|
121
|
+
if patterns is not None:
|
|
122
|
+
# Compile the regex strings into regex objects
|
|
123
|
+
self.patterns = []
|
|
124
|
+
for terminal, regex in patterns:
|
|
125
|
+
self.patterns.append( (terminal, re.compile(regex)) )
|
|
126
|
+
|
|
127
|
+
def get_token_pos(self):
|
|
128
|
+
"""Get the current token position in the input text."""
|
|
129
|
+
return len(self.tokens)
|
|
130
|
+
|
|
131
|
+
def get_char_pos(self):
|
|
132
|
+
"""Get the current char position in the input text."""
|
|
133
|
+
return self.pos
|
|
134
|
+
|
|
135
|
+
def get_prev_char_pos(self, i=None):
|
|
136
|
+
"""Get the previous position (one token back) in the input text."""
|
|
137
|
+
if self.pos == 0: return 0
|
|
138
|
+
if i is None: i = -1
|
|
139
|
+
return self.tokens[i][0]
|
|
140
|
+
|
|
141
|
+
def get_line_number(self):
|
|
142
|
+
"""Get the line number of the current position in the input text."""
|
|
143
|
+
# TODO: make this work at any token/char position
|
|
144
|
+
return self.first_line_number + self.get_input_scanned().count('\n')
|
|
145
|
+
|
|
146
|
+
def get_column_number(self):
|
|
147
|
+
"""Get the column number of the current position in the input text."""
|
|
148
|
+
s = self.get_input_scanned()
|
|
149
|
+
i = s.rfind('\n') # may be -1, but that's okay in this case
|
|
150
|
+
return len(s) - (i+1)
|
|
151
|
+
|
|
152
|
+
def get_input_scanned(self):
|
|
153
|
+
"""Get the portion of the input that has been tokenized."""
|
|
154
|
+
return self.input[:self.pos]
|
|
155
|
+
|
|
156
|
+
def get_input_unscanned(self):
|
|
157
|
+
"""Get the portion of the input that has not yet been tokenized."""
|
|
158
|
+
return self.input[self.pos:]
|
|
159
|
+
|
|
160
|
+
def interp_token(self, i, restrict=None):
|
|
161
|
+
"""Get the i'th token in the input.
|
|
162
|
+
|
|
163
|
+
If i is one past the end, then scan for another token.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
|
|
167
|
+
restrict : [token, ...] or None; if restrict is None, then any
|
|
168
|
+
token is allowed. You may call token(i) more than once.
|
|
169
|
+
However, the restrict set may never be larger than what was
|
|
170
|
+
passed in on the first call to token(i).
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
if i == len(self.tokens):
|
|
174
|
+
self.scan(restrict)
|
|
175
|
+
if i < len(self.tokens):
|
|
176
|
+
# Make sure the restriction is more restricted. This
|
|
177
|
+
# invariant is needed to avoid ruining tokenization at
|
|
178
|
+
# position i+1 and higher.
|
|
179
|
+
if restrict and self.restrictions[i]:
|
|
180
|
+
for r in restrict:
|
|
181
|
+
if r not in self.restrictions[i]:
|
|
182
|
+
raise NotImplementedError("Unimplemented: restriction set changed")
|
|
183
|
+
return self.tokens[i]
|
|
184
|
+
raise NoMoreTokens()
|
|
185
|
+
|
|
186
|
+
def compiled_token(self,i,restrict=0):
|
|
187
|
+
try:
|
|
188
|
+
return StarScan.token(i)
|
|
189
|
+
except IndexError:
|
|
190
|
+
raise NoMoreTokens()
|
|
191
|
+
|
|
192
|
+
def __repr__(self):
|
|
193
|
+
"""Print the last 10 tokens that have been scanned in"""
|
|
194
|
+
output = ''
|
|
195
|
+
if self.scantype != "flex":
|
|
196
|
+
for t in self.tokens[-10:]:
|
|
197
|
+
output = '%s\n (@%s) %s = %s' % (output,t[0],t[2],repr(t[3]))
|
|
198
|
+
else:
|
|
199
|
+
out_tokens = StarScan.last_ten()
|
|
200
|
+
for t in out_tokens:
|
|
201
|
+
output = '%s\n (~line %s) %s = %s' % (output,t[0],t[2],repr(t[3]))
|
|
202
|
+
return output
|
|
203
|
+
|
|
204
|
+
def interp_scan(self, restrict):
|
|
205
|
+
"""Should scan another token and add it to the list, self.tokens,
|
|
206
|
+
and add the restriction to self.restrictions"""
|
|
207
|
+
# Prepare accepted pattern list
|
|
208
|
+
if restrict:
|
|
209
|
+
# only patterns in the 'restrict' parameter or in self.ignore
|
|
210
|
+
# are accepted
|
|
211
|
+
accepted_patterns=[]
|
|
212
|
+
for p_name, p_regexp in self.patterns:
|
|
213
|
+
if p_name not in restrict and p_name not in self.ignore:
|
|
214
|
+
pass
|
|
215
|
+
else:
|
|
216
|
+
accepted_patterns.append((p_name,p_regexp))
|
|
217
|
+
else:
|
|
218
|
+
# every pattern is good
|
|
219
|
+
accepted_patterns=self.patterns
|
|
220
|
+
# Keep looking for a token, ignoring any in self.ignore
|
|
221
|
+
while 1:
|
|
222
|
+
# Search the patterns for the longest match, with earlier
|
|
223
|
+
# tokens in the list having preference
|
|
224
|
+
best_match = -1
|
|
225
|
+
best_pat = '(error)'
|
|
226
|
+
for p,regexp in accepted_patterns:
|
|
227
|
+
m = regexp.match(self.input, self.pos)
|
|
228
|
+
if m and len(m.group(0)) > best_match:
|
|
229
|
+
# We got a match that's better than the previous one
|
|
230
|
+
best_pat = p
|
|
231
|
+
best_match = len(m.group(0))
|
|
232
|
+
|
|
233
|
+
# If we didn't find anything, raise an error
|
|
234
|
+
if best_pat == '(error)' and best_match < 0:
|
|
235
|
+
msg = 'Bad Token'
|
|
236
|
+
if restrict:
|
|
237
|
+
msg = 'Trying to find one of '+', '.join(restrict)
|
|
238
|
+
raise YappsSyntaxError(self.pos, msg)
|
|
239
|
+
|
|
240
|
+
# If we found something that isn't to be ignored, return it
|
|
241
|
+
if best_pat not in self.ignore:
|
|
242
|
+
# Create a token with this data
|
|
243
|
+
token = (self.pos, self.pos+best_match, best_pat,
|
|
244
|
+
self.input[self.pos:self.pos+best_match])
|
|
245
|
+
self.pos = self.pos + best_match
|
|
246
|
+
# Only add this token if it's not in the list
|
|
247
|
+
# (to prevent looping)
|
|
248
|
+
if not self.tokens or token != self.tokens[-1]:
|
|
249
|
+
self.tokens.append(token)
|
|
250
|
+
self.restrictions.append(restrict)
|
|
251
|
+
return
|
|
252
|
+
else:
|
|
253
|
+
# This token should be ignored ..
|
|
254
|
+
self.pos = self.pos + best_match
|
|
255
|
+
|
|
256
|
+
def compiled_scan(self,restrict):
|
|
257
|
+
token = StarScan.scan()
|
|
258
|
+
print("Calling compiled scan, got %s" % repr(token))
|
|
259
|
+
if token[2] not in restrict:
|
|
260
|
+
msg = "Bad Token"
|
|
261
|
+
if restrict:
|
|
262
|
+
msg = "Trying to find one of " + ", ".join(restrict)
|
|
263
|
+
raise YappsSyntaxError(self.pos,msg)
|
|
264
|
+
self.tokens.append(token)
|
|
265
|
+
self.restrictions.append(restrict)
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
class Parser:
|
|
269
|
+
"""Base class for Yapps-generated parsers.
|
|
270
|
+
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
def __init__(self, scanner):
|
|
274
|
+
self._scanner = scanner
|
|
275
|
+
self._pos = 0
|
|
276
|
+
|
|
277
|
+
def _peek(self, *types):
|
|
278
|
+
"""Returns the token type for lookahead; if there are any args
|
|
279
|
+
then the list of args is the set of token types to allow"""
|
|
280
|
+
tok = self._scanner.token(self._pos, types)
|
|
281
|
+
return tok[2]
|
|
282
|
+
|
|
283
|
+
def _scan(self, type):
|
|
284
|
+
"""Returns the matched text, and moves to the next token"""
|
|
285
|
+
tok = self._scanner.token(self._pos, [type])
|
|
286
|
+
if tok[2] != type:
|
|
287
|
+
raise YappsSyntaxError(tok[0], 'Trying to find '+type+' :'+ ' ,')
|
|
288
|
+
self._pos = 1 + self._pos
|
|
289
|
+
return tok[3]
|
|
290
|
+
|
|
291
|
+
class Context:
|
|
292
|
+
"""Class to represent the parser's call stack.
|
|
293
|
+
|
|
294
|
+
Every rule creates a Context that links to its parent rule. The
|
|
295
|
+
contexts can be used for debugging.
|
|
296
|
+
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
def __init__(self, parent, scanner, tokenpos, rule, args=()):
|
|
300
|
+
"""Create a new context.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
parent: Context object or None
|
|
304
|
+
scanner: Scanner object
|
|
305
|
+
pos: integer (scanner token position)
|
|
306
|
+
rule: string (name of the rule)
|
|
307
|
+
args: tuple listing parameters to the rule
|
|
308
|
+
|
|
309
|
+
"""
|
|
310
|
+
self.parent = parent
|
|
311
|
+
self.scanner = scanner
|
|
312
|
+
self.tokenpos = tokenpos
|
|
313
|
+
self.rule = rule
|
|
314
|
+
self.args = args
|
|
315
|
+
|
|
316
|
+
def __str__(self):
|
|
317
|
+
output = ''
|
|
318
|
+
if self.parent: output = str(self.parent) + ' > '
|
|
319
|
+
output += self.rule
|
|
320
|
+
return output
|
|
321
|
+
|
|
322
|
+
#
|
|
323
|
+
# Note that this sort of error printout is useless with the
|
|
324
|
+
# compiled scanner
|
|
325
|
+
#
|
|
326
|
+
|
|
327
|
+
def print_line_with_pointer(text, p):
|
|
328
|
+
"""Print the line of 'text' that includes position 'p',
|
|
329
|
+
along with a second line with a single caret (^) at position p"""
|
|
330
|
+
|
|
331
|
+
# TODO: separate out the logic for determining the line/character
|
|
332
|
+
# location from the logic for determining how to display an
|
|
333
|
+
# 80-column line to stderr.
|
|
334
|
+
|
|
335
|
+
# Now try printing part of the line
|
|
336
|
+
text = text[max(p-80, 0):p+80]
|
|
337
|
+
p = p - max(p-80, 0)
|
|
338
|
+
|
|
339
|
+
# Strip to the left
|
|
340
|
+
i = text[:p].rfind('\n')
|
|
341
|
+
j = text[:p].rfind('\r')
|
|
342
|
+
if i < 0 or (0 <= j < i): i = j
|
|
343
|
+
if 0 <= i < p:
|
|
344
|
+
p = p - i - 1
|
|
345
|
+
text = text[i+1:]
|
|
346
|
+
|
|
347
|
+
# Strip to the right
|
|
348
|
+
i = text.find('\n', p)
|
|
349
|
+
j = text.find('\r', p)
|
|
350
|
+
if i < 0 or (0 <= j < i): i = j
|
|
351
|
+
if i >= 0:
|
|
352
|
+
text = text[:i]
|
|
353
|
+
|
|
354
|
+
# Now shorten the text
|
|
355
|
+
while len(text) > 70 and p > 60:
|
|
356
|
+
# Cut off 10 chars
|
|
357
|
+
text = "..." + text[10:]
|
|
358
|
+
p = p - 7
|
|
359
|
+
|
|
360
|
+
# Now print the string, along with an indicator
|
|
361
|
+
print('> ',text,file=sys.stderr)
|
|
362
|
+
print('> ',' '*p + '^',file=sys.stderr)
|
|
363
|
+
|
|
364
|
+
def print_error(input, err, scanner):
|
|
365
|
+
"""Print error messages, the parser stack, and the input text -- for human-readable error messages."""
|
|
366
|
+
# NOTE: this function assumes 80 columns :-(
|
|
367
|
+
# Figure out the line number
|
|
368
|
+
line_number = scanner.get_line_number()
|
|
369
|
+
column_number = scanner.get_column_number()
|
|
370
|
+
print('%d:%d: %s' % (line_number, column_number, err.msg),file=sys.stderr)
|
|
371
|
+
|
|
372
|
+
context = err.context
|
|
373
|
+
if not context:
|
|
374
|
+
print_line_with_pointer(input, err.charpos)
|
|
375
|
+
|
|
376
|
+
while context:
|
|
377
|
+
# TODO: add line number
|
|
378
|
+
print('while parsing %s%s:' % (context.rule, tuple(context.args)),file=sys.stderr)
|
|
379
|
+
print_line_with_pointer(input, context.scanner.get_prev_char_pos(context.tokenpos))
|
|
380
|
+
context = context.parent
|
|
381
|
+
|
|
382
|
+
def wrap_error_reporter(parser, rule):
|
|
383
|
+
try:
|
|
384
|
+
return getattr(parser, rule)()
|
|
385
|
+
except YappsSyntaxError as e:
|
|
386
|
+
input = parser._scanner.input
|
|
387
|
+
print_error(input, e, parser._scanner)
|
|
388
|
+
except NoMoreTokens:
|
|
389
|
+
print('Could not complete parsing; stopped around here:',file=sys.stderr)
|
|
390
|
+
print(parser._scanner,file=sys.stderr)
|
pyemaps/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<!-- # Open Source __pyEMAPS__ Development Guidelines -->
|
|
2
|
+
|
|
3
|
+
1. <a id="contents"></a>[What's in pyEMAPS](#overview)
|
|
4
|
+
2. <a id="contents"></a>[Build pyEMAPS](#build)
|
|
5
|
+
3. <a id="contents"></a>[Installation](#installation)
|
|
6
|
+
4. <a id="contents"></a>[Testing](#test)
|
|
7
|
+
5. <a id="contents"></a>[Development Guidelines](#process)
|
|
8
|
+
5. <a id="contents"></a>[pyEMAPS Contributor License Agreement](#CLA)
|
|
9
|
+
5. <a id="contents"></a>[Code of Conduct](#COC)
|
|
10
|
+
|
|
11
|
+
## What's in pyEMAPS [`↩`](#contents) <a id="overview"></a>
|
|
12
|
+
**pyEMAPS** package is a collection of python modules and libraries designed
|
|
13
|
+
for transmission electron diffraction simulations and crystallographic calculations
|
|
14
|
+
for all crystal systems. The simulation is based on the theories of kinematical and
|
|
15
|
+
dynamical electron diffraction, as described by Spence and Zuo in the book of Electron Microdiffraction (Plenum, New York, 1992).
|
|
16
|
+
|
|
17
|
+
The base version is free for personal and commercial use, while additional licensed features in 4DSTEM are available for enhanced functionality.
|
|
18
|
+
|
|
19
|
+
### Features:
|
|
20
|
+
Free base features:
|
|
21
|
+
- Crystallography
|
|
22
|
+
- Crystal constructor
|
|
23
|
+
- Loading from a built-in crystal library
|
|
24
|
+
- CIF import
|
|
25
|
+
- Crystallographic transformations
|
|
26
|
+
- Real and reciprocal vector calculations
|
|
27
|
+
- Stereodiagram plotting
|
|
28
|
+
- Crystal Structure Factors Calculations
|
|
29
|
+
|
|
30
|
+
- Electron diffraction pattern, dynamical
|
|
31
|
+
- Convergent beam electron diffraction
|
|
32
|
+
- Electron scattering Matrix
|
|
33
|
+
- Electron dispersion
|
|
34
|
+
|
|
35
|
+
- Electron diffraction pattern, kinematical
|
|
36
|
+
- Selected area diffraction
|
|
37
|
+
- Kikuchi and high order Laue zone line geometry
|
|
38
|
+
- Convergent beam electron diffraction
|
|
39
|
+
- Electron powder diffraction simulations
|
|
40
|
+
|
|
41
|
+
**Extended features** (requires license):
|
|
42
|
+
- Electron diffraction patterns database builder and explore
|
|
43
|
+
- Electron diffraction pattern indexing
|
|
44
|
+
- Annular bright and dark field
|
|
45
|
+
- Masked images
|
|
46
|
+
|
|
47
|
+
Check our full [pyemaps documentation](https://emlab-solutions.github.io/pyemaps) for details of __pyEMAPS__ python classes designs and their interfaces.
|
|
48
|
+
|
|
49
|
+
We are looking for experts in electron microscope and crystallography, and/or python enthusiasts to join pyEMAPS project as contributors, please contact us at support@emlabsoftware.com if you are interested.
|
|
50
|
+
|
|
51
|
+
We greatly appreciate your contributions to pyEMAPS and look forward to making it a tool for the communty to accelaerate microscopy and crystallography education and research. If you benefit from __pyEMAPS__ in your microscopy and crystallography research and education, go to [PayPal](https://www.paypal.com/paypalme/pyemaps22) to donate. Your generous donations keep us in the business of providing software tools to the education and research communities.
|
|
52
|
+
|
|
53
|
+
See instructions below for contributor:
|
|
54
|
+
|
|
55
|
+
## Build pyEMAPS [`↩`](#content) <a id="build"></a>
|
|
56
|
+
|
|
57
|
+
Requirements and recommendations:
|
|
58
|
+
* __Python__: Version 3.12, 3.13, 3.14 or 3.15
|
|
59
|
+
* __Operating Systems__: Windows
|
|
60
|
+
* __VSCode__: code editing (recommended, for contributors only)
|
|
61
|
+
|
|
62
|
+
Building __pyEMAPS__ itself needs no compiler, because it is pure Python. Its compiled backend, __emaps__, supports Windows with Python 3.12 to 3.15, so __pyEMAPS__ does too. pyEMAPS 1.1.x remains available for Python 3.7. We are also looking for contributors to extend it to other platforms.
|
|
63
|
+
|
|
64
|
+
Like many python development, a virtual python environment is recommended to isolate your pyEMAPS development environment from your other python development projects.
|
|
65
|
+
|
|
66
|
+
Below are steps to establish __pyEMAPS__ build environment with python 3.12 as an example:
|
|
67
|
+
|
|
68
|
+
* Download the Python 3.12 Windows installer (64-bit) from [python.org](https://www.python.org/downloads/windows/).
|
|
69
|
+
* Run the installer, choose the following in installer GUI:
|
|
70
|
+
|
|
71
|
+
* Select Customize Installation
|
|
72
|
+
* Check and select pip
|
|
73
|
+
* Install for all users/specific users
|
|
74
|
+
* Create a virtual environment called "pyemaps_build" for the build
|
|
75
|
+
* Create a dev directory:
|
|
76
|
+
```
|
|
77
|
+
mkdir pyemaps-dev
|
|
78
|
+
```
|
|
79
|
+
(or choose your own directory name)
|
|
80
|
+
* Create pyemaps_build virtual environemnt:
|
|
81
|
+
```
|
|
82
|
+
py -3.12 -m venv pyemaps_build
|
|
83
|
+
```
|
|
84
|
+
* Activate pyemaps_build vitual environment:
|
|
85
|
+
```
|
|
86
|
+
.\pyemaps_build\Scripts\activate
|
|
87
|
+
```
|
|
88
|
+
* Update pip:
|
|
89
|
+
```
|
|
90
|
+
python -m pip install pip --upgrade
|
|
91
|
+
```
|
|
92
|
+
* Install other python packages required for building pyemaps, using pip:
|
|
93
|
+
```
|
|
94
|
+
pip install setuptools wheel build
|
|
95
|
+
```
|
|
96
|
+
* Build directory structure:
|
|
97
|
+
|
|
98
|
+
<pre>
|
|
99
|
+
📦pyemaps
|
|
100
|
+
┣━ 📂.git <----Source repository configurations
|
|
101
|
+
┣━ 📂build <----Intermediate build folder
|
|
102
|
+
┣━ 📂dist <----Build package files
|
|
103
|
+
┣━ 📂cdata <----Built-in crystal data in proprietory format
|
|
104
|
+
┣━ 📂CifFile <----CIF crystal data reader
|
|
105
|
+
┣━ 📂diffract <----Diffraction modules python interfaces
|
|
106
|
+
┣━ 📂docs <----Documentation using Sphinx. Document your changes!
|
|
107
|
+
┣━ 📂samples <----Sample code. Offical sample code for guiding pyemaps usages
|
|
108
|
+
┣━ 📂scattering <----Scattering data module
|
|
109
|
+
┣━ 📂spg <----Space group data module
|
|
110
|
+
┗━ 📂test <----Test cases including sanity, unit and stree tests. Keep adding them!
|
|
111
|
+
</pre>
|
|
112
|
+
where _docs_ for creation of documents requires Sphinx python package:
|
|
113
|
+
- Sphinx for documentation can be installed:
|
|
114
|
+
```
|
|
115
|
+
pip install Sphinx=5.3.0
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
* Build pyEMAPS package, run:
|
|
119
|
+
```
|
|
120
|
+
python build_pyemaps -t -v 1.0.0
|
|
121
|
+
```
|
|
122
|
+
Here the option "-t" indicates build is for local testing purposes.
|
|
123
|
+
You can replace the version number '1.0.0' with the one you desire.
|
|
124
|
+
|
|
125
|
+
When the option _-v_ is not specified. The build script will figure out the version
|
|
126
|
+
from pypi.org repository.
|
|
127
|
+
|
|
128
|
+
Note: there will be two additional diretcories:
|
|
129
|
+
- _build_. folder holding intermediate build files.
|
|
130
|
+
- _dist_. folder for final package that includes .whl and .tgz. The former is the package file and the latter is the current source code used to build the pacage in compressed format.
|
|
131
|
+
|
|
132
|
+
## Installation [`↩`](#contents) <a id="installation"></a>
|
|
133
|
+
|
|
134
|
+
* Install the new pyEMAPS package:
|
|
135
|
+
```
|
|
136
|
+
pip install .\dist\pyemaps-1.0.0-py3-none-any.whl
|
|
137
|
+
```
|
|
138
|
+
This will install new pyEMAPS just built in current python virtual environment
|
|
139
|
+
along with all of its dependent packages.
|
|
140
|
+
|
|
141
|
+
If you have a previous _pyemaps_ with versiual or larger than current package version.
|
|
142
|
+
In that case, you need to uninstall previous version before installing the new package:
|
|
143
|
+
```
|
|
144
|
+
pip uninstall pyemaps
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Testing [`↩`](#contents) <a id="test"></a>
|
|
148
|
+
|
|
149
|
+
Current pyEMAPS development comes with a set of basic testing suites, including
|
|
150
|
+
* sanity checks
|
|
151
|
+
* unit tests
|
|
152
|
+
* stress tests.
|
|
153
|
+
|
|
154
|
+
To run these test suites, call:
|
|
155
|
+
```
|
|
156
|
+
.\test\sanity\sanity.bat
|
|
157
|
+
```
|
|
158
|
+
We are looking for your tests contributions to make the development to keep high quality standards for _pyEMAPS_.
|
|
159
|
+
|
|
160
|
+
## Development Guidelines [`↩`](#contents) <a id="process"></a>
|
|
161
|
+
|
|
162
|
+
As any open source development project, we strongly recommend contributors to adhere to the following guidelines:
|
|
163
|
+
|
|
164
|
+
* __Issues creation__. Before making changes for bug fix or feature implementation, create an issue in the pyEMAPS' repository. Your concise description of the issue(s) serves well not only in laying good foundations for your design and implementations, but also in communicating to your fellow contributors.
|
|
165
|
+
|
|
166
|
+
* __Development Branch__. Create your own branch from 'main' where you work on your changes. The main branch is kept as production base.
|
|
167
|
+
|
|
168
|
+
* __Backwards Compatibilities Tests__. Existing test suites designed for testing most of the features are to ensure that no existing implementations are unintentionally broken after new changes are introduced.
|
|
169
|
+
|
|
170
|
+
* __New Tests Additions__. Writing new test cases for the new implementations are strongly encouraged.
|
|
171
|
+
|
|
172
|
+
* __Pull Requests__. Before merging your changes into production branch "main", a pull request is required. The request should also accompany a list of code reviewers from the contributors.
|
|
173
|
+
|
|
174
|
+
* __Code Review__. We took code review seriously, whether you are reviewing code or your code is being reviewed. This process provides important step of making sound implementations and preventing new bugs.
|
|
175
|
+
|
|
176
|
+
* __Documentation__. You are the author of the new implementations, so please take the pride by documenting them!
|
|
177
|
+
We use sphinx tool for documentation generation. To install sphinx, run:
|
|
178
|
+
```
|
|
179
|
+
pip install -U sphinx
|
|
180
|
+
```
|
|
181
|
+
in your python build virtual environment (pyemaps_build in our example).
|
|
182
|
+
|
|
183
|
+
Most of the documentations files are in "docs" directory. After making documentation changes to record your changes, compile them with sphinx:
|
|
184
|
+
```
|
|
185
|
+
sphinx-build -b html source .
|
|
186
|
+
```
|
|
187
|
+
The changes resulted from the above can be previewed by opening index.html with avaliable browser.
|
|
188
|
+
|
|
189
|
+
## pyEMAPS Contributor License Agreement [`↩`](#contents) <a id="CLA"></a>
|
|
190
|
+
|
|
191
|
+
Please read and agree to our [Contributor License Agreement](CONTRIBUTING.md) before contributing to this project.
|
|
192
|
+
|
|
193
|
+
## Code of Conduct [`↩`](#contents) <a id="COC"></a>
|
|
194
|
+
|
|
195
|
+
pyEMAPS is committed to providing a welcoming and inclusive environment for all contributors. We follow the [Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct/) as our code of conduct. Please make sure to review it before contributing.
|
|
196
|
+
|
|
197
|
+
**[Read the Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct/)**
|
|
198
|
+
|
|
199
|
+
Any instances of unacceptable behavior can be reported to support@emlabsoftware.com.
|
|
200
|
+
|
|
201
|
+
|
pyemaps/__config__.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# This file is generated by numpy's setup.py
|
|
2
|
+
# It contains system_info results at the time of building this package.
|
|
3
|
+
__all__ = ["get_info","show"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
extra_dll_dir = os.path.join(os.path.dirname(__file__), '.libs')
|
|
10
|
+
|
|
11
|
+
if sys.platform == 'win32' and os.path.isdir(extra_dll_dir):
|
|
12
|
+
if sys.version_info >= (3, 8):
|
|
13
|
+
os.add_dll_directory(extra_dll_dir)
|
|
14
|
+
else:
|
|
15
|
+
os.environ.setdefault('PATH', '')
|
|
16
|
+
os.environ['PATH'] += os.pathsep + extra_dll_dir
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_info(name):
|
|
20
|
+
g = globals()
|
|
21
|
+
return g.get(name, g.get(name + "_info", {}))
|
|
22
|
+
|
|
23
|
+
def show():
|
|
24
|
+
"""
|
|
25
|
+
Show libraries in the system on which NumPy was built.
|
|
26
|
+
|
|
27
|
+
Print information about various resources (libraries, library
|
|
28
|
+
directories, include directories, etc.) in the system on which
|
|
29
|
+
NumPy was built.
|
|
30
|
+
|
|
31
|
+
See Also
|
|
32
|
+
--------
|
|
33
|
+
get_include : Returns the directory containing NumPy C
|
|
34
|
+
header files.
|
|
35
|
+
|
|
36
|
+
Notes
|
|
37
|
+
-----
|
|
38
|
+
Classes specifying the information to be printed are defined
|
|
39
|
+
in the `numpy.distutils.system_info` module.
|
|
40
|
+
|
|
41
|
+
Information may include:
|
|
42
|
+
|
|
43
|
+
* ``language``: language used to write the libraries (mostly
|
|
44
|
+
C or f77)
|
|
45
|
+
* ``libraries``: names of libraries found in the system
|
|
46
|
+
* ``library_dirs``: directories containing the libraries
|
|
47
|
+
* ``include_dirs``: directories containing library header files
|
|
48
|
+
* ``src_dirs``: directories containing library source files
|
|
49
|
+
* ``define_macros``: preprocessor macros used by
|
|
50
|
+
``distutils.setup``
|
|
51
|
+
* ``baseline``: minimum CPU features required
|
|
52
|
+
* ``found``: dispatched features supported in the system
|
|
53
|
+
* ``not found``: dispatched features that are not supported
|
|
54
|
+
in the system
|
|
55
|
+
|
|
56
|
+
Examples
|
|
57
|
+
--------
|
|
58
|
+
>>> import numpy as np
|
|
59
|
+
>>> np.show_config()
|
|
60
|
+
blas_opt_info:
|
|
61
|
+
language = c
|
|
62
|
+
define_macros = [('HAVE_CBLAS', None)]
|
|
63
|
+
libraries = ['openblas', 'openblas']
|
|
64
|
+
library_dirs = ['/usr/local/lib']
|
|
65
|
+
"""
|
|
66
|
+
from numpy.core._multiarray_umath import (
|
|
67
|
+
__cpu_features__, __cpu_baseline__, __cpu_dispatch__
|
|
68
|
+
)
|
|
69
|
+
for name,info_dict in globals().items():
|
|
70
|
+
if name[0] == "_" or type(info_dict) is not type({}): continue
|
|
71
|
+
print(name + ":")
|
|
72
|
+
if not info_dict:
|
|
73
|
+
print(" NOT AVAILABLE")
|
|
74
|
+
for k,v in info_dict.items():
|
|
75
|
+
v = str(v)
|
|
76
|
+
if k == "sources" and len(v) > 200:
|
|
77
|
+
v = v[:60] + " ...\n... " + v[-60:]
|
|
78
|
+
print(" %s = %s" % (k,v))
|
|
79
|
+
|
|
80
|
+
features_found, features_not_found = [], []
|
|
81
|
+
for feature in __cpu_dispatch__:
|
|
82
|
+
if __cpu_features__[feature]:
|
|
83
|
+
features_found.append(feature)
|
|
84
|
+
else:
|
|
85
|
+
features_not_found.append(feature)
|
|
86
|
+
|
|
87
|
+
print("Supported SIMD extensions in this NumPy install:")
|
|
88
|
+
print(" baseline = %s" % (','.join(__cpu_baseline__)))
|
|
89
|
+
print(" found = %s" % (','.join(features_found)))
|
|
90
|
+
print(" not found = %s" % (','.join(features_not_found)))
|
|
91
|
+
|