def __init__(self, degree): b_degree = degree - 3 x = Symbol('x') poly_set = leg.basis_functions(deg=degree) pts0 = gauss_legendre_points(b_degree+1)[1:-1] pts1 = np.array([-1., 1.]) # The coefficient matrix B = np.zeros((degree+1, degree+1)) B[:, 0] = [(-1)**k for k in range(degree+1)] # Values are (-1) B[:, 1] = np.ones(degree+1) # Values at (1) # Val at -1, val at 1, dval at -1, dval at 1, the rest of polyevaluas. for row, f in enumerate(poly_set): vals = lambdify(x, f, 'numpy')(pts0) if isinstance(vals, (int, float)): vals = vals*np.ones(len(pts0)) dvals = lambdify(x, f.diff(x, 1), 'numpy')(pts1) if isinstance(dvals, (int, float)): dvals = dvals*np.ones(len(pts1)) B[row, 2:4] = dvals B[row, 4:] = vals # Invert to get the coefficients of the nodal basis self.alpha = np.linalg.inv(B) # Having coeffcient comes in handy if some matrix M is given w.r.t # poly_set basis then alpha.M.alpha.T is the matrix represented in # nodal basis # Symbolic nodal basis self.sym_basis = [sum(c*f for c, f in zip(row, poly_set)) for row in self.alpha] # For numerical evaluation the row in alpha are coeffcients of Legendre # polynomials so we can use legval # Finally remember the dofs as coordinates val, val, dval, dval self.dofs = np.hstack([pts1, pts1, pts0]) # And my finite element self.cell = ReferenceIntervalCell() # HACK computing df/dx without the derivatives. This is exact for P3 and # lower. Represent the L(f) = df/dx(p) = \int Riesz(L) * f dx # Riesz(L) = l is a polynomial of degree 3-compute its coeficients # They are given by mass matrix of the nodal basis xq, wq = np.polynomial.legendre.leggauss(degree+1) M = self.alpha.dot(leg.mass_matrix(degree).dot(self.alpha.T)) beta = np.linalg.inv(M) # Now I have expansion w.r.t nodal. Combine with alpha to get Legendre # beta = beta.dot(self.alpha) # beta[2:4] are the coefs of df/dx eval at -1 and 1. Need them only at # quadrature points self.riesz = (np.polynomial.legendre.legval(xq, beta[2].dot(self.alpha)), np.polynomial.legendre.legval(xq, beta[3].dot(self.alpha))) # Remember these for later evaluations self.quad = xq, wq
def _solve(mode, points, degree, n_cells, u, f): ''' In mode == convergence: Solve -u`` = f with dirichet bcs bdry of (-1, 1) given by exact solution. The Vh space is CG_space of degree elements and n_cells. Return hmin, error for convergence computation. In mode == cond: Just return h and the matrix A. ''' # Element. The polynomial space is spanned by Legendre basis poly_set = leg.basis_functions(degree) dof_set = points(degree) element = LagrangeElement(poly_set, dof_set) # Mesh mesh = IntervalMesh(a=-1, b=1, n_cells=n_cells) # Space V = FunctionSpace(mesh, element) bc = DirichletBC(V, u) # Need mass matrix to intefrate the rhs Mpoly_matrix = leg.mass_matrix(degree) Mget_geom_tensor = lambda cell: 1./cell.Jac M = assemble_matrix(V, Mpoly_matrix, Mget_geom_tensor, timer=0) # Stiffness matrix for Laplacian Apoly_matrix = leg.stiffness_matrix(degree) Aget_geom_tensor = lambda cell: cell.Jac A = assemble_matrix(V, Apoly_matrix, Aget_geom_tensor, timer=0) # Interpolant of source fV = V.interpolate(f) # Integrate in L2 to get the vector b = M.dot(fV.vector) # Apply boundary conditions bc.apply(A, b, True) x = spsolve(A, b) if mode == 'condition': return mesh.hmin(), A # As function uh = Function(V, x) # Error norm # Higher order DG element fine_degree = degree + 3 poly_set = leg.basis_functions(fine_degree) dof_set = chebyshev_points(fine_degree) element = LagrangeElement(poly_set, dof_set) # THe space V_fine = FunctionSpace(mesh, element, 'L2') # Interpolate exact solution to fine u_fine = V_fine.interpolate(u) # Interpolate approx solution fine uh_fine = V_fine.interpolate(uh) # Difference vector e = u_fine.vector - uh_fine.vector # Need matrix for integration of H10 norm Apoly_matrix = leg.stiffness_matrix(fine_degree) A_fine = assemble_matrix(V_fine, Apoly_matrix, Aget_geom_tensor, timer=1) # Integrate the error e = sqrt(np.sum(e*A_fine.dot(e))) # Mesh size hmin = mesh.hmin() return hmin, e
def cg_optimal_dofs_restricted(deg, vary=0): ''' What are optimal dofs that give smallest condition number in L^2 norm. By CG I mean that I constraint two dofs to be at (-1, 1). The remaining deg-1 points are to be determined. For even degrees symmetry is dof at 0 is also forced For vary None: the symmetry is used and `all` the points are used for search, i.e. this is multi-d problem. For vary=int: that guy and its mirror are changed, i.e. this is 1-d optimization problem. ''' poly_set = leg.basis_functions(deg) M = leg.mass_matrix(deg) # Only allow negative (make life easier for mirroring) and not midpoint or -1 if vary: assert 0 < vary < (deg/2 + 1 if deg % 2 else deg/2) # Want to minimize this def cond_number(x): # 2: [-1, y0, -1] # (3, 4): [-1, y0, -y0, -1], [-1, -y0, 0, y0, 1], # (5, 6): [-1, y0, y1, -y1, -y0, -1], [-1, y0, y1, 0, -y1, -y0, -1] # One-d optimzation if vary: x_ = chebyshev_points(deg) x_[vary] = x x_[deg-vary] = -x dof_set = x_ # Multi-d optimization else: if deg == 2: dof_set = np.r_[-1, x, 1] # Combine no zero elif deg % 2 == 1: dof_set = np.r_[-1, x, -x[::-1], 1] # Combine w/ zero else: dof_set = np.r_[-1, x, 0, -x[::-1], 1] element = LagrangeElement(poly_set, dof_set) alpha = element.alpha M_ = alpha.dot(M.dot(alpha.T)) return np.linalg.cond(M_) # Initial guess # 1d optim x0 = chebyshev_points(deg) if vary: x0 = x0[vary] else: # Multi-d optim if deg == 2: x0 = x0[1] # Combine no zero elif deg % 2 == 1: x0 = x0[1:deg/2+1] else: x0 = x0[1:deg/2] # Optimize res = minimize(cond_number, x0) return res, x0, cond_number(x0)
def solve(n_cells, degree=3, with_plot=False): # Problem w = 3 * np.pi x = Symbol("x") u = sin(w * x) f = -u.diff(x, 2) # As Expr u = Expression(u) f = Expression(f) # Space # element = HermiteElement(degree) poly_set = leg.basis_functions(degree) dof_set = chebyshev_points(degree) element = LagrangeElement(poly_set, dof_set) mesh = IntervalMesh(a=-1, b=1, n_cells=n_cells) V = FunctionSpace(mesh, element) bc = DirichletBC(V, u) # Need mass matrix to intefrate the rhs M = assemble_matrix(V, "mass", get_geom_tensor=None, timer=0) # NOTE We cannot you apply the alpha transform idea because the functions # are mapped with this selective weight on 2nd, 3rd functions. So some rows # of alpha would have to be multiplied by weights which are cell specific. # And then on top of this there would be a dx = J*dy term. Better just to # use the qudrature representations # Mpoly_matrix = leg.mass_matrix(degree) # M_ = assemble_matrix(V, Mpoly_matrix, Mget_geom_tensor, timer=0) # Stiffness matrix for Laplacian A = assemble_matrix(V, "stiffness", get_geom_tensor=None, timer=0) # NOTE the above # Apoly_matrix = leg.stiffness_matrix(degree) # A_ = assemble_matrix(V, Apoly_matrix, Aget_geom_tensor, timer=0) # Interpolant of source fV = V.interpolate(f) # Integrate in L2 to get the vector b = M.dot(fV.vector) # Apply boundary conditions bc.apply(A, b, True) x = spsolve(A, b) # As function uh = Function(V, x) # This is a (slow) way of plotting the high order if with_plot: fig = plt.figure() ax = fig.gca() uV = V.interpolate(u) for cell in Cells(mesh): a, b = cell.vertices[0, 0], cell.vertices[1, 0] x = np.linspace(a, b, 100) y = uh.eval_cell(x, cell) ax.plot(x, y, color=random.choice(["b", "g", "m", "c"])) y = uV.eval_cell(x, cell) ax.plot(x, y, color="r") y = u.eval_cell(x, cell) ax.plot(x, y, color="k") plt.show() # Error norm in CG high order fine_degree = degree + 3 poly_set = leg.basis_functions(fine_degree) dof_set = chebyshev_points(fine_degree) element = LagrangeElement(poly_set, dof_set) V_fine = FunctionSpace(mesh, element) # Interpolate exact solution to fine u_fine = V_fine.interpolate(u) # Interpolate approx solution fine uh_fine = V_fine.interpolate(uh) # Difference vector e = u_fine.vector - uh_fine.vector # L2 if False: Apoly_matrix = leg.mass_matrix(fine_degree) get_geom_tensor = lambda cell: 1.0 / cell.Jac # Need matrix for integration of H10 norm else: Apoly_matrix = leg.stiffness_matrix(fine_degree) get_geom_tensor = lambda cell: cell.Jac A_fine = assemble_matrix(V_fine, Apoly_matrix, get_geom_tensor, timer=0) # Integrate the error e = sqrt(np.sum(e * A_fine.dot(e))) # Mesh size hmin = mesh.hmin() # Add the cond number kappa = np.linalg.cond(A.toarray()) return hmin, e, kappa, A.shape[0]