pyTEMlib 0.2025.3.0__py3-none-any.whl → 0.2025.4.1__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/atom_tools.py +2 -0
- pyTEMlib/file_tools.py +128 -3
- pyTEMlib/graph_tools.py +14 -4
- pyTEMlib/image_tools.py +53 -79
- pyTEMlib/probe_tools.py +200 -4
- pyTEMlib/version.py +2 -2
- {pytemlib-0.2025.3.0.dist-info → pytemlib-0.2025.4.1.dist-info}/METADATA +3 -2
- {pytemlib-0.2025.3.0.dist-info → pytemlib-0.2025.4.1.dist-info}/RECORD +12 -12
- {pytemlib-0.2025.3.0.dist-info → pytemlib-0.2025.4.1.dist-info}/WHEEL +1 -1
- {pytemlib-0.2025.3.0.dist-info → pytemlib-0.2025.4.1.dist-info}/entry_points.txt +0 -0
- {pytemlib-0.2025.3.0.dist-info → pytemlib-0.2025.4.1.dist-info/licenses}/LICENSE +0 -0
- {pytemlib-0.2025.3.0.dist-info → pytemlib-0.2025.4.1.dist-info}/top_level.txt +0 -0
pyTEMlib/atom_tools.py
CHANGED
pyTEMlib/file_tools.py
CHANGED
|
@@ -23,6 +23,9 @@ import ase.io
|
|
|
23
23
|
import SciFiReaders
|
|
24
24
|
import pyNSID
|
|
25
25
|
import sidpy
|
|
26
|
+
import sidpy
|
|
27
|
+
import xml.etree.ElementTree as ET
|
|
28
|
+
import collections
|
|
26
29
|
import ipywidgets as widgets
|
|
27
30
|
from IPython.display import display
|
|
28
31
|
|
|
@@ -875,7 +878,6 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
875
878
|
else:
|
|
876
879
|
if isinstance(datasets, dict):
|
|
877
880
|
dataset_dict = datasets
|
|
878
|
-
|
|
879
881
|
else:
|
|
880
882
|
dataset_dict = {}
|
|
881
883
|
for index, dataset in enumerate(datasets):
|
|
@@ -917,23 +919,28 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
917
919
|
dset.title = dset.title + '_SI'
|
|
918
920
|
dset = dset.T
|
|
919
921
|
dset.title = dset.title[11:]
|
|
922
|
+
dset.add_provenance('pyTEMlib', 'open_file', version=pyTEMlib.__version__, linked_data='emi_converted_by_hyperspy')
|
|
920
923
|
dataset_dict[f'Channel_{index:03d}'] = dset
|
|
924
|
+
|
|
921
925
|
return dataset_dict
|
|
922
926
|
except ImportError:
|
|
923
927
|
print('This file type needs hyperspy to be installed to be able to be read')
|
|
924
928
|
return
|
|
925
929
|
elif extension == '.emd':
|
|
926
930
|
reader = SciFiReaders.EMDReader(filename, sum_frames=sum_frames)
|
|
927
|
-
|
|
931
|
+
provenance = 'SciFiReader.EMDReader'
|
|
928
932
|
elif 'edax' in extension.lower():
|
|
929
933
|
if 'h5' in extension:
|
|
930
934
|
reader = SciFiReaders.EDAXReader(filename)
|
|
935
|
+
provenance = 'SciFiReader.EDAXReader'
|
|
931
936
|
|
|
932
937
|
elif extension in ['.ndata', '.h5']:
|
|
933
938
|
reader = SciFiReaders.NionReader(filename)
|
|
934
|
-
|
|
939
|
+
provenance = 'SciFiReader.NionReader'
|
|
940
|
+
|
|
935
941
|
elif extension in ['.mrc']:
|
|
936
942
|
reader = SciFiReaders.MRCReader(filename)
|
|
943
|
+
provenance = 'SciFiReader.MRCReader'
|
|
937
944
|
|
|
938
945
|
else:
|
|
939
946
|
raise NotImplementedError('extension not supported')
|
|
@@ -985,6 +992,7 @@ def open_file(filename=None, h5_group=None, write_hdf_file=False, sum_frames=Fa
|
|
|
985
992
|
if isinstance(dset, dict):
|
|
986
993
|
dataset_dict = dset
|
|
987
994
|
for dataset in dataset_dict.values():
|
|
995
|
+
dataset.add_provenance('pyTEMlib', 'open_file', version=pyTEMlib.__version__, linked_data = 'SciFiReader')
|
|
988
996
|
dataset.metadata['filename'] = filename
|
|
989
997
|
|
|
990
998
|
elif isinstance(dset, list):
|
|
@@ -1422,6 +1430,123 @@ def h5_get_crystal_structure(structure_group):
|
|
|
1422
1430
|
# ToDo: Read all of info dictionary
|
|
1423
1431
|
return atoms
|
|
1424
1432
|
|
|
1433
|
+
import collections
|
|
1434
|
+
def etree_to_dict(element):
|
|
1435
|
+
"""Recursively converts an ElementTree object into a nested dictionary."""
|
|
1436
|
+
d = {element.tag: {} if element.attrib else None}
|
|
1437
|
+
children = list(element)
|
|
1438
|
+
if children:
|
|
1439
|
+
dd = collections.defaultdict(list)
|
|
1440
|
+
for dc in map(etree_to_dict, children):
|
|
1441
|
+
for k, v in dc.items():
|
|
1442
|
+
dd[k].append(v)
|
|
1443
|
+
d = {element.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
|
|
1444
|
+
if element.attrib:
|
|
1445
|
+
d[element.tag].update(('@' + k, v) for k, v in element.attrib.items())
|
|
1446
|
+
if element.text:
|
|
1447
|
+
text = element.text.strip()
|
|
1448
|
+
if children or element.attrib:
|
|
1449
|
+
if text:
|
|
1450
|
+
d[element.tag]['#text'] = text
|
|
1451
|
+
else:
|
|
1452
|
+
d[element.tag] = text
|
|
1453
|
+
return d
|
|
1454
|
+
|
|
1455
|
+
def read_adorned_metadata(image):
|
|
1456
|
+
xml_str = image.metadata.metadata_as_xml
|
|
1457
|
+
root = ET.fromstring(xml_str)
|
|
1458
|
+
metadata_dict = etree_to_dict(root)
|
|
1459
|
+
detector = 'detector'
|
|
1460
|
+
|
|
1461
|
+
if 'Detectors' in metadata_dict['Metadata']:
|
|
1462
|
+
if 'ScanningDetector' in metadata_dict['Metadata']['Detectors']:
|
|
1463
|
+
detector = metadata_dict['Metadata']['Detectors']['ScanningDetector']['DetectorName']
|
|
1464
|
+
elif 'ImagingDetector' in metadata_dict['Metadata']['Detectors']:
|
|
1465
|
+
detector = metadata_dict['Metadata']['Detectors']['ImagingDetector']['DetectorName']
|
|
1466
|
+
segment = ''
|
|
1467
|
+
if 'CustomPropertyGroup' in metadata_dict['Metadata']:
|
|
1468
|
+
if 'CustomProperties' in metadata_dict['Metadata']['CustomPropertyGroup']:
|
|
1469
|
+
for list_item in metadata_dict['Metadata']['CustomPropertyGroup']['CustomProperties']:
|
|
1470
|
+
|
|
1471
|
+
if isinstance(list_item, dict):
|
|
1472
|
+
for key in list_item:
|
|
1473
|
+
for item in list_item[key]:
|
|
1474
|
+
if '@name' in item:
|
|
1475
|
+
if item['@name']== 'DetectorCommercialName':
|
|
1476
|
+
detector = item['@value']
|
|
1477
|
+
if item['@name']== 'StemSegment':
|
|
1478
|
+
segment = '_'+item['@value']
|
|
1479
|
+
return detector+segment, metadata_dict['Metadata']
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
def get_metadata_from_adorned(ds):
|
|
1483
|
+
ds.metadata['experiment']= {}
|
|
1484
|
+
if 'Optics' in ds.original_metadata:
|
|
1485
|
+
if 'LastMeasuredScreenCurrent' in ds.original_metadata['Optics']:
|
|
1486
|
+
ds.metadata['experiment']['current'] = float(ds.original_metadata['Optics']['LastMeasuredScreenCurrent'])
|
|
1487
|
+
if 'ConvergenceAngle' in ds.original_metadata['Optics']:
|
|
1488
|
+
ds.metadata['experiment']['convergence_angle'] = float(ds.original_metadata['Optics']['ConvergenceAngle'])
|
|
1489
|
+
if 'AccelerationVoltage' in ds.original_metadata['Optics']:
|
|
1490
|
+
ds.metadata['experiment']['acceleration_voltage'] = float(ds.original_metadata['Optics']['AccelerationVoltage'])
|
|
1491
|
+
if 'SpotIndex' in ds.original_metadata['Optics']:
|
|
1492
|
+
ds.metadata['experiment']['spot_size'] = ds.original_metadata['Optics']['SpotIndex']
|
|
1493
|
+
if' StagesSettings' in ds.original_metadata:
|
|
1494
|
+
if 'StagePosition' in ds.original_metadata['StagesSettings']:
|
|
1495
|
+
ds.metadata['experiment']['stage_position'] = ds.original_metadata['StagesSettings']['StagePosition']
|
|
1496
|
+
if 'Detectors' in ds.original_metadata:
|
|
1497
|
+
if 'ScanningDetector' in ds.original_metadata['Detectors']:
|
|
1498
|
+
ds.metadata['experiment']['detector'] = ds.original_metadata['Detectors']['ScanningDetector']['DetectorName']
|
|
1499
|
+
elif 'ImagingDetector' in ds.original_metadata['Detectors']:
|
|
1500
|
+
ds.metadata['experiment']['detector'] = ds.original_metadata['Detectors']['ImagingDetector']['DetectorName']
|
|
1501
|
+
ds.metadata['experiment']['exposure_time'] = ds.original_metadata['Detectors']['ImagingDetector']['ExposureTime']
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
def adorned_to_sidpy(images):
|
|
1505
|
+
"""
|
|
1506
|
+
Convert a list of adorned images to a dictionary of Sidpy datasets.
|
|
1507
|
+
Each dataset is created from the image data and adorned metadata.
|
|
1508
|
+
The datasets are stored in a dictionary with keys 'Channel_000', 'Channel_001', etc.
|
|
1509
|
+
The dimensions of the datasets are set based on the image data shape and pixel sizes.
|
|
1510
|
+
The original metadata is also stored in the dataset.
|
|
1511
|
+
Args:
|
|
1512
|
+
images (list or object): A list of adorned images or a single adorned image.
|
|
1513
|
+
Returns:
|
|
1514
|
+
dict: A dictionary of Sidpy datasets, where each dataset corresponds to an image.
|
|
1515
|
+
"""
|
|
1516
|
+
|
|
1517
|
+
data_sets = {}
|
|
1518
|
+
if not isinstance(images, list):
|
|
1519
|
+
images = [images]
|
|
1520
|
+
for index, image in enumerate(images):
|
|
1521
|
+
name, original_metadata = read_adorned_metadata(image)
|
|
1522
|
+
data_sets[f'Channel_{index:03}'] = sidpy.Dataset.from_array(image.data.T, title=name)
|
|
1523
|
+
ds = data_sets[f'Channel_{index:03}']
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
ds.original_metadata = original_metadata
|
|
1527
|
+
|
|
1528
|
+
pixel_size_x_m = float(ds.original_metadata['BinaryResult']['PixelSize']['X']['#text'])
|
|
1529
|
+
pixel_size_y_m = float(ds.original_metadata['BinaryResult']['PixelSize']['Y']['#text'])
|
|
1530
|
+
pixel_size_x_nm = pixel_size_x_m * 1e9
|
|
1531
|
+
pixel_size_y_nm = pixel_size_y_m * 1e9
|
|
1532
|
+
if image.data.ndim == 3:
|
|
1533
|
+
ds.data_type = 'image_stack'
|
|
1534
|
+
ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]),
|
|
1535
|
+
name='frame', units='frame', quantity='Length', dimension_type='temporal'))
|
|
1536
|
+
ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_y_nm,
|
|
1537
|
+
name='y', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1538
|
+
ds.set_dimension(2, sidpy.Dimension(np.arange(image.data.shape[2]) * pixel_size_x_nm,
|
|
1539
|
+
name='x', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1540
|
+
else:
|
|
1541
|
+
ds.data_type = 'image'
|
|
1542
|
+
ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]) * pixel_size_y_nm,
|
|
1543
|
+
name='y', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1544
|
+
ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_x_nm,
|
|
1545
|
+
name='x', units='nm', quantity='Length', dimension_type='spatial'))
|
|
1546
|
+
|
|
1547
|
+
get_metadata_from_adorned(ds)
|
|
1548
|
+
return data_sets
|
|
1549
|
+
|
|
1425
1550
|
|
|
1426
1551
|
###############################################
|
|
1427
1552
|
# Support old pyTEM file format
|
pyTEMlib/graph_tools.py
CHANGED
|
@@ -808,15 +808,18 @@ def breadth_first_search2(graph, initial, projected_crystal):
|
|
|
808
808
|
a_lattice_vector = projection_tags['lattice_vector']['a']
|
|
809
809
|
b_lattice_vector = projection_tags['lattice_vector']['b']
|
|
810
810
|
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
811
|
-
near =
|
|
811
|
+
near = []
|
|
812
812
|
else:
|
|
813
813
|
# get lattice vectors to hopp along through graph
|
|
814
814
|
projected_unit_cell = projected_crystal.cell[:2, :2]
|
|
815
815
|
a_lattice_vector = projected_unit_cell[0]
|
|
816
816
|
b_lattice_vector = projected_unit_cell[1]
|
|
817
817
|
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
818
|
-
near =
|
|
818
|
+
near = projection_tags['near_base'] # all nearest atoms
|
|
819
819
|
# get k next nearest neighbours for each node
|
|
820
|
+
main = np.array([a_lattice_vector, -a_lattice_vector, b_lattice_vector, -b_lattice_vector]) # vectors of unit cell
|
|
821
|
+
near = np.append(main, near, axis=0)
|
|
822
|
+
|
|
820
823
|
neighbour_tree = scipy.spatial.KDTree(graph)
|
|
821
824
|
distances, indices = neighbour_tree.query(graph, # let's get all neighbours
|
|
822
825
|
k=50) # projection_tags['number_of_nearest_neighbours']*2 + 1)
|
|
@@ -852,10 +855,17 @@ def breadth_first_search2(graph, initial, projected_crystal):
|
|
|
852
855
|
|
|
853
856
|
|
|
854
857
|
|
|
855
|
-
def
|
|
858
|
+
def breadth_first_search_felxible(graph, initial, lattice_parameter, tolerance=1):
|
|
856
859
|
""" breadth first search of atoms viewed as a graph
|
|
857
|
-
|
|
860
|
+
This is a rotational invariant search of atoms in a lattice, and returns the angles of unit cells.
|
|
861
|
+
We only use the ideal lattice parameter to determine the lattice.
|
|
858
862
|
"""
|
|
863
|
+
if isinstance(lattice_parameter, ase.Atoms):
|
|
864
|
+
lattice_parameter = lattice_parameter.cell.lengths()[:2]
|
|
865
|
+
elif isinstance(lattice_parameter, float):
|
|
866
|
+
lattice_parameter = [lattice_parameter]
|
|
867
|
+
lattice_parameter = np.array(lattice_parameter)
|
|
868
|
+
|
|
859
869
|
neighbour_tree = scipy.spatial.KDTree(graph)
|
|
860
870
|
distances, indices = neighbour_tree.query(graph, # let's get all neighbours
|
|
861
871
|
k=50) # projection_tags['number_of_nearest_neighbours']*2 + 1)
|
pyTEMlib/image_tools.py
CHANGED
|
@@ -182,24 +182,21 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
182
182
|
assert isinstance(dset, sidpy.Dataset), 'Expected a sidpy Dataset'
|
|
183
183
|
|
|
184
184
|
selection = []
|
|
185
|
-
|
|
186
|
-
# image_dim = get_image_dims(sidpy.DimensionTypes.SPATIAL)
|
|
187
|
-
|
|
185
|
+
image_dims = pyTEMlib.sidpy_tools.get_image_dims(dset)
|
|
188
186
|
if dset.data_type == sidpy.DataType.IMAGE_STACK:
|
|
189
|
-
image_dim = dset.get_image_dims()
|
|
190
187
|
stack_dim = dset.get_dimensions_by_type('TEMPORAL')
|
|
191
188
|
|
|
192
|
-
if len(
|
|
189
|
+
if len(image_dims) != 2:
|
|
193
190
|
raise ValueError('need at least two SPATIAL dimension for an image stack')
|
|
194
191
|
|
|
195
192
|
for i in range(dset.ndim):
|
|
196
|
-
if i in
|
|
193
|
+
if i in image_dims:
|
|
197
194
|
selection.append(slice(None))
|
|
198
195
|
if len(stack_dim) == 0:
|
|
199
|
-
|
|
196
|
+
stack_dims = i
|
|
200
197
|
selection.append(slice(None))
|
|
201
198
|
elif i in stack_dim:
|
|
202
|
-
|
|
199
|
+
stack_dims = i
|
|
203
200
|
selection.append(slice(None))
|
|
204
201
|
else:
|
|
205
202
|
selection.append(slice(0, 1))
|
|
@@ -229,7 +226,6 @@ def fourier_transform(dset: sidpy.Dataset) -> sidpy.Dataset:
|
|
|
229
226
|
|
|
230
227
|
fft_dset.set_dimension(0, sidpy.Dimension(np.fft.fftshift(np.fft.fftfreq(new_image.shape[0],
|
|
231
228
|
d=dset.x[1]-dset.x[0])),
|
|
232
|
-
|
|
233
229
|
name='u', units=units_x, dimension_type='RECIPROCAL',
|
|
234
230
|
quantity='reciprocal_length'))
|
|
235
231
|
fft_dset.set_dimension(1, sidpy.Dimension(np.fft.fftshift(np.fft.fftfreq(new_image.shape[1],
|
|
@@ -599,13 +595,13 @@ def demon_registration(dataset, verbose=False):
|
|
|
599
595
|
demon_registered.title = 'Non-Rigid Registration'
|
|
600
596
|
demon_registered.source = dataset.title
|
|
601
597
|
|
|
602
|
-
demon_registered.metadata =
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
598
|
+
demon_registered.metadata =dataset.metadata.copy()
|
|
599
|
+
if 'analysis' not in demon_registered.metadata:
|
|
600
|
+
demon_registered.metadata['analysis'] = {}
|
|
601
|
+
demon_registered.metadata['analysis']['non_rigid_demon_registration'] = {'package': 'simpleITK',
|
|
602
|
+
'method': 'DiscreteGaussian',
|
|
603
|
+
'variance': 2,
|
|
604
|
+
'input_dataset': dataset.source}
|
|
609
605
|
demon_registered.data_type = 'IMAGE_STACK'
|
|
610
606
|
return demon_registered
|
|
611
607
|
|
|
@@ -613,7 +609,7 @@ def demon_registration(dataset, verbose=False):
|
|
|
613
609
|
###############################
|
|
614
610
|
# Rigid Registration New 05/09/2020
|
|
615
611
|
|
|
616
|
-
def rigid_registration(dataset,
|
|
612
|
+
def rigid_registration(dataset, normalization=None):
|
|
617
613
|
"""
|
|
618
614
|
Rigid registration of image stack with pixel accuracy
|
|
619
615
|
|
|
@@ -635,51 +631,32 @@ def rigid_registration(dataset, sub_pixel=True):
|
|
|
635
631
|
raise TypeError('We need a sidpy.Dataset')
|
|
636
632
|
if dataset.data_type.name != 'IMAGE_STACK':
|
|
637
633
|
raise TypeError('Registration makes only sense for an image stack')
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
print('need two spatial dimensions')
|
|
653
|
-
if len(frame_dim) != 1:
|
|
654
|
-
print('need one frame dimensions')
|
|
655
|
-
|
|
656
|
-
nopix = dataset.shape[spatial_dim[0]]
|
|
657
|
-
nopiy = dataset.shape[spatial_dim[1]]
|
|
658
|
-
nimages = dataset.shape[frame_dim[0]]
|
|
659
|
-
|
|
660
|
-
print('Stack contains ', nimages, ' images, each with', nopix, ' pixels in x-direction and ', nopiy,
|
|
661
|
-
' pixels in y-direction')
|
|
662
|
-
|
|
663
|
-
fixed = dataset[tuple(selection)].squeeze().compute()
|
|
664
|
-
fft_fixed = np.fft.fft2(fixed)
|
|
634
|
+
|
|
635
|
+
if isinstance (normalization, str):
|
|
636
|
+
if normalization.lower() != 'phase':
|
|
637
|
+
nomralization = None
|
|
638
|
+
else:
|
|
639
|
+
normalization = None
|
|
640
|
+
|
|
641
|
+
if dataset.get_dimensions_by_type('TEMPORAL')[0] != 0:
|
|
642
|
+
raise TypeError('Image stack does not have correct frame dimension')
|
|
643
|
+
|
|
644
|
+
stack_dim = dataset.get_dimensions_by_type('TEMPORAL', return_axis=True)[0]
|
|
645
|
+
image_dim = dataset.get_image_dims(return_axis=True)
|
|
646
|
+
if len(image_dim) != 2:
|
|
647
|
+
raise ValueError('need at least two SPATIAL dimension for an image stack')
|
|
665
648
|
|
|
666
649
|
relative_drift = [[0., 0.]]
|
|
667
|
-
|
|
668
|
-
for i in
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
cc_image = np.fft.fftshift(np.fft.ifft2(image_product))
|
|
678
|
-
shift = np.array(ndimage.maximum_position(cc_image.real))-cc_image.shape[0]/2
|
|
679
|
-
fft_fixed = fft_moving
|
|
680
|
-
relative_drift.append(shift)
|
|
681
|
-
rig_reg, drift = rig_reg_drift(dataset, relative_drift)
|
|
682
|
-
crop_reg, input_crop = crop_image_stack(rig_reg, drift)
|
|
650
|
+
im1 = np.fft.fft2(np.array(dataset[0]))
|
|
651
|
+
for i in range(1, len(stack_dim)):
|
|
652
|
+
im2 = np.fft.fft2(np.array(dataset[i]))
|
|
653
|
+
shift, error, _ = skimage.registration.phase_cross_correlation(im1, im2, normalization=normalization, space='fourier')
|
|
654
|
+
print(shift)
|
|
655
|
+
im1 = im2.copy()
|
|
656
|
+
relative_drift.append(shift)
|
|
657
|
+
|
|
658
|
+
rig_reg, drift = pyTEMlib.image_tools.rig_reg_drift(dataset, relative_drift)
|
|
659
|
+
crop_reg, input_crop = pyTEMlib.image_tools.crop_image_stack(rig_reg, drift)
|
|
683
660
|
|
|
684
661
|
rigid_registered = sidpy.Dataset.from_array(crop_reg,
|
|
685
662
|
title='Rigid Registration',
|
|
@@ -688,21 +665,21 @@ def rigid_registration(dataset, sub_pixel=True):
|
|
|
688
665
|
units=dataset.units)
|
|
689
666
|
rigid_registered.title = 'Rigid_Registration'
|
|
690
667
|
rigid_registered.source = dataset.title
|
|
691
|
-
rigid_registered.metadata = {'
|
|
692
|
-
'input_crop': input_crop, 'input_shape': dataset.shape[1:]}
|
|
668
|
+
rigid_registered.metadata['analysis'] = {'rigid_registration': {'drift': drift,
|
|
669
|
+
'input_crop': input_crop, 'input_shape': dataset.shape[1:]}}
|
|
693
670
|
rigid_registered.metadata['experiment'] = dataset.metadata['experiment'].copy()
|
|
694
671
|
rigid_registered.set_dimension(0, sidpy.Dimension(np.arange(rigid_registered.shape[0]),
|
|
695
672
|
name='frame', units='frame', quantity='time',
|
|
696
673
|
dimension_type='temporal'))
|
|
697
674
|
|
|
698
|
-
array_x =
|
|
699
|
-
rigid_registered.set_dimension(1, sidpy.Dimension(array_x,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
array_y =
|
|
703
|
-
rigid_registered.set_dimension(2, sidpy.Dimension(array_y,
|
|
704
|
-
|
|
705
|
-
|
|
675
|
+
array_x = image_dim[0].values[input_crop[0]:input_crop[1]]
|
|
676
|
+
rigid_registered.set_dimension(1, sidpy.Dimension(array_x, name='x',
|
|
677
|
+
units='nm', quantity='Length',
|
|
678
|
+
dimension_type='spatial'))
|
|
679
|
+
array_y =image_dim[1].values[input_crop[2]:input_crop[3]]
|
|
680
|
+
rigid_registered.set_dimension(2, sidpy.Dimension(array_y, name='y',
|
|
681
|
+
units='nm', quantity='Length',
|
|
682
|
+
dimension_type='spatial'))
|
|
706
683
|
rigid_registered.data_type = 'IMAGE_STACK'
|
|
707
684
|
return rigid_registered.rechunk({0: 'auto', 1: -1, 2: -1})
|
|
708
685
|
|
|
@@ -778,13 +755,12 @@ def crop_image_stack(rig_reg, drift):
|
|
|
778
755
|
-------
|
|
779
756
|
numpy array
|
|
780
757
|
"""
|
|
758
|
+
xpmax = int(rig_reg.shape[1] - -np.floor(np.min(np.array(drift)[:, 0])))
|
|
759
|
+
xpmin = int(np.ceil(np.max(np.array(drift)[:, 0])))
|
|
760
|
+
ypmax = int(rig_reg.shape[1] - -np.floor(np.min(np.array(drift)[:, 1])))
|
|
761
|
+
ypmin = int(np.ceil(np.max(np.array(drift)[:, 1])))
|
|
781
762
|
|
|
782
|
-
xpmin
|
|
783
|
-
xpmax = int(rig_reg.shape[1] - np.ceil(np.max(np.array(drift)[:, 0])))
|
|
784
|
-
ypmin = int(-np.floor(np.min(np.array(drift)[:, 1])))
|
|
785
|
-
ypmax = int(rig_reg.shape[2] - np.ceil(np.max(np.array(drift)[:, 1])))
|
|
786
|
-
|
|
787
|
-
return rig_reg[:, xpmin:xpmax, ypmin:ypmax], [xpmin, xpmax, ypmin, ypmax]
|
|
763
|
+
return rig_reg[:, xpmin:xpmax, ypmin:ypmax:], [xpmin, xpmax, ypmin, ypmax]
|
|
788
764
|
|
|
789
765
|
|
|
790
766
|
class ImageWithLineProfile:
|
|
@@ -1416,8 +1392,6 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1416
1392
|
|
|
1417
1393
|
response_ft = fftpack.fft2(probe_c)
|
|
1418
1394
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
1395
|
ap_angle = o_image.metadata['experiment']['convergence_angle']
|
|
1422
1396
|
if ap_angle > .1:
|
|
1423
1397
|
ap_angle /= 1000 # now in rad
|
|
@@ -1452,7 +1426,7 @@ def decon_lr(o_image, probe, verbose=False):
|
|
|
1452
1426
|
# de = 100
|
|
1453
1427
|
dest = 100
|
|
1454
1428
|
i = 0
|
|
1455
|
-
while abs(dest) > 0.
|
|
1429
|
+
while abs(dest) > 0.0001: # or abs(de) > .025:
|
|
1456
1430
|
i += 1
|
|
1457
1431
|
error_old = np.sum(error.real)
|
|
1458
1432
|
est_old = est.copy()
|
pyTEMlib/probe_tools.py
CHANGED
|
@@ -107,6 +107,7 @@ def get_chi(ab, size_x, size_y, verbose=False):
|
|
|
107
107
|
return chi, aperture
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
|
|
110
111
|
def print_aberrations(ab):
|
|
111
112
|
from IPython.display import HTML, display
|
|
112
113
|
output = '<html><body>'
|
|
@@ -133,6 +134,203 @@ def print_aberrations(ab):
|
|
|
133
134
|
display(HTML(output))
|
|
134
135
|
|
|
135
136
|
|
|
137
|
+
|
|
138
|
+
def print_aberrations_polar(ab):
|
|
139
|
+
from IPython.display import HTML, display
|
|
140
|
+
|
|
141
|
+
ab['C12_r'], ab['C12_phi'] = cart2pol(ab['C12a'], ab['C12b'])
|
|
142
|
+
ab['C21_r'], ab['C21_phi'] = cart2pol(ab['C21a'], ab['C21b'])
|
|
143
|
+
ab['C23_r'], ab['C23_phi'] = cart2pol(ab['C23a'], ab['C23b'])
|
|
144
|
+
ab['C32_r'], ab['C32_phi'] = cart2pol(ab['C32a'], ab['C32b'])
|
|
145
|
+
ab['C34_r'], ab['C34_phi'] = cart2pol(ab['C34a'], ab['C34b'])
|
|
146
|
+
ab['C41_r'], ab['C41_phi'] = cart2pol(ab['C41a'], ab['C41b'])
|
|
147
|
+
ab['C43_r'], ab['C43_phi'] = cart2pol(ab['C43a'], ab['C43b'])
|
|
148
|
+
ab['C45_r'], ab['C45_phi'] = cart2pol(ab['C45a'], ab['C45b'])
|
|
149
|
+
ab['C52_r'], ab['C52_phi'] = cart2pol(ab['C52a'], ab['C52b'])
|
|
150
|
+
ab['C54_r'], ab['C54_phi'] = cart2pol(ab['C54a'], ab['C54b'])
|
|
151
|
+
ab['C56_r'], ab['C56_phi'] = cart2pol(ab['C56a'], ab['C56b'])
|
|
152
|
+
|
|
153
|
+
output = '<html><body>'
|
|
154
|
+
output += f"Aberrations [nm] for acceleration voltage: {ab['acceleration_voltage'] / 1e3:.0f} kV"
|
|
155
|
+
output += '<table>'
|
|
156
|
+
output += f"<tr><td> C10 </td><td> {ab['C10']:.1f} </tr>"
|
|
157
|
+
output += f"<tr><td> C12(A1): r </td><td> {ab['C12_r']:20.1f} <td> φ </td><td> {ab['C12_phi']:20.1f} </tr>"
|
|
158
|
+
output += f"<tr><td> C21a (B2): r</td><td> {ab['C21_r']:20.1f} <td> φ </td><td> {ab['C21_phi']:20.1f} "
|
|
159
|
+
output += f" <td> C23a (A2) </td><td> {ab['C23_r']:20.1f} <td> φ </td><td> {ab['C23_phi']:20.1f} </tr>"
|
|
160
|
+
output += f"<tr><td> C30 </td><td> {ab['C30']:.1f} </tr>"
|
|
161
|
+
output += f"<tr><td> C32 (S3) </td><td> {ab['C32_r']:20.1f} <td> φ </td><td> {ab['C32_phi']:20.1f} "
|
|
162
|
+
output += f"<td> C34a (A3) </td><td> {ab['C34a']:20.1f} <td> φ </td><td> {ab['C34_phi']:20.1f} </tr>"
|
|
163
|
+
output += f"<tr><td> C41 (B4) </td><td> {ab['C41_r']:.3g} <td> φ </td><td> {ab['C41_phi']:20.1f} "
|
|
164
|
+
output += f" <td> C43 (D4) </td><td> {ab['C43_r']:.3g} <td> φ (D4) </td><td> {ab['C43_phi']:20.1f} "
|
|
165
|
+
output += f" <td> C45 (A4) </td><td> {ab['C45_r']:.3g} <td> φ (A4)</td><td> {ab['C45_phi']:20.1f} </tr>"
|
|
166
|
+
output += f"<tr><td> C50 </td><td> {ab['C50']:.3g} </tr>"
|
|
167
|
+
output += f"<tr><td> C52 </td><td> {ab['C52a']:.3g} <td> φ </td><td> {ab['C52_phi']:20.1f} "
|
|
168
|
+
output += f"<td> C54 </td><td> {ab['C54_r']:.3g} <td> C54 φ </td><td> {ab['C54_phi']:.1f} "
|
|
169
|
+
output += f"<td> C56 </td><td> {ab['C56_r']:.3g} <td> C56 </td><td> {ab['C56_phi']:.1f} </tr>"
|
|
170
|
+
output += f"<tr><td> Cc </td><td> {ab['Cc']:.3g} </tr>"
|
|
171
|
+
|
|
172
|
+
output += '</table></body></html>'
|
|
173
|
+
|
|
174
|
+
display(HTML(output))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def pol2cart(rho, theta):
|
|
178
|
+
x = rho * np.cos(theta)
|
|
179
|
+
y = rho * np.sin(theta)
|
|
180
|
+
return x, y
|
|
181
|
+
|
|
182
|
+
def ceos_to_nion(ab):
|
|
183
|
+
aberrations = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
|
|
184
|
+
'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
|
|
185
|
+
'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
|
|
186
|
+
'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0., 'C70': 0.}
|
|
187
|
+
aberrations['acceleration_voltage'] = 200000
|
|
188
|
+
for key in ab.keys():
|
|
189
|
+
if key == 'C1':
|
|
190
|
+
aberrations['C10'] = ab['C10']
|
|
191
|
+
elif key == 'A1-a':
|
|
192
|
+
x, y = pol2cart(ab['A1-a'], ab['A1-p'])
|
|
193
|
+
aberrations['C12a'] = x
|
|
194
|
+
aberrations['C12b'] = y
|
|
195
|
+
elif key == 'B2-a':
|
|
196
|
+
x, y = pol2cart(ab['B2-a'], ab['B2-p'])
|
|
197
|
+
aberrations['C21a'] = 3 * x
|
|
198
|
+
aberrations['C21b'] = 3 * y
|
|
199
|
+
elif key == 'A2-a':
|
|
200
|
+
x, y = pol2cart(ab['A2-a'], ab['A2-p'])
|
|
201
|
+
aberrations['C23a'] = x
|
|
202
|
+
aberrations['C23b'] = y
|
|
203
|
+
elif key == 'C3':
|
|
204
|
+
aberrations['C30'] = ab['C3']
|
|
205
|
+
elif key == 'S3-a':
|
|
206
|
+
x, y = pol2cart(ab['S3-a'], ab['S3-p'])
|
|
207
|
+
aberrations['C32a'] = 4 * x
|
|
208
|
+
aberrations['C32b'] = 4 * y
|
|
209
|
+
elif key == 'A3-a':
|
|
210
|
+
x, y = pol2cart(ab['A3-a'], ab['A3-p'])
|
|
211
|
+
aberrations['C34a'] = x
|
|
212
|
+
aberrations['C34b'] = y
|
|
213
|
+
elif key == 'B4-a':
|
|
214
|
+
x, y = pol2cart(ab['B4-a'], ab['B4-p'])
|
|
215
|
+
aberrations['C41a'] = 4 * x
|
|
216
|
+
aberrations['C41b'] = 4 * y
|
|
217
|
+
elif key == 'D4-a':
|
|
218
|
+
x, y = pol2cart(ab['D4-a'], ab['D4-p'])
|
|
219
|
+
aberrations['C43a'] = 4 * x
|
|
220
|
+
aberrations['C43b'] = 4 * y
|
|
221
|
+
elif key == 'A4-a':
|
|
222
|
+
x, y = pol2cart(ab['A4-a'], ab['A4-p'])
|
|
223
|
+
aberrations['C45a'] = x
|
|
224
|
+
aberrations['C45b'] = y
|
|
225
|
+
elif key == 'C5':
|
|
226
|
+
aberrations['C50'] = ab['C5']
|
|
227
|
+
elif key == 'A5-a':
|
|
228
|
+
x, y = pol2cart(ab['A5-a'], ab['A5-p'])
|
|
229
|
+
aberrations['C56a'] = x
|
|
230
|
+
aberrations['C56b'] = y
|
|
231
|
+
return aberrations
|
|
232
|
+
|
|
233
|
+
def ceos_carth_to_nion(ab):
|
|
234
|
+
aberrations = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
|
|
235
|
+
'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
|
|
236
|
+
'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
|
|
237
|
+
'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0., 'C70': 0.}
|
|
238
|
+
aberrations['acceleration_voltage'] = 200000
|
|
239
|
+
for key in ab.keys():
|
|
240
|
+
if key == 'C1':
|
|
241
|
+
aberrations['C10'] = ab['C1'][0]*1e9
|
|
242
|
+
elif key == 'A1':
|
|
243
|
+
aberrations['C12a'] = ab['A1'][0]*1e9
|
|
244
|
+
aberrations['C12b'] = ab['A1'][1]*1e9
|
|
245
|
+
elif key == 'B2':
|
|
246
|
+
print('B2', ab['B2'])
|
|
247
|
+
aberrations['C21a'] = 3 * ab['B2'][0]*1e9
|
|
248
|
+
aberrations['C21b'] = 3 * ab['B2'][1]*1e9
|
|
249
|
+
elif key == 'A2':
|
|
250
|
+
aberrations['C23a'] = ab['A2'][0]*1e9
|
|
251
|
+
aberrations['C23b'] = ab['A2'][1]*1e9
|
|
252
|
+
elif key == 'C3':
|
|
253
|
+
aberrations['C30'] = ab['C3'][0]*1e9
|
|
254
|
+
elif key == 'S3':
|
|
255
|
+
aberrations['C32a'] = 4 * ab['S3'][0]*1e9
|
|
256
|
+
aberrations['C32b'] = 4 * ab['S3'][1]*1e9
|
|
257
|
+
elif key == 'A3':
|
|
258
|
+
aberrations['C34a'] = ab['A3'][0]*1e9
|
|
259
|
+
aberrations['C34b'] = ab['A3'][1]*1e9
|
|
260
|
+
elif key == 'B4':
|
|
261
|
+
aberrations['C41a'] = 4 * ab['B4'][0]*1e9
|
|
262
|
+
aberrations['C41b'] = 4 * ab['B4'][1]*1e9
|
|
263
|
+
elif key == 'D4':
|
|
264
|
+
aberrations['C43a'] = 4 * ab['D4'][0]*1e9
|
|
265
|
+
aberrations['C43b'] = 4 * ab['D4'][1]*1e9
|
|
266
|
+
elif key == 'A4':
|
|
267
|
+
aberrations['C45a'] = ab['A4'][0]*1e9
|
|
268
|
+
aberrations['C45b'] = ab['A4'][1]*1e9
|
|
269
|
+
elif key == 'C5':
|
|
270
|
+
aberrations['C50'] = ab['C5'][0]*1e9
|
|
271
|
+
elif key == 'A5':
|
|
272
|
+
aberrations['C56a'] = ab['A5'][0]*1e9
|
|
273
|
+
aberrations['C56b'] = ab['A5'][1]*1e9
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
return aberrations
|
|
277
|
+
|
|
278
|
+
def cart2pol(x, y):
|
|
279
|
+
theta = np.arctan2(y, x)
|
|
280
|
+
rho = np.hypot(x, y)
|
|
281
|
+
return theta, rho
|
|
282
|
+
|
|
283
|
+
def nion_to_ceos(ab):
|
|
284
|
+
aberrations = {'C1': 0, 'A1-a': 0, 'A1-b': 0, 'B2-a': 0, 'B2-p': 0, 'A2-a': 0, 'A2-p': 0, 'C3': 0.,
|
|
285
|
+
'S3-a': 0., 'S3-p': -0., 'A3-a': 0., 'A3-p': 0., 'B4-a': 0., 'B4-p': -0., 'D4-a': 0.,
|
|
286
|
+
'D4-p': -0., 'A4-s': -0., 'A4-p': -0., 'C5': 0., 'A5-a': -0., 'A5-p': 0.}
|
|
287
|
+
aberrations['acceleration_voltage'] = 200000
|
|
288
|
+
for key in ab.keys():
|
|
289
|
+
if key == 'C10':
|
|
290
|
+
aberrations['C1'] = ab['C10']
|
|
291
|
+
elif key == 'C12a':
|
|
292
|
+
r, p = cart2pol(ab['C12a'], ab['C12b'])
|
|
293
|
+
aberrations['A1-a'] = r
|
|
294
|
+
aberrations['A1-p'] = p
|
|
295
|
+
elif key == 'C21a':
|
|
296
|
+
r, p = cart2pol(ab['C21a'], ab['C21b'])
|
|
297
|
+
aberrations['B2-a'] = r/3
|
|
298
|
+
aberrations['B2-p'] = p
|
|
299
|
+
elif key == 'C23a':
|
|
300
|
+
r, p = cart2pol(ab['C23a'], ab['C23b'])
|
|
301
|
+
aberrations['A2-a'] = r
|
|
302
|
+
aberrations['A2-p'] = p
|
|
303
|
+
elif key == 'C30':
|
|
304
|
+
aberrations['C3'] = ab['C30']
|
|
305
|
+
elif key == 'C32a':
|
|
306
|
+
r, p = cart2pol(ab['C32a'], ab['C32b'])
|
|
307
|
+
aberrations['S3-a'] = r/4
|
|
308
|
+
aberrations['S3-p'] = p
|
|
309
|
+
elif key == 'C34a':
|
|
310
|
+
r, p = cart2pol(ab['C34a'], ab['C34b'])
|
|
311
|
+
aberrations['A3-a'] = r
|
|
312
|
+
aberrations['A3-p'] = p
|
|
313
|
+
elif key == 'C41a':
|
|
314
|
+
r, p = cart2pol(ab['C41a'], ab['C41b'])
|
|
315
|
+
aberrations['B4-a'] = r/4
|
|
316
|
+
aberrations['B4-p'] = p
|
|
317
|
+
elif key == 'C43a':
|
|
318
|
+
r, p = cart2pol(ab['C43a'], ab['C43b'])
|
|
319
|
+
aberrations['D4-a'] = r/4
|
|
320
|
+
aberrations['D4-p'] = p
|
|
321
|
+
elif key == 'C31a':
|
|
322
|
+
r, p = cart2pol(ab['C41a'], ab['C41b'])
|
|
323
|
+
aberrations['A4-a'] = r
|
|
324
|
+
aberrations['A4-p'] = p
|
|
325
|
+
elif key == 'C50':
|
|
326
|
+
aberrations['C5'] = ab['C50']
|
|
327
|
+
elif key == 'C56a':
|
|
328
|
+
r, p = cart2pol(ab['C56a'], ab['C56b'])
|
|
329
|
+
aberrations['A5-a'] = r
|
|
330
|
+
aberrations['A5-p'] = p
|
|
331
|
+
|
|
332
|
+
return aberrations
|
|
333
|
+
|
|
136
334
|
def get_ronchigram(size, ab, scale='mrad'):
|
|
137
335
|
""" Get Ronchigram
|
|
138
336
|
|
|
@@ -447,15 +645,13 @@ def get_target_aberrations(TEM_name, acceleration_voltage):
|
|
|
447
645
|
'C30': 123,
|
|
448
646
|
'C32a': 95.3047364258614, 'C32b': -189.72105710231244, 'C34a': -47.45099594807912, 'C34b': -94.67424667529909,
|
|
449
647
|
'C41a': -905.31842572806, 'C41b': 981.316128853203, 'C43a': 4021.8433526960034, 'C43b': 131.72716642732158,
|
|
450
|
-
'C45a': -4702.390968272048, 'C45b': -208.25028574642903, 'C50': -
|
|
451
|
-
'C50': 552000., 'C52a': -0., 'C52b': 0.,
|
|
648
|
+
'C45a': -4702.390968272048, 'C45b': -208.25028574642903, 'C50': 552000., 'C52a': -0., 'C52b': 0.,
|
|
452
649
|
'C54a': -0., 'C54b': -0., 'C56a': -36663.643489934424, 'C56b': 21356.079837905396,
|
|
453
650
|
'acceleration_voltage': 200000,
|
|
454
651
|
'FOV': 34.241659495148205,
|
|
455
652
|
'Cc': 1* 1e6,
|
|
456
653
|
'convergence_angle': 30,
|
|
457
654
|
'wavelength': 0.0025079340450548005}
|
|
458
|
-
|
|
459
655
|
return ab
|
|
460
656
|
|
|
461
657
|
|
|
@@ -488,7 +684,7 @@ def get_ronchigram_2(size, ab, scale='mrad', threshold=3):
|
|
|
488
684
|
aperture[mask] = 0.
|
|
489
685
|
|
|
490
686
|
v_noise = np.random.rand(size_x, size_y)
|
|
491
|
-
smoothing =
|
|
687
|
+
smoothing = 10
|
|
492
688
|
phi_r = ndimage.gaussian_filter(v_noise, sigma=(smoothing, smoothing), order=0)
|
|
493
689
|
|
|
494
690
|
sigma = 6 # 6 for carbon and thin
|
pyTEMlib/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyTEMlib
|
|
3
|
-
Version: 0.2025.
|
|
3
|
+
Version: 0.2025.4.1
|
|
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
|
|
@@ -48,6 +48,7 @@ Dynamic: description
|
|
|
48
48
|
Dynamic: home-page
|
|
49
49
|
Dynamic: keywords
|
|
50
50
|
Dynamic: license
|
|
51
|
+
Dynamic: license-file
|
|
51
52
|
Dynamic: platform
|
|
52
53
|
Dynamic: requires-dist
|
|
53
54
|
Dynamic: summary
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pyTEMlib/__init__.py,sha256=nEN93amIEoZxO7rJgN71ABeCoXrnmaywBbE97l5lPio,178
|
|
2
2
|
pyTEMlib/animation.py,sha256=G9ykYo6yB5jexjKienShO9C2b8xEXKbf7t47apwcwTw,25188
|
|
3
|
-
pyTEMlib/atom_tools.py,sha256=
|
|
3
|
+
pyTEMlib/atom_tools.py,sha256=iXJbK66k3HbTNOGSWRLeUV01JlggUo1Qq89HJqbSv54,7341
|
|
4
4
|
pyTEMlib/config_dir.py,sha256=4evlo9P2Yht-AnqaLI-WweLjDQcislbAP3I7P7EZsPU,2085
|
|
5
5
|
pyTEMlib/core_loss_widget.py,sha256=XQk071ZHMGYOM6RTSIaXQlZtnFQjUyxTWbQ2ZXOFMfU,30901
|
|
6
6
|
pyTEMlib/crystal_tools.py,sha256=g4OXyvd5NLw7vaXhjDP3P6VZpVV6eiyuPn8MdgR2amI,61652
|
|
@@ -10,13 +10,13 @@ pyTEMlib/eds_tools.py,sha256=Ilof2Cars-1ILXx5g2RsU2G4BgrPwjOHgQ7-OabmbrU,28000
|
|
|
10
10
|
pyTEMlib/eels_dialog.py,sha256=QG_PU3uuzus_3I3zjfaxb2a9iYq8B053zYw-B52JklM,32595
|
|
11
11
|
pyTEMlib/eels_dialog_utilities.py,sha256=73W9jFbPx-eeLEiSaBptTgGLr40bIYYfSyzLnZbhfvo,51761
|
|
12
12
|
pyTEMlib/eels_tools.py,sha256=pl75ZDQHz1JngxD84T_595Kfrl5TEJSETPGgkXfBtrA,88688
|
|
13
|
-
pyTEMlib/file_tools.py,sha256=
|
|
13
|
+
pyTEMlib/file_tools.py,sha256=RniTJrcuiyt7PefE5SSY_z2pTZPTNhFRjb2oVXVa4nE,66373
|
|
14
14
|
pyTEMlib/file_tools_qt.py,sha256=tLZACS4JyGH_AOzNR_SGAhjA01y4VJB261opPhGMlm8,7223
|
|
15
|
-
pyTEMlib/graph_tools.py,sha256=
|
|
15
|
+
pyTEMlib/graph_tools.py,sha256=VWuTgFGeu4gn4cfRgf-76kO6u2B1ZV_dz6gLfx2k4NY,46570
|
|
16
16
|
pyTEMlib/graph_viz.py,sha256=m5PwSn6l2r0bsaLWBDSHc9IGR3_PneG2BrZgnEdi07I,13644
|
|
17
17
|
pyTEMlib/image_dialog.py,sha256=F-ZgKq7UnMtPPd1b9eqb7t8MXDfWN-8hVKwB2Il0x28,6235
|
|
18
18
|
pyTEMlib/image_dlg.py,sha256=n5gradDiYOFGEQ3k_Wlk9RUYYzl4bl_hKLzNVcYteNE,5694
|
|
19
|
-
pyTEMlib/image_tools.py,sha256=
|
|
19
|
+
pyTEMlib/image_tools.py,sha256=iBVsAr6-oQY6y2LTSbUIz6nMeAIOsSX5zPBezekJsFg,52334
|
|
20
20
|
pyTEMlib/info_widget.py,sha256=lkzQOuNVlkaasiZDtc5UtYk541-plYNfnW4DQwQB_iA,53467
|
|
21
21
|
pyTEMlib/info_widget3.py,sha256=QSbdSj6m57KQTir2fNhulVgjOu9EQL31c-9SzTlghnE,55495
|
|
22
22
|
pyTEMlib/interactive_image.py,sha256=5PwypcA1OjLAD-fi8bmWWFHuOjdIPVY9Dh59V24WuDA,34
|
|
@@ -25,14 +25,14 @@ pyTEMlib/low_loss_widget.py,sha256=0SxHOAuUnuNjDrJSNS2PeWD6hjyuB5FCYxmVfsDTq5c,2
|
|
|
25
25
|
pyTEMlib/microscope.py,sha256=iigUF1UImHEfmL2wqEBBj3aNRgEYouDbIln8VCo4_KM,1545
|
|
26
26
|
pyTEMlib/peak_dialog.py,sha256=r3mhYvECC9niFFItS1oGa96F2nFthsE5kfpA3yVXQaE,51872
|
|
27
27
|
pyTEMlib/peak_dlg.py,sha256=qcjcnhwpGa4jBCeXzwQz9sCyX-tHsLLQ67ToqfKOiQY,11550
|
|
28
|
-
pyTEMlib/probe_tools.py,sha256=
|
|
28
|
+
pyTEMlib/probe_tools.py,sha256=sDW9CW3SMwjvSHYcEufceismHv_LVkqxcS-gCtEklCg,37926
|
|
29
29
|
pyTEMlib/sidpy_tools.py,sha256=0oIx-qMtEmcZmLazQKW19dd-KoxyY3B15aIeMcyHA8E,4878
|
|
30
30
|
pyTEMlib/simulation_tools.py,sha256=RmegD5TpQMU68uASvzZWVplAqs7bM5KkF6bWDWLjyc0,2799
|
|
31
|
-
pyTEMlib/version.py,sha256=
|
|
31
|
+
pyTEMlib/version.py,sha256=1mG-HaMBzuYiDf1nig-t9gxjgThQprsDbDaDFGoWGBY,94
|
|
32
32
|
pyTEMlib/xrpa_x_sections.py,sha256=m4gaH7gaJiNi-CsIT9aKoH4fB6MQIAe876kxEmzSebI,1825392
|
|
33
|
-
pytemlib-0.2025.
|
|
34
|
-
pytemlib-0.2025.
|
|
35
|
-
pytemlib-0.2025.
|
|
36
|
-
pytemlib-0.2025.
|
|
37
|
-
pytemlib-0.2025.
|
|
38
|
-
pytemlib-0.2025.
|
|
33
|
+
pytemlib-0.2025.4.1.dist-info/licenses/LICENSE,sha256=7HdBF6SXIBd38bHOKkQd4DYR1KV-OYm9mwB16fM-984,1062
|
|
34
|
+
pytemlib-0.2025.4.1.dist-info/METADATA,sha256=Cp3K3rOFMT-MZjC2EK0mKXBYU495_9UFUkZlg2yIH9Y,3515
|
|
35
|
+
pytemlib-0.2025.4.1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
36
|
+
pytemlib-0.2025.4.1.dist-info/entry_points.txt,sha256=zn2yO1IWTutI3c7C9e3GdARCvm43JURoOhqQ8YylV4Y,43
|
|
37
|
+
pytemlib-0.2025.4.1.dist-info/top_level.txt,sha256=rPLVH0UJxrPSPgSoKScTjL1K_X69JFzsYYnDnYTYIlU,9
|
|
38
|
+
pytemlib-0.2025.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|