pyTEMlib 0.2020.11.1__py3-none-any.whl → 0.2024.9.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.
Potentially problematic release.
This version of pyTEMlib might be problematic. Click here for more details.
- pyTEMlib/__init__.py +11 -11
- pyTEMlib/animation.py +631 -0
- pyTEMlib/atom_tools.py +240 -245
- pyTEMlib/config_dir.py +57 -33
- pyTEMlib/core_loss_widget.py +658 -0
- pyTEMlib/crystal_tools.py +1255 -0
- pyTEMlib/diffraction_plot.py +756 -0
- pyTEMlib/dynamic_scattering.py +293 -0
- pyTEMlib/eds_tools.py +609 -0
- pyTEMlib/eels_dialog.py +749 -491
- pyTEMlib/{interactive_eels.py → eels_dialog_utilities.py} +1199 -1177
- pyTEMlib/eels_tools.py +2031 -1698
- pyTEMlib/file_tools.py +1276 -560
- pyTEMlib/file_tools_qt.py +193 -0
- pyTEMlib/graph_tools.py +1166 -450
- pyTEMlib/graph_viz.py +449 -0
- pyTEMlib/image_dialog.py +158 -0
- pyTEMlib/image_dlg.py +146 -232
- pyTEMlib/image_tools.py +1399 -1028
- pyTEMlib/info_widget.py +933 -0
- pyTEMlib/interactive_image.py +1 -226
- pyTEMlib/kinematic_scattering.py +1196 -0
- pyTEMlib/low_loss_widget.py +176 -0
- pyTEMlib/microscope.py +61 -81
- pyTEMlib/peak_dialog.py +1047 -410
- pyTEMlib/peak_dlg.py +286 -242
- pyTEMlib/probe_tools.py +653 -207
- pyTEMlib/sidpy_tools.py +153 -136
- pyTEMlib/simulation_tools.py +104 -87
- pyTEMlib/version.py +6 -3
- pyTEMlib/xrpa_x_sections.py +20972 -0
- {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/LICENSE +21 -21
- pyTEMlib-0.2024.9.0.dist-info/METADATA +92 -0
- pyTEMlib-0.2024.9.0.dist-info/RECORD +37 -0
- {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/WHEEL +5 -5
- {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/entry_points.txt +0 -1
- pyTEMlib/KinsCat.py +0 -2758
- pyTEMlib/__version__.py +0 -2
- pyTEMlib/data/TEMlibrc +0 -68
- pyTEMlib/data/edges_db.csv +0 -189
- pyTEMlib/data/edges_db.pkl +0 -0
- pyTEMlib/data/fparam.txt +0 -103
- pyTEMlib/data/microscopes.csv +0 -7
- pyTEMlib/data/microscopes.xml +0 -167
- pyTEMlib/data/path.txt +0 -1
- pyTEMlib/defaults_parser.py +0 -90
- pyTEMlib/dm3_reader.py +0 -613
- pyTEMlib/edges_db.py +0 -76
- pyTEMlib/eels_dlg.py +0 -224
- pyTEMlib/hdf_utils.py +0 -483
- pyTEMlib/image_tools1.py +0 -2194
- pyTEMlib/info_dialog.py +0 -237
- pyTEMlib/info_dlg.py +0 -202
- pyTEMlib/nion_reader.py +0 -297
- pyTEMlib/nsi_reader.py +0 -170
- pyTEMlib/structure_tools.py +0 -316
- pyTEMlib/test.py +0 -2072
- pyTEMlib-0.2020.11.1.dist-info/METADATA +0 -20
- pyTEMlib-0.2020.11.1.dist-info/RECORD +0 -45
- {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/top_level.txt +0 -0
pyTEMlib/graph_viz.py
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
"""
|
|
2
|
+
##################################################################
|
|
3
|
+
# plotting functions for graph_tools
|
|
4
|
+
##################################################################
|
|
5
|
+
|
|
6
|
+
part of pyTEMlib
|
|
7
|
+
a pycrosccopy package
|
|
8
|
+
|
|
9
|
+
Author: Gerd Duscher
|
|
10
|
+
First Version: 2022-01-08
|
|
11
|
+
"""
|
|
12
|
+
import numpy as np
|
|
13
|
+
import ase
|
|
14
|
+
|
|
15
|
+
import plotly.graph_objects as go
|
|
16
|
+
import plotly.express as px
|
|
17
|
+
|
|
18
|
+
import pyTEMlib.crystal_tools
|
|
19
|
+
import pyTEMlib.graph_tools
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def plot_super_cell(super_cell, shift_x=0.):
|
|
23
|
+
""" make a super_cell to plot with extra atoms at periodic boundaries"""
|
|
24
|
+
|
|
25
|
+
if not isinstance(super_cell, ase.Atoms):
|
|
26
|
+
raise TypeError('Need an ase Atoms object')
|
|
27
|
+
|
|
28
|
+
plot_boundary = super_cell * (2, 2, 3)
|
|
29
|
+
plot_boundary.positions[:, 0] = plot_boundary.positions[:, 0] - super_cell.cell[0, 0] * shift_x
|
|
30
|
+
|
|
31
|
+
del plot_boundary[plot_boundary.positions[:, 2] > super_cell.cell[2, 2] * 1.5 + 0.1]
|
|
32
|
+
del plot_boundary[plot_boundary.positions[:, 1] > super_cell.cell[1, 1] + 0.1]
|
|
33
|
+
del plot_boundary[plot_boundary.positions[:, 0] > super_cell.cell[0, 0] + 0.1]
|
|
34
|
+
del plot_boundary[plot_boundary.positions[:, 0] < -0.1]
|
|
35
|
+
plot_boundary.cell = super_cell.cell * (1, 1, 1.5)
|
|
36
|
+
|
|
37
|
+
return plot_boundary
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def plot_polyhedron(polyhedra, indices, center=False):
|
|
41
|
+
"""
|
|
42
|
+
Information to plot polyhedra with plotly
|
|
43
|
+
|
|
44
|
+
Parameter
|
|
45
|
+
---------
|
|
46
|
+
polyhedra: dict
|
|
47
|
+
dictionary of all polyhedra
|
|
48
|
+
indices: list or integer
|
|
49
|
+
list or index of polyhedron to plot.
|
|
50
|
+
center: boolean
|
|
51
|
+
whether to center polyhedra on origin
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
data: dict
|
|
56
|
+
instructions to plot for plotly
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if isinstance(indices, int):
|
|
60
|
+
indices = [indices]
|
|
61
|
+
if len(indices) == 0:
|
|
62
|
+
print('Did not find any polyhedra')
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
center_point = np.mean(polyhedra[indices[0]]['vertices'], axis=0)
|
|
66
|
+
|
|
67
|
+
if center:
|
|
68
|
+
print(center_point)
|
|
69
|
+
center = center_point
|
|
70
|
+
else:
|
|
71
|
+
center = [0, 0, 0]
|
|
72
|
+
|
|
73
|
+
data = []
|
|
74
|
+
for index in indices:
|
|
75
|
+
polyhedron = polyhedra[index]
|
|
76
|
+
|
|
77
|
+
vertices = polyhedron['vertices'] - center
|
|
78
|
+
faces = np.array(polyhedron['triangles'])
|
|
79
|
+
x, y, z = vertices.T
|
|
80
|
+
i_i, j_j, k_k = faces.T
|
|
81
|
+
|
|
82
|
+
mesh = dict(type='mesh3d',
|
|
83
|
+
x=x,
|
|
84
|
+
y=y,
|
|
85
|
+
z=z,
|
|
86
|
+
i=i_i,
|
|
87
|
+
j=j_j,
|
|
88
|
+
k=k_k,
|
|
89
|
+
name='',
|
|
90
|
+
opacity=0.2,
|
|
91
|
+
color=px.colors.qualitative.Light24[len(vertices) % 24]
|
|
92
|
+
)
|
|
93
|
+
tri_vertices = vertices[faces]
|
|
94
|
+
x_e = []
|
|
95
|
+
y_e = []
|
|
96
|
+
z_e = []
|
|
97
|
+
for t_v in tri_vertices:
|
|
98
|
+
x_e += [t_v[k % 3][0] for k in range(4)] + [None]
|
|
99
|
+
y_e += [t_v[k % 3][1] for k in range(4)] + [None]
|
|
100
|
+
z_e += [t_v[k % 3][2] for k in range(4)] + [None]
|
|
101
|
+
|
|
102
|
+
# define the lines to be plotted
|
|
103
|
+
lines = dict(type='scatter3d',
|
|
104
|
+
x=x_e,
|
|
105
|
+
y=y_e,
|
|
106
|
+
z=z_e,
|
|
107
|
+
mode='lines',
|
|
108
|
+
name='',
|
|
109
|
+
line=dict(color='rgb(70,70,70)', width=1.5))
|
|
110
|
+
data.append(mesh)
|
|
111
|
+
data.append(lines)
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def plot_bonds(polyhedra):
|
|
116
|
+
"""
|
|
117
|
+
Information to plot bonds with plotly
|
|
118
|
+
|
|
119
|
+
Parameter
|
|
120
|
+
---------
|
|
121
|
+
polyhedra: dict
|
|
122
|
+
dictionary of all polyhedra
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
data: dict
|
|
127
|
+
instructions to plot for plotly
|
|
128
|
+
"""
|
|
129
|
+
indices = range(len(polyhedra))
|
|
130
|
+
|
|
131
|
+
data = []
|
|
132
|
+
for index in indices:
|
|
133
|
+
polyhedron = polyhedra[index]
|
|
134
|
+
|
|
135
|
+
vertices = polyhedron['vertices']
|
|
136
|
+
faces = np.array(polyhedron['triangles'])
|
|
137
|
+
x, y, z = vertices.T
|
|
138
|
+
i_i, j_j, k_k = faces.T
|
|
139
|
+
|
|
140
|
+
tri_vertices = vertices[faces]
|
|
141
|
+
x_e = []
|
|
142
|
+
y_e = []
|
|
143
|
+
z_e = []
|
|
144
|
+
for t_v in tri_vertices:
|
|
145
|
+
x_e += [t_v[k % 3][0] for k in range(4)] + [None]
|
|
146
|
+
y_e += [t_v[k % 3][1] for k in range(4)] + [None]
|
|
147
|
+
z_e += [t_v[k % 3][2] for k in range(4)] + [None]
|
|
148
|
+
|
|
149
|
+
# define the lines to be plotted
|
|
150
|
+
lines = dict(type='scatter3d',
|
|
151
|
+
x=x_e,
|
|
152
|
+
y=y_e,
|
|
153
|
+
z=z_e,
|
|
154
|
+
mode='lines',
|
|
155
|
+
name='',
|
|
156
|
+
line=dict(color='rgb(70,70,70)', width=1.5))
|
|
157
|
+
data.append(lines)
|
|
158
|
+
return data
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_boundary_polyhedra(polyhedra, boundary_x=0, boundary_width=0.5, verbose=True, z_lim=[0, 100]):
|
|
162
|
+
"""
|
|
163
|
+
get indices of polyhedra at boundary (assumed to be parallel to x-axis)
|
|
164
|
+
|
|
165
|
+
Parameter
|
|
166
|
+
---------
|
|
167
|
+
polyhedra: dict
|
|
168
|
+
dictionary of all polyhedra
|
|
169
|
+
boundary_x: float
|
|
170
|
+
position of boundary in Angstrom
|
|
171
|
+
boundary_width: float
|
|
172
|
+
width of boundary where center of polyhedra are considered in Angstrom
|
|
173
|
+
verbose: boolean
|
|
174
|
+
optional
|
|
175
|
+
z_lim: list
|
|
176
|
+
upper and lower limit of polyhedra to plot
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
boundary_polyhedra: list
|
|
181
|
+
list of polyhedra at boundary
|
|
182
|
+
"""
|
|
183
|
+
boundary_polyhedra = []
|
|
184
|
+
for key, polyhedron in polyhedra.items():
|
|
185
|
+
center = polyhedron['vertices'].mean(axis=0)
|
|
186
|
+
if abs(center[0] - boundary_x) < 0.5 and (z_lim[0] < center[2] < z_lim[1]):
|
|
187
|
+
boundary_polyhedra.append(key)
|
|
188
|
+
if verbose:
|
|
189
|
+
print(key, polyhedron['length'], center)
|
|
190
|
+
|
|
191
|
+
return boundary_polyhedra
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def plot_with_polyhedra(polyhedra, indices, atoms=None, title=''):
|
|
195
|
+
"""
|
|
196
|
+
plot atoms and polyhedra with plotly
|
|
197
|
+
|
|
198
|
+
Parameter
|
|
199
|
+
---------
|
|
200
|
+
polyhedra: dict
|
|
201
|
+
dictionary of all polyhedra
|
|
202
|
+
indices: list or integer
|
|
203
|
+
list or index of polyhedron to plot.
|
|
204
|
+
atoms: ase.Atoms
|
|
205
|
+
optional structure info to plot atoms (with correct color)
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
fig: plotly.figure
|
|
210
|
+
plotly figure instance
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
data = plot_polyhedron(polyhedra, indices)
|
|
214
|
+
if not isinstance(atoms, ase.Atoms):
|
|
215
|
+
atoms = None
|
|
216
|
+
|
|
217
|
+
data[0]['opacity'] = 0.05
|
|
218
|
+
fig = go.Figure(data=data)
|
|
219
|
+
if atoms is not None:
|
|
220
|
+
fig.add_trace(go.Scatter3d(
|
|
221
|
+
mode='markers',
|
|
222
|
+
x=atoms.positions[:, 0], y=atoms.positions[:, 1], z=atoms.positions[:, 2],
|
|
223
|
+
marker=dict(
|
|
224
|
+
color=atoms.get_atomic_numbers(),
|
|
225
|
+
size=5,
|
|
226
|
+
sizemode='diameter',
|
|
227
|
+
colorscale=["blue", "green", "red"])))
|
|
228
|
+
|
|
229
|
+
fig.update_layout(width=1000, height=700, showlegend=False)
|
|
230
|
+
fig.update_layout(scene_aspectmode='data',
|
|
231
|
+
scene_aspectratio=dict(x=1, y=1, z=1))
|
|
232
|
+
|
|
233
|
+
camera = {'up': {'x': 1, 'y': 0, 'z': 0},
|
|
234
|
+
'center': {'x': 0, 'y': 0, 'z': 0},
|
|
235
|
+
'eye': {'x': 0, 'y': 0, 'z': 1}}
|
|
236
|
+
|
|
237
|
+
fig.update_layout(scene_camera=camera, title=title)
|
|
238
|
+
fig.update_scenes(camera_projection_type="orthographic")
|
|
239
|
+
return fig
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def plot_supercell(supercell, size=(1, 1, 1), shift_x=0.25, title=''):
|
|
243
|
+
"""
|
|
244
|
+
plot supercell with plotly
|
|
245
|
+
|
|
246
|
+
Parameter
|
|
247
|
+
---------
|
|
248
|
+
supercell: ase.Atoms
|
|
249
|
+
optional structure info to plot atoms (with correct color)
|
|
250
|
+
shift_x: float
|
|
251
|
+
amount of shift in x direction of supercell
|
|
252
|
+
title: str
|
|
253
|
+
title of plot
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
fig: plotly.figure
|
|
258
|
+
plotly figure instance
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
plot_cell = pyTEMlib.graph_tools.plot_super_cell(supercell * size, shift_x=shift_x)
|
|
262
|
+
|
|
263
|
+
# grain_boundary.cell.volume
|
|
264
|
+
supercell_area = supercell.cell.lengths()[1] / supercell.cell.lengths()[2]
|
|
265
|
+
print(supercell.symbols)
|
|
266
|
+
volume__bulk_atom = 16.465237835776012
|
|
267
|
+
ideal_volume = len(supercell.positions) * volume__bulk_atom
|
|
268
|
+
print(len(supercell.positions) * volume__bulk_atom, supercell.cell.volume)
|
|
269
|
+
x_0 = ideal_volume / supercell.cell.lengths()[1] / supercell.cell.lengths()[2]
|
|
270
|
+
print(f'Zero volume expansion supercell length: {x_0 / 10:.2f} nm; '
|
|
271
|
+
f' compared to actual {supercell.cell.lengths()[0] / 10:.2f} nm')
|
|
272
|
+
|
|
273
|
+
fig = go.Figure(data=[
|
|
274
|
+
go.Scatter3d(x=plot_cell.positions[:, 0], y=plot_cell.positions[:, 1], z=plot_cell.positions[:, 2],
|
|
275
|
+
mode='markers',
|
|
276
|
+
marker=dict(
|
|
277
|
+
color=plot_cell.get_atomic_numbers(),
|
|
278
|
+
size=5,
|
|
279
|
+
sizemode='diameter',
|
|
280
|
+
colorscale=["blue", "green", "red"]))])
|
|
281
|
+
|
|
282
|
+
fig.update_layout(width=700, margin=dict(r=10, l=10, b=10, t=10))
|
|
283
|
+
fig.update_layout(scene_aspectmode='data',
|
|
284
|
+
scene_aspectratio=dict(x=1, y=1, z=1))
|
|
285
|
+
|
|
286
|
+
camera = dict(
|
|
287
|
+
up=dict(x=0, y=1, z=0),
|
|
288
|
+
center=dict(x=0, y=0, z=0),
|
|
289
|
+
eye=dict(x=0, y=0, z=1)
|
|
290
|
+
)
|
|
291
|
+
fig.update_layout(scene_camera=camera, title=title)
|
|
292
|
+
fig.update_scenes(camera_projection_type="orthographic")
|
|
293
|
+
return fig
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def plot_supercell_bonds(polyhedra, atoms, volumes=None, atom_size=15, title=''):
|
|
297
|
+
"""
|
|
298
|
+
plot atoms and bonds with plotly
|
|
299
|
+
|
|
300
|
+
Parameter
|
|
301
|
+
---------
|
|
302
|
+
polyhedra: dict
|
|
303
|
+
dictionary of all polyhedra
|
|
304
|
+
atoms: ase.Atoms
|
|
305
|
+
optional structure info to plot atoms (with correct color)
|
|
306
|
+
volumes: list
|
|
307
|
+
list of volumes, optional structure
|
|
308
|
+
atoms_size: float
|
|
309
|
+
sie of atoms to plot
|
|
310
|
+
title: str
|
|
311
|
+
title of plot
|
|
312
|
+
|
|
313
|
+
Returns
|
|
314
|
+
-------
|
|
315
|
+
fig: plotly.figure
|
|
316
|
+
plotly figure instance
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
data = plot_bonds(polyhedra)
|
|
320
|
+
if volumes is None:
|
|
321
|
+
volumes = [atom_size] * len(atoms.get_atomic_numbers())
|
|
322
|
+
|
|
323
|
+
fig = go.Figure(data=data)
|
|
324
|
+
fig.add_trace(go.Scatter3d(
|
|
325
|
+
mode='markers',
|
|
326
|
+
x=atoms.positions[:, 0], y=atoms.positions[:, 1], z=atoms.positions[:, 2],
|
|
327
|
+
marker=dict(
|
|
328
|
+
color=atoms.get_atomic_numbers(),
|
|
329
|
+
size=np.asarray(volumes) ** 2 / 10,
|
|
330
|
+
sizemode='diameter',
|
|
331
|
+
colorscale=["blue", "green", "red"])))
|
|
332
|
+
|
|
333
|
+
fig.update_layout(width=1000, height=700, showlegend=False)
|
|
334
|
+
fig.update_layout(scene_aspectmode='data',
|
|
335
|
+
scene_aspectratio=dict(x=1, y=1, z=1))
|
|
336
|
+
|
|
337
|
+
camera = {'up': {'x': 0, 'y': 1, 'z': 0},
|
|
338
|
+
'center': {'x': 0, 'y': 0, 'z': 0},
|
|
339
|
+
'eye': {'x': 0, 'y': 0, 'z': 1}}
|
|
340
|
+
fig.update_layout(scene_camera=camera, title=title)
|
|
341
|
+
fig.update_scenes(camera_projection_type="orthographic")
|
|
342
|
+
return fig
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def plot_supercell_polyhedra(polyhedra, indices, atoms, volumes=None, title=''):
|
|
346
|
+
"""
|
|
347
|
+
plot atoms and polyhedra with plotly
|
|
348
|
+
|
|
349
|
+
Parameter
|
|
350
|
+
---------
|
|
351
|
+
polyhedra: dict
|
|
352
|
+
dictionary of all polyhedra
|
|
353
|
+
indices: list
|
|
354
|
+
list of indices of polyhedra to plot
|
|
355
|
+
atoms: ase.Atoms
|
|
356
|
+
optional structure info to plot atoms (with correct color)
|
|
357
|
+
volumes: list
|
|
358
|
+
list of volumes, optional structure
|
|
359
|
+
title: str
|
|
360
|
+
title of plot
|
|
361
|
+
|
|
362
|
+
Returns
|
|
363
|
+
-------
|
|
364
|
+
fig: plotly.figure
|
|
365
|
+
plotly figure instance
|
|
366
|
+
"""
|
|
367
|
+
data = plot_polyhedron(polyhedra, indices)
|
|
368
|
+
if volumes is None:
|
|
369
|
+
volumes = [10] * len(atoms.get_atomic_numbers())
|
|
370
|
+
|
|
371
|
+
fig = go.Figure(data=data)
|
|
372
|
+
fig.add_trace(go.Scatter3d(
|
|
373
|
+
mode='markers',
|
|
374
|
+
x=atoms.positions[:, 0], y=atoms.positions[:, 1], z=atoms.positions[:, 2],
|
|
375
|
+
marker=dict(
|
|
376
|
+
color=atoms.get_atomic_numbers(),
|
|
377
|
+
size=np.asarray(volumes)**2 / 10,
|
|
378
|
+
sizemode='diameter',
|
|
379
|
+
colorscale=["blue", "green", "red"])))
|
|
380
|
+
|
|
381
|
+
fig.update_layout(width=1000, height=700, showlegend=False)
|
|
382
|
+
fig.update_layout(scene_aspectmode='data',
|
|
383
|
+
scene_aspectratio=dict(x=1, y=1, z=1))
|
|
384
|
+
|
|
385
|
+
camera = {'up': {'x': 0, 'y': 1, 'z': 0},
|
|
386
|
+
'center': {'x': 0, 'y': 0, 'z': 0},
|
|
387
|
+
'eye': {'x': 0, 'y': 0, 'z': 1}}
|
|
388
|
+
fig.update_layout(scene_camera=camera, title=title)
|
|
389
|
+
fig.update_scenes(camera_projection_type="orthographic")
|
|
390
|
+
return fig
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def show_polyhedra(polyhedra, boundary_polyhedra, atoms, volumes=None, title=f''):
|
|
394
|
+
"""
|
|
395
|
+
plot polyhedra and atoms of vertices with plotly
|
|
396
|
+
|
|
397
|
+
Parameter
|
|
398
|
+
---------
|
|
399
|
+
polyhedra: dict
|
|
400
|
+
dictionary of all polyhedra
|
|
401
|
+
boundary_polyhedra: list
|
|
402
|
+
list of indices of polyhedra to plot
|
|
403
|
+
atoms: ase.Atoms
|
|
404
|
+
optional structure info to plot atoms (with correct color)
|
|
405
|
+
volumes: list
|
|
406
|
+
list of volumes, optional structure
|
|
407
|
+
title: str
|
|
408
|
+
title of plot
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
fig: plotly.figure
|
|
413
|
+
plotly figure instance
|
|
414
|
+
"""
|
|
415
|
+
|
|
416
|
+
data = plot_polyhedron(polyhedra, boundary_polyhedra)
|
|
417
|
+
atom_indices = []
|
|
418
|
+
for poly in boundary_polyhedra:
|
|
419
|
+
atom_indices.extend(polyhedra[poly]['indices'])
|
|
420
|
+
atom_indices = list(set(atom_indices))
|
|
421
|
+
atomic_numbers = []
|
|
422
|
+
atomic_volumes = []
|
|
423
|
+
for atom in atom_indices:
|
|
424
|
+
atomic_numbers.append(atoms[atom].number)
|
|
425
|
+
atomic_volumes.append(volumes[atoms[atom].index] ** 2 / 10)
|
|
426
|
+
|
|
427
|
+
if volumes is None:
|
|
428
|
+
atomic_volumes = [10] * len(atoms.get_atomic_numbers())
|
|
429
|
+
fig = go.Figure(data=data)
|
|
430
|
+
|
|
431
|
+
fig.add_trace(go.Scatter3d(
|
|
432
|
+
mode='markers',
|
|
433
|
+
x=atoms.positions[atom_indices, 0], y=atoms.positions[atom_indices, 1], z=atoms.positions[atom_indices, 2],
|
|
434
|
+
marker=dict(
|
|
435
|
+
color=atomic_numbers,
|
|
436
|
+
size=atomic_volumes,
|
|
437
|
+
sizemode='diameter',
|
|
438
|
+
colorscale=["blue", "green", "red"])))
|
|
439
|
+
|
|
440
|
+
fig.update_layout(width=1000, height=700, showlegend=False)
|
|
441
|
+
fig.update_layout(scene_aspectmode='data',
|
|
442
|
+
scene_aspectratio=dict(x=1, y=1, z=1))
|
|
443
|
+
|
|
444
|
+
camera = {'up': {'x': 1, 'y': 0, 'z': 0},
|
|
445
|
+
'center': {'x': 0, 'y': 0, 'z': 0},
|
|
446
|
+
'eye': {'x': 0, 'y': 0, 'z': 1}}
|
|
447
|
+
fig.update_layout(scene_camera=camera, title=title)
|
|
448
|
+
fig.update_scenes(camera_projection_type="orthographic")
|
|
449
|
+
return fig
|
pyTEMlib/image_dialog.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Input Dialog for Image Analysis
|
|
3
|
+
|
|
4
|
+
Author: Gerd Duscher
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
# -*- coding: utf-8 -*-
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import sidpy
|
|
11
|
+
|
|
12
|
+
Qt_available = True
|
|
13
|
+
try:
|
|
14
|
+
from PyQt5 import QtCore, QtWidgets
|
|
15
|
+
except:
|
|
16
|
+
Qt_available = False
|
|
17
|
+
# print('Qt dialogs are not available')
|
|
18
|
+
|
|
19
|
+
from matplotlib.widgets import SpanSelector
|
|
20
|
+
from skimage import exposure
|
|
21
|
+
|
|
22
|
+
from pyTEMlib.image_dlg import *
|
|
23
|
+
from pyTEMlib.microscope import microscope
|
|
24
|
+
|
|
25
|
+
_version = 000
|
|
26
|
+
|
|
27
|
+
if Qt_available:
|
|
28
|
+
class ImageDialog(QtWidgets.QDialog):
|
|
29
|
+
"""
|
|
30
|
+
Input Dialog for Image Analysis
|
|
31
|
+
|
|
32
|
+
Opens a PyQt5 GUi Dialog that allows to set the experimental parameter necessary for a Quantification.
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
The dialog operates on a sidpy dataset
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, dataset, parent=None):
|
|
39
|
+
super(ImageDialog, self).__init__(parent)
|
|
40
|
+
if not isinstance(dataset, sidpy.Dataset):
|
|
41
|
+
raise TypeError("we need a sidpy.Dataset")
|
|
42
|
+
self.parent = parent
|
|
43
|
+
self.debug = 0
|
|
44
|
+
self.dataset = dataset
|
|
45
|
+
self.image = np.array(self.dataset)
|
|
46
|
+
self.v_min = np.array(dataset).min()
|
|
47
|
+
self.v_max = np.array(dataset).max()
|
|
48
|
+
|
|
49
|
+
self.ui = UiDialog(self)
|
|
50
|
+
self.setWindowTitle('Image Info')
|
|
51
|
+
|
|
52
|
+
self.dataset.plot()
|
|
53
|
+
self.histogram()
|
|
54
|
+
self.cid = self.ui.histogram.axes.figure.canvas.mpl_connect('button_press_event', self.onclick)
|
|
55
|
+
|
|
56
|
+
self.span = SpanSelector(self.ui.histogram.axes, self.on_select, 'horizontal', useblit=False,
|
|
57
|
+
button=1, minspan=5,
|
|
58
|
+
rectprops=dict(alpha=0.3, facecolor='blue'))
|
|
59
|
+
minimum_info = {'size': self.dataset.shape,
|
|
60
|
+
'exposure_time': 0.0,
|
|
61
|
+
'convergence_angle': 0.0,
|
|
62
|
+
'acceleration_voltage': 100.0,
|
|
63
|
+
'binning': 1, 'conversion': 1.0,
|
|
64
|
+
'flux': 1.0, 'current': 1.0}
|
|
65
|
+
|
|
66
|
+
if 'experiment' not in self.dataset.metadata:
|
|
67
|
+
self.dataset.metadata['experiment'] = {}
|
|
68
|
+
for key, value in minimum_info.items():
|
|
69
|
+
if key not in self.dataset.metadata['experiment']:
|
|
70
|
+
self.dataset.metadata['experiment'][key] = value
|
|
71
|
+
self.experiment = self.dataset.metadata['experiment']
|
|
72
|
+
self.set_action()
|
|
73
|
+
self.update()
|
|
74
|
+
|
|
75
|
+
def histogram(self, bins=256):
|
|
76
|
+
ax_hist = self.ui.histogram.axes
|
|
77
|
+
ax_hist.clear()
|
|
78
|
+
|
|
79
|
+
hist, bin_edges = np.histogram(np.array(self.image), range=[self.v_min, self.v_max], bins=bins, density=True)
|
|
80
|
+
ax_hist.plot(np.array(bin_edges)[:-1], np.array(hist))
|
|
81
|
+
|
|
82
|
+
image = self.image * 1.0
|
|
83
|
+
image[image < self.v_min] = self.v_min
|
|
84
|
+
image[image > self.v_max] = self.v_max
|
|
85
|
+
|
|
86
|
+
img_cdf, bins = exposure.cumulative_distribution(np.array(image), bins)
|
|
87
|
+
ax_hist.plot(bins, img_cdf * hist.max(), 'r')
|
|
88
|
+
ax_hist.figure.canvas.draw()
|
|
89
|
+
self.span = SpanSelector(self.ui.histogram.axes, self.on_select, 'horizontal', useblit=False,
|
|
90
|
+
button=1, minspan=5,
|
|
91
|
+
rectprops=dict(alpha=0.3, facecolor='blue'))
|
|
92
|
+
self.plot()
|
|
93
|
+
|
|
94
|
+
def onclick(self, event):
|
|
95
|
+
if event.dblclick:
|
|
96
|
+
self.v_min = np.array(self.dataset).min()
|
|
97
|
+
self.v_max = np.array(self.dataset).max()
|
|
98
|
+
self.histogram()
|
|
99
|
+
|
|
100
|
+
def on_select(self, v_min, v_max):
|
|
101
|
+
self.v_min = v_min
|
|
102
|
+
self.v_max = v_max
|
|
103
|
+
self.histogram()
|
|
104
|
+
|
|
105
|
+
def plot(self):
|
|
106
|
+
ax = self.dataset.view.axis
|
|
107
|
+
img = self.dataset.view.img
|
|
108
|
+
img.set_data(self.image)
|
|
109
|
+
img.set_clim(vmin=self.v_min, vmax=self.v_max)
|
|
110
|
+
ax.figure.canvas.draw()
|
|
111
|
+
|
|
112
|
+
def on_enter(self):
|
|
113
|
+
sender = self.sender()
|
|
114
|
+
if sender == self.ui.timeEdit:
|
|
115
|
+
value = float(str(sender.displayText()).strip())
|
|
116
|
+
self.experiment['exposure_time'] = value
|
|
117
|
+
sender.setText(f"{value:.2f}")
|
|
118
|
+
elif sender == self.ui.convEdit:
|
|
119
|
+
value = float(str(sender.displayText()).strip())
|
|
120
|
+
self.experiment['convergence_angle'] = value
|
|
121
|
+
sender.setText(f"{value:.2f}")
|
|
122
|
+
elif sender == self.ui.E0Edit:
|
|
123
|
+
value = float(str(sender.displayText()).strip())
|
|
124
|
+
self.experiment['acceleration_voltage'] = value * 1000.0
|
|
125
|
+
sender.setText(f"{value:.2f}")
|
|
126
|
+
|
|
127
|
+
def on_list_enter(self):
|
|
128
|
+
sender = self.sender()
|
|
129
|
+
if sender == self.ui.TEMList:
|
|
130
|
+
microscope.set_microscope(self.ui.TEMList.currentText())
|
|
131
|
+
self.setWindowTitle(microscope.name)
|
|
132
|
+
|
|
133
|
+
self.experiment['microscope'] = microscope.name
|
|
134
|
+
self.experiment['convergence_angle'] = microscope.alpha
|
|
135
|
+
self.experiment['acceleration_voltage'] = microscope.E0
|
|
136
|
+
self.update()
|
|
137
|
+
|
|
138
|
+
def update(self):
|
|
139
|
+
self.ui.convEdit.setText(f"{self.experiment['convergence_angle']:.2f}")
|
|
140
|
+
self.ui.E0Edit.setText(f"{self.experiment['acceleration_voltage']/1000.:.2f}")
|
|
141
|
+
|
|
142
|
+
self.ui.timeEdit.setText(f"{self.experiment['exposure_time']:.6f}")
|
|
143
|
+
size_text = f'{self.dataset.shape[0]}'
|
|
144
|
+
for size in self.dataset.shape[1:]:
|
|
145
|
+
size_text = size_text + f' x {size}'
|
|
146
|
+
self.ui.sizeEdit.setText(size_text)
|
|
147
|
+
|
|
148
|
+
# self.ui.binningEdit.setText(f"{self.experiment['binning']}")
|
|
149
|
+
# self.ui.conversionEdit.setText(f"{self.experiment['conversion']:.2f}")
|
|
150
|
+
# self.ui.fluxEdit.setText(f"{self.experiment['flux']:.2f}")
|
|
151
|
+
# self.ui.VOAEdit.setText(f"{self.experiment['current']:.2f}")
|
|
152
|
+
|
|
153
|
+
def set_action(self):
|
|
154
|
+
self.ui.timeEdit.editingFinished.connect(self.on_enter)
|
|
155
|
+
self.ui.TEMList.activated[str].connect(self.on_list_enter)
|
|
156
|
+
|
|
157
|
+
self.ui.convEdit.editingFinished.connect(self.on_enter)
|
|
158
|
+
self.ui.E0Edit.editingFinished.connect(self.on_enter)
|