def test_bin_points(self): # # Cell vertices # v1 = Vertex((0, 0)) v2 = Vertex((1, 0)) v3 = Vertex((1, 1)) v4 = Vertex((0, 1)) # Cell HalfEdges h12 = HalfEdge(v1, v2) h23 = HalfEdge(v2, v3) h34 = HalfEdge(v3, v4) h41 = HalfEdge(v4, v1) # Cell cell = QuadCell([h12, h23, h34, h41]) # Split cell twice cell.split() cell.get_child(1).split() # # Error: point not in cell # x = np.array([[-1, -1]]) self.assertRaises(Exception, cell.bin_points, *(x, )) # # Corner points # x = convert_to_array([v1, v2, v3, v4]) bins = cell.bin_points(x) # There should be four bins self.assertEqual(len(bins), 4) # No binning cells should have children for c, dummy in bins: self.assertFalse(c.has_children()) # # Center point # x = np.array([[0.5, 0.5]]) bins = cell.bin_points(x) self.assertEqual(len(bins), 1) # # Mark # sf = '1' for child in cell.get_children(): child.mark(sf) x = np.array([[0.75, 0.25]]) bins = cell.bin_points(x, subforest_flag=sf) for c, dummy in bins: self.assertTrue(c.is_marked(sf)) self.assertFalse(c.has_children(flag=sf))
def test_subcell_position(self): # Define HalfEdge v1, v2 = Vertex((1,1)), Vertex((2,3)) he_ref = HalfEdge(v1,v2) # Define Bad HalfEdge he_bad = HalfEdge(Vertex((1.5,1.3)), Vertex((2,3))) # Assert Error self.assertRaises(Exception, he_ref.subcell_position, he_bad) # Define Good HalfEdge V = convert_to_array([v1,v2]) # Specify points along Half-Edge t_min, t_max = 0.3, 0.5 W_base = Vertex(tuple(V[0] + t_min*(V[1]-V[0]))) W_head = Vertex(tuple(V[0] + t_max*(V[1]-V[0]))) he_good = HalfEdge(W_base, W_head) pos, width = he_ref.subcell_position(he_good) # Check whether computed value matches reference self.assertAlmostEqual(pos, t_min) self.assertAlmostEqual(width, t_max-t_min)
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): # New interval I = Interval(Vertex(2), Vertex(5)) # New point x = np.array([0, 1, 0.5]) # Map point to physical interval y, mg = I.reference_map(x, jac_r2p=True, hess_r2p=True) # Verify jac = mg['jac_r2p'] hess = mg['hess_r2p'] self.assertTrue(type(y) is np.ndarray) self.assertTrue( np.allclose(y, convert_to_array([(2, ), (5, ), (3.5, )], dim=1))) self.assertTrue(type(jac) is list) self.assertEqual(jac, [3, 3, 3]) self.assertTrue(type(hess) is list) self.assertEqual(hess, [0, 0, 0]) # Map back to reference domain xx, mg = I.reference_map(y, jac_p2r=True, hess_p2r=True, mapsto='reference') ijac = mg['jac_p2r'] ihess = mg['hess_p2r'] # Verify for i in range(3): self.assertEqual(xx[i], x[i]) self.assertEqual(ijac[i], 1 / jac[i]) self.assertEqual(ihess[i], hess[i])
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_reference_quadcell(self): """ Test Constructor TODO: - test 1D: - test all reference cells (?) """ # ===================================================================== # Reference Interval/Cell # ===================================================================== # # 1D # # # Check if reference interval contain the correct vertices # etypes = ['DQ0', 'DQ1', 'DQ2', 'DQ3', 'Q1', 'Q2', 'Q3'] dv = dict.fromkeys(etypes) dv['DQ0'] = {0: [0.5], 1: {0: [0.25], 1: [0.75]}} dv['DQ1'] = {0: [0, 1], 1: {0: [0, 0.5], 1: [0.5, 1]}} dv['DQ2'] = {0: [0, 1, 0.5], 1: {0: [0, 0.5, 0.25], 1: [0.5, 1, 0.75]}} dv['DQ3'] = { 0: [0, 1, 1 / 3, 2 / 3], 1: { 0: [0, 0.5, 1 / 6, 1 / 3], 1: [0.5, 1, 2 / 3, 5 / 6] } } dv['Q1'] = {0: [0, 1], 1: {0: [0, 0.5], 1: [0.5, 1]}} dv['Q2'] = {0: [0, 1, 0.5], 1: {0: [0, 0.5, 0.25], 1: [0.5, 1, 0.75]}} dv['Q3'] = { 0: [0, 1, 1 / 3, 2 / 3], 1: { 0: [0, 0.5, 1 / 6, 1 / 3], 1: [0.5, 1, 2 / 3, 5 / 6] } } for etype in etypes: element = QuadFE(1, etype) cell = element.reference_cell() for level in range(2): if level == 0: v = convert_to_array(cell.get_dof_vertices(level)) self.assertTrue( np.allclose(np.array(dv[etype][level]), v[:, 0])) elif level == 1: for child in range(2): v = convert_to_array( cell.get_dof_vertices(level, child)) self.assertTrue( np.allclose(np.array(dv[etype][level][child]), v[:, 0])) # # Check if the vertices on the fine level are correctly linked with those on the coarse level # # TODO: # # Test Positions # for etype in ['DQ0', 'DQ1', 'DQ2', 'DQ3', 'Q1', 'Q2', 'Q3']: element = QuadFE(2, etype) cell = element.reference_cell() if etype == 'Q1' or etype == 'DQ1': # Level 1, child 0, vertex 0 = Level 0, vertex 0 vertex = cell.get_dof_vertices(1, 0, 0) self.assertEqual(vertex.get_pos(1, 0), vertex.get_pos(0)) # Level 1, child 2, vertex 2 = Level 0, vertex 1 vertex = cell.get_dof_vertices(1, 2, 2) self.assertEqual(vertex.get_pos(1, 2), vertex.get_pos(0)) # Level 1, child 2, vertex 0 has no inherited vertex vertex = cell.get_dof_vertices(1, 2, 0) self.assertIsNone(vertex.get_pos(0)) elif etype == 'Q2': # All children share middle vertex for i in range(4): vertex = cell.get_dof_vertices(1, i, (i + 2) % 4) self.assertEqual(vertex.get_pos(0), 8) elif etype == 'DQ2': # Only first child shares middle vertex for i in range(4): vertex = cell.get_dof_vertices(1, i, (i + 2) % 4) if i == 0: self.assertEqual(vertex.get_pos(0), 8) else: self.assertIsNone(vertex.get_pos(0)) # Level 1, child 1, vertex 0 has no inherited vertex vertex = cell.get_dof_vertices(1, 1, 0) self.assertIsNone(vertex.get_pos(0)) # Level 1, child 1, vertex 5 has no inherited vertex vertex = cell.get_dof_vertices(1, 1, 5) self.assertIsNone(vertex.get_pos(0)) elif etype == 'Q3' or etype == 'DQ3': # Level 1, child 2, vertex 2 = Level 0, vertex 2 vertex = cell.get_dof_vertices(1, 2, 2) self.assertEqual(vertex.get_pos(1, 2), vertex.get_pos(0)) # Level 1, child 2, vertex 6 = Level 0, vertex 7 vertex = cell.get_dof_vertices(1, 2, 6) self.assertEqual(vertex.get_pos(0), 7) # Level 1, child 2, vertex 12 = Level 0, vertex 15 vertex = cell.get_dof_vertices(1, 2, 12) self.assertEqual(vertex.get_pos(0), 15)
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))
def add_dirichlet_constraint(self, bnd_marker, dirichlet_function=0, on_boundary=True): """ Modify an assembled bilinear/linear pair to account for Dirichlet boundary conditions. The system matrix is modified "in place", i.e. a11 a12 a13 a14 u1 b1 a21 a22 a23 a24 u2 = b2 a31 a32 a33 a34 u3 b3 a41 a42 a43 a44 u4 b4 Suppose Dirichlet conditions u2=g2 and u4=g4 are prescribed. The system is converted to a11 0 a13 0 u1 b1 - a12*g2 - a14*g4 0 1 0 0 u2 = 0 a31 0 a33 0 u3 b3 - a32*g2 - a34*g4 0 0 0 1 u4 0 The solution [u1,u3]^T of this system is then enlarged with the dirichlet boundary values g2 and g4 by invoking 'resolve_constraints' Inputs: bnd_marker: str/int flag to identify boundary dirichlet_function: Function, defining the Dirichlet boundary conditions. on_boundary: bool, True if function values are prescribed on boundary. Notes: To maintain the dimensions of the matrix, the trial and test function spaces must be the same, i.e. it must be a Galerkin approximation. Specifying the Dirichlet conditions this way is necessary if there are hanging nodes, since a Dirichlet node may be a supporting node for one of the hanging nodes. Inputs: bnd_marker: flag, used to mark the Dirichlet boundary dirichlet_fn: Function, specifying the function values on the Dirichlet boundary. Outputs: None Modified Attributes: __A: modify Dirichlet rows and colums (shrink) __b: modify right hand side (shrink) dirichlet: add dictionary, {mask: np.ndarray, vals: np.ndarray} """ # # Get Dofs Associated with Dirichlet boundary # subforest_flag = self.get_basis().subforest_flag() dh = self.get_dofhandler() if dh.mesh.dim()==1: # # One dimensional mesh # dirichlet_dofs = dh.get_region_dofs(entity_type='vertex', \ entity_flag=bnd_marker,\ interior=False, \ on_boundary=on_boundary,\ subforest_flag=subforest_flag) elif dh.mesh.dim()==2: # # Two dimensional mesh # dirichlet_dofs = dh.get_region_dofs(entity_type='half_edge', entity_flag=bnd_marker, interior=False, on_boundary=on_boundary, \ subforest_flag=subforest_flag) # # Evaluate dirichlet function at vertices associated with dirichlet dofs # dirichlet_vertices = dh.get_dof_vertices(dirichlet_dofs) if isinstance(dirichlet_function, numbers.Number): # # Dirichlet function is constant # n_dirichlet = len(dirichlet_dofs) if dirichlet_function==0: # # Homogeneous boundary conditions # dirichlet_vals = np.zeros(n_dirichlet) else: # # Non-homogeneous, constant boundary conditions # dirichlet_vals = dirichlet_function*np.ones(n_dirichlet) else: # # Nonhomogeneous, nonconstant Dirichlet boundary conditions # x_dir = convert_to_array(dirichlet_vertices) dirichlet_vals = dirichlet_function.eval(x_dir).ravel() constraints = dh.constraints for dof, val in zip(dirichlet_dofs, dirichlet_vals): constraints['constrained_dofs'].append(dof) constraints['supporting_dofs'].append([]) constraints['coefficients'].append([]) constraints['affine_terms'].append(val)
def test_eval(self): # # Out of the box covariance kernels # fig, ax = plt.subplots(6, 4, figsize=(5, 7)) cov_names = [ 'constant', 'linear', 'gaussian', 'exponential', 'matern', 'rational' ] anisotropies = {1: [None, 2], 2: [None, np.diag([2, 1])]} m_count = 0 for mesh in [Mesh1D(resolution=(10, )), QuadMesh(resolution=(10, 10))]: # Dimension dim = mesh.dim() # # Construct computational mesh # # Piecewise constant elements element = QuadFE(dim, 'DQ0') # Define dofhandler -> get vertices dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() dofhandler.set_dof_vertices() v = dofhandler.get_dof_vertices() # Define meshgrid for 1 and 2 dimensions n_dofs = dofhandler.n_dofs() M1, M2 = np.mgrid[0:n_dofs, 0:n_dofs] if dim == 1: X = v[:, 0][M1].ravel() Y = v[:, 0][M2].ravel() elif dim == 2: X = np.array([v[:, 0][M1].ravel(), v[:, 1][M1].ravel()]).T Y = np.array([v[:, 0][M2].ravel(), v[:, 1][M2].ravel()]).T x = convert_to_array(X, dim=dim) y = convert_to_array(Y, dim=dim) a_count = 0 isotropic_label = ['isotropic', 'anisotropic'] for M in anisotropies[dim]: # Cycle through anisotropies # Define covariance parameters cov_pars = { 'constant': { 'sgm': 1 }, 'linear': { 'sgm': 1, 'M': M }, 'gaussian': { 'sgm': 1, 'l': 0.1, 'M': M }, 'exponential': { 'l': 0.1, 'M': M }, 'matern': { 'sgm': 1, 'nu': 2, 'l': 0.5, 'M': M }, 'rational': { 'a': 3, 'M': M } } c_count = 0 for cov_name in cov_names: C = CovKernel(cov_name, cov_pars[cov_name]) Z = C.eval((x, y)).reshape(M1.shape) col = int(m_count * 2**1 + a_count * 2**0) row = c_count ax[row, col].imshow(Z) if col == 0: ax[row, col].set_ylabel(cov_name) if row == 0: ax[row, col].set_title('%dD mesh\n %s' % (dim, isotropic_label[a_count])) ax[row, col].set_xticks([], []) ax[row, col].set_yticks([], []) c_count += 1 a_count += 1 m_count += 1 fig.savefig('test_covkernel_eval.eps')