示例#1
0
    def testInnerFirstAndSecondOrderCoeff(self):
        """Tests handling both inner_first_order_coeff and inner_second_order_coeff.

    We saw previously that the solution of
    `u_{t} - u_{xx} - u_{yy} - 2u_{x} - 4u_{y} - 5u = 0` is
    `u = exp(-x-2y) v`, where `v` solves the diffusion equation. Substitute now
    `u = exp(-x-2y) v` without expanding the derivatives:
    `v_{t} - exp(x)[exp(-x)v]_{xx} - exp(2y)[exp(-2y)v]_{yy} -
      2exp(x)[exp(-x)v]_{x} - 4exp(2y)[exp(-2y)v]_{y} - 5v = 0`.
    Solve this equation and expect the solution of the diffusion equation.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 251],
                                  dtype=tf.float32)
        ys, xs = grid

        final_t = 0.1
        time_step = 0.002

        def second_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [[-tf.exp(2 * y), None], [None, -tf.exp(x)]]

        def inner_second_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [[tf.exp(-2 * y), None], [None, tf.exp(-x)]]

        def first_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [-4 * tf.exp(2 * y), -2 * tf.exp(x)]

        def inner_first_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [tf.exp(-2 * y), tf.exp(-x)]

        def zeroth_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return -5

        initial = _reference_2d_pde_initial_cond(xs, ys)
        expected = _reference_2d_pde_solution(xs, ys, final_t)

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            first_order_coeff_fn=first_order_coeff_fn,
            zeroth_order_coeff_fn=zeroth_order_coeff_fn,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn,
            inner_first_order_coeff_fn=inner_first_order_coeff_fn)[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#2
0
    def testInnerFirstAndSecondOrderCoeff(self):
        """Tests handling both inner_first_order_coeff and inner_second_order_coeff.

    We saw previously that the solution of `u_{t} - u_{xx} - 2u_{x} - u = 0` is
    `u = exp(-x) v`, where v solves the diffusion equation. Substitute now
    `u = exp(-x) v` without expanding the derivatives:
    `v_{t} - exp(x)[exp(-x)v]_{xx} - 2exp(x)[exp(-x)v]_{x} - v = 0`.
    Solve this equation and expect the solution of the diffusion equation.
    """
        grid = grids.uniform_grid(minimums=[0],
                                  maximums=[1],
                                  sizes=[501],
                                  dtype=tf.float32)
        xs = grid[0]

        final_t = 0.1
        time_step = 0.001

        def second_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [[-tf.exp(x)]]

        def inner_second_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [[tf.exp(-x)]]

        def first_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [-2 * tf.exp(x)]

        def inner_first_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [tf.exp(-x)]

        def zeroth_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return -1

        initial = _reference_pde_initial_cond(xs)
        expected = _reference_pde_solution(xs, final_t)

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            first_order_coeff_fn=first_order_coeff_fn,
            zeroth_order_coeff_fn=zeroth_order_coeff_fn,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn,
            inner_first_order_coeff_fn=inner_first_order_coeff_fn)[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#3
0
    def testHeatEquation_InForwardDirection(self):
        """Test solving heat equation with various time marching schemes.

    Tests solving heat equation with the boundary conditions
    `u(x, t=1) = e * sin(x)`, `u(-2 pi n - pi / 2, t) = -e^t`, and
    `u(2 pi n + pi / 2, t) = -e^t` with some integer `n` for `u(x, t=0)`.

    The exact solution is `u(x, t=0) = sin(x)`.

    All time marching schemes should yield reasonable results given small enough
    time steps. First-order accurate schemes (explicit, implicit, weighted with
    theta != 0.5) require smaller time step than second-order accurate ones
    (Crank-Nicolson, Extrapolation).
    """
        final_time = 1.0

        def initial_cond_fn(x):
            return tf.sin(x)

        def expected_result_fn(x):
            return np.exp(-final_time) * tf.sin(x)

        @dirichlet
        def lower_boundary_fn(t, x):
            del x
            return -tf.exp(-t)

        @dirichlet
        def upper_boundary_fn(t, x):
            del x
            return tf.exp(-t)

        grid = grids.uniform_grid(minimums=[-10.5 * math.pi],
                                  maximums=[10.5 * math.pi],
                                  sizes=[1000],
                                  dtype=np.float32)

        def second_order_coeff_fn(t, x):
            del t, x
            return [[-1]]

        final_values = initial_cond_fn(grid[0])

        result = fd_solvers.solve_forward(
            start_time=0.0,
            end_time=final_time,
            coord_grid=grid,
            values_grid=final_values,
            time_step=0.01,
            boundary_conditions=[(lower_boundary_fn, upper_boundary_fn)],
            second_order_coeff_fn=second_order_coeff_fn)[0]

        actual = self.evaluate(result)
        expected = self.evaluate(expected_result_fn(grid[0]))
        self.assertLess(np.max(np.abs(actual - expected)), 1e-3)
示例#4
0
    def testAnisotropicDiffusion_InForwardDirection(self):
        """Tests solving 2d diffusion equation in forward direction.

    The equation is `u_{t} - Dx u_{xx} - Dy u_{yy} = 0`.
    The initial condition is a gaussian centered at (0, 0) with variance sigma.
    The variance along each dimension should evolve as `sigma + 2 Dx (t - t_0)`
    and `sigma + 2 Dy (t - t_0)`.
    """
        grid = grids.uniform_grid(minimums=[-10, -20],
                                  maximums=[10, 20],
                                  sizes=[201, 301],
                                  dtype=tf.float32)
        ys = self.evaluate(grid[0])
        xs = self.evaluate(grid[1])

        diff_coeff_x = 0.4  # Dx
        diff_coeff_y = 0.25  # Dy
        time_step = 0.1
        final_t = 1.0
        initial_variance = 1

        def quadratic_coeff_fn(t, location_grid):
            del t, location_grid
            u_xx = -diff_coeff_x
            u_yy = -diff_coeff_y
            u_xy = None
            return [[u_yy, u_xy], [u_xy, u_xx]]

        final_values = tf.expand_dims(tf.constant(np.outer(
            _gaussian(ys, initial_variance), _gaussian(xs, initial_variance)),
                                                  dtype=tf.float32),
                                      axis=0)
        bound_cond = [(_zero_boundary, _zero_boundary),
                      (_zero_boundary, _zero_boundary)]
        step_fn = douglas_adi_step(theta=0.5)
        result = fd_solvers.solve_forward(
            start_time=0.0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=final_values,
            time_step=time_step,
            one_step_fn=step_fn,
            boundary_conditions=bound_cond,
            second_order_coeff_fn=quadratic_coeff_fn,
            dtype=grid[0].dtype)

        variance_x = initial_variance + 2 * diff_coeff_x * final_t
        variance_y = initial_variance + 2 * diff_coeff_y * final_t
        expected = np.outer(_gaussian(ys, variance_y),
                            _gaussian(xs, variance_x))

        self._assertClose(expected, result)
示例#5
0
    def testReferenceEquation_WithTransformationYieldingMixedTerm(self):
        """Tests an equation with mixed terms against exact solution.

    Take the reference equation `v_{t} = v_{xx} + v_{yy}` and substitute
    `v(x, y, t) = u(x, 2y - x, t)`. This yields
    `u_{t} = u_{xx} + 5u_{zz} - 2u_{xz}`, where `z = 2y - x`.
    Having `u(x, z, t) = v(x, (x+z)/2, t)` where `v(x, y, t)` is the known
    solution of the reference equation, we derive the boundary conditions
    and the expected solution for `u(x, y, t)`.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 251],
                                  dtype=tf.float32)

        final_t = 0.1
        time_step = 0.002

        def second_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return [[-5, 1], [None, -1]]

        @dirichlet
        def boundary_lower_z(t, coord_grid):
            x = coord_grid[1]
            return _reference_pde_solution(x, t) * _reference_pde_solution(
                x / 2, t)

        @dirichlet
        def boundary_upper_z(t, coord_grid):
            x = coord_grid[1]
            return _reference_pde_solution(x, t) * _reference_pde_solution(
                (x + 1) / 2, t)

        z_mesh, x_mesh = tf.meshgrid(grid[0], grid[1], indexing='ij')
        initial = (_reference_pde_initial_cond(x_mesh) *
                   _reference_pde_initial_cond((x_mesh + z_mesh) / 2))
        expected = (_reference_pde_solution(x_mesh, final_t) *
                    _reference_pde_solution((x_mesh + z_mesh) / 2, final_t))

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            boundary_conditions=[(boundary_lower_z, boundary_upper_z),
                                 (_zero_boundary, _zero_boundary)])[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#6
0
    def testReference_WithExponentMultiplier(self):
        """Tests solving diffusion equation with an exponent multiplier.

    Take the heat equation `v_{t} - v_{xx} - v_{yy} = 0` and substitute
    `v = exp(x + 2y) u`.
    This yields `u_{t} - u_{xx} - u_{yy} - 2u_{x} - 4u_{y} - 5u = 0`. The test
    compares numerical solution of this equation to the exact one, which is the
    diffusion equation solution times `exp(-x-2y)`.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 301],
                                  dtype=tf.float32)
        ys, xs = grid

        final_t = 0.1
        time_step = 0.002

        def second_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return [[-1, None], [None, -1]]

        def first_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return [-4, -2]

        def zeroth_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return -5

        exp = _dir_prod(tf.exp(-2 * ys), tf.exp(-xs))
        initial = exp * _reference_2d_pde_initial_cond(xs, ys)
        expected = exp * _reference_2d_pde_solution(xs, ys, final_t)

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            first_order_coeff_fn=first_order_coeff_fn,
            zeroth_order_coeff_fn=zeroth_order_coeff_fn)[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#7
0
    def testReferenceEquation(self):
        """Tests the equation used as reference for a few further tests.

    We solve the heat equation `u_t = u_xx + u_yy` on x = [0...1], y = [0...1]
    with boundary conditions `u(x, y, t=0) = (1/2 - |x-1/2|)(1/2-|y-1/2|), and
    zero Dirichlet on all spatial boundaries.

    The exact solution of the diffusion equation with zero-Dirichlet rectangular
    boundaries is `u(x, y, t) = u(x, t) * u(y, t)`,
    `u(z, t) = sum_{n=1..inf} b_n sin(pi n z) exp(-n^2 pi^2 t)`,
    `b_n = 2 integral_{0..1} sin(pi n z) u(z, t=0) dz.`

    The initial conditions are taken so that the integral easily calculates, and
    the sum can be approximated by a few first terms (given large enough `t`).
    See the result in _reference_heat_equation_solution.

    Using this solution helps to simplify the tests, as we don't have to
    maintain complicated boundary conditions in each test or tweak the
    parameters to keep the "support" of the function far from boundaries.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 301],
                                  dtype=tf.float32)
        ys, xs = grid

        final_t = 0.1
        time_step = 0.002

        def second_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return [[-1, None], [None, -1]]

        initial = _reference_2d_pde_initial_cond(xs, ys)
        expected = _reference_2d_pde_solution(xs, ys, final_t)
        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn)[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#8
0
    def testInnerSecondOrderCoeff(self):
        """Tests handling inner_second_order_coeff.

    As in previous test, take the diffusion equation
    `v_{t} - v_{xx} - v_{yy} = 0` and substitute `v = exp(x + 2y) u`, but this
    time keep exponent under the derivative:
    `u_{t} - exp(-x)[exp(x)u]_{xx} - exp(-2y)[exp(2y)u]_{yy} = 0`.
    Expect the same solution as in previous test.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 251],
                                  dtype=tf.float32)
        ys, xs = grid

        final_t = 0.1
        time_step = 0.002

        def second_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [[-tf.exp(-2 * y), None], [None, -tf.exp(-x)]]

        def inner_second_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [[tf.exp(2 * y), None], [None, tf.exp(x)]]

        exp = _dir_prod(tf.exp(-2 * ys), tf.exp(-xs))
        initial = exp * _reference_2d_pde_initial_cond(xs, ys)
        expected = exp * _reference_2d_pde_solution(xs, ys, final_t)

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn)[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#9
0
    def testInnerSecondOrderCoeff(self):
        """Tests handling inner_second_order_coeff.

    As in previous test, take the diffusion equation `v_{t} - v_{xx} = 0` and
    substitute `v = exp(x) u`, but this time keep exponent under the derivative:
    `u_{t} - exp(-x)[exp(x)u]_{xx} = 0`. Expect the same solution as in
    previous test.
    """
        grid = grids.uniform_grid(minimums=[0],
                                  maximums=[1],
                                  sizes=[501],
                                  dtype=tf.float32)
        xs = grid[0]

        final_t = 0.1
        time_step = 0.001

        def second_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [[-tf.exp(-x)]]

        def inner_second_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [[tf.exp(x)]]

        initial = tf.exp(-xs) * _reference_pde_initial_cond(xs)
        expected = tf.exp(-xs) * _reference_pde_solution(xs, final_t)

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn)[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#10
0
    def testCompareExpandedAndNotExpandedPdes(self):
        """Tests comparing PDEs with expanded derivatives and without.

    The equation is
    `u_{t} + [x u]_{x} + [y^2 u]_{y} - [sin(x) u]_{xx} - [cos(y) u]_yy
     + [x^3 y^2 u]_{xy} = 0`.
    Solve the equation, expand the derivatives and solve the equation again.
    Expect the results to be equal.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 251],
                                  dtype=tf.float32)

        final_t = 0.1
        time_step = 0.002
        y, x = grid

        initial = _reference_2d_pde_initial_cond(x, y)  # arbitrary

        def inner_second_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [[-tf.math.cos(y), x**3 * y**2 / 2],
                    [None, -tf.math.sin(x)]]

        def inner_first_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [y**2, x]

        result_not_expanded = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn,
            inner_first_order_coeff_fn=inner_first_order_coeff_fn)[0]

        def second_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [[-tf.math.cos(y), x**3 * y**2 / 2],
                    [None, -tf.math.sin(x)]]

        def first_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return [
                y**2 * (1 + 3 * x**2) + 2 * tf.math.sin(y),
                x * (1 + 2 * x**2 * y) - 2 * tf.math.cos(x)
            ]

        def zeroth_order_coeff_fn(t, coord_grid):
            del t
            y, x = tf.meshgrid(*coord_grid, indexing='ij')
            return 1 + 2 * y + tf.math.sin(x) + tf.math.cos(x) + 6 * x**2 * y

        result_expanded = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            first_order_coeff_fn=first_order_coeff_fn,
            zeroth_order_coeff_fn=zeroth_order_coeff_fn)[0]

        self.assertAllClose(result_not_expanded,
                            result_expanded,
                            atol=1e-3,
                            rtol=1e-3)
示例#11
0
    def testInnerMixedSecondOrderCoeffs(self):
        """Tests handling coefficients under the mixed second derivative.

    Take the equation from the previous test,
    `u_{t} = u_{xx} + 5u_{zz} - 2u_{xz}` and substitute `u = exp(xz) w`,
    leaving the exponent under the derivatives:
    `w_{t} = exp(-xz) [exp(xz) u]_{xx} + 5 exp(-xz) [exp(xz) u]_{zz}
    - 2 exp(-xz) [exp(xz) u]_{xz}`.
    We now have a coefficient under the mixed derivative. Test that the solution
    is `w = exp(-xz) u`, where u is from the previous test.
    """
        grid = grids.uniform_grid(minimums=[0, 0],
                                  maximums=[1, 1],
                                  sizes=[201, 251],
                                  dtype=tf.float32)

        final_t = 0.1
        time_step = 0.002

        def second_order_coeff_fn(t, coord_grid):
            del t,
            z, x = tf.meshgrid(*coord_grid, indexing='ij')
            exp = tf.math.exp(-z * x)
            return [[-5 * exp, exp], [None, -exp]]

        def inner_second_order_coeff_fn(t, coord_grid):
            del t,
            z, x = tf.meshgrid(*coord_grid, indexing='ij')
            exp = tf.math.exp(z * x)
            return [[exp, exp], [None, exp]]

        @dirichlet
        def boundary_lower_z(t, coord_grid):
            x = coord_grid[1]
            return _reference_pde_solution(x, t) * _reference_pde_solution(
                x / 2, t)

        @dirichlet
        def boundary_upper_z(t, coord_grid):
            x = coord_grid[1]
            return tf.exp(-x) * _reference_pde_solution(
                x, t) * _reference_pde_solution((x + 1) / 2, t)

        z, x = tf.meshgrid(*grid, indexing='ij')
        exp = tf.math.exp(-z * x)
        initial = exp * (_reference_pde_initial_cond(x) *
                         _reference_pde_initial_cond((x + z) / 2))
        expected = exp * (_reference_pde_solution(x, final_t) *
                          _reference_pde_solution((x + z) / 2, final_t))

        actual = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn,
            boundary_conditions=[(boundary_lower_z, boundary_upper_z),
                                 (_zero_boundary, _zero_boundary)])[0]

        self.assertAllClose(expected, actual, atol=1e-3, rtol=1e-3)
示例#12
0
    def testCompareExpandedAndNotExpandedPdes(self):
        """Tests comparing PDEs with expanded derivatives and without.

    Take equation `u_{t} - [x^2 u]_{xx} + [x u]_{x} = 0`.
    Expanding the derivatives yields `u_{t} - x^2 u_{xx} - 3x u_{x} - u = 0`.
    Solve both equations and expect the results to be equal.
    """
        grid = grids.uniform_grid(minimums=[0],
                                  maximums=[1],
                                  sizes=[501],
                                  dtype=tf.float32)
        xs = grid[0]

        final_t = 0.1
        time_step = 0.001

        initial = _reference_pde_initial_cond(xs)  # arbitrary

        def inner_second_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [[-tf.square(x)]]

        def inner_first_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [x]

        result_not_expanded = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            inner_second_order_coeff_fn=inner_second_order_coeff_fn,
            inner_first_order_coeff_fn=inner_first_order_coeff_fn)[0]

        def second_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [[-tf.square(x)]]

        def first_order_coeff_fn(t, coord_grid):
            del t
            x = coord_grid[0]
            return [-3 * x]

        def zeroth_order_coeff_fn(t, coord_grid):
            del t, coord_grid
            return -1

        result_expanded = fd_solvers.solve_forward(
            start_time=0,
            end_time=final_t,
            coord_grid=grid,
            values_grid=initial,
            time_step=time_step,
            second_order_coeff_fn=second_order_coeff_fn,
            first_order_coeff_fn=first_order_coeff_fn,
            zeroth_order_coeff_fn=zeroth_order_coeff_fn)[0]

        self.assertAllClose(result_not_expanded,
                            result_expanded,
                            atol=1e-3,
                            rtol=1e-3)