Skip to content

Commit 6b78427

Browse files
committed
chore: resolve merge conflict
2 parents f29ad91 + 52f1c79 commit 6b78427

File tree

11 files changed

+226
-42
lines changed

11 files changed

+226
-42
lines changed

doc/source/api/diffpy.utils.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ diffpy.utils.transforms module
2828
:undoc-members:
2929
:show-inheritance:
3030

31+
diffpy.utils.validators module
32+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33+
34+
.. automodule:: diffpy.utils.validators
35+
:members:
36+
:undoc-members:
37+
:show-inheritance:
38+
3139
diffpy.utils.tools module
3240
^^^^^^^^^^^^^^^^^^^^^^^^^
3341

doc/source/examples/diffraction_objects_example.rst

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,14 @@ For convenience, you can also apply an offset to the scaled new diffraction obje
129129
DiffractionObject convenience functions
130130
---------------------------------------
131131

132-
1) create a copy of a diffraction object using the ``copy`` method
132+
1. create a copy of a diffraction object using the ``copy`` method
133133
when you want to preserve the original data while working with a modified version.
134134

135135
.. code-block:: python
136136
137137
copy_of_calculated = calculated.copy()
138138
139-
2) test the equality of two diffraction objects. For example,
139+
2. test the equality of two diffraction objects. For example,
140140

141141
.. code-block:: python
142142
@@ -145,15 +145,38 @@ DiffractionObject convenience functions
145145
146146
will return ``True``.
147147

148-
3) make arithmetic operations on the intensities of diffraction objects. e.g.,
148+
3. make arithmetic operations on the intensities of diffraction objects.
149+
For example, you can do scalar operations on a single diffraction object,
150+
which will modify the intensity values (``yarrays``) without affecting other properties:
149151

150152
.. code-block:: python
151153
152-
doubled_object = 2 * diff_object1 # Double the intensities
153-
sum_object = diff_object1 + diff_object2 # Sum the intensities
154-
subtract_scaled = diff_object1 - 5 * diff_object2 # subtract 5 * obj2 from obj 1
154+
increased_intensity = diff_object1 + 5 # Increases the intensities by 5
155+
decreased_intensity = diff_object1 - 1 # Decreases the intensities by 1
156+
doubled_object = 2 * diff_object1 # Double the intensities
157+
reduced_intensity = diff_object1 / 2 # Halves the intensities
155158
156-
4) get the value of the DiffractionObject at a given point in one of the xarrays
159+
You can also do binary operations between two diffraction objects, as long as they are on the same ``q/tth/d-array``.
160+
The operation will apply to the intensity values, while other properties
161+
(such as ``xarrays``, ``xtype``, and ``metadata``) will be inherited
162+
from the left-hand side diffraction object (``diff_object1``).
163+
For example:
164+
165+
.. code-block:: python
166+
167+
sum_object = diff_object1 + diff_object2 # Sum the intensities
168+
subtract_scaled = diff_object1 - 5 * diff_object2 # Subtract 5 * obj2 from obj 1
169+
multiplied_object = diff_object1 * diff_object2 # Multiply the intensities
170+
divided_object = diff_object1 / diff_object2 # Divide the intensities
171+
172+
You cannot perform operations between diffraction objects and incompatible types.
173+
For example, attempting to add a diffraction object and a string will raise an error:
174+
175+
.. code-block:: python
176+
177+
diff_object1 + "string_value" # This will raise an error
178+
179+
4. get the value of the DiffractionObject at a given point in one of the xarrays
157180

158181
.. code-block:: python
159182
@@ -170,7 +193,7 @@ you can find its closest index for ``q=0.25`` by typing either of the following:
170193
index = do.get_array_index(0.25) # no xtype passed, defaults to do._input_xtype, or in this example, q
171194
index = do.get_array_index(0.25, xtype="q") # explicitly choose an xtype to specify a value
172195
173-
5) The ``dump`` function saves the diffraction data and relevant information to an xy format file with headers
196+
5. The ``dump`` function saves the diffraction data and relevant information to an xy format file with headers
174197
(widely used chi format used, for example, by Fit2D and diffpy. These files can be read by ``LoadData()``
175198
in ``diffpy.utils.parsers``).
176199

news/docs-do.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* no news added: adding documentation for diffraction object operation examples
4+
5+
**Changed:**
6+
7+
* <news item>
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>

news/is-float.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* <news item>
4+
5+
**Changed:**
6+
7+
* Rename the `isfloat` function to `is_number`, and move it to the `diffpy/utils/utilsvalidators.py` directory
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>

news/no-news.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* No news added: just making sure the previous add, mul, div, and sub tests are working as expected.
4+
5+
**Changed:**
6+
7+
* <news item>
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>

src/diffpy/utils/diffraction_objects.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
XQUANTITIES = ANGLEQUANTITIES + DQUANTITIES + QQUANTITIES
1515
XUNITS = ["degrees", "radians", "rad", "deg", "inv_angs", "inv_nm", "nm-1", "A-1"]
1616

17-
y_grid_length_mismatch_emsg = (
18-
"The two objects have different y-array lengths. "
19-
"Please ensure the length of the y-value during initialization is identical."
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."
2021
)
2122

2223
invalid_add_type_emsg = (
@@ -301,7 +302,9 @@ def _check_operation_compatibility(self, other):
301302
raise TypeError(invalid_add_type_emsg)
302303
if isinstance(other, DiffractionObject):
303304
if self.all_arrays.shape != other.all_arrays.shape:
304-
raise ValueError(y_grid_length_mismatch_emsg)
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)
305308

306309
@property
307310
def all_arrays(self):

src/diffpy/utils/parsers/loaddata.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import numpy
1919

20+
from diffpy.utils import validators
21+
2022

2123
def loadData(filename, minrows=10, headers=False, hdel="=", hignore=None, **kwargs):
2224
"""Find and load data from a text file.
@@ -139,7 +141,7 @@ def countcolumnsvalues(line):
139141
name = hpair[0]
140142
value = hpair[1]
141143
# check if data value should be stored as float
142-
if isfloat(hpair[1]):
144+
if validators.is_number(hpair[1]):
143145
value = float(hpair[1])
144146
hdata.update({name: value})
145147
# continue search for the start of datablock
@@ -331,16 +333,3 @@ def _findDataBlocks(self):
331333
self.headers.append(header)
332334
self.datasets.append(data)
333335
return
334-
335-
336-
# End of class TextDataLoader
337-
338-
339-
def isfloat(s):
340-
"""True if s is convertible to float."""
341-
try:
342-
float(s)
343-
return True
344-
except ValueError:
345-
pass
346-
return False

src/diffpy/utils/validators.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
def is_number(string):
2+
"""Check if the provided string can be converted to a float.
3+
4+
Since integers can be converted to floats, this function will return True for integers as well.
5+
Hence, we can use this function to check if a string is a number.
6+
7+
Parameters
8+
----------
9+
string : str
10+
The string to evaluate for numeric conversion.
11+
12+
Returns
13+
-------
14+
bool
15+
The boolean whether `string` can be successfully converted to float.
16+
17+
Examples
18+
--------
19+
>>> is_number("3.14")
20+
True
21+
22+
>>> is_number("-1.23")
23+
True
24+
25+
>>> is_number("007")
26+
True
27+
28+
>>> is_number("five")
29+
False
30+
31+
>>> is_number("3.14.15")
32+
False
33+
34+
>>> is_number("NaN")
35+
True
36+
37+
>>> is_number("Infinity")
38+
True
39+
40+
>>> is_number("Inf")
41+
True
42+
"""
43+
try:
44+
float(string)
45+
return True
46+
except ValueError:
47+
return False

tests/conftest.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ def invalid_add_type_error_msg():
8181

8282

8383
@pytest.fixture
84-
def y_grid_size_mismatch_error_msg():
84+
def x_values_not_equal_error_msg():
8585
return (
86-
"The two objects have different y-array lengths. "
87-
"Please ensure the length of the y-value during initialization is identical."
86+
"The two objects have different values in x arrays (my_do.all_arrays[:, [1, 2, 3]]). "
87+
"Please ensure the x values of the two objects are identical by re-instantiating "
88+
"the DiffractionObject with the correct x value inputs."
8889
)

tests/test_diffraction_objects.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -780,28 +780,28 @@ def test_scalar_operations(operation, starting_yarray, scalar_value, expected_ya
780780

781781

782782
@pytest.mark.parametrize(
783-
"operation, " "expected_do_1_all_arrays_with_y_modified, " "expected_do_2_all_arrays_with_y_modified",
783+
"operation, expected_do_1_all_arrays_with_y_modified, expected_do_2_all_arrays_with_y_modified",
784784
[
785785
# Test addition, subtraction, multiplication, and division of two DO objects
786786
( # Test addition of two DO objects, expect combined yarray values
787787
"add",
788788
np.array([[2.0, 0.51763809, 30.0, 12.13818192], [4.0, 1.0, 60.0, 6.28318531]]),
789-
np.array([[2.0, 6.28318531, 100.70777771, 1], [4.0, 3.14159265, 45.28748053, 2.0]]),
789+
np.array([[2.0, 0.51763809, 30.0, 12.13818192], [4.0, 1.0, 60.0, 6.28318531]]),
790790
),
791791
( # Test subtraction of two DO objects, expect differences in yarray values
792792
"sub",
793793
np.array([[0.0, 0.51763809, 30.0, 12.13818192], [0.0, 1.0, 60.0, 6.28318531]]),
794-
np.array([[0.0, 6.28318531, 100.70777771, 1], [0.0, 3.14159265, 45.28748053, 2.0]]),
794+
np.array([[0.0, 0.51763809, 30.0, 12.13818192], [0.0, 1.0, 60.0, 6.28318531]]),
795795
),
796796
( # Test multiplication of two DO objects, expect multiplication in yarray values
797797
"mul",
798798
np.array([[1.0, 0.51763809, 30.0, 12.13818192], [4.0, 1.0, 60.0, 6.28318531]]),
799-
np.array([[1.0, 6.28318531, 100.70777771, 1], [4.0, 3.14159265, 45.28748053, 2.0]]),
799+
np.array([[1.0, 0.51763809, 30.0, 12.13818192], [4.0, 1.0, 60.0, 6.28318531]]),
800800
),
801801
( # Test division of two DO objects, expect division in yarray values
802802
"div",
803803
np.array([[1.0, 0.51763809, 30.0, 12.13818192], [1.0, 1.0, 60.0, 6.28318531]]),
804-
np.array([[1.0, 6.28318531, 100.70777771, 1], [1.0, 3.14159265, 45.28748053, 2.0]]),
804+
np.array([[1.0, 0.51763809, 30.0, 12.13818192], [1.0, 1.0, 60.0, 6.28318531]]),
805805
),
806806
],
807807
)
@@ -810,15 +810,14 @@ def test_binary_operator_on_do(
810810
expected_do_1_all_arrays_with_y_modified,
811811
expected_do_2_all_arrays_with_y_modified,
812812
do_minimal_tth,
813-
do_minimal_d,
814813
):
815814
do_1 = do_minimal_tth
816-
do_2 = do_minimal_d
815+
do_2 = do_minimal_tth
817816
assert np.allclose(
818817
do_1.all_arrays, np.array([[1.0, 0.51763809, 30.0, 12.13818192], [2.0, 1.0, 60.0, 6.28318531]])
819818
)
820819
assert np.allclose(
821-
do_2.all_arrays, np.array([[1.0, 6.28318531, 100.70777771, 1], [2.0, 3.14159265, 45.28748053, 2.0]])
820+
do_2.all_arrays, np.array([[1.0, 0.51763809, 30.0, 12.13818192], [2.0, 1.0, 60.0, 6.28318531]])
822821
)
823822

824823
if operation == "add":
@@ -856,13 +855,31 @@ def test_operator_invalid_type(do_minimal_tth, invalid_add_type_error_msg):
856855

857856

858857
@pytest.mark.parametrize("operation", ["add", "sub", "mul", "div"])
859-
def test_operator_invalid_yarray_length(operation, do_minimal, do_minimal_tth, y_grid_size_mismatch_error_msg):
860-
# Add two DO objects with different yarray lengths, expect ValueError
858+
def test_operator_invalid_xarray_values_not_equal(
859+
operation, do_minimal_tth, do_minimal_d, x_values_not_equal_error_msg
860+
):
861+
# Add two DO objects with different xarray values but equal in shape, expect ValueError
862+
do_1 = do_minimal_tth
863+
do_2 = do_minimal_d
864+
with pytest.raises(ValueError, match=re.escape(x_values_not_equal_error_msg)):
865+
if operation == "add":
866+
do_1 + do_2
867+
elif operation == "sub":
868+
do_1 - do_2
869+
elif operation == "mul":
870+
do_1 * do_2
871+
elif operation == "div":
872+
do_1 / do_2
873+
874+
875+
@pytest.mark.parametrize("operation", ["add", "sub", "mul", "div"])
876+
def test_operator_invalid_xarray_shape_not_equal(
877+
operation, do_minimal, do_minimal_tth, x_values_not_equal_error_msg
878+
):
879+
# Add two DO objects with different xarrays shape, expect ValueError
861880
do_1 = do_minimal
862881
do_2 = do_minimal_tth
863-
assert len(do_1.all_arrays[:, 0]) == 0
864-
assert len(do_2.all_arrays[:, 0]) == 2
865-
with pytest.raises(ValueError, match=re.escape(y_grid_size_mismatch_error_msg)):
882+
with pytest.raises(ValueError, match=re.escape(x_values_not_equal_error_msg)):
866883
if operation == "add":
867884
do_1 + do_2
868885
elif operation == "sub":

tests/test_validators.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pytest
2+
3+
from diffpy.utils.validators import is_number
4+
5+
6+
@pytest.mark.parametrize(
7+
"input,expected",
8+
[
9+
("3.14", True), # Standard float
10+
("2", True), # Integer
11+
("-100", True), # Negative integer
12+
("-3.14", True), # Negative float
13+
("0", True), # Zero
14+
("4.5e-1", True), # Scientific notation
15+
("abc", False), # Non-numeric string
16+
("", False), # Empty string
17+
("3.14.15", False), # Multiple dots
18+
("2+3", False), # Arithmetic expression
19+
("NaN", True), # Not a Number (special float value)
20+
("Infinity", True), # Positive infinity
21+
("-Infinity", True), # Negative infinity
22+
("Inf", True), # Positive infinity
23+
("-Inf", True), # Negative infinity
24+
],
25+
)
26+
def test_is_number(input, expected):
27+
assert is_number(input) == expected

0 commit comments

Comments
 (0)