def div(symbol): """ convenience function for creating a :class:`Divergence` Parameters ---------- symbol : :class:`Symbol` the divergence will be performed on this sub-symbol Returns ------- :class:`Divergence` the divergence of ``symbol`` """ # Divergence of a broadcast is zero if isinstance(symbol, pybamm.PrimaryBroadcastToEdges): new_child = pybamm.PrimaryBroadcast(0, symbol.child.domain) return pybamm.PrimaryBroadcast(new_child, symbol.domain) # Divergence commutes with Negate operator if isinstance(symbol, pybamm.Negate): return -div(symbol.orphans[0]) else: return Divergence(symbol)
def _get_coupled_variables_from_potential(self, variables, phi_e_av): i_boundary_cc = variables["Current collector current density"] param = self.param l_n = param.l_n l_p = param.l_p x_n = pybamm.standard_spatial_vars.x_n x_p = pybamm.standard_spatial_vars.x_p phi_e_n = pybamm.PrimaryBroadcast(phi_e_av, ["negative electrode"]) phi_e_s = pybamm.PrimaryBroadcast(phi_e_av, ["separator"]) phi_e_p = pybamm.PrimaryBroadcast(phi_e_av, ["positive electrode"]) phi_e = pybamm.Concatenation(phi_e_n, phi_e_s, phi_e_p) i_e_n = pybamm.outer(i_boundary_cc, x_n / l_n) i_e_s = pybamm.PrimaryBroadcast(i_boundary_cc, ["separator"]) i_e_p = pybamm.outer(i_boundary_cc, (1 - x_p) / l_p) i_e = pybamm.Concatenation(i_e_n, i_e_s, i_e_p) variables.update(self._get_standard_potential_variables(phi_e, phi_e_av)) variables.update(self._get_standard_current_variables(i_e)) eta_c_av = pybamm.Scalar(0) # concentration overpotential delta_phi_e_av = pybamm.Scalar(0) # ohmic losses variables.update(self._get_split_overpotential(eta_c_av, delta_phi_e_av)) return variables
def test_concatenation_simplify(self): # Primary broadcast var = pybamm.Variable("var", "current collector") a = pybamm.PrimaryBroadcast(var, "negative electrode") b = pybamm.PrimaryBroadcast(var, "separator") c = pybamm.PrimaryBroadcast(var, "positive electrode") concat = pybamm.concatenation(a, b, c) self.assertIsInstance(concat, pybamm.PrimaryBroadcast) self.assertEqual(concat.orphans[0], var) self.assertEqual( concat.domain, ["negative electrode", "separator", "positive electrode"]) # Full broadcast a = pybamm.FullBroadcast(0, "negative electrode", "current collector") b = pybamm.FullBroadcast(0, "separator", "current collector") c = pybamm.FullBroadcast(0, "positive electrode", "current collector") concat = pybamm.concatenation(a, b, c) self.assertIsInstance(concat, pybamm.FullBroadcast) self.assertEqual(concat.orphans[0].id, pybamm.Scalar(0).id) self.assertEqual( concat.domain, ["negative electrode", "separator", "positive electrode"]) self.assertEqual(concat.auxiliary_domains, {"secondary": ["current collector"]})
def nasty_hack_to_get_phi_s(self, variables): "This restates what is already in the electrode submodel which we should not do" param = self.param x_n = pybamm.standard_spatial_vars.x_n x_p = pybamm.standard_spatial_vars.x_p eps = variables[self.domain + " electrode porosity"] i_boundary_cc = variables["Current collector current density"] i_e = variables[self.domain + " electrolyte current density"] i_s = pybamm.PrimaryBroadcast(i_boundary_cc, self.domain_for_broadcast) - i_e if self.domain == "Negative": conductivity = param.sigma_n * (1 - eps) ** param.b_n phi_s = -pybamm.IndefiniteIntegral(i_s / conductivity, x_n) elif self.domain == "Positive": phi_e_s = variables["Separator electrolyte potential"] delta_phi_p = variables["Positive electrode surface potential difference"] conductivity = param.sigma_p * (1 - eps) ** param.b_p phi_s = -pybamm.IndefiniteIntegral( i_s / conductivity, x_p ) + pybamm.PrimaryBroadcast( pybamm.boundary_value(phi_e_s, "right") + pybamm.boundary_value(delta_phi_p, "left"), "positive electrode", ) return phi_s
def format(self, left, right): "Format children left and right into compatible form" # Turn numbers into scalars if isinstance(left, numbers.Number): left = pybamm.Scalar(left) if isinstance(right, numbers.Number): right = pybamm.Scalar(right) # Check both left and right are pybamm Symbols if not (isinstance(left, pybamm.Symbol) and isinstance(right, pybamm.Symbol)): raise NotImplementedError( """'{}' not implemented for symbols of type {} and {}""".format( self.__class__.__name__, type(left), type(right) ) ) # Do some broadcasting in special cases, to avoid having to do this manually if left.domain != [] and right.domain != []: if ( left.domain != right.domain and "secondary" in right.auxiliary_domains and left.domain == right.auxiliary_domains["secondary"] ): left = pybamm.PrimaryBroadcast(left, right.domain) if ( right.domain != left.domain and "secondary" in left.auxiliary_domains and right.domain == left.auxiliary_domains["secondary"] ): right = pybamm.PrimaryBroadcast(right, left.domain) return left, right
def set_rhs(self, variables): "Composite reaction-diffusion with source terms from leading order" param = self.param eps_0 = variables["Leading-order porosity"] deps_0_dt = variables["Leading-order porosity change"] c_e = variables["Electrolyte concentration"] N_e = variables["Electrolyte flux"] if self.extended is False: sum_s_j = variables[ "Leading-order sum of electrolyte reaction source terms" ] elif self.extended == "distributed": sum_s_j = variables["Sum of electrolyte reaction source terms"] elif self.extended == "average": sum_s_j_n_av = variables[ "Sum of x-averaged negative electrode electrolyte reaction source terms" ] sum_s_j_p_av = variables[ "Sum of x-averaged positive electrode electrolyte reaction source terms" ] sum_s_j = pybamm.Concatenation( pybamm.PrimaryBroadcast(sum_s_j_n_av, "negative electrode"), pybamm.FullBroadcast(0, "separator", "current collector"), pybamm.PrimaryBroadcast(sum_s_j_p_av, "positive electrode"), ) source_terms = sum_s_j / self.param.gamma_e self.rhs = { c_e: (1 / eps_0) * (-pybamm.div(N_e) / param.C_e + source_terms - c_e * deps_0_dt) }
def test_public_functions(self): param = pybamm.standard_parameters_lead_acid a = pybamm.Scalar(0) a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) variables = { "Current collector current density": a, "Negative electrode potential": a_n, "Negative electrolyte potential": a_n, "Negative electrode open circuit potential": a_n, "Negative electrolyte concentration": a_n, "Negative electrode temperature": a_n, } submodel = pybamm.interface.ButlerVolmer(param, "Negative", "lead-acid main") std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() variables = { "Current collector current density": a, "Positive electrode potential": a_p, "Positive electrolyte potential": a_p, "Positive electrode open circuit potential": a_p, "Positive electrolyte concentration": a_p, "Positive electrode temperature": a_p, "Negative electrode interfacial current density": a_n, "Negative electrode exchange current density": a_n, } submodel = pybamm.interface.ButlerVolmer(param, "Positive", "lead-acid main") std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
def get_fundamental_variables(self): c_e_av = pybamm.standard_variables.c_e_av c_e_n = pybamm.PrimaryBroadcast(c_e_av, ["negative electrode"]) c_e_s = pybamm.PrimaryBroadcast(c_e_av, ["separator"]) c_e_p = pybamm.PrimaryBroadcast(c_e_av, ["positive electrode"]) return self._get_standard_concentration_variables(c_e_n, c_e_s, c_e_p)
def test_public_functions(self): param = pybamm.standard_parameters_lead_acid a = pybamm.Scalar(0) variables = { "Current collector current density": a, "Negative electrolyte current density": pybamm.PrimaryBroadcast(a, ["negative electrode"]), "Positive electrolyte current density": pybamm.PrimaryBroadcast(a, ["positive electrode"]), "Negative electrode porosity": a, "Positive electrode porosity": a, "Separator electrolyte potential": a, "Positive electrode surface potential difference": a, } submodel = pybamm.electrode.ohm.SurfaceForm(param, "Negative") std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() submodel = pybamm.electrode.ohm.SurfaceForm(param, "Positive") std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
def get_coupled_variables(self, variables): j_n = variables[ "X-averaged negative electrode interfacial current density"] j_p = variables[ "X-averaged positive electrode interfacial current density"] j_sei_n = variables[ "X-averaged negative electrode sei interfacial current density"] beta_sei_n = self.param.beta_sei_n deps_n_dt = pybamm.PrimaryBroadcast( -self.param.beta_surf_n * j_n + beta_sei_n * j_sei_n, ["negative electrode"]) deps_s_dt = pybamm.FullBroadcast( 0, "separator", auxiliary_domains={"secondary": "current collector"}) deps_p_dt = pybamm.PrimaryBroadcast(-self.param.beta_surf_p * j_p, ["positive electrode"]) variables.update( self._get_standard_porosity_change_variables( deps_n_dt, deps_s_dt, deps_p_dt)) return variables
def test_shape_and_size_for_testing(self): scal = pybamm.Scalar(1) self.assertEqual(scal.shape_for_testing, scal.shape) self.assertEqual(scal.size_for_testing, scal.size) state = pybamm.StateVector(slice(10, 25), domain="test") state2 = pybamm.StateVector(slice(10, 25), domain="test 2") self.assertEqual(state.shape_for_testing, state.shape) param = pybamm.Parameter("a") self.assertEqual(param.shape_for_testing, ()) func = pybamm.FunctionParameter("func", {"state": state}) self.assertEqual(func.shape_for_testing, state.shape_for_testing) concat = pybamm.concatenation(state, state2) self.assertEqual(concat.shape_for_testing, (30, 1)) self.assertEqual(concat.size_for_testing, 30) var = pybamm.Variable("var", domain="negative electrode") broadcast = pybamm.PrimaryBroadcast(0, "negative electrode") self.assertEqual(var.shape_for_testing, broadcast.shape_for_testing) self.assertEqual((var + broadcast).shape_for_testing, broadcast.shape_for_testing) var = pybamm.Variable("var", domain=["random domain", "other domain"]) broadcast = pybamm.PrimaryBroadcast(0, ["random domain", "other domain"]) self.assertEqual(var.shape_for_testing, broadcast.shape_for_testing) self.assertEqual((var + broadcast).shape_for_testing, broadcast.shape_for_testing) sym = pybamm.Symbol("sym") with self.assertRaises(NotImplementedError): sym.shape_for_testing
def test_jac_of_domain_concatenation(self): # create mesh disc = get_1p1d_discretisation_for_testing() mesh = disc.mesh y = pybamm.StateVector(slice(0, 1500)) # Jacobian of a DomainConcatenation of constants is a zero matrix of the # appropriate size a_dom = ["negative electrode"] b_dom = ["separator"] c_dom = ["positive electrode"] cc_npts = mesh["current collector"][0].npts curr_coll_vector = pybamm.Vector(np.ones(cc_npts), domain="current collector") a = 2 * pybamm.PrimaryBroadcast(curr_coll_vector, a_dom) b = pybamm.PrimaryBroadcast(curr_coll_vector, b_dom) c = 3 * pybamm.PrimaryBroadcast(curr_coll_vector, c_dom) conc = pybamm.Concatenation(a, b, c) disc.set_variable_slices([conc]) conc_disc = disc.process_symbol(conc) jac = conc_disc.jac(y).evaluate().toarray() np.testing.assert_array_equal(jac, np.zeros((1500, 1500))) # Jacobian of a DomainConcatenation of StateVectors a = pybamm.Variable("a", domain=a_dom) b = pybamm.Variable("b", domain=b_dom) c = pybamm.Variable("c", domain=c_dom) conc = pybamm.Concatenation(a, b, c) disc.set_variable_slices([conc]) conc_disc = disc.process_symbol(conc) y0 = np.ones(1500) jac = conc_disc.jac(y).evaluate(y=y0).toarray() np.testing.assert_array_equal(jac, np.eye(1500))
def preprocess_binary(left, right): if isinstance(left, numbers.Number): left = pybamm.Scalar(left) if isinstance(right, numbers.Number): right = pybamm.Scalar(right) # Check both left and right are pybamm Symbols if not (isinstance(left, pybamm.Symbol) and isinstance(right, pybamm.Symbol)): raise NotImplementedError( """BinaryOperator not implemented for symbols of type {} and {}""". format(type(left), type(right))) # Do some broadcasting in special cases, to avoid having to do this manually if left.domain != [] and right.domain != []: if (left.domain != right.domain and "secondary" in right.auxiliary_domains and left.domain == right.auxiliary_domains["secondary"]): left = pybamm.PrimaryBroadcast(left, right.domain) if (right.domain != left.domain and "secondary" in left.auxiliary_domains and right.domain == left.auxiliary_domains["secondary"]): right = pybamm.PrimaryBroadcast(right, left.domain) return left, right
def test_public_function(self): param = pybamm.standard_parameters_lead_acid a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) a_s = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["separator"]) a = pybamm.Scalar(0) variables = { "Current collector current density": a, "Negative electrode potential": a_n, "Negative electrolyte potential": a_n, "Negative electrolyte concentration": a_n, "Negative electrode temperature": a_n, "X-averaged positive electrode oxygen interfacial current density": a, "Separator tortuosity": a_s, "Separator oxygen concentration": a_s, "Leading-order negative electrode oxygen interfacial current density": a, } submodel = pybamm.interface.DiffusionLimited( param, "Negative", "lead-acid oxygen", "leading" ) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() submodel = pybamm.interface.DiffusionLimited( param, "Negative", "lead-acid oxygen", "composite" ) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() submodel = pybamm.interface.DiffusionLimited( param, "Negative", "lead-acid oxygen", "full" ) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
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): c_ox_av = pybamm.Variable( "X-averaged oxygen concentration", domain="current collector" ) c_ox_n = pybamm.PrimaryBroadcast(c_ox_av, "negative electrode") c_ox_s = pybamm.PrimaryBroadcast(c_ox_av, "separator") c_ox_p = pybamm.PrimaryBroadcast(c_ox_av, "positive electrode") return self._get_standard_concentration_variables(c_ox_n, c_ox_s, c_ox_p)
def get_coupled_variables(self, variables): # Set up param = self.param l_n = param.l_n x_n = pybamm.standard_spatial_vars.x_n x_s = pybamm.standard_spatial_vars.x_s x_p = pybamm.standard_spatial_vars.x_p p_s = variables["X-averaged separator pressure"] j_n_av = variables["X-averaged negative electrode interfacial current density"] j_p_av = variables["X-averaged positive electrode interfacial current density"] p_n = param.beta_n * j_n_av * (-(x_n ** 2) + param.l_n ** 2) / 2 + p_s p_p = param.beta_n * j_n_av * ((x_p - 1) ** 2 - param.l_p ** 2) / 2 + p_s variables.update(self._get_standard_neg_pos_pressure_variables(p_n, p_p)) # Volume-averaged velocity v_box_n = param.beta_n * j_n_av * x_n v_box_p = param.beta_p * j_p_av * (x_p - 1) variables.update( self._get_standard_neg_pos_velocity_variables(v_box_n, v_box_p) ) div_v_box_n = pybamm.PrimaryBroadcast( param.beta_n * j_n_av, "negative electrode" ) div_v_box_p = pybamm.PrimaryBroadcast( param.beta_p * j_p_av, "positive electrode" ) variables.update( self._get_standard_neg_pos_acceleration_variables(div_v_box_n, div_v_box_p) ) # Transverse velocity in the separator determines through-cell velocity div_Vbox_s = variables[ "X-averaged separator transverse volume-averaged acceleration" ] i_boundary_cc = variables["Current collector current density"] v_box_n_right = param.beta_n * i_boundary_cc div_v_box_s_av = -div_Vbox_s div_v_box_s = pybamm.PrimaryBroadcast(div_v_box_s_av, "separator") # Simple formula for velocity in the separator v_box_s = div_v_box_s_av * (x_s - l_n) + v_box_n_right variables.update( self._get_standard_sep_velocity_variables(v_box_s, div_v_box_s) ) variables.update(self._get_standard_whole_cell_velocity_variables(variables)) variables.update( self._get_standard_whole_cell_acceleration_variables(variables) ) variables.update(self._get_standard_whole_cell_pressure_variables(variables)) return variables
def test_public_functions(self): param = pybamm.standard_parameters_lead_acid a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) variables = { "Negative electrode interfacial current density": a_n, "Positive electrode interfacial current density": a_p, } submodel = pybamm.porosity.Full(param) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
def test_public_functions(self): param = pybamm.standard_parameters_lithium_ion a = pybamm.Scalar(0) a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) a_s = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["separator"]) a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) variables = { "Current collector current density": a, "Negative electrode porosity": a_n, "Negative electrolyte concentration": a_n, "Negative electrode interfacial current density": a_n, "X-averaged negative electrode interfacial current density": a, "X-averaged negative electrode total interfacial current density": a, } icd = " interfacial current density" reactions = { "main": { "Negative": { "s": 1, "aj": "Negative electrode" + icd }, "Positive": { "s": 1, "aj": "Positive electrode" + icd }, } } spf = pybamm.electrolyte.stefan_maxwell.conductivity.surface_potential_form submodel = spf.LeadingOrderAlgebraic(param, "Negative", reactions) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() submodel = spf.LeadingOrderDifferential(param, "Negative", reactions) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() variables = { "Current collector current density": a, "Negative electrolyte potential": a_n, "Negative electrolyte current density": a_n, "Separator electrolyte potential": a_s, "Separator electrolyte current density": a_s, "Positive electrode porosity": a_p, "Positive electrolyte concentration": a_p, "X-averaged positive electrode interfacial current density": a, "X-averaged positive electrode total interfacial current density": a, } submodel = spf.LeadingOrderAlgebraic(param, "Positive", reactions) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() submodel = spf.LeadingOrderDifferential(param, "Positive", reactions) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
def get_fundamental_variables(self): eps_n_pc = pybamm.standard_variables.eps_n_pc eps_s_pc = pybamm.standard_variables.eps_s_pc eps_p_pc = pybamm.standard_variables.eps_p_pc eps_n = pybamm.PrimaryBroadcast(eps_n_pc, "negative electrode") eps_s = pybamm.PrimaryBroadcast(eps_s_pc, "separator") eps_p = pybamm.PrimaryBroadcast(eps_p_pc, "positive electrode") variables = self._get_standard_porosity_variables(eps_n, eps_s, eps_p) return variables
def get_fundamental_variables(self): if self.domain == "Negative": c_s_xav = pybamm.standard_variables.c_s_n_xav c_s = pybamm.PrimaryBroadcast(c_s_xav, ["negative electrode"]) elif self.domain == "Positive": c_s_xav = pybamm.standard_variables.c_s_p_xav c_s = pybamm.PrimaryBroadcast(c_s_xav, ["positive electrode"]) variables = self._get_standard_concentration_variables(c_s, c_s_xav) return variables
def test_x_average(self): a = pybamm.Scalar(1) average_a = pybamm.x_average(a) self.assertEqual(average_a.id, a.id) average_broad_a = pybamm.x_average( pybamm.PrimaryBroadcast(a, ["negative electrode"])) self.assertEqual(average_broad_a.evaluate(), np.array([1])) conc_broad = pybamm.Concatenation( pybamm.PrimaryBroadcast(1, ["negative electrode"]), pybamm.PrimaryBroadcast(2, ["separator"]), pybamm.PrimaryBroadcast(3, ["positive electrode"]), ) average_conc_broad = pybamm.x_average(conc_broad) self.assertIsInstance(average_conc_broad, pybamm.Division) for domain in [ ["negative electrode"], ["separator"], ["positive electrode"], ["negative electrode", "separator", "positive electrode"], ]: a = pybamm.Symbol("a", domain=domain) x = pybamm.SpatialVariable("x", domain) av_a = pybamm.x_average(a) self.assertIsInstance(av_a, pybamm.Division) self.assertIsInstance(av_a.children[0], pybamm.Integral) self.assertEqual(av_a.children[0].integration_variable[0].domain, x.domain) self.assertEqual(av_a.domain, []) a = pybamm.Symbol("a", domain="new domain") av_a = pybamm.x_average(a) self.assertEqual(av_a.domain, []) self.assertIsInstance(av_a, pybamm.Division) self.assertIsInstance(av_a.children[0], pybamm.Integral) self.assertEqual(av_a.children[0].integration_variable[0].domain, a.domain) self.assertIsInstance(av_a.children[1], pybamm.Integral) self.assertEqual(av_a.children[1].integration_variable[0].domain, a.domain) self.assertEqual(av_a.children[1].children[0].id, pybamm.ones_like(a).id) # x-average of symbol that evaluates on edges raises error symbol_on_edges = pybamm.PrimaryBroadcastToEdges(1, "domain") with self.assertRaisesRegex( ValueError, "Can't take the x-average of a symbol that evaluates on edges" ): pybamm.x_average(symbol_on_edges)
def get_fundamental_variables(self): T_x_av = pybamm.standard_variables.T_av T_n = pybamm.PrimaryBroadcast(T_x_av, "negative electrode") T_s = pybamm.PrimaryBroadcast(T_x_av, "separator") T_p = pybamm.PrimaryBroadcast(T_x_av, "positive electrode") T = pybamm.Concatenation(T_n, T_s, T_p) T_cn = T_x_av T_cp = T_x_av variables = self._get_standard_fundamental_variables(T, T_cn, T_cp) return variables
def get_fundamental_variables(self): T_amb = self.param.T_amb(pybamm.t * self.param.timescale) T_x_av = pybamm.PrimaryBroadcast(T_amb, "current collector") T_vol_av = pybamm.PrimaryBroadcast(T_amb, "current collector") T_cn = T_x_av T_n = pybamm.PrimaryBroadcast(T_x_av, "negative electrode") T_s = pybamm.PrimaryBroadcast(T_x_av, "separator") T_p = pybamm.PrimaryBroadcast(T_x_av, "positive electrode") T_cp = T_x_av variables = self._get_standard_fundamental_variables( T_cn, T_n, T_s, T_p, T_cp, T_x_av, T_vol_av) return variables
def set_initial_conditions(self, variables): c_s = variables[self.domain + " particle concentration"] if self.domain == "Negative": x_n = pybamm.PrimaryBroadcast(pybamm.standard_spatial_vars.x_n, "negative particle") c_init = self.param.c_n_init(x_n) elif self.domain == "Positive": x_p = pybamm.PrimaryBroadcast(pybamm.standard_spatial_vars.x_p, "positive particle") c_init = self.param.c_p_init(x_p) self.initial_conditions = {c_s: c_init}
def get_coupled_variables(self, variables): param = self.param l_n = param.l_n l_s = param.l_s l_p = param.l_p x_s = pybamm.standard_spatial_vars.x_s x_p = pybamm.standard_spatial_vars.x_p # Unpack eps_s_0_av = variables["Leading-order x-averaged separator porosity"] eps_p_0_av = variables[ "Leading-order x-averaged positive electrode porosity"] # Diffusivities D_ox_s = (eps_s_0_av**param.b_s) * param.curlyD_ox D_ox_p = (eps_p_0_av**param.b_p) * param.curlyD_ox # Reactions sj_ox_p = sum(reaction["Positive"]["s_ox"] * variables["Leading-order x-averaged " + reaction["Positive"]["aj"].lower()] for reaction in self.reactions.values()) # Fluxes N_ox_n_1 = pybamm.FullBroadcast(0, "negative electrode", "current collector") N_ox_s_1 = -pybamm.PrimaryBroadcast(sj_ox_p * l_p, "separator") N_ox_p_1 = pybamm.outer(sj_ox_p, x_p - 1) # Concentrations c_ox_n_1 = pybamm.FullBroadcast(0, "negative electrode", "current collector") c_ox_s_1 = pybamm.outer(sj_ox_p * l_p / D_ox_s, x_s - l_n) c_ox_p_1 = pybamm.outer( -sj_ox_p / (2 * D_ox_p), (x_p - 1)**2 - l_p**2) + pybamm.PrimaryBroadcast( sj_ox_p * l_p * l_s / D_ox_s, "positive electrode") # Update variables c_ox = pybamm.Concatenation(param.C_e * c_ox_n_1, param.C_e * c_ox_s_1, param.C_e * c_ox_p_1) variables.update(self._get_standard_concentration_variables(c_ox)) N_ox = pybamm.Concatenation(param.C_e * N_ox_n_1, param.C_e * N_ox_s_1, param.C_e * N_ox_p_1) variables.update(self._get_standard_flux_variables(N_ox)) return variables
def get_fundamental_variables(self): T_x_av = pybamm.standard_variables.T_av T_vol_av = self._yz_average(T_x_av) T_cn = T_x_av T_n = pybamm.PrimaryBroadcast(T_x_av, "negative electrode") T_s = pybamm.PrimaryBroadcast(T_x_av, "separator") T_p = pybamm.PrimaryBroadcast(T_x_av, "positive electrode") T_cp = T_x_av variables = self._get_standard_fundamental_variables( T_cn, T_n, T_s, T_p, T_cp, T_x_av, T_vol_av) return variables
def test_public_functions(self): param = pybamm.standard_parameters_lithium_ion phi_s_cn = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["current collector"]) phi_s_cp = pybamm.PrimaryBroadcast(pybamm.Scalar(3), ["current collector"]) coupled_variables.update( { "Negative current collector potential": phi_s_cn, "Positive current collector potential": phi_s_cp, } ) submodel = pybamm.thermal.x_lumped.SetTemperature1D(param) std_tests = tests.StandardSubModelTests(submodel, coupled_variables) std_tests.test_all()
def get_coupled_variables(self, variables): # Unpack c_e_0 = variables["Leading-order " + self.domain.lower() + " electrolyte concentration"] c_e = variables[self.domain + " electrolyte concentration"] c_e_1 = (c_e - c_e_0) / self.param.C_e dj_dc_0 = self._get_dj_dc(variables) dj_ddeltaphi_0 = self._get_dj_ddeltaphi(variables) # Update delta_phi with new phi_e and phi_s variables = self._get_delta_phi(variables) delta_phi_0 = variables["Leading-order " + self.domain.lower() + " electrode surface potential difference"] delta_phi = variables[self.domain + " electrode surface potential difference"] delta_phi_1 = (delta_phi - delta_phi_0) / self.param.C_e j_0 = variables["Leading-order " + self.domain.lower() + " electrode" + self.reaction_name + " interfacial current density"] j_1 = (pybamm.PrimaryBroadcast(dj_dc_0, self.domain_for_broadcast) * c_e_1 + pybamm.PrimaryBroadcast( dj_ddeltaphi_0, self.domain_for_broadcast) * delta_phi_1) j = j_0 + self.param.C_e * j_1 # Get exchange-current density j0 = self._get_exchange_current_density(variables) # Get open-circuit potential variables and reaction overpotential ocp, dUdT = self._get_open_circuit_potential(variables) eta_r = delta_phi - ocp variables.update(self._get_standard_interfacial_current_variables(j)) variables.update(self._get_standard_exchange_current_variables(j0)) variables.update(self._get_standard_overpotential_variables(eta_r)) variables.update(self._get_standard_ocp_variables(ocp, dUdT)) if ("Negative electrode" + self.reaction_name + " interfacial current density" in variables and "Positive electrode" + self.reaction_name + " interfacial current density" in variables): variables.update( self._get_standard_whole_cell_interfacial_current_variables( variables)) variables.update( self._get_standard_whole_cell_exchange_current_variables( variables)) return variables
def _separator_velocity(self, variables): """ A private method to calculate x- and z-components of velocity in the separator Parameters ---------- variables : dict Dictionary of variables in the whole model. Returns ------- v_box_s : :class:`pybamm.Symbol` The x-component of velocity in the separator dVbox_dz : :class:`pybamm.Symbol` The z-component of velocity in the separator """ # Set up param = self.param l_n = pybamm.geometric_parameters.l_n l_s = pybamm.geometric_parameters.l_s x_s = pybamm.standard_spatial_vars.x_s # Difference in negative and positive electrode velocities determines the # velocity in the separator i_boundary_cc = variables["Current collector current density"] v_box_n_right = param.beta_n * i_boundary_cc v_box_p_left = param.beta_p * i_boundary_cc d_vbox_s__dx = (v_box_p_left - v_box_n_right) / l_s # Simple formula for velocity in the separator dVbox_dz = pybamm.Concatenation( pybamm.FullBroadcast( 0, "negative electrode", auxiliary_domains={"secondary": "current collector"}, ), pybamm.PrimaryBroadcast(-d_vbox_s__dx, "separator"), pybamm.FullBroadcast( 0, "positive electrode", auxiliary_domains={"secondary": "current collector"}, ), ) v_box_s = pybamm.outer(d_vbox_s__dx, (x_s - l_n)) + pybamm.PrimaryBroadcast( v_box_n_right, "separator") return v_box_s, dVbox_dz