def test_boundary_integral(self): mesh = get_2p1d_mesh_for_testing(include_particles=False) spatial_methods = { "macroscale": pybamm.FiniteVolume(), "current collector": pybamm.ScikitFiniteElement(), } disc = pybamm.Discretisation(mesh, spatial_methods) var = pybamm.Variable("var", domain="current collector") disc.set_variable_slices([var]) full = pybamm.BoundaryIntegral(var) neg = pybamm.BoundaryIntegral(var, region="negative tab") pos = pybamm.BoundaryIntegral(var, region="positive tab") full_disc = disc.process_symbol(full) neg_disc = disc.process_symbol(neg) pos_disc = disc.process_symbol(pos) # check integrating 1 gives correct *dimensionless* region lengths perimeter = 2 * (1 + 0.8) l_tab_n = 0.1 / 0.5 l_tab_p = 0.1 / 0.5 constant_y = np.ones(mesh["current collector"].npts) # Integral around boundary is exact np.testing.assert_array_almost_equal( full_disc.evaluate(None, constant_y), perimeter ) # Ideally mesh edges should line up with tab edges.... then we would get # better agreement between actual and numerical tab width np.testing.assert_array_almost_equal( neg_disc.evaluate(None, constant_y), l_tab_n, decimal=1 ) np.testing.assert_array_almost_equal( pos_disc.evaluate(None, constant_y), l_tab_p, decimal=1 )
def test_evaluates_on_edges(self): a = pybamm.StateVector(slice(0, 10), domain="test") self.assertFalse(pybamm.Index(a, slice(1)).evaluates_on_edges("primary")) self.assertFalse(pybamm.Laplacian(a).evaluates_on_edges("primary")) self.assertFalse(pybamm.GradientSquared(a).evaluates_on_edges("primary")) self.assertFalse(pybamm.BoundaryIntegral(a).evaluates_on_edges("primary")) self.assertTrue(pybamm.Upwind(a).evaluates_on_edges("primary")) self.assertTrue(pybamm.Downwind(a).evaluates_on_edges("primary"))
def set_boundary_conditions(self, variables): phi_s_cn = variables["Negative current collector potential"] phi_s_cp = variables["Positive current collector potential"] param = self.param applied_current = variables["Total current density"] cc_area = self._get_effective_current_collector_area() # Note: we divide by the *numerical* tab area so that the correct total # current is applied. That is, numerically integrating the current density # around the boundary gives the applied current exactly. positive_tab_area = pybamm.BoundaryIntegral( pybamm.PrimaryBroadcast(param.l_cp, "current collector"), region="positive tab", ) # cc_area appears here due to choice of non-dimensionalisation pos_tab_bc = ( -applied_current * cc_area / (param.sigma_cp * param.delta ** 2 * positive_tab_area) ) # Boundary condition needs to be on the variables that go into the Laplacian, # even though phi_s_cp isn't a pybamm.Variable object # In the 2+1D model, the equations for the current collector potentials # are solved on a 2D domain and the regions "negative tab" and "positive tab" # are the projections of the tabs onto this 2D domain. # In the 2D formulation it is assumed that no flux boundary conditions # are applied everywhere apart from the tabs. # The reference potential is taken to be zero on the negative tab, # giving the zero Dirichlet condition on phi_s_cn. Elsewhere, the boundary # is insulated, giving no flux conditions on phi_s_cn. This is automatically # applied everywhere, apart from the region corresponding to the projection # of the positive tab, so we need to explitly apply a zero-flux boundary # condition on the region "positive tab" for phi_s_cn. # A current is drawn from the positive tab, giving the non-zero Neumann # boundary condition on phi_s_cp at "positive tab". Elsewhere, the boundary is # insulated, so, as with phi_s_cn, we need to explicitly give the zero-flux # condition on the region "negative tab" for phi_s_cp. self.boundary_conditions = { phi_s_cn: { "negative tab": (pybamm.Scalar(0), "Dirichlet"), "positive tab": (pybamm.Scalar(0), "Neumann"), }, phi_s_cp: { "negative tab": (pybamm.Scalar(0), "Neumann"), "positive tab": (pos_tab_bc, "Neumann"), }, }
def __init__(self): super().__init__() self.name = "Effective resistance in current collector model (2D)" self.param = pybamm.standard_parameters_lithium_ion # Get necessary parameters param = self.param l_cn = param.l_cn l_cp = param.l_cp l_tab_p = param.l_tab_p A_tab_p = l_cp * l_tab_p sigma_cn_dbl_prime = param.sigma_cn_dbl_prime sigma_cp_dbl_prime = param.sigma_cp_dbl_prime delta = param.delta # Set model variables -- we solve a auxilliary problem in each current collector # then relate this to the potentials and resistances later f_n = pybamm.Variable("Unit solution in negative current collector", domain="current collector") f_p = pybamm.Variable("Unit solution in positive current collector", domain="current collector") # Governing equations -- we impose that the average of f_p is zero # by introducing a Lagrange multiplier c = pybamm.Variable("Lagrange multiplier") self.algebraic = { f_n: pybamm.laplacian(f_n) + pybamm.source(1, f_n), c: pybamm.laplacian(f_p) - pybamm.source(1, f_p) + c * pybamm.DefiniteIntegralVector(f_p, vector_type="column"), f_p: pybamm.yz_average(f_p) + 0 * c, } # Boundary conditons pos_tab_bc = l_cp / A_tab_p self.boundary_conditions = { f_n: { "negative tab": (0, "Dirichlet"), "positive tab": (0, "Neumann") }, f_p: { "negative tab": (0, "Neumann"), "positive tab": (pos_tab_bc, "Neumann"), }, } # "Initial conditions" provides initial guess for solver self.initial_conditions = { f_n: pybamm.Scalar(0), f_p: pybamm.Scalar(0), c: pybamm.Scalar(0), } # Define effective current collector resistance R_cc_n = delta * pybamm.yz_average(f_n) / (l_cn * sigma_cn_dbl_prime) R_cc_p = (delta * pybamm.BoundaryIntegral(f_p, "positive tab") / (l_cp * sigma_cp_dbl_prime)) R_cc = R_cc_n + R_cc_p R_scale = param.potential_scale / param.I_typ self.variables = { "Unit solution in negative current collector": f_n, "Unit solution in positive current collector": f_p, "Effective current collector resistance": R_cc, "Effective current collector resistance [Ohm]": R_cc * R_scale, "Effective negative current collector resistance": R_cc_n, "Effective negative current collector resistance [Ohm]": R_cc_n * R_scale, "Effective positive current collector resistance": R_cc_p, "Effective positive current collector resistance [Ohm]": R_cc_p * R_scale, } # Add spatial variables var = pybamm.standard_spatial_vars L_y = pybamm.geometric_parameters.L_y L_z = pybamm.geometric_parameters.L_z self.variables.update({ "y": var.y, "y [m]": var.y * L_y, "z": var.z, "z [m]": var.z * L_z }) pybamm.citations.register("timms2020")