From 1baaaeefa06d27e70bbc742c01179e10a4f6ae58 Mon Sep 17 00:00:00 2001 From: Sparks29032 Date: Thu, 18 Dec 2025 16:33:17 -0800 Subject: [PATCH 1/3] Fix scale + stretch + apply with extrap warning --- src/diffpy/morph/morph_io.py | 63 ++++++++++++++++---------------- src/diffpy/morph/morphs/morph.py | 1 + 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/diffpy/morph/morph_io.py b/src/diffpy/morph/morph_io.py index 5c013a7..b0d80b6 100644 --- a/src/diffpy/morph/morph_io.py +++ b/src/diffpy/morph/morph_io.py @@ -501,38 +501,39 @@ def tabulate_results(multiple_morph_results): def handle_extrapolation_warnings(morph): if morph is not None: extrapolation_info = morph.extrapolation_info - is_extrap_low = extrapolation_info["is_extrap_low"] - is_extrap_high = extrapolation_info["is_extrap_high"] - cutoff_low = extrapolation_info["cutoff_low"] - cutoff_high = extrapolation_info["cutoff_high"] - - if is_extrap_low and is_extrap_high: - wmsg = ( - "Warning: points with grid value below " - f"{cutoff_low} and above " - f"{cutoff_high} " - f"are extrapolated." - ) - elif is_extrap_low: - wmsg = ( - "Warning: points with grid value below " - f"{cutoff_low} " - f"are extrapolated." - ) - elif is_extrap_high: - wmsg = ( - "Warning: points with grid value above " - f"{cutoff_high} " - f"are extrapolated." - ) - else: - wmsg = None + if extrapolation_info is not None: + is_extrap_low = extrapolation_info["is_extrap_low"] + is_extrap_high = extrapolation_info["is_extrap_high"] + cutoff_low = extrapolation_info["cutoff_low"] + cutoff_high = extrapolation_info["cutoff_high"] + + if is_extrap_low and is_extrap_high: + wmsg = ( + "Warning: points with grid value below " + f"{cutoff_low} and above " + f"{cutoff_high} " + f"are extrapolated." + ) + elif is_extrap_low: + wmsg = ( + "Warning: points with grid value below " + f"{cutoff_low} " + f"are extrapolated." + ) + elif is_extrap_high: + wmsg = ( + "Warning: points with grid value above " + f"{cutoff_high} " + f"are extrapolated." + ) + else: + wmsg = None - if wmsg: - warnings.warn( - wmsg, - UserWarning, - ) + if wmsg: + warnings.warn( + wmsg, + UserWarning, + ) def handle_check_increase_warning(squeeze_morph): diff --git a/src/diffpy/morph/morphs/morph.py b/src/diffpy/morph/morphs/morph.py index 7ffccd1..8552d5d 100644 --- a/src/diffpy/morph/morphs/morph.py +++ b/src/diffpy/morph/morphs/morph.py @@ -124,6 +124,7 @@ def __init__(self, config=None): All configuration variables. """ # declare empty attributes + self.extrapolation_info = None if config is None: config = {} self.x_morph_in = None From 32a5abd3e5da2cd2d55803b5a41c42a69db695ab Mon Sep 17 00:00:00 2001 From: Sparks29032 Date: Thu, 18 Dec 2025 16:45:02 -0800 Subject: [PATCH 2/3] Add test + news --- news/extrap_fix.rst | 23 ++++++++++++++++++++++ pyproject.toml | 2 +- tests/test_morphstretch.py | 40 ++++++++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 news/extrap_fix.rst diff --git a/news/extrap_fix.rst b/news/extrap_fix.rst new file mode 100644 index 0000000..f40a9c5 --- /dev/null +++ b/news/extrap_fix.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Applying a function with no extrapolation no longer produces an error. + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index 735b5e7..8473781 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ maintainers = [ { name="Simon J.L. Billinge group", email="simon.billinge@gmail.com" }, ] -description = "Python package for manipulating and comparing PDF profiles" +description = "Python package for manipulating and comparing 1D signals." keywords = ['diffpy', 'pdf', 'data interpretation'] readme = "README.rst" requires-python = ">=3.11, <3.14" diff --git a/tests/test_morphstretch.py b/tests/test_morphstretch.py index 311c92e..0b7d507 100644 --- a/tests/test_morphstretch.py +++ b/tests/test_morphstretch.py @@ -101,16 +101,16 @@ def test_morphshift_extrapolate(user_filesystem, stretch, wmsg_gen): y_morph = numpy.sin(x_morph) x_target = x_morph.copy() y_target = y_morph.copy() - with pytest.warns() as w: + with pytest.warns() as warning: morphpy.morph_arrays( numpy.array([x_morph, y_morph]).T, numpy.array([x_target, y_target]).T, stretch=stretch, apply=True, ) - assert len(w) == 1 - assert w[0].category is UserWarning - actual_wmsg = str(w[0].message) + assert len(warning) == 1 + assert warning[0].category is UserWarning + actual_wmsg = str(warning[0].message) expected_wmsg = wmsg_gen([min(x_morph), max(x_morph)]) assert actual_wmsg == expected_wmsg @@ -131,3 +131,35 @@ def test_morphshift_extrapolate(user_filesystem, stretch, wmsg_gen): ) with pytest.warns(UserWarning, match=expected_wmsg): single_morph(parser, opts, pargs, stdout_flag=False) + + +def test_morphshift_no_warning(user_filesystem): + # Apply a stretch with no extrapolation + # There should be no warning or errors produced + x_morph = numpy.linspace(1, 10, 101) + y_morph = numpy.sin(x_morph) + x_target = x_morph.copy() + y_target = y_morph.copy() + morphpy.morph_arrays( + numpy.array([x_morph, y_morph]).T, + numpy.array([x_target, y_target]).T, + stretch=0, + apply=True, + ) + + # CLI test + morph_file, target_file = create_morph_data_file( + user_filesystem / "cwd_dir", x_morph, y_morph, x_target, y_target + ) + + parser = create_option_parser() + (opts, pargs) = parser.parse_args( + [ + "--stretch=0", + f"{morph_file.as_posix()}", + f"{target_file.as_posix()}", + "--apply", + "-n", + ] + ) + single_morph(parser, opts, pargs, stdout_flag=False) From d3e5a91e947ff4c764ee580631dd19f270d033eb Mon Sep 17 00:00:00 2001 From: Sparks29032 Date: Thu, 18 Dec 2025 16:49:26 -0800 Subject: [PATCH 3/3] Add more tests --- tests/test_morphshift.py | 33 +++++++++++++++++++++++++++++++++ tests/test_morphsqueeze.py | 38 ++++++++++++++++++++++++++++++++++++++ tests/test_morphstretch.py | 1 + 3 files changed, 72 insertions(+) diff --git a/tests/test_morphshift.py b/tests/test_morphshift.py index 1ae2e0f..28b2d1b 100644 --- a/tests/test_morphshift.py +++ b/tests/test_morphshift.py @@ -105,3 +105,36 @@ def test_morphshift_extrapolate(user_filesystem, capsys, hshift, wmsg_gen): ) with pytest.warns(UserWarning, match=expected_wmsg): single_morph(parser, opts, pargs, stdout_flag=False) + + +def test_morphshift_no_warning(user_filesystem): + # Apply a shift with no extrapolation + # There should be no warning or errors produced + x_morph = numpy.linspace(0, 10, 101) + y_morph = numpy.sin(x_morph) + x_target = x_morph.copy() + y_target = y_morph.copy() + morphpy.morph_arrays( + numpy.array([x_morph, y_morph]).T, + numpy.array([x_target, y_target]).T, + hshift=0, + apply=True, + ) + + # CLI test + morph_file, target_file = create_morph_data_file( + user_filesystem / "cwd_dir", x_morph, y_morph, x_target, y_target + ) + + parser = create_option_parser() + (opts, pargs) = parser.parse_args( + [ + "--scale=1", + "--hshift=0", + f"{morph_file.as_posix()}", + f"{target_file.as_posix()}", + "--apply", + "-n", + ] + ) + single_morph(parser, opts, pargs, stdout_flag=False) diff --git a/tests/test_morphsqueeze.py b/tests/test_morphsqueeze.py index 2bb65b0..8a17208 100644 --- a/tests/test_morphsqueeze.py +++ b/tests/test_morphsqueeze.py @@ -174,6 +174,44 @@ def test_morphsqueeze_extrapolate(user_filesystem, squeeze_coeffs, wmsg_gen): single_morph(parser, opts, pargs, stdout_flag=False) +def test_morphsqueeze_no_warning(user_filesystem): + # Apply a squeeze with no extrapolation + # There should be no warning or errors produced + squeeze_coeffs = {"a0": 0, "a1": 0} + x_morph = np.linspace(0, 10, 101) + y_morph = np.sin(x_morph) + x_target = x_morph.copy() + y_target = y_morph.copy() + morph = MorphSqueeze() + morph.squeeze = squeeze_coeffs + coeffs = [squeeze_coeffs[f"a{i}"] for i in range(len(squeeze_coeffs))] + morphpy.morph_arrays( + np.array([x_morph, y_morph]).T, + np.array([x_target, y_target]).T, + squeeze=coeffs, + apply=True, + ) + + # CLI test + morph_file, target_file = create_morph_data_file( + user_filesystem / "cwd_dir", x_morph, y_morph, x_target, y_target + ) + + parser = create_option_parser() + (opts, pargs) = parser.parse_args( + [ + "--scale=1", + "--squeeze", + ",".join(map(str, coeffs)), + f"{morph_file.as_posix()}", + f"{target_file.as_posix()}", + "--apply", + "-n", + ] + ) + single_morph(parser, opts, pargs, stdout_flag=False) + + def test_non_unique_grid(): # Test giving morphsqueeze a non-unique grid # Expect it to return a unique grid diff --git a/tests/test_morphstretch.py b/tests/test_morphstretch.py index 0b7d507..fb07620 100644 --- a/tests/test_morphstretch.py +++ b/tests/test_morphstretch.py @@ -155,6 +155,7 @@ def test_morphshift_no_warning(user_filesystem): parser = create_option_parser() (opts, pargs) = parser.parse_args( [ + "--scale=1", "--stretch=0", f"{morph_file.as_posix()}", f"{target_file.as_posix()}",