Skip to content
Merged
10 changes: 4 additions & 6 deletions src/diffpy/utils/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
import numpy as np

wavelength_warning_emsg = (
"INFO: no wavelength has been specified. You can continue "
"to use the DiffractionObject but some of its powerful features "
"will not be available. To specify a wavelength, set "
"diffraction_object.wavelength = [number], "
"where diffraction_object is the variable name of you Diffraction Object, "
"and number is the wavelength in angstroms."
"No wavelength has been specified. You can continue to use the DiffractionObject, but "
"some of its powerful features will not be available. "
"To specify a wavelength, if you have do = DiffractionObject(xarray, yarray, 'tth'), "
"you may set do.wavelength = 1.54 for a wavelength of 1.54 angstroms."
)
invalid_tth_emsg = "Two theta exceeds 180 degrees. Please check the input values for errors."
invalid_q_or_d_or_wavelength_emsg = (
Expand Down
42 changes: 21 additions & 21 deletions tests/test_diffraction_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ def test_diffraction_objects_equality(inputs1, inputs2, expected):


def test_on_xtype():
test = DiffractionObject(wavelength=2 * np.pi, xarray=np.array([30, 60]), yarray=np.array([1, 2]), xtype="tth")
assert np.allclose(test.on_xtype("tth"), [np.array([30, 60]), np.array([1, 2])])
assert np.allclose(test.on_xtype("2theta"), [np.array([30, 60]), np.array([1, 2])])
assert np.allclose(test.on_xtype("q"), [np.array([0.51764, 1]), np.array([1, 2])])
assert np.allclose(test.on_xtype("d"), [np.array([12.13818, 6.28319]), np.array([1, 2])])
do = DiffractionObject(wavelength=2 * np.pi, xarray=np.array([30, 60]), yarray=np.array([1, 2]), xtype="tth")
assert np.allclose(do.on_xtype("tth"), [np.array([30, 60]), np.array([1, 2])])
assert np.allclose(do.on_xtype("2theta"), [np.array([30, 60]), np.array([1, 2])])
assert np.allclose(do.on_xtype("q"), [np.array([0.51764, 1]), np.array([1, 2])])
assert np.allclose(do.on_xtype("d"), [np.array([12.13818, 6.28319]), np.array([1, 2])])


def test_init_invalid_xtype():
Expand All @@ -184,34 +184,34 @@ def test_init_invalid_xtype():

params_index = [
# UC1: exact match
([4 * np.pi, np.array([30.005, 60]), np.array([1, 2]), "tth", "tth", 30.005], [0]),
(4 * np.pi, np.array([30.005, 60]), np.array([1, 2]), "tth", "tth", 30.005, [0]),
# UC2: target value lies in the array, returns the (first) closest index
([4 * np.pi, np.array([30, 60]), np.array([1, 2]), "tth", "tth", 45], [0]),
([4 * np.pi, np.array([30, 60]), np.array([1, 2]), "tth", "q", 0.25], [0]),
(4 * np.pi, np.array([30, 60]), np.array([1, 2]), "tth", "tth", 45, [0]),
(4 * np.pi, np.array([30, 60]), np.array([1, 2]), "tth", "q", 0.25, [0]),
# UC3: target value out of the range, returns the closest index
([4 * np.pi, np.array([0.25, 0.5, 0.71]), np.array([1, 2, 3]), "q", "q", 0.1], [0]),
([4 * np.pi, np.array([30, 60]), np.array([1, 2]), "tth", "tth", 63], [1]),
(4 * np.pi, np.array([0.25, 0.5, 0.71]), np.array([1, 2, 3]), "q", "q", 0.1, [0]),
(4 * np.pi, np.array([30, 60]), np.array([1, 2]), "tth", "tth", 63, [1]),
]


@pytest.mark.parametrize("inputs, expected", params_index)
def test_get_array_index(inputs, expected):
test = DiffractionObject(wavelength=inputs[0], xarray=inputs[1], yarray=inputs[2], xtype=inputs[3])
actual = test.get_array_index(value=inputs[5], xtype=inputs[4])
assert actual == expected[0]
@pytest.mark.parametrize("wavelength, xarray, yarray, xtype_1, xtype_2, value, expected_index", params_index)
def test_get_array_index(wavelength, xarray, yarray, xtype_1, xtype_2, value, expected_index):
do = DiffractionObject(wavelength=wavelength, xarray=xarray, yarray=yarray, xtype=xtype_1)
actual_index = do.get_array_index(value=value, xtype=xtype_2)
assert actual_index == expected_index


def test_get_array_index_bad():
test = DiffractionObject(wavelength=2 * np.pi, xarray=np.array([]), yarray=np.array([]), xtype="tth")
do = DiffractionObject(wavelength=2 * np.pi, xarray=np.array([]), yarray=np.array([]), xtype="tth")
with pytest.raises(ValueError, match=re.escape("The 'tth' array is empty. Please ensure it is initialized.")):
test.get_array_index(value=30)
do.get_array_index(value=30)


def test_dump(tmp_path, mocker):
x, y = np.linspace(0, 5, 6), np.linspace(0, 5, 6)
directory = Path(tmp_path)
file = directory / "testfile"
test = DiffractionObject(
do = DiffractionObject(
wavelength=1.54,
name="test",
scat_quantity="x-ray",
Expand All @@ -222,7 +222,7 @@ def test_dump(tmp_path, mocker):
)
mocker.patch("importlib.metadata.version", return_value="3.3.0")
with freeze_time("2012-01-14"):
test.dump(file, "q")
do.dump(file, "q")
with open(file, "r") as f:
actual = f.read()
expected = (
Expand Down Expand Up @@ -360,15 +360,15 @@ def test_all_array_getter():


def test_all_array_setter():
actual_do = DiffractionObject()
do = DiffractionObject()

# Attempt to directly modify the property
with pytest.raises(
AttributeError,
match="Direct modification of attribute 'all_arrays' is not allowed. "
"Please use 'input_data' to modify 'all_arrays'.",
):
actual_do.all_arrays = np.empty((4, 4))
do.all_arrays = np.empty((4, 4))


def test_id_getter():
Expand Down
79 changes: 47 additions & 32 deletions tests/test_transforms.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,96 @@
import re

import numpy as np
import pytest

from diffpy.utils.transforms import d_to_q, d_to_tth, q_to_d, q_to_tth, tth_to_d, tth_to_q

params_q_to_tth = [
# UC1: Empty q values, no wavelength, return empty arrays
([None, np.empty((0))], np.empty((0))),
(None, np.empty((0)), np.empty((0))),
# UC2: Empty q values, wavelength specified, return empty arrays
([4 * np.pi, np.empty((0))], np.empty(0)),
(4 * np.pi, np.empty((0)), np.empty(0)),
# UC3: User specified valid q values, no wavelength, return empty arrays
(
[None, np.array([0, 0.2, 0.4, 0.6, 0.8, 1])],
None,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
np.array([0, 1, 2, 3, 4, 5]),
),
# UC4: User specified valid q values (with wavelength)
# expected tth values are 2*arcsin(q) in degrees
([4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0])], np.array([0, 90.0, 180.0])),
(4 * np.pi, np.array([0, 1 / np.sqrt(2), 1.0]), np.array([0, 90.0, 180.0])),
]


@pytest.mark.parametrize("inputs, expected", params_q_to_tth)
def test_q_to_tth(inputs, expected):
actual = q_to_tth(inputs[1], inputs[0])
assert np.allclose(expected, actual)
@pytest.mark.parametrize("wavelength, q, expected_tth", params_q_to_tth)
def test_q_to_tth(wavelength, q, expected_tth):

wavelength_warning_emsg = (
"No wavelength has been specified. You can continue to use the DiffractionObject, but "
"some of its powerful features will not be available. "
"To specify a wavelength, if you have do = DiffractionObject(xarray, yarray, 'tth'), "
"you may set do.wavelength = 1.54 for a wavelength of 1.54 angstroms."
)
if wavelength is None:
with pytest.warns(UserWarning, match=re.escape(wavelength_warning_emsg)):
actual_tth = q_to_tth(q, wavelength)
else:
actual_tth = q_to_tth(q, wavelength)

assert np.allclose(expected_tth, actual_tth)


params_q_to_tth_bad = [
# UC1: user specified invalid q values that result in tth > 180 degrees
(
[4 * np.pi, np.array([0.2, 0.4, 0.6, 0.8, 1, 1.2])],
[
ValueError,
"The supplied input array and wavelength will result in an impossible two-theta. "
"Please check these values and re-instantiate the DiffractionObject with correct values.",
],
4 * np.pi,
np.array([0.2, 0.4, 0.6, 0.8, 1, 1.2]),
ValueError,
"The supplied input array and wavelength will result in an impossible two-theta. "
"Please check these values and re-instantiate the DiffractionObject with correct values.",
),
# UC2: user specified a wrong wavelength that result in tth > 180 degrees
(
[100, np.array([0, 0.2, 0.4, 0.6, 0.8, 1])],
[
ValueError,
"The supplied input array and wavelength will result in an impossible two-theta. "
"Please check these values and re-instantiate the DiffractionObject with correct values.",
],
100,
np.array([0, 0.2, 0.4, 0.6, 0.8, 1]),
ValueError,
"The supplied input array and wavelength will result in an impossible two-theta. "
"Please check these values and re-instantiate the DiffractionObject with correct values.",
),
]


@pytest.mark.parametrize("inputs, expected", params_q_to_tth_bad)
def test_q_to_tth_bad(inputs, expected):
with pytest.raises(expected[0], match=expected[1]):
q_to_tth(inputs[1], inputs[0])
@pytest.mark.parametrize("q, wavelength, expected_error_type, expected_error_msg", params_q_to_tth_bad)
def test_q_to_tth_bad(q, wavelength, expected_error_type, expected_error_msg):
with pytest.raises(expected_error_type, match=expected_error_msg):
q_to_tth(wavelength, q)


params_tth_to_q = [
# UC0: User specified empty tth values (without wavelength)
([None, np.array([])], np.array([])),
(None, np.array([]), np.array([])),
# UC1: User specified empty tth values (with wavelength)
([4 * np.pi, np.array([])], np.array([])),
(4 * np.pi, np.array([]), np.array([])),
# UC2: User specified valid tth values between 0-180 degrees (without wavelength)
(
[None, np.array([0, 30, 60, 90, 120, 180])],
None,
np.array([0, 30, 60, 90, 120, 180]),
np.array([0, 1, 2, 3, 4, 5]),
),
# UC3: User specified valid tth values between 0-180 degrees (with wavelength)
# expected q values are sin15, sin30, sin45, sin60, sin90
(
[4 * np.pi, np.array([0, 30.0, 60.0, 90.0, 120.0, 180.0])],
4 * np.pi,
np.array([0, 30.0, 60.0, 90.0, 120.0, 180.0]),
np.array([0, 0.258819, 0.5, 0.707107, 0.866025, 1]),
),
]


@pytest.mark.parametrize("inputs, expected", params_tth_to_q)
def test_tth_to_q(inputs, expected):
actual = tth_to_q(inputs[1], inputs[0])
assert np.allclose(actual, expected)
@pytest.mark.parametrize("wavelength, tth, expected_q", params_tth_to_q)
def test_tth_to_q(wavelength, tth, expected_q):
actual_q = tth_to_q(tth, wavelength)
assert np.allclose(actual_q, expected_q)


params_tth_to_q_bad = [
Expand Down
Loading