pyTEMlib 0.2025.2.1__tar.gz → 0.2025.3.0__tar.gz
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-0.2025.2.1/pyTEMlib.egg-info → pytemlib-0.2025.3.0}/PKG-INFO +2 -2
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/core_loss_widget.py +2 -4
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/eels_dialog.py +1 -1
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/eels_tools.py +11 -10
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/file_tools.py +14 -18
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/graph_tools.py +85 -35
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/image_tools.py +29 -18
- pytemlib-0.2025.3.0/pyTEMlib/version.py +6 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0/pyTEMlib.egg-info}/PKG-INFO +2 -2
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib.egg-info/requires.txt +1 -1
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/setup.py +1 -1
- pytemlib-0.2025.2.1/pyTEMlib/version.py +0 -6
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/LICENSE +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/MANIFEST.in +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/README.rst +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/__init__.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/animation.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/atom_tools.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/config_dir.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/crystal_tools.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/diffraction_plot.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/dynamic_scattering.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/eds_tools.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/eels_dialog_utilities.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/file_tools_qt.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/graph_viz.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/image_dialog.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/image_dlg.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/info_widget.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/info_widget3.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/interactive_image.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/kinematic_scattering.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/low_loss_widget.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/microscope.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/peak_dialog.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/peak_dlg.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/probe_tools.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/sidpy_tools.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/simulation_tools.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib/xrpa_x_sections.py +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib.egg-info/SOURCES.txt +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib.egg-info/dependency_links.txt +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib.egg-info/entry_points.txt +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/pyTEMlib.egg-info/top_level.txt +0 -0
- {pytemlib-0.2025.2.1 → pytemlib-0.2025.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pyTEMlib
|
|
3
|
-
Version: 0.2025.
|
|
3
|
+
Version: 0.2025.3.0
|
|
4
4
|
Summary: pyTEM: TEM Data Quantification library through a model-based approach
|
|
5
5
|
Home-page: https://pycroscopy.github.io/pyTEMlib/about.html
|
|
6
6
|
Author: Gerd Duscher
|
|
@@ -39,7 +39,7 @@ Requires-Dist: spglib
|
|
|
39
39
|
Requires-Dist: scikit-image
|
|
40
40
|
Requires-Dist: scikit-learn
|
|
41
41
|
Requires-Dist: pyNSID>=0.0.7
|
|
42
|
-
Requires-Dist: sidpy>=0.12.
|
|
42
|
+
Requires-Dist: sidpy>=0.12.7
|
|
43
43
|
Requires-Dist: SciFiReaders>=0.0.8
|
|
44
44
|
Dynamic: author
|
|
45
45
|
Dynamic: author-email
|
|
@@ -500,8 +500,7 @@ class CoreLoss(object):
|
|
|
500
500
|
self.core_loss_tab[11, 0].value = edge['areal_density']
|
|
501
501
|
self.core_loss_tab[11, 2].value = 'a.u.'
|
|
502
502
|
else:
|
|
503
|
-
dispersion = self.parent.energy_scale
|
|
504
|
-
self.parent.energy_scale[0]
|
|
503
|
+
dispersion = self.parent.energy_scale.slope
|
|
505
504
|
self.core_loss_tab[11, 0].value = np.round(
|
|
506
505
|
edge['areal_density']/self.dataset.metadata['experiment']['flux_ppm']*1e-6, 2)
|
|
507
506
|
self.core_loss_tab[11, 2].value = 'atoms/nm²'
|
|
@@ -663,8 +662,7 @@ class CoreLoss(object):
|
|
|
663
662
|
|
|
664
663
|
edge['areal_density'] = self.core_loss_tab[11, 0].value
|
|
665
664
|
if self.parent.y_scale != 1.0:
|
|
666
|
-
dispersion = self.parent.energy_scale
|
|
667
|
-
self.parent.energy_scale[0]
|
|
665
|
+
dispersion = self.parent.energy_scale.slope
|
|
668
666
|
edge['areal_density'] = self.core_loss_tab[11, 0].value * \
|
|
669
667
|
self.dataset.metadata['experiment']['flux_ppm']/1e-6
|
|
670
668
|
if 'model' in self.edges:
|
|
@@ -335,7 +335,7 @@ class CompositionWidget(object):
|
|
|
335
335
|
self.dd = (self.energy_scale[0], self.energy_scale[1])
|
|
336
336
|
|
|
337
337
|
self.dataset.metadata['experiment']['offset'] = self.energy_scale[0]
|
|
338
|
-
self.dataset.metadata['experiment']['dispersion'] = self.
|
|
338
|
+
self.dataset.metadata['experiment']['dispersion'] = self.spec_dim.slope
|
|
339
339
|
if 'edges' not in self.dataset.metadata or self.dataset.metadata['edges'] == {}:
|
|
340
340
|
self.dataset.metadata['edges'] = {'0': {}, 'model': {}, 'use_low_loss': False}
|
|
341
341
|
|
|
@@ -42,7 +42,6 @@ from pyTEMlib.xrpa_x_sections import x_sections
|
|
|
42
42
|
|
|
43
43
|
import sidpy
|
|
44
44
|
from sidpy.proc.fitter import SidFitter
|
|
45
|
-
from sidpy.base.num_utils import get_slope
|
|
46
45
|
|
|
47
46
|
# we have a function called find peaks - is it necessary?
|
|
48
47
|
# or could we just use scipy.signal import find_peaks
|
|
@@ -604,7 +603,8 @@ def fit_plasmon(dataset: Union[sidpy.Dataset, np.ndarray], startFitEnergy: float
|
|
|
604
603
|
guess_pos = np.argmax(fit_dset)
|
|
605
604
|
guess_amplitude = fit_dset[guess_pos]
|
|
606
605
|
guess_width =(endFitEnergy-startFitEnergy)/4
|
|
607
|
-
guess_pos = energy[guess_pos]
|
|
606
|
+
guess_pos = energy[start_fit_pixel+guess_pos]
|
|
607
|
+
|
|
608
608
|
if guess_width >8:
|
|
609
609
|
guess_width=8
|
|
610
610
|
try:
|
|
@@ -618,7 +618,7 @@ def fit_plasmon(dataset: Union[sidpy.Dataset, np.ndarray], startFitEnergy: float
|
|
|
618
618
|
p0=[guess_pos, guess_width, guess_amplitude])
|
|
619
619
|
except:
|
|
620
620
|
popt=[0,0,0]
|
|
621
|
-
|
|
621
|
+
|
|
622
622
|
plasmon = dataset.like_data(energy_loss_function(energy, popt[0], popt[1], popt[2]))
|
|
623
623
|
plasmon *= anglog
|
|
624
624
|
start_plasmon = np.searchsorted(energy, 0)+1
|
|
@@ -652,16 +652,16 @@ def fit_plasmon(dataset: Union[sidpy.Dataset, np.ndarray], startFitEnergy: float
|
|
|
652
652
|
def angle_correction(spectrum):
|
|
653
653
|
|
|
654
654
|
acceleration_voltage = spectrum.metadata['experiment']['acceleration_voltage']
|
|
655
|
-
energy_scale = spectrum.get_spectral_dims(return_axis=True)[0]
|
|
655
|
+
energy_scale = spectrum.get_spectral_dims(return_axis=True)[0]
|
|
656
656
|
# eff_beta = effective_collection_angle(energy_scale, spectrum.metadata['experiment']['convergence_angle'],
|
|
657
657
|
# spectrum.metadata['experiment']['collection_angle'],acceleration_voltage)
|
|
658
658
|
|
|
659
659
|
|
|
660
|
-
epc = energy_scale
|
|
660
|
+
epc = energy_scale.slope # input('ev per channel : ');
|
|
661
661
|
|
|
662
662
|
alpha = spectrum.metadata['experiment']['convergence_angle'] # input('Alpha (mrad) : ');
|
|
663
663
|
beta = spectrum.metadata['experiment']['collection_angle']# input('Beta (mrad) : ');
|
|
664
|
-
e = energy_scale
|
|
664
|
+
e = energy_scale.values
|
|
665
665
|
e0 = acceleration_voltage/1000 # input('E0 (keV) : ');
|
|
666
666
|
|
|
667
667
|
T = 1000.0*e0*(1.+e0/1022.12)/(1.0+e0/511.06)**2 # %eV # equ.5.2a or Appendix E p 427
|
|
@@ -1261,7 +1261,7 @@ def second_derivative(dataset: sidpy.Dataset, sensitivity: float=2.5) -> None:
|
|
|
1261
1261
|
"""Calculates second derivative of a sidpy.dataset"""
|
|
1262
1262
|
|
|
1263
1263
|
dim = dataset.get_spectral_dims()
|
|
1264
|
-
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1264
|
+
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1265
1265
|
if dataset.data_type.name == 'SPECTRAL_IMAGE':
|
|
1266
1266
|
spectrum = dataset.view.get_spectrum()
|
|
1267
1267
|
else:
|
|
@@ -1269,7 +1269,7 @@ def second_derivative(dataset: sidpy.Dataset, sensitivity: float=2.5) -> None:
|
|
|
1269
1269
|
|
|
1270
1270
|
spec = scipy.ndimage.gaussian_filter(spectrum, 3)
|
|
1271
1271
|
|
|
1272
|
-
dispersion =
|
|
1272
|
+
dispersion = energy_scale.slope
|
|
1273
1273
|
second_dif = np.roll(spec, -3) - 2 * spec + np.roll(spec, +3)
|
|
1274
1274
|
second_dif[:3] = 0
|
|
1275
1275
|
second_dif[-3:] = 0
|
|
@@ -1405,8 +1405,9 @@ def identify_edges(dataset: sidpy.Dataset, noise_level: float=2.0):
|
|
|
1405
1405
|
|
|
1406
1406
|
"""
|
|
1407
1407
|
dim = dataset.get_spectral_dims()
|
|
1408
|
-
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1409
|
-
dispersion =
|
|
1408
|
+
energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
|
|
1409
|
+
dispersion = energy_scale.slope
|
|
1410
|
+
|
|
1410
1411
|
spec = scipy.ndimage.gaussian_filter(dataset, 3/dispersion) # smooth with 3eV wideGaussian
|
|
1411
1412
|
|
|
1412
1413
|
first_derivative = spec - np.roll(spec, +2)
|
|
@@ -33,8 +33,6 @@ import pyTEMlib.crystal_tools
|
|
|
33
33
|
from pyTEMlib.config_dir import config_path
|
|
34
34
|
from pyTEMlib.sidpy_tools import *
|
|
35
35
|
|
|
36
|
-
from pyTEMlib.sidpy_tools import *
|
|
37
|
-
|
|
38
36
|
Qt_available = True
|
|
39
37
|
try:
|
|
40
38
|
from PyQt5 import QtCore, QtWidgets, QtGui
|
|
@@ -44,8 +42,6 @@ except ModuleNotFoundError:
|
|
|
44
42
|
|
|
45
43
|
Dimension = sidpy.Dimension
|
|
46
44
|
|
|
47
|
-
# Austin commented the line below - it is not used anywhere in the code, and it gives import errors 9-14-2024
|
|
48
|
-
# get_slope = sidpy.base.num_utils.get_slopes
|
|
49
45
|
__version__ = '2024.9.14'
|
|
50
46
|
|
|
51
47
|
from traitlets import Unicode, Bool, validate, TraitError
|
|
@@ -116,7 +112,6 @@ class FileWidget2(ipywidgets.DOMWidget):
|
|
|
116
112
|
value='None',
|
|
117
113
|
description='directory:',
|
|
118
114
|
disabled=False,
|
|
119
|
-
button_style='',
|
|
120
115
|
layout=widgets.Layout(width='90%'))
|
|
121
116
|
|
|
122
117
|
|
|
@@ -203,8 +198,7 @@ class FileWidget3(FileWidget2):
|
|
|
203
198
|
self.loaded_datasets = widgets.Dropdown(options=self.dataset_list,
|
|
204
199
|
value=self.dataset_list[0],
|
|
205
200
|
description='loaded datasets:',
|
|
206
|
-
disabled=False
|
|
207
|
-
button_style='')
|
|
201
|
+
disabled=False)
|
|
208
202
|
|
|
209
203
|
ui = widgets.HBox([select_button, add_button, self.loaded_datasets])
|
|
210
204
|
display(ui)
|
|
@@ -313,14 +307,12 @@ class FileWidget(ipywidgets.DOMWidget):
|
|
|
313
307
|
value='None',
|
|
314
308
|
description='directory:',
|
|
315
309
|
disabled=False,
|
|
316
|
-
button_style='',
|
|
317
310
|
layout=widgets.Layout(width='90%'))
|
|
318
311
|
self.dataset_list = ['None']
|
|
319
312
|
self.loaded_datasets = widgets.Dropdown(options=self.dataset_list,
|
|
320
313
|
value=self.dataset_list[0],
|
|
321
314
|
description='loaded datasets:',
|
|
322
|
-
disabled=False
|
|
323
|
-
button_style='')
|
|
315
|
+
disabled=False)
|
|
324
316
|
|
|
325
317
|
self.set_options()
|
|
326
318
|
ui = widgets.VBox([self.path_choice, self.select_files, widgets.HBox([select_button, add_button,
|
|
@@ -446,8 +438,7 @@ class ChooseDataset(object):
|
|
|
446
438
|
self.select_image = widgets.Dropdown(options=self.dataset_list,
|
|
447
439
|
value=self.dataset_list[0],
|
|
448
440
|
description='select dataset:',
|
|
449
|
-
disabled=False
|
|
450
|
-
button_style='')
|
|
441
|
+
disabled=False)
|
|
451
442
|
if show_dialog:
|
|
452
443
|
display(self.select_image)
|
|
453
444
|
|
|
@@ -484,7 +475,7 @@ class ChooseDataset(object):
|
|
|
484
475
|
self.dataset = self.datasets[self.key]
|
|
485
476
|
self.dataset.title = self.dataset.title.split('/')[-1]
|
|
486
477
|
self.dataset.title = self.dataset.title.split('/')[-1]
|
|
487
|
-
|
|
478
|
+
|
|
488
479
|
|
|
489
480
|
def add_to_dict(file_dict, name):
|
|
490
481
|
full_name = os.path.join(file_dict['directory'], name)
|
|
@@ -968,13 +959,18 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
968
959
|
id = dset[key1].original_metadata[key]['InstrumentId']
|
|
969
960
|
dset[key1].metadata['experiment']['instrument'] = model + str(id)
|
|
970
961
|
if key == 'Optics':
|
|
971
|
-
|
|
962
|
+
if 'LastMeasuredScreenCurrent' in dset[key1].original_metadata[key]:
|
|
963
|
+
dset[key1].metadata['experiment']['current'] = float(dset[key1].original_metadata[key]['LastMeasuredScreenCurrent'])
|
|
972
964
|
if key == 'Scan':
|
|
973
|
-
|
|
974
|
-
|
|
965
|
+
if 'DwellTime' in dset[key1].original_metadata[key]:
|
|
966
|
+
dset[key1].metadata['experiment']['pixel_time'] = float(dset[key1].original_metadata[key]['DwellTime'])
|
|
967
|
+
if 'FrameTime' in dset[key1].original_metadata[key]:
|
|
968
|
+
dset[key1].metadata['experiment']['exposure_time'] = float(dset[key1].original_metadata[key]['FrameTime'])
|
|
975
969
|
if key == 'Sample':
|
|
976
|
-
|
|
977
|
-
|
|
970
|
+
if 'SampleDescription' in dset[key1].original_metadata[key]:
|
|
971
|
+
dset[key1].metadata['experiment']['sample'] = dset[key1].original_metadata[key]['SampleDescription']
|
|
972
|
+
if 'SampleId' in dset[key1].original_metadata[key]:
|
|
973
|
+
dset[key1].metadata['experiment']['sample_id'] = dset[key1].original_metadata[key]['SampleId']
|
|
978
974
|
if key == 'Detectors':
|
|
979
975
|
if 'detector' in dset[key1].metadata['experiment']:
|
|
980
976
|
used_detector = dset[key1].metadata['experiment']['detector']
|
|
@@ -5,7 +5,6 @@ import numpy as np
|
|
|
5
5
|
# import ase
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
-
# from scipy.spatial import cKDTree, Voronoi, ConvexHull
|
|
9
8
|
import scipy.spatial
|
|
10
9
|
import scipy.optimize
|
|
11
10
|
import scipy.interpolate
|
|
@@ -20,7 +19,7 @@ import pyTEMlib.crystal_tools
|
|
|
20
19
|
from tqdm.auto import tqdm, trange
|
|
21
20
|
|
|
22
21
|
from .graph_viz import *
|
|
23
|
-
|
|
22
|
+
QT_available = False
|
|
24
23
|
|
|
25
24
|
###########################################################################
|
|
26
25
|
# utility functions
|
|
@@ -212,6 +211,14 @@ def get_voronoi(tetrahedra, atoms, bond_radii=None, optimize=True):
|
|
|
212
211
|
"""
|
|
213
212
|
|
|
214
213
|
extent = atoms.cell.lengths()
|
|
214
|
+
print('extent', extent)
|
|
215
|
+
|
|
216
|
+
if np.abs(atoms.positions[:, 2]).sum() <= 0.01:
|
|
217
|
+
positions = atoms.positions[:, :2]
|
|
218
|
+
extent = extent[:2]
|
|
219
|
+
else:
|
|
220
|
+
positions = atoms.positions
|
|
221
|
+
|
|
215
222
|
if atoms.info is None:
|
|
216
223
|
atoms.info = {}
|
|
217
224
|
|
|
@@ -232,8 +239,8 @@ def get_voronoi(tetrahedra, atoms, bond_radii=None, optimize=True):
|
|
|
232
239
|
r_a = []
|
|
233
240
|
for vert in vertices:
|
|
234
241
|
r_a.append(bond_radii[vert])
|
|
235
|
-
voronoi, radius = interstitial_sphere_center(
|
|
236
|
-
|
|
242
|
+
voronoi, radius = interstitial_sphere_center(positions[vertices], r_a, optimize=optimize)
|
|
243
|
+
|
|
237
244
|
r_a = np.average(r_a) # np.min(r_a)
|
|
238
245
|
r_aa.append(r_a)
|
|
239
246
|
|
|
@@ -247,7 +254,7 @@ def get_voronoi(tetrahedra, atoms, bond_radii=None, optimize=True):
|
|
|
247
254
|
def find_overlapping_spheres(voronoi_vertices, r_vv, r_a, cheat=1.):
|
|
248
255
|
"""Find overlapping spheres"""
|
|
249
256
|
|
|
250
|
-
vertex_tree = scipy.spatial.
|
|
257
|
+
vertex_tree = scipy.spatial.KDTree(voronoi_vertices)
|
|
251
258
|
|
|
252
259
|
pairs = vertex_tree.query_pairs(r=r_a * 2)
|
|
253
260
|
|
|
@@ -424,7 +431,7 @@ def get_non_periodic_supercell(super_cell):
|
|
|
424
431
|
return atoms
|
|
425
432
|
|
|
426
433
|
def get_connectivity_matrix(crystal, atoms, polyhedra):
|
|
427
|
-
crystal_tree = scipy.spatial.
|
|
434
|
+
crystal_tree = scipy.spatial.KDTree(crystal.positions)
|
|
428
435
|
|
|
429
436
|
|
|
430
437
|
connectivity_matrix = np.zeros([len(atoms),len(atoms)], dtype=int)
|
|
@@ -476,8 +483,8 @@ def get_bonds(crystal, shift= 0., verbose = False, cheat=1.0):
|
|
|
476
483
|
other = []
|
|
477
484
|
super_cell_atoms =[]
|
|
478
485
|
|
|
479
|
-
atoms_tree = scipy.spatial.
|
|
480
|
-
crystal_tree = scipy.spatial.
|
|
486
|
+
atoms_tree = scipy.spatial.KDTree(atoms.positions-crystal.cell.lengths())
|
|
487
|
+
crystal_tree = scipy.spatial.KDTree(crystal.positions)
|
|
481
488
|
connectivity_matrix = np.zeros([len(atoms),len(atoms)], dtype=float)
|
|
482
489
|
|
|
483
490
|
for polyhedron in polyhedra.values():
|
|
@@ -699,18 +706,21 @@ def find_polyhedra(atoms, optimize=True, cheat=1.0, bond_radii=None):
|
|
|
699
706
|
raise TypeError('This function needs an ase.Atoms object')
|
|
700
707
|
|
|
701
708
|
if np.abs(atoms.positions[:, 2]).sum() <= 0.01:
|
|
702
|
-
|
|
709
|
+
positions = atoms.positions[:, :2]
|
|
710
|
+
print('2D')
|
|
703
711
|
else:
|
|
704
|
-
|
|
712
|
+
positions = atoms.positions
|
|
713
|
+
tetrahedra = scipy.spatial.Delaunay(positions)
|
|
705
714
|
|
|
706
715
|
voronoi_vertices, voronoi_tetrahedrons, r_vv, r_a = get_voronoi(tetrahedra, atoms, optimize=optimize, bond_radii=bond_radii)
|
|
707
|
-
|
|
708
|
-
|
|
716
|
+
|
|
717
|
+
if positions.shape[1] < 3:
|
|
718
|
+
r_vv = np.array(r_vv)*1.
|
|
709
719
|
overlapping_pairs = find_overlapping_spheres(voronoi_vertices, r_vv, r_a, cheat=cheat)
|
|
710
720
|
|
|
711
721
|
clusters, visited_all = find_interstitial_clusters(overlapping_pairs)
|
|
712
722
|
|
|
713
|
-
if
|
|
723
|
+
if positions.shape[1] < 3:
|
|
714
724
|
rings = get_polygons(atoms, clusters, voronoi_tetrahedrons)
|
|
715
725
|
return rings
|
|
716
726
|
else:
|
|
@@ -770,7 +780,7 @@ def sort_polyhedra_by_vertices(polyhedra, visible=range(4, 100), z_lim=[0, 100],
|
|
|
770
780
|
##########################
|
|
771
781
|
# New Graph Stuff
|
|
772
782
|
##########################
|
|
773
|
-
def
|
|
783
|
+
def breadth_first_search2(graph, initial, projected_crystal):
|
|
774
784
|
""" breadth first search of atoms viewed as a graph
|
|
775
785
|
|
|
776
786
|
the projection dictionary has to contain the following items
|
|
@@ -794,15 +804,20 @@ def breadth_first_search(graph, initial, projected_crystal):
|
|
|
794
804
|
"""
|
|
795
805
|
|
|
796
806
|
projection_tags = projected_crystal.info['projection']
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
807
|
+
if 'lattice_vector' in projection_tags:
|
|
808
|
+
a_lattice_vector = projection_tags['lattice_vector']['a']
|
|
809
|
+
b_lattice_vector = projection_tags['lattice_vector']['b']
|
|
810
|
+
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
811
|
+
near = main
|
|
812
|
+
else:
|
|
813
|
+
# get lattice vectors to hopp along through graph
|
|
814
|
+
projected_unit_cell = projected_crystal.cell[:2, :2]
|
|
815
|
+
a_lattice_vector = projected_unit_cell[0]
|
|
816
|
+
b_lattice_vector = projected_unit_cell[1]
|
|
817
|
+
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
818
|
+
near = np.append(main, projection_tags['near_base'], axis=0) # all nearest atoms
|
|
804
819
|
# get k next nearest neighbours for each node
|
|
805
|
-
neighbour_tree = scipy.spatial.
|
|
820
|
+
neighbour_tree = scipy.spatial.KDTree(graph)
|
|
806
821
|
distances, indices = neighbour_tree.query(graph, # let's get all neighbours
|
|
807
822
|
k=50) # projection_tags['number_of_nearest_neighbours']*2 + 1)
|
|
808
823
|
# print(projection_tags['number_of_nearest_neighbours'] * 2 + 1)
|
|
@@ -835,6 +850,46 @@ def breadth_first_search(graph, initial, projected_crystal):
|
|
|
835
850
|
|
|
836
851
|
return graph[visited], ideal
|
|
837
852
|
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
def breath_first_search(graph, initial, lattice_parameter, tolerance=1):
|
|
856
|
+
""" breadth first search of atoms viewed as a graph
|
|
857
|
+
we only
|
|
858
|
+
"""
|
|
859
|
+
neighbour_tree = scipy.spatial.KDTree(graph)
|
|
860
|
+
distances, indices = neighbour_tree.query(graph, # let's get all neighbours
|
|
861
|
+
k=50) # projection_tags['number_of_nearest_neighbours']*2 + 1)
|
|
862
|
+
visited = [] # the atoms we visited
|
|
863
|
+
angles = [] # atoms at ideal lattice
|
|
864
|
+
sub_lattice = [] # atoms in base and disregarded
|
|
865
|
+
queue = [initial]
|
|
866
|
+
queue_angles=[0]
|
|
867
|
+
|
|
868
|
+
while queue:
|
|
869
|
+
node = queue.pop(0)
|
|
870
|
+
angle = queue_angles.pop(0)
|
|
871
|
+
if node not in visited:
|
|
872
|
+
visited.append(node)
|
|
873
|
+
angles.append(angle)
|
|
874
|
+
neighbors = indices[node]
|
|
875
|
+
for i, neighbour in enumerate(neighbors):
|
|
876
|
+
if neighbour not in visited:
|
|
877
|
+
hopp = graph[node] - graph[neighbour]
|
|
878
|
+
distance_to_ideal = np.linalg.norm(hopp)
|
|
879
|
+
if np.min(np.abs(distance_to_ideal - lattice_parameter)) < tolerance:
|
|
880
|
+
queue.append(neighbour)
|
|
881
|
+
queue_angles.append(np.arctan2(hopp[1], hopp[0]))
|
|
882
|
+
angles[0] = angles[1]
|
|
883
|
+
out_atoms = np.stack([graph[visited][:, 0], graph[visited][:, 1], angles])
|
|
884
|
+
return out_atoms.T, visited
|
|
885
|
+
|
|
886
|
+
def delete_rim_atoms(atoms, extent, rim_distance):
|
|
887
|
+
rim = np.where(atoms[:, :2] - extent > -rim_distance)[0]
|
|
888
|
+
middle_atoms = np.delete(atoms, rim, axis=0)
|
|
889
|
+
rim = np.where(middle_atoms[:, :2].min(axis=1)<rim_distance)[0]
|
|
890
|
+
middle_atoms = np.delete(middle_atoms, rim, axis=0)
|
|
891
|
+
return middle_atoms
|
|
892
|
+
|
|
838
893
|
####################
|
|
839
894
|
# Distortion Matrix
|
|
840
895
|
####################
|
|
@@ -992,7 +1047,7 @@ def get_significant_vertices(vertices, distance=3):
|
|
|
992
1047
|
list of points that are all a minimum of 3 apart.
|
|
993
1048
|
"""
|
|
994
1049
|
|
|
995
|
-
tt = scipy.spatial.
|
|
1050
|
+
tt = scipy.spatial.KDTree(np.array(vertices))
|
|
996
1051
|
near = tt.query_ball_point(vertices, distance)
|
|
997
1052
|
ideal_vertices = []
|
|
998
1053
|
for indices in near:
|
|
@@ -1146,21 +1201,16 @@ def undistort_stack(distortion_matrix, data):
|
|
|
1146
1201
|
nimages = data.shape[0]
|
|
1147
1202
|
done = 0
|
|
1148
1203
|
|
|
1149
|
-
|
|
1150
|
-
progress = ft.ProgressDialog("Correct Scan Distortions", nimages)
|
|
1204
|
+
|
|
1151
1205
|
for i in range(nimages):
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
# progress output :
|
|
1158
|
-
sys.stdout.write("[%-50s] %d%%" % ('=' * done, 2 * done))
|
|
1159
|
-
sys.stdout.flush()
|
|
1206
|
+
done = int((i + 1) / nimages * 50)
|
|
1207
|
+
sys.stdout.write('\r')
|
|
1208
|
+
# progress output :
|
|
1209
|
+
sys.stdout.write("[%-50s] %d%%" % ('=' * done, 2 * done))
|
|
1210
|
+
sys.stdout.flush()
|
|
1160
1211
|
|
|
1161
1212
|
interpolated[i, :, :] = griddata(corrected, intensity_values[i, :], (grid_x, grid_y), method='linear')
|
|
1162
|
-
|
|
1163
|
-
progress.set_value(nimages)
|
|
1213
|
+
|
|
1164
1214
|
print(':-)')
|
|
1165
1215
|
print('You have successfully completed undistortion of image stack')
|
|
1166
1216
|
return interpolated
|
|
@@ -212,6 +212,7 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
212
212
|
return
|
|
213
213
|
|
|
214
214
|
new_image = new_image - new_image.min()
|
|
215
|
+
|
|
215
216
|
fft_transform = (np.fft.fftshift(np.fft.fft2(new_image)))
|
|
216
217
|
|
|
217
218
|
image_dims = pyTEMlib.sidpy_tools.get_image_dims(dset)
|
|
@@ -227,12 +228,12 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
227
228
|
fft_dset.modality = 'fft'
|
|
228
229
|
|
|
229
230
|
fft_dset.set_dimension(0, sidpy.Dimension(np.fft.fftshift(np.fft.fftfreq(new_image.shape[0],
|
|
230
|
-
d=
|
|
231
|
+
d=dset.x[1]-dset.x[0])),
|
|
231
232
|
|
|
232
233
|
name='u', units=units_x, dimension_type='RECIPROCAL',
|
|
233
234
|
quantity='reciprocal_length'))
|
|
234
235
|
fft_dset.set_dimension(1, sidpy.Dimension(np.fft.fftshift(np.fft.fftfreq(new_image.shape[1],
|
|
235
|
-
d=
|
|
236
|
+
d=dset.y[1]- dset.y[0])),
|
|
236
237
|
name='v', units=units_y, dimension_type='RECIPROCAL',
|
|
237
238
|
quantity='reciprocal_length'))
|
|
238
239
|
|
|
@@ -319,7 +320,9 @@ def diffractogram_spots(dset, spot_threshold, return_center=True, eps=0.1):
|
|
|
319
320
|
print(f'Found {spots_random.shape[0]} reflections')
|
|
320
321
|
|
|
321
322
|
# Needed for conversion from pixel to Reciprocal space
|
|
322
|
-
|
|
323
|
+
image_dims = dset.get_image_dims(return_axis=True)
|
|
324
|
+
rec_scale = np.array([image_dims[0].slope, image_dims[1].slope])
|
|
325
|
+
|
|
323
326
|
spots_random[:, :2] = spots_random[:, :2]*rec_scale+[dset.u.values[0], dset.v.values[0]]
|
|
324
327
|
# sort reflections
|
|
325
328
|
spots_random[:, 2] = np.linalg.norm(spots_random[:, 0:2], axis=1)
|
|
@@ -516,7 +519,8 @@ def complete_registration(main_dataset, storage_channel=None):
|
|
|
516
519
|
|
|
517
520
|
rigid_registered_dataset = rigid_registration(main_dataset)
|
|
518
521
|
|
|
519
|
-
|
|
522
|
+
print(rigid_registered_dataset)
|
|
523
|
+
rigid_registered_dataset.data_type = 'IMAGE_STACK'
|
|
520
524
|
print('Non-Rigid_Registration')
|
|
521
525
|
|
|
522
526
|
non_rigid_registered = demon_registration(rigid_registered_dataset)
|
|
@@ -580,7 +584,6 @@ def demon_registration(dataset, verbose=False):
|
|
|
580
584
|
resampler.SetDefaultPixelValue(0)
|
|
581
585
|
|
|
582
586
|
for i in trange(nimages):
|
|
583
|
-
|
|
584
587
|
moving = sitk.GetImageFromArray(dataset[i])
|
|
585
588
|
moving_f = sitk.DiscreteGaussian(moving, 2.0)
|
|
586
589
|
displacement_field = demons.Execute(fixed, moving_f)
|
|
@@ -603,6 +606,7 @@ def demon_registration(dataset, verbose=False):
|
|
|
603
606
|
if 'input_shape' in dataset.metadata:
|
|
604
607
|
demon_registered.metadata['input_shape'] = dataset.metadata['input_shape']
|
|
605
608
|
demon_registered.metadata['input_dataset'] = dataset.source
|
|
609
|
+
demon_registered.data_type = 'IMAGE_STACK'
|
|
606
610
|
return demon_registered
|
|
607
611
|
|
|
608
612
|
|
|
@@ -699,6 +703,7 @@ def rigid_registration(dataset, sub_pixel=True):
|
|
|
699
703
|
rigid_registered.set_dimension(2, sidpy.Dimension(array_y,
|
|
700
704
|
'y', units='nm', quantity='Length',
|
|
701
705
|
dimension_type='spatial'))
|
|
706
|
+
rigid_registered.data_type = 'IMAGE_STACK'
|
|
702
707
|
return rigid_registered.rechunk({0: 'auto', 1: -1, 2: -1})
|
|
703
708
|
|
|
704
709
|
|
|
@@ -902,9 +907,9 @@ def get_profile(dataset, line, spline_order=-1):
|
|
|
902
907
|
"""
|
|
903
908
|
xv, yv = get_line_selection_points(line)
|
|
904
909
|
if dataset.data_type.name == 'IMAGE':
|
|
905
|
-
dataset.get_image_dims()
|
|
906
|
-
xv /=
|
|
907
|
-
yv /=
|
|
910
|
+
image_dims = dataset.get_image_dims(return_axis=True)
|
|
911
|
+
xv /= image_dims[0].slope
|
|
912
|
+
yv /= image_dims[1].slope
|
|
908
913
|
profile = scipy.ndimage.map_coordinates(np.array(dataset), [xv, yv])
|
|
909
914
|
|
|
910
915
|
profile_dataset = sidpy.Dataset.from_array(profile.sum(axis=0))
|
|
@@ -1101,7 +1106,7 @@ def clean_svd(im, pixel_size=1, source_size=5):
|
|
|
1101
1106
|
patch_size = int(source_size/pixel_size)
|
|
1102
1107
|
if patch_size < 3:
|
|
1103
1108
|
patch_size = 3
|
|
1104
|
-
patches = image.extract_patches_2d(im, (patch_size, patch_size))
|
|
1109
|
+
patches = image.extract_patches_2d(np.array(im), (patch_size, patch_size))
|
|
1105
1110
|
patches = patches.reshape(patches.shape[0], patches.shape[1]*patches.shape[2])
|
|
1106
1111
|
|
|
1107
1112
|
num_components = 32
|
|
@@ -1110,6 +1115,8 @@ def clean_svd(im, pixel_size=1, source_size=5):
|
|
|
1110
1115
|
u_im_size = int(np.sqrt(u.shape[0]))
|
|
1111
1116
|
reduced_image = u[:, 0].reshape(u_im_size, u_im_size)
|
|
1112
1117
|
reduced_image = reduced_image/reduced_image.sum()*im.sum()
|
|
1118
|
+
if isinstance(im, sidpy.Dataset):
|
|
1119
|
+
reduced_image = im.like_data(reduced_image)
|
|
1113
1120
|
return reduced_image
|
|
1114
1121
|
|
|
1115
1122
|
|
|
@@ -1402,11 +1409,18 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1402
1409
|
error = np.ones(o_image.shape, dtype=np.complex64)
|
|
1403
1410
|
est = np.ones(o_image.shape, dtype=np.complex64)
|
|
1404
1411
|
source = np.ones(o_image.shape, dtype=np.complex64)
|
|
1412
|
+
o_image = o_image - o_image.min()
|
|
1413
|
+
image_mult = o_image.max()
|
|
1414
|
+
o_image = o_image / o_image.max()
|
|
1405
1415
|
source.real = o_image
|
|
1406
1416
|
|
|
1407
1417
|
response_ft = fftpack.fft2(probe_c)
|
|
1408
1418
|
|
|
1409
|
-
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
ap_angle = o_image.metadata['experiment']['convergence_angle']
|
|
1422
|
+
if ap_angle > .1:
|
|
1423
|
+
ap_angle /= 1000 # now in rad
|
|
1410
1424
|
|
|
1411
1425
|
e0 = float(o_image.metadata['experiment']['acceleration_voltage'])
|
|
1412
1426
|
|
|
@@ -1438,19 +1452,16 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1438
1452
|
# de = 100
|
|
1439
1453
|
dest = 100
|
|
1440
1454
|
i = 0
|
|
1441
|
-
while abs(dest) > 0.
|
|
1455
|
+
while abs(dest) > 0.001: # or abs(de) > .025:
|
|
1442
1456
|
i += 1
|
|
1443
1457
|
error_old = np.sum(error.real)
|
|
1444
1458
|
est_old = est.copy()
|
|
1445
1459
|
error = source / np.real(fftpack.fftshift(fftpack.ifft2(fftpack.fft2(est) * response_ft)))
|
|
1446
1460
|
est = est * np.real(fftpack.fftshift(fftpack.ifft2(fftpack.fft2(error) * np.conjugate(response_ft))))
|
|
1447
|
-
|
|
1448
|
-
# est = np.real(fftpack.fftshift(fftpack.ifft2(fftpack.fft2(est)*fftpack.fftshift(aperture) )))
|
|
1449
|
-
|
|
1461
|
+
|
|
1450
1462
|
error_new = np.real(np.sum(np.power(error, 2))) - error_old
|
|
1451
1463
|
dest = np.sum(np.power((est - est_old).real, 2)) / np.sum(est) * 100
|
|
1452
|
-
|
|
1453
|
-
|
|
1464
|
+
|
|
1454
1465
|
if error_old != 0:
|
|
1455
1466
|
de = error_new / error_old * 1.0
|
|
1456
1467
|
else:
|
|
@@ -1466,10 +1477,10 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1466
1477
|
print('terminate')
|
|
1467
1478
|
progress.update(1)
|
|
1468
1479
|
progress.write(f"converged in {i} iterations")
|
|
1469
|
-
# progress.close()
|
|
1470
1480
|
print('\n Lucy-Richardson deconvolution converged in ' + str(i) + ' iterations')
|
|
1471
|
-
est2 = np.real(fftpack.ifft2(fftpack.fft2(est) * fftpack.fftshift(aperture)))
|
|
1481
|
+
est2 = np.real(fftpack.ifft2(fftpack.fft2(est) * fftpack.fftshift(aperture)))*image_mult
|
|
1472
1482
|
out_dataset = o_image.like_data(est2)
|
|
1473
1483
|
out_dataset.title = 'Lucy Richardson deconvolution'
|
|
1474
1484
|
out_dataset.data_type = 'image'
|
|
1475
1485
|
return out_dataset
|
|
1486
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pyTEMlib
|
|
3
|
-
Version: 0.2025.
|
|
3
|
+
Version: 0.2025.3.0
|
|
4
4
|
Summary: pyTEM: TEM Data Quantification library through a model-based approach
|
|
5
5
|
Home-page: https://pycroscopy.github.io/pyTEMlib/about.html
|
|
6
6
|
Author: Gerd Duscher
|
|
@@ -39,7 +39,7 @@ Requires-Dist: spglib
|
|
|
39
39
|
Requires-Dist: scikit-image
|
|
40
40
|
Requires-Dist: scikit-learn
|
|
41
41
|
Requires-Dist: pyNSID>=0.0.7
|
|
42
|
-
Requires-Dist: sidpy>=0.12.
|
|
42
|
+
Requires-Dist: sidpy>=0.12.7
|
|
43
43
|
Requires-Dist: SciFiReaders>=0.0.8
|
|
44
44
|
Dynamic: author
|
|
45
45
|
Dynamic: author-email
|
|
@@ -49,7 +49,7 @@ setuptools.setup(
|
|
|
49
49
|
author_email="gduscher@utk.edu",
|
|
50
50
|
|
|
51
51
|
install_requires=['scipy', 'numpy', 'pillow', 'ase', 'tqdm', 'plotly', 'pandas', 'requests', 'lxml', 'ipympl',
|
|
52
|
-
'spglib', 'scikit-image', 'scikit-learn', 'pyNSID>=0.0.7', 'sidpy>=0.12.
|
|
52
|
+
'spglib', 'scikit-image', 'scikit-learn', 'pyNSID>=0.0.7', 'sidpy>=0.12.7', 'SciFiReaders>=0.0.8'], # 'PyQt5> 1.0'],#
|
|
53
53
|
setup_requires=['pytest-runner'],
|
|
54
54
|
tests_require=['pytest'],
|
|
55
55
|
platforms=['Linux', 'Mac OSX', 'Windows 11/10'],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|