diff --git a/news/gt.rst b/news/gt.rst new file mode 100644 index 0000000..db01cae --- /dev/null +++ b/news/gt.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* qmin/qmax limits in reciprocal space grid so qmin and qmax are not excluded + +**Security:** + +* diff --git a/requirements/test.txt b/requirements/test.txt index a727786..d9d0c8c 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -4,3 +4,4 @@ codecov coverage pytest-cov pytest-env +pytest-mock diff --git a/src/diffpy/fourigui/fourigui.py b/src/diffpy/fourigui/fourigui.py index c3c632d..ca7e280 100755 --- a/src/diffpy/fourigui/fourigui.py +++ b/src/diffpy/fourigui/fourigui.py @@ -291,14 +291,11 @@ def load_cube(self): self.slider.grid(row=0, column=0, padx=10, pady=10, sticky=tk.N + tk.E + tk.S + tk.W) if not self.loaded: - fig, ax = plt.subplots(figsize=(4.95, 4.95)) fig = plt.gcf() DPI = fig.get_dpi() fig.set_size_inches(500 / float(DPI), 500 / float(DPI)) - self.plane_num.set(np.shape(self.cube)[0] // 2) - if self.axis.get() == 0: self.im = plt.imshow(self.cube[self.plane_num.get(), :, :]) elif self.axis.get() == 1: @@ -319,7 +316,6 @@ def load_cube(self): self.canvas.draw() self.canvas.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.loaded = True - else: self.plot_plane() self.transformed = False @@ -485,7 +481,7 @@ def applycutoff(self): r2_outer = qmax**2 i, j, k = np.meshgrid(np.arange(xdim), np.arange(ydim), np.arange(zdim)) r2 = (i - xdim // 2) ** 2 + (j - ydim // 2) ** 2 + (k - zdim // 2) ** 2 - mask = (r2 <= r2_inner) | (r2 >= r2_outer) # True if voxel is out of range + mask = (r2 < r2_inner) | (r2 > r2_outer) # True if voxel is out of range sphere[mask] = np.nan # therefore set to np.nan if out of range if self.space.get(): diff --git a/tests/integration_test.py b/tests/integration_test.py index 214ce22..f343d5f 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -1,5 +1,4 @@ import unittest -import warnings import h5py import numpy as np @@ -98,45 +97,6 @@ def test_fft_testdataset3(self): # then self.assertTrue(np.allclose(result, self.test_gofr_cut_15to35px)) - def test_applycutoff_range1(self): - # given - self.test_gui.plot_plane = lambda *a, **b: () - self.test_gui.cube = self.test_sofq - self.test_gui.qminentry.insert(0, "10") - self.test_gui.qmaxentry.insert(0, "40") - - # Desired behavior is nans in the arrays below qmin and above qmax. As a result, np.nanmax will generate - # runtimewarnings when it # encounters slices that are all nans. capture these so tests pass cleanly - # without warnings - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=RuntimeWarning) - # when - self.test_gui.applycutoff() - result = self.test_gui.cube - - # then - self.assertTrue(np.allclose(np.nan_to_num(result), np.nan_to_num(self.test_sofq_cut_10to40px))) - - def test_applycutoff_range2(self): - # given - self.test_gui.plot_plane = lambda *a, **b: () - self.test_gui.cube = self.test_sofq - self.test_gui.qminentry.insert(0, "15") - self.test_gui.qmaxentry.insert(0, "35") - - # Desired behavior is nans in the arrays below qmin and above qmax. As a result, np.nanmax will generate - # runtimewarnings when it # encounters slices that are all nans. capture these so tests pass cleanly - # without warnings - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=RuntimeWarning) - # when - self.test_gui.applycutoff() - result = self.test_gui.cube - - # then - # with self.assertWarns(RuntimeWarning): - self.assertTrue(np.allclose(np.nan_to_num(result), np.nan_to_num(self.test_sofq_cut_15to35px))) - if __name__ == "__main__": unittest.main() diff --git a/tests/unit_test.py b/tests/test_fourigui.py similarity index 59% rename from tests/unit_test.py rename to tests/test_fourigui.py index b3b9ce3..52a556b 100644 --- a/tests/unit_test.py +++ b/tests/test_fourigui.py @@ -1,6 +1,8 @@ +import tkinter as tk import unittest import h5py +import numpy as np from diffpy.fourigui.fourigui import Gui @@ -146,5 +148,81 @@ def test_fft_111(self): self.assertTrue(self.test_gui.transformed and self.test_gui.transcutted) +def test_applycutoff(mocker): + root = tk.Tk() + fg = Gui() + # qmin of 1 and qmax of 2 is expected to leave the central pixel and corner + # pixels as NaN's + mocker.patch.object(fg.qminentry, "get", return_value=1.0) + mocker.patch.object(fg.qmaxentry, "get", return_value=2.0) + mocker.patch.object(fg, "plot_plane") # we don't want it to plot anything so intercept + fg.cutted = False + fg.cube = np.ones((5, 5, 5)) + expected_ones = np.ones((5, 5, 5)) + expected_recip = np.array( + [ + [ + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, 1, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + ], + [ + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, 1, 1, 1, np.nan], + [np.nan, 1, 1, 1, np.nan], + [np.nan, 1, 1, 1, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + ], + [ + [np.nan, np.nan, 1, np.nan, np.nan], + [np.nan, 1, 1, 1, np.nan], + [1, 1, np.nan, 1, 1], + [np.nan, 1, 1, 1, np.nan], + [np.nan, np.nan, 1, np.nan, np.nan], + ], + [ + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, 1, 1, 1, np.nan], + [np.nan, 1, 1, 1, np.nan], + [np.nan, 1, 1, 1, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + ], + [ + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, 1, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + [np.nan, np.nan, np.nan, np.nan, np.nan], + ], + ] + ) + # test the case where fg.space is 0 + fg.applycutoff() + np.testing.assert_array_equal(fg.cube_reci, expected_ones) + np.testing.assert_array_equal(fg.cube_recicut, expected_recip) + root.destroy() # Clean up Tkinter instance + + # test the case where fg.space is 1 + root = tk.Tk() + fg = Gui() + # qmin of 1 and qmax of 2 is expected to leave the central pixel and corner + # pixels as NaN's + mocker.patch.object(fg.qminentry, "get", return_value=1) + mocker.patch.object(fg.qmaxentry, "get", return_value=2) + mocker.patch.object( + fg, "fft" + ) # we don't want it to do the fft so intercept. Should be tested separately (fixme). + fg.cutted = False + fg.cube_reci = np.ones((5, 5, 5)) + fg.cube = np.ones((5, 5, 5)) + mocker.patch.object(fg.space, "get", return_value=1) + fg.applycutoff() + np.testing.assert_array_equal(fg.cube_real, expected_ones) + np.testing.assert_array_equal(fg.cube_recicut, expected_recip) + root.destroy() # Clean up Tkinter instance + + if __name__ == "__main__": unittest.main()