pyTEMlib 0.2025.12.0__py3-none-any.whl

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