From 03eaa877afffa54a16d5dac86d3d2c63be661312 Mon Sep 17 00:00:00 2001 From: yucongalicechen Date: Thu, 5 Dec 2024 14:20:14 -0500 Subject: [PATCH 1/7] initial commit, add docstring and tests --- src/diffpy/utils/transforms.py | 57 ++++++++++++++++++++------ tests/test_transforms.py | 74 +++++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/diffpy/utils/transforms.py b/src/diffpy/utils/transforms.py index 71e50599..f13632f8 100644 --- a/src/diffpy/utils/transforms.py +++ b/src/diffpy/utils/transforms.py @@ -16,6 +16,9 @@ "The supplied q-array and wavelength will result in an impossible two-theta. " "Please check these values and re-instantiate the DiffractionObject with correct values." ) +invalid_input_emsg = ( + "Input values have resulted in an infinite output. Please ensure there are no zeros in the input." +) def _validate_inputs(q, wavelength): @@ -58,7 +61,7 @@ def q_to_tth(q, wavelength): ------- tth : 1D array The array of :math:`2\theta` values in degrees numpy.array([tths]). - This is the correct format for loading into diffpy.utils.DiffractionOject.on_tth + This is the correct format for loading into diffpy.utils.DiffractionObject.on_tth. """ _validate_inputs(q, wavelength) q.astype(float) @@ -92,9 +95,8 @@ def tth_to_q(tth, wavelength): Parameters ---------- - tth : 2D array - The array of :math:`2\theta` values and :math: 'i' intensity values, np.array([[tths], [is]]). - This is the same format as, and so can accept, diffpy.utils.DiffractionOject.on_tth + tth : 1D array + The array of :math:`2\theta` values np.array([tths]). The units of tth are expected in degrees. wavelength : float @@ -102,11 +104,10 @@ def tth_to_q(tth, wavelength): Returns ------- - on_q : 2D array - The array of :math:`q` values and :math: 'i' intensity values unchanged, - np.array([[qs], [is]]). + q : 1D array + The array of :math:`q` values np.array([qs]). The units for the q-values are the inverse of the units of the provided wavelength. - This is the correct format for loading into diffpy.utils.DiffractionOject.on_q + This is the correct format for loading into diffpy.utils.DiffractionObject.on_q. """ tth.astype(float) if np.any(np.deg2rad(tth) > np.pi): @@ -121,8 +122,24 @@ def tth_to_q(tth, wavelength): return q -def q_to_d(qarray): - return 2.0 * np.pi / copy(qarray) +def q_to_d(q): + r""" + Helper function to convert q to d on independent variable axis, using :math:`d = \frac{2 \pi}{q}`. + + Parameters + ---------- + q : 1D array + The array of :math:`q` values np.array([qs]). + The units of q must be reciprocal of the units of wavelength. + + Returns + ------- + d : 1D array + The array of :math:`d` values np.array([ds]). + """ + if 0 in q: + raise ValueError(invalid_input_emsg) + return 2.0 * np.pi / copy(q)[::-1] def tth_to_d(ttharray, wavelength): @@ -130,8 +147,24 @@ def tth_to_d(ttharray, wavelength): return 2.0 * np.pi / copy(qarray) -def d_to_q(darray): - return 2.0 * np.pi / copy(darray) +def d_to_q(d): + r""" + Helper function to convert q to d using :math:`d = \frac{2 \pi}{q}`. + + Parameters + ---------- + d : 1D array + The array of :math:`d` values np.array([ds]). + + Returns + ------- + q : 1D array + The array of :math:`q` values np.array([qs]). + The units of q must be reciprocal of the units of wavelength. + """ + if 0 in d: + raise ValueError(invalid_input_emsg) + return 2.0 * np.pi / copy(d)[::-1] def d_to_tth(darray, wavelength): diff --git a/tests/test_transforms.py b/tests/test_transforms.py index e8b15492..db35d1ef 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from diffpy.utils.transforms import q_to_tth, tth_to_q +from diffpy.utils.transforms import d_to_q, q_to_d, q_to_tth, tth_to_q params_q_to_tth = [ # UC1: Empty q values, no wavelength, return empty arrays @@ -64,7 +64,7 @@ def test_q_to_tth_bad(inputs, expected): np.array([0, 1, 2, 3, 4, 5]), ), # UC3: User specified valid tth values between 0-180 degrees (with wavelength) - # expected q vales are sin15, sin30, sin45, sin60, sin90 + # 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])], np.array([0, 0.258819, 0.5, 0.707107, 0.866025, 1]), @@ -96,3 +96,73 @@ def test_tth_to_q(inputs, expected): def test_tth_to_q_bad(inputs, expected): with pytest.raises(expected[0], match=expected[1]): tth_to_q(inputs[1], inputs[0]) + + +params_q_to_d = [ + # UC1: User specified empty q values + ([np.array([])], np.array([])), + # UC2: User specified valid q values + ( + [np.array([np.pi / 6, 1 * np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi, 5 * np.pi])], + np.array([0.4, 0.5, 0.66667, 1, 2, 12]), + ), +] + + +@pytest.mark.parametrize("inputs, expected", params_q_to_d) +def test_q_to_d(inputs, expected): + actual = q_to_d(inputs[0]) + assert np.allclose(actual, expected) + + +params_q_to_d_bad = [ + # UC1: user specified an invalid q value that results in an infinite d value + ( + [np.array([0, 1, 2, 3, 4])], + [ + ValueError, + "Input values have resulted in an infinite output. Please ensure there are no zeros in the input.", + ], + ), +] + + +@pytest.mark.parametrize("inputs, expected", params_q_to_d_bad) +def test_q_to_d_bad(inputs, expected): + with pytest.raises(expected[0], match=expected[1]): + q_to_d(inputs[0]) + + +params_d_to_q = [ + # UC1: User specified empty d values + ([np.array([])], np.array([])), + # UC2: User specified valid d values + ( + [np.array([np.pi / 6, 1 * np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi, 5 * np.pi])], + np.array([0.4, 0.5, 0.66667, 1, 2, 12]), + ), +] + + +@pytest.mark.parametrize("inputs, expected", params_d_to_q) +def test_d_to_q(inputs, expected): + actual = d_to_q(inputs[0]) + assert np.allclose(actual, expected) + + +params_d_to_q_bad = [ + # UC1: user specified an invalid d value that results in an infinite q value + ( + [np.array([0, 1, 2, 3, 4])], + [ + ValueError, + "Input values have resulted in an infinite output. Please ensure there are no zeros in the input.", + ], + ), +] + + +@pytest.mark.parametrize("inputs, expected", params_d_to_q_bad) +def test_d_to_q_bad(inputs, expected): + with pytest.raises(expected[0], match=expected[1]): + d_to_q(inputs[0]) From 76d89755d006ec785872b63baf7790a58155e0f0 Mon Sep 17 00:00:00 2001 From: yucongalicechen Date: Fri, 6 Dec 2024 10:49:33 -0500 Subject: [PATCH 2/7] change value error to warning msg --- src/diffpy/utils/transforms.py | 14 ++++------- tests/test_transforms.py | 44 ++++------------------------------ 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/src/diffpy/utils/transforms.py b/src/diffpy/utils/transforms.py index f13632f8..b4a74bf7 100644 --- a/src/diffpy/utils/transforms.py +++ b/src/diffpy/utils/transforms.py @@ -16,9 +16,7 @@ "The supplied q-array and wavelength will result in an impossible two-theta. " "Please check these values and re-instantiate the DiffractionObject with correct values." ) -invalid_input_emsg = ( - "Input values have resulted in an infinite output. Please ensure there are no zeros in the input." -) +inf_output_msg = "WARNING: The largest output is infinite and cannot be plotted." def _validate_inputs(q, wavelength): @@ -61,7 +59,6 @@ def q_to_tth(q, wavelength): ------- tth : 1D array The array of :math:`2\theta` values in degrees numpy.array([tths]). - This is the correct format for loading into diffpy.utils.DiffractionObject.on_tth. """ _validate_inputs(q, wavelength) q.astype(float) @@ -107,7 +104,6 @@ def tth_to_q(tth, wavelength): q : 1D array The array of :math:`q` values np.array([qs]). The units for the q-values are the inverse of the units of the provided wavelength. - This is the correct format for loading into diffpy.utils.DiffractionObject.on_q. """ tth.astype(float) if np.any(np.deg2rad(tth) > np.pi): @@ -138,8 +134,8 @@ def q_to_d(q): The array of :math:`d` values np.array([ds]). """ if 0 in q: - raise ValueError(invalid_input_emsg) - return 2.0 * np.pi / copy(q)[::-1] + warnings.warn(inf_output_msg) + return 2.0 * np.pi / copy(q) def tth_to_d(ttharray, wavelength): @@ -163,8 +159,8 @@ def d_to_q(d): The units of q must be reciprocal of the units of wavelength. """ if 0 in d: - raise ValueError(invalid_input_emsg) - return 2.0 * np.pi / copy(d)[::-1] + warnings.warn(inf_output_msg) + return 2.0 * np.pi / copy(d) def d_to_tth(darray, wavelength): diff --git a/tests/test_transforms.py b/tests/test_transforms.py index db35d1ef..6c2e4c6e 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -103,8 +103,8 @@ def test_tth_to_q_bad(inputs, expected): ([np.array([])], np.array([])), # UC2: User specified valid q values ( - [np.array([np.pi / 6, 1 * np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi, 5 * np.pi])], - np.array([0.4, 0.5, 0.66667, 1, 2, 12]), + [np.array([5 * np.pi, 4 * np.pi, 3 * np.pi, 2 * np.pi, np.pi, 0])], + np.array([0.4, 0.5, 0.66667, 1, 2, np.inf]), ), ] @@ -115,31 +115,13 @@ def test_q_to_d(inputs, expected): assert np.allclose(actual, expected) -params_q_to_d_bad = [ - # UC1: user specified an invalid q value that results in an infinite d value - ( - [np.array([0, 1, 2, 3, 4])], - [ - ValueError, - "Input values have resulted in an infinite output. Please ensure there are no zeros in the input.", - ], - ), -] - - -@pytest.mark.parametrize("inputs, expected", params_q_to_d_bad) -def test_q_to_d_bad(inputs, expected): - with pytest.raises(expected[0], match=expected[1]): - q_to_d(inputs[0]) - - params_d_to_q = [ # UC1: User specified empty d values ([np.array([])], np.array([])), # UC2: User specified valid d values ( - [np.array([np.pi / 6, 1 * np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi, 5 * np.pi])], - np.array([0.4, 0.5, 0.66667, 1, 2, 12]), + [np.array([0, 1 * np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi, 5 * np.pi])], + np.array([np.inf, 2, 1, 0.66667, 0.5, 0.4]), ), ] @@ -148,21 +130,3 @@ def test_q_to_d_bad(inputs, expected): def test_d_to_q(inputs, expected): actual = d_to_q(inputs[0]) assert np.allclose(actual, expected) - - -params_d_to_q_bad = [ - # UC1: user specified an invalid d value that results in an infinite q value - ( - [np.array([0, 1, 2, 3, 4])], - [ - ValueError, - "Input values have resulted in an infinite output. Please ensure there are no zeros in the input.", - ], - ), -] - - -@pytest.mark.parametrize("inputs, expected", params_d_to_q_bad) -def test_d_to_q_bad(inputs, expected): - with pytest.raises(expected[0], match=expected[1]): - d_to_q(inputs[0]) From ccea574ba34ae69179c38ade63026d22bfc7321a Mon Sep 17 00:00:00 2001 From: yucongalicechen Date: Fri, 6 Dec 2024 14:20:28 -0500 Subject: [PATCH 3/7] better name for the warning msg --- src/diffpy/utils/transforms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/diffpy/utils/transforms.py b/src/diffpy/utils/transforms.py index b4a74bf7..bc6416ea 100644 --- a/src/diffpy/utils/transforms.py +++ b/src/diffpy/utils/transforms.py @@ -16,7 +16,7 @@ "The supplied q-array and wavelength will result in an impossible two-theta. " "Please check these values and re-instantiate the DiffractionObject with correct values." ) -inf_output_msg = "WARNING: The largest output is infinite and cannot be plotted." +inf_output_wmsg = "WARNING: The largest output is infinite and cannot be plotted." def _validate_inputs(q, wavelength): @@ -134,7 +134,7 @@ def q_to_d(q): The array of :math:`d` values np.array([ds]). """ if 0 in q: - warnings.warn(inf_output_msg) + warnings.warn(inf_output_wmsg) return 2.0 * np.pi / copy(q) @@ -159,7 +159,7 @@ def d_to_q(d): The units of q must be reciprocal of the units of wavelength. """ if 0 in d: - warnings.warn(inf_output_msg) + warnings.warn(inf_output_wmsg) return 2.0 * np.pi / copy(d) From 79399a20d42b92ab40061896670f322250db74c9 Mon Sep 17 00:00:00 2001 From: yucongalicechen Date: Fri, 6 Dec 2024 17:14:53 -0500 Subject: [PATCH 4/7] add warning message about wavelength in tth_to_q --- src/diffpy/utils/transforms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/diffpy/utils/transforms.py b/src/diffpy/utils/transforms.py index bc6416ea..92689284 100644 --- a/src/diffpy/utils/transforms.py +++ b/src/diffpy/utils/transforms.py @@ -113,6 +113,7 @@ def tth_to_q(tth, wavelength): pre_factor = (4.0 * np.pi) / wavelength q = pre_factor * np.sin(np.deg2rad(tth / 2)) else: # return intensities vs. an x-array that is just the index + warnings.warn(wavelength_warning_emsg, UserWarning) for i, _ in enumerate(q): q[i] = i return q From 664df765d7bea7c5288ee357974177868b803a6b Mon Sep 17 00:00:00 2001 From: yucongalicechen Date: Fri, 6 Dec 2024 17:15:26 -0500 Subject: [PATCH 5/7] add news --- news/d-q.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 news/d-q.rst diff --git a/news/d-q.rst b/news/d-q.rst new file mode 100644 index 00000000..92105f07 --- /dev/null +++ b/news/d-q.rst @@ -0,0 +1,23 @@ +**Added:** + +* functions to convert between d and q + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From 6295aa5aa83df68357b961d2c70f12f49602d4c8 Mon Sep 17 00:00:00 2001 From: Simon Billinge Date: Fri, 6 Dec 2024 19:49:10 -0500 Subject: [PATCH 6/7] small tweak to infinite d message --- src/diffpy/utils/transforms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/diffpy/utils/transforms.py b/src/diffpy/utils/transforms.py index 92689284..a762529a 100644 --- a/src/diffpy/utils/transforms.py +++ b/src/diffpy/utils/transforms.py @@ -16,7 +16,7 @@ "The supplied q-array and wavelength will result in an impossible two-theta. " "Please check these values and re-instantiate the DiffractionObject with correct values." ) -inf_output_wmsg = "WARNING: The largest output is infinite and cannot be plotted." +inf_output_wmsg = "INFO: The largest d-value in the array is infinite. This is allowed, but it will not be plotted." def _validate_inputs(q, wavelength): @@ -135,7 +135,7 @@ def q_to_d(q): The array of :math:`d` values np.array([ds]). """ if 0 in q: - warnings.warn(inf_output_wmsg) + print(inf_output_wmsg) return 2.0 * np.pi / copy(q) From 39f1c542a854fcf2e462a7190f833efc0ff87d12 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 00:49:19 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit hooks --- src/diffpy/utils/transforms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/diffpy/utils/transforms.py b/src/diffpy/utils/transforms.py index a762529a..0d02fe54 100644 --- a/src/diffpy/utils/transforms.py +++ b/src/diffpy/utils/transforms.py @@ -16,7 +16,9 @@ "The supplied q-array and wavelength will result in an impossible two-theta. " "Please check these values and re-instantiate the DiffractionObject with correct values." ) -inf_output_wmsg = "INFO: The largest d-value in the array is infinite. This is allowed, but it will not be plotted." +inf_output_wmsg = ( + "INFO: The largest d-value in the array is infinite. This is allowed, but it will not be plotted." +) def _validate_inputs(q, wavelength):