예제 #1
0
    def test_flat_dimensions_remove(self):

        geom = gd.UniformGrid([101, 1], x0=[0, 0], dx=[0.01, 1])

        data = np.array([i * np.linspace(1, 5, 1) for i in range(101)])
        ug_data = gd.UniformGridData(geom, data)

        ug_data.flat_dimensions_remove()

        flat_geom = gd.UniformGrid([101], x0=[0], x1=[1])

        self.assertEqual(
            ug_data, gd.UniformGridData(flat_geom, np.linspace(0, 100, 101))
        )

        # Check invalidation of spline
        self.assertTrue(ug_data.invalid_spline)

        # Test from 3D to 2D
        grid_data3d = gdu.sample_function_from_uniformgrid(
            lambda x, y, z: x * (y + 2) * (z + 5),
            gd.UniformGrid([10, 20, 1], x0=[0, 1, 0], dx=[1, 1, 1]),
        )
        grid_2d = gd.UniformGrid([10, 20], x0=[0, 1], dx=[1, 1])

        expected_data2d = gdu.sample_function_from_uniformgrid(
            lambda x, y: x * (y + 2) * (0 + 5), grid_2d
        )

        self.assertEqual(
            grid_data3d.flat_dimensions_removed(), expected_data2d
        )
예제 #2
0
    def setUp(self):
        # Let's test with a TimeSeries, a UniformGridData, and a
        # HierarchicalGridData

        x = np.linspace(0, 2 * np.pi, 100)
        y = np.sin(x)

        self.TS = ts.TimeSeries(x, y)

        self.grid_2d = gd.UniformGrid([10, 20], x0=[0.5, 1], dx=[1, 1])

        self.ugd = gdu.sample_function_from_uniformgrid(
            lambda x, y: x * (y + 2), self.grid_2d)

        grid_2d_1 = gd.UniformGrid([10, 20],
                                   x0=[0.5, 1],
                                   dx=[1, 1],
                                   ref_level=0)

        self.ugd1 = gdu.sample_function_from_uniformgrid(
            lambda x, y: x * (y + 2), grid_2d_1)

        grid_2d_2 = gd.UniformGrid([10, 20],
                                   x0=[1, 2],
                                   dx=[3, 0.4],
                                   ref_level=1)

        self.ugd2 = gdu.sample_function_from_uniformgrid(
            lambda x, y: x * (y + 2), grid_2d_2)

        self.hg = gd.HierarchicalGridData([self.ugd1, self.ugd2])
예제 #3
0
    def test_partial_derivated(self):
        # Here we are also testing _call_component_method

        geom = gd.UniformGrid(
            [8001, 3], x0=[0, 0], x1=[2 * np.pi, 1], ref_level=0
        )
        geom2 = gd.UniformGrid(
            [10001, 3], x0=[0, 0], x1=[2 * np.pi, 1], ref_level=1
        )

        sin_wave1 = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom
        )
        sin_wave2 = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom2
        )
        original_sin1 = sin_wave1.copy()
        original_sin2 = sin_wave2.copy()

        sin_wave = gd.HierarchicalGridData([sin_wave1] + [sin_wave2])
        sin_copy = sin_wave.copy()

        # Second derivative should still be a -sin
        sin_wave.partial_derive(0, order=2)

        self.assertTrue(
            np.allclose(-sin_wave[0][0].data, original_sin1.data, atol=1e-3)
        )
        self.assertTrue(
            np.allclose(-sin_wave[1][0].data, original_sin2.data, atol=1e-3)
        )

        # Test _call_component_method with non-string name
        with self.assertRaises(TypeError):
            sin_wave._call_component_method(sin_wave)

        # Test _call_component_method with non existing method
        with self.assertRaises(ValueError):
            sin_wave._call_component_method("lol")

        gradient = sin_copy.gradient(order=2)
        # Along the first direction (it's a HierarchicalGridData)
        partial_x = gradient[0]

        self.assertTrue(
            np.allclose(-partial_x[0][0].data, original_sin1.data, atol=1e-3)
        )
        # First refinement_level
        self.assertTrue(
            np.allclose(-partial_x[1][0].data, original_sin2.data, atol=1e-3)
        )
예제 #4
0
    def test_merge_refinement_levels(self):
        # This also tests to_UniformGridData

        # We redefine this to be ref_level=1
        grid1 = gd.UniformGrid([4, 5], x0=[0, 1], x1=[3, 5], ref_level=1)
        grid2 = gd.UniformGrid([11, 21], x0=[4, 6], x1=[14, 26], ref_level=1)

        grids = [grid1, grid2]

        # Here we use the same data with another big refinement level sampled
        # from the same function
        big_grid = gd.UniformGrid(
            [16, 26], x0=[0, 1], x1=[30, 51], ref_level=0
        )
        # Big grid has resolution 2 dx of grids

        def product(x, y):
            return x * (y + 2)

        grid_data_two_comp = [
            gdu.sample_function_from_uniformgrid(product, g) for g in grids
        ]

        big_grid_data = gdu.sample_function_from_uniformgrid(product, big_grid)
        hg = gd.HierarchicalGridData(grid_data_two_comp + [big_grid_data])
        # When I merge the data I should just get big_grid at the resolution
        # of self.grid_data_two_comp
        expected_grid = gd.UniformGrid(
            [31, 51], x0=[0, 1], x1=[30, 51], ref_level=-1
        )

        expected_data = gdu.sample_function_from_uniformgrid(
            product, expected_grid
        )
        # Test with resample
        self.assertEqual(
            hg.merge_refinement_levels(resample=True), expected_data
        )

        # If we don't resample there will be points that are "wrong" because we
        # compute them with the nearest neighbors of the lowest resolution grid
        # For example, the point with coordinate (5, 1) falls inside the lowest
        # resolution grid, so its value will be the value of the closest point
        # in big_grid (6, 1) -> 18.
        self.assertEqual(hg.merge_refinement_levels()((5, 1)), 18)
        self.assertEqual(hg.merge_refinement_levels().grid, expected_grid)

        # Test a case with only one refinement level, so just returning a copy
        hg_one = gd.HierarchicalGridData([big_grid_data])
        self.assertEqual(hg_one.merge_refinement_levels(), big_grid_data)
예제 #5
0
    def test_coordinates(self):
        def square(x, y):
            return x * (y + 2)

        grid_data = gdu.sample_function_from_uniformgrid(square, self.geom)

        self.assertTrue(
            np.allclose(
                grid_data.coordinates_from_grid()[0],
                self.geom.coordinates()[0],
            )
        )

        # This is a list of UniformGridData
        grids = grid_data.coordinates()
        # Here we check that they agree on two coordinates
        for dim in range(len(grids)):
            self.assertAlmostEqual(
                grids[dim](self.geom[2, 3]), self.geom[2, 3][dim]
            )

        # Here we test coordiantes_meshgrid()
        self.assertTrue(
            np.allclose(
                grid_data.coordinates_meshgrid()[0], self.geom.coordinates()[0]
            )
        )
예제 #6
0
    def test_call_evalute_with_spline(self):

        # Teting call is the same as evalute_with_spline

        hg = gd.HierarchicalGridData(self.grid_data)
        # Test with multiple components
        hg3 = gd.HierarchicalGridData(self.grid_data_two_comp)

        # Scalar input
        self.assertAlmostEqual(hg((2, 3)), 10)
        self.assertAlmostEqual(hg3((2, 3)), 10)

        # Vector input in, vector input out
        self.assertEqual(hg([(2, 3)]).shape, (1,))

        # Scalar input that pretends to be vector
        self.assertAlmostEqual(hg([(2, 3)]), 10)
        self.assertAlmostEqual(hg3([(2, 3)]), 10)

        # Vector input
        self.assertCountEqual(hg([(2, 3), (3, 2)]), [10, 12])
        self.assertCountEqual(hg3([(2, 3), (3, 2)]), [10, 12])

        def product(x, y):
            return x * (y + 2)

        # Uniform grid as input
        grid = gd.UniformGrid([3, 5], x0=[0, 1], x1=[2, 5])
        grid_data = gdu.sample_function_from_uniformgrid(product, grid)
        self.assertTrue(np.allclose(hg3(grid), grid_data.data))
예제 #7
0
    def test_init(self):

        # Test incorrect arguments
        # Not a list
        with self.assertRaises(TypeError):
            gd.HierarchicalGridData(0)

        # Empty list
        with self.assertRaises(ValueError):
            gd.HierarchicalGridData([])

        # Not a list of UniformGridData
        with self.assertRaises(TypeError):
            gd.HierarchicalGridData([0])

        # Inconsistent number of dimensions
        def product1(x):
            return x

        def product2(x, y):
            return x * y

        prod_data1 = gdu.sample_function(product1, [101], [0], [3])
        prod_data2 = gdu.sample_function(product2, [101, 101], [0, 0], [3, 3])

        with self.assertRaises(ValueError):
            gd.HierarchicalGridData([prod_data1, prod_data2])

        # Only one component
        one = gd.HierarchicalGridData([prod_data1])
        # Test content
        self.assertDictEqual(
            one.grid_data_dict, {-1: [prod_data1.ghost_zones_removed()]}
        )

        grid = gd.UniformGrid([101], x0=[0], x1=[3], ref_level=2)

        # Two components at two different levels
        prod_data1_level2 = gdu.sample_function_from_uniformgrid(
            product1, grid
        )
        two = gd.HierarchicalGridData([prod_data1, prod_data1_level2])
        self.assertDictEqual(
            two.grid_data_dict,
            {
                -1: [prod_data1.ghost_zones_removed()],
                2: [prod_data1_level2.ghost_zones_removed()],
            },
        )

        # Test a good grid
        hg_many_components = gd.HierarchicalGridData(self.grid_data)
        self.assertEqual(
            hg_many_components.grid_data_dict[0], [self.expected_data]
        )

        # Test a grid with two separate components
        hg3 = gd.HierarchicalGridData(self.grid_data_two_comp)
        self.assertEqual(hg3.grid_data_dict[0], self.grid_data_two_comp)
예제 #8
0
    def test_sample_function(self):

        # Test not grid as input
        with self.assertRaises(TypeError):
            gdu.sample_function_from_uniformgrid(np.sin, 0)

        # Test 1d
        geom = gd.UniformGrid(100, x0=0, x1=2 * np.pi)
        data = np.sin(np.linspace(0, 2 * np.pi, 100))

        self.assertEqual(
            gdu.sample_function(np.sin, 100, 0, 2 * np.pi),
            gd.UniformGridData(geom, data),
        )

        # Test with additional arguments
        geom_ref_level = gd.UniformGrid(100, x0=0, x1=2 * np.pi, ref_level=0)
        self.assertEqual(
            gdu.sample_function(np.sin, 100, 0, 2 * np.pi, ref_level=0),
            gd.UniformGridData(geom_ref_level, data),
        )

        # Test 2d
        geom2d = gd.UniformGrid([100, 200], x0=[0, 1], x1=[1, 2])

        def square(x, y):
            return x * y

        # Test function takes too few arguments
        with self.assertRaises(TypeError):
            gdu.sample_function_from_uniformgrid(lambda x: x, geom2d)

        # Test function takes too many arguments
        with self.assertRaises(TypeError):
            gdu.sample_function_from_uniformgrid(square, geom)

        # Test other TypeError
        with self.assertRaises(TypeError):
            gdu.sample_function_from_uniformgrid(np.sin, geom2d)

        data2d = np.vectorize(square)(*geom2d.coordinates(as_same_shape=True))

        self.assertEqual(
            gdu.sample_function(square, [100, 200], [0, 1], [1, 2]),
            gd.UniformGridData(geom2d, data2d),
        )

        self.assertEqual(
            gdu.sample_function_from_uniformgrid(square, geom2d),
            gd.UniformGridData(geom2d, data2d),
        )
예제 #9
0
    def test__apply_binary(self):

        hg1 = gd.HierarchicalGridData(self.grid_data)

        # Test incompatible types
        with self.assertRaises(TypeError):
            hg1 + "hey"

        def neg_product(x, y):
            return -x * (y + 2)

        neg_data = gdu.sample_function_from_uniformgrid(
            neg_product, self.expected_grid
        )

        hg2 = gd.HierarchicalGridData([neg_data])

        zero = hg1 + hg2
        zero += 0

        # To check that zero is indeed zero we check that the abs max of the
        # data is 0
        self.assertEqual(np.amax(np.abs(zero[0][0].data)), 0)

        # Test incompatible refinement levels

        neg_data_level2 = gdu.sample_function_from_uniformgrid(
            neg_product, self.expected_grid_level2
        )

        with self.assertRaises(ValueError):
            hg1 + gd.HierarchicalGridData([neg_data_level2])

        # Test with multiple components
        hg3 = gd.HierarchicalGridData(self.grid_data_two_comp)

        hg4 = hg3.copy()
        hg4[0][0] *= -1
        hg4[0][1] *= -1

        zero2 = hg3 + hg4
        self.assertEqual(np.amax(np.abs(zero2[0][0].data)), 0)
        self.assertEqual(np.amax(np.abs(zero2[0][1].data)), 0)
예제 #10
0
    def test_finest_coarsest_level(self):
        geom = gd.UniformGrid(
            [81, 3], x0=[0, 0], x1=[2 * np.pi, 1], ref_level=0
        )
        geom2 = gd.UniformGrid(
            [11, 3], x0=[0, 0], x1=[2 * np.pi, 1], ref_level=1
        )

        sin_wave1 = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom
        )
        sin_wave2 = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom2
        )

        sin_wave = gd.HierarchicalGridData([sin_wave1] + [sin_wave2])

        self.assertEqual(sin_wave.finest_level, sin_wave2)
        self.assertEqual(sin_wave.coarsest_level, sin_wave1)
예제 #11
0
    def setUp(self):
        # Here we split the rectangle with x0 = [0, 1], x1 = [14, 26]
        # and shape [14, 26] in 4 pieces
        grid1 = gd.UniformGrid([4, 5], x0=[0, 1], x1=[3, 5], ref_level=0)
        grid2 = gd.UniformGrid([11, 21], x0=[4, 6], x1=[14, 26], ref_level=0)
        grid3 = gd.UniformGrid([11, 5], x0=[4, 1], x1=[14, 5], ref_level=0)
        grid4 = gd.UniformGrid([4, 21], x0=[0, 6], x1=[3, 26], ref_level=0)

        self.grids0 = [grid1, grid2, grid3, grid4]
        # self.grids1 are not to be merged because they do not fill the space
        self.grids1 = [grid1, grid2]

        def product(x, y):
            return x * (y + 2)

        self.grid_data = [
            gdu.sample_function_from_uniformgrid(product, g)
            for g in self.grids0
        ]

        self.grid_data_two_comp = [
            gdu.sample_function_from_uniformgrid(product, g)
            for g in self.grids1
        ]

        self.expected_grid = gd.UniformGrid(
            [15, 26], x0=[0, 1], x1=[14, 26], ref_level=0
        )

        self.expected_data = gdu.sample_function_from_uniformgrid(
            product, self.expected_grid
        )

        # We also consider one grid data with a different refinement level
        self.expected_grid_level2 = gd.UniformGrid(
            [15, 26], x0=[0, 1], x1=[14, 26], ref_level=2
        )

        self.expected_data_level2 = gdu.sample_function_from_uniformgrid(
            product, self.expected_grid_level2
        )
예제 #12
0
    def test_iter(self):

        hg1 = gd.HierarchicalGridData(self.grid_data)

        for ref_level, comp, data in hg1:
            self.assertTrue(isinstance(data, gd.UniformGridData))
            self.assertEqual(ref_level, 0)
            self.assertEqual(comp, 0)

        hg3 = gd.HierarchicalGridData(self.grid_data_two_comp)

        comp_index = 0
        for ref_level, comp, data in hg3:
            self.assertEqual(ref_level, 0)
            self.assertEqual(comp, comp_index)
            self.assertTrue(isinstance(data, gd.UniformGridData))
            comp_index += 1

        # Test from finest
        geom = gd.UniformGrid(
            [81, 3], x0=[0, 0], x1=[2 * np.pi, 1], ref_level=0
        )
        geom2 = gd.UniformGrid(
            [11, 3], x0=[0, 0], x1=[2 * np.pi, 1], ref_level=1
        )

        sin_wave1 = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom
        )
        sin_wave2 = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom2
        )

        sin_wave = gd.HierarchicalGridData([sin_wave1] + [sin_wave2])

        index = 1
        for ref_level, comp, data in sin_wave.iter_from_finest():
            self.assertEqual(ref_level, index)
            self.assertEqual(comp, 0)
            self.assertTrue(isinstance(data, gd.UniformGridData))
            index -= 1
예제 #13
0
    def test_slice(self):

        grid_data = gdu.sample_function_from_uniformgrid(
            lambda x, y, z: x * (y + 2) * (z + 5),
            gd.UniformGrid([10, 20, 30], x0=[0, 1, 2], dx=[1, 2, 0.1]),
        )
        grid_data_copied = grid_data.copy()

        # Test cut is wrong dimension
        with self.assertRaises(ValueError):
            grid_data.slice([1, 2])

        # Test no cut
        grid_data.slice([None, None, None])
        self.assertEqual(grid_data, grid_data_copied)

        # Test cut point outside the grid
        with self.assertRaises(ValueError):
            grid_data.slice([1, 2, 1000])

        # Test resample

        # Test cut along one dimension
        grid_data.slice([None, None, 3], resample=True)
        expected_no_z = gdu.sample_function_from_uniformgrid(
            lambda x, y: x * (y + 2) * (3 + 5),
            gd.UniformGrid([10, 20], x0=[0, 1], dx=[1, 2]),
        )
        self.assertEqual(grid_data, expected_no_z)

        # Test no resample
        #
        # Reset grid data
        grid_data = grid_data_copied.copy()
        grid_data.slice([None, None, 3], resample=False)
        self.assertEqual(grid_data, expected_no_z)
예제 #14
0
    def test_properties(self):
        def square(x, y):
            return x * (y + 2)

        grid_data = gdu.sample_function_from_uniformgrid(square, self.geom)

        self.assertCountEqual(grid_data.x0, self.geom.x0)
        self.assertCountEqual(grid_data.origin, self.geom.x0)
        self.assertCountEqual(grid_data.shape, self.geom.shape)
        self.assertCountEqual(grid_data.x1, self.geom.x1)
        self.assertCountEqual(grid_data.dx, self.geom.dx)
        self.assertCountEqual(grid_data.delta, self.geom.dx)
        self.assertCountEqual(grid_data.num_ghost, self.geom.num_ghost)
        self.assertEqual(grid_data.ref_level, self.geom.ref_level)
        self.assertEqual(grid_data.component, self.geom.component)
        self.assertEqual(grid_data.time, self.geom.time)
        self.assertEqual(grid_data.iteration, self.geom.iteration)
        self.assertTrue(np.allclose(grid_data.data_xyz, grid_data.data.T))
예제 #15
0
    def test__apply_unary(self):

        hg1 = gd.HierarchicalGridData(self.grid_data)

        def neg_product(x, y):
            return -x * (y + 2)

        neg_data = gdu.sample_function_from_uniformgrid(
            neg_product, self.expected_grid
        )

        hg2 = gd.HierarchicalGridData([neg_data])

        self.assertEqual(-hg1, hg2)

        # Test with multiple components
        hg3 = gd.HierarchicalGridData(self.grid_data_two_comp)

        hg4 = hg3.copy()
        hg4[0][0] *= -1
        hg4[0][1] *= -1

        self.assertEqual(-hg3, hg4)
예제 #16
0
    def test_partial_derive(self):

        geom = gd.UniformGrid([8001, 3], x0=[0, 0], x1=[2 * np.pi, 1])

        sin_wave = gdu.sample_function_from_uniformgrid(
            lambda x, y: np.sin(x), geom
        )
        original_sin = sin_wave.copy()

        # Error dimension not found
        with self.assertRaises(ValueError):
            sin_wave.partial_derived(5)

        # Second derivative should still be a -sin
        sin_wave.partial_derive(0, order=2)

        self.assertTrue(
            np.allclose(-sin_wave.data, original_sin.data, atol=1e-3)
        )

        gradient = original_sin.gradient(order=2)
        self.assertTrue(
            np.allclose(-gradient[0].data, original_sin.data, atol=1e-3)
        )
예제 #17
0
    def test_resampled(self):
        def product(x, y):
            return x * (y + 2)

        def product_complex(x, y):
            return (1 + 1j) * x * (y + 2)

        prod_data = gdu.sample_function(product, [101, 201], [0, 1], [3, 4])
        prod_data_complex = gdu.sample_function(
            product_complex, [3001, 2801], [0, 1], [3, 4]
        )
        # Check error
        with self.assertRaises(TypeError):
            prod_data.resampled(2)

        # Check same grid
        self.assertEqual(prod_data.resampled(prod_data.grid), prod_data)

        new_grid = gd.UniformGrid([51, 101], x0=[1, 2], x1=[2, 3])

        resampled = prod_data_complex.resampled(new_grid)
        exp_resampled = gdu.sample_function_from_uniformgrid(
            product_complex, new_grid
        )

        self.assertEqual(resampled.grid, new_grid)
        self.assertTrue(np.allclose(resampled.data, exp_resampled.data))

        # Check that the method of the spline is linear
        self.assertEqual(prod_data_complex.spline_imag.method, "linear")

        # Test using nearest interpolation
        resampled_nearest = prod_data_complex.resampled(
            new_grid, piecewise_constant=True
        )

        self.assertTrue(
            np.allclose(resampled_nearest.data, exp_resampled.data, atol=1e-3)
        )

        # Check that the method of the spline hasn't linear
        self.assertEqual(prod_data_complex.spline_imag.method, "linear")

        # Check single number
        self.assertAlmostEqual(resampled_nearest((2, 2.5)), 9 * (1 + 1j))

        # Check with one point
        new_grid2 = gd.UniformGrid([11, 1], x0=[1, 2], dx=[0.1, 1])
        resampled2 = prod_data_complex.resampled(new_grid2)
        prod_data_one_point = gdu.sample_function_from_uniformgrid(
            product_complex, new_grid2
        )

        self.assertEqual(resampled2, prod_data_one_point)

        # Resample from 3d to 2d

        grid_data3d = gdu.sample_function_from_uniformgrid(
            lambda x, y, z: x * (y + 2) * (z + 5),
            gd.UniformGrid([10, 20, 11], x0=[0, 1, 0], dx=[1, 2, 0.1]),
        )
        grid_2d = gd.UniformGrid([10, 20, 1], [0, 1, 0], dx=[1, 2, 0.1])

        expected_data2d = gdu.sample_function_from_uniformgrid(
            lambda x, y, z: x * (y + 2) * (z + 5), grid_2d
        )

        self.assertEqual(grid_data3d.resampled(grid_2d), expected_data2d)
예제 #18
0
    def test_splines(self):

        # Let's start with 1d.
        sin_data = gdu.sample_function(np.sin, 12000, 0, 2 * np.pi)
        sin_data_complex = sin_data + 1j * sin_data

        # Test unknown ext
        with self.assertRaises(ValueError):
            sin_data.evaluate_with_spline(1, ext=3)

        # Test k!=0!=1
        with self.assertRaises(ValueError):
            sin_data._make_spline(k=3)

        self.assertAlmostEqual(
            sin_data_complex.evaluate_with_spline([np.pi / 3]),
            (1 + 1j) * np.sin(np.pi / 3),
        )

        # Test with point in cell but outside boundary, in

        # We change the boundary values to be different from 0
        sin_data_complex_plus_one = sin_data_complex + 1 + 1j

        dx = sin_data_complex.dx[0]
        # At the boundary, we do a constant extrapolation, so the value should
        # be the boundary value
        self.assertAlmostEqual(
            sin_data_complex_plus_one.evaluate_with_spline([0 - 0.25 * dx]),
            (1 + 1j),
        )
        self.assertAlmostEqual(
            sin_data_complex_plus_one.evaluate_with_spline(
                [2 * np.pi + 0.25 * dx]
            ),
            (1 + 1j),
        )

        # Test __call__
        self.assertAlmostEqual(
            sin_data_complex([np.pi / 3]),
            (1 + 1j) * np.sin(np.pi / 3),
        )

        # Test on a point of the grid
        point = [sin_data.grid.coordinates_1d[0][2]]
        self.assertAlmostEqual(
            sin_data_complex(point),
            (1 + 1j) * np.sin(point[0]),
        )

        # Test on a point outside the grid with the lookup table
        with self.assertRaises(ValueError):
            sin_data_complex._nearest_neighbor_interpolation(
                np.array([1000]),
                ext=2,
            ),

        # Test on a point outside the grid with the lookup table and
        # ext = 1
        self.assertEqual(
            sin_data_complex.evaluate_with_spline(
                [1000], ext=1, piecewise_constant=True
            ),
            0,
        )

        # Vector input
        self.assertTrue(
            np.allclose(
                sin_data_complex.evaluate_with_spline(
                    [[np.pi / 3], [np.pi / 4]]
                ),
                np.array(
                    [
                        (1 + 1j) * np.sin(np.pi / 3),
                        (1 + 1j) * np.sin(np.pi / 4),
                    ]
                ),
            )
        )

        # Vector input in, vector input out
        self.assertEqual(sin_data_complex([[1]]).shape, (1,))

        # Now 2d
        def product(x, y):
            return x * (y + 2)

        prod_data = gdu.sample_function(product, [101, 101], [0, 0], [3, 3])
        prod_data_complex = (1 + 1j) * prod_data

        self.assertAlmostEqual(
            prod_data_complex.evaluate_with_spline((2, 3)),
            (1 + 1j) * 10,
        )

        # Vector input
        self.assertTrue(
            np.allclose(
                prod_data_complex.evaluate_with_spline([(1, 0), (2, 3)]),
                np.array([(1 + 1j) * 2, (1 + 1j) * 10]),
            )
        )

        self.assertTrue(
            np.allclose(
                prod_data_complex.evaluate_with_spline(
                    [[(1, 0), (2, 3)], [(3, 1), (0, 0)]]
                ),
                np.array([[(1 + 1j) * 2, (1 + 1j) * 10], [(1 + 1j) * 9, 0]]),
            )
        )

        # Real data
        self.assertAlmostEqual(
            prod_data.evaluate_with_spline((2, 3)),
            10,
        )

        # Extrapolate outside
        self.assertAlmostEqual(
            prod_data.evaluate_with_spline((20, 20), ext=1), 0
        )

        self.assertAlmostEqual(
            prod_data_complex.evaluate_with_spline((20, 20), ext=1), 0
        )

        self.assertTrue(prod_data_complex.spline_real.bounds_error)
        self.assertTrue(prod_data_complex.spline_imag.bounds_error)

        # Test on a UniformGrid
        sin_data = gdu.sample_function(np.sin, 12000, 0, 2 * np.pi)
        linspace = gd.UniformGrid(101, x0=0, x1=3)
        output = sin_data(linspace)
        self.assertTrue(
            np.allclose(output.data, np.sin(linspace.coordinates()))
        )

        # Incompatible dimensions
        with self.assertRaises(ValueError):
            sin_data(gd.UniformGrid([101, 201], x0=[0, 1], x1=[3, 4]))

        # Test with grid that has a flat dimension
        prod_data_flat = gdu.sample_function_from_uniformgrid(
            product, gd.UniformGrid([101, 1], x0=[0, 0], dx=[1, 3])
        )

        # y = 1 is in the flat cell, where y = 0, and here we are using nearest
        # interpolation
        self.assertAlmostEqual(prod_data_flat((1, 1)), 2)
        # Vector
        self.assertCountEqual(prod_data_flat([(1, 1), (2, 1)]), [2, 4])