Buckets:
| from fe_approx1D import basis, affine_mapping | |
| import sys | |
| from math import sqrt | |
| import numpy as np | |
| import sympy as sym | |
| """ | |
| This module extends and replaces functions in the module fe_approx1D. | |
| Two major changes are implemented: | |
| * an element is defined in terms of a reference cell, | |
| a set of vertices, a set of degrees of freedom, | |
| with a dof map and a geometric mapping onto the physical | |
| space | |
| * numerical integration (Midpoint, Trapezoidal, Simpson | |
| rules) can be used in the reference cell | |
| """ | |
| def mesh_uniform(N_e, d, Omega=[0,1], symbolic=False): | |
| """ | |
| Return a 1D finite element mesh on Omega with N_e elements of | |
| the polynomial degree d. The elements have uniform length. | |
| Return vertices (vertices), local vertex to global | |
| vertex mapping (cells), and local to global degree of freedom | |
| mapping (dof_map). | |
| If symbolic is True, the vertices are expressed as rational | |
| sympy expressions with the symbol h as element length. | |
| """ | |
| if symbolic: | |
| h = sym.Symbol('h') # element length | |
| dx = h*sym.Rational(1, d) # node spacing | |
| vertices = [Omega[0] + i*dx for i in range(N_e + 1)] | |
| else: | |
| vertices = np.linspace(Omega[0], Omega[1], N_e + 1).tolist() | |
| if d == 0: | |
| dof_map = [[e] for e in range(N_e)] | |
| else: | |
| dof_map = [[e*d + i for i in range(d+1)] for e in range(N_e)] | |
| cells = [[e, e+1] for e in range(N_e)] | |
| return vertices, cells, dof_map | |
| def element_matrix(phi, Omega_e, symbolic=True, numint=None): | |
| n = len(phi) | |
| A_e = sym.zeros((n, n)) | |
| X = sym.Symbol('X') | |
| if symbolic: | |
| h = sym.Symbol('h') | |
| else: | |
| h = Omega_e[1] - Omega_e[0] | |
| detJ = h/2 # dx/dX | |
| if numint is None: | |
| for r in range(n): | |
| for s in range(r, n): | |
| A_e[r,s] = sym.integrate(phi[r]*phi[s]*detJ, (X, -1, 1)) | |
| A_e[s,r] = A_e[r,s] | |
| else: | |
| # symbolic=False means phi is function | |
| for r in range(n): | |
| for s in range(r, n): | |
| for j in range(len(numint[0])): | |
| Xj, wj = numint[0][j], numint[1][j] | |
| A_e[r,s] += phi[r](Xj)*phi[s](Xj)*detJ*wj | |
| A_e[s,r] = A_e[r,s] | |
| return A_e | |
| def element_vector(f, phi, Omega_e, symbolic=True, numint=None): | |
| n = len(phi) | |
| b_e = sym.zeros((n, 1)) | |
| # Make f a function of X (via f.subs to avoid real numbers from lambdify) | |
| X = sym.Symbol('X') | |
| if symbolic: | |
| h = sym.Symbol('h') | |
| else: | |
| h = Omega_e[1] - Omega_e[0] | |
| x = (Omega_e[0] + Omega_e[1])/2 + h/2*X # mapping | |
| f = f.subs('x', x) | |
| detJ = h/2 | |
| if numint is None: | |
| for r in range(n): | |
| if symbolic: | |
| I = sym.integrate(f*phi[r]*detJ, (X, -1, 1)) | |
| if not symbolic or isinstance(I, sym.Integral): | |
| # Ensure h is numerical | |
| h = Omega_e[1] - Omega_e[0] | |
| detJ = h/2 | |
| f_func = lambdify([X], f) | |
| # phi is function | |
| integrand = lambda X: f_func(X)*phi[r](X)*detJ | |
| #integrand = integrand.subs(sym.pi, np.pi) | |
| # integrand may still contain symbols like sym.pi that | |
| # prevents numerical evaluation... | |
| try: | |
| I = sym.mpmath.quad(integrand, [-1, 1]) | |
| except Exception as e: | |
| print 'Could not integrate f*phi[r] numerically:' | |
| print e | |
| sys.exit(0) | |
| b_e[r] = I | |
| else: | |
| #phi = [sym.lambdify([X], phi[r]) for r in range(n)] | |
| # f contains h from the mapping, substitute X with Xj | |
| # instead of f = sym.lambdify([X], f) | |
| for r in range(n): | |
| for j in range(len(numint[0])): | |
| Xj, wj = numint[0][j], numint[1][j] | |
| fj = f.subs(X, Xj) | |
| b_e[r] += fj*phi[r](Xj)*detJ*wj | |
| return b_e | |
| def exemplify_element_matrix_vector(f, d, symbolic=True, numint=False): | |
| Omega_e = [0.1, 0.2] | |
| A_e = element_matrix(phi, Omega_e=Omega_e, | |
| symbolic=symbolic, numint=numint) | |
| integration_msg = """ | |
| Symbolic integration failed, and then numerical integration | |
| encountered an undefined symbol (because of the symbolic expressions): | |
| %s""" | |
| if symbolic: | |
| h = sym.Symbol('h') | |
| Omega_e=[1*h, 2*h] | |
| try: | |
| b_e = element_vector(f, phi, Omega_e=Omega_e, | |
| symbolic=symbolic, numint=numint) | |
| except NameError as e: | |
| raise NameError(integration_msg % e) | |
| print 'Element matrix:\n', A_e | |
| print 'Element vector:\n', b_e | |
| def assemble(vertices, cells, dof_map, phi, f, | |
| symbolic=True, numint=None): | |
| import sets | |
| N_n = len(list(set(np.array(dof_map).ravel()))) | |
| N_e = len(cells) | |
| if symbolic: | |
| A = sym.zeros((N_n, N_n)) | |
| b = sym.zeros((N_n, 1)) # note: (N_n, 1) matrix | |
| else: | |
| A = np.zeros((N_n, N_n)) | |
| b = np.zeros(N_n) | |
| for e in range(N_e): | |
| Omega_e = [vertices[cells[e][0]], vertices[cells[e][1]]] | |
| A_e = element_matrix(phi[e], Omega_e, symbolic, numint) | |
| b_e = element_vector(f, phi[e], Omega_e, symbolic, numint) | |
| #print 'element', e | |
| #print b_e | |
| for r in range(len(dof_map[e])): | |
| for s in range(len(dof_map[e])): | |
| A[dof_map[e][r],dof_map[e][s]] += A_e[r,s] | |
| b[dof_map[e][r]] += b_e[r] | |
| return A, b | |
| 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 | |
| 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 | |
| if __name__ == '__main__': | |
| pass | |
Xet Storage Details
- Size:
- 11.3 kB
- Xet hash:
- d31ed6578bd7650efd93c9614e6b23080d4996ce3ed30a4a9556aa2651376e36
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.