Skip to content

Commit 75e0ef8

Browse files
authored
Merge pull request #211 from yucongalicechen/scaleto
tests for scale_to
2 parents 9fa7ce7 + 53413fb commit 75e0ef8

File tree

3 files changed

+202
-21
lines changed

3 files changed

+202
-21
lines changed

Diff for: news/scaleto.rst

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* functionality to rescale diffraction objects, placing one on top of another at a specified point
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>

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

+27-21
Original file line numberDiff line numberDiff line change
@@ -401,40 +401,46 @@ def on_tth(self):
401401
def on_d(self):
402402
return [self.all_arrays[:, 3], self.all_arrays[:, 0]]
403403

404-
def scale_to(self, target_diff_object, xtype=None, xvalue=None):
404+
def scale_to(self, target_diff_object, q=None, tth=None, d=None, offset=0):
405405
"""
406-
Return a new diffraction object which is the current object but recaled in y to the target
406+
returns a new diffraction object which is the current object but rescaled in y to the target
407+
408+
The y-value in the target at the closest specified x-value will be used as the factor to scale to.
409+
The entire array is scaled by this factor so that one object places on top of the other at that point.
410+
If multiple values of `q`, `tth`, or `d` are provided, or none are provided, an error will be raised.
407411
408412
Parameters
409413
----------
410414
target_diff_object: DiffractionObject
411-
the diffraction object you want to scale the current one on to
412-
xtype: string, optional. Default is Q
413-
the xtype, from {XQUANTITIES}, that you will specify a point from to scale to
414-
xvalue: float. Default is the midpoint of the array
415-
the y-value in the target at this x-value will be used as the factor to scale to.
416-
The entire array is scaled be the factor that places on on top of the other at that point.
417-
xvalue does not have to be in the x-array, the point closest to this point will be used for the scaling.
415+
the diffraction object you want to scale the current one onto
416+
417+
q, tth, d : float, optional, must specify exactly one of them
418+
The value of the x-array where you want the curves to line up vertically.
419+
Specify a value on one of the allowed grids, q, tth, or d), e.g., q=10.
420+
421+
offset : float, optional, default is 0
422+
an offset to add to the scaled y-values
418423
419424
Returns
420425
-------
421426
the rescaled DiffractionObject as a new object
422-
423427
"""
424-
scaled = deepcopy(self)
425-
if xtype is None:
426-
xtype = "q"
428+
scaled = self.copy()
429+
count = sum([q is not None, tth is not None, d is not None])
430+
if count != 1:
431+
raise ValueError(
432+
"You must specify exactly one of 'q', 'tth', or 'd'. Please rerun specifying only one."
433+
)
427434

435+
xtype = "q" if q is not None else "tth" if tth is not None else "d"
428436
data = self.on_xtype(xtype)
429437
target = target_diff_object.on_xtype(xtype)
430-
if xvalue is None:
431-
xvalue = data[0][0] + (data[0][-1] - data[0][0]) / 2.0
432-
433-
xindex = (np.abs(data[0] - xvalue)).argmin()
434-
ytarget = target[1][xindex]
435-
yself = data[1][xindex]
436-
scaled.on_tth[1] = data[1] * ytarget / yself
437-
scaled.on_q[1] = data[1] * ytarget / yself
438+
439+
xvalue = q if xtype == "q" else tth if xtype == "tth" else d
440+
441+
xindex_data = (np.abs(data[0] - xvalue)).argmin()
442+
xindex_target = (np.abs(target[0] - xvalue)).argmin()
443+
scaled._all_arrays[:, 0] = data[1] * target[1][xindex_target] / data[1][xindex_data] + offset
438444
return scaled
439445

440446
def on_xtype(self, xtype):

Diff for: tests/test_diffraction_objects.py

+152
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,158 @@ def test_init_invalid_xtype():
182182
DiffractionObject(xtype="invalid_type")
183183

184184

185+
params_scale_to = [
186+
# UC1: same x-array and y-array, check offset
187+
(
188+
{
189+
"xarray": np.array([10, 15, 25, 30, 60, 140]),
190+
"yarray": np.array([2, 3, 4, 5, 6, 7]),
191+
"xtype": "tth",
192+
"wavelength": 2 * np.pi,
193+
"target_xarray": np.array([10, 15, 25, 30, 60, 140]),
194+
"target_yarray": np.array([2, 3, 4, 5, 6, 7]),
195+
"target_xtype": "tth",
196+
"target_wavelength": 2 * np.pi,
197+
"q": None,
198+
"tth": 60,
199+
"d": None,
200+
"offset": 2.1,
201+
},
202+
{"xtype": "tth", "yarray": np.array([4.1, 5.1, 6.1, 7.1, 8.1, 9.1])},
203+
),
204+
# UC2: same length x-arrays with exact x-value match
205+
(
206+
{
207+
"xarray": np.array([10, 15, 25, 30, 60, 140]),
208+
"yarray": np.array([10, 20, 25, 30, 60, 100]),
209+
"xtype": "tth",
210+
"wavelength": 2 * np.pi,
211+
"target_xarray": np.array([10, 20, 25, 30, 60, 140]),
212+
"target_yarray": np.array([2, 3, 4, 5, 6, 7]),
213+
"target_xtype": "tth",
214+
"target_wavelength": 2 * np.pi,
215+
"q": None,
216+
"tth": 60,
217+
"d": None,
218+
"offset": 0,
219+
},
220+
{"xtype": "tth", "yarray": np.array([1, 2, 2.5, 3, 6, 10])},
221+
),
222+
# UC3: same length x-arrays with approximate x-value match
223+
(
224+
{
225+
"xarray": np.array([0.12, 0.24, 0.31, 0.4]),
226+
"yarray": np.array([10, 20, 40, 60]),
227+
"xtype": "q",
228+
"wavelength": 2 * np.pi,
229+
"target_xarray": np.array([0.14, 0.24, 0.31, 0.4]),
230+
"target_yarray": np.array([1, 3, 4, 5]),
231+
"target_xtype": "q",
232+
"target_wavelength": 2 * np.pi,
233+
"q": 0.1,
234+
"tth": None,
235+
"d": None,
236+
"offset": 0,
237+
},
238+
{"xtype": "q", "yarray": np.array([1, 2, 4, 6])},
239+
),
240+
# UC4: different x-array lengths with approximate x-value match
241+
(
242+
{
243+
"xarray": np.array([10, 25, 30.1, 40.2, 61, 120, 140]),
244+
"yarray": np.array([10, 20, 30, 40, 50, 60, 100]),
245+
"xtype": "tth",
246+
"wavelength": 2 * np.pi,
247+
"target_xarray": np.array([20, 25.5, 32, 45, 50, 62, 100, 125, 140]),
248+
"target_yarray": np.array([1.1, 2, 3, 3.5, 4, 5, 10, 12, 13]),
249+
"target_xtype": "tth",
250+
"target_wavelength": 2 * np.pi,
251+
"q": None,
252+
"tth": 60,
253+
"d": None,
254+
"offset": 0,
255+
},
256+
# scaling factor is calculated at index = 4 (tth=61) for self and index = 5 for target (tth=62)
257+
{"xtype": "tth", "yarray": np.array([1, 2, 3, 4, 5, 6, 10])},
258+
),
259+
]
260+
261+
262+
@pytest.mark.parametrize("inputs, expected", params_scale_to)
263+
def test_scale_to(inputs, expected):
264+
orig_diff_object = DiffractionObject(
265+
xarray=inputs["xarray"], yarray=inputs["yarray"], xtype=inputs["xtype"], wavelength=inputs["wavelength"]
266+
)
267+
target_diff_object = DiffractionObject(
268+
xarray=inputs["target_xarray"],
269+
yarray=inputs["target_yarray"],
270+
xtype=inputs["target_xtype"],
271+
wavelength=inputs["target_wavelength"],
272+
)
273+
scaled_diff_object = orig_diff_object.scale_to(
274+
target_diff_object, q=inputs["q"], tth=inputs["tth"], d=inputs["d"], offset=inputs["offset"]
275+
)
276+
# Check the intensity data is the same as expected
277+
assert np.allclose(scaled_diff_object.on_xtype(expected["xtype"])[1], expected["yarray"])
278+
279+
280+
params_scale_to_bad = [
281+
# UC1: user did not specify anything
282+
(
283+
{
284+
"xarray": np.array([0.1, 0.2, 0.3]),
285+
"yarray": np.array([1, 2, 3]),
286+
"xtype": "q",
287+
"wavelength": 2 * np.pi,
288+
"target_xarray": np.array([0.05, 0.1, 0.2, 0.3]),
289+
"target_yarray": np.array([5, 10, 20, 30]),
290+
"target_xtype": "q",
291+
"target_wavelength": 2 * np.pi,
292+
"q": None,
293+
"tth": None,
294+
"d": None,
295+
"offset": 0,
296+
}
297+
),
298+
# UC2: user specified more than one of q, tth, and d
299+
(
300+
{
301+
"xarray": np.array([10, 25, 30.1, 40.2, 61, 120, 140]),
302+
"yarray": np.array([10, 20, 30, 40, 50, 60, 100]),
303+
"xtype": "tth",
304+
"wavelength": 2 * np.pi,
305+
"target_xarray": np.array([20, 25.5, 32, 45, 50, 62, 100, 125, 140]),
306+
"target_yarray": np.array([1.1, 2, 3, 3.5, 4, 5, 10, 12, 13]),
307+
"target_xtype": "tth",
308+
"target_wavelength": 2 * np.pi,
309+
"q": None,
310+
"tth": 60,
311+
"d": 10,
312+
"offset": 0,
313+
}
314+
),
315+
]
316+
317+
318+
@pytest.mark.parametrize("inputs", params_scale_to_bad)
319+
def test_scale_to_bad(inputs):
320+
orig_diff_object = DiffractionObject(
321+
xarray=inputs["xarray"], yarray=inputs["yarray"], xtype=inputs["xtype"], wavelength=inputs["wavelength"]
322+
)
323+
target_diff_object = DiffractionObject(
324+
xarray=inputs["target_xarray"],
325+
yarray=inputs["target_yarray"],
326+
xtype=inputs["target_xtype"],
327+
wavelength=inputs["target_wavelength"],
328+
)
329+
with pytest.raises(
330+
ValueError, match="You must specify exactly one of 'q', 'tth', or 'd'. Please rerun specifying only one."
331+
):
332+
orig_diff_object.scale_to(
333+
target_diff_object, q=inputs["q"], tth=inputs["tth"], d=inputs["d"], offset=inputs["offset"]
334+
)
335+
336+
185337
params_index = [
186338
# UC1: exact match
187339
(4 * np.pi, np.array([30.005, 60]), np.array([1, 2]), "tth", "tth", 30.005, [0]),

0 commit comments

Comments
 (0)