Example #1
0
 def __init__(self, N=0, quad="GC", bc=(0, 0),
              domain=(-1., 1.), scaled=False):
     ChebyshevBase.__init__(self, N, quad, domain=domain)
     from shenfun.tensorproductspace import BoundaryValues
     self.CT = Basis(N, quad)
     self._scaled = scaled
     self._factor = np.ones(1)
     self.plan(N, 0, np.float, {})
     self.bc = BoundaryValues(self, bc=bc)
Example #2
0
 def __init__(self,
              N,
              quad="LG",
              bc=(0, 0, 0, 0),
              domain=(-1., 1.),
              padding_factor=1,
              dealias_direct=False):
     from shenfun.tensorproductspace import BoundaryValues
     LegendreBase.__init__(self,
                           N,
                           quad=quad,
                           domain=domain,
                           padding_factor=padding_factor,
                           dealias_direct=dealias_direct)
     self.LT = Basis(N, quad)
     self._factor1 = np.zeros(0)
     self._factor2 = np.zeros(0)
     self.plan(int(N * padding_factor), 0, np.float, {})
     self.bc = BoundaryValues(self, bc=bc)
Example #3
0
 def __init__(self,
              N,
              quad="LG",
              bc=(0., 0.),
              domain=(-1., 1.),
              scaled=False,
              padding_factor=1,
              dealias_direct=False):
     LegendreBase.__init__(self,
                           N,
                           quad=quad,
                           domain=domain,
                           padding_factor=padding_factor,
                           dealias_direct=dealias_direct)
     from shenfun.tensorproductspace import BoundaryValues
     self.LT = Basis(N, quad)
     self._scaled = scaled
     self._factor = np.ones(1)
     self.plan(int(N * padding_factor), 0, np.float, {})
     self.bc = BoundaryValues(self, bc=bc)
Example #4
0
 def __init__(self,
              N=0,
              quad="LG",
              bc=(0., 0.),
              plan=False,
              domain=(-1., 1.),
              scaled=False):
     LegendreBase.__init__(self, N, quad, domain=domain)
     from shenfun.tensorproductspace import BoundaryValues
     self.LT = Basis(N, quad)
     self._scaled = scaled
     self._factor = np.ones(1)
     if plan:
         self.plan(N, 0, np.float, {})
     self.bc = BoundaryValues(self, bc=bc)
Example #5
0
 def __init__(self,
              N,
              quad='JG',
              bc=(0, 0),
              domain=(-1., 1.),
              padding_factor=1,
              dealias_direct=False):
     JacobiBase.__init__(self,
                         N,
                         quad=quad,
                         alpha=-1,
                         beta=-1,
                         domain=domain,
                         padding_factor=padding_factor,
                         dealias_direct=dealias_direct)
     assert bc in ((0, 0), 'Dirichlet')
     from shenfun.tensorproductspace import BoundaryValues
     self.bc = BoundaryValues(self, bc=bc)
     self.plan(int(N * padding_factor), 0, np.float, {})
Example #6
0
 def __init__(self,
              N,
              quad='JG',
              domain=(-1., 1.),
              dtype=np.float,
              padding_factor=1,
              dealias_direct=False,
              coordinates=None):
     JacobiBase.__init__(self,
                         N,
                         quad=quad,
                         alpha=-3,
                         beta=-3,
                         domain=domain,
                         dtype=dtype,
                         padding_factor=padding_factor,
                         dealias_direct=dealias_direct,
                         coordinates=coordinates)
     from shenfun.tensorproductspace import BoundaryValues
     self.bc = BoundaryValues(self, bc=(0, ) * 6)
Example #7
0
 def __init__(self,
              N,
              quad='JG',
              bc=(0, 0),
              domain=(-1., 1.),
              dtype=np.float,
              padding_factor=1,
              dealias_direct=False,
              coordinates=None):
     JacobiBase.__init__(self,
                         N,
                         quad=quad,
                         alpha=-1,
                         beta=-1,
                         domain=domain,
                         dtype=dtype,
                         padding_factor=padding_factor,
                         dealias_direct=dealias_direct,
                         coordinates=coordinates)
     assert bc in ((0, 0), 'Dirichlet')
     from shenfun.tensorproductspace import BoundaryValues
     self._bc_basis = None
     self.bc = BoundaryValues(self, bc=bc)
Example #8
0
class ShenBiharmonicBasis(LegendreBase):
    """Shen biharmonic basis

    Homogeneous Dirichlet and Neumann boundary conditions.

    Parameters
    ----------
        N : int
            Number of quadrature points
        quad : str, optional
            Type of quadrature

            - LG - Legendre-Gauss
            - GL - Legendre-Gauss-Lobatto
        4-tuple of numbers, optional
            The values of the 4 boundary conditions at x=(-1, 1).
            The two Dirichlet first and then the Neumann.
        domain : 2-tuple of floats, optional
            The computational domain
        padding_factor : float, optional
            Factor for padding backward transforms.
        dealias_direct : bool, optional
            Set upper 1/3 of coefficients to zero before backward transform
    """
    def __init__(self,
                 N,
                 quad="LG",
                 bc=(0, 0, 0, 0),
                 domain=(-1., 1.),
                 padding_factor=1,
                 dealias_direct=False):
        from shenfun.tensorproductspace import BoundaryValues
        LegendreBase.__init__(self,
                              N,
                              quad=quad,
                              domain=domain,
                              padding_factor=padding_factor,
                              dealias_direct=dealias_direct)
        self.LT = Basis(N, quad)
        self._factor1 = np.zeros(0)
        self._factor2 = np.zeros(0)
        self.plan(int(N * padding_factor), 0, np.float, {})
        self.bc = BoundaryValues(self, bc=bc)

    @staticmethod
    def boundary_condition():
        return 'Biharmonic'

    @property
    def has_nonhomogeneous_bcs(self):
        return self.bc.has_nonhomogeneous_bcs()

    def _composite_basis(self, V, argument=0):
        P = np.zeros_like(V)
        k = np.arange(V.shape[1]).astype(np.float)[:-4]
        P[:, :-4] = V[:, :-4] - (2 * (2 * k + 5) /
                                 (2 * k + 7)) * V[:, 2:-2] + (
                                     (2 * k + 3) / (2 * k + 7)) * V[:, 4:]
        if argument == 1:
            P[:, -4:] = np.tensordot(V[:, :4],
                                     BCBiharmonicBasis.coefficient_matrix(),
                                     (1, 1))
        return P

    def set_factor_arrays(self, v):
        s = self.sl[self.slice()]
        if not self._factor1.shape == v[s].shape:
            k = self.wavenumbers().astype(np.float)
            self._factor1 = (-2 * (2 * k + 5) / (2 * k + 7)).astype(float)
            self._factor2 = ((2 * k + 3) / (2 * k + 7)).astype(float)

    def scalar_product(self,
                       input_array=None,
                       output_array=None,
                       fast_transform=False):
        output = LegendreBase.scalar_product(self, input_array, output_array,
                                             False)
        output[self.sl[slice(-4, None)]] = 0
        return output

    #@optimizer
    def set_w_hat(self, w_hat, fk, f1, f2):  # pragma: no cover
        s = self.sl[self.slice()]
        s2 = self.sl[slice(2, -2)]
        s4 = self.sl[slice(4, None)]
        w_hat[s] = fk[s]
        w_hat[s2] += f1 * fk[s]
        w_hat[s4] += f2 * fk[s]
        return w_hat

    def sympy_basis(self, i=0):
        x = sympy.symbols('x')
        if i < self.N - 4:
            f = (sympy.legendre(i, x) - 2 * (2 * i + 5.) /
                 (2 * i + 7.) * sympy.legendre(i + 2, x) +
                 ((2 * i + 3.) / (2 * i + 7.)) * sympy.legendre(i + 4, x))
        else:
            f = 0
            for j, c in enumerate(
                    BCBiharmonicBasis.coefficient_matrix()[i - (self.N - 4)]):
                f += c * sympy.legendre(j, x)
        return f

    def evaluate_basis(self, x, i=0, output_array=None):
        x = np.atleast_1d(x)
        if output_array is None:
            output_array = np.zeros(x.shape)
        if i < self.N - 4:
            output_array[:] = eval_legendre(
                i, x) - 2 * (2 * i + 5.) / (2 * i + 7.) * eval_legendre(
                    i + 2, x) + ((2 * i + 3.) /
                                 (2 * i + 7.)) * eval_legendre(i + 4, x)
        else:
            output_array[:] = sympy.lambdify(sympy.symbols('x'),
                                             self.sympy_basis(i))(x)
        return output_array

    def evaluate_basis_derivative(self, x=None, i=0, k=0, output_array=None):
        if x is None:
            x = self.mesh(False, False)
        if output_array is None:
            output_array = np.zeros(x.shape)
        x = np.atleast_1d(x)
        if i < self.N - 4:
            basis = np.zeros(self.shape(True))
            basis[np.array([i, i + 2,
                            i + 4])] = (1, -2 * (2 * i + 5.) / (2 * i + 7.),
                                        ((2 * i + 3.) / (2 * i + 7.)))
            basis = leg.Legendre(basis)
            if k > 0:
                basis = basis.deriv(k)
            output_array[:] = basis(x)
        else:
            output_array[:] = sympy.lambdify(
                sympy.symbols('x'),
                self.sympy_basis(i).diff(sympy.symbols('x'), k))(x)
        return output_array

    def to_ortho(self, input_array, output_array=None):
        if output_array is None:
            output_array = np.zeros_like(input_array.v)
        self.set_factor_arrays(input_array)
        output_array = self.set_w_hat(output_array, input_array, self._factor1,
                                      self._factor2)
        self.bc.add_to_orthogonal(output_array, input_array)
        return output_array

    def slice(self):
        return slice(0, self.N - 4)

    def eval(self, x, u, output_array=None):
        if output_array is None:
            output_array = np.zeros(x.shape)
        x = self.map_reference_domain(x)
        w_hat = work[(u, 0, True)]
        self.set_factor_arrays(u)
        output_array[:] = leg.legval(x, u[:-4])
        w_hat[2:-2] = self._factor1 * u[:-4]
        output_array += leg.legval(x, w_hat[:-2])
        w_hat[4:] = self._factor2 * u[:-4]
        w_hat[:4] = 0
        output_array += leg.legval(x, w_hat)
        return output_array

    def forward(self,
                input_array=None,
                output_array=None,
                fast_transform=False):
        self.scalar_product(input_array, fast_transform=fast_transform)
        u = self.scalar_product.tmp_array
        self.bc.add_mass_rhs(u)
        self.apply_inverse_mass(u)
        self._truncation_forward(u, self.forward.output_array)
        self.bc.set_boundary_dofs(self.forward.output_array, False)
        if output_array is not None:
            output_array[...] = self.forward.output_array
            return output_array
        return self.forward.output_array

    def get_bc_basis(self):
        return BCBiharmonicBasis(self.N, quad=self.quad, domain=self.domain)

    def plan(self, shape, axis, dtype, options):
        if isinstance(axis, tuple):
            assert len(axis) == 1
            axis = axis[0]

        if isinstance(self.forward, Transform):
            if self.forward.input_array.shape == shape and self.axis == axis:
                # Already planned
                return

        self.LT.plan(shape, axis, dtype, options)
        U, V = self.LT.forward.input_array, self.LT.forward.output_array
        self.axis = axis
        if self.padding_factor > 1. + 1e-8:
            trunc_array = self._get_truncarray(shape, V.dtype)
            self.forward = Transform(self.forward, None, U, V, trunc_array)
            self.backward = Transform(self.backward, None, trunc_array, V, U)
            self.backward_uniform = Transform(self.backward_uniform, None,
                                              trunc_array, V, U)
        else:
            self.forward = Transform(self.forward, None, U, V, V)
            self.backward = Transform(self.backward, None, V, V, U)
            self.backward_uniform = Transform(self.backward_uniform, None, V,
                                              V, U)
        self.scalar_product = Transform(self.scalar_product, None, U, V, V)
        self.si = islicedict(axis=self.axis, dimensions=self.dimensions)
        self.sl = slicedict(axis=self.axis, dimensions=self.dimensions)
Example #9
0
class ShenDirichletBasis(LegendreBase):
    """Shen Legendre basis for Dirichlet boundary conditions

    Parameters
    ----------
        N : int
            Number of quadrature points
        quad : str, optional
            Type of quadrature

            - LG - Legendre-Gauss
            - GL - Legendre-Gauss-Lobatto

        bc : tuple of numbers
            Boundary conditions at edges of domain
        domain : 2-tuple of floats, optional
            The computational domain
        scaled : bool, optional
            Whether or not to scale test functions with 1/sqrt(4k+6).
            Scaled test functions give a stiffness matrix equal to the
            identity matrix.
        padding_factor : float, optional
            Factor for padding backward transforms.
        dealias_direct : bool, optional
            Set upper 1/3 of coefficients to zero before backward transform
    """
    def __init__(self,
                 N,
                 quad="LG",
                 bc=(0., 0.),
                 domain=(-1., 1.),
                 scaled=False,
                 padding_factor=1,
                 dealias_direct=False):
        LegendreBase.__init__(self,
                              N,
                              quad=quad,
                              domain=domain,
                              padding_factor=padding_factor,
                              dealias_direct=dealias_direct)
        from shenfun.tensorproductspace import BoundaryValues
        self.LT = Basis(N, quad)
        self._scaled = scaled
        self._factor = np.ones(1)
        self.plan(int(N * padding_factor), 0, np.float, {})
        self.bc = BoundaryValues(self, bc=bc)

    @staticmethod
    def boundary_condition():
        return 'Dirichlet'

    @property
    def has_nonhomogeneous_bcs(self):
        return self.bc.has_nonhomogeneous_bcs()

    def set_factor_array(self, v):
        if self.is_scaled():
            if not self._factor.shape == v.shape:
                k = self.wavenumbers().astype(np.float)
                self._factor = 1. / np.sqrt(4 * k + 6)

    def is_scaled(self):
        return self._scaled

    def _composite_basis(self, V, argument=0):
        P = np.zeros(V.shape)
        if not self.is_scaled():
            P[:, :-2] = V[:, :-2] - V[:, 2:]
        else:
            k = np.arange(self.N - 2).astype(np.float)
            P[:, :-2] = (V[:, :-2] - V[:, 2:]) / np.sqrt(4 * k + 6)
        if argument == 1:
            P[:, -2] = (V[:, 0] - V[:, 1]) / 2
            P[:, -1] = (V[:, 0] + V[:, 1]) / 2
        return P

    def to_ortho(self, input_array, output_array=None):
        if output_array is None:
            output_array = np.zeros_like(input_array.v)
        s0 = self.sl[slice(0, -2)]
        s1 = self.sl[slice(2, None)]

        if self.is_scaled():
            k = self.wavenumbers()
            output_array[s0] = input_array[s0] / np.sqrt(4 * k + 6)
            output_array[s1] -= input_array[s0] / np.sqrt(4 * k + 6)

        else:
            output_array[s0] = input_array[s0]
            output_array[s1] -= input_array[s0]

        self.bc.add_to_orthogonal(output_array, input_array)
        return output_array

    def slice(self):
        return slice(0, self.N - 2)

    def sympy_basis(self, i=0):
        x = sympy.symbols('x')
        f = sympy.legendre(i, x) - sympy.legendre(i + 2, x)
        if self.is_scaled():
            f /= np.sqrt(4 * i + 6)
        return f

    def evaluate_basis(self, x, i=0, output_array=None):
        x = np.atleast_1d(x)
        if output_array is None:
            output_array = np.zeros(x.shape)
        output_array[:] = eval_legendre(i, x) - eval_legendre(i + 2, x)
        if self.is_scaled():
            output_array /= np.sqrt(4 * i + 6)
        return output_array

    def evaluate_basis_derivative(self, x=None, i=0, k=0, output_array=None):
        if x is None:
            x = self.mesh(False, False)
        if output_array is None:
            output_array = np.zeros(x.shape)
        x = np.atleast_1d(x)
        basis = np.zeros(self.shape(True))
        basis[np.array([i, i + 2])] = (1, -1)
        basis = leg.Legendre(basis)
        if k > 0:
            basis = basis.deriv(k)
        output_array[:] = basis(x)
        if self.is_scaled():
            output_array /= np.sqrt(4 * i + 6)
        return output_array

    def vandermonde_scalar_product(self, input_array, output_array):
        SpectralBase.vandermonde_scalar_product(self, input_array,
                                                output_array)
        output_array[self.si[-2]] = 0
        output_array[self.si[-1]] = 0

    def eval(self, x, u, output_array=None):
        x = np.atleast_1d(x)
        if output_array is None:
            output_array = np.zeros(x.shape)
        x = self.map_reference_domain(x)
        w_hat = work[(u, 0, True)]
        self.set_factor_array(u)
        output_array[:] = leg.legval(x, u[:-2] * self._factor)
        w_hat[2:] = u[:-2] * self._factor
        output_array -= leg.legval(x, w_hat)
        output_array += 0.5 * (u[-1] * (1 + x) + u[-2] * (1 - x))
        return output_array

    def forward(self,
                input_array=None,
                output_array=None,
                fast_transform=False):
        self.scalar_product(input_array, fast_transform=fast_transform)
        u = self.scalar_product.tmp_array
        self.bc.add_mass_rhs(u)
        self.apply_inverse_mass(u)
        self.bc.set_boundary_dofs(u, False)
        self._truncation_forward(u, self.forward.output_array)
        if output_array is not None:
            output_array[...] = self.forward.output_array
            return output_array
        return self.forward.output_array

    def plan(self, shape, axis, dtype, options):
        if isinstance(axis, tuple):
            assert len(axis) == 1
            axis = axis[0]

        if isinstance(self.forward, Transform):
            if self.forward.input_array.shape == shape and self.axis == axis:
                # Already planned
                return

        self.LT.plan(shape, axis, dtype, options)
        U, V = self.LT.forward.input_array, self.LT.forward.output_array
        self.axis = axis
        if self.padding_factor > 1. + 1e-8:
            trunc_array = self._get_truncarray(shape, V.dtype)
            self.forward = Transform(self.forward, None, U, V, trunc_array)
            self.backward = Transform(self.backward, None, trunc_array, V, U)
            self.backward_uniform = Transform(self.backward_uniform, None,
                                              trunc_array, V, U)
        else:
            self.forward = Transform(self.forward, None, U, V, V)
            self.backward = Transform(self.backward, None, V, V, U)
            self.backward_uniform = Transform(self.backward_uniform, None, V,
                                              V, U)
        self.scalar_product = Transform(self.scalar_product, None, U, V, V)
        self.si = islicedict(axis=self.axis, dimensions=self.dimensions)
        self.sl = slicedict(axis=self.axis, dimensions=self.dimensions)

    def get_bc_basis(self):
        return BCBasis(self.N,
                       quad=self.quad,
                       domain=self.domain,
                       scaled=self._scaled)

    def get_dealiased(self, padding_factor=1.5, dealias_direct=False):
        return ShenDirichletBasis(self.N,
                                  quad=self.quad,
                                  padding_factor=padding_factor,
                                  dealias_direct=dealias_direct,
                                  domain=self.domain,
                                  bc=self.bc.bc)
Example #10
0
class ShenDirichletBasis(ChebyshevBase):
    """Shen basis for Dirichlet boundary conditions

    Parameters
    ----------
        N : int, optional
            Number of quadrature points
        quad : str, optional
               Type of quadrature

               - GL - Chebyshev-Gauss-Lobatto
               - GC - Chebyshev-Gauss

        bc : 2-tuple of floats, optional
             Boundary conditions at x=(1,-1). For Poisson eq.
        plan : bool, optional
               Plan transforms on __init__ or not. If basis is part of a
               TensorProductSpace, then planning needs to be delayed.
        domain : 2-tuple of floats, optional
                 The computational domain
        scaled : bool, optional
                 Whether or not to use scaled basis
    """

    def __init__(self, N=0, quad="GC", bc=(0, 0), plan=False,
                 domain=(-1., 1.), scaled=False):
        ChebyshevBase.__init__(self, N, quad, domain=domain)
        from shenfun.tensorproductspace import BoundaryValues
        self.CT = Basis(N, quad, plan=plan)
        self._scaled = scaled
        self._factor = np.ones(1)
        if plan:
            self.plan(N, 0, np.float, {})
        self.bc = BoundaryValues(self, bc=bc)

    def get_vandermonde_basis(self, V):
        P = np.zeros(V.shape)
        P[:, :-2] = V[:, :-2] - V[:, 2:]
        P[:, -2] = (V[:, 0] + V[:, 1])/2
        P[:, -1] = (V[:, 0] - V[:, 1])/2
        return P

    def is_scaled(self):
        """Return True if scaled basis is used, otherwise False"""
        return False

    def evaluate_scalar_product(self, input_array, output_array, fast_transform=True):
        if fast_transform is False:
            self.vandermonde_scalar_product(input_array, output_array)
            return
        output = self.CT.scalar_product(fast_transform=fast_transform)
        s0 = self.sl(0)
        s1 = self.sl(1)
        c0 = 0.5*(output[s0] + output[s1])
        c1 = 0.5*(output[s0] - output[s1])
        s0[self.axis] = slice(0, -2)
        s1[self.axis] = slice(2, None)
        output[s0] -= output[s1]
        s0[self.axis] = -2
        output[s0] = c0
        s0[self.axis] = -1
        output[s0] = c1

    def evaluate_expansion_all(self, input_array, output_array, fast_transform=True):
        if fast_transform is False:
            SpectralBase.evaluate_expansion_all(self, input_array, output_array, False)
            return
        w_hat = work[(input_array, 0)]
        s0 = self.sl(slice(0, -2))
        s1 = self.sl(slice(2, None))
        w_hat[s0] = input_array[s0]
        w_hat[s1] -= input_array[s0]
        self.bc.apply_before(w_hat, False, (0.5, 0.5))
        self.CT.backward(w_hat)
        assert output_array is self.CT.backward.output_array

    def slice(self):
        return slice(0, self.N-2)

    def eval(self, x, fk, output_array=None):
        if output_array is None:
            output_array = np.zeros(x.shape)
        x = self.map_reference_domain(x)
        w_hat = work[(fk, 0)]
        output_array[:] = n_cheb.chebval(x, fk[:-2])
        w_hat[2:] = fk[:-2]
        output_array -= n_cheb.chebval(x, w_hat)
        output_array += 0.5*(fk[-1]*(1+x)+fk[-2]*(1-x))
        return output_array

    def plan(self, shape, axis, dtype, options):
        if isinstance(axis, tuple):
            axis = axis[0]

        if isinstance(self.forward, Transform):
            if self.forward.input_array.shape == shape and self.axis == axis:
                # Already planned
                return

        self.CT.plan(shape, axis, dtype, options)
        self.axis = self.CT.axis
        xfftn_fwd = self.CT.forward.xfftn
        xfftn_bck = self.CT.backward.xfftn
        U = xfftn_fwd.input_array
        V = xfftn_fwd.output_array
        self.forward = Transform(self.forward, xfftn_fwd, U, V, V)
        self.backward = Transform(self.backward, xfftn_bck, V, V, U)
        self.scalar_product = Transform(self.scalar_product, xfftn_fwd, U, V, V)
Example #11
0
class ShenDirichletBasis(ChebyshevBase):
    """Shen basis for Dirichlet boundary conditions

    Parameters
    ----------
        N : int, optional
            Number of quadrature points
        quad : str, optional
               Type of quadrature

               - GL - Chebyshev-Gauss-Lobatto
               - GC - Chebyshev-Gauss

        bc : 2-tuple of floats, optional
             Boundary conditions at x=(1,-1). For Poisson eq.
        domain : 2-tuple of floats, optional
                 The computational domain
        scaled : bool, optional
                 Whether or not to use scaled basis
    """

    def __init__(self, N=0, quad="GC", bc=(0, 0),
                 domain=(-1., 1.), scaled=False):
        ChebyshevBase.__init__(self, N, quad, domain=domain)
        from shenfun.tensorproductspace import BoundaryValues
        self.CT = Basis(N, quad)
        self._scaled = scaled
        self._factor = np.ones(1)
        self.plan(N, 0, np.float, {})
        self.bc = BoundaryValues(self, bc=bc)

    @staticmethod
    def boundary_condition():
        return 'Dirichlet'

    def _composite_basis(self, V):
        P = np.zeros(V.shape)
        P[:, :-2] = V[:, :-2] - V[:, 2:]
        P[:, -2] = (V[:, 0] + V[:, 1])/2
        P[:, -1] = (V[:, 0] - V[:, 1])/2
        return P

    def sympy_basis(self, i=0):
        x = sympy.symbols('x')
        return sympy.chebyshevt(i, x) - sympy.chebyshevt(i+2, x)

    def evaluate_basis(self, x, i=0, output_array=None):
        x = np.atleast_1d(x)
        if output_array is None:
            output_array = np.zeros(x.shape)
        if i < self.N-2:
            w = np.arccos(x)
            output_array[:] = np.cos(i*w) - np.cos((i+2)*w)
        elif i == self.N-2:
            output_array[:] = 0.5*(1+x)
        elif i == self.N-1:
            output_array[:] = 0.5*(1-x)
        return output_array

    def is_scaled(self):
        """Return True if scaled basis is used, otherwise False"""
        return False

    def evaluate_scalar_product(self, input_array, output_array, fast_transform=True):
        if fast_transform is False:
            self.vandermonde_scalar_product(input_array, output_array)
            return
        output = self.CT.scalar_product(fast_transform=fast_transform)
        s0 = self.si[0]
        s1 = self.si[1]
        c0 = 0.5*(output[s0] + output[s1])
        c1 = 0.5*(output[s0] - output[s1])
        s0 = self.sl[slice(0, -2)]
        s1 = self.sl[slice(2, None)]
        output[s0] -= output[s1]
        output[self.si[-2]] = c0
        output[self.si[-1]] = c1

    def evaluate_expansion_all(self, input_array, output_array, fast_transform=True):
        if fast_transform is False:
            SpectralBase.evaluate_expansion_all(self, input_array, output_array, False)
            return
        w_hat = work[(input_array, 0, True)]
        s0 = self.sl[slice(0, -2)]
        s1 = self.sl[slice(2, None)]
        w_hat[s0] = input_array[s0]
        w_hat[s1] -= input_array[s0]
        self.bc.apply_before(w_hat, False, (0.5, 0.5))
        self.CT.backward(w_hat)
        assert output_array is self.CT.backward.output_array

    def slice(self):
        return slice(0, self.N-2)

    def eval(self, x, u, output_array=None):
        if output_array is None:
            output_array = np.zeros(x.shape)
        x = self.map_reference_domain(x)
        w_hat = work[(u, 0, True)]
        output_array[:] = n_cheb.chebval(x, u[:-2])
        w_hat[2:] = u[:-2]
        output_array -= n_cheb.chebval(x, w_hat)
        output_array += 0.5*(u[-1]*(1-x)+u[-2]*(1+x))
        return output_array

    def plan(self, shape, axis, dtype, options):
        if isinstance(axis, tuple):
            assert len(axis) == 1
            axis = axis[-1]

        if isinstance(self.forward, Transform):
            if self.forward.input_array.shape == shape and self.axis == axis:
                # Already planned
                return

        self.CT.plan(shape, axis, dtype, options)
        self.CT.tensorproductspace = self.tensorproductspace
        xfftn_fwd = self.CT.forward.xfftn
        xfftn_bck = self.CT.backward.xfftn
        U = xfftn_fwd.input_array
        V = xfftn_fwd.output_array
        self.axis = axis
        self.forward = Transform(self.forward, xfftn_fwd, U, V, V)
        self.backward = Transform(self.backward, xfftn_bck, V, V, U)
        self.scalar_product = Transform(self.scalar_product, xfftn_fwd, U, V, V)
        self.si = islicedict(axis=self.axis, dimensions=self.dimensions())
        self.sl = slicedict(axis=self.axis, dimensions=self.dimensions())