Skip to content

Commit da32ae3

Browse files
authored
Merge pull request #228 from bobleesj/no-empty-object
Do not allow an empty instance of DiffractionObject - require `xarrays` `yarrays` `xtype`
2 parents 8f83456 + ba4b985 commit da32ae3

File tree

4 files changed

+170
-172
lines changed

4 files changed

+170
-172
lines changed

Diff for: news/no-empty-object.rst

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* <news item>
4+
5+
**Changed:**
6+
7+
* `DiffractionObject` requires 3 input parameters of `xarray`, `yarray`, `xtype`, to be instantiated. It can be instantiated with empty arrays.
8+
9+
**Deprecated:**
10+
11+
* <news item>
12+
13+
**Removed:**
14+
15+
* <news item>
16+
17+
**Fixed:**
18+
19+
* <news item>
20+
21+
**Security:**
22+
23+
* <news item>

Diff for: src/diffpy/utils/diffraction_objects.py

+79-77
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,84 @@ def _setter_wmsg(attribute):
3535

3636

3737
class DiffractionObject:
38+
"""
39+
Initialize a DiffractionObject instance.
40+
41+
Parameters
42+
----------
43+
xarray : array-like
44+
The independent variable array containing "q", "tth", or "d" values.
45+
yarray : array-like
46+
The dependent variable array corresponding to intensity values.
47+
xtype : str
48+
The type of the independent variable in `xarray`. Must be one of {*XQUANTITIES}.
49+
wavelength : float, optional
50+
The wavelength of the incoming beam, specified in angstroms (Å). Default is none.
51+
scat_quantity : str, optional
52+
The type of scattering experiment (e.g., "x-ray", "neutron"). Default is an empty string "".
53+
name : str, optional
54+
The name or label for the scattering data. Default is an empty string "".
55+
metadata : dict, optional
56+
The additional metadata associated with the diffraction object. Default is {}.
57+
58+
Examples
59+
--------
60+
Create a DiffractionObject for X-ray scattering data:
61+
62+
>>> import numpy as np
63+
>>> from diffpy.utils.diffraction_objects import DiffractionObject
64+
...
65+
>>> x = np.array([0.12, 0.24, 0.31, 0.4]) # independent variable (e.g., q)
66+
>>> y = np.array([10, 20, 40, 60]) # intensity values
67+
>>> metadata = {
68+
... "sample": "rock salt from the beach",
69+
... "composition": "NaCl",
70+
... "temperature": "300 K,",
71+
... "experimenters": "Phill, Sally"
72+
... }
73+
>>> do = DiffractionObject(
74+
... xarray=x,
75+
... yarray=y,
76+
... xtype="q",
77+
... wavelength=1.54,
78+
... scat_quantity="x-ray",
79+
... name="beach_rock_salt_1",
80+
... metadata=metadata
81+
... )
82+
>>> print(do.metadata)
83+
"""
84+
3885
def __init__(
39-
self, name=None, wavelength=None, scat_quantity=None, metadata=None, xarray=None, yarray=None, xtype=None
86+
self,
87+
xarray,
88+
yarray,
89+
xtype,
90+
wavelength=None,
91+
scat_quantity="",
92+
name="",
93+
metadata={},
4094
):
41-
if name is None:
42-
name = ""
43-
self.name = name
44-
if metadata is None:
45-
metadata = {}
46-
self.metadata = metadata
47-
if xtype is None:
48-
xtype = ""
49-
self.scat_quantity = scat_quantity
50-
self.wavelength = wavelength
51-
52-
if xarray is None:
53-
xarray = np.empty(0)
54-
if yarray is None:
55-
yarray = np.empty(0)
5695

5796
self._id = uuid.uuid4()
58-
self.input_data(xarray, yarray, xtype)
97+
self._input_data(xarray, yarray, xtype, wavelength, scat_quantity, name, metadata)
98+
99+
def _input_data(self, xarray, yarray, xtype, wavelength, scat_quantity, name, metadata):
100+
if xtype not in XQUANTITIES:
101+
raise ValueError(_xtype_wmsg(xtype))
102+
if len(xarray) != len(yarray):
103+
raise ValueError(
104+
"'xarray' and 'yarray' are different lengths. They must "
105+
"correspond to each other and have the same length. "
106+
"Please re-initialize 'DiffractionObject'"
107+
"with valid 'xarray' and 'yarray's"
108+
)
109+
self.scat_quantity = scat_quantity
110+
self.wavelength = wavelength
111+
self.metadata = metadata
112+
self.name = name
113+
self._input_xtype = xtype
114+
self._set_arrays(xarray, yarray, xtype)
115+
self._set_min_max_xarray()
59116

60117
def __eq__(self, other):
61118
if not isinstance(other, DiffractionObject):
@@ -231,16 +288,16 @@ def get_array_index(self, value, xtype=None):
231288
the index of the value in the array
232289
"""
233290

234-
if xtype is None:
235-
xtype = self._input_xtype
291+
xtype = self._input_xtype
236292
array = self.on_xtype(xtype)[0]
237293
if len(array) == 0:
238294
raise ValueError(f"The '{xtype}' array is empty. Please ensure it is initialized.")
239295
i = (np.abs(array - value)).argmin()
240296
return i
241297

242-
def _set_xarrays(self, xarray, xtype):
298+
def _set_arrays(self, xarray, yarray, xtype):
243299
self._all_arrays = np.empty(shape=(len(xarray), 4))
300+
self._all_arrays[:, 0] = yarray
244301
if xtype.lower() in QQUANTITIES:
245302
self._all_arrays[:, 1] = xarray
246303
self._all_arrays[:, 2] = q_to_tth(xarray, self.wavelength)
@@ -253,70 +310,15 @@ def _set_xarrays(self, xarray, xtype):
253310
self._all_arrays[:, 3] = xarray
254311
self._all_arrays[:, 1] = d_to_q(xarray)
255312
self._all_arrays[:, 2] = d_to_tth(xarray, self.wavelength)
313+
314+
def _set_min_max_xarray(self):
256315
self.qmin = np.nanmin(self._all_arrays[:, 1], initial=np.inf)
257316
self.qmax = np.nanmax(self._all_arrays[:, 1], initial=0.0)
258317
self.tthmin = np.nanmin(self._all_arrays[:, 2], initial=np.inf)
259318
self.tthmax = np.nanmax(self._all_arrays[:, 2], initial=0.0)
260319
self.dmin = np.nanmin(self._all_arrays[:, 3], initial=np.inf)
261320
self.dmax = np.nanmax(self._all_arrays[:, 3], initial=0.0)
262321

263-
def input_data(
264-
self,
265-
xarray,
266-
yarray,
267-
xtype,
268-
metadata={},
269-
scat_quantity=None,
270-
name=None,
271-
wavelength=None,
272-
):
273-
f"""
274-
insert a new scattering quantity into the scattering object
275-
276-
Parameters
277-
----------
278-
xarray array-like of floats
279-
the independent variable array
280-
yarray array-like of floats
281-
the dependent variable array
282-
xtype string
283-
the type of quantity for the independent variable from {*XQUANTITIES, }
284-
metadata, scat_quantity, name and wavelength are optional. They have the same
285-
meaning as in the constructor. Values will only be overwritten if non-empty values are passed.
286-
287-
Returns
288-
-------
289-
Nothing. Updates the object in place.
290-
291-
"""
292-
293-
# Check xarray and yarray have the same length
294-
if len(xarray) != len(yarray):
295-
raise ValueError(
296-
"'xarray' and 'yarray' must have the same length. "
297-
"Please re-initialize 'DiffractionObject' or re-run the method 'input_data' "
298-
"with 'xarray' and 'yarray' of identical length."
299-
)
300-
301-
self._set_xarrays(xarray, xtype)
302-
self._all_arrays[:, 0] = yarray
303-
self._input_xtype = xtype
304-
# only update these optional values if non-empty quantities are passed to avoid overwriting
305-
# valid data inadvertently
306-
if metadata:
307-
self.metadata = metadata
308-
if scat_quantity is not None:
309-
self.scat_quantity = scat_quantity
310-
if name is not None:
311-
self.name = name
312-
if wavelength is not None:
313-
self.wavelength = wavelength
314-
315-
# Check xtype is valid. An empty string is the default value.
316-
if xtype != "":
317-
if xtype not in XQUANTITIES:
318-
raise ValueError(_xtype_wmsg(xtype))
319-
320322
def _get_original_array(self):
321323
if self._input_xtype in QQUANTITIES:
322324
return self.on_q(), "q"

Diff for: tests/conftest.py

+7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ def _load(filename):
3333
return _load
3434

3535

36+
@pytest.fixture
37+
def do_minimal():
38+
# Create an instance of DiffractionObject with empty xarray and yarray values, and a non-empty wavelength
39+
return DiffractionObject(xarray=np.empty(0), yarray=np.empty(0), xtype="tth", wavelength=1.54)
40+
41+
3642
@pytest.fixture
3743
def do_minimal_tth():
44+
# Create an instance of DiffractionObject with non-empty xarray, yarray, and wavelength values
3845
return DiffractionObject(wavelength=2 * np.pi, xarray=np.array([30, 60]), yarray=np.array([1, 2]), xtype="tth")

0 commit comments

Comments
 (0)