def test_energy_fourier(N): B0 = FunctionSpace(N[0], 'F', dtype='D') B1 = FunctionSpace(N[1], 'F', dtype='D') B2 = FunctionSpace(N[2], 'F', dtype='d') for bases, axes in zip(((B0, B1, B2), (B0, B2, B1)), ((0, 1, 2), (2, 0, 1))): T = TensorProductSpace(comm, bases, axes=axes) u_hat = Function(T) u_hat[:] = np.random.random( u_hat.shape) + 1j * np.random.random(u_hat.shape) u = u_hat.backward() u_hat = u.forward(u_hat) u = u_hat.backward(u) e0 = comm.allreduce(np.sum(u.v * u.v) / np.prod(N)) e1 = fourier.energy_fourier(u_hat, T) assert abs(e0 - e1) < 1e-10
def test_backward_uniform(family): T = FunctionSpace(N, family) uT = Function(T, buffer=f) ub = uT.backward(kind='uniform') xj = T.mesh(uniform=True) fj = sp.lambdify(x, f)(xj) assert np.linalg.norm(fj - ub) < 1e-8
def test_padding_biharmonic(family): N = 8 B = FunctionSpace(N, family, bc=(0, 0, 0, 0)) Bp = B.get_dealiased(1.5) u = Function(B) u[:(N - 4)] = np.random.random(N - 4) up = Array(Bp) up = Bp.backward(u, fast_transform=False) uf = Bp.forward(up, fast_transform=False) assert np.linalg.norm(uf - u) < 1e-12 if family == 'C': up = Bp.backward(u) uf = Bp.forward(up) assert np.linalg.norm(uf - u) < 1e-12 # Test padding 2D F = FunctionSpace(N, 'F', dtype='d') T = TensorProductSpace(comm, (B, F)) Tp = T.get_dealiased(1.5) u = Function(T) u[:-4, :-1] = np.random.random(u[:-4, :-1].shape) up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8 # Test padding 3D F1 = FunctionSpace(N, 'F', dtype='D') T = TensorProductSpace(comm, (F1, F, B)) Tp = T.get_dealiased(1.5) u = Function(T) u[:, :, :-4] = np.random.random(u[:, :, :-4].shape) u = u.backward().forward() # Clean up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8
def test_padding_neumann(family): N = 8 B = FunctionSpace(N, family, bc={'left': ('N', 0), 'right': ('N', 0)}) Bp = B.get_dealiased(1.5) u = Function(B) u[1:-2] = np.random.random(N - 3) up = Array(Bp) up = Bp.backward(u, fast_transform=False) uf = Bp.forward(up, fast_transform=False) assert np.linalg.norm(uf - u) < 1e-12 if family == 'C': up = Bp.backward(u) uf = Bp.forward(up) assert np.linalg.norm(uf - u) < 1e-12 # Test padding 2D F = FunctionSpace(N, 'F', dtype='d') T = TensorProductSpace(comm, (B, F)) Tp = T.get_dealiased(1.5) u = Function(T) u[1:-2, :-1] = np.random.random(u[1:-2, :-1].shape) up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8 # Test padding 3D F1 = FunctionSpace(N, 'F', dtype='D') T = TensorProductSpace(comm, (F1, F, B)) Tp = T.get_dealiased(1.5) u = Function(T) u[:, :, 1:-2] = np.random.random(u[:, :, 1:-2].shape) u = u.backward().forward() # Clean up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8
def test_project_2dirichlet(quad): x, y = symbols("x,y") ue = (cos(4*y)*sin(2*x))*(1-x**2)*(1-y**2) sizes = (18, 17) D0 = lbases.ShenDirichlet(sizes[0], quad=quad) D1 = lbases.ShenDirichlet(sizes[1], quad=quad) B0 = lbases.Orthogonal(sizes[0], quad=quad) B1 = lbases.Orthogonal(sizes[1], quad=quad) DD = TensorProductSpace(comm, (D0, D1)) BD = TensorProductSpace(comm, (B0, D1)) DB = TensorProductSpace(comm, (D0, B1)) BB = TensorProductSpace(comm, (B0, B1)) X = DD.local_mesh(True) uh = Function(DD, buffer=ue) dudx_hat = project(Dx(uh, 0, 1), BD) dx = Function(BD, buffer=ue.diff(x, 1)) assert np.allclose(dx, dudx_hat, 0, 1e-5) dudy = project(Dx(uh, 1, 1), DB).backward() duedy = Array(DB, buffer=ue.diff(y, 1)) assert np.allclose(duedy, dudy, 0, 1e-5) us_hat = Function(BB) uq = uh.backward() us = project(uq, BB, output_array=us_hat).backward() assert np.allclose(us, uq, 0, 1e-5) dudxy = project(Dx(us_hat, 0, 1) + Dx(us_hat, 1, 1), BB).backward() dxy = Array(BB, buffer=ue.diff(x, 1) + ue.diff(y, 1)) assert np.allclose(dxy, dudxy, 0, 1e-5), np.linalg.norm(dxy-dudxy)
def test_quasiGalerkin(basis): N = 40 T = FunctionSpace(N, 'C') S = FunctionSpace(N, 'C', basis=basis) u = TrialFunction(S) v = TestFunction(T) A = inner(v, div(grad(u))) B = inner(v, u) Q = chebyshev.quasi.QIGmat(N) A = Q*A B = Q*B M = B-A sol = la.Solve(M, S) f_hat = inner(v, Array(T, buffer=fe)) f_hat[:-2] = Q.diags('csc')*f_hat[:-2] u_hat = Function(S) u_hat = sol(f_hat, u_hat) uj = u_hat.backward() ua = Array(S, buffer=ue) bb = Q*np.ones(N) assert abs(np.sum(bb[:8])) < 1e-8 if S.boundary_condition().lower() == 'neumann': xj, wj = S.points_and_weights() ua -= np.sum(ua*wj)/np.pi # normalize uj -= np.sum(uj*wj)/np.pi # normalize assert np.sqrt(inner(1, (uj-ua)**2)) < 1e-5
def test_backward2D(): T = FunctionSpace(N, 'C') L = FunctionSpace(N, 'L') F = FunctionSpace(N, 'F', dtype='d') TT = TensorProductSpace(comm, (T, F)) TL = TensorProductSpace(comm, (L, F)) uT = Function(TT, buffer=f) uL = Function(TL, buffer=f) u2 = uL.backward(kind=TT) uT2 = project(u2, TT) assert np.linalg.norm(uT2 - uT) TT = TensorProductSpace(comm, (F, T)) TL = TensorProductSpace(comm, (F, L)) uT = Function(TT, buffer=f) uL = Function(TL, buffer=f) u2 = uL.backward(kind=TT) uT2 = project(u2, TT) assert np.linalg.norm(uT2 - uT)
def test_backward2ND(): T0 = FunctionSpace(N, 'C') L0 = FunctionSpace(N, 'L') T1 = FunctionSpace(N, 'C') L1 = FunctionSpace(N, 'L') TT = TensorProductSpace(comm, (T0, T1)) LL = TensorProductSpace(comm, (L0, L1)) uT = Function(TT, buffer=h) uL = Function(LL, buffer=h) u2 = uL.backward(kind=TT) uT2 = project(u2, TT) assert np.linalg.norm(uT2 - uT)
def test_backward3D(): T = FunctionSpace(N, 'C') L = FunctionSpace(N, 'L') F0 = FunctionSpace(N, 'F', dtype='D') F1 = FunctionSpace(N, 'F', dtype='d') TT = TensorProductSpace(comm, (F0, T, F1)) TL = TensorProductSpace(comm, (F0, L, F1)) uT = Function(TT, buffer=h) uL = Function(TL, buffer=h) u2 = uL.backward(kind=TT) uT2 = project(u2, TT) assert np.linalg.norm(uT2 - uT)
def test_padding_orthogonal(family): N = 8 B = FunctionSpace(N, family) Bp = B.get_dealiased(1.5) u = Function(B) u[:] = np.random.random(u.shape) up = Array(Bp) if family != 'F': up = Bp.backward(u, fast_transform=False) uf = Bp.forward(up, fast_transform=False) assert np.linalg.norm(uf - u) < 1e-12 if family in ('C', 'F'): up = Bp.backward(u, fast_transform=True) uf = Bp.forward(up, fast_transform=True) assert np.linalg.norm(uf - u) < 1e-12 # Test padding 2D dtype = 'D' if family == 'F' else 'd' F = FunctionSpace(N, 'F', dtype=dtype) T = TensorProductSpace(comm, (F, B)) Tp = T.get_dealiased(1.5) u = Function(T) u[:] = np.random.random(u.shape) u = u.backward().forward() up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8 # Test padding 3D F1 = FunctionSpace(N, 'F', dtype='D') T = TensorProductSpace(comm, (F1, F, B)) Tp = T.get_dealiased(1.5) u = Function(T) u[:] = np.random.random(u.shape) u = u.backward().forward() # Clean up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8
def test_backward(): T = FunctionSpace(N, 'C') L = FunctionSpace(N, 'L') uT = Function(T, buffer=f) uL = Function(L, buffer=f) uLT = uL.backward(kind=T) uT2 = project(uLT, T) assert np.linalg.norm(uT2 - uT) < 1e-8 uTL = uT.backward(kind=L) uL2 = project(uTL, L) assert np.linalg.norm(uL2 - uL) < 1e-8 T2 = FunctionSpace(N, 'C', bc=(f.subs(x, -1), f.subs(x, 1))) L = FunctionSpace(N, 'L') uT = Function(T2, buffer=f) uL = Function(L, buffer=f) uLT = uL.backward(kind=T2) uT2 = project(uLT, T2) assert np.linalg.norm(uT2 - uT) < 1e-8
def main(N, family, bci, bcj, plotting=False): global fe, ue BX = FunctionSpace(N, family=family, bc=bcx[bci], domain=xdomain) BY = FunctionSpace(N, family=family, bc=bcy[bcj], domain=ydomain) T = TensorProductSpace(comm, (BX, BY)) u = TrialFunction(T) v = TestFunction(T) # Get f on quad points fj = Array(T, buffer=fe) # Compare with analytical solution ua = Array(T, buffer=ue) if T.use_fixed_gauge: mean = dx(ua, weighted=True) / inner(1, Array(T, val=1)) # Compute right hand side of Poisson equation f_hat = Function(T) f_hat = inner(v, fj, output_array=f_hat) # Get left hand side of Poisson equation A = inner(v, -div(grad(u))) u_hat = Function(T) sol = la.Solver2D(A, fixed_gauge=mean if T.use_fixed_gauge else None) u_hat = sol(f_hat, u_hat) uj = u_hat.backward() assert np.allclose(uj, ua), np.linalg.norm(uj - ua) print("Error=%2.16e" % (np.sqrt(dx((uj - ua)**2)))) if 'pytest' not in os.environ and plotting is True: import matplotlib.pyplot as plt X, Y = T.local_mesh(True) plt.contourf(X, Y, uj, 100) plt.colorbar() plt.figure() plt.contourf(X, Y, ua, 100) plt.colorbar() plt.figure() plt.contourf(X, Y, ua - uj, 100) plt.colorbar()
def main(N, family, bci): bc = bcs[bci] if bci == 0: SD = FunctionSpace(N, family=family, bc=bc, domain=domain, mean=mean[family.lower()]) else: SD = FunctionSpace(N, family=family, bc=bc, domain=domain) u = TrialFunction(SD) v = TestFunction(SD) # Get f on quad points fj = Array(SD, buffer=fe) # Compute right hand side of Poisson equation f_hat = Function(SD) f_hat = inner(v, fj, output_array=f_hat) # Get left hand side of Poisson equation A = inner(v, div(grad(u))) u_hat = Function(SD).set_boundary_dofs() if isinstance(A, list): bc_mat = extract_bc_matrices([A]) A = A[0] f_hat -= bc_mat[0].matvec(u_hat, Function(SD)) u_hat = A.solve(f_hat, u_hat) uj = u_hat.backward() uh = uj.forward() # Compare with analytical solution ua = Array(SD, buffer=ue) assert np.allclose(uj, ua), np.linalg.norm(uj - ua) if 'pytest' not in os.environ: print("Error=%2.16e" % (np.sqrt(dx((uj - ua)**2)))) import matplotlib.pyplot as plt plt.plot(SD.mesh(), uj, 'b', SD.mesh(), ua, 'r')
def test_quasiTau(bc): N = 40 T = FunctionSpace(N, 'C') u = TrialFunction(T) v = TestFunction(T) A = inner(v, div(grad(u))) B = inner(v, u) Q = chebyshev.quasi.QITmat(N) A = Q*A B = Q*B bb = Q*np.ones(N) assert abs(np.sum(bb[:8])) < 1e-8 M = B-A M0 = M.diags().tolil() if bc == 'Dirichlet': M0[0] = np.ones(N) nn = np.ones(N) nn[1::2] = -1 M0[1] = nn elif bc == 'Neumann': nn = np.arange(N)**2 M0[0] = nn.copy() nn[1::2] *= -1 M0[1] = nn M0 = M0.tocsc() f_hat = inner(v, Array(T, buffer=fe)) gh = Q.diags('csc')*f_hat gh[:2] = 0 u_hat = Function(T) u_hat[:] = scp.linalg.spsolve(M0, gh) uj = u_hat.backward() ua = Array(T, buffer=ue) if bc == 'Neumann': xj, wj = T.points_and_weights() ua -= np.sum(ua*wj)/np.pi # normalize uj -= np.sum(uj*wj)/np.pi # normalize assert np.sqrt(inner(1, (uj-ua)**2)) < 1e-5
def test_padding(family): N = 8 B = FunctionSpace(N, family, bc=(-1, 1), domain=(-2, 2)) Bp = B.get_dealiased(1.5) u = Function(B).set_boundary_dofs() #u[:(N-2)] = np.random.random(N-2) u[:(N - 2)] = 1 up = Array(Bp) up = Bp.backward(u, fast_transform=False) uf = Bp.forward(up, fast_transform=False) assert np.linalg.norm(uf - u) < 1e-12 if family == 'C': up = Bp.backward(u) uf = Bp.forward(up) assert np.linalg.norm(uf - u) < 1e-12 # Test padding 2D F = FunctionSpace(N, 'F', dtype='d') T = TensorProductSpace(comm, (B, F)) Tp = T.get_dealiased(1.5) u = Function(T).set_boundary_dofs() u[:-2, :-1] = np.random.random(u[:-2, :-1].shape) up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8 # Test padding 3D F1 = FunctionSpace(N, 'F', dtype='D') T = TensorProductSpace(comm, (F1, F, B)) Tp = T.get_dealiased(1.5) u = Function(T).set_boundary_dofs() u[:, :, :-2] = np.random.random(u[:, :, :-2].shape) u = u.backward().forward() # Clean up = Tp.backward(u) uc = Tp.forward(up) assert np.linalg.norm(u - uc) < 1e-8
SD = FunctionSpace(N, 'Hermite') u = TrialFunction(SD) v = TestFunction(SD) # Get f on quad points fj = Array(SD, buffer=fe) # Compute right hand side of Poisson equation f_hat = Function(SD) f_hat = inner(v, -fj, output_array=f_hat) # Get left hand side of Poisson equation A = inner(grad(v), grad(u)) f_hat = A / f_hat uj = f_hat.backward() uh = uj.forward() # Compare with analytical solution ua = Array(SD, buffer=ue) print("Error=%2.16e" % (np.linalg.norm(uj - ua))) assert np.allclose(uj, ua, atol=1e-5) point = np.array([0.1, 0.2]) p = SD.eval(point, f_hat) assert np.allclose(p, lambdify(x, ue)(point), atol=1e-5) if 'pytest' not in os.environ: import matplotlib.pyplot as plt xx = np.linspace(-8, 8, 100) plt.plot(xx, lambdify(x, ue)(xx), 'r', xx, uh.eval(xx), 'bo', markersize=2)
# Size of discretization N = int(sys.argv[-2]) SD = FunctionSpace(N, family=family, bc='NeumannDirichlet', domain=domain) u = TrialFunction(SD) v = TestFunction(SD) # Get f on quad points fj = Array(SD, buffer=fe) # Compute right hand side of Poisson equation f_hat = Function(SD) f_hat = inner(v, fj, output_array=f_hat) # Get left hand side of Poisson equation A = inner(v, div(grad(u))) u_hat = Function(SD) u_hat = A.solve(f_hat, u_hat) uj = u_hat.backward() uh = uj.forward() # Compare with analytical solution ua = Array(SD, buffer=ue) print("Error=%2.16e" % (np.sqrt(dx((uj - ua)**2)))) assert np.allclose(uj, ua) if 'pytest' not in os.environ: import matplotlib.pyplot as plt plt.plot(SD.mesh(), uj, 'b', SD.mesh(), ua, 'r') plt.show()
gn = Array(FY, buffer=neumann_condition) evaluate_x_bndry = FX.evaluate_basis_all(-1) project_g = inner(gn, v_bndry) bndry_integral = np.outer(evaluate_x_bndry, project_g) rhs -= bndry_integral Sol = sf.la.SolverGeneric2ND(mat) u_hat = Function(T) u_hat = Sol(rhs, u_hat) u_ana = Array(T, buffer=ua) l2_error = np.linalg.norm(u_hat.backward() - u_ana) print(l2_error) xx, yy = T.mesh() X, Y = np.meshgrid(xx.squeeze(), yy.squeeze()) fig = plt.figure() ax = fig.add_subplot(1, 2, 1, projection='3d') ax.plot_surface(X, Y, np.transpose(u_ana)) ax.set_xlabel('x') ax.set_ylabel('y') ax = fig.add_subplot(1, 2, 2, projection='3d') ax.plot_surface(X, Y, np.transpose(u_hat.backward())) ax.set_xlabel('x') ax.set_ylabel('y')
def test_eval_tensor(typecode, dim, ST, quad): # Using sympy to compute an analytical solution # Testing for Dirichlet and regular basis x, y, z = symbols("x,y,z") sizes = (22, 21) funcx = { '': (1 - x**2) * sin(np.pi * x), 'Dirichlet': (1 - x**2) * sin(np.pi * x), 'Neumann': (1 - x**2) * sin(np.pi * x), 'Biharmonic': (1 - x**2) * sin(2 * np.pi * x) } funcy = { '': (1 - y**2) * sin(np.pi * y), 'Dirichlet': (1 - y**2) * sin(np.pi * y), 'Neumann': (1 - y**2) * sin(np.pi * y), 'Biharmonic': (1 - y**2) * sin(2 * np.pi * y) } funcz = { '': (1 - z**2) * sin(np.pi * z), 'Dirichlet': (1 - z**2) * sin(np.pi * z), 'Neumann': (1 - z**2) * sin(np.pi * z), 'Biharmonic': (1 - z**2) * sin(2 * np.pi * z) } funcs = { (1, 0): cos(2 * y) * funcx[ST.boundary_condition()], (1, 1): cos(2 * x) * funcy[ST.boundary_condition()], (2, 0): sin(6 * z) * cos(4 * y) * funcx[ST.boundary_condition()], (2, 1): sin(2 * z) * cos(4 * x) * funcy[ST.boundary_condition()], (2, 2): sin(2 * x) * cos(4 * y) * funcz[ST.boundary_condition()] } syms = {1: (x, y), 2: (x, y, z)} points = None if comm.Get_rank() == 0: points = np.random.random((dim + 1, 4)) points = comm.bcast(points) t_0 = 0 t_1 = 0 t_2 = 0 for shape in product(*([sizes] * dim)): #for shape in ((64, 64),): bases = [] for n in shape[:-1]: bases.append(Basis(n, 'F', dtype=typecode.upper())) bases.append(Basis(shape[-1], 'F', dtype=typecode)) for axis in range(dim + 1): #for axis in (0,): ST0 = ST(shape[-1], quad=quad) bases.insert(axis, ST0) # Spectral space must be aligned in nonperiodic direction, hence axes fft = TensorProductSpace(comm, bases, dtype=typecode, axes=axes[dim][axis]) print('axes', axes[dim][axis]) print('bases', bases) #print(bases[0].axis, bases[1].axis) X = fft.local_mesh(True) ue = funcs[(dim, axis)] ul = lambdify(syms[dim], ue, 'numpy') uu = ul(*X).astype(typecode) uq = ul(*points).astype(typecode) u_hat = Function(fft) u_hat = fft.forward(uu, u_hat) t0 = time() result = fft.eval(points, u_hat, method=0) t_0 += time() - t0 assert np.allclose(uq, result, 0, 1e-6) t0 = time() result = fft.eval(points, u_hat, method=1) t_1 += time() - t0 assert np.allclose(uq, result, 0, 1e-6) t0 = time() result = fft.eval(points, u_hat, method=2) t_2 += time() - t0 print(uq) assert np.allclose(uq, result, 0, 1e-6), uq / result result = u_hat.eval(points) assert np.allclose(uq, result, 0, 1e-6) ua = u_hat.backward() assert np.allclose(uu, ua, 0, 1e-6) ua = Array(fft) ua = u_hat.backward(ua) assert np.allclose(uu, ua, 0, 1e-6) bases.pop(axis) fft.destroy() print('method=0', t_0) print('method=1', t_1) print('method=2', t_2)
# Compute right hand side of biharmonic equation f_hat = inner(v, fj) # Get left hand side of biharmonic equation if family == 'chebyshev': # No integration by parts due to weights matrices = inner(v, div(grad(div(grad(u))))) else: # Use form with integration by parts. matrices = inner(div(grad(v)), div(grad(u))) # Create linear algebra solver H = SolverGeneric2NP(matrices) # Solve and transform to real space u_hat = Function(T) # Solution spectral space u_hat = H(f_hat, u_hat) # Solve uq = u_hat.backward() # Compare with analytical solution uj = ul(*X) print(abs(uj - uq).max()) assert np.allclose(uj, uq) points = np.array([[0.2, 0.3], [0.1, 0.5]]) p = T.eval(points, u_hat, method=2) assert np.allclose(p, ul(*points)) if 'pytest' not in os.environ: import matplotlib.pyplot as plt plt.figure() plt.contourf(X[0], X[1], uq) plt.colorbar()
def test_eval_tensor(typecode, dim, ST, quad): # Using sympy to compute an analytical solution # Testing for Dirichlet and regular basis x, y, z = symbols("x,y,z") sizes = (25, 24) funcx = (x**2 - 1) * cos(2 * np.pi * x) funcy = (y**2 - 1) * cos(2 * np.pi * y) funcz = (z**2 - 1) * cos(2 * np.pi * z) funcs = { (1, 0): cos(4 * y) * funcx, (1, 1): cos(4 * x) * funcy, (2, 0): (sin(6 * z) + cos(4 * y)) * funcx, (2, 1): (sin(2 * z) + cos(4 * x)) * funcy, (2, 2): (sin(2 * x) + cos(4 * y)) * funcz } syms = {1: (x, y), 2: (x, y, z)} points = np.array([[0.1] * (dim + 1), [0.01] * (dim + 1), [0.4] * (dim + 1), [0.5] * (dim + 1)]) for shape in product(*([sizes] * dim)): bases = [] for n in shape[:-1]: bases.append(Basis(n, 'F', dtype=typecode.upper())) bases.append(Basis(shape[-1], 'F', dtype=typecode)) if dim < 3: n = min(shape) if typecode in 'fdg': n //= 2 n += 1 if n < comm.size: continue for axis in range(dim + 1): ST0 = ST(shape[-1], quad=quad) bases.insert(axis, ST0) # Spectral space must be aligned in nonperiodic direction, hence axes fft = TensorProductSpace(comm, bases, dtype=typecode, axes=axes[dim][axis]) X = fft.local_mesh(True) ue = funcs[(dim, axis)] ul = lambdify(syms[dim], ue, 'numpy') uu = ul(*X).astype(typecode) uq = ul(*points.T).astype(typecode) u_hat = Function(fft) u_hat = fft.forward(uu, u_hat) result = fft.eval(points, u_hat, cython=True) assert np.allclose(uq, result, 0, 1e-6) result = fft.eval(points, u_hat, cython=False) assert np.allclose(uq, result, 0, 1e-6) result = u_hat.eval(points) assert np.allclose(uq, result, 0, 1e-6) ua = u_hat.backward() assert np.allclose(uu, ua, 0, 1e-6) ua = Array(fft) ua = u_hat.backward(ua) assert np.allclose(uu, ua, 0, 1e-6) bases.pop(axis) fft.destroy()