diffpy.utils 0.6.0rc0__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.
- diffpy/__init__.py +29 -0
- diffpy/utils/__init__.py +21 -0
- diffpy/utils/diffraction_objects.py +590 -0
- diffpy/utils/parsers/__init__.py +15 -0
- diffpy/utils/parsers/custom_exceptions.py +55 -0
- diffpy/utils/parsers/loaddata.py +335 -0
- diffpy/utils/parsers/serialization.py +204 -0
- diffpy/utils/resampler.py +181 -0
- diffpy/utils/tools.py +347 -0
- diffpy/utils/transforms.py +213 -0
- diffpy/utils/validators.py +47 -0
- diffpy/utils/version.py +23 -0
- diffpy/utils/wx/__init__.py +15 -0
- diffpy/utils/wx/gridutils.py +166 -0
- diffpy.utils-0.6.0rc0.dist-info/AUTHORS.rst +10 -0
- diffpy.utils-0.6.0rc0.dist-info/LICENSE.rst +141 -0
- diffpy.utils-0.6.0rc0.dist-info/LICENSE_DANSE.txt +41 -0
- diffpy.utils-0.6.0rc0.dist-info/METADATA +174 -0
- diffpy.utils-0.6.0rc0.dist-info/RECORD +21 -0
- diffpy.utils-0.6.0rc0.dist-info/WHEEL +5 -0
- diffpy.utils-0.6.0rc0.dist-info/top_level.txt +1 -0
diffpy/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2010 The Trustees of Columbia University
|
|
7
|
+
# in the City of New York. All rights reserved.
|
|
8
|
+
# (c) 2024 The Trustees of Columbia University in the City of New York.
|
|
9
|
+
# All rights reserved.
|
|
10
|
+
#
|
|
11
|
+
# File coded by: Billinge Group members and community contributors.
|
|
12
|
+
#
|
|
13
|
+
# See GitHub contributions for a more detailed list of contributors.
|
|
14
|
+
# https://github.com/diffpy/diffpy.utils/graphs/contributors
|
|
15
|
+
#
|
|
16
|
+
# See LICENSE.rst for license information.
|
|
17
|
+
#
|
|
18
|
+
##############################################################################
|
|
19
|
+
"""diffpy - tools for structure analysis by diffraction.
|
|
20
|
+
|
|
21
|
+
Blank namespace package.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
from pkgutil import extend_path
|
|
26
|
+
|
|
27
|
+
__path__ = extend_path(__path__, __name__)
|
|
28
|
+
|
|
29
|
+
# End of file
|
diffpy/utils/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# (c) 2024 The Trustees of Columbia University in the City of New York.
|
|
5
|
+
# All rights reserved.
|
|
6
|
+
#
|
|
7
|
+
# File coded by: Billinge Group members and community contributors.
|
|
8
|
+
#
|
|
9
|
+
# See GitHub contributions for a more detailed list of contributors.
|
|
10
|
+
# https://github.com/diffpy/diffpy.utils/graphs/contributors
|
|
11
|
+
#
|
|
12
|
+
# See LICENSE.rst for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
"""Shared utilities for diffpy packages."""
|
|
16
|
+
|
|
17
|
+
# package version
|
|
18
|
+
from diffpy.utils.version import __version__
|
|
19
|
+
|
|
20
|
+
# silence the pyflakes syntax checker
|
|
21
|
+
assert __version__ or True
|
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import uuid
|
|
3
|
+
import warnings
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from diffpy.utils.tools import get_package_info
|
|
9
|
+
from diffpy.utils.transforms import d_to_q, d_to_tth, q_to_d, q_to_tth, tth_to_d, tth_to_q
|
|
10
|
+
|
|
11
|
+
QQUANTITIES = ["q"]
|
|
12
|
+
ANGLEQUANTITIES = ["angle", "tth", "twotheta", "2theta"]
|
|
13
|
+
DQUANTITIES = ["d", "dspace"]
|
|
14
|
+
XQUANTITIES = ANGLEQUANTITIES + DQUANTITIES + QQUANTITIES
|
|
15
|
+
XUNITS = ["degrees", "radians", "rad", "deg", "inv_angs", "inv_nm", "nm-1", "A-1"]
|
|
16
|
+
|
|
17
|
+
x_values_not_equal_emsg = (
|
|
18
|
+
"The two objects have different values in x arrays (my_do.all_arrays[:, [1, 2, 3]]). "
|
|
19
|
+
"Please ensure the x values of the two objects are identical by re-instantiating "
|
|
20
|
+
"the DiffractionObject with the correct x value inputs."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
invalid_add_type_emsg = (
|
|
24
|
+
"You may only add a DiffractionObject with another DiffractionObject or a scalar value. "
|
|
25
|
+
"Please rerun by adding another DiffractionObject instance or a scalar value. "
|
|
26
|
+
"e.g., my_do_1 + my_do_2 or my_do + 10 or 10 + my_do"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _xtype_wmsg(xtype):
|
|
31
|
+
return (
|
|
32
|
+
f"I don't know how to handle the xtype, '{xtype}'. "
|
|
33
|
+
f"Please rerun specifying an xtype from {*XQUANTITIES, }"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _setter_wmsg(attribute):
|
|
38
|
+
return (
|
|
39
|
+
f"Direct modification of attribute '{attribute}' is not allowed. "
|
|
40
|
+
f"Please use 'input_data' to modify '{attribute}'.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DiffractionObject:
|
|
45
|
+
"""Class for storing and manipulating diffraction data.
|
|
46
|
+
|
|
47
|
+
DiffractionObject stores data produced from X-ray, neutron,
|
|
48
|
+
and electron scattering experiments. The object can transform
|
|
49
|
+
between different scattering quantities such as q (scattering vector),
|
|
50
|
+
2θ (two-theta angle), and d (interplanar spacing), and perform various
|
|
51
|
+
operations like scaling, addition, subtraction, and comparison for equality
|
|
52
|
+
between diffraction objects.
|
|
53
|
+
|
|
54
|
+
Attributes
|
|
55
|
+
----------
|
|
56
|
+
scat_quantity : str
|
|
57
|
+
The type of scattering experiment (e.g., "x-ray", "neutron"). Default is an empty string "".
|
|
58
|
+
wavelength : float
|
|
59
|
+
The wavelength of the incoming beam, specified in angstroms (Å). Default is none.
|
|
60
|
+
name: str
|
|
61
|
+
The name or label for the scattering data. Default is an empty string "".
|
|
62
|
+
qmin : float
|
|
63
|
+
The minimum q value.
|
|
64
|
+
qmax : float
|
|
65
|
+
The maximum q value.
|
|
66
|
+
tthmin : float
|
|
67
|
+
The minimum two-theta value.
|
|
68
|
+
tthmax : float
|
|
69
|
+
The maximum two-theta value.
|
|
70
|
+
dmin : float
|
|
71
|
+
The minimum d-spacing value.
|
|
72
|
+
dmax : float
|
|
73
|
+
The maximum d-spacing value.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
xarray,
|
|
79
|
+
yarray,
|
|
80
|
+
xtype,
|
|
81
|
+
wavelength=None,
|
|
82
|
+
scat_quantity="",
|
|
83
|
+
name="",
|
|
84
|
+
metadata={},
|
|
85
|
+
):
|
|
86
|
+
"""Initialize a DiffractionObject instance.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
xarray : ndarray
|
|
91
|
+
The independent variable array containing "q", "tth", or "d" values.
|
|
92
|
+
yarray : ndarray
|
|
93
|
+
The dependent variable array corresponding to intensity values.
|
|
94
|
+
xtype : str
|
|
95
|
+
The type of the independent variable in `xarray`. Must be one of {*XQUANTITIES}.
|
|
96
|
+
wavelength : float, optional, default is None.
|
|
97
|
+
The wavelength of the incoming beam, specified in angstroms (Å)
|
|
98
|
+
scat_quantity : str, optional, default is an empty string "".
|
|
99
|
+
The type of scattering experiment (e.g., "x-ray", "neutron").
|
|
100
|
+
name : str, optional, default is an empty string "".
|
|
101
|
+
The name or label for the scattering data.
|
|
102
|
+
metadata : dict, optional, default is an empty dictionary {}
|
|
103
|
+
The additional metadata associated with the diffraction object.
|
|
104
|
+
|
|
105
|
+
Examples
|
|
106
|
+
--------
|
|
107
|
+
Create a DiffractionObject for X-ray scattering data:
|
|
108
|
+
>>> import numpy as np
|
|
109
|
+
>>> from diffpy.utils.diffraction_objects import DiffractionObject
|
|
110
|
+
...
|
|
111
|
+
>>> x = np.array([0.12, 0.24, 0.31, 0.4]) # independent variable (e.g., q)
|
|
112
|
+
>>> y = np.array([10, 20, 40, 60]) # intensity values
|
|
113
|
+
>>> metadata = {
|
|
114
|
+
... "sample": "rock salt from the beach",
|
|
115
|
+
... "composition": "NaCl",
|
|
116
|
+
... "temperature": "300 K,",
|
|
117
|
+
... "experimenters": "Phill, Sally"
|
|
118
|
+
... }
|
|
119
|
+
>>> do = DiffractionObject(
|
|
120
|
+
... xarray=x,
|
|
121
|
+
... yarray=y,
|
|
122
|
+
... xtype="q",
|
|
123
|
+
... wavelength=1.54,
|
|
124
|
+
... scat_quantity="x-ray",
|
|
125
|
+
... name="beach_rock_salt_1",
|
|
126
|
+
... metadata=metadata
|
|
127
|
+
... )
|
|
128
|
+
>>> print(do.metadata)
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
self._uuid = uuid.uuid4()
|
|
132
|
+
self._input_data(xarray, yarray, xtype, wavelength, scat_quantity, name, metadata)
|
|
133
|
+
|
|
134
|
+
def _input_data(self, xarray, yarray, xtype, wavelength, scat_quantity, name, metadata):
|
|
135
|
+
if xtype not in XQUANTITIES:
|
|
136
|
+
raise ValueError(_xtype_wmsg(xtype))
|
|
137
|
+
if len(xarray) != len(yarray):
|
|
138
|
+
raise ValueError(
|
|
139
|
+
"'xarray' and 'yarray' are different lengths. They must "
|
|
140
|
+
"correspond to each other and have the same length. "
|
|
141
|
+
"Please re-initialize 'DiffractionObject'"
|
|
142
|
+
"with valid 'xarray' and 'yarray's"
|
|
143
|
+
)
|
|
144
|
+
self.scat_quantity = scat_quantity
|
|
145
|
+
self.wavelength = wavelength
|
|
146
|
+
self.metadata = metadata
|
|
147
|
+
self.name = name
|
|
148
|
+
self._input_xtype = xtype
|
|
149
|
+
self._set_arrays(xarray, yarray, xtype)
|
|
150
|
+
self._set_min_max_xarray()
|
|
151
|
+
|
|
152
|
+
def __eq__(self, other):
|
|
153
|
+
if not isinstance(other, DiffractionObject):
|
|
154
|
+
return NotImplemented
|
|
155
|
+
self_attributes = [key for key in self.__dict__ if not key.startswith("_")]
|
|
156
|
+
other_attributes = [key for key in other.__dict__ if not key.startswith("_")]
|
|
157
|
+
if not sorted(self_attributes) == sorted(other_attributes):
|
|
158
|
+
return False
|
|
159
|
+
for key in self_attributes:
|
|
160
|
+
value = getattr(self, key)
|
|
161
|
+
other_value = getattr(other, key)
|
|
162
|
+
if isinstance(value, float):
|
|
163
|
+
if (
|
|
164
|
+
not (value is None and other_value is None)
|
|
165
|
+
and (value is None)
|
|
166
|
+
or (other_value is None)
|
|
167
|
+
or not np.isclose(value, other_value, rtol=1e-5)
|
|
168
|
+
):
|
|
169
|
+
return False
|
|
170
|
+
elif isinstance(value, list) and all(isinstance(i, np.ndarray) for i in value):
|
|
171
|
+
if not all(np.allclose(i, j, rtol=1e-5) for i, j in zip(value, other_value)):
|
|
172
|
+
return False
|
|
173
|
+
else:
|
|
174
|
+
if value != other_value:
|
|
175
|
+
return False
|
|
176
|
+
return True
|
|
177
|
+
|
|
178
|
+
def __add__(self, other):
|
|
179
|
+
"""Add a scalar value or another DiffractionObject to the yarray of the
|
|
180
|
+
DiffractionObject.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
other : DiffractionObject, int, or float
|
|
185
|
+
The item to be added. If `other` is a scalar value, this value will be added to each element of the
|
|
186
|
+
yarray of this DiffractionObject instance. If `other` is another DiffractionObject, the yarrays of the
|
|
187
|
+
two DiffractionObjects will be combined element-wise. The result is a new DiffractionObject instance,
|
|
188
|
+
representing the addition and using the xarray from the left-hand side DiffractionObject.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
DiffractionObject
|
|
193
|
+
The new DiffractionObject instance with modified yarray values. This instance is a deep copy of the
|
|
194
|
+
original with the additions applied.
|
|
195
|
+
|
|
196
|
+
Raises
|
|
197
|
+
------
|
|
198
|
+
ValueError
|
|
199
|
+
Raised when the xarrays of two DiffractionObject instances are not equal.
|
|
200
|
+
TypeError
|
|
201
|
+
Raised when `other` is not an instance of DiffractionObject, int, or float.
|
|
202
|
+
|
|
203
|
+
Examples
|
|
204
|
+
--------
|
|
205
|
+
Add a scalar value to the yarray of a DiffractionObject instance:
|
|
206
|
+
>>> new_do = my_do + 10.1
|
|
207
|
+
>>> new_do = 10.1 + my_do
|
|
208
|
+
|
|
209
|
+
Combine the yarrays of two DiffractionObject instances:
|
|
210
|
+
>>> new_do = my_do_1 + my_do_2
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
self._check_operation_compatibility(other)
|
|
214
|
+
summed_do = deepcopy(self)
|
|
215
|
+
if isinstance(other, (int, float)):
|
|
216
|
+
summed_do._all_arrays[:, 0] += other
|
|
217
|
+
if isinstance(other, DiffractionObject):
|
|
218
|
+
summed_do._all_arrays[:, 0] += other.all_arrays[:, 0]
|
|
219
|
+
return summed_do
|
|
220
|
+
|
|
221
|
+
__radd__ = __add__
|
|
222
|
+
|
|
223
|
+
def __sub__(self, other):
|
|
224
|
+
"""Subtract scalar value or another DiffractionObject to the yarray of
|
|
225
|
+
the DiffractionObject.
|
|
226
|
+
|
|
227
|
+
This method behaves similarly to the `__add__` method, but performs subtraction instead of addition.
|
|
228
|
+
For details on parameters, returns, and exceptions, refer to the documentation for `__add__`.
|
|
229
|
+
|
|
230
|
+
Examples
|
|
231
|
+
--------
|
|
232
|
+
Subtract a scalar value from the yarray of a DiffractionObject instance:
|
|
233
|
+
>>> new_do = my_do - 10.1
|
|
234
|
+
|
|
235
|
+
Subtract the yarrays of two DiffractionObject instances:
|
|
236
|
+
>>> new_do = my_do_1 - my_do_2
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
self._check_operation_compatibility(other)
|
|
240
|
+
subtracted_do = deepcopy(self)
|
|
241
|
+
if isinstance(other, (int, float)):
|
|
242
|
+
subtracted_do._all_arrays[:, 0] -= other
|
|
243
|
+
if isinstance(other, DiffractionObject):
|
|
244
|
+
subtracted_do._all_arrays[:, 0] -= other.all_arrays[:, 0]
|
|
245
|
+
return subtracted_do
|
|
246
|
+
|
|
247
|
+
__rsub__ = __sub__
|
|
248
|
+
|
|
249
|
+
def __mul__(self, other):
|
|
250
|
+
"""Multiply a scalar value or another DiffractionObject with the yarray
|
|
251
|
+
of this DiffractionObject.
|
|
252
|
+
|
|
253
|
+
This method behaves similarly to the `__add__` method, but performs multiplication instead of addition.
|
|
254
|
+
For details on parameters, returns, and exceptions, refer to the documentation for `__add__`.
|
|
255
|
+
|
|
256
|
+
Examples
|
|
257
|
+
--------
|
|
258
|
+
Multiply a scalar value with the yarray of a DiffractionObject instance:
|
|
259
|
+
>>> new_do = my_do * 3.5
|
|
260
|
+
|
|
261
|
+
Multiply the yarrays of two DiffractionObject instances:
|
|
262
|
+
>>> new_do = my_do_1 * my_do_2
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
self._check_operation_compatibility(other)
|
|
266
|
+
multiplied_do = deepcopy(self)
|
|
267
|
+
if isinstance(other, (int, float)):
|
|
268
|
+
multiplied_do._all_arrays[:, 0] *= other
|
|
269
|
+
if isinstance(other, DiffractionObject):
|
|
270
|
+
multiplied_do._all_arrays[:, 0] *= other.all_arrays[:, 0]
|
|
271
|
+
return multiplied_do
|
|
272
|
+
|
|
273
|
+
__rmul__ = __mul__
|
|
274
|
+
|
|
275
|
+
def __truediv__(self, other):
|
|
276
|
+
"""Divide the yarray of this DiffractionObject by a scalar value or
|
|
277
|
+
another DiffractionObject.
|
|
278
|
+
|
|
279
|
+
This method behaves similarly to the `__add__` method, but performs division instead of addition.
|
|
280
|
+
For details on parameters, returns, and exceptions, refer to the documentation for `__add__`.
|
|
281
|
+
|
|
282
|
+
Examples
|
|
283
|
+
--------
|
|
284
|
+
Divide the yarray of a DiffractionObject instance by a scalar value:
|
|
285
|
+
>>> new_do = my_do / 2.0
|
|
286
|
+
|
|
287
|
+
Divide the yarrays of two DiffractionObject instances:
|
|
288
|
+
>>> new_do = my_do_1 / my_do_2
|
|
289
|
+
"""
|
|
290
|
+
self._check_operation_compatibility(other)
|
|
291
|
+
divided_do = deepcopy(self)
|
|
292
|
+
if isinstance(other, (int, float)):
|
|
293
|
+
divided_do._all_arrays[:, 0] /= other
|
|
294
|
+
if isinstance(other, DiffractionObject):
|
|
295
|
+
divided_do._all_arrays[:, 0] /= other.all_arrays[:, 0]
|
|
296
|
+
return divided_do
|
|
297
|
+
|
|
298
|
+
__rtruediv__ = __truediv__
|
|
299
|
+
|
|
300
|
+
def _check_operation_compatibility(self, other):
|
|
301
|
+
if not isinstance(other, (DiffractionObject, int, float)):
|
|
302
|
+
raise TypeError(invalid_add_type_emsg)
|
|
303
|
+
if isinstance(other, DiffractionObject):
|
|
304
|
+
if self.all_arrays.shape != other.all_arrays.shape:
|
|
305
|
+
raise ValueError(x_values_not_equal_emsg)
|
|
306
|
+
if not np.allclose(self.all_arrays[:, [1, 2, 3]], other.all_arrays[:, [1, 2, 3]]):
|
|
307
|
+
raise ValueError(x_values_not_equal_emsg)
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def all_arrays(self):
|
|
311
|
+
"""The 2D array containing `xarray` and `yarray` values.
|
|
312
|
+
|
|
313
|
+
Returns
|
|
314
|
+
-------
|
|
315
|
+
ndarray
|
|
316
|
+
The shape (len(data), 4) 2D array with columns containing the `yarray` (intensity)
|
|
317
|
+
and the `xarray` values in q, tth, and d.
|
|
318
|
+
|
|
319
|
+
Examples
|
|
320
|
+
--------
|
|
321
|
+
To access specific arrays individually, use these slices:
|
|
322
|
+
|
|
323
|
+
>>> my_do.all_arrays[:, 0] # yarray
|
|
324
|
+
>>> my_do.all_arrays[:, 1] # xarray in q
|
|
325
|
+
>>> my_do.all_arrays[:, 2] # xarray in tth
|
|
326
|
+
>>> my_do.all_arrays[:, 3] # xarray in d
|
|
327
|
+
"""
|
|
328
|
+
return self._all_arrays
|
|
329
|
+
|
|
330
|
+
@all_arrays.setter
|
|
331
|
+
def all_arrays(self, _):
|
|
332
|
+
raise AttributeError(_setter_wmsg("all_arrays"))
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def input_xtype(self):
|
|
336
|
+
"""The type of the independent variable in `xarray`.
|
|
337
|
+
|
|
338
|
+
Returns
|
|
339
|
+
-------
|
|
340
|
+
input_xtype : str
|
|
341
|
+
The type of `xarray`, which must be one of {*XQUANTITIES}.
|
|
342
|
+
"""
|
|
343
|
+
return self._input_xtype
|
|
344
|
+
|
|
345
|
+
@input_xtype.setter
|
|
346
|
+
def input_xtype(self, _):
|
|
347
|
+
raise AttributeError(_setter_wmsg("input_xtype"))
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def uuid(self):
|
|
351
|
+
"""The unique identifier for the DiffractionObject instance.
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
uuid : UUID
|
|
356
|
+
The unique identifier of the DiffractionObject instance.
|
|
357
|
+
"""
|
|
358
|
+
return self._uuid
|
|
359
|
+
|
|
360
|
+
@uuid.setter
|
|
361
|
+
def uuid(self, _):
|
|
362
|
+
raise AttributeError(_setter_wmsg("uuid"))
|
|
363
|
+
|
|
364
|
+
def get_array_index(self, xtype, xvalue):
|
|
365
|
+
"""Return the index of the closest value in the array associated with
|
|
366
|
+
the specified xtype and the value provided.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
xtype : str
|
|
371
|
+
The type of the independent variable in `xarray`. Must be one of {*XQUANTITIES}.
|
|
372
|
+
xvalue : float
|
|
373
|
+
The value of the xtype to find the closest index for.
|
|
374
|
+
|
|
375
|
+
Returns
|
|
376
|
+
-------
|
|
377
|
+
index : int
|
|
378
|
+
The index of the closest value in the array associated with the specified xtype and the value provided.
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
xtype = self._input_xtype
|
|
382
|
+
xarray = self.on_xtype(xtype)[0]
|
|
383
|
+
if len(xarray) == 0:
|
|
384
|
+
raise ValueError(f"The '{xtype}' array is empty. Please ensure it is initialized.")
|
|
385
|
+
index = (np.abs(xarray - xvalue)).argmin()
|
|
386
|
+
return index
|
|
387
|
+
|
|
388
|
+
def _set_arrays(self, xarray, yarray, xtype):
|
|
389
|
+
self._all_arrays = np.empty(shape=(len(xarray), 4))
|
|
390
|
+
self._all_arrays[:, 0] = yarray
|
|
391
|
+
if xtype.lower() in QQUANTITIES:
|
|
392
|
+
self._all_arrays[:, 1] = xarray
|
|
393
|
+
self._all_arrays[:, 2] = q_to_tth(xarray, self.wavelength)
|
|
394
|
+
self._all_arrays[:, 3] = q_to_d(xarray)
|
|
395
|
+
elif xtype.lower() in ANGLEQUANTITIES:
|
|
396
|
+
self._all_arrays[:, 2] = xarray
|
|
397
|
+
self._all_arrays[:, 1] = tth_to_q(xarray, self.wavelength)
|
|
398
|
+
self._all_arrays[:, 3] = tth_to_d(xarray, self.wavelength)
|
|
399
|
+
elif xtype.lower() in DQUANTITIES:
|
|
400
|
+
self._all_arrays[:, 3] = xarray
|
|
401
|
+
self._all_arrays[:, 1] = d_to_q(xarray)
|
|
402
|
+
self._all_arrays[:, 2] = d_to_tth(xarray, self.wavelength)
|
|
403
|
+
|
|
404
|
+
def _set_min_max_xarray(self):
|
|
405
|
+
self.qmin = np.nanmin(self._all_arrays[:, 1], initial=np.inf)
|
|
406
|
+
self.qmax = np.nanmax(self._all_arrays[:, 1], initial=0.0)
|
|
407
|
+
self.tthmin = np.nanmin(self._all_arrays[:, 2], initial=np.inf)
|
|
408
|
+
self.tthmax = np.nanmax(self._all_arrays[:, 2], initial=0.0)
|
|
409
|
+
self.dmin = np.nanmin(self._all_arrays[:, 3], initial=np.inf)
|
|
410
|
+
self.dmax = np.nanmax(self._all_arrays[:, 3], initial=0.0)
|
|
411
|
+
|
|
412
|
+
def _get_original_array(self):
|
|
413
|
+
if self._input_xtype in QQUANTITIES:
|
|
414
|
+
return self.on_q(), "q"
|
|
415
|
+
elif self._input_xtype in ANGLEQUANTITIES:
|
|
416
|
+
return self.on_tth(), "tth"
|
|
417
|
+
elif self._input_xtype in DQUANTITIES:
|
|
418
|
+
return self.on_d(), "d"
|
|
419
|
+
|
|
420
|
+
def on_q(self):
|
|
421
|
+
"""Return the tuple of two 1D numpy arrays containing q and y data.
|
|
422
|
+
|
|
423
|
+
Returns
|
|
424
|
+
-------
|
|
425
|
+
(q-array, y-array) : tuple of ndarray
|
|
426
|
+
The tuple containing two 1D numpy arrays with q and y data
|
|
427
|
+
"""
|
|
428
|
+
return [self.all_arrays[:, 1], self.all_arrays[:, 0]]
|
|
429
|
+
|
|
430
|
+
def on_tth(self):
|
|
431
|
+
"""Return the tuple of two 1D numpy arrays containing tth and y data.
|
|
432
|
+
|
|
433
|
+
Returns
|
|
434
|
+
-------
|
|
435
|
+
(tth-array, y-array) : tuple of ndarray
|
|
436
|
+
The tuple containing two 1D numpy arrays with tth and y data
|
|
437
|
+
"""
|
|
438
|
+
return [self.all_arrays[:, 2], self.all_arrays[:, 0]]
|
|
439
|
+
|
|
440
|
+
def on_d(self):
|
|
441
|
+
"""Return the tuple of two 1D numpy arrays containing d and y data.
|
|
442
|
+
|
|
443
|
+
Returns
|
|
444
|
+
-------
|
|
445
|
+
(d-array, y-array) : tuple of ndarray
|
|
446
|
+
The tuple containing two 1D numpy arrays with d and y data
|
|
447
|
+
"""
|
|
448
|
+
return [self.all_arrays[:, 3], self.all_arrays[:, 0]]
|
|
449
|
+
|
|
450
|
+
def scale_to(self, target_diff_object, q=None, tth=None, d=None, offset=None):
|
|
451
|
+
"""Return a new diffraction object which is the current object but
|
|
452
|
+
rescaled in y to the target.
|
|
453
|
+
|
|
454
|
+
By default, if `q`, `tth`, or `d` are not provided, scaling is based on the max intensity from each object.
|
|
455
|
+
Otherwise, y-value in the target at the closest specified x-value will be used as the factor to scale to.
|
|
456
|
+
The entire array is scaled by this factor so that one object places on top of the other at that point.
|
|
457
|
+
If multiple values of `q`, `tth`, or `d` are provided, an error will be raised.
|
|
458
|
+
|
|
459
|
+
Parameters
|
|
460
|
+
----------
|
|
461
|
+
target_diff_object: DiffractionObject
|
|
462
|
+
The diffraction object you want to scale the current one onto.
|
|
463
|
+
|
|
464
|
+
q, tth, d : float, optional, default is None
|
|
465
|
+
The value of the x-array where you want the curves to line up vertically.
|
|
466
|
+
Specify a value on one of the allowed grids, q, tth, or d), e.g., q=10.
|
|
467
|
+
|
|
468
|
+
offset : float, optional, default is None
|
|
469
|
+
The offset to add to the scaled y-values.
|
|
470
|
+
|
|
471
|
+
Returns
|
|
472
|
+
-------
|
|
473
|
+
scaled_do : DiffractionObject
|
|
474
|
+
The rescaled DiffractionObject as a new object.
|
|
475
|
+
"""
|
|
476
|
+
if offset is None:
|
|
477
|
+
offset = 0
|
|
478
|
+
scaled_do = self.copy()
|
|
479
|
+
count = sum([q is not None, tth is not None, d is not None])
|
|
480
|
+
if count > 1:
|
|
481
|
+
raise ValueError(
|
|
482
|
+
"You must specify none or exactly one of 'q', 'tth', or 'd'. "
|
|
483
|
+
"Please provide either none or one value."
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
if count == 0:
|
|
487
|
+
q_target_max = max(target_diff_object.on_q()[1])
|
|
488
|
+
q_self_max = max(self.on_q()[1])
|
|
489
|
+
scaled_do._all_arrays[:, 0] = scaled_do._all_arrays[:, 0] * q_target_max / q_self_max + offset
|
|
490
|
+
return scaled_do
|
|
491
|
+
|
|
492
|
+
xtype = "q" if q is not None else "tth" if tth is not None else "d"
|
|
493
|
+
data = self.on_xtype(xtype)
|
|
494
|
+
target = target_diff_object.on_xtype(xtype)
|
|
495
|
+
|
|
496
|
+
xvalue = q if xtype == "q" else tth if xtype == "tth" else d
|
|
497
|
+
|
|
498
|
+
xindex_data = (np.abs(data[0] - xvalue)).argmin()
|
|
499
|
+
xindex_target = (np.abs(target[0] - xvalue)).argmin()
|
|
500
|
+
scaled_do._all_arrays[:, 0] = data[1] * target[1][xindex_target] / data[1][xindex_data] + offset
|
|
501
|
+
return scaled_do
|
|
502
|
+
|
|
503
|
+
def on_xtype(self, xtype):
|
|
504
|
+
"""Return a tuple of two 1D numpy arrays containing x and y data.
|
|
505
|
+
|
|
506
|
+
Parameters
|
|
507
|
+
----------
|
|
508
|
+
xtype : str
|
|
509
|
+
The type of quantity for the independent variable chosen from {*XQUANTITIES, }
|
|
510
|
+
|
|
511
|
+
Raises
|
|
512
|
+
------
|
|
513
|
+
ValueError
|
|
514
|
+
Raised when the specified xtype is not among {*XQUANTITIES, }
|
|
515
|
+
|
|
516
|
+
Returns
|
|
517
|
+
-------
|
|
518
|
+
(xarray, yarray) : tuple of ndarray
|
|
519
|
+
The tuple containing two 1D numpy arrays with x and y data for the specified xtype.
|
|
520
|
+
"""
|
|
521
|
+
if xtype.lower() in ANGLEQUANTITIES:
|
|
522
|
+
return self.on_tth()
|
|
523
|
+
elif xtype.lower() in QQUANTITIES:
|
|
524
|
+
return self.on_q()
|
|
525
|
+
elif xtype.lower() in DQUANTITIES:
|
|
526
|
+
return self.on_d()
|
|
527
|
+
else:
|
|
528
|
+
raise ValueError(_xtype_wmsg(xtype))
|
|
529
|
+
|
|
530
|
+
def dump(self, filepath, xtype=None):
|
|
531
|
+
"""Dump the xarray and yarray of the diffraction object to a two-column
|
|
532
|
+
file, with the associated information included in the header.
|
|
533
|
+
|
|
534
|
+
Parameters
|
|
535
|
+
----------
|
|
536
|
+
filepath : str
|
|
537
|
+
The filepath where the diffraction object will be dumped
|
|
538
|
+
xtype : str, optional, default is q
|
|
539
|
+
The type of quantity for the independent variable chosen from {*XQUANTITIES, }
|
|
540
|
+
|
|
541
|
+
Examples
|
|
542
|
+
--------
|
|
543
|
+
To save a diffraction object to a file named "diffraction_data.chi" in the current directory
|
|
544
|
+
with the independent variable 'q':
|
|
545
|
+
|
|
546
|
+
>>> file = "diffraction_data.chi"
|
|
547
|
+
>>> do.dump(file, xtype="q")
|
|
548
|
+
|
|
549
|
+
To save the diffraction data to a file in a subfolder `output`:
|
|
550
|
+
|
|
551
|
+
>>> file = "./output/diffraction_data.chi"
|
|
552
|
+
>>> do.dump(file, xtype="q")
|
|
553
|
+
|
|
554
|
+
To save the diffraction data with a different independent variable, such as 'tth':
|
|
555
|
+
|
|
556
|
+
>>> file = "diffraction_data_tth.chi"
|
|
557
|
+
>>> do.dump(file, xtype="tth")
|
|
558
|
+
"""
|
|
559
|
+
if xtype is None:
|
|
560
|
+
xtype = "q"
|
|
561
|
+
if xtype in QQUANTITIES:
|
|
562
|
+
data_to_save = np.column_stack((self.on_q()[0], self.on_q()[1]))
|
|
563
|
+
elif xtype in ANGLEQUANTITIES:
|
|
564
|
+
data_to_save = np.column_stack((self.on_tth()[0], self.on_tth()[1]))
|
|
565
|
+
elif xtype in DQUANTITIES:
|
|
566
|
+
data_to_save = np.column_stack((self.on_d()[0], self.on_d()[1]))
|
|
567
|
+
else:
|
|
568
|
+
warnings.warn(_xtype_wmsg(xtype))
|
|
569
|
+
self.metadata.update(get_package_info("diffpy.utils", metadata=self.metadata))
|
|
570
|
+
self.metadata["creation_time"] = datetime.datetime.now()
|
|
571
|
+
|
|
572
|
+
with open(filepath, "w") as f:
|
|
573
|
+
f.write(
|
|
574
|
+
f"[DiffractionObject]\nname = {self.name}\nwavelength = {self.wavelength}\n"
|
|
575
|
+
f"scat_quantity = {self.scat_quantity}\n"
|
|
576
|
+
)
|
|
577
|
+
for key, value in self.metadata.items():
|
|
578
|
+
f.write(f"{key} = {value}\n")
|
|
579
|
+
f.write("\n#### start data\n")
|
|
580
|
+
np.savetxt(f, data_to_save, delimiter=" ")
|
|
581
|
+
|
|
582
|
+
def copy(self):
|
|
583
|
+
"""Create a deep copy of the DiffractionObject instance.
|
|
584
|
+
|
|
585
|
+
Returns
|
|
586
|
+
-------
|
|
587
|
+
DiffractionObject
|
|
588
|
+
The new instance of DiffractionObject, which is a deep copy of the current instance.
|
|
589
|
+
"""
|
|
590
|
+
return deepcopy(self)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
##############################################################################
|
|
3
|
+
#
|
|
4
|
+
# diffpy.utils by DANSE Diffraction group
|
|
5
|
+
# Simon J. L. Billinge
|
|
6
|
+
# (c) 2010 The Trustees of Columbia University
|
|
7
|
+
# in the City of New York. All rights reserved.
|
|
8
|
+
#
|
|
9
|
+
# File coded by: Chris Farrow
|
|
10
|
+
#
|
|
11
|
+
# See AUTHORS.txt for a list of people who contributed.
|
|
12
|
+
# See LICENSE_DANSE.txt for license information.
|
|
13
|
+
#
|
|
14
|
+
##############################################################################
|
|
15
|
+
"""Various utilities related to data parsing and manipulation."""
|