Skip to content

Commit 6f29b6e

Browse files
initial commit
1 parent 1199a48 commit 6f29b6e

File tree

2 files changed

+138
-14
lines changed

2 files changed

+138
-14
lines changed

src/diffpy/utils/transforms.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
"and number is the wavelength in angstroms."
1313
)
1414
invalid_tth_emsg = "Two theta exceeds 180 degrees. Please check the input values for errors."
15-
invalid_q_or_wavelength_emsg = (
16-
"The supplied q-array and wavelength will result in an impossible two-theta. "
15+
invalid_q_or_d_or_wavelength_emsg = (
16+
"The supplied input array and wavelength will result in an impossible two-theta. "
1717
"Please check these values and re-instantiate the DiffractionObject with correct values."
1818
)
1919
inf_output_wmsg = "WARNING: The largest output is infinite and cannot be plotted."
@@ -25,7 +25,7 @@ def _validate_inputs(q, wavelength):
2525
return np.empty(0)
2626
pre_factor = wavelength / (4 * np.pi)
2727
if np.any(np.abs(q * pre_factor) > 1.0):
28-
raise ValueError(invalid_q_or_wavelength_emsg)
28+
raise ValueError(invalid_q_or_d_or_wavelength_emsg)
2929

3030

3131
def q_to_tth(q, wavelength):
@@ -130,7 +130,9 @@ def tth_to_d(tth, wavelength):
130130
r"""
131131
Helper function to convert two-theta to d on independent variable axis.
132132
133-
Uses the formula .. math:: d = \frac{\lambda}{2 \sin\left(\frac{2\theta}{2}\right)}.
133+
The formula is .. math:: d = \frac{\lambda}{2 \sin\left(\frac{2\theta}{2}\right)}.
134+
135+
Here we convert tth to q first, then to d.
134136
135137
Parameters
136138
----------
@@ -146,17 +148,43 @@ def tth_to_d(tth, wavelength):
146148
d : 1D array
147149
The array of :math:`d` values np.array([ds]).
148150
"""
149-
tth = np.deg2rad(tth)
150-
if 0 in tth:
151+
q = tth_to_q(tth, wavelength)
152+
d = copy(tth)
153+
if wavelength is None:
154+
warnings.warn(wavelength_warning_emsg, UserWarning)
155+
for i, _ in enumerate(tth):
156+
d[i] = i
157+
return d
158+
if 0 in q:
151159
warnings.warn(inf_output_wmsg)
152-
qarray = tth_to_q(tth, wavelength)
153-
return 2.0 * np.pi / copy(qarray)
160+
return 2.0 * np.pi / copy(q)
154161

155162

156163
def d_to_q(darray):
157164
return 2.0 * np.pi / copy(darray)
158165

159166

160-
def d_to_tth(darray, wavelength):
161-
qarray = d_to_q(darray)
162-
return q_to_tth(qarray, wavelength)
167+
def d_to_tth(d, wavelength):
168+
r"""
169+
Helper function to convert d to two-theta on independent variable axis.
170+
171+
The formula is .. math:: 2\theta = 2 \arcsin\left(\frac{\lambda}{2d}\right).
172+
173+
Here we convert d to q first, then to tth.
174+
175+
Parameters
176+
----------
177+
d : 1D array
178+
The array of :math:`d` values np.array([ds]).
179+
180+
wavelength : float
181+
Wavelength of the incoming x-rays/neutrons/electrons
182+
183+
Returns
184+
-------
185+
tth : 1D array
186+
The array of :math:`2\theta` values np.array([tths]).
187+
The units of tth are expected in degrees.
188+
"""
189+
q = d_to_q(d)
190+
return q_to_tth(q, wavelength)

tests/test_transforms.py

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22
import pytest
33

4-
from diffpy.utils.transforms import q_to_tth, tth_to_q
4+
from diffpy.utils.transforms import d_to_tth, q_to_tth, tth_to_d, tth_to_q
55

66
params_q_to_tth = [
77
# UC1: Empty q values, no wavelength, return empty arrays
@@ -31,7 +31,7 @@ def test_q_to_tth(inputs, expected):
3131
[4 * np.pi, np.array([0.2, 0.4, 0.6, 0.8, 1, 1.2])],
3232
[
3333
ValueError,
34-
"The supplied q-array and wavelength will result in an impossible two-theta. "
34+
"The supplied input array and wavelength will result in an impossible two-theta. "
3535
"Please check these values and re-instantiate the DiffractionObject with correct values.",
3636
],
3737
),
@@ -40,7 +40,7 @@ def test_q_to_tth(inputs, expected):
4040
[100, np.array([0, 0.2, 0.4, 0.6, 0.8, 1])],
4141
[
4242
ValueError,
43-
"The supplied q-array and wavelength will result in an impossible two-theta. "
43+
"The supplied input array and wavelength will result in an impossible two-theta. "
4444
"Please check these values and re-instantiate the DiffractionObject with correct values.",
4545
],
4646
),
@@ -96,3 +96,99 @@ def test_tth_to_q(inputs, expected):
9696
def test_tth_to_q_bad(inputs, expected):
9797
with pytest.raises(expected[0], match=expected[1]):
9898
tth_to_q(inputs[1], inputs[0])
99+
100+
101+
params_tth_to_d = [
102+
# UC0: User specified empty tth values (without wavelength)
103+
([None, np.array([])], np.array([])),
104+
# UC1: User specified empty tth values (with wavelength)
105+
([4 * np.pi, np.array([])], np.array([])),
106+
# UC2: User specified valid tth values between 0-180 degrees (without wavelength)
107+
(
108+
[None, np.array([0, 30, 60, 90, 120, 180])],
109+
np.array([0, 1, 2, 3, 4, 5]),
110+
),
111+
# UC3: User specified valid tth values between 0-180 degrees (with wavelength)
112+
(
113+
[4 * np.pi, np.array([0, 30.0, 60.0, 90.0, 120.0, 180.0])],
114+
np.array([np.inf, 24.27636, 12.56637, 8.88577, 7.25520, 6.28319]),
115+
),
116+
]
117+
118+
119+
@pytest.mark.parametrize("inputs, expected", params_tth_to_d)
120+
def test_tth_to_d(inputs, expected):
121+
actual = tth_to_d(inputs[1], inputs[0])
122+
assert np.allclose(actual, expected)
123+
124+
125+
params_tth_to_d_bad = [
126+
# UC1: user specified an invalid tth value of > 180 degrees (without wavelength)
127+
(
128+
[None, np.array([0, 30, 60, 90, 120, 181])],
129+
[ValueError, "Two theta exceeds 180 degrees. Please check the input values for errors."],
130+
),
131+
# UC2: user specified an invalid tth value of > 180 degrees (with wavelength)
132+
(
133+
[4 * np.pi, np.array([0, 30, 60, 90, 120, 181])],
134+
[ValueError, "Two theta exceeds 180 degrees. Please check the input values for errors."],
135+
),
136+
]
137+
138+
139+
@pytest.mark.parametrize("inputs, expected", params_tth_to_d_bad)
140+
def test_tth_to_d_bad(inputs, expected):
141+
with pytest.raises(expected[0], match=expected[1]):
142+
tth_to_d(inputs[1], inputs[0])
143+
144+
145+
params_d_to_tth = [
146+
# UC1: Empty d values, no wavelength, return empty arrays
147+
([None, np.empty((0))], np.empty((0))),
148+
# UC2: Empty d values, wavelength specified, return empty arrays
149+
([4 * np.pi, np.empty((0))], np.empty(0)),
150+
# UC3: User specified valid d values, no wavelength, return empty arrays
151+
(
152+
[None, np.array([0, 0.2, 0.4, 0.6, 0.8, 1])],
153+
np.array([0, 1, 2, 3, 4, 5]),
154+
),
155+
# UC4: User specified valid d values (with wavelength)
156+
(
157+
[4 * np.pi, np.array([4 * np.pi, 4 / np.sqrt(2) * np.pi, 4 / np.sqrt(3) * np.pi])],
158+
np.array([60.0, 90.0, 120.0]),
159+
),
160+
]
161+
162+
163+
@pytest.mark.parametrize("inputs, expected", params_d_to_tth)
164+
def test_d_to_tth(inputs, expected):
165+
actual = d_to_tth(inputs[1], inputs[0])
166+
assert np.allclose(expected, actual)
167+
168+
169+
params_d_to_tth_bad = [
170+
# UC1: user specified invalid d values that result in tth > 180 degrees
171+
(
172+
[4 * np.pi, np.array([0.2, 0.4, 0.6, 0.8, 1, 1.2])],
173+
[
174+
ValueError,
175+
"The supplied input array and wavelength will result in an impossible two-theta. "
176+
"Please check these values and re-instantiate the DiffractionObject with correct values.",
177+
],
178+
),
179+
# UC2: user specified a wrong wavelength that result in tth > 180 degrees
180+
(
181+
[100, np.array([0, 0.2, 0.4, 0.6, 0.8, 1])],
182+
[
183+
ValueError,
184+
"The supplied input array and wavelength will result in an impossible two-theta. "
185+
"Please check these values and re-instantiate the DiffractionObject with correct values.",
186+
],
187+
),
188+
]
189+
190+
191+
@pytest.mark.parametrize("inputs, expected", params_d_to_tth_bad)
192+
def test_d_to_tth_bad(inputs, expected):
193+
with pytest.raises(expected[0], match=expected[1]):
194+
d_to_tth(inputs[1], inputs[0])

0 commit comments

Comments
 (0)