def runTest(self): @LinearForm def load(v, w): x = w.x return (np.sin(np.pi * x[0]) * np.sin(np.pi * x[1]) * (2.0 * np.pi**2) * v) m = self.mesh Nitrs = 9 L2s = np.zeros(Nitrs) H1s = np.zeros(Nitrs) for itr in range(Nitrs): ib = self.create_basis(m, itr + 2) A = asm(laplace, ib) b = asm(load, ib) D = ib.find_dofs() x = solve(*condense(A, b, D=D)) # calculate error L2s[itr], H1s[itr] = self.compute_error(m, ib, x) self.assertLess(H1s[-1], 1e-10) self.assertLess(L2s[-1], 1e-11)
def runTest(self): m = MeshTri() fbasis1 = FacetBasis(m, ElementTriP1() * ElementTriP1(), facets=m.facets_satisfying(lambda x: x[0] == 0)) fbasis2 = FacetBasis(m, ElementTriP1(), facets=lambda x: x[0] == 0) fbasis3 = FacetBasis(m, ElementTriP1(), facets='left') @BilinearForm def uv1(u, p, v, q, w): return u * v + p * q @BilinearForm def uv2(u, v, w): return u * v A = asm(uv1, fbasis1) B = asm(uv2, fbasis2) C = asm(uv2, fbasis2) assert_allclose(A[0].todense()[0, ::2], B[0].todense()[0]) assert_allclose(A[0].todense()[0, ::2], C[0].todense()[0])
def runTest(self): self.case[0].refine() if self.intorder is not None: basis = FacetBasis(*self.case, intorder=self.intorder) else: basis = FacetBasis(*self.case) @linear_form def linf(v, dv, w): return np.sum(w.n**2, axis=0) * v b = asm(linf, basis) m = self.case[0] self.assertAlmostEqual(b @ np.ones(b.shape), 2 * m.p.shape[0], places=10) if self.test_integrate_volume: # by Gauss theorem this integrates to one for itr in range(m.p.shape[0]): @linear_form def linf(v, dv, w): return w.n[itr] * v b = asm(linf, basis) self.assertAlmostEqual(b @ m.p[itr, :], 1.0, places=5)
def save_nullspaces(): # points, cells = meshzoo.rectangle_tri((0.0, 0.0), (1.0, 1.0), 20) points, cells = meshzoo.disk(6, 20) @fem.BilinearForm def flux(u, v, w): return dot(w.n, u.grad) * v mesh = fem.MeshTri(points.T, cells.T) basis = fem.InteriorBasis(mesh, fem.ElementTriP1()) facet_basis = fem.FacetBasis(basis.mesh, basis.elem) lap = fem.asm(laplace, basis) boundary_terms = fem.asm(flux, facet_basis) A_dense = (lap - boundary_terms).toarray() # the right null space are the affine-linear functions rns = scipy.linalg.null_space(A_dense).T mesh.save("nullspace-right.vtk", point_data={ "k0": rns[0], "k1": rns[1], "k2": rns[2] }) # the left null space is a bit weird; something around the boundaries lns = scipy.linalg.null_space(A_dense.T).T mesh.save("nullspace-left.vtk", point_data={ "k0": lns[0], "k1": lns[1], "k2": lns[2] })
def runTest(self): m = self.case[0].refined() if self.intorder is not None: basis = FacetBasis(m, self.case[1], intorder=self.intorder) else: basis = FacetBasis(m, self.case[1]) @LinearForm def linf(v, w): return np.sum(w.n**2, axis=0) * v b = asm(linf, basis) ones = projection(lambda x: 1.0 + x[0] * 0., basis, I=basis.get_dofs().flatten(), expand=True) self.assertAlmostEqual(b @ ones, 2 * m.p.shape[0], places=10) if self.test_integrate_volume: # by Gauss theorem this integrates to one for itr in range(m.p.shape[0]): @LinearForm def linf(v, w): return w.n[itr] * v b = asm(linf, basis) self.assertAlmostEqual(b @ m.p[itr, :], 1.0, places=5)
def runTest(self): self.createBasis() @BilinearForm def uv(u, v, w): return u * v B = asm(uv, self.fbasis) # assemble the same matrix using multiple threads @BilinearForm(nthreads=2) def uvt(u, v, w): return u * v Bt = asm(uvt, self.fbasis) @LinearForm def gv(v, w): return 1.0 * v g = asm(gv, self.fbasis) ones = np.ones(g.shape) self.assertAlmostEqual(ones @ g, self.boundary_area, places=4) self.assertAlmostEqual(ones @ (B @ ones), self.boundary_area, places=4) self.assertAlmostEqual(ones @ (Bt @ ones), self.boundary_area, places=4)
def get_poisson_steps_scikitfem(points, cells, tol): from skfem import ( MeshTri, MeshTet, InteriorBasis, ElementTriP1, ElementTetP1, asm, condense, ) from skfem.models.poisson import laplace, unit_load import krypy if cells.shape[1] == 3: mesh = MeshTri(points.T, cells.T) e = ElementTriP1() else: assert cells.shape[1] == 4 mesh = MeshTet(points.T, cells.T) e = ElementTetP1() basis = InteriorBasis(mesh, e) # assemble A = asm(laplace, basis) b = asm(unit_load, basis) A, b = condense(A, b, I=mesh.interior_nodes(), expand=False) _, info = krypy.cg(A, b, tol=tol) return info.iter
def assemble_mass_form(self, symbol, boundary_conditions, region="interior"): """ Assembles the form of the finite element mass matrix over the domain interior or boundary. Parameters ---------- symbol: :class:`pybamm.Variable` The variable corresponding to the equation for which we are calculating the mass matrix. boundary_conditions : dict The boundary conditions of the model ({symbol.id: {"negative tab": neg. tab bc, "positive tab": pos. tab bc}}) region: str, optional The domain over which to assemble the mass matrix form. Can be "interior" (default) or "boundary". Returns ------- :class:`pybamm.Matrix` The (sparse) mass matrix for the spatial method. """ # get primary domain mesh domain = symbol.domain[0] mesh = self.mesh[domain][0] # create form for mass @skfem.bilinear_form def mass_form(u, du, v, dv, w): return u * v # assemble mass matrix if region == "interior": mass = skfem.asm(mass_form, mesh.basis) if region == "boundary": mass = skfem.asm(mass_form, mesh.facet_basis) # get boundary conditions and type if symbol.id in boundary_conditions: _, neg_bc_type = boundary_conditions[symbol.id]["negative tab"] _, pos_bc_type = boundary_conditions[symbol.id]["positive tab"] if neg_bc_type == "Dirichlet": # set source terms to zero on boundary by zeroing out mass matrix self.bc_apply(mass, mesh.negative_tab_dofs, zero=True) if pos_bc_type == "Dirichlet": # set source terms to zero on boundary by zeroing out mass matrix self.bc_apply(mass, mesh.positive_tab_dofs, zero=True) return pybamm.Matrix(mass)
def runTest(self): """Solve Stokes problem, try splitting and other small things.""" m = MeshTri().refined() m = m.refined(3).with_boundaries({ 'up': lambda x: x[1] == 1., 'rest': lambda x: x[1] != 1., }) e = ElementVectorH1(ElementTriP2()) * ElementTriP1() basis = CellBasis(m, e) @BilinearForm def bilinf(u, p, v, q, w): from skfem.helpers import grad, ddot, div return (ddot(grad(u), grad(v)) - div(u) * q - div(v) * p - 1e-2 * p * q) S = asm(bilinf, basis) D = basis.find_dofs(skip=['u^2']) x = basis.zeros() x[D['up'].all('u^1^1')] = .1 x = solve(*condense(S, x=x, D=D)) (u, u_basis), (p, p_basis) = basis.split(x) self.assertEqual(len(u), m.p.shape[1] * 2 + m.facets.shape[1] * 2) self.assertEqual(len(p), m.p.shape[1]) self.assertTrue(np.sum(p - x[basis.nodal_dofs[2]]) < 1e-8) U, P = basis.interpolate(x) self.assertTrue(isinstance(U.value, np.ndarray)) self.assertTrue(isinstance(P.value, np.ndarray)) self.assertTrue(P.shape[0] == m.nelements) self.assertTrue((basis.doflocs[:, D['up'].all()][1] == 1.).all()) # test blocks splitting of forms while at it C1 = asm(bilinf.block(1, 1), CellBasis(m, ElementTriP1())) C2 = S[basis.nodal_dofs[-1]].T[basis.nodal_dofs[-1]].T self.assertTrue(abs((C1 - C2).min()) < 1e-10) self.assertTrue(abs((C1 - C2).max()) < 1e-10) # test splitting ElementVector (ux, uxbasis), (uy, uybasis) = u_basis.split(u) assert_allclose(ux[uxbasis.nodal_dofs[0]], u[u_basis.nodal_dofs[0]]) assert_allclose(ux[uxbasis.facet_dofs[0]], u[u_basis.facet_dofs[0]]) assert_allclose(uy[uybasis.nodal_dofs[0]], u[u_basis.nodal_dofs[1]]) assert_allclose(uy[uybasis.facet_dofs[0]], u[u_basis.facet_dofs[1]])
def _fit_skfem(x0, y0, points, cells, lmbda: float, degree: int = 1, solver: str = "lsqr"): import skfem from skfem.helpers import dot from skfem.models.poisson import laplace assert degree == 1 if cells.shape[1] == 2: mesh = skfem.MeshLine(np.ascontiguousarray(points.T), np.ascontiguousarray(cells.T)) element = skfem.ElementLineP1() elif cells.shape[1] == 3: mesh = skfem.MeshTri(np.ascontiguousarray(points.T), np.ascontiguousarray(cells.T)) element = skfem.ElementTriP1() else: assert cells.shape[1] == 4 mesh = skfem.MeshQuad(np.ascontiguousarray(points.T), np.ascontiguousarray(cells.T)) element = skfem.ElementQuad1() @skfem.BilinearForm def mass(u, v, _): return u * v @skfem.BilinearForm def flux(u, v, w): return dot(w.n, u.grad) * v basis = skfem.CellBasis(mesh, element) facet_basis = skfem.FacetBasis(basis.mesh, basis.elem) lap = skfem.asm(laplace, basis) boundary_terms = skfem.asm(flux, facet_basis) A = lap - boundary_terms A *= lmbda # get the evaluation matrix E = basis.probes(x0.T) # mass matrix M = skfem.asm(mass, basis) x = _solve(A, M, E, y0, solver) return basis, x
def initOnes(self, basis): @bilinear_form def mass(u, du, ddu, v, dv, ddv, w): return u * v @linear_form def ones(v, dv, ddv, w): return 1.0 * v M = asm(mass, basis) f = asm(ones, basis) return solve(M, f)
def initOnes(self, basis): @BilinearForm def mass(u, v, w): return u * v @LinearForm def ones(v, w): return 1. * v M = asm(mass, basis) f = asm(ones, basis) return solve(M, f)
def boundary_integral_vector(self, domain, region): """A node in the expression tree representing an integral operator over the boundary of a domain .. math:: I = \\int_{\\partial a}\\!f(u)\\,du, where :math:`\\partial a` is the boundary of the domain, and :math:`u\\in\\text{domain boundary}`. Parameters ---------- domain : list The domain(s) of the variable in the integrand region : str The region of the boundary over which to integrate. If region is `entire` the integration is carried out over the entire boundary. If region is `negative tab` or `positive tab` then the integration is only carried out over the appropriate part of the boundary corresponding to the tab. Returns ------- :class:`pybamm.Matrix` The finite element integral vector for the domain """ # get primary domain mesh if isinstance(domain, list): domain = domain[0] mesh = self.mesh[domain][0] # make form for the boundary integral @skfem.linear_form def integral_form(v, dv, w): return v if region == "entire": # assemble over all facets integration_vector = skfem.asm(integral_form, mesh.facet_basis) elif region == "negative tab": # assemble over negative tab facets integration_vector = skfem.asm(integral_form, mesh.negative_tab_basis) elif region == "positive tab": # assemble over positive tab facets integration_vector = skfem.asm(integral_form, mesh.positive_tab_basis) return pybamm.Matrix(integration_vector[np.newaxis, :])
def runTest(self): m = MeshLine(np.linspace(0., 1.)).refined(2) ib = InteriorBasis(m, self.e) fb = FacetBasis(m, self.e) @LinearForm def boundary_flux(v, w): return v * (w.x[0] == 1.) L = asm(laplace, ib) b = asm(boundary_flux, fb) D = m.nodes_satisfying(lambda x: x == 0.0) I = ib.complement_dofs(D) # noqa E741 u = solve(*condense(L, b, I=I)) # noqa E741 np.testing.assert_array_almost_equal(u[ib.nodal_dofs[0]], m.p[0], -10)
def runTest(self): m = MeshLine(np.linspace(0., 1.)).refined(2) ib = InteriorBasis(m, self.e) fb = FacetBasis(m, self.e) @LinearForm def boundary_flux(v, w): return v * (w.x[0] == 1) - v * (w.x[0] == 0) L = asm(laplace, ib) M = asm(mass, ib) b = asm(boundary_flux, fb) u = solve(L + 1e-6 * M, b) np.testing.assert_array_almost_equal(u[ib.nodal_dofs[0]], m.p[0] - .5, -4)
def runTest(self): @LinearForm def load(v, w): x = w.x if x.shape[0] == 1: return (np.sin(np.pi * x[0]) * (np.pi**2) * v) elif x.shape[0] == 2: return (np.sin(np.pi * x[0]) * np.sin(np.pi * x[1]) * (2.0 * np.pi**2) * v) elif x.shape[0] == 3: return (np.sin(np.pi * x[0]) * np.sin(np.pi * x[1]) * np.sin(np.pi * x[2]) * (3.0 * np.pi**2) * v) else: raise Exception("The dimension not supported") m = self.mesh Nitrs = 3 L2s = np.zeros(Nitrs) H1s = np.zeros(Nitrs) hs = np.zeros(Nitrs) for itr in range(Nitrs): if itr > 0: m = self.do_refined(m, itr) ib = self.create_basis(m) A = asm(laplace, ib) b = asm(load, ib) D = self.get_bc_nodes(ib) x = solve(*condense(A, b, D=D)) # calculate error L2s[itr], H1s[itr] = self.compute_error(m, ib, x) hs[itr] = m.param() rateL2 = np.polyfit(np.log(hs), np.log(L2s), 1)[0] rateH1 = np.polyfit(np.log(hs), np.log(H1s), 1)[0] self.assertLess(np.abs(rateL2 - self.rateL2), self.eps, msg='observed L2 rate: {}'.format(rateL2)) self.assertLess(np.abs(rateH1 - self.rateH1), self.eps, msg='observed H1 rate: {}'.format(rateH1)) self.assertLess(H1s[-1], 0.3) self.assertLess(L2s[-1], 0.008)
def test_point_source(etype): mesh = MeshLine1().refined() basis = CellBasis(mesh, etype()) source = np.array([0.7]) u = solve(*condense(asm(laplace, basis), basis.point_source(source), D=basis.get_dofs())) exact = np.stack([(1 - source) * mesh.p, (1 - mesh.p) * source]).min(0) assert_almost_equal(u[basis.nodal_dofs], exact)
def runTest(self): m = MeshLine(np.linspace(0., 1.)) m.refine(2) e = ElementLineP1() ib = InteriorBasis(m, e) fb = FacetBasis(m, e) @linear_form def boundary_flux(v, dv, w): return v * (w.x[0] == 1) - v * (w.x[0] == 0) L = asm(laplace, ib) M = asm(mass, ib) b = asm(boundary_flux, fb) u = solve(L + 1e-6 * M, b) self.assertTrue(np.sum(np.abs(u - m.p[0, :] + 0.5)) < 1e-4)
def runTest(self): m = self.case[0]() m.refine(self.prerefs) hs = [] L2s = [] for itr in range(3): e = self.case[1]() ib = InteriorBasis(m, e) @BilinearForm def bilinf(u, v, w): return ddot(dd(u), dd(v)) @LinearForm def linf(v, w): return 1. * v K = asm(bilinf, ib) f = asm(linf, ib) x = solve(*condense(K, f, D=ib.get_dofs().all())) X = ib.interpolate(x) def exact(x): return (x ** 2 - 2. * x ** 3 + x ** 4) / 24. @Functional def error(w): return (w.w - exact(w.x)) ** 2 L2 = np.sqrt(error.assemble(ib, w=X)) L2s.append(L2) hs.append(m.param()) m.refine() hs = np.array(hs) L2s = np.array(L2s) pfit = np.polyfit(np.log10(hs), np.log10(L2s), 1) self.assertGreater(pfit[0], self.limits[0]) self.assertLess(pfit[0], self.limits[1]) self.assertLess(L2s[-1], self.abs_limit)
def test_multimesh_2(): m = MeshTri() m1 = MeshTri.init_refdom() m2 = MeshTri.init_refdom().scaled((-1., -1.)).translated(( 1., 1., )) M = m1 @ m2 E = [ElementTriP1(), ElementTriP1()] basis1 = list(map(Basis, M, E)) basis2 = Basis(m, ElementTriP1()) M1 = asm(mass, basis1) M2 = asm(mass, basis2) assert_array_almost_equal(M1.toarray(), M2.toarray())
def runTest(self): m = MeshLine(np.linspace(0., 1.)).refined(2) ib = InteriorBasis(m, self.e) m.define_boundary('left', lambda x: x[0] == 0.0) m.define_boundary('right', lambda x: x[0] == 1.0) fb = FacetBasis(m, self.e, facets=m.boundaries['right']) @LinearForm def boundary_flux(v, w): return -w.x[0] * v L = asm(laplace, ib) b = asm(boundary_flux, fb) D = ib.find_dofs()['left'].all() I = ib.complement_dofs(D) # noqa E741 u = solve(*condense(L, b, I=I)) # noqa E741 np.testing.assert_array_almost_equal(u[ib.nodal_dofs[0]], -m.p[0], -10)
def runTest(self): m = MeshLine(np.linspace(0., 1.)) m.refine(2) e = ElementLineP1() ib = InteriorBasis(m, e) fb = FacetBasis(m, e) @linear_form def boundary_flux(v, dv, w): return -v * (w.x[0] == 1.) L = asm(laplace, ib) b = asm(boundary_flux, fb) D = m.nodes_satisfying(lambda x: x == 0.0) I = ib.complement_dofs(D) # noqa E741 u = solve(*condense(L, b, I=I)) # noqa E741 self.assertTrue(np.sum(np.abs(u + m.p[0, :])) < 1e-10)
def gradient(self, symbol, discretised_symbol, boundary_conditions): """Matrix-vector multiplication to implement the gradient operator. The gradient w of the function u is approximated by the finite element method using the same function space as u, i.e. we solve w = grad(u), which corresponds to the weak form w*v*dx = grad(u)*v*dx, where v is a suitable test function. Parameters ---------- symbol: :class:`pybamm.Symbol` The symbol that we will take the laplacian of. discretised_symbol: :class:`pybamm.Symbol` The discretised symbol of the correct size boundary_conditions : dict The boundary conditions of the model ({symbol.id: {"negative tab": neg. tab bc, "positive tab": pos. tab bc}}) Returns ------- :class: `pybamm.Concatenation` A concatenation that contains the result of acting the discretised gradient on the child discretised_symbol. The first column corresponds to the y-component of the gradient and the second column corresponds to the z component of the gradient. """ domain = symbol.domain[0] mesh = self.mesh[domain][0] # get gradient matrix grad_y_matrix, grad_z_matrix = self.gradient_matrix( symbol, boundary_conditions) # assemble mass matrix (there is no need to zero out entries here, since # boundary conditions are already accounted for in the governing pde # for the symbol we are taking the gradient of. we just want to get the # correct weights) @skfem.bilinear_form def mass_form(u, du, v, dv, w): return u * v mass = skfem.asm(mass_form, mesh.basis) # we need the inverse mass_inv = pybamm.Matrix(inv(csc_matrix(mass))) # compute gradient grad_y = mass_inv @ (grad_y_matrix @ discretised_symbol) grad_z = mass_inv @ (grad_z_matrix @ discretised_symbol) # create concatenation grad = pybamm.Concatenation(grad_y, grad_z, check_domain=False, concat_fun=np.hstack) grad.domain = domain return grad
def runTest(self): self.createBasis() @BilinearForm def uv(u, v, w): return u * v B = asm(uv, self.fbasis) @LinearForm def gv(v, w): return 1.0 * v g = asm(gv, self.fbasis) ones = np.ones(g.shape) self.assertAlmostEqual(ones @ g, self.boundary_area, places=4) self.assertAlmostEqual(ones @ (B @ ones), self.boundary_area, places=4)
def runTest(self): m = MeshTri() e = ElementTriP1() basis = Basis(m, e) self.interior_area = 1 @BilinearForm(dtype=np.complex64) def complexmass(u, v, w): return 1j * u * v @LinearForm(dtype=np.complex64) def complexfun(v, w): return 1j * v M = asm(complexmass, basis) f = asm(complexfun, basis) ones = np.ones(M.shape[1]) self.assertAlmostEqual(np.dot(ones, M @ ones), 1j * self.interior_area) self.assertAlmostEqual(np.dot(ones, f), 1j * self.interior_area)
def runTest(self): m = MeshHex() # check that these assemble to the same matrix ec = ElementHex1() * ElementHex1() * ElementHex1() ev = ElementVectorH1(ElementHex1()) basisc = Basis(m, ec) basisv = Basis(m, ev) @BilinearForm def bilinf_ev(u, v, w): from skfem.helpers import dot return dot(u, v) @BilinearForm def bilinf_ec(ux, uy, uz, vx, vy, vz, w): return ux * vx + uy * vy + uz * vz Kv = asm(bilinf_ev, basisv) Kc = asm(bilinf_ec, basisc) self.assertAlmostEqual(np.sum(np.sum((Kv - Kc).todense())), 0.)
def runTest(self): m = MeshQuad().refined(3) e = ElementQuad1() basis = InteriorBasis(m, e) @Functional def x_squared(w): return w.x[0]**2 y = asm(x_squared, basis) self.assertAlmostEqual(y, 1. / 3.) self.assertEqual(len(x_squared.elemental(basis)), m.t.shape[1])
def test_evaluate_functional(mtype, e, mtype2): m = mtype().refined(3) if mtype2 is not None: m = mtype2.from_mesh(m) basis = Basis(m, e) @Functional def x_squared(w): return w.x[0]**2 y = asm(x_squared, basis) assert_almost_equal(y, 1. / 3.) assert_equal(len(x_squared.elemental(basis)), m.t.shape[1])
def test_multimesh(): m1 = MeshTri().refined() m2 = MeshQuad().refined().translated((1., 0.)) m3 = MeshTri().refined().translated((2., 0.)) M = m1 @ m2 @ m3 assert len(M) == 3 E = [ElementTriP1(), ElementQuad1(), ElementTriP1()] basis = list(map(Basis, M, E)) Mm = asm(mass, basis) assert Mm.shape[0] == 3 * 7
def runTest(self): mtype, etype = self.case m = mtype().refined(3) bnd = m.facets_satisfying(lambda x: x[0] == 1.0) fb = FacetBasis(m, etype(), facets=bnd) @BilinearForm def uv(u, v, w): x, y, z = w.x return x**2 * y**2 * z**2 * u * v B = asm(uv, fb) ones = np.ones(B.shape[0]) self.assertAlmostEqual(ones @ (B @ ones), 0.11111111, places=5)