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.
Files changed (112) hide show
  1. pyemaps/COPYING +798 -0
  2. pyemaps/CifFile/CifFile_module.py +3117 -0
  3. pyemaps/CifFile/StarFile.py +2496 -0
  4. pyemaps/CifFile/TypeContentsParser.py +82 -0
  5. pyemaps/CifFile/YappsStarParser_1_0.py +289 -0
  6. pyemaps/CifFile/YappsStarParser_1_1.py +288 -0
  7. pyemaps/CifFile/YappsStarParser_2_0.py +369 -0
  8. pyemaps/CifFile/YappsStarParser_STAR2.py +382 -0
  9. pyemaps/CifFile/__init__.py +7 -0
  10. pyemaps/CifFile/drel/__init__.py +1 -0
  11. pyemaps/CifFile/drel/drel_ast_yacc.py +582 -0
  12. pyemaps/CifFile/drel/drel_lex.py +228 -0
  13. pyemaps/CifFile/drel/drel_runtime.py +126 -0
  14. pyemaps/CifFile/drel/parsetab.py +185 -0
  15. pyemaps/CifFile/drel/py_from_ast.py +628 -0
  16. pyemaps/CifFile/parsetab.py +185 -0
  17. pyemaps/CifFile/yapps3_compiled_rt.py +390 -0
  18. pyemaps/README.md +201 -0
  19. pyemaps/__config__.py +91 -0
  20. pyemaps/__init__.py +233 -0
  21. pyemaps/__main__.py +219 -0
  22. pyemaps/__version__.py +1 -0
  23. pyemaps/cdata/Aluminium.xtl +4 -0
  24. pyemaps/cdata/AluminiumOxide.xtl +5 -0
  25. pyemaps/cdata/Aluminium_FCC.xtl +4 -0
  26. pyemaps/cdata/BariumTitanate_180k.xtl +6 -0
  27. pyemaps/cdata/BariumTitanate_270k.xtl +7 -0
  28. pyemaps/cdata/BariumTitanate_Tetra.xtl +7 -0
  29. pyemaps/cdata/BiMnO3.xtl +27 -0
  30. pyemaps/cdata/Boron_Tetra.xtl +8 -0
  31. pyemaps/cdata/CadmiumSelenide_Hex.xtl +5 -0
  32. pyemaps/cdata/CadmiumSulfide_Cubic.xtl +5 -0
  33. pyemaps/cdata/CadmiumSulfide_Hex.xtl +5 -0
  34. pyemaps/cdata/Chromium_BCC.xtl +4 -0
  35. pyemaps/cdata/CoSb3_Skutterudite.xtl +5 -0
  36. pyemaps/cdata/CopperOxide.xtl +5 -0
  37. pyemaps/cdata/Copper_FCC.xtl +4 -0
  38. pyemaps/cdata/Cu2O_Cuprite.xtl +5 -0
  39. pyemaps/cdata/Diamond.xtl +4 -0
  40. pyemaps/cdata/ErbiumPyrogermanate.xtl +9 -0
  41. pyemaps/cdata/FePd_Tetra.xtl +5 -0
  42. pyemaps/cdata/FeS2_Pyrite.xtl +15 -0
  43. pyemaps/cdata/GalliumAntimonide.xtl +5 -0
  44. pyemaps/cdata/GalliumArsenide.xtl +5 -0
  45. pyemaps/cdata/GalliumNitride.xtl +5 -0
  46. pyemaps/cdata/Germanium.xtl +4 -0
  47. pyemaps/cdata/Gold_FCC.xtl +4 -0
  48. pyemaps/cdata/IndiumArsenide.xtl +5 -0
  49. pyemaps/cdata/LaMnO3.xtl +7 -0
  50. pyemaps/cdata/LeadZirconateTitanate.xtl +8 -0
  51. pyemaps/cdata/Li2MnO3.xtl +13 -0
  52. pyemaps/cdata/NaFeO2.xtl +6 -0
  53. pyemaps/cdata/Nb3Sn.xtl +5 -0
  54. pyemaps/cdata/Pentacene.xtl +39 -0
  55. pyemaps/cdata/Silicon.xtl +4 -0
  56. pyemaps/cdata/StrontiumTitanate.xtl +6 -0
  57. pyemaps/cdata/TelluriumDioxide.xtl +5 -0
  58. pyemaps/cdata/TinDioxide_RT.xtl +5 -0
  59. pyemaps/cdata/TitaniumDioxide_Anatase.xtl +5 -0
  60. pyemaps/cdata/TitaniumDioxide_Rutile.xtl +5 -0
  61. pyemaps/cdata/TungstenDiselenide.xtl +5 -0
  62. pyemaps/cdata/VanadiumDioxide_RT.xtl +6 -0
  63. pyemaps/cdata/ZincOxide.xtl +5 -0
  64. pyemaps/cdata/Zinc_HCP.xtl +4 -0
  65. pyemaps/cdata/ZirconiumNitride.xtl +5 -0
  66. pyemaps/cdata/limno2.xtl +6 -0
  67. pyemaps/crystals.py +1415 -0
  68. pyemaps/ddiffs.py +147 -0
  69. pyemaps/diffract/__init__.py +70 -0
  70. pyemaps/diffract/bloch_dec.py +779 -0
  71. pyemaps/diffract/csf_dec.py +242 -0
  72. pyemaps/diffract/dif_dec.py +519 -0
  73. pyemaps/diffract/dpgen_dec.py +175 -0
  74. pyemaps/diffract/mxtal_dec.py +201 -0
  75. pyemaps/diffract/powder_dec.py +137 -0
  76. pyemaps/diffract/stereo_dec.py +111 -0
  77. pyemaps/display.py +791 -0
  78. pyemaps/emcontrols.py +1017 -0
  79. pyemaps/errors.py +266 -0
  80. pyemaps/fileutils.py +1001 -0
  81. pyemaps/kdiffs.py +1221 -0
  82. pyemaps/license.txt +42 -0
  83. pyemaps/samples/adf.py +137 -0
  84. pyemaps/samples/al.img +0 -0
  85. pyemaps/samples/al_dpgen.py +57 -0
  86. pyemaps/samples/al_ediom.py +94 -0
  87. pyemaps/samples/convert_image.py +88 -0
  88. pyemaps/samples/dm_diff.py +225 -0
  89. pyemaps/samples/mask.im3 +0 -0
  90. pyemaps/samples/powder.py +51 -0
  91. pyemaps/samples/raw.img +0 -0
  92. pyemaps/samples/si_bloch.py +120 -0
  93. pyemaps/samples/si_constructor.py +51 -0
  94. pyemaps/samples/si_csf.py +59 -0
  95. pyemaps/samples/si_dif.py +134 -0
  96. pyemaps/samples/si_lacbed.py +59 -0
  97. pyemaps/samples/si_pyemaps.py +80 -0
  98. pyemaps/samples/si_scm.py +156 -0
  99. pyemaps/samples/si_stereo.py +116 -0
  100. pyemaps/scattering/__init__.py +26 -0
  101. pyemaps/scattering/sct_dec.py +31 -0
  102. pyemaps/spg/__init__.py +25 -0
  103. pyemaps/spg/spg_dec.py +60 -0
  104. pyemaps/stackimg.py +1031 -0
  105. pyemaps/xtal.py +222 -0
  106. pyemaps-2.0.0.dist-info/METADATA +231 -0
  107. pyemaps-2.0.0.dist-info/RECORD +112 -0
  108. pyemaps-2.0.0.dist-info/WHEEL +5 -0
  109. pyemaps-2.0.0.dist-info/entry_points.txt +2 -0
  110. pyemaps-2.0.0.dist-info/licenses/COPYING +798 -0
  111. pyemaps-2.0.0.dist-info/licenses/license.txt +42 -0
  112. 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()