def test_line_integral(self): # Define quadrature rule rule = GaussRule(2, shape='interval') w = rule.weights() x_ref = rule.nodes() # function f to be integrated over edge e f = lambda x, y: x**2 * y e = HalfEdge(Vertex((0, 0)), Vertex((1, 1))) # Map rule to physical entity x_phys, mg = e.reference_map(x_ref, jac_r2p=True) jacobian = mg['jac_r2p'] x_phys = convert_to_array(x_phys, dim=2) fvec = f(x_phys[:, 0], x_phys[:, 1]) jac = np.linalg.norm(jacobian[0]) self.assertAlmostEqual(np.dot(fvec,w)*jac,np.sqrt(2)/4,places=10,\ msg='Failed to integrate x^2y.') self.assertAlmostEqual(np.sum(w)*jac, np.sqrt(2), places=10,\ msg='Failed to integrate 1.')
def test_reference_map(self): v_sw = Vertex((0, 0)) v_se = Vertex((3, 1)) v_ne = Vertex((2, 3)) v_nw = Vertex((-1, 1)) h12 = HalfEdge(v_sw, v_se) h23 = HalfEdge(v_se, v_ne) h34 = HalfEdge(v_ne, v_nw) h41 = HalfEdge(v_nw, v_sw) cell = QuadCell([h12, h23, h34, h41]) # # Map corner vertices of reference cell to physical vertices # y_refs = np.array([[0, 0], [1, 0], [1, 1], [0, 1]]) x = list(convert_to_array(cell.get_vertices())) x_phys = cell.reference_map(list(y_refs)) self.assertTrue(np.allclose(np.array(x),x_phys),\ 'Mapped vertices should coincide '+\ 'with physical cell vertices.') # # Jacobian: Area of cell by integration # rule_2d = GaussRule(order=4, shape='quadrilateral') r = rule_2d.nodes() wg = rule_2d.weights() dummy, mg = cell.reference_map(list(r), jac_r2p=True) jac = mg['jac_r2p'] area = 0 for i in range(4): j = jac[i] w = wg[i] area += np.abs(np.linalg.det(j)) * w self.assertAlmostEqual(cell.area(), area, 7,\ 'Area computed via numerical quadrature '+\ 'not close to actual area') # # Try different formats # # Array x = np.array(x) x_ref = cell.reference_map(x, mapsto='reference') self.assertTrue(np.allclose(y_refs, np.array(x_ref)),\ 'Map array to reference: incorrect output.') # Single point x = x[0, :] x_ref = cell.reference_map(x, mapsto='reference') self.assertTrue(np.allclose(x, x_ref)) # # Map corner vertices to reference points # x = convert_to_array(cell.get_vertices()) y = cell.reference_map(x, mapsto='reference') self.assertTrue(np.allclose(y, y_refs), \ 'Corner vertices should map '+\ 'onto (0,0),(1,0),(1,1),(0,1).') # # Map random points in [0,1]^2 onto cell and back again # # Generate random points t = np.random.rand(5) s = np.random.rand(5) x = np.array([s, t]).T # Map to physical cell x_phy = cell.reference_map(x) # Check whether points are contained in cell in_cell = cell.contains_points(x_phy) self.assertTrue(all(in_cell), \ 'All points mapped from [0,1]^2 '+\ 'should be contained in the cell.') # Map back to reference cell x_ref = cell.reference_map(x_phy, mapsto='reference') self.assertTrue(np.allclose(np.array(x_ref), np.array(x)),\ 'Points mapped to physical cell and back should '+\ 'be unchanged.') # # Compute the hessian and compare with finite difference approximation # h = 1e-8 x = np.array([[0.5, 0.5], [0.5 + h, 0.5], [0.5 - h, 0.5], [0.5, 0.5 + h], [0.5, 0.5 - h]]) x_ref, mg = cell.reference_map(x, mapsto='reference', hess_p2r=True, jac_p2r=True) J = mg['jac_p2r'] H = mg['hess_p2r'] # sxx sxx_fd = (J[1][0, 0] - J[2][0, 0]) / (2 * h) sxx = H[0][0, 0, 0] self.assertAlmostEqual(sxx_fd, sxx, 7, \ 'Hessian calculation not close to '+\ 'finite difference approximation') # syx syx_fd = (J[1][0, 1] - J[2][0, 1]) / (2 * h) sxy = H[0][0, 1, 0] syx = H[0][1, 0, 0] self.assertAlmostEqual(sxy, syx, 7, 'Mixed derivatives not equal.') self.assertAlmostEqual(syx_fd, sxy, 7, \ 'Hessian calculation not close to '+\ 'finite difference approximation') # syy syy_fd = (J[3][0, 1] - J[4][0, 1]) / (2 * h) syy = H[0][1, 1, 0] self.assertAlmostEqual(syy_fd, syy, 7, \ 'Hessian calculation not close to '+\ 'finite difference approximation') # txx txx_fd = (J[1][1, 0] - J[2][1, 0]) / (2 * h) txx = H[0][0, 0, 1] self.assertAlmostEqual(txx_fd, txx, 7, \ 'Hessian calculation not close to '+\ 'finite difference approximation') # txy txy_fd = (J[3][1, 0] - J[4][1, 0]) / (2 * h) txy = H[0][0, 1, 1] tyx = H[0][1, 0, 1] self.assertAlmostEqual(txy, tyx, 7, 'Mixed derivatives not equal.') self.assertAlmostEqual(txy_fd, txy, 7, \ 'Hessian calculation not close to '+\ 'finite difference approximation') # tyy tyy_fd = (J[3][1, 1] - J[4][1, 1]) / (2 * h) tyy = H[0][1, 1, 1] self.assertAlmostEqual(tyy_fd, tyy, 7, \ 'Hessian calculation not close to '+\ 'finite difference approximation')
def test_constructor(self): # ===================================================================== # Test 1D # ===================================================================== # # Kernel consists of a single explicit Function: # f1 = lambda x: x+2 f = Explicit(f1, dim=1) k = Kernel(f) x = np.linspace(0,1,100) n_points = len(x) # Check that it evaluates correctly. self.assertTrue(np.allclose(f1(x), k.eval(x).ravel())) # Check shape of kernel self.assertEqual(k.eval(x).shape, (n_points,1)) # # Kernel consists of a combination of two explicit functions # f1 = Explicit(lambda x: x+2, dim=1) f2 = Explicit(lambda x: x**2 + 1, dim=1) F = lambda f1, f2: f1**2 + f2 f_t = lambda x: (x+2)**2 + x**2 + 1 k = Kernel([f1,f2], F=F) # Check evaluation self.assertTrue(np.allclose(f_t(x), k.eval(x).ravel())) # Check shape self.assertEqual(k.eval(x).shape, (n_points,1)) # # Same thing as above, but with nodal functions # mesh = Mesh1D(resolution=(1,)) Q1 = QuadFE(1,'Q1') Q2 = QuadFE(1,'Q2') dQ1 = DofHandler(mesh,Q1) dQ2 = DofHandler(mesh,Q2) # Distribute dofs [dQ.distribute_dofs() for dQ in [dQ1,dQ2]] # Basis functions phi1 = Basis(dQ1,'u') phi2 = Basis(dQ2,'u') f1 = Nodal(lambda x: x+2, basis=phi1) f2 = Nodal(lambda x: x**2 + 1, basis=phi2) k = Kernel([f1,f2], F=F) # Check evaluation self.assertTrue(np.allclose(f_t(x), k.eval(x).ravel())) # # Replace f2 above with its derivative # k = Kernel([f1,f2], derivatives=['f', 'fx'], F=F) f_t = lambda x: (x+2)**2 + 2*x # Check derivative evaluation F = F(f1, df2_dx) self.assertTrue(np.allclose(f_t(x), k.eval(x).ravel())) # # Sampling # one = Constant(1) f1 = Explicit(lambda x: x**2 + 1, dim=1) # Sampled function a = np.linspace(0,1,11) n_samples = len(a) # Define Dofhandler dh = DofHandler(mesh, Q2) dh.distribute_dofs() dh.set_dof_vertices() xv = dh.get_dof_vertices() n_dofs = dh.n_dofs() phi = Basis(dh, 'u') # Evaluate parameterized function at mesh dof vertices f2_m = np.empty((n_dofs, n_samples)) for i in range(n_samples): f2_m[:,i] = xv.ravel() + a[i]*xv.ravel()**2 f2 = Nodal(data=f2_m, basis=phi) # Define kernel F = lambda f1, f2, one: f1 + f2 + one k = Kernel([f1,f2,one], F=F) # Evaluate on a fine mesh x = np.linspace(0,1,100) n_points = len(x) self.assertEqual(k.eval(x).shape, (n_points, n_samples)) for i in range(n_samples): # Check evaluation self.assertTrue(np.allclose(k.eval(x)[:,i], f1.eval(x)[:,i] + x + a[i]*x**2+ 1)) # # Sample multiple constant functions # f1 = Constant(data=a) f2 = Explicit(lambda x: 1 + x**2, dim=1) f3 = Nodal(data=f2_m[:,-1], basis=phi) F = lambda f1, f2, f3: f1 + f2 + f3 k = Kernel([f1,f2,f3], F=F) x = np.linspace(0,1,100) for i in range(n_samples): self.assertTrue(np.allclose(k.eval(x)[:,i], \ a[i] + f2.eval(x)[:,i] + f3.eval(x)[:,i])) # # Submeshes # mesh = Mesh1D(resolution=(1,)) mesh_labels = Tree(regular=False) mesh = Mesh1D(resolution=(1,)) Q1 = QuadFE(1,'Q1') Q2 = QuadFE(1,'Q2') dQ1 = DofHandler(mesh,Q1) dQ2 = DofHandler(mesh,Q2) # Distribute dofs [dQ.distribute_dofs() for dQ in [dQ1,dQ2]] # Basis p1 = Basis(dQ1) p2 = Basis(dQ2) f1 = Nodal(lambda x: x, basis=p1) f2 = Nodal(lambda x: -2+2*x**2, basis=p2) one = Constant(np.array([1,2])) F = lambda f1, f2, one: 2*f1**2 + f2 + one I = mesh.cells.get_child(0) kernel = Kernel([f1,f2, one], F=F) rule1D = GaussRule(5,shape='interval') x = I.reference_map(rule1D.nodes())
def test_accuracy(self): """ Test the Accuracy of the Gauss rule on the reference cell 1D Rule: (Interval) Number of Nodes: 1,2,3,4,5,6 Accuracy: 2N-1 2D Rule (Quadrilateral): Number of Nodes: 1,4,9,16,25,36 Accuracy: (2n-1, 2n-1), where n = sqrt(N) TODO: 2D Rule (Triangle) """ # # 1D Polynomials and exact integrals # order_1d = [1, 2, 3, 4, 5, 6] polynomials_1d = { 1: lambda x: 2 * np.ones(x.shape), 2: lambda x: x**3 - 2 * x**2 + x + 1, 3: lambda x: x**5 - 2 * x**2 + 2, 4: lambda x: x**7 + 2 * x**6 + 3 * x, 5: lambda x: x**9 + 3 * x**4 + 1, 6: lambda x: x**11 - 3 * x**8 + 1 } integrals_1d = { 1: 2, 2: 13 / 12, 3: 3 / 2, 4: 107 / 56, 5: 17 / 10, 6: 3 / 4 } # # 2D Polynomials and exact integrals # order_2d = [1, 4, 9, 16, 25, 36] polynomials_2d = { 1: lambda x, y: np.ones(x.shape), 4: lambda x, y: x * y + x + y, 9: lambda x, y: (x**5 - 2 * x**2 + 2) * (y**4 - 2 * y), 16: lambda x, y: (x**7 + 2 * x**6 + 3 * x) * (y**5 + 2 * y**2), 25: lambda x, y: (x**9 + 3 * x**4 + 1) * (y**9 + 3 * y**4 + 1), 36: lambda x, y: (x**11 - 3 * x**8 + 1) * (y**11 + 2 * y) } integrals_2d = { 1: 1, 4: 5 / 4, 9: -6 / 5, 16: 535 / 336, 25: 289 / 100, 36: 13 / 16 } # Combine information order = {1: order_1d, 2: order_2d} polynomials = {1: polynomials_1d, 2: polynomials_2d} integrals = {1: integrals_1d, 2: integrals_2d} # # Iterate over dimensions # for dim in [1, 2]: element = QuadFE(dim, 'Q1') # # Iterate over orders # for n in order[dim]: # Define Gauss Rule rule = GaussRule(n, element) x = rule.nodes() w = rule.weights() # Compute approximate integral f = polynomials[dim][n] if dim == 1: Ia = np.dot(w, f(x)) elif dim == 2: Ia = np.dot(w, f(x[:, 0], x[:, 1])) # Compute exact integral Ie = integrals[dim][n] # Compare self.assertAlmostEqual(Ia, Ie, 10)
def test_shape(self): """ Test shape functions """ test_functions = {'Q1': (lambda x,y: (x+1)*(y-1), lambda x,y: y-1, \ lambda x,y: x+1), 'Q2': (lambda x,y: x**2 -1, lambda x,y: 2*x, \ lambda x,y: 0*x), 'Q3': (lambda x,y: x**3 - y**3, lambda x,y: 3*x**2, \ lambda x,y: -3*y**2)} # # Over reference cell # cell_integrals = { 'Q1': [-0.75, -0.5, 1.5], 'Q2': [-2 / 3., 1.0, 0.0], 'Q3': [0., 1.0, -1.0] } derivatives = [(0, ), (1, 0), (1, 1)] for etype in ['Q1', 'Q2', 'Q3']: element = QuadFE(2, etype) n_dofs = element.n_dofs() x_ref = element.reference_nodes() # # Sanity check # I = np.eye(n_dofs) self.assertTrue(np.allclose(element.shape(x_ref),I),\ 'Shape functions incorrect at reference nodes.') y = np.random.rand(5, 2) rule2d = GaussRule(9, element=element) weights = rule2d.weights() x_gauss = rule2d.nodes() f_nodes = test_functions[etype][0](x_ref[:, 0], x_ref[:, 1]) for i in range(3): phi = element.shape(y, derivatives=derivatives[i]) f = test_functions[etype][i] # # Interpolation # fvals = f(y[:, 0], y[:, 1]) self.assertTrue(np.allclose(np.dot(phi,f_nodes),fvals),\ 'Shape function interpolation failed.') # # Integration # phi = element.shape(x_gauss, derivatives=derivatives[i]) self.assertAlmostEqual(np.dot(weights,np.dot(phi,f_nodes)),\ cell_integrals[etype][i],places=8,\ msg='Incorrect integral.') # On Physical cell # # Non-rectangular quadcell # test_functions['Q1'] = (lambda x, y: (x + 1) + (y - 1), lambda x, y: np.ones(x.shape), lambda x, y: np.ones(y.shape)) # Vertices v0 = Vertex((0, 0)) v1 = Vertex((0.5, 0.5)) v2 = Vertex((0, 2)) v3 = Vertex((-0.5, 0.5)) # half_edges h01 = HalfEdge(v0, v1) h12 = HalfEdge(v1, v2) h23 = HalfEdge(v2, v3) h30 = HalfEdge(v3, v0) # quadcell cell = QuadCell([h01, h12, h23, h30]) for etype in ['Q1', 'Q2', 'Q3']: element = QuadFE(2, etype) n_dofs = element.n_dofs() x_ref = element.reference_nodes() x, mg = cell.reference_map(x_ref, jac_p2r=True, hess_p2r=True) # # Sanity check # I = np.eye(n_dofs) shape = element.shape(x_ref, cell, jac_p2r=mg['jac_p2r'], hess_p2r=mg['hess_p2r']) self.assertTrue(np.allclose(shape,I),\ 'Shape functions incorrect at reference nodes.') # Random points in the reference domain y_ref = np.random.rand(5, 2) y, mg = cell.reference_map(y_ref, jac_p2r=True, hess_p2r=True) self.assertTrue(all(cell.contains_points(y)),\ 'Cell should contain all mapped points') f_nodes = test_functions[etype][0](x[:, 0], x[:, 1]) for i in range(3): phi = element.shape(y_ref, cell=cell, derivatives=derivatives[i], jac_p2r=mg['jac_p2r'], hess_p2r=mg['hess_p2r']) f = test_functions[etype][i] # # Interpolation # fvals = f(y[:, 0], y[:, 1]) self.assertTrue(np.allclose(np.dot(phi,f_nodes),fvals),\ 'Shape function interpolation failed.')
def test_shape_eval(self): """ Routine evaluates all shape functions on given cell """ # # Diamond-shaped region # # Vertices A = Vertex((0, -1)) B = Vertex((1, 0)) C = Vertex((0, 1)) D = Vertex((-1, 0)) # Half-edges AB = HalfEdge(A, B) BC = HalfEdge(B, C) CD = HalfEdge(C, D) DA = HalfEdge(D, A) # Cell cell = QuadCell([AB, BC, CD, DA]) # 1D Quadrature Rule rule = GaussRule(4, shape='interval') xx = rule.nodes() ww = rule.weights() # # Map rule to physical domain (CD) # xg, mg = CD.reference_map(xx, jac_r2p=True) xg = convert_to_array(xg) # Modify weights jac = mg['jac_r2p'] wg = ww * np.array(np.linalg.norm(jac[0])) # Check length of edge is sqrt(2) self.assertTrue(np.allclose(np.sum(wg), np.sqrt(2))) # Check Int x^2 y on CD f = lambda x, y: x**2 * y fx = f(xg[:, 0], xg[:, 1]) self.assertTrue(np.allclose(np.sum(fx * wg), np.sqrt(2) * (1 / 12))) # # Use shape functions to evaluate line integral # # # Map 1D rule onto reference cell # Q = QuadFE(2, 'Q1') rcell = Q.reference_cell() # Determine equivalent Half-edge on reference element i_he = cell.get_half_edges().index(CD) ref_he = rcell.get_half_edge(i_he) # Get 2D reference nodes b, h = convert_to_array(ref_he.get_vertices()) x_ref = np.array([b[i] + xx * (h[i] - b[i]) for i in range(2)]).T # Map 2D reference point to phyisical cell xxg, mg = cell.reference_map(x_ref, jac_r2p=False, jac_p2r=True, hess_p2r=True) self.assertTrue(np.allclose(xxg, xg)) # Evaluate the shape functions phi = Q.shape(x_ref, cell, [(0, ), (1, 0), (1, 1)], mg['jac_p2r']) # x = phi1 - phi3 self.assertTrue(np.allclose(phi[0][:, 1] - phi[0][:, 3], xg[:, 0])) # y = -phi0 + phi2 self.assertTrue(np.allclose(-phi[0][:, 0] + phi[0][:, 2], xg[:, 1])) # g(x,y) = x - y = phi*[1,1,-1-1] c = np.array([1, 1, -1, -1]) g = phi[0].dot(c) # Int_CD x-y ds self.assertTrue(np.allclose(np.sum(wg * g), -np.sqrt(2))) # Integrals involving phi_x, phi_y gx = phi[1].dot(c) gy = phi[2].dot(c) n = CD.unit_normal() self.assertTrue(np.allclose(np.sum(wg * (gx * n[0] + gy * n[1])), -2))
] cell = QuadCell(halfedges) cell.split() plot = Plot(quickview=False) fig, ax = plt.subplots(1, 1) # Plot cell and children for c in cell.traverse(): vertices = [v.coordinates() for v in c.get_vertices()] poly = plt.Polygon(vertices, fc=clrs.to_rgba('w'), edgecolor=(0, 0, 0, 1)) ax.add_patch(poly) # Define Gauss Rule child = cell.get_child(0) gauss = GaussRule(4, shape='quadrilateral') # Map rule to sw child xg, wg, mg = gauss.mapped_rule(child, jac_p2r=True, hess_p2r=True) plt.plot(xg[:, 0], xg[:, 1], '.k') # Subdivide reference cell with same tree structure r0, r1, r2, r3 = Vertex((0, 0)), Vertex((1, 0)), Vertex((1, 1)), Vertex((0, 1)) half_edges = [ HalfEdge(r0, r1), HalfEdge(r1, r2), HalfEdge(r2, r3), HalfEdge(r3, r0) ]