nxs-analysis-tools 0.0.45__tar.gz → 0.0.47__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 nxs-analysis-tools might be problematic. Click here for more details.
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/LICENSE +1 -1
- {nxs_analysis_tools-0.0.45/src/nxs_analysis_tools.egg-info → nxs_analysis_tools-0.0.47}/PKG-INFO +2 -2
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/pyproject.toml +1 -1
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/_meta/__init__.py +2 -2
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/chess.py +54 -12
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/datareduction.py +100 -29
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/pairdistribution.py +83 -38
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47/src/nxs_analysis_tools.egg-info}/PKG-INFO +2 -2
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools.egg-info/SOURCES.txt +2 -0
- nxs_analysis_tools-0.0.47/tests/test_accurate_highlight.py +388 -0
- nxs_analysis_tools-0.0.47/tests/test_sum_axis.py +299 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/MANIFEST.in +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/README.md +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/setup.cfg +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/setup.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/__init__.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/fitting.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools.egg-info/dependency_links.txt +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools.egg-info/requires.txt +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools.egg-info/top_level.txt +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_chess.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_chess_fitting.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_datareduction.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_fitting.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_lmfit.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_mask_plotting.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_pairdistribution.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_plot_slice_with_ndarray.py +0 -0
- {nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/tests/test_symmetrizer_rectangular_plane.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2023 Steven J. Gomez Alvarado
|
|
3
|
+
Copyright (c) 2023-2025 Steven J. Gomez Alvarado
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
{nxs_analysis_tools-0.0.45/src/nxs_analysis_tools.egg-info → nxs_analysis_tools-0.0.47}/PKG-INFO
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nxs-analysis-tools
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.47
|
|
4
4
|
Summary: Reduce and transform nexus format (.nxs) scattering data.
|
|
5
5
|
Author-email: "Steven J. Gomez Alvarado" <stevenjgomez@ucsb.edu>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
8
|
-
Copyright (c) 2023 Steven J. Gomez Alvarado
|
|
8
|
+
Copyright (c) 2023-2025 Steven J. Gomez Alvarado
|
|
9
9
|
|
|
10
10
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
11
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
__project__ = 'nxs-analysis-tools'
|
|
5
5
|
__author__ = 'Steven J. Gomez Alvarado'
|
|
6
6
|
__email__ = 'stevenjgomez@ucsb.edu'
|
|
7
|
-
__copyright__ = f"2023, {__author__}"
|
|
7
|
+
__copyright__ = f"2023-2025, {__author__}"
|
|
8
8
|
__license__ = 'MIT'
|
|
9
|
-
__version__ = '0.0.
|
|
9
|
+
__version__ = '0.0.47'
|
|
10
10
|
__repo_url__ = 'https://github.com/stevenjgomez/nxs_analysis_tools'
|
|
@@ -58,9 +58,9 @@ class TempDependence:
|
|
|
58
58
|
Initialize Scissors and LinecutModel objects for each temperature.
|
|
59
59
|
set_data(temperature, data):
|
|
60
60
|
Set the dataset for a specific temperature.
|
|
61
|
-
load_transforms(temperatures_list=None):
|
|
61
|
+
load_transforms(temperatures_list=None, print_tree=True):
|
|
62
62
|
Load transform datasets (from nxrefine) based on temperature.
|
|
63
|
-
load_datasets(file_ending='hkli.nxs', temperatures_list=None):
|
|
63
|
+
load_datasets(file_ending='hkli.nxs', temperatures_list=None, print_tree=True):
|
|
64
64
|
Load datasets (CHESS format) from the specified folder.
|
|
65
65
|
get_sample_directory():
|
|
66
66
|
Get the folder path where the datasets are located.
|
|
@@ -98,6 +98,8 @@ class TempDependence:
|
|
|
98
98
|
Fit the line cut models for each temperature.
|
|
99
99
|
plot_fit(mdheadings=False, **kwargs):
|
|
100
100
|
Plot the fit results for each temperature.
|
|
101
|
+
plot_order_parameter(self):
|
|
102
|
+
Plot the temperature dependence of the peakheight parameter.
|
|
101
103
|
print_fit_report():
|
|
102
104
|
Print the fit report for each temperature.
|
|
103
105
|
"""
|
|
@@ -186,7 +188,7 @@ class TempDependence:
|
|
|
186
188
|
"""
|
|
187
189
|
self.datasets[temperature] = data
|
|
188
190
|
|
|
189
|
-
def load_transforms(self, temperatures_list=None):
|
|
191
|
+
def load_transforms(self, temperatures_list=None, print_tree=True):
|
|
190
192
|
"""
|
|
191
193
|
Load transform datasets (from nxrefine) based on temperature.
|
|
192
194
|
|
|
@@ -194,6 +196,8 @@ class TempDependence:
|
|
|
194
196
|
----------
|
|
195
197
|
temperatures_list : list of int or None, optional
|
|
196
198
|
List of temperatures to load. If None, all available temperatures are loaded.
|
|
199
|
+
print_tree : bool, optional
|
|
200
|
+
Whether to print the data tree upon loading. Default True.
|
|
197
201
|
"""
|
|
198
202
|
# Convert all temperatures to strings
|
|
199
203
|
if temperatures_list:
|
|
@@ -231,7 +235,7 @@ class TempDependence:
|
|
|
231
235
|
|
|
232
236
|
# Save dataset
|
|
233
237
|
try:
|
|
234
|
-
self.datasets[self.temperatures[i]] = load_transform(path)
|
|
238
|
+
self.datasets[self.temperatures[i]] = load_transform(path, print_tree)
|
|
235
239
|
except Exception as e:
|
|
236
240
|
# Report temperature that was unable to load, then raise exception.
|
|
237
241
|
temp_failed = self.temperatures[i]
|
|
@@ -245,7 +249,7 @@ class TempDependence:
|
|
|
245
249
|
# Initialize linecutmodel object
|
|
246
250
|
self.linecutmodels[self.temperatures[i]] = LinecutModel()
|
|
247
251
|
|
|
248
|
-
def load_datasets(self, file_ending='hkli.nxs', temperatures_list=None):
|
|
252
|
+
def load_datasets(self, file_ending='hkli.nxs', temperatures_list=None, print_tree=True):
|
|
249
253
|
"""
|
|
250
254
|
Load datasets (CHESS format) from the specified folder.
|
|
251
255
|
|
|
@@ -256,6 +260,8 @@ class TempDependence:
|
|
|
256
260
|
temperatures_list : list of int or None, optional
|
|
257
261
|
The list of specific temperatures to load. If None, all available temperatures are
|
|
258
262
|
loaded. The default is None.
|
|
263
|
+
print_tree : bool, optional
|
|
264
|
+
Whether to print the data tree upon loading. Default True.
|
|
259
265
|
"""
|
|
260
266
|
temperature_folders = [] # Empty list to store temperature folder names
|
|
261
267
|
for item in os.listdir(self.sample_directory):
|
|
@@ -278,7 +284,7 @@ class TempDependence:
|
|
|
278
284
|
filepath = os.path.join(self.sample_directory, T, file)
|
|
279
285
|
|
|
280
286
|
# Load dataset at each temperature
|
|
281
|
-
self.datasets[T] = load_data(filepath)
|
|
287
|
+
self.datasets[T] = load_data(filepath, print_tree)
|
|
282
288
|
|
|
283
289
|
# Initialize scissors object at each temperature
|
|
284
290
|
self.scissors[T] = Scissors()
|
|
@@ -370,15 +376,13 @@ class TempDependence:
|
|
|
370
376
|
A dictionary of linecuts obtained from the cutting operation.
|
|
371
377
|
"""
|
|
372
378
|
|
|
373
|
-
center = center if center is not None else self.scissors[self.temperatures[0]].center
|
|
374
|
-
window = window if window is not None else self.scissors[self.temperatures[0]].window
|
|
375
|
-
axis = axis if axis is not None else self.scissors[self.temperatures[0]].axis
|
|
376
|
-
|
|
377
379
|
for T in self.temperatures:
|
|
378
380
|
if verbose:
|
|
379
381
|
print("-------------------------------")
|
|
380
382
|
print("Cutting T = " + T + " K data...")
|
|
381
|
-
self.scissors[T].
|
|
383
|
+
self.scissors[T].set_center(center)
|
|
384
|
+
self.scissors[T].set_window(window)
|
|
385
|
+
self.scissors[T].cut_data(axis=axis, verbose=verbose)
|
|
382
386
|
self.linecuts[T] = self.scissors[T].linecut
|
|
383
387
|
self.linecutmodels[T].set_data(self.linecuts[T])
|
|
384
388
|
|
|
@@ -466,7 +470,7 @@ class TempDependence:
|
|
|
466
470
|
_, ax = plt.subplots()
|
|
467
471
|
p = ax.pcolormesh(X, Y, v_2d, **kwargs)
|
|
468
472
|
plt.colorbar(p, label='counts')
|
|
469
|
-
ax.set(xlabel=
|
|
473
|
+
ax.set(xlabel=self.xlabel, ylabel=r'$T$ (K)')
|
|
470
474
|
|
|
471
475
|
return p
|
|
472
476
|
|
|
@@ -641,6 +645,44 @@ class TempDependence:
|
|
|
641
645
|
title=f"{T} K",
|
|
642
646
|
**kwargs)
|
|
643
647
|
|
|
648
|
+
def plot_order_parameter(self):
|
|
649
|
+
"""
|
|
650
|
+
Plot the temperature dependence of the peak height (order parameter).
|
|
651
|
+
|
|
652
|
+
This method extracts the peak height from each temperature-dependent
|
|
653
|
+
line cut fit stored in `linecutmodels` and plots it as a function
|
|
654
|
+
of temperature using matplotlib.
|
|
655
|
+
|
|
656
|
+
Returns
|
|
657
|
+
-------
|
|
658
|
+
Figure
|
|
659
|
+
Matplotlib Figure object containing the peak height vs. temperature plot.
|
|
660
|
+
Axes
|
|
661
|
+
Matplotlib Axes object associated with the figure.
|
|
662
|
+
|
|
663
|
+
Notes
|
|
664
|
+
-----
|
|
665
|
+
- Temperature values are converted to integers for plotting.
|
|
666
|
+
- Peak heights are extracted from the 'peakheight' parameter in the model results.
|
|
667
|
+
- The plot uses standard axes labels with temperature in Kelvin.
|
|
668
|
+
"""
|
|
669
|
+
|
|
670
|
+
# Create an array of temperature values
|
|
671
|
+
temperatures = [int(T) for T in self.temperatures]
|
|
672
|
+
|
|
673
|
+
# Create an empty list for the peak heights
|
|
674
|
+
peakheights = []
|
|
675
|
+
|
|
676
|
+
# Extract the peakheight at every temperature
|
|
677
|
+
for T in self.temperatures:
|
|
678
|
+
peakheights.append(self.linecutmodels[T].modelresult.params['peakheight'].value)
|
|
679
|
+
|
|
680
|
+
# Plot the peakheights vs. temperature
|
|
681
|
+
fig, ax = plt.subplots()
|
|
682
|
+
ax.plot(temperatures, peakheights)
|
|
683
|
+
ax.set(xlabel='$T$ (K)', ylabel='peakheight')
|
|
684
|
+
return fig, ax
|
|
685
|
+
|
|
644
686
|
def print_fit_report(self):
|
|
645
687
|
"""
|
|
646
688
|
Plot the fit results.
|
{nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/datareduction.py
RENAMED
|
@@ -16,10 +16,10 @@ from scipy import ndimage
|
|
|
16
16
|
# Specify items on which users are allowed to perform standalone imports
|
|
17
17
|
__all__ = ['load_data', 'load_transform', 'plot_slice', 'Scissors',
|
|
18
18
|
'reciprocal_lattice_params', 'rotate_data', 'rotate_data_2D'
|
|
19
|
-
|
|
19
|
+
'array_to_nxdata', 'Padder']
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def load_data(path):
|
|
22
|
+
def load_data(path, print_tree=True):
|
|
23
23
|
"""
|
|
24
24
|
Load data from a NeXus file at a specified path. It is assumed that the data follows the CHESS
|
|
25
25
|
file structure (i.e., root/entry/data/counts, etc.).
|
|
@@ -29,6 +29,9 @@ def load_data(path):
|
|
|
29
29
|
path : str
|
|
30
30
|
The path to the NeXus data file.
|
|
31
31
|
|
|
32
|
+
print_tree : bool, optional
|
|
33
|
+
Whether to print the data tree upon loading. Default True.
|
|
34
|
+
|
|
32
35
|
Returns
|
|
33
36
|
-------
|
|
34
37
|
data : nxdata object
|
|
@@ -38,31 +41,42 @@ def load_data(path):
|
|
|
38
41
|
|
|
39
42
|
g = nxload(path)
|
|
40
43
|
try:
|
|
41
|
-
print(g.entry.data.tree)
|
|
44
|
+
print(g.entry.data.tree) if print_tree else None
|
|
42
45
|
except NeXusError:
|
|
43
46
|
pass
|
|
44
47
|
|
|
45
48
|
return g.entry.data
|
|
46
49
|
|
|
47
50
|
|
|
48
|
-
def load_transform(path):
|
|
51
|
+
def load_transform(path, print_tree=True):
|
|
49
52
|
"""
|
|
50
53
|
Load data obtained from nxrefine output from a specified path.
|
|
51
54
|
|
|
52
55
|
Parameters
|
|
53
56
|
----------
|
|
54
|
-
path : str
|
|
57
|
+
path : str
|
|
58
|
+
The path to the transform data file.
|
|
59
|
+
|
|
60
|
+
print_tree : bool, optional
|
|
61
|
+
Whether to print the data tree upon loading. Default True.
|
|
55
62
|
|
|
56
63
|
Returns
|
|
57
64
|
-------
|
|
58
|
-
data : nxdata object
|
|
65
|
+
data : nxdata object
|
|
66
|
+
The loaded data stored in a nxdata object.
|
|
59
67
|
"""
|
|
68
|
+
|
|
60
69
|
g = nxload(path)
|
|
61
|
-
|
|
70
|
+
|
|
71
|
+
data = NXdata(NXfield(g.entry.transform.data.nxdata.transpose(2, 1, 0), name='counts'),
|
|
62
72
|
(g.entry.transform.Qh, g.entry.transform.Qk, g.entry.transform.Ql))
|
|
63
73
|
|
|
74
|
+
print(data.tree) if print_tree else None
|
|
75
|
+
|
|
76
|
+
return data
|
|
77
|
+
|
|
64
78
|
|
|
65
|
-
def array_to_nxdata(array, data_template, signal_name=
|
|
79
|
+
def array_to_nxdata(array, data_template, signal_name=None):
|
|
66
80
|
"""
|
|
67
81
|
Create an NXdata object from an input array and an NXdata template,
|
|
68
82
|
with an optional signal name.
|
|
@@ -78,7 +92,7 @@ def array_to_nxdata(array, data_template, signal_name='counts'):
|
|
|
78
92
|
|
|
79
93
|
signal_name : str, optional
|
|
80
94
|
The name of the signal within the NXdata object. If not provided,
|
|
81
|
-
the
|
|
95
|
+
the signal name is inherited from the data_template.
|
|
82
96
|
|
|
83
97
|
Returns
|
|
84
98
|
-------
|
|
@@ -87,11 +101,13 @@ def array_to_nxdata(array, data_template, signal_name='counts'):
|
|
|
87
101
|
based on the template.
|
|
88
102
|
"""
|
|
89
103
|
d = data_template
|
|
104
|
+
if signal_name is None:
|
|
105
|
+
signal_name = d.signal
|
|
90
106
|
return NXdata(NXfield(array, name=signal_name),
|
|
91
107
|
tuple(d[d.axes[i]] for i in range(len(d.axes))))
|
|
92
108
|
|
|
93
109
|
|
|
94
|
-
def plot_slice(data, X=None, Y=None, transpose=False, vmin=None, vmax=None,
|
|
110
|
+
def plot_slice(data, X=None, Y=None, sum_axis=None, transpose=False, vmin=None, vmax=None,
|
|
95
111
|
skew_angle=90, ax=None, xlim=None, ylim=None,
|
|
96
112
|
xticks=None, yticks=None, cbar=True, logscale=False,
|
|
97
113
|
symlogscale=False, cmap='viridis', linthresh=1,
|
|
@@ -114,6 +130,10 @@ def plot_slice(data, X=None, Y=None, transpose=False, vmin=None, vmax=None,
|
|
|
114
130
|
The Y axis values. If None, a default range from 0 to the number of
|
|
115
131
|
rows in `data` is used.
|
|
116
132
|
|
|
133
|
+
sum_axis : int, optional
|
|
134
|
+
If the input data is 3D, this specifies the axis to sum over in order
|
|
135
|
+
to reduce the data to 2D for plotting. Required if `data` has three dimensions.
|
|
136
|
+
|
|
117
137
|
transpose : bool, optional
|
|
118
138
|
If True, transpose the dataset and its axes before plotting.
|
|
119
139
|
Default is False.
|
|
@@ -183,7 +203,37 @@ def plot_slice(data, X=None, Y=None, transpose=False, vmin=None, vmax=None,
|
|
|
183
203
|
p : :class:`matplotlib.collections.QuadMesh`
|
|
184
204
|
The `matplotlib` QuadMesh object representing the plotted data.
|
|
185
205
|
"""
|
|
206
|
+
is_array = False
|
|
207
|
+
is_nxdata = False
|
|
208
|
+
|
|
186
209
|
if isinstance(data, np.ndarray):
|
|
210
|
+
is_array = True
|
|
211
|
+
elif isinstance(data, (NXdata, NXfield)):
|
|
212
|
+
is_nxdata = True
|
|
213
|
+
else:
|
|
214
|
+
raise TypeError(f"Unexpected data type: {type(data)}. "
|
|
215
|
+
f"Supported types are np.ndarray and NXdata.")
|
|
216
|
+
|
|
217
|
+
# If three-dimensional, demand sum_axis to reduce to two dimensions.
|
|
218
|
+
if is_array and len(data.shape) == 3:
|
|
219
|
+
assert sum_axis is not None, "sum_axis must be specified when data is a 3D array"
|
|
220
|
+
|
|
221
|
+
data = data.sum(axis=sum_axis)
|
|
222
|
+
|
|
223
|
+
if is_nxdata and len(data.shape) == 3:
|
|
224
|
+
assert sum_axis is not None, "sum_axis must be specified when data is a 3D array"
|
|
225
|
+
|
|
226
|
+
arr = data.nxsignal.nxdata
|
|
227
|
+
arr = arr.sum(axis=sum_axis)
|
|
228
|
+
|
|
229
|
+
# Create a 2D template from the original nxdata
|
|
230
|
+
slice_obj = [slice(None)] * len(data.shape)
|
|
231
|
+
slice_obj[sum_axis] = 0
|
|
232
|
+
|
|
233
|
+
# Use the 2D template to create a new nxdata
|
|
234
|
+
data = array_to_nxdata(arr, data[slice_obj])
|
|
235
|
+
|
|
236
|
+
if is_array:
|
|
187
237
|
if X is None:
|
|
188
238
|
X = NXfield(np.linspace(0, data.shape[0], data.shape[0]), name='x')
|
|
189
239
|
if Y is None:
|
|
@@ -193,7 +243,7 @@ def plot_slice(data, X=None, Y=None, transpose=False, vmin=None, vmax=None,
|
|
|
193
243
|
data = data.transpose()
|
|
194
244
|
data = NXdata(NXfield(data, name='value'), (X, Y))
|
|
195
245
|
data_arr = data[data.signal].nxdata.transpose()
|
|
196
|
-
elif
|
|
246
|
+
elif is_nxdata:
|
|
197
247
|
if X is None:
|
|
198
248
|
X = data[data.axes[0]]
|
|
199
249
|
if Y is None:
|
|
@@ -202,9 +252,6 @@ def plot_slice(data, X=None, Y=None, transpose=False, vmin=None, vmax=None,
|
|
|
202
252
|
X, Y = Y, X
|
|
203
253
|
data = data.transpose()
|
|
204
254
|
data_arr = data[data.signal].nxdata.transpose()
|
|
205
|
-
else:
|
|
206
|
-
raise TypeError(f"Unexpected data type: {type(data)}. "
|
|
207
|
-
f"Supported types are np.ndarray and NXdata.")
|
|
208
255
|
|
|
209
256
|
# Display Markdown heading
|
|
210
257
|
if mdheading is None:
|
|
@@ -543,22 +590,31 @@ class Scissors:
|
|
|
543
590
|
|
|
544
591
|
return self.linecut
|
|
545
592
|
|
|
546
|
-
def highlight_integration_window(self, data=None, label=None, highlight_color='red',
|
|
593
|
+
def highlight_integration_window(self, data=None, width=None, height=None, label=None, highlight_color='red',
|
|
594
|
+
**kwargs):
|
|
547
595
|
"""
|
|
548
|
-
Plots integration window highlighted on the three principal cross
|
|
549
|
-
temperature dataset.
|
|
596
|
+
Plots the integration window highlighted on the three principal 2D cross-sections of a 3D dataset.
|
|
550
597
|
|
|
551
598
|
Parameters
|
|
552
599
|
----------
|
|
553
600
|
data : array-like, optional
|
|
554
|
-
The
|
|
555
|
-
|
|
601
|
+
The 3D dataset to visualize. If not provided, uses `self.data`.
|
|
602
|
+
width : float, optional
|
|
603
|
+
Width of the visible x-axis range in each subplot. Used to zoom in on the integration region.
|
|
604
|
+
height : float, optional
|
|
605
|
+
Height of the visible y-axis range in each subplot. Used to zoom in on the integration region.
|
|
556
606
|
label : str, optional
|
|
557
|
-
|
|
607
|
+
Label for the rectangle patch marking the integration window, used in the legend.
|
|
558
608
|
highlight_color : str, optional
|
|
559
|
-
|
|
560
|
-
**kwargs :
|
|
561
|
-
Additional keyword arguments to
|
|
609
|
+
Color of the rectangle edges highlighting the integration window. Default is 'red'.
|
|
610
|
+
**kwargs : dict, optional
|
|
611
|
+
Additional keyword arguments passed to `plot_slice` for customizing the plot (e.g., colormap, vmin, vmax).
|
|
612
|
+
|
|
613
|
+
Returns
|
|
614
|
+
-------
|
|
615
|
+
p1, p2, p3 : matplotlib.collections.QuadMesh
|
|
616
|
+
The plotted QuadMesh objects for the three cross-sections:
|
|
617
|
+
XY at fixed Z, XZ at fixed Y, and YZ at fixed X.
|
|
562
618
|
|
|
563
619
|
"""
|
|
564
620
|
data = self.data if data is None else data
|
|
@@ -568,7 +624,7 @@ class Scissors:
|
|
|
568
624
|
# Create a figure and subplots
|
|
569
625
|
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
|
|
570
626
|
|
|
571
|
-
# Plot cross
|
|
627
|
+
# Plot cross-section 1
|
|
572
628
|
slice_obj = [slice(None)] * data.ndim
|
|
573
629
|
slice_obj[2] = center[2]
|
|
574
630
|
|
|
@@ -587,7 +643,12 @@ class Scissors:
|
|
|
587
643
|
)
|
|
588
644
|
ax.add_patch(rect_diffuse)
|
|
589
645
|
|
|
590
|
-
|
|
646
|
+
if 'xlim' not in kwargs and width is not None:
|
|
647
|
+
ax.set(xlim=(center[0] - width / 2, center[0] + width / 2))
|
|
648
|
+
if 'ylim' not in kwargs and height is not None:
|
|
649
|
+
ax.set(ylim=(center[1] - height / 2, center[1] + height / 2))
|
|
650
|
+
|
|
651
|
+
# Plot cross-section 2
|
|
591
652
|
slice_obj = [slice(None)] * data.ndim
|
|
592
653
|
slice_obj[1] = center[1]
|
|
593
654
|
|
|
@@ -606,7 +667,12 @@ class Scissors:
|
|
|
606
667
|
)
|
|
607
668
|
ax.add_patch(rect_diffuse)
|
|
608
669
|
|
|
609
|
-
|
|
670
|
+
if 'xlim' not in kwargs and width is not None:
|
|
671
|
+
ax.set(xlim=(center[0] - width / 2, center[0] + width / 2))
|
|
672
|
+
if 'ylim' not in kwargs and height is not None:
|
|
673
|
+
ax.set(ylim=(center[2] - height / 2, center[2] + height / 2))
|
|
674
|
+
|
|
675
|
+
# Plot cross-section 3
|
|
610
676
|
slice_obj = [slice(None)] * data.ndim
|
|
611
677
|
slice_obj[0] = center[0]
|
|
612
678
|
|
|
@@ -625,6 +691,11 @@ class Scissors:
|
|
|
625
691
|
)
|
|
626
692
|
ax.add_patch(rect_diffuse)
|
|
627
693
|
|
|
694
|
+
if 'xlim' not in kwargs and width is not None:
|
|
695
|
+
ax.set(xlim=(center[1] - width / 2, center[1] + width / 2))
|
|
696
|
+
if 'ylim' not in kwargs and height is not None:
|
|
697
|
+
ax.set(ylim=(center[2] - height / 2, center[2] + height / 2))
|
|
698
|
+
|
|
628
699
|
# Adjust subplot padding
|
|
629
700
|
fig.subplots_adjust(wspace=0.5)
|
|
630
701
|
|
|
@@ -649,7 +720,7 @@ class Scissors:
|
|
|
649
720
|
|
|
650
721
|
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
|
|
651
722
|
|
|
652
|
-
# Plot cross
|
|
723
|
+
# Plot cross-section 1
|
|
653
724
|
slice_obj = [slice(None)] * data.ndim
|
|
654
725
|
slice_obj[2] = center[2]
|
|
655
726
|
p1 = plot_slice(data[slice_obj],
|
|
@@ -669,7 +740,7 @@ class Scissors:
|
|
|
669
740
|
**kwargs)
|
|
670
741
|
axes[1].set_aspect(len(data[data.axes[0]].nxdata) / len(data[data.axes[2]].nxdata))
|
|
671
742
|
|
|
672
|
-
# Plot cross
|
|
743
|
+
# Plot cross-section 3
|
|
673
744
|
slice_obj = [slice(None)] * data.ndim
|
|
674
745
|
slice_obj[0] = center[0]
|
|
675
746
|
p2 = plot_slice(data[slice_obj],
|
|
@@ -999,7 +1070,7 @@ class Padder:
|
|
|
999
1070
|
self.data = data
|
|
1000
1071
|
|
|
1001
1072
|
self.steps = tuple((data[axis].nxdata[1] - data[axis].nxdata[0])
|
|
1002
|
-
|
|
1073
|
+
for axis in data.axes)
|
|
1003
1074
|
|
|
1004
1075
|
# Absolute value of the maximum value; assumes the domain of the input
|
|
1005
1076
|
# is symmetric (eg, -H_min = H_max)
|
{nxs_analysis_tools-0.0.45 → nxs_analysis_tools-0.0.47}/src/nxs_analysis_tools/pairdistribution.py
RENAMED
|
@@ -458,20 +458,35 @@ class Symmetrizer3D:
|
|
|
458
458
|
self.a_star, self.b_star, self.c_star, \
|
|
459
459
|
self.al_star, self.be_star, self.ga_star = self.reciprocal_lattice_params
|
|
460
460
|
|
|
461
|
-
def symmetrize(self):
|
|
461
|
+
def symmetrize(self, positive_values=True):
|
|
462
462
|
"""
|
|
463
|
-
|
|
464
|
-
|
|
463
|
+
Symmetrize the 3D dataset by sequentially applying 2D symmetrization
|
|
464
|
+
on the three principal planes.
|
|
465
465
|
|
|
466
|
-
This method
|
|
467
|
-
|
|
468
|
-
|
|
466
|
+
This method performs symmetrization along the (q1-q2), (q1-q3),
|
|
467
|
+
and (q2-q3) planes, ensuring that the dataset maintains expected
|
|
468
|
+
symmetry properties. Optionally, negative values resulting from the
|
|
469
|
+
symmetrization process can be set to zero.
|
|
470
|
+
|
|
471
|
+
Parameters
|
|
472
|
+
----------
|
|
473
|
+
positive_values : bool, optional
|
|
474
|
+
If True, sets negative symmetrized values to zero (default is True).
|
|
469
475
|
|
|
470
476
|
Returns
|
|
471
477
|
-------
|
|
472
|
-
|
|
473
|
-
The symmetrized 3D dataset.
|
|
478
|
+
NXdata
|
|
479
|
+
The symmetrized 3D dataset stored in the `symmetrized` attribute.
|
|
480
|
+
|
|
481
|
+
Notes
|
|
482
|
+
-----
|
|
483
|
+
- Symmetrization is performed sequentially across three principal
|
|
484
|
+
planes using corresponding 2D symmetrization methods.
|
|
485
|
+
- The process prints progress updates and timing information.
|
|
486
|
+
- If `theta_max` is not set for a particular plane symmetrizer,
|
|
487
|
+
that plane is skipped.
|
|
474
488
|
"""
|
|
489
|
+
|
|
475
490
|
starttime = time.time()
|
|
476
491
|
data = self.data
|
|
477
492
|
q1, q2, q3 = self.q1, self.q2, self.q3
|
|
@@ -480,7 +495,8 @@ class Symmetrizer3D:
|
|
|
480
495
|
if self.plane1symmetrizer.theta_max is not None:
|
|
481
496
|
print('Symmetrizing ' + self.plane1 + ' planes...')
|
|
482
497
|
for k, value in enumerate(q3):
|
|
483
|
-
print(f'Symmetrizing {q3.nxname}={value:.02f}
|
|
498
|
+
print(f'Symmetrizing {q3.nxname}={value:.02f}.'
|
|
499
|
+
f'..', end='\r')
|
|
484
500
|
data_symmetrized = self.plane1symmetrizer.symmetrize_2d(data[:, :, k])
|
|
485
501
|
out_array[:, :, k] = data_symmetrized[data.signal].nxdata
|
|
486
502
|
print('\nSymmetrized ' + self.plane1 + ' planes.')
|
|
@@ -505,7 +521,8 @@ class Symmetrizer3D:
|
|
|
505
521
|
out_array[i, :, :] = data_symmetrized[data.signal].nxdata
|
|
506
522
|
print('\nSymmetrized ' + self.plane3 + ' planes.')
|
|
507
523
|
|
|
508
|
-
|
|
524
|
+
if positive_values:
|
|
525
|
+
out_array[out_array < 0] = 0
|
|
509
526
|
|
|
510
527
|
stoptime = time.time()
|
|
511
528
|
print(f"\nSymmetrization finished in {((stoptime - starttime) / 60):.02f} minutes.")
|
|
@@ -1032,15 +1049,28 @@ class Interpolator:
|
|
|
1032
1049
|
"""
|
|
1033
1050
|
self.kernel = kernel
|
|
1034
1051
|
|
|
1035
|
-
def interpolate(self):
|
|
1052
|
+
def interpolate(self, verbose=True, positive_values=True):
|
|
1036
1053
|
"""
|
|
1037
1054
|
Perform interpolation on the dataset using the specified kernel.
|
|
1038
1055
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1056
|
+
This method convolves the dataset with a kernel using `convolve_fft`
|
|
1057
|
+
to perform interpolation. The resulting interpolated data is stored
|
|
1058
|
+
in the `interpolated` attribute.
|
|
1059
|
+
|
|
1060
|
+
Parameters
|
|
1061
|
+
----------
|
|
1062
|
+
verbose : bool, optional
|
|
1063
|
+
If True, prints progress messages and timing information
|
|
1064
|
+
(default is True).
|
|
1065
|
+
positive_values : bool, optional
|
|
1066
|
+
If True, sets negative interpolated values to zero
|
|
1067
|
+
(default is True).
|
|
1042
1068
|
|
|
1043
|
-
|
|
1069
|
+
Notes
|
|
1070
|
+
-----
|
|
1071
|
+
- The convolution operation is performed in Fourier space.
|
|
1072
|
+
- If a previous interpolation time is recorded, it is displayed
|
|
1073
|
+
before starting a new interpolation.
|
|
1044
1074
|
|
|
1045
1075
|
Returns
|
|
1046
1076
|
-------
|
|
@@ -1048,21 +1078,22 @@ class Interpolator:
|
|
|
1048
1078
|
"""
|
|
1049
1079
|
start = time.time()
|
|
1050
1080
|
|
|
1051
|
-
if self.interp_time:
|
|
1081
|
+
if self.interp_time and verbose:
|
|
1052
1082
|
print(f"Last interpolation took {self.interp_time / 60:.2f} minutes.")
|
|
1053
1083
|
|
|
1054
|
-
print("Running interpolation...")
|
|
1084
|
+
print("Running interpolation...") if verbose else None
|
|
1055
1085
|
result = np.real(
|
|
1056
1086
|
convolve_fft(self.data[self.data.signal].nxdata,
|
|
1057
1087
|
self.kernel, allow_huge=True, return_fft=False))
|
|
1058
|
-
print("Interpolation finished.")
|
|
1088
|
+
print("Interpolation finished.") if verbose else None
|
|
1059
1089
|
|
|
1060
1090
|
end = time.time()
|
|
1061
1091
|
interp_time = end - start
|
|
1062
1092
|
|
|
1063
|
-
print(f'Interpolation took {interp_time / 60:.2f} minutes.')
|
|
1093
|
+
print(f'Interpolation took {interp_time / 60:.2f} minutes.') if verbose else None
|
|
1064
1094
|
|
|
1065
|
-
|
|
1095
|
+
if positive_values:
|
|
1096
|
+
result[result < 0] = 0
|
|
1066
1097
|
self.interpolated = array_to_nxdata(result, self.data)
|
|
1067
1098
|
|
|
1068
1099
|
def set_tukey_window(self, tukey_alphas=(1.0, 1.0, 1.0)):
|
|
@@ -1479,21 +1510,34 @@ class DeltaPDF:
|
|
|
1479
1510
|
self.interpolator.set_kernel(kernel)
|
|
1480
1511
|
self.kernel = kernel
|
|
1481
1512
|
|
|
1482
|
-
def interpolate(self):
|
|
1513
|
+
def interpolate(self, verbose=True, positive_values=True):
|
|
1483
1514
|
"""
|
|
1484
1515
|
Perform interpolation on the dataset using the specified kernel.
|
|
1485
1516
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1517
|
+
This method convolves the dataset with a kernel using `convolve_fft`
|
|
1518
|
+
to perform interpolation. The resulting interpolated data is stored
|
|
1519
|
+
in the `interpolated` attribute.
|
|
1520
|
+
|
|
1521
|
+
Parameters
|
|
1522
|
+
----------
|
|
1523
|
+
verbose : bool, optional
|
|
1524
|
+
If True, prints progress messages and timing information
|
|
1525
|
+
(default is True).
|
|
1526
|
+
positive_values : bool, optional
|
|
1527
|
+
If True, sets negative interpolated values to zero
|
|
1528
|
+
(default is True).
|
|
1489
1529
|
|
|
1490
|
-
|
|
1530
|
+
Notes
|
|
1531
|
+
-----
|
|
1532
|
+
- The convolution operation is performed in Fourier space.
|
|
1533
|
+
- If a previous interpolation time is recorded, it is displayed
|
|
1534
|
+
before starting a new interpolation.
|
|
1491
1535
|
|
|
1492
1536
|
Returns
|
|
1493
1537
|
-------
|
|
1494
1538
|
None
|
|
1495
1539
|
"""
|
|
1496
|
-
self.interpolator.interpolate()
|
|
1540
|
+
self.interpolator.interpolate(verbose, positive_values)
|
|
1497
1541
|
self.interpolated = self.interpolator.interpolated
|
|
1498
1542
|
|
|
1499
1543
|
def set_tukey_window(self, tukey_alphas=(1.0, 1.0, 1.0)):
|
|
@@ -1578,14 +1622,16 @@ class DeltaPDF:
|
|
|
1578
1622
|
"""
|
|
1579
1623
|
Perform a 3D Fourier Transform on the padded data.
|
|
1580
1624
|
|
|
1581
|
-
This
|
|
1582
|
-
using
|
|
1583
|
-
|
|
1584
|
-
|
|
1625
|
+
This method applies an inverse Fourier Transform to the padded data
|
|
1626
|
+
using `pyfftw` for optimized performance. The result is stored in
|
|
1627
|
+
the `fft` attribute as an NXdata object containing the transformed
|
|
1628
|
+
spatial frequency components.
|
|
1585
1629
|
|
|
1586
1630
|
Parameters
|
|
1587
1631
|
----------
|
|
1588
|
-
|
|
1632
|
+
is_2d : bool, optional
|
|
1633
|
+
If True, performs the FFT only along the first two axes,
|
|
1634
|
+
skipping the out-of-plane direction (default is False).
|
|
1589
1635
|
|
|
1590
1636
|
Returns
|
|
1591
1637
|
-------
|
|
@@ -1593,13 +1639,12 @@ class DeltaPDF:
|
|
|
1593
1639
|
|
|
1594
1640
|
Notes
|
|
1595
1641
|
-----
|
|
1596
|
-
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
of the original data axes.
|
|
1602
|
-
|
|
1642
|
+
- Calls `fourier_transform_nxdata` to perform the transformation.
|
|
1643
|
+
- The FFT is computed in two stages: first along the last dimension,
|
|
1644
|
+
then along the first two dimensions.
|
|
1645
|
+
- The output includes frequency components computed from the step
|
|
1646
|
+
sizes of the original data axes.
|
|
1603
1647
|
|
|
1604
1648
|
"""
|
|
1649
|
+
|
|
1605
1650
|
self.fft = fourier_transform_nxdata(self.padded, is_2d=is_2d)
|