def u_glob(U, cells, vertices, dof_map, resolution_per_element=51): """ Compute (x, y) coordinates of a curve y = u(x), where u is a finite element function: u(x) = sum_i of U_i*phi_i(x). (The solution of the linear system is in U.) Method: Run through each element and compute curve coordinates over the element. This function works with cells, vertices, and dof_map. """ x_patches = [] u_patches = [] nodes = {} # node coordinates (use dict to avoid multiple values) for e in range(len(cells)): Omega_e = (vertices[cells[e][0]], vertices[cells[e][-1]]) d = len(dof_map[e]) - 1 phi = basis(d) X = np.linspace(-1, 1, resolution_per_element) x = affine_mapping(X, Omega_e) x_patches.append(x) u_cell = 0 # u(x) over this cell for r in range(d+1): i = dof_map[e][r] # global dof number u_cell += U[i]*phi[r](X) u_patches.append(u_cell) # Compute global coordinates of local nodes, # assuming all dofs corresponds to values at nodes X = np.linspace(-1, 1, d+1) x = affine_mapping(X, Omega_e) for r in range(d+1): nodes[dof_map[e][r]] = x[r] nodes = np.array([nodes[i] for i in sorted(nodes)]) x = np.concatenate(x_patches) u = np.concatenate(u_patches) return x, u, nodes
def approximate(f, symbolic=False, d=1, N_e=4, numint=None, Omega=[0, 1], filename='tmp'): """ Compute the finite element approximation, using Lagrange elements of degree d, to a symbolic expression f (with x as independent variable) on a domain Omega. N_e is the number of elements. symbolic=True implies symbolic expressions in the calculations, while symbolic=False means numerical computing. numint is the name of the numerical integration rule (Trapezoidal, Simpson, GaussLegendre2, GaussLegendre3, GaussLegendre4, etc.). numint=None implies exact integration. """ numint_name = numint # save name if symbolic: if numint == 'Trapezoidal': numint = [[sym.S(-1), sym.S(1)], [sym.S(1), sym.S(1)]] # sympy integers elif numint == 'Simpson': numint = [[sym.S(-1), sym.S(0), sym.S(1)], [sym.Rational(1,3), sym.Rational(4,3), sym.Rational(1,3)]] elif numint == 'Midpoint': numint = [[sym.S(0)], [sym.S(2)]] elif numint == 'GaussLegendre2': numint = [[-1/sym.sqrt(3), 1/sym.sqrt(3)], [sym.S(1), sym.S(1)]] elif numint == 'GaussLegendre3': numint = [[-sym.sqrt(sym.Rational(3,5)), 0, sym.sqrt(sym.Rational(3,5))], [sym.Rational(5,9), sym.Rational(8,9), sym.Rational(5,9)]] elif numint is not None: print 'Numerical rule %s is not supported for symbolic computing' % numint numint = None else: if numint == 'Trapezoidal': numint = [[-1, 1], [1, 1]] elif numint == 'Simpson': numint = [[-1, 0, 1], [1./3, 4./3, 1./3]] elif numint == 'Midpoint': numint = [[0], [2]] elif numint == 'GaussLegendre2': numint = [[-1/sqrt(3), 1/sqrt(3)], [1, 1]] elif numint == 'GaussLegendre3': numint = [[-sqrt(3./5), 0, sqrt(3./5)], [5./9, 8./9, 5./9]] elif numint == 'GaussLegendre4': numint = [[-0.86113631, -0.33998104, 0.33998104, 0.86113631], [ 0.34785485, 0.65214515, 0.65214515, 0.34785485]] elif numint == 'GaussLegendre5': numint = [[-0.90617985, -0.53846931, -0. , 0.53846931, 0.90617985], [ 0.23692689, 0.47862867, 0.56888889, 0.47862867, 0.23692689]] elif numint is not None: print 'Numerical rule %s is not supported for numerical computing' % numint numint = None vertices, cells, dof_map = mesh_uniform(N_e, d, Omega, symbolic) # phi is a list where phi[e] holds the basis in cell no e # (this is required by assemble, which can work with # meshes with different types of elements). # len(dof_map[e]) is the number of nodes in cell e, # and the degree of the polynomial is len(dof_map[e])-1 phi = [basis(len(dof_map[e])-1) for e in range(N_e)] print 'phi basis (reference element):\n', phi A, b = assemble(vertices, cells, dof_map, phi, f, symbolic=symbolic, numint=numint) print 'cells:', cells print 'vertices:', vertices print 'dof_map:', dof_map print 'A:\n', A print 'b:\n', b #print sym.latex(A, mode='plain') #print sym.latex(b, mode='plain') if symbolic: c = A.LUsolve(b) else: c = np.linalg.solve(A, b) print 'c:\n', c if not symbolic: print 'Plain interpolation/collocation:' x = sym.Symbol('x') f = sym.lambdify([x], f, modules='numpy') try: f_at_vertices = [f(xc) for xc in vertices] print f_at_vertices except Exception as e: print 'could not evaluate f numerically:' print e # else: nodes are symbolic so f(nodes[i]) only makes sense # in the non-symbolic case if filename is not None: title = 'P%d, N_e=%d' % (d, N_e) if numint is None: title += ', exact integration' else: title += ', integration: %s' % numint_name x_u, u = u_glob(np.asarray(c), vertices, cells, dof_map, resolution_per_element=51) x_f = np.linspace(Omega[0], Omega[1], 10001) # mesh for f import scitools.std as plt plt.plot(x_u, u, '-', x_f, f(x_f), '--') plt.legend(['u', 'f']) plt.title(title) plt.savefig(filename + '.pdf') plt.savefig(filename + '.png') return c
def approximate(f, d, N_e, numint, Omega=[0,1], filename='tmp'): """ Compute the finite element approximation, using Lagrange elements of degree d, to a Python functionn f on a domain Omega. N_e is the number of elements. numint is the name of the numerical integration rule (Trapezoidal, Simpson, GaussLegendre2, GaussLegendre3, GaussLegendre4, etc.). numint=None implies exact integration. """ from math import sqrt numint_name = numint # save name if numint == 'Trapezoidal': numint = [[-1, 1], [1, 1]] elif numint == 'Simpson': numint = [[-1, 0, 1], [1./3, 4./3, 1./3]] elif numint == 'Midpoint': numint = [[0], [2]] elif numint == 'GaussLegendre2': numint = [[-1/sqrt(3), 1/sqrt(3)], [1, 1]] elif numint == 'GaussLegendre3': numint = [[-sqrt(3./5), 0, sqrt(3./5)], [5./9, 8./9, 5./9]] elif numint == 'GaussLegendre4': numint = [[-0.86113631, -0.33998104, 0.33998104, 0.86113631], [ 0.34785485, 0.65214515, 0.65214515, 0.34785485]] elif numint == 'GaussLegendre5': numint = [[-0.90617985, -0.53846931, -0. , 0.53846931, 0.90617985], [ 0.23692689, 0.47862867, 0.56888889, 0.47862867, 0.23692689]] elif numint is not None: print 'Numerical rule %s is not supported for numerical computing' % numint sys.exit(1) vertices, cells, dof_map = mesh_uniform(N_e, d, Omega) # phi is a list where phi[e] holds the basis in cell no e # (this is required by assemble, which can work with # meshes with different types of elements). # len(dof_map[e]) is the number of nodes in cell e, # and the degree of the polynomial is len(dof_map[e])-1 phi = [basis(len(dof_map[e])-1) for e in range(N_e)] A, b = assemble(vertices, cells, dof_map, phi, f, numint=numint) print 'cells:', cells print 'vertices:', vertices print 'dof_map:', dof_map print 'A:\n', A print 'b:\n', b c = np.linalg.solve(A, b) print 'c:\n', c if filename is not None: title = 'P%d, N_e=%d' % (d, N_e) title += ', integration: %s' % numint_name x_u, u, _ = u_glob(np.asarray(c), vertices, cells, dof_map, resolution_per_element=51) x_f = np.linspace(Omega[0], Omega[1], 10001) # mesh for f import scitools.std as plt plt.plot(x_u, u, '-', x_f, f(x_f), '--') plt.legend(['u', 'f']) plt.title(title) plt.savefig(filename + '.pdf') plt.savefig(filename + '.png') return c
def approximate(f, d, N_e, numint, Omega=[0, 1], filename='tmp'): """ Compute the finite element approximation, using Lagrange elements of degree d, to a Python functionn f on a domain Omega. N_e is the number of elements. numint is the name of the numerical integration rule (Trapezoidal, Simpson, GaussLegendre2, GaussLegendre3, GaussLegendre4, etc.). numint=None implies exact integration. """ from math import sqrt numint_name = numint # save name if numint == 'Trapezoidal': numint = [[-1, 1], [1, 1]] elif numint == 'Simpson': numint = [[-1, 0, 1], [1. / 3, 4. / 3, 1. / 3]] elif numint == 'Midpoint': numint = [[0], [2]] elif numint == 'GaussLegendre2': numint = [[-1 / sqrt(3), 1 / sqrt(3)], [1, 1]] elif numint == 'GaussLegendre3': numint = [[-sqrt(3. / 5), 0, sqrt(3. / 5)], [5. / 9, 8. / 9, 5. / 9]] elif numint == 'GaussLegendre4': numint = [[-0.86113631, -0.33998104, 0.33998104, 0.86113631], [0.34785485, 0.65214515, 0.65214515, 0.34785485]] elif numint == 'GaussLegendre5': numint = [[-0.90617985, -0.53846931, -0., 0.53846931, 0.90617985], [0.23692689, 0.47862867, 0.56888889, 0.47862867, 0.23692689]] elif numint is not None: print 'Numerical rule %s is not supported '\ 'for numerical computing' % numint sys.exit(1) vertices, cells, dof_map = mesh_uniform(N_e, d, Omega) # phi is a list where phi[e] holds the basis in cell no e # (this is required by assemble, which can work with # meshes with different types of elements). # len(dof_map[e]) is the number of nodes in cell e, # and the degree of the polynomial is len(dof_map[e])-1 phi = [basis(len(dof_map[e]) - 1) for e in range(N_e)] A, b = assemble(vertices, cells, dof_map, phi, f, numint=numint) print 'cells:', cells print 'vertices:', vertices print 'dof_map:', dof_map print 'A:\n', A print 'b:\n', b c = np.linalg.solve(A, b) print 'c:\n', c if filename is not None: title = 'P%d, N_e=%d' % (d, N_e) title += ', integration: %s' % numint_name x_u, u, _ = u_glob(np.asarray(c), vertices, cells, dof_map, resolution_per_element=51) x_f = np.linspace(Omega[0], Omega[1], 10001) # mesh for f import scitools.std as plt plt.plot(x_u, u, '-', x_f, f(x_f), '--') plt.legend(['u', 'f']) plt.title(title) plt.savefig(filename + '.pdf') plt.savefig(filename + '.png') return c
def approximate(f, symbolic=False, d=1, N_e=4, numint=None, Omega=[0, 1], collocation=False, filename='tmp'): """ Compute the finite element approximation, using Lagrange elements of degree d, to a symbolic expression f (with x as independent variable) on a domain Omega. N_e is the number of elements. symbolic=True implies symbolic expressions in the calculations, while symbolic=False means numerical computing. numint is the name of the numerical integration rule (Trapezoidal, Simpson, GaussLegendre2, GaussLegendre3, GaussLegendre4, etc.). numint=None implies exact integration. """ numint_name = numint # save name if symbolic: if numint == 'Trapezoidal': numint = [[sym.S(-1), sym.S(1)], [sym.S(1), sym.S(1)]] # sympy integers elif numint == 'Simpson': numint = [[sym.S(-1), sym.S(0), sym.S(1)], [sym.Rational(1,3), sym.Rational(4,3), sym.Rational(1,3)]] elif numint == 'Midpoint': numint = [[sym.S(0)], [sym.S(2)]] elif numint == 'GaussLegendre2': numint = [[-1/sym.sqrt(3), 1/sym.sqrt(3)], [sym.S(1), sym.S(1)]] elif numint == 'GaussLegendre3': numint = [[-sym.sqrt(sym.Rational(3,5)), 0, sym.sqrt(sym.Rational(3,5))], [sym.Rational(5,9), sym.Rational(8,9), sym.Rational(5,9)]] elif numint is not None: print 'Numerical rule %s is not supported for symbolic computing' % numint numint = None else: if numint == 'Trapezoidal': numint = [[-1, 1], [1, 1]] elif numint == 'Simpson': numint = [[-1, 0, 1], [1./3, 4./3, 1./3]] elif numint == 'Midpoint': numint = [[0], [2]] elif numint == 'GaussLegendre2': numint = [[-1/sqrt(3), 1/sqrt(3)], [1, 1]] elif numint == 'GaussLegendre3': numint = [[-sqrt(3./5), 0, sqrt(3./5)], [5./9, 8./9, 5./9]] elif numint == 'GaussLegendre4': numint = [[-0.86113631, -0.33998104, 0.33998104, 0.86113631], [ 0.34785485, 0.65214515, 0.65214515, 0.34785485]] elif numint == 'GaussLegendre5': numint = [[-0.90617985, -0.53846931, -0. , 0.53846931, 0.90617985], [ 0.23692689, 0.47862867, 0.56888889, 0.47862867, 0.23692689]] elif numint is not None: print 'Numerical rule %s is not supported for numerical computing' % numint numint = None vertices, cells, dof_map = mesh_uniform(N_e, d, Omega, symbolic) # phi is a list where phi[e] holds the basis in cell no e # (this is required by assemble, which can work with # meshes with different types of elements). # len(dof_map[e]) is the number of nodes in cell e, # and the degree of the polynomial is len(dof_map[e])-1 phi = [basis(len(dof_map[e])-1) for e in range(N_e)] A, b = assemble(vertices, cells, dof_map, phi, f, symbolic=symbolic, numint=numint) print 'cells:', cells print 'vertices:', vertices print 'dof_map:', dof_map print 'A:\n', A print 'b:\n', b #print sym.latex(A, mode='plain') #print sym.latex(b, mode='plain') if symbolic: c = A.LUsolve(b) c = np.asarray([c[i,0] for i in range(c.shape[0])]) else: c = np.linalg.solve(A, b) print 'c:\n', c x = sym.Symbol('x') f = sym.lambdify([x], f, modules='numpy') if collocation and not symbolic: print 'Plain interpolation/collocation:' # Should use vertices, but compute all nodes! f_at_vertices = [f(xc) for xc in vertices] print f_at_vertices if filename is not None: title = 'P%d, N_e=%d' % (d, N_e) if numint is None: title += ', exact integration' else: title += ', integration: %s' % numint_name x_u, u, _ = u_glob(c, vertices, cells, dof_map, resolution_per_element=51) x_f = np.linspace(Omega[0], Omega[1], 10001) # mesh for f import scitools.std as plt plt.plot(x_u, u, '-', x_f, f(x_f), '--') plt.legend(['u', 'f']) plt.title(title) plt.savefig(filename + '.pdf') plt.savefig(filename + '.png') return c