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
pyemaps/display.py
ADDED
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. This file is part of pyEMAPS
|
|
3
|
+
|
|
4
|
+
.. ----
|
|
5
|
+
|
|
6
|
+
.. pyEMAPS is free software. You can redistribute it and/or modify
|
|
7
|
+
.. it under the terms of the GNU General Public License as published
|
|
8
|
+
.. by the Free Software Foundation, either version 3 of the License,
|
|
9
|
+
.. or (at your option) any later version..
|
|
10
|
+
|
|
11
|
+
.. pyEMAPS is distributed in the hope that it will be useful,
|
|
12
|
+
.. but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
.. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
.. GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
.. You should have received a copy of the GNU General Public License
|
|
17
|
+
.. along with pyEMAPS. If not, see `<https://www.gnu.org/licenses/>`_.
|
|
18
|
+
|
|
19
|
+
.. Contact supprort@emlabsoftware.com for any questions and comments.
|
|
20
|
+
|
|
21
|
+
.. ----
|
|
22
|
+
|
|
23
|
+
.. Author: EMLab Solutions, Inc.
|
|
24
|
+
.. Date: May 07, 2022
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import matplotlib, sys, os
|
|
29
|
+
hasDisplay = True
|
|
30
|
+
if 'linux' in sys.platform and "DISPLAY" not in os.environ:
|
|
31
|
+
hasDisplay = False
|
|
32
|
+
matplotlib.use('Agg')
|
|
33
|
+
elif 'win32' in sys.platform:
|
|
34
|
+
matplotlib.use('TkAgg') # make sure that the backend is Tkinker
|
|
35
|
+
|
|
36
|
+
import matplotlib.pyplot as plt
|
|
37
|
+
import matplotlib.patches as patches
|
|
38
|
+
import multiprocessing as mp
|
|
39
|
+
|
|
40
|
+
from pyemaps import BlochListError
|
|
41
|
+
from pyemaps import DP
|
|
42
|
+
|
|
43
|
+
from .fileutils import *
|
|
44
|
+
|
|
45
|
+
import time
|
|
46
|
+
|
|
47
|
+
from . import XMAX, YMAX #(75,75)
|
|
48
|
+
|
|
49
|
+
DISPLAY_MULTIPLE_SIZE = 500
|
|
50
|
+
DISPLAY_ONE_SIZE = 900
|
|
51
|
+
PLOT_MULTIPLIER = 6
|
|
52
|
+
|
|
53
|
+
clrs = ["#2973A5", "cyan", "limegreen", "yellow", "red"]
|
|
54
|
+
gclrs=plt.get_cmap('gray')
|
|
55
|
+
|
|
56
|
+
TY_DIF = 1
|
|
57
|
+
TY_BLOCH = 2
|
|
58
|
+
TY_STEREO = 3
|
|
59
|
+
|
|
60
|
+
feat_lookup={'dif': TY_DIF,
|
|
61
|
+
'bloch': TY_BLOCH,
|
|
62
|
+
'stereo':TY_STEREO}
|
|
63
|
+
|
|
64
|
+
def _get_feature(ty):
|
|
65
|
+
if ty < TY_DIF or ty > TY_STEREO:
|
|
66
|
+
raise ValueError("Feature lookup failed")
|
|
67
|
+
|
|
68
|
+
return list(feat_lookup.keys())[list(feat_lookup.values()).index(ty)]
|
|
69
|
+
|
|
70
|
+
def _find_dpi():
|
|
71
|
+
matplotlib.use('TkAgg')
|
|
72
|
+
dpi = 96 #default
|
|
73
|
+
try:
|
|
74
|
+
import tkinter as tk
|
|
75
|
+
except ImportError:
|
|
76
|
+
return dpi
|
|
77
|
+
|
|
78
|
+
root = tk.Tk()
|
|
79
|
+
root.withdraw()
|
|
80
|
+
try:
|
|
81
|
+
import ctypes
|
|
82
|
+
except ImportError as e:
|
|
83
|
+
print(f'failed to find display resolution')
|
|
84
|
+
print(f'Use default dpi of 96 for display')
|
|
85
|
+
|
|
86
|
+
return dpi
|
|
87
|
+
|
|
88
|
+
width_px = root.winfo_screenwidth()
|
|
89
|
+
user32 = ctypes.windll.user32
|
|
90
|
+
user32.SetProcessDPIAware()
|
|
91
|
+
w = user32.GetSystemMetrics(0)
|
|
92
|
+
|
|
93
|
+
dpi = w*96/width_px
|
|
94
|
+
return dpi
|
|
95
|
+
|
|
96
|
+
def _isLinux():
|
|
97
|
+
import sys
|
|
98
|
+
return ('linux' in sys.platform)
|
|
99
|
+
|
|
100
|
+
def _isWin():
|
|
101
|
+
import sys
|
|
102
|
+
return (sys.platform == 'win32')
|
|
103
|
+
|
|
104
|
+
# seconds a window with bClose=True stays up after its last image
|
|
105
|
+
CLOSE_DELAY = 3
|
|
106
|
+
|
|
107
|
+
class DifPlotter:
|
|
108
|
+
# '''
|
|
109
|
+
# diffraction data plotter that receives data from the pipe and
|
|
110
|
+
# plot them with pyplot
|
|
111
|
+
# '''
|
|
112
|
+
def __init__(self):
|
|
113
|
+
self.name = 'Silicon'
|
|
114
|
+
self.save = 0
|
|
115
|
+
self.difData = None
|
|
116
|
+
self.emc = None
|
|
117
|
+
self.cShow = True
|
|
118
|
+
self.bSave = False
|
|
119
|
+
self.layout = 'individual'
|
|
120
|
+
|
|
121
|
+
def savePlot(self):
|
|
122
|
+
if self.save:
|
|
123
|
+
save_type = _get_feature(self.type)
|
|
124
|
+
save_to = compose_ofn(None, self.name, ty=save_type)
|
|
125
|
+
plt.savefig(save_to + '.png')
|
|
126
|
+
print(f'image saved to: {save_to}.png')
|
|
127
|
+
|
|
128
|
+
def terminate(self):
|
|
129
|
+
if self.bClose:
|
|
130
|
+
# keep the last image up for CLOSE_DELAY seconds, then close the window
|
|
131
|
+
plt.pause(CLOSE_DELAY)
|
|
132
|
+
plt.close(self.fig) #just close the current figure
|
|
133
|
+
# Otherwise:
|
|
134
|
+
# users must close this window after done
|
|
135
|
+
|
|
136
|
+
def plotKDif(self):
|
|
137
|
+
idx, emc, dp, mode, kshow, ishow = self.difData
|
|
138
|
+
|
|
139
|
+
if self.layout == 'table':
|
|
140
|
+
n1, n2 = _getGridPos(idx, 3)
|
|
141
|
+
else:
|
|
142
|
+
n1, n2 = 0, 0
|
|
143
|
+
|
|
144
|
+
iax = self.axes[n1, n2]
|
|
145
|
+
iax.clear()
|
|
146
|
+
iax.set_xlim(-XMAX*PLOT_MULTIPLIER, XMAX*PLOT_MULTIPLIER)
|
|
147
|
+
iax.set_ylim(-YMAX*PLOT_MULTIPLIER, YMAX*PLOT_MULTIPLIER)
|
|
148
|
+
iax.set_axis_off()
|
|
149
|
+
iax.set_aspect('equal')
|
|
150
|
+
|
|
151
|
+
if dp.nklines > 0:
|
|
152
|
+
intlist = [k.intensity for k in dp.klines]
|
|
153
|
+
lint, hint = min(intlist), max(intlist)
|
|
154
|
+
|
|
155
|
+
for kl in dp.klines:
|
|
156
|
+
|
|
157
|
+
if not kshow:
|
|
158
|
+
opacity = 0.0
|
|
159
|
+
else:
|
|
160
|
+
opacity = kl.calOpacity(lint,hint)
|
|
161
|
+
|
|
162
|
+
kl *=PLOT_MULTIPLIER
|
|
163
|
+
xx = [kl.pt1.x, kl.pt2.x]
|
|
164
|
+
yy = [kl.pt1.y, kl.pt2.y]
|
|
165
|
+
|
|
166
|
+
iax.plot(xx, yy, 'k', alpha=opacity, linewidth=1.75)
|
|
167
|
+
|
|
168
|
+
if dp.nhlines > 0:
|
|
169
|
+
intlist = [h.intensity for h in dp.hlines]
|
|
170
|
+
lint, hint = min(intlist), max(intlist)
|
|
171
|
+
|
|
172
|
+
for hl in dp.hlines:
|
|
173
|
+
opacity = hl.calOpacity(lint, hint)
|
|
174
|
+
hl *=PLOT_MULTIPLIER
|
|
175
|
+
xx = [hl.pt1.x, hl.pt2.x]
|
|
176
|
+
yy = [hl.pt1.y, hl.pt2.y]
|
|
177
|
+
iax.plot(xx, yy, 'k', alpha=opacity, linewidth=1.75)
|
|
178
|
+
|
|
179
|
+
for d in dp.disks:
|
|
180
|
+
d *= PLOT_MULTIPLIER
|
|
181
|
+
centre = (d.c.x, d.c.y)
|
|
182
|
+
|
|
183
|
+
bFill = True if mode == 1 else False
|
|
184
|
+
dis = patches.Circle(centre,
|
|
185
|
+
d.r,
|
|
186
|
+
fill=bFill,
|
|
187
|
+
linewidth = 0.5,
|
|
188
|
+
alpha=1.0,
|
|
189
|
+
fc='blue')
|
|
190
|
+
iax.add_patch(dis)
|
|
191
|
+
|
|
192
|
+
if ishow:
|
|
193
|
+
iax.text(centre[0],centre[1],
|
|
194
|
+
str(d.idx),
|
|
195
|
+
{'color': 'red', 'fontsize': 8},
|
|
196
|
+
horizontalalignment='center',
|
|
197
|
+
verticalalignment='bottom' if mode == 1 else 'center')
|
|
198
|
+
|
|
199
|
+
if self.cShow:
|
|
200
|
+
self.plotControls(emc,iax)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def plotDDif(self):
|
|
204
|
+
|
|
205
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
206
|
+
|
|
207
|
+
idx, emc, img, color = self.difData
|
|
208
|
+
|
|
209
|
+
if self.layout == 'table':
|
|
210
|
+
n1, n2 = _getGridPos(idx, 3)
|
|
211
|
+
else:
|
|
212
|
+
n1, n2 = 0, 0
|
|
213
|
+
|
|
214
|
+
iax = self.axes[n1, n2]
|
|
215
|
+
clrMap = gclrs #default to grey
|
|
216
|
+
if color:
|
|
217
|
+
clrMap = LinearSegmentedColormap.from_list("mycmap", clrs)
|
|
218
|
+
|
|
219
|
+
iax.clear()
|
|
220
|
+
iax.set_axis_off()
|
|
221
|
+
|
|
222
|
+
# image intensity check:
|
|
223
|
+
# if intensity is below threshold,
|
|
224
|
+
# don't display the image
|
|
225
|
+
imax = img.max()
|
|
226
|
+
if imax < 1.0e-10:
|
|
227
|
+
ishape = np.shape(img)
|
|
228
|
+
img = np.zeros(ishape)
|
|
229
|
+
wmsg = "No intensity detected at: \n\n" + emc.plot_format()
|
|
230
|
+
iax.text(ishape[0]/2,ishape[1]/2,
|
|
231
|
+
wmsg,
|
|
232
|
+
{'color': 'white', 'fontsize': 10},
|
|
233
|
+
horizontalalignment='center',
|
|
234
|
+
verticalalignment='center')
|
|
235
|
+
|
|
236
|
+
iax.imshow(img,
|
|
237
|
+
# norm=colors.LogNorm(vmin=imin, vmax=imax), --- logrithmatic image normalization, not used now
|
|
238
|
+
cmap=clrMap)
|
|
239
|
+
if self.cShow:
|
|
240
|
+
self.plotControls(emc,iax)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def plotControls(self, emc, ax):
|
|
244
|
+
controls_text = emc.plot_format()
|
|
245
|
+
|
|
246
|
+
# finding control text plot or coordinates:
|
|
247
|
+
x0= x1 = 0.0
|
|
248
|
+
y0= 0.0
|
|
249
|
+
|
|
250
|
+
x0, x1 = ax.get_xlim()
|
|
251
|
+
y0, y1 = ax.get_ylim()
|
|
252
|
+
|
|
253
|
+
width = x1-x0
|
|
254
|
+
height = (y1-y0)/8
|
|
255
|
+
|
|
256
|
+
textrec = patches.Rectangle((x0, y0-height),
|
|
257
|
+
width, height,
|
|
258
|
+
linewidth=0.0,
|
|
259
|
+
fill = False)
|
|
260
|
+
ax.add_patch(textrec)
|
|
261
|
+
|
|
262
|
+
ax.text(x0 + 0.5*width, y0 - 0.5*height, controls_text,
|
|
263
|
+
{'color': 'grey', 'fontsize': 6},
|
|
264
|
+
ha='center',
|
|
265
|
+
va='center')
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def plotStereo(self):
|
|
269
|
+
idx, emc, sdata, ishow, zl= self.difData
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
if self.layout == 'table':
|
|
273
|
+
n1, n2 = _getGridPos(idx, 3)
|
|
274
|
+
else:
|
|
275
|
+
n1, n2 = 0, 0
|
|
276
|
+
|
|
277
|
+
iax = self.axes[n1, n2]
|
|
278
|
+
iax.clear()
|
|
279
|
+
|
|
280
|
+
iax.set_axis_off()
|
|
281
|
+
|
|
282
|
+
# adding a unit disk
|
|
283
|
+
unitdis = patches.Circle((0.0, 0.0),
|
|
284
|
+
1.0,
|
|
285
|
+
fill=False,
|
|
286
|
+
linewidth = 0.5,
|
|
287
|
+
alpha=1.0,
|
|
288
|
+
color='blue')
|
|
289
|
+
iax.add_patch(unitdis)
|
|
290
|
+
|
|
291
|
+
for s in sdata:
|
|
292
|
+
c, r, idx = s['c'], s['r'], s['idx']
|
|
293
|
+
radius =float(r)
|
|
294
|
+
index = (int(idx[0]), int(idx[1]), int(idx[2]))
|
|
295
|
+
centre = (float(c[0]), float(c[1]))
|
|
296
|
+
dis = patches.Circle(centre,
|
|
297
|
+
radius,
|
|
298
|
+
fill=True,
|
|
299
|
+
linewidth = 0.5,
|
|
300
|
+
alpha=1.0,
|
|
301
|
+
fc='blue')
|
|
302
|
+
iax.add_patch(dis)
|
|
303
|
+
|
|
304
|
+
if ishow:
|
|
305
|
+
if abs(index[0]) <= zl and \
|
|
306
|
+
abs(index[1]) <= zl and \
|
|
307
|
+
abs(index[2]) <= zl:
|
|
308
|
+
|
|
309
|
+
iax.text(centre[0],centre[1],
|
|
310
|
+
str(index),
|
|
311
|
+
{'color': 'red', 'fontsize': 8},
|
|
312
|
+
horizontalalignment='center',
|
|
313
|
+
verticalalignment='bottom')
|
|
314
|
+
|
|
315
|
+
# set the limits
|
|
316
|
+
iax.set_xlim([-1.0, 1.0])
|
|
317
|
+
iax.set_ylim([-1.0, 1.0])
|
|
318
|
+
|
|
319
|
+
if self.cShow:
|
|
320
|
+
self.plotControls(emc,iax)
|
|
321
|
+
|
|
322
|
+
def call_back(self):
|
|
323
|
+
|
|
324
|
+
while self.pipe.poll():
|
|
325
|
+
command = self.pipe.recv()
|
|
326
|
+
|
|
327
|
+
if command is None:
|
|
328
|
+
self.terminate()
|
|
329
|
+
return False
|
|
330
|
+
elif command == ():
|
|
331
|
+
self.savePlot()
|
|
332
|
+
else:
|
|
333
|
+
self.difData = command
|
|
334
|
+
|
|
335
|
+
if self.type == 1: #diffraction plot type
|
|
336
|
+
self.plotKDif()
|
|
337
|
+
|
|
338
|
+
elif self.type == 2: #bloch type plot
|
|
339
|
+
self.plotDDif()
|
|
340
|
+
|
|
341
|
+
elif self.type == 3: #stereo type plot
|
|
342
|
+
self.plotStereo()
|
|
343
|
+
|
|
344
|
+
else:
|
|
345
|
+
raise ValueError("No data to plot")
|
|
346
|
+
|
|
347
|
+
self.fig.canvas.draw_idle()
|
|
348
|
+
|
|
349
|
+
plt.pause(1.0)
|
|
350
|
+
|
|
351
|
+
return True
|
|
352
|
+
|
|
353
|
+
def showImage(self):
|
|
354
|
+
if _isLinux() and not hasDisplay:
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
plt.show()
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def position_fig(self, x, y):
|
|
361
|
+
backend = matplotlib.get_backend()
|
|
362
|
+
if backend == 'TkAgg':
|
|
363
|
+
self.fig.canvas.manager.window.wm_geometry("+%d+%d" % (x, y))
|
|
364
|
+
|
|
365
|
+
elif backend == 'WXAgg':
|
|
366
|
+
self.fig.canvas.manager.window.SetPosition((x, y))
|
|
367
|
+
else:
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def __call__(self, pipe, type, name, bSave, n1, n2, cShow, bClose, layout):
|
|
372
|
+
import sys
|
|
373
|
+
|
|
374
|
+
self.pipe = pipe
|
|
375
|
+
self.name = name
|
|
376
|
+
self.save = bSave
|
|
377
|
+
self.layout = layout
|
|
378
|
+
self.cShow = cShow
|
|
379
|
+
self.bClose = bClose
|
|
380
|
+
|
|
381
|
+
if sys.platform == 'win32':
|
|
382
|
+
curr_dpi = _find_dpi()
|
|
383
|
+
else:
|
|
384
|
+
curr_dpi = 96
|
|
385
|
+
|
|
386
|
+
if layout == 'table':
|
|
387
|
+
display_size = DISPLAY_MULTIPLE_SIZE * n2
|
|
388
|
+
else:
|
|
389
|
+
display_size = DISPLAY_ONE_SIZE
|
|
390
|
+
|
|
391
|
+
self.fig, self.axes = plt.subplots(nrows=n1,
|
|
392
|
+
ncols=n2,
|
|
393
|
+
figsize=(display_size/curr_dpi,
|
|
394
|
+
display_size/curr_dpi),
|
|
395
|
+
squeeze=False) #setting image size in pixels
|
|
396
|
+
for x in self.axes.ravel():
|
|
397
|
+
x.axis("off")
|
|
398
|
+
x.set_aspect('equal')
|
|
399
|
+
|
|
400
|
+
if hasDisplay:
|
|
401
|
+
self.position_fig(20, 20)
|
|
402
|
+
|
|
403
|
+
if type == 1:
|
|
404
|
+
pyemaps_title = 'PYEMAPS - Kinematic Diffraction'
|
|
405
|
+
|
|
406
|
+
elif type == 2:
|
|
407
|
+
pyemaps_title = 'PYEMAPS - Dynamic Diffraction'
|
|
408
|
+
|
|
409
|
+
elif type == 3:
|
|
410
|
+
pyemaps_title = 'PYEMAPS -Stereodiagram'
|
|
411
|
+
else:
|
|
412
|
+
raise ValueError("Unsupported data type")
|
|
413
|
+
|
|
414
|
+
self.type = type
|
|
415
|
+
if hasDisplay:
|
|
416
|
+
# canvas.set_window_title was removed in matplotlib 3.6
|
|
417
|
+
if self.fig.canvas.manager is not None:
|
|
418
|
+
self.fig.canvas.manager.set_window_title(pyemaps_title)
|
|
419
|
+
|
|
420
|
+
self.fig.suptitle(self.name, va='top', fontsize=24)
|
|
421
|
+
|
|
422
|
+
timer = self.fig.canvas.new_timer(interval=1500)
|
|
423
|
+
timer.add_callback(self.call_back)
|
|
424
|
+
timer.start()
|
|
425
|
+
|
|
426
|
+
self.showImage()
|
|
427
|
+
|
|
428
|
+
class NBPlot:
|
|
429
|
+
# '''
|
|
430
|
+
# Creating a non-bloch plot object with a pipe object sending diffraction data
|
|
431
|
+
# to difPlotter
|
|
432
|
+
# '''
|
|
433
|
+
def __init__(self, type = TY_DIF, n = 1, name='', bSave=False, cShow = True, bClose = False, layout='individual'):
|
|
434
|
+
|
|
435
|
+
if layout == 'table':
|
|
436
|
+
n1, n2 = _getGridDims(n, 3)
|
|
437
|
+
else:
|
|
438
|
+
n1, n2 = 1, 1
|
|
439
|
+
|
|
440
|
+
self.plot_pipe, plotter_pipe = mp.Pipe()
|
|
441
|
+
self.plotter = DifPlotter()
|
|
442
|
+
self.plot_process = mp.Process(
|
|
443
|
+
target=self.plotter,
|
|
444
|
+
args=(plotter_pipe,
|
|
445
|
+
type,
|
|
446
|
+
name,
|
|
447
|
+
bSave,
|
|
448
|
+
n1,
|
|
449
|
+
n2,
|
|
450
|
+
cShow,
|
|
451
|
+
bClose,
|
|
452
|
+
layout),
|
|
453
|
+
daemon=False)
|
|
454
|
+
|
|
455
|
+
self.plot_process.start()
|
|
456
|
+
|
|
457
|
+
def plot(self, data = (), finished=False):
|
|
458
|
+
send = self.plot_pipe.send
|
|
459
|
+
if finished:
|
|
460
|
+
send(None)
|
|
461
|
+
else:
|
|
462
|
+
send(data)
|
|
463
|
+
|
|
464
|
+
def showDif(dpl=None,
|
|
465
|
+
cShow=True,
|
|
466
|
+
kShow=True,
|
|
467
|
+
iShow=True,
|
|
468
|
+
layout='individual',
|
|
469
|
+
bSave = False,
|
|
470
|
+
bClose = False):
|
|
471
|
+
|
|
472
|
+
"""
|
|
473
|
+
Render kinematic diffraction pattern generated by pyemaps.
|
|
474
|
+
|
|
475
|
+
:param dpl: Kinematic difraction pattern object list `DPList <pyemaps.kdiffs.html#pyemaps.kdiffs.DPList>`_
|
|
476
|
+
:type dpl: DPList
|
|
477
|
+
|
|
478
|
+
:param cShow: Plot control annotation. `True` (default): plot the control parameters on lower left corner; `False` do not plot.
|
|
479
|
+
:type cShow: bool, optional
|
|
480
|
+
|
|
481
|
+
:param kShow: Whether to display Kikuchi lines.
|
|
482
|
+
:type kShow: bool
|
|
483
|
+
|
|
484
|
+
:param iShow: Whether to display Miller indexes.
|
|
485
|
+
:type iShow: bool
|
|
486
|
+
|
|
487
|
+
:param layout: layout format. individual (default): plotting one by one, table: plotting in a table of 3 columns
|
|
488
|
+
:type layout: str, optional
|
|
489
|
+
|
|
490
|
+
:param bSave: Whether to save the diplay into a .png image file.
|
|
491
|
+
:type bSave: bool
|
|
492
|
+
|
|
493
|
+
:param bClose: Whether to close the plotting window by itself, 3 seconds after the last image. Default: False - users must close it.
|
|
494
|
+
:type bClose: bool, optional
|
|
495
|
+
|
|
496
|
+
"""
|
|
497
|
+
from pyemaps import DPList
|
|
498
|
+
|
|
499
|
+
if not dpl or not isinstance(dpl, DPList):
|
|
500
|
+
print('Nothing to plot: must pass a valid Diffraction object')
|
|
501
|
+
|
|
502
|
+
mode = dpl.mode
|
|
503
|
+
name = dpl.name
|
|
504
|
+
if _isLinux() and not hasDisplay: bSave = True
|
|
505
|
+
#always save to file on linux as it may just the commandline
|
|
506
|
+
n = len(dpl.diffList)
|
|
507
|
+
|
|
508
|
+
pl = NBPlot(TY_DIF, n, name, bSave, cShow, bClose, layout)
|
|
509
|
+
for i, cdp in enumerate(dpl.diffList):
|
|
510
|
+
c, dp = cdp
|
|
511
|
+
d = (i, c, dp, mode, kShow, iShow)
|
|
512
|
+
pl.plot(data = d)
|
|
513
|
+
time.sleep(1.0)
|
|
514
|
+
pl.plot()
|
|
515
|
+
time.sleep(1.0)
|
|
516
|
+
|
|
517
|
+
pl.plot(finished=True)
|
|
518
|
+
|
|
519
|
+
def showBloch(bimgs,
|
|
520
|
+
cShow=True,
|
|
521
|
+
bColor = False,
|
|
522
|
+
layout='individual',
|
|
523
|
+
bSave = False,
|
|
524
|
+
bClose = False):
|
|
525
|
+
"""
|
|
526
|
+
Render dynamic diffraction pattern generated by pyemaps.
|
|
527
|
+
|
|
528
|
+
:param bimgs: Optional. Dynamic difraction pattern object list `BImgList <pyemaps.ddiffs.html#pyemaps.ddiffs.BlochImgs>`_
|
|
529
|
+
:type bimgs: BlochImgs
|
|
530
|
+
|
|
531
|
+
:param cShow: Plot control annotation. `True` (default): plot the control parameters on lower left corner; `False` do not plot.
|
|
532
|
+
:type cShow: bool, optional
|
|
533
|
+
|
|
534
|
+
:param bColor: Optional. Whether to display the image in predefined color map.
|
|
535
|
+
:type bColor: bool
|
|
536
|
+
|
|
537
|
+
:param layout: layout format. individual (default): plotting one by one, table: plotting in a table of 3 columns
|
|
538
|
+
:type layout: str, optional
|
|
539
|
+
|
|
540
|
+
:param bSave: Optional. Whether to save the image into a .im3 image file.
|
|
541
|
+
:type bSave: bool
|
|
542
|
+
|
|
543
|
+
:param bClose: Whether to close the plotting window by itself, 3 seconds after the last image. Default: False - users must close it.
|
|
544
|
+
:type bClose: bool, optional
|
|
545
|
+
|
|
546
|
+
"""
|
|
547
|
+
from pyemaps import BImgList
|
|
548
|
+
|
|
549
|
+
if not bimgs or not isinstance(bimgs, BImgList):
|
|
550
|
+
raise BlochListError('showBloch must have BImgList object as its first input')
|
|
551
|
+
|
|
552
|
+
name = bimgs.name
|
|
553
|
+
|
|
554
|
+
if _isLinux() and not hasDisplay: bSave = True
|
|
555
|
+
#always save to file on linux as it may just the commandline
|
|
556
|
+
|
|
557
|
+
n = len(bimgs.blochList)
|
|
558
|
+
pl = NBPlot(TY_BLOCH, n, name, bSave, cShow, bClose, layout)
|
|
559
|
+
|
|
560
|
+
for i, cimg in enumerate(bimgs.blochList):
|
|
561
|
+
c, img = cimg
|
|
562
|
+
|
|
563
|
+
d = (i, c, img, bColor)
|
|
564
|
+
pl.plot(data = d)
|
|
565
|
+
time.sleep(1.0)
|
|
566
|
+
|
|
567
|
+
pl.plot()
|
|
568
|
+
time.sleep(1.0)
|
|
569
|
+
|
|
570
|
+
pl.plot(finished=True)
|
|
571
|
+
|
|
572
|
+
def showStereo(slist, name,
|
|
573
|
+
cShow= True, #em_controls annotated or not
|
|
574
|
+
iShow = False, #display miller indexes or not
|
|
575
|
+
zLimit = 2, # miller indexes limit (absolute value)
|
|
576
|
+
layout='individual', # individual or table display format
|
|
577
|
+
bSave=False, # save the rendering to a file or not
|
|
578
|
+
bClose = False): # close the plotting window when done
|
|
579
|
+
|
|
580
|
+
"""
|
|
581
|
+
Render stereodiagram generated by pyemaps.
|
|
582
|
+
|
|
583
|
+
:param slist: Stereodiagram output from `generateStereo <pyemaps.crystals.html#pyemaps.crystals.Crystal.generateStereo>`_.
|
|
584
|
+
:type slist: list, required
|
|
585
|
+
|
|
586
|
+
:param cShow: Plot control annotation. `True` (default): plot the control parameters on lower left corner; `False` do not plot.
|
|
587
|
+
:type cShow: bool, optional
|
|
588
|
+
|
|
589
|
+
:param iShow: Whether to display Miller indexes or not.
|
|
590
|
+
:type iShow: bool, optional
|
|
591
|
+
|
|
592
|
+
:param zLimit: Miller indexes cutoff number.
|
|
593
|
+
:type zLimit: int, optional
|
|
594
|
+
|
|
595
|
+
:param layout: layout format. individual (default): plotting one by one in sequence, table: plotting in a table of 3 columns
|
|
596
|
+
:type layout: str, optional
|
|
597
|
+
|
|
598
|
+
:param bSave: Whether to save the image into a .png image file.
|
|
599
|
+
:type bSave: bool, optional
|
|
600
|
+
|
|
601
|
+
:param bClose: Whether to close the plotting window by itself, 3 seconds after the last image. Default: False - users must close it.
|
|
602
|
+
:type bClose: bool, optional
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
.. note::
|
|
607
|
+
|
|
608
|
+
When zlimit is set at 2, it is applied to all elements of each
|
|
609
|
+
Miller index. For example, (3, 1, 0) will not be rendered.
|
|
610
|
+
|
|
611
|
+
"""
|
|
612
|
+
if _isLinux() and not hasDisplay: bSave = True
|
|
613
|
+
#always save to file on linux as it may just the commandline
|
|
614
|
+
|
|
615
|
+
n = len(slist)
|
|
616
|
+
|
|
617
|
+
pl = NBPlot(TY_STEREO, n, name, bSave, cShow, bClose, layout)
|
|
618
|
+
for i, ss in enumerate(slist):
|
|
619
|
+
c, s = ss
|
|
620
|
+
d = (i, c, s, iShow, zLimit)
|
|
621
|
+
pl.plot(data = d)
|
|
622
|
+
time.sleep(1.0)
|
|
623
|
+
pl.plot()
|
|
624
|
+
time.sleep(1.0)
|
|
625
|
+
pl.plot(finished=True)
|
|
626
|
+
|
|
627
|
+
def _getGridDims(n, nCol = 3):
|
|
628
|
+
|
|
629
|
+
nr = n % nCol
|
|
630
|
+
|
|
631
|
+
nrows = n // nCol
|
|
632
|
+
|
|
633
|
+
if nr != 0:
|
|
634
|
+
nrows += 1
|
|
635
|
+
|
|
636
|
+
ncols = nr
|
|
637
|
+
if n >= 3:
|
|
638
|
+
ncols = 3
|
|
639
|
+
|
|
640
|
+
return nrows, ncols
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _getGridPos(i, nCols = 3):
|
|
644
|
+
|
|
645
|
+
ncols = i % nCols
|
|
646
|
+
|
|
647
|
+
nrows = i // nCols
|
|
648
|
+
|
|
649
|
+
return nrows, ncols
|
|
650
|
+
|
|
651
|
+
# image display for stem4d module
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def normalizeImage(img):
|
|
655
|
+
if img is None:
|
|
656
|
+
print("Probe error: image file empty")
|
|
657
|
+
return False
|
|
658
|
+
y0 = np.mean(img, dtype=np.float32)
|
|
659
|
+
medImg = np.std(img, dtype=np.float32)
|
|
660
|
+
|
|
661
|
+
Ymin1 = y0 - 3.0 * medImg
|
|
662
|
+
Ymin = np.amin(img)
|
|
663
|
+
if Ymin < Ymin1: Ymin = Ymin1
|
|
664
|
+
|
|
665
|
+
Ymax1 = y0 + 3.0 * medImg
|
|
666
|
+
Ymax = np.amax(img)
|
|
667
|
+
if Ymax > Ymax1: Ymax = Ymax1
|
|
668
|
+
|
|
669
|
+
if Ymin >= Ymax:
|
|
670
|
+
print("not finding right min-max: ")
|
|
671
|
+
return False
|
|
672
|
+
ran = Ymax-Ymin
|
|
673
|
+
|
|
674
|
+
img[img < Ymin] = Ymin
|
|
675
|
+
img[img > Ymax] = Ymax
|
|
676
|
+
img = (img - Ymin)/ran
|
|
677
|
+
return True
|
|
678
|
+
|
|
679
|
+
def displayXImage(img,
|
|
680
|
+
fsize=(0,0),
|
|
681
|
+
bColor= False,
|
|
682
|
+
isMask = False,
|
|
683
|
+
bIndexed=False,
|
|
684
|
+
iShow = False,
|
|
685
|
+
ds = None,
|
|
686
|
+
suptitle = ''):
|
|
687
|
+
"""
|
|
688
|
+
Internal stem4d helper function displaying image.
|
|
689
|
+
|
|
690
|
+
:return:
|
|
691
|
+
:rtype: None
|
|
692
|
+
|
|
693
|
+
"""
|
|
694
|
+
|
|
695
|
+
import matplotlib.pyplot as plt
|
|
696
|
+
import matplotlib.patches as patches
|
|
697
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
698
|
+
clrs = ["#2973A5", "cyan", "limegreen", "yellow", "red"]
|
|
699
|
+
qedDPI = 600
|
|
700
|
+
gclrs=plt.get_cmap('gray')
|
|
701
|
+
STEM4D_TITLE = 'pyEMAPS 4DSTEM'
|
|
702
|
+
|
|
703
|
+
nr,nc = fsize
|
|
704
|
+
|
|
705
|
+
if isMask:
|
|
706
|
+
bsize = (0.5, 0.5*int(nr/nc))
|
|
707
|
+
else:
|
|
708
|
+
bsize = (1.0, 1.0*int(nr/nc))
|
|
709
|
+
|
|
710
|
+
fig, ax = plt.subplots(figsize=bsize, dpi=qedDPI)
|
|
711
|
+
|
|
712
|
+
# canvas.set_window_title was removed in matplotlib 3.6
|
|
713
|
+
if fig.canvas.manager is not None:
|
|
714
|
+
fig.canvas.manager.set_window_title(STEM4D_TITLE)
|
|
715
|
+
# ax.set_xlabel(suptitle, fontsize=1.5)
|
|
716
|
+
fig.suptitle(suptitle, va='top', fontsize=1.5)
|
|
717
|
+
|
|
718
|
+
clrMap = gclrs #default to grey
|
|
719
|
+
if bColor:
|
|
720
|
+
clrMap = LinearSegmentedColormap.from_list("mycmap", clrs)
|
|
721
|
+
plt.imshow(img, cmap = clrMap)
|
|
722
|
+
|
|
723
|
+
ax.set_axis_off()
|
|
724
|
+
|
|
725
|
+
count = 0
|
|
726
|
+
if ds is not None:
|
|
727
|
+
for d in ds:
|
|
728
|
+
cx, cy, rad, indx, itype = d
|
|
729
|
+
|
|
730
|
+
if bIndexed and itype != 2 and count != 0:
|
|
731
|
+
continue
|
|
732
|
+
|
|
733
|
+
centre = (cx, cy)
|
|
734
|
+
|
|
735
|
+
circle = plt.Circle(centre, rad,
|
|
736
|
+
fill=False,
|
|
737
|
+
linewidth = 0.1,
|
|
738
|
+
# alpha=0.6,
|
|
739
|
+
color='white')
|
|
740
|
+
ax.add_patch(circle)
|
|
741
|
+
count += 1
|
|
742
|
+
|
|
743
|
+
if iShow:
|
|
744
|
+
ax.text(centre[0],centre[1],
|
|
745
|
+
str(indx),
|
|
746
|
+
{'color': 'red', 'fontsize': 1.4},
|
|
747
|
+
horizontalalignment='center',
|
|
748
|
+
verticalalignment='bottom')
|
|
749
|
+
|
|
750
|
+
plt.show()
|
|
751
|
+
|
|
752
|
+
def plot2Powder(pw1, pw2, bClose=True):
|
|
753
|
+
"""
|
|
754
|
+
plot two powder diffractions in one matplotlib plot using from the
|
|
755
|
+
powder diffraction data generated from `generatePowder <pyemaps.crystals.html#pyemaps.crystals.Crystal.generatePowder>`_
|
|
756
|
+
|
|
757
|
+
:param pw1: powder diffraction data of an array of 2 x 1000
|
|
758
|
+
:type pw1: numpy array, required
|
|
759
|
+
|
|
760
|
+
:param pw1: powder diffraction data of an array of 2 x 1000
|
|
761
|
+
:type pw1: numpy array, required
|
|
762
|
+
|
|
763
|
+
:param bClose: boolean value indicating closure of the diffraction powder plot after about 3 seconds desplay.
|
|
764
|
+
:type bClose: Boolean, optional
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
"""
|
|
768
|
+
|
|
769
|
+
fig, (ax1, ax2) = plt.subplots(nrows = 2)
|
|
770
|
+
|
|
771
|
+
title = 'pyEMAPS - Powder Diffraction'
|
|
772
|
+
# canvas.set_window_title was removed in matplotlib 3.6
|
|
773
|
+
if fig.canvas.manager is not None:
|
|
774
|
+
fig.canvas.manager.set_window_title(title)
|
|
775
|
+
|
|
776
|
+
ax1.plot(pw1[0], pw1[1], 'r')
|
|
777
|
+
ax1.set_title('Silicon')
|
|
778
|
+
ax2.plot(pw2[0], pw2[1], 'b')
|
|
779
|
+
ax2.set_title('Diamond')
|
|
780
|
+
|
|
781
|
+
ax1.set_ylabel('Intensity')
|
|
782
|
+
ax2.set_ylabel('Intensity /w Absorption')
|
|
783
|
+
ax2.set_xlabel('Scattering Angle 2\u03F4 (Rad)')
|
|
784
|
+
|
|
785
|
+
fig.suptitle("Electron Powder Diffraction", fontsize=14, fontweight='bold')
|
|
786
|
+
plt.subplots_adjust(hspace = 0.4)
|
|
787
|
+
|
|
788
|
+
plt.show(block=False)
|
|
789
|
+
if bClose:
|
|
790
|
+
plt.pause(3)
|
|
791
|
+
plt.close()
|