def test_yz_average(self): a = pybamm.Scalar(1) z_average_a = pybamm.z_average(a) yz_average_a = pybamm.yz_average(a) self.assertEqual(z_average_a.id, a.id) self.assertEqual(yz_average_a.id, a.id) z_average_broad_a = pybamm.z_average( pybamm.PrimaryBroadcast(a, ["current collector"])) yz_average_broad_a = pybamm.yz_average( pybamm.PrimaryBroadcast(a, ["current collector"])) self.assertEqual(z_average_broad_a.evaluate(), np.array([1])) self.assertEqual(yz_average_broad_a.evaluate(), np.array([1])) a = pybamm.Variable("a", domain=["current collector"]) y = pybamm.SpatialVariable("y", ["current collector"]) z = pybamm.SpatialVariable("z", ["current collector"]) z_av_a = pybamm.z_average(a) yz_av_a = pybamm.yz_average(a) self.assertIsInstance(yz_av_a, pybamm.Division) self.assertIsInstance(z_av_a, pybamm.Division) self.assertIsInstance(z_av_a.children[0], pybamm.Integral) self.assertIsInstance(yz_av_a.children[0], pybamm.Integral) self.assertEqual(z_av_a.children[0].integration_variable[0].domain, z.domain) self.assertEqual(yz_av_a.children[0].integration_variable[0].domain, y.domain) self.assertEqual(yz_av_a.children[0].integration_variable[1].domain, z.domain) self.assertIsInstance(z_av_a.children[1], pybamm.Integral) self.assertIsInstance(yz_av_a.children[1], pybamm.Integral) self.assertEqual(z_av_a.children[1].integration_variable[0].domain, a.domain) self.assertEqual(z_av_a.children[1].children[0].id, pybamm.ones_like(a).id) self.assertEqual(yz_av_a.children[1].integration_variable[0].domain, y.domain) self.assertEqual(yz_av_a.children[1].integration_variable[0].domain, z.domain) self.assertEqual(yz_av_a.children[1].children[0].id, pybamm.ones_like(a).id) self.assertEqual(z_av_a.domain, []) self.assertEqual(yz_av_a.domain, []) a = pybamm.Symbol("a", domain="bad domain") with self.assertRaises(pybamm.DomainError): pybamm.z_average(a) with self.assertRaises(pybamm.DomainError): pybamm.yz_average(a) # average of symbol that evaluates on edges raises error symbol_on_edges = pybamm.PrimaryBroadcastToEdges(1, "domain") with self.assertRaisesRegex( ValueError, "Can't take the z-average of a symbol that evaluates on edges" ): pybamm.z_average(symbol_on_edges)
def get_fundamental_variables(self): # Get necessary parameters param = self.param l_cn = param.l_cn l_cp = param.l_cp sigma_cn_dbl_prime = param.sigma_cn_dbl_prime sigma_cp_dbl_prime = param.sigma_cp_dbl_prime delta = param.delta # aspect ratio # Set model variables: Note: we solve using a scaled version that is # better conditioned R_cn_scaled = pybamm.Variable( "Scaled negative current collector resistance", domain="current collector" ) R_cp_scaled = pybamm.Variable( "Scaled positive current collector resistance", domain="current collector" ) R_cn = delta * R_cn_scaled / (l_cn * sigma_cn_dbl_prime) R_cp = delta * R_cp_scaled / (l_cp * sigma_cp_dbl_prime) # Define effective current collector resistance if self.options["dimensionality"] == 1: R_cc_n = -pybamm.z_average(R_cn) R_cc_p = -pybamm.z_average(R_cp) elif self.options["dimensionality"] == 2: R_cc_n = -pybamm.yz_average(R_cn) R_cc_p = -pybamm.yz_average(R_cp) R_cc = R_cc_n + R_cc_p R_scale = param.potential_scale / param.I_typ variables = { "Scaled negative current collector resistance": R_cn_scaled, "Negative current collector resistance": R_cn, "Negative current collector resistance [Ohm]": R_cn * R_scale, "Scaled positive current collector resistance": R_cp_scaled, "Positive current collector resistance": R_cp, "Positive current collector resistance [Ohm]": R_cp * R_scale, "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 if self.options["dimensionality"] == 1: variables.update({"z": var.z, "z [m]": var.z * L_z}) elif self.options["dimensionality"] == 2: variables.update( {"y": var.y, "y [m]": var.y * L_y, "z": var.z, "z [m]": var.z * L_z} ) return variables
def _yz_average(self, var): "Computes the y-z average" # TODO: change the behaviour of z_average and yz_average so the if statement # can be removed if self.cc_dimension in [0, 1]: return pybamm.z_average(var) elif self.cc_dimension == 2: return pybamm.yz_average(var)
def test_yz_average(self): a = pybamm.Scalar(1) z_average_a = pybamm.z_average(a) yz_average_a = pybamm.yz_average(a) self.assertEqual(z_average_a.id, a.id) self.assertEqual(yz_average_a.id, a.id) z_average_broad_a = pybamm.z_average( pybamm.Broadcast(a, ["current collector"])) yz_average_broad_a = pybamm.yz_average( pybamm.Broadcast(a, ["current collector"])) self.assertEqual(z_average_broad_a.evaluate(), np.array([1])) self.assertEqual(yz_average_broad_a.evaluate(), np.array([1])) a = pybamm.Symbol("a", domain=["current collector"]) y = pybamm.SpatialVariable("y", ["current collector"]) z = pybamm.SpatialVariable("z", ["current collector"]) z_av_a = pybamm.z_average(a) yz_av_a = pybamm.yz_average(a) self.assertIsInstance(z_av_a, pybamm.Division) self.assertIsInstance(yz_av_a, pybamm.Division) self.assertIsInstance(z_av_a.children[0], pybamm.Integral) self.assertIsInstance(yz_av_a.children[0], pybamm.Integral) self.assertEqual(z_av_a.children[0].integration_variable[0].domain, z.domain) self.assertEqual(yz_av_a.children[0].integration_variable[0].domain, y.domain) self.assertEqual(yz_av_a.children[0].integration_variable[1].domain, z.domain) self.assertEqual(z_av_a.domain, []) self.assertEqual(yz_av_a.domain, []) a = pybamm.Symbol("a", domain="bad domain") with self.assertRaises(pybamm.DomainError): pybamm.z_average(a) with self.assertRaises(pybamm.DomainError): pybamm.yz_average(a)
def test_averages(self): # create discretisation disc = get_discretisation_for_testing( cc_method=pybamm.ZeroDimensionalSpatialMethod) # create and discretise variable var = pybamm.Variable("var", domain="current collector") disc.set_variable_slices([var]) var_disc = disc.process_symbol(var) # check average returns the same value y = np.array([1]) for expression in [pybamm.z_average(var), pybamm.yz_average(var)]: expr_disc = disc.process_symbol(expression) np.testing.assert_array_equal(var_disc.evaluate(y=y), expr_disc.evaluate(y=y))
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")
def _yz_average(self, var): """Computes the y-z average by integration over y and z""" return pybamm.yz_average(var)