pyTEMlib 0.2025.4.2__py3-none-any.whl → 0.2025.9.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.

Files changed (94) hide show
  1. build/lib/pyTEMlib/__init__.py +33 -0
  2. build/lib/pyTEMlib/animation.py +640 -0
  3. build/lib/pyTEMlib/atom_tools.py +238 -0
  4. build/lib/pyTEMlib/config_dir.py +31 -0
  5. build/lib/pyTEMlib/crystal_tools.py +1219 -0
  6. build/lib/pyTEMlib/diffraction_plot.py +756 -0
  7. build/lib/pyTEMlib/dynamic_scattering.py +293 -0
  8. build/lib/pyTEMlib/eds_tools.py +826 -0
  9. build/lib/pyTEMlib/eds_xsections.py +432 -0
  10. build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
  11. build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  12. build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
  13. build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  14. build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  15. build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  16. build/lib/pyTEMlib/file_reader.py +274 -0
  17. build/lib/pyTEMlib/file_tools.py +811 -0
  18. build/lib/pyTEMlib/get_bote_salvat.py +69 -0
  19. build/lib/pyTEMlib/graph_tools.py +1153 -0
  20. build/lib/pyTEMlib/graph_viz.py +599 -0
  21. build/lib/pyTEMlib/image/__init__.py +37 -0
  22. build/lib/pyTEMlib/image/image_atoms.py +270 -0
  23. build/lib/pyTEMlib/image/image_clean.py +197 -0
  24. build/lib/pyTEMlib/image/image_distortion.py +299 -0
  25. build/lib/pyTEMlib/image/image_fft.py +277 -0
  26. build/lib/pyTEMlib/image/image_graph.py +926 -0
  27. build/lib/pyTEMlib/image/image_registration.py +316 -0
  28. build/lib/pyTEMlib/image/image_utilities.py +309 -0
  29. build/lib/pyTEMlib/image/image_window.py +421 -0
  30. build/lib/pyTEMlib/image_tools.py +699 -0
  31. build/lib/pyTEMlib/interactive_image.py +1 -0
  32. build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
  33. build/lib/pyTEMlib/microscope.py +61 -0
  34. build/lib/pyTEMlib/probe_tools.py +906 -0
  35. build/lib/pyTEMlib/sidpy_tools.py +153 -0
  36. build/lib/pyTEMlib/simulation_tools.py +104 -0
  37. build/lib/pyTEMlib/test.py +437 -0
  38. build/lib/pyTEMlib/utilities.py +314 -0
  39. build/lib/pyTEMlib/version.py +5 -0
  40. build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
  41. pyTEMlib/__init__.py +25 -3
  42. pyTEMlib/animation.py +31 -22
  43. pyTEMlib/atom_tools.py +29 -34
  44. pyTEMlib/config_dir.py +2 -28
  45. pyTEMlib/crystal_tools.py +129 -165
  46. pyTEMlib/eds_tools.py +559 -342
  47. pyTEMlib/eds_xsections.py +432 -0
  48. pyTEMlib/eels_tools/__init__.py +44 -0
  49. pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  50. pyTEMlib/eels_tools/eels_database.py +134 -0
  51. pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  52. pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  53. pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  54. pyTEMlib/file_reader.py +274 -0
  55. pyTEMlib/file_tools.py +260 -1130
  56. pyTEMlib/get_bote_salvat.py +69 -0
  57. pyTEMlib/graph_tools.py +101 -174
  58. pyTEMlib/graph_viz.py +150 -0
  59. pyTEMlib/image/__init__.py +37 -0
  60. pyTEMlib/image/image_atoms.py +270 -0
  61. pyTEMlib/image/image_clean.py +197 -0
  62. pyTEMlib/image/image_distortion.py +299 -0
  63. pyTEMlib/image/image_fft.py +277 -0
  64. pyTEMlib/image/image_graph.py +926 -0
  65. pyTEMlib/image/image_registration.py +316 -0
  66. pyTEMlib/image/image_utilities.py +309 -0
  67. pyTEMlib/image/image_window.py +421 -0
  68. pyTEMlib/image_tools.py +154 -928
  69. pyTEMlib/kinematic_scattering.py +1 -1
  70. pyTEMlib/probe_tools.py +1 -1
  71. pyTEMlib/test.py +437 -0
  72. pyTEMlib/utilities.py +314 -0
  73. pyTEMlib/version.py +2 -3
  74. pyTEMlib/xrpa_x_sections.py +14 -10
  75. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
  76. pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
  77. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
  78. pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
  79. pyTEMlib/core_loss_widget.py +0 -721
  80. pyTEMlib/eels_dialog.py +0 -754
  81. pyTEMlib/eels_dialog_utilities.py +0 -1199
  82. pyTEMlib/eels_tools.py +0 -2359
  83. pyTEMlib/file_tools_qt.py +0 -193
  84. pyTEMlib/image_dialog.py +0 -158
  85. pyTEMlib/image_dlg.py +0 -146
  86. pyTEMlib/info_widget.py +0 -1086
  87. pyTEMlib/info_widget3.py +0 -1120
  88. pyTEMlib/low_loss_widget.py +0 -479
  89. pyTEMlib/peak_dialog.py +0 -1129
  90. pyTEMlib/peak_dlg.py +0 -286
  91. pytemlib-0.2025.4.2.dist-info/RECORD +0 -38
  92. pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
  93. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
  94. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,274 @@
1
+ """file_reader: tools to load other data files
2
+
3
+ ##################################
4
+
5
+ 2025 copied from file_tools
6
+
7
+ ##################################
8
+ """
9
+ import typing
10
+ import collections
11
+ import xml
12
+ import numpy as np
13
+
14
+
15
+ # =============================================
16
+ # Include pycroscopy libraries #
17
+ # =============================================
18
+ import pyNSID
19
+ import sidpy
20
+
21
+ def etree_to_dict(element: xml.etree.ElementTree.Element) -> dict[str, typing.Any]:
22
+ """Recursively converts an ElementTree object into a nested dictionary."""
23
+ d = {element.tag: {} if element.attrib else None}
24
+ children = list(element)
25
+ if children:
26
+ dd = collections.defaultdict(list)
27
+ for dc in map(etree_to_dict, children):
28
+ for k, v in dc.items():
29
+ dd[k].append(v)
30
+ d = {element.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
31
+ if element.attrib:
32
+ d[element.tag].update(('@' + k, v) for k, v in element.attrib.items())
33
+ if element.text:
34
+ text = element.text.strip()
35
+ if children or element.attrib:
36
+ if text:
37
+ d[element.tag]['#text'] = text
38
+ else:
39
+ d[element.tag] = text
40
+ return d
41
+
42
+ def read_adorned_metadata(image: typing.Any) -> tuple[str, dict[str, typing.Any]]:
43
+ """Extract metadata from an adorned image."""
44
+ xml_str = image.metadata.metadata_as_xml
45
+ root = xml.etree.ElementTree.fromstring(xml_str)
46
+ metadata_dict = etree_to_dict(root)
47
+ detector = 'detector'
48
+
49
+ if 'Detectors' in metadata_dict['Metadata']:
50
+ if 'ScanningDetector' in metadata_dict['Metadata']['Detectors']:
51
+ detector = metadata_dict['Metadata']['Detectors']['ScanningDetector']['DetectorName']
52
+ elif 'ImagingDetector' in metadata_dict['Metadata']['Detectors']:
53
+ detector = metadata_dict['Metadata']['Detectors']['ImagingDetector']['DetectorName']
54
+ segment = ''
55
+ if 'CustomPropertyGroup' in metadata_dict['Metadata']:
56
+ if 'CustomProperties' in metadata_dict['Metadata']['CustomPropertyGroup']:
57
+ for list_item in metadata_dict['Metadata']['CustomPropertyGroup']['CustomProperties']:
58
+ if isinstance(list_item, dict):
59
+ for key in list_item:
60
+ for item in list_item[key]:
61
+ if '@name' in item:
62
+ if item['@name']== 'DetectorCommercialName':
63
+ detector = item['@value']
64
+ if item['@name']== 'StemSegment':
65
+ segment = '_'+item['@value']
66
+ return detector+segment, metadata_dict['Metadata']
67
+
68
+
69
+ def get_metadata_from_adorned(ds: sidpy.Dataset):
70
+ """Extract relevant metadata from adorned image metadata and add to sidpy.Dataset"""
71
+ exp_meta = {}
72
+ orig_meta = ds.original_metadata
73
+ if 'Optics' in orig_meta:
74
+ if 'LastMeasuredScreenCurrent' in orig_meta['Optics']:
75
+ exp_meta['current'] = float(orig_meta['Optics']['LastMeasuredScreenCurrent'])
76
+ if 'ConvergenceAngle' in orig_meta['Optics']:
77
+ exp_meta['convergence_angle'] = float(orig_meta['Optics']['ConvergenceAngle'])
78
+ if 'AccelerationVoltage' in orig_meta['Optics']:
79
+ exp_meta['acceleration_voltage'] = float(orig_meta['Optics']['AccelerationVoltage'])
80
+ if 'SpotIndex' in orig_meta['Optics']:
81
+ exp_meta['spot_size'] = orig_meta['Optics']['SpotIndex']
82
+ if 'StagesSettings' in orig_meta:
83
+ if 'StagePosition' in orig_meta['StagesSettings']:
84
+ exp_meta['stage_position'] = orig_meta['StagesSettings']['StagePosition']
85
+ if 'Detectors' in orig_meta:
86
+ if 'ScanningDetector' in orig_meta['Detectors']:
87
+ exp_meta['detector'] = orig_meta['Detectors']['ScanningDetector']['DetectorName']
88
+ elif 'ImagingDetector' in orig_meta['Detectors']:
89
+ exp_meta['detector'] = orig_meta['Detectors']['ImagingDetector']['DetectorName']
90
+ exp_meta['exposure_time'] = orig_meta['Detectors']['ImagingDetector']['ExposureTime']
91
+ ds.metadata['experiment'] = exp_meta
92
+
93
+ def adorned_to_sidpy(images: typing.Union[list[typing.Any], typing.Any]
94
+ ) -> dict[str, sidpy.Dataset]:
95
+ """
96
+ Convert a list of adorned images to a dictionary of Sidpy datasets.
97
+ Each dataset is created from the image data and adorned metadata.
98
+ The datasets are stored in a dictionary with keys 'Channel_000', 'Channel_001', etc.
99
+ The dimensions of the datasets are set based on the image data shape and pixel sizes.
100
+ The original metadata is also stored in the dataset.
101
+ Args:
102
+ images (list or object): A list of adorned images or a single adorned image.
103
+ Returns:
104
+ dict: A dictionary of Sidpy datasets, where each dataset corresponds to an image.
105
+ """
106
+
107
+ data_sets = {}
108
+ if not isinstance(images, list):
109
+ images = [images]
110
+ for index, image in enumerate(images):
111
+ name, original_metadata = read_adorned_metadata(image)
112
+ data_sets[f'Channel_{index:03}'] = sidpy.Dataset.from_array(image.data.T, title=name)
113
+ ds = data_sets[f'Channel_{index:03}']
114
+
115
+ ds.original_metadata = original_metadata
116
+
117
+ pixel_size_x_m = float(ds.original_metadata['BinaryResult']['PixelSize']['X']['#text'])
118
+ pixel_size_y_m = float(ds.original_metadata['BinaryResult']['PixelSize']['Y']['#text'])
119
+ pixel_size_x_nm = pixel_size_x_m * 1e9
120
+ pixel_size_y_nm = pixel_size_y_m * 1e9
121
+ if image.data.ndim == 3:
122
+ ds.data_type = 'image_stack'
123
+ ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]),
124
+ name='frame', units='frame', quantity='Length',
125
+ dimension_type='temporal'))
126
+ ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_y_nm,
127
+ name='y', units='nm', quantity='Length',
128
+ dimension_type='spatial'))
129
+ ds.set_dimension(2, sidpy.Dimension(np.arange(image.data.shape[2]) * pixel_size_x_nm,
130
+ name='x', units='nm', quantity='Length',
131
+ dimension_type='spatial'))
132
+ else:
133
+ ds.data_type = 'image'
134
+ ds.set_dimension(0, sidpy.Dimension(np.arange(image.data.shape[0]) * pixel_size_y_nm,
135
+ name='y', units='nm', quantity='Length',
136
+ dimension_type='spatial'))
137
+ ds.set_dimension(1, sidpy.Dimension(np.arange(image.data.shape[1]) * pixel_size_x_nm,
138
+ name='x', units='nm', quantity='Length',
139
+ dimension_type='spatial'))
140
+ get_metadata_from_adorned(ds)
141
+ return data_sets
142
+
143
+
144
+ ###############################################
145
+ # Support old pyTEM file format
146
+ ###############################################
147
+
148
+ def read_old_h5group(current_channel: typing.Any) -> typing.Optional[sidpy.Dataset]:
149
+ """Make a sidpy.Dataset from pyUSID style hdf5 group
150
+
151
+ Parameters
152
+ ----------
153
+ current_channel: h5_group
154
+
155
+ Returns
156
+ -------
157
+ sidpy.Dataset
158
+ """
159
+
160
+ dim_dir = []
161
+ if 'nDim_Data' in current_channel:
162
+ h5_dataset = current_channel['nDim_Data']
163
+ reader = pyNSID.NSIDReader(h5_dataset.file.filename)
164
+ dataset = reader.read(h5_dataset)
165
+ dataset.h5_file = current_channel.file
166
+ return dataset
167
+ if 'Raw_Data' in current_channel:
168
+ if 'image_stack' in current_channel:
169
+ sid_dataset = sidpy.Dataset.from_array(np.swapaxes(current_channel['image_stack'][()], 2, 0))
170
+ dim_dir = ['SPATIAL', 'SPATIAL', 'TEMPORAL']
171
+ elif 'data' in current_channel:
172
+ sid_dataset = sidpy.Dataset.from_array(current_channel['data'][()])
173
+ dim_dir = ['SPATIAL', 'SPATIAL']
174
+ else:
175
+ size_x = int(current_channel['spatial_size_x'][()])
176
+ size_y = int(current_channel['spatial_size_y'][()])
177
+ if 'spectral_size_x' in current_channel:
178
+ size_s = int(current_channel['spectral_size_x'][()])
179
+ else:
180
+ size_s = 0
181
+ data = np.reshape(current_channel['Raw_Data'][()], (size_x, size_y, size_s))
182
+ sid_dataset = sidpy.Dataset.from_array(data)
183
+ if size_x > 1:
184
+ dim_dir.append('SPATIAL')
185
+ if size_y > 1:
186
+ dim_dir.append('SPATIAL')
187
+ if size_s > 1:
188
+ dim_dir.append('SPECTRAL')
189
+ sid_dataset.h5_dataset = current_channel['Raw_Data']
190
+
191
+ elif 'data' in current_channel:
192
+ sid_dataset = sidpy.Dataset.from_array(current_channel['data'][()])
193
+ dim_dir = ['SPATIAL', 'SPATIAL']
194
+ sid_dataset.h5_dataset = current_channel['data']
195
+ else:
196
+ return
197
+
198
+ if 'SPATIAL' in dim_dir:
199
+ if 'SPECTRAL' in dim_dir:
200
+ sid_dataset.data_type = sidpy.DataType.SPECTRAL_IMAGE
201
+ elif 'TEMPORAL' in dim_dir:
202
+ sid_dataset.data_type = sidpy.DataType.IMAGE_STACK
203
+ else:
204
+ sid_dataset.data_type = sidpy.DataType.IMAGE
205
+ else:
206
+ sid_dataset.data_type = sidpy.DataType.SPECTRUM
207
+
208
+ sid_dataset.quantity = 'intensity'
209
+ sid_dataset.units = 'counts'
210
+ if 'analysis' in current_channel:
211
+ sid_dataset.source = current_channel['analysis'][()]
212
+
213
+ set_dimensions(sid_dataset, current_channel)
214
+
215
+ return sid_dataset
216
+
217
+
218
+ def set_dimensions(dset, current_channel):
219
+ """Attaches correct dimension from old pyTEMlib style.
220
+
221
+ Parameters
222
+ ----------
223
+ dset: sidpy.Dataset
224
+ current_channel: hdf5.Group
225
+ """
226
+ dim = 0
227
+ if dset.data_type == sidpy.DataType.IMAGE_STACK:
228
+ dset.set_dimension(dim, sidpy.Dimension(np.arange(dset.shape[dim]), name='frame',
229
+ units='frame', quantity='stack',
230
+ dimension_type='TEMPORAL'))
231
+ dim += 1
232
+ if 'IMAGE' in dset.data_type:
233
+
234
+ if 'spatial_scale_x' in current_channel:
235
+ scale_x = current_channel['spatial_scale_x'][()]
236
+ else:
237
+ scale_x = 1
238
+ if 'spatial_units' in current_channel:
239
+ units_x = current_channel['spatial_units'][()]
240
+ if len(units_x) < 2:
241
+ units_x = 'pixel'
242
+ else:
243
+ units_x = 'generic'
244
+ if 'spatial_scale_y' in current_channel:
245
+ scale_y = current_channel['spatial_scale_y'][()]
246
+ else:
247
+ scale_y = 0
248
+ dset.set_dimension(dim, sidpy.Dimension('x', np.arange(dset.shape[dim])*scale_x,
249
+ units=units_x, quantity='Length',
250
+ dimension_type='SPATIAL'))
251
+ dim += 1
252
+ dset.set_dimension(dim, sidpy.Dimension('y', np.arange(dset.shape[dim])*scale_y,
253
+ units=units_x, quantity='Length',
254
+ dimension_type='SPATIAL'))
255
+ dim += 1
256
+ if dset.data_type in [sidpy.DataType.SPECTRUM, sidpy.DataType.SPECTRAL_IMAGE]:
257
+ if 'spectral_scale_x' in current_channel:
258
+ scale_s = current_channel['spectral_scale_x'][()]
259
+ else:
260
+ scale_s = 1.0
261
+ if 'spectral_units_x' in current_channel:
262
+ units_s = current_channel['spectral_units_x']
263
+ else:
264
+ units_s = 'eV'
265
+
266
+ if 'spectral_offset_x' in current_channel:
267
+ offset = current_channel['spectral_offset_x']
268
+ else:
269
+ offset = 0.0
270
+ dset.set_dimension(dim, sidpy.Dimension(np.arange(dset.shape[dim]) * scale_s + offset,
271
+ name='energy',
272
+ units=units_s,
273
+ quantity='energy_loss',
274
+ dimension_type='SPECTRAL'))