def test_p2d_mass_matrix_shape(self): """ Test mass matrix shape in the pseudo 2-dimensional case """ c = pybamm.Variable("c", domain=["negative particle"]) N = pybamm.grad(c) model = pybamm.BaseModel() model.rhs = {c: pybamm.div(N)} model.initial_conditions = {c: pybamm.Scalar(0)} model.boundary_conditions = { c: { "left": (0, "Dirichlet"), "right": (0, "Dirichlet") } } model.variables = {"c": c, "N": N} mesh = get_p2d_mesh_for_testing() spatial_methods = {"negative particle": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) prim_pts = mesh["negative particle"][0].npts sec_pts = len(mesh["negative particle"]) mass_local = eye(prim_pts) mass = kron(eye(sec_pts), mass_local) np.testing.assert_array_equal(mass.toarray(), model.mass_matrix.entries.toarray())
def test_boundary_value_domain(self): mesh = get_p2d_mesh_for_testing() spatial_methods = { "macroscale": pybamm.FiniteVolume(), "negative particle": pybamm.FiniteVolume(), "positive particle": pybamm.FiniteVolume(), } disc = pybamm.Discretisation(mesh, spatial_methods) c_s_n = pybamm.Variable("c_s_n", domain=["negative particle"]) c_s_p = pybamm.Variable("c_s_p", domain=["positive particle"]) disc.set_variable_slices([c_s_n, c_s_p]) # surface values c_s_n_surf = pybamm.surf(c_s_n) c_s_p_surf = pybamm.surf(c_s_p) # domain for boundary values must now be explicitly set c_s_n_surf_disc = disc.process_symbol(c_s_n_surf) c_s_p_surf_disc = disc.process_symbol(c_s_p_surf) self.assertEqual(c_s_n_surf_disc.domain, []) self.assertEqual(c_s_p_surf_disc.domain, []) c_s_n_surf.domain = ["negative electrode"] c_s_p_surf.domain = ["positive electrode"] c_s_n_surf_disc = disc.process_symbol(c_s_n_surf) c_s_p_surf_disc = disc.process_symbol(c_s_p_surf) self.assertEqual(c_s_n_surf_disc.domain, ["negative electrode"]) self.assertEqual(c_s_p_surf_disc.domain, ["positive electrode"])
def test_p2d_spherical_grad_div_shapes_Neumann_bcs(self): """ Test grad and div with Dirichlet boundary conditions (applied by grad on var) in the pseudo 2-dimensional case """ mesh = get_p2d_mesh_for_testing() spatial_methods = {"negative particle": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) n_mesh = mesh["negative particle"] mesh.add_ghost_meshes() disc.mesh.add_ghost_meshes() # test grad var = pybamm.Variable("var", domain=["negative particle"]) grad_eqn = pybamm.grad(var) disc.set_variable_slices([var]) grad_eqn_disc = disc.process_symbol(grad_eqn) prim_pts = n_mesh[0].npts sec_pts = len(n_mesh) constant_y = np.kron(np.ones(sec_pts), np.ones(prim_pts)) grad_eval = grad_eqn_disc.evaluate(None, constant_y) grad_eval = np.reshape(grad_eval, [sec_pts, prim_pts - 1]) np.testing.assert_array_equal(grad_eval, np.zeros([sec_pts, prim_pts - 1])) # div # div (grad r^2) = 6, N_left = N_right = 0 N = pybamm.grad(var) div_eqn = pybamm.div(N) boundary_conditions = { var.id: { "left": (pybamm.Scalar(0), "Neumann"), "right": (pybamm.Scalar(0), "Neumann"), } } disc.bcs = boundary_conditions div_eqn_disc = disc.process_symbol(div_eqn) const = 6 * np.ones(sec_pts * prim_pts) div_eval = div_eqn_disc.evaluate(None, const) div_eval = np.reshape(div_eval, [sec_pts, prim_pts]) np.testing.assert_array_almost_equal(div_eval, np.zeros([sec_pts, prim_pts]))
def get_error(m): # create mesh and discretisation p2d, uniform in x mesh = get_p2d_mesh_for_testing(3, m) disc = pybamm.Discretisation(mesh, spatial_methods) submesh = mesh["negative particle"] r = submesh[0].nodes r_edge = pybamm.standard_spatial_vars.r_n_edge N = r_edge ** 2 * pybamm.sin(r_edge) div_eqn = pybamm.div(N) # Define exact solutions # N = r**2*sin(r) --> div(N) = 4*r*sin(r) - r**2*cos(r) div_exact = 4 * r * np.sin(r) + r ** 2 * np.cos(r) div_exact = np.kron(np.ones(len(submesh)), div_exact) # Discretise and evaluate div_eqn_disc = disc.process_symbol(div_eqn) div_approx = div_eqn_disc.evaluate() return div_approx[:, 0] - div_exact
def get_error(m): # create mesh and discretisation p2d, x-dependent mesh = get_p2d_mesh_for_testing(6, m) disc = pybamm.Discretisation(mesh, spatial_methods) submesh_r = mesh["negative particle"] r = submesh_r.nodes r_edge = pybamm.standard_spatial_vars.r_n_edge x = pybamm.standard_spatial_vars.x_n N = pybamm.PrimaryBroadcast( x, "negative particle") * (r_edge**2 * pybamm.sin(r_edge)) div_eqn = pybamm.div(N) # Define exact solutions # N = r**2*sin(r) --> div(N) = 4*r*sin(r) - r**2*cos(r) div_exact = 4 * r * np.sin(r) + r**2 * np.cos(r) div_exact = np.kron(mesh["negative electrode"].nodes, div_exact) # Discretise and evaluate div_eqn_disc = disc.process_symbol(div_eqn) div_approx = div_eqn_disc.evaluate() return div_approx[:, 0] - div_exact
def get_error(m): # create mesh and discretisation p2d, x-dependent mesh = get_p2d_mesh_for_testing(6, m) disc = pybamm.Discretisation(mesh, spatial_methods) submesh_r = mesh["negative particle"] r = submesh_r[0].nodes r_edge = submesh_r[0].edges x = pybamm.Vector(mesh["negative electrode"][0].nodes) N = pybamm.Matrix( np.kron(x.entries[:, 0], r_edge**2 * np.sin(r_edge)), domain=["negative particle"], ) div_eqn = pybamm.div(N) # Define exact solutions # N = r**2*sin(r) --> div(N) = 4*r*sin(r) - r**2*cos(r) div_exact = 4 * r * np.sin(r) + r**2 * np.cos(r) div_exact = np.kron(x.entries[:, 0], div_exact) # Discretise and evaluate div_eqn_disc = disc.process_symbol(div_eqn) div_approx = div_eqn_disc.evaluate() return div_approx[:, 0] - div_exact
def test_extrapolate_2d_models(self): # create discretisation mesh = get_p2d_mesh_for_testing() method_options = { "extrapolation": { "order": "linear", "use bcs": False } } spatial_methods = { "macroscale": pybamm.FiniteVolume(method_options), "negative particle": pybamm.FiniteVolume(method_options), "positive particle": pybamm.FiniteVolume(method_options), "current collector": pybamm.FiniteVolume(method_options), } disc = pybamm.Discretisation(mesh, spatial_methods) # Microscale var = pybamm.Variable("var", domain="negative particle") extrap_right = pybamm.BoundaryValue(var, "right") disc.set_variable_slices([var]) extrap_right_disc = disc.process_symbol(extrap_right) self.assertEqual(extrap_right_disc.domain, []) # domain for boundary values must now be explicitly set extrap_right.domain = ["negative electrode"] disc.set_variable_slices([var]) extrap_right_disc = disc.process_symbol(extrap_right) self.assertEqual(extrap_right_disc.domain, ["negative electrode"]) # evaluate y_macro = mesh["negative electrode"][0].nodes y_micro = mesh["negative particle"][0].nodes y = np.outer(y_macro, y_micro).reshape(-1, 1) # extrapolate to r=1 --> should evaluate to y_macro np.testing.assert_array_almost_equal( extrap_right_disc.evaluate(y=y)[:, 0], y_macro) var = pybamm.Variable("var", domain="positive particle") extrap_right = pybamm.BoundaryValue(var, "right") disc.set_variable_slices([var]) extrap_right_disc = disc.process_symbol(extrap_right) self.assertEqual(extrap_right_disc.domain, []) # domain for boundary values must now be explicitly set extrap_right.domain = ["positive electrode"] disc.set_variable_slices([var]) extrap_right_disc = disc.process_symbol(extrap_right) self.assertEqual(extrap_right_disc.domain, ["positive electrode"]) # 2d macroscale mesh = get_1p1d_mesh_for_testing() disc = pybamm.Discretisation(mesh, spatial_methods) var = pybamm.Variable("var", domain="negative electrode") extrap_right = pybamm.BoundaryValue(var, "right") disc.set_variable_slices([var]) extrap_right_disc = disc.process_symbol(extrap_right) self.assertEqual(extrap_right_disc.domain, []) # test extrapolate to "negative tab" gives same as "left" and # "positive tab" gives same "right" (see get_mesh_for_testing) var = pybamm.Variable("var", domain="current collector") disc.set_variable_slices([var]) submesh = mesh["current collector"] constant_y = np.ones_like(submesh[0].nodes[:, np.newaxis]) extrap_neg = pybamm.BoundaryValue(var, "negative tab") extrap_neg_disc = disc.process_symbol(extrap_neg) extrap_left = pybamm.BoundaryValue(var, "left") extrap_left_disc = disc.process_symbol(extrap_left) np.testing.assert_array_equal( extrap_neg_disc.evaluate(None, constant_y), extrap_left_disc.evaluate(None, constant_y), ) extrap_pos = pybamm.BoundaryValue(var, "positive tab") extrap_pos_disc = disc.process_symbol(extrap_pos) extrap_right = pybamm.BoundaryValue(var, "right") extrap_right_disc = disc.process_symbol(extrap_right) np.testing.assert_array_equal( extrap_pos_disc.evaluate(None, constant_y), extrap_right_disc.evaluate(None, constant_y), )
def test_p2d_add_ghost_nodes(self): # create discretisation mesh = get_p2d_mesh_for_testing() spatial_methods = { "macroscale": pybamm.FiniteVolume(), "negative particle": pybamm.FiniteVolume(), "positive particle": pybamm.FiniteVolume(), } disc = pybamm.Discretisation(mesh, spatial_methods) # add ghost nodes c_s_n = pybamm.Variable("c_s_n", domain=["negative particle"]) c_s_p = pybamm.Variable("c_s_p", domain=["positive particle"]) disc.set_variable_slices([c_s_n]) disc_c_s_n = pybamm.StateVector(*disc.y_slices[c_s_n.id]) disc.set_variable_slices([c_s_p]) disc_c_s_p = pybamm.StateVector(*disc.y_slices[c_s_p.id]) bcs = { "left": (pybamm.Scalar(0), "Dirichlet"), "right": (pybamm.Scalar(3), "Dirichlet"), } sp_meth = pybamm.FiniteVolume() sp_meth.build(mesh) c_s_n_plus_ghost, _ = sp_meth.add_ghost_nodes(c_s_n, disc_c_s_n, bcs) c_s_p_plus_ghost, _ = sp_meth.add_ghost_nodes(c_s_p, disc_c_s_p, bcs) mesh_s_n = mesh["negative particle"] mesh_s_p = mesh["positive particle"] n_prim_pts = mesh_s_n[0].npts n_sec_pts = len(mesh_s_n) p_prim_pts = mesh_s_p[0].npts p_sec_pts = len(mesh_s_p) y_s_n_test = np.kron(np.ones(n_sec_pts), np.ones(n_prim_pts)) y_s_p_test = np.kron(np.ones(p_sec_pts), np.ones(p_prim_pts)) # evaluate with and without ghost points c_s_n_eval = disc_c_s_n.evaluate(None, y_s_n_test) c_s_n_ghost_eval = c_s_n_plus_ghost.evaluate(None, y_s_n_test) c_s_p_eval = disc_c_s_p.evaluate(None, y_s_p_test) c_s_p_ghost_eval = c_s_p_plus_ghost.evaluate(None, y_s_p_test) # reshape to make easy to deal with c_s_n_eval = np.reshape(c_s_n_eval, [n_sec_pts, n_prim_pts]) c_s_n_ghost_eval = np.reshape(c_s_n_ghost_eval, [n_sec_pts, n_prim_pts + 2]) c_s_p_eval = np.reshape(c_s_p_eval, [p_sec_pts, p_prim_pts]) c_s_p_ghost_eval = np.reshape(c_s_p_ghost_eval, [p_sec_pts, p_prim_pts + 2]) np.testing.assert_array_equal(c_s_n_ghost_eval[:, 1:-1], c_s_n_eval) np.testing.assert_array_equal(c_s_p_ghost_eval[:, 1:-1], c_s_p_eval) np.testing.assert_array_equal( (c_s_n_ghost_eval[:, 0] + c_s_n_ghost_eval[:, 1]) / 2, 0) np.testing.assert_array_equal( (c_s_p_ghost_eval[:, 0] + c_s_p_ghost_eval[:, 1]) / 2, 0) np.testing.assert_array_equal( (c_s_n_ghost_eval[:, -2] + c_s_n_ghost_eval[:, -1]) / 2, 3) np.testing.assert_array_equal( (c_s_p_ghost_eval[:, -2] + c_s_p_ghost_eval[:, -1]) / 2, 3)