def get_coupled_variables(self, variables): eps = separator_and_positive_only(variables["Porosity"]) c_ox = variables[ "Separator and positive electrode oxygen concentration"] # TODO: allow charge and convection? v_box = pybamm.Scalar(0) param = self.param b = pybamm.Concatenation( pybamm.FullBroadcast(param.b_s, ["separator"], "current collector"), pybamm.FullBroadcast(param.b_p, ["positive electrode"], "current collector"), ) N_ox_diffusion = -(eps**b) * param.curlyD_ox * pybamm.grad(c_ox) N_ox = N_ox_diffusion + c_ox * v_box # Flux in the negative electrode is zero N_ox = pybamm.Concatenation( pybamm.FullBroadcast(0, "negative electrode", "current collector"), N_ox) variables.update(self._get_standard_flux_variables(N_ox)) return variables
def _get_whole_cell_variables(self, variables): """ A private function to obtain the potential and current concatenated across the whole cell. Note: requires 'variables' to contain the potential and current in the subdomains: 'negative electrode', 'separator', and 'positive electrode'. Parameters ---------- variables : dict The variables that have been set in the rest of the model. Returns ------- variables : dict The variables including the whole-cell electrolyte potentials and currents. """ phi_e_n = variables["Negative electrolyte potential"] phi_e_s = variables["Separator electrolyte potential"] phi_e_p = variables["Positive electrolyte potential"] phi_e = pybamm.Concatenation(phi_e_n, phi_e_s, phi_e_p) phi_e_av = pybamm.x_average(phi_e) i_e_n = variables["Negative electrolyte current density"] i_e_s = variables["Separator electrolyte current density"] i_e_p = variables["Positive electrolyte current density"] 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)) 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)) self.assertEqual(state.shape_for_testing, state.shape) param = pybamm.Parameter("a") self.assertEqual(param.shape_for_testing, ()) func = pybamm.FunctionParameter("func", state) self.assertEqual(func.shape_for_testing, state.shape_for_testing) concat = pybamm.Concatenation() self.assertEqual(concat.shape_for_testing, (0, )) concat = pybamm.Concatenation(state, state) self.assertEqual(concat.shape_for_testing, (30, 1)) self.assertEqual(concat.size_for_testing, 30) var = pybamm.Variable("var", domain="negative electrode") broadcast = pybamm.Broadcast(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.Broadcast(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 get_fundamental_variables(self): eps_n_av = self.param.epsilon_n eps_s_av = self.param.epsilon_s eps_p_av = self.param.epsilon_p eps_n = pybamm.FullBroadcast( eps_n_av, "negative electrode", "current collector" ) eps_s = pybamm.FullBroadcast(eps_s_av, "separator", "current collector") eps_p = pybamm.FullBroadcast( eps_p_av, "positive electrode", "current collector" ) eps = pybamm.Concatenation(eps_n, eps_s, eps_p) deps_n_dt = pybamm.FullBroadcast(0, "negative electrode", "current collector") deps_s_dt = pybamm.FullBroadcast(0, "separator", "current collector") deps_p_dt = pybamm.FullBroadcast(0, "positive electrode", "current collector") deps_dt = pybamm.Concatenation(deps_n_dt, deps_s_dt, deps_p_dt) variables = self._get_standard_porosity_variables(eps, set_leading_order=True) variables.update( self._get_standard_porosity_change_variables( deps_dt, set_leading_order=True ) ) return variables
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 test_public_functions(self): param = pybamm.LeadAcidParameters() a = pybamm.Scalar(0) variables = { "Porosity": pybamm.Concatenation( pybamm.FullBroadcast(a, "negative electrode", "current collector"), pybamm.FullBroadcast(a, "separator", "current collector"), pybamm.FullBroadcast(a, "positive electrode", "current collector"), ), "Electrolyte tortuosity": pybamm.Concatenation( pybamm.FullBroadcast(a, "negative electrode", "current collector"), pybamm.FullBroadcast(a, "separator", "current collector"), pybamm.FullBroadcast(a, "positive electrode", "current collector"), ), "Porosity change": pybamm.Concatenation( pybamm.FullBroadcast(a, "negative electrode", "current collector"), pybamm.FullBroadcast(a, "separator", "current collector"), pybamm.FullBroadcast(a, "positive electrode", "current collector"), ), "Volume-averaged velocity": a, "Negative electrode interfacial current density": pybamm.FullBroadcast( a, "negative electrode", "current collector" ), "Positive electrode interfacial current density": pybamm.FullBroadcast( a, "positive electrode", "current collector" ), "Positive electrode oxygen interfacial current " "density": pybamm.FullBroadcast( a, "positive electrode", "current collector" ), } submodel = pybamm.oxygen_diffusion.Full(param) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
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 _get_standard_whole_cell_exchange_current_variables(self, variables): i_typ = self.param.i_typ L_x = self.param.L_x j_n_scale = i_typ / (self.param.a_n_dim * L_x) j_p_scale = i_typ / (self.param.a_p_dim * L_x) j0_n = variables[ "Negative electrode" + self.reaction_name + " exchange current density" ] j0_s = pybamm.FullBroadcast(0, "separator", "current collector") j0_p = variables[ "Positive electrode" + self.reaction_name + " exchange current density" ] j0 = pybamm.Concatenation(j0_n, j0_s, j0_p) j0_dim = pybamm.Concatenation(j_n_scale * j0_n, j0_s, j_p_scale * j0_p) if self.reaction_name == "": variables = { "Exchange current density": j0, "Exchange current density [A.m-2]": j0_dim, "Exchange current density per volume [A.m-3]": i_typ / L_x * j0, } else: reaction_name = self.reaction_name[1:].capitalize() variables = { reaction_name + " exchange current density": j0, reaction_name + " exchange current density [A.m-2]": j0_dim, reaction_name + " exchange current density per volume [A.m-3]": i_typ / L_x * j0, } return variables
def test_symbol_visualise(self): param = pybamm.standard_parameters_lithium_ion one_n = pybamm.FullBroadcast(1, ["negative electrode"], "current collector") one_p = pybamm.FullBroadcast(1, ["positive electrode"], "current collector") zero_n = pybamm.FullBroadcast(0, ["negative electrode"], "current collector") zero_s = pybamm.FullBroadcast(0, ["separator"], "current collector") zero_p = pybamm.FullBroadcast(0, ["positive electrode"], "current collector") deps_dt = pybamm.Concatenation(zero_n, zero_s, zero_p) v_box = pybamm.Scalar(0) variables = { "Porosity": param.epsilon, "Electrolyte tortuosity": param.epsilon**1.5, "Porosity change": deps_dt, "Volume-averaged velocity": v_box, "Negative electrode interfacial current density": one_n, "Positive electrode interfacial current density": one_p, "Cell temperature": pybamm.Concatenation(zero_n, zero_s, zero_p), } icd = " interfacial current density" reactions = { "main": { "Negative": { "s": 1, "aj": "Negative electrode" + icd }, "Positive": { "s": 1, "aj": "Positive electrode" + icd }, } } model = pybamm.electrolyte.stefan_maxwell.diffusion.Full( param, reactions) variables.update(model.get_fundamental_variables()) variables.update(model.get_coupled_variables(variables)) model.set_rhs(variables) c_e = pybamm.standard_variables.c_e rhs = model.rhs[c_e] rhs.visualise("StefanMaxwell_test.png") self.assertTrue(os.path.exists("StefanMaxwell_test.png")) with self.assertRaises(ValueError): rhs.visualise("StefanMaxwell_test")
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 tor_s_0_av = variables["Leading-order x-averaged separator tortuosity"] tor_p_0_av = variables[ "Leading-order x-averaged positive electrolyte tortuosity" ] # Diffusivities D_ox_s = tor_s_0_av * param.curlyD_ox D_ox_p = tor_p_0_av * 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 = sj_ox_p * (x_p - 1) # Concentrations c_ox_n_1 = pybamm.FullBroadcast(0, "negative electrode", "current collector") c_ox_s_1 = sj_ox_p * l_p / D_ox_s * (x_s - l_n) c_ox_p_1 = ( -sj_ox_p / (2 * D_ox_p) * ((x_p - 1) ** 2 - l_p ** 2) + sj_ox_p * l_p * l_s / D_ox_s ) # 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 test_concatenation_domains(self): a = pybamm.Symbol("a", domain=["negative electrode"]) b = pybamm.Symbol("b", domain=["separator", "positive electrode"]) c = pybamm.Symbol("c", domain=["test"]) conc = pybamm.Concatenation(a, b, c) self.assertEqual( conc.domain, ["negative electrode", "separator", "positive electrode", "test"], ) # Can't concatenate nodes with overlapping domains d = pybamm.Symbol("d", domain=["separator"]) with self.assertRaises(pybamm.DomainError): pybamm.Concatenation(a, b, d)
def test_public_functions(self): param = pybamm.standard_parameters_lead_acid a = pybamm.Scalar(0) variables = { "Porosity": pybamm.Concatenation( pybamm.FullBroadcast(a, "negative electrode", "current collector"), pybamm.FullBroadcast(a, "separator", "current collector"), pybamm.FullBroadcast(a, "positive electrode", "current collector"), ), "Electrolyte tortuosity": pybamm.Concatenation( pybamm.FullBroadcast(a, "negative electrode", "current collector"), pybamm.FullBroadcast(a, "separator", "current collector"), pybamm.FullBroadcast(a, "positive electrode", "current collector"), ), "Porosity change": pybamm.Concatenation( pybamm.FullBroadcast(a, "negative electrode", "current collector"), pybamm.FullBroadcast(a, "separator", "current collector"), pybamm.FullBroadcast(a, "positive electrode", "current collector"), ), "Volume-averaged velocity": a, "Negative electrode interfacial current density": pybamm.FullBroadcast(a, "negative electrode", "current collector"), "Positive electrode interfacial current density": pybamm.FullBroadcast(a, "positive electrode", "current collector"), } reactions = { "main": { "Negative": { "s_ox": param.s_ox_Ox, "aj": "Negative electrode interfacial current density", }, "Positive": { "s_ox": param.s_ox_Ox, "aj": "Positive electrode interfacial current density", }, } } submodel = pybamm.oxygen_diffusion.Full(param, reactions) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
def _set_dimensionless_parameters(self): "Defines the dimensionless parameters" # Density self.rho_cn = self.rho_cn_dim * self.c_p_cn_dim / self.rho_eff_dim self.rho_n = self.rho_n_dim * self.c_p_n_dim / self.rho_eff_dim self.rho_s = self.rho_s_dim * self.c_p_s_dim / self.rho_eff_dim self.rho_p = self.rho_p_dim * self.c_p_p_dim / self.rho_eff_dim self.rho_cp = self.rho_cp_dim * self.c_p_cp_dim / self.rho_eff_dim self.rho_k = pybamm.Concatenation( pybamm.FullBroadcast( self.rho_n, ["negative electrode"], "current collector" ), pybamm.FullBroadcast(self.rho_s, ["separator"], "current collector"), pybamm.FullBroadcast( self.rho_p, ["positive electrode"], "current collector" ), ) # Thermal conductivity self.lambda_cn = self.lambda_cn_dim / self.lambda_eff_dim self.lambda_n = self.lambda_n_dim / self.lambda_eff_dim self.lambda_s = self.lambda_s_dim / self.lambda_eff_dim self.lambda_p = self.lambda_p_dim / self.lambda_eff_dim self.lambda_cp = self.lambda_cp_dim / self.lambda_eff_dim self.lambda_k = pybamm.Concatenation( pybamm.FullBroadcast( self.lambda_n, ["negative electrode"], "current collector" ), pybamm.FullBroadcast(self.lambda_s, ["separator"], "current collector"), pybamm.FullBroadcast( self.lambda_p, ["positive electrode"], "current collector" ), ) # Relative temperature rise self.Theta = self.Delta_T / self.T_ref # Cooling coefficient self.h_cn = self.h_cn_dim * self.geo.L_x / self.lambda_eff_dim self.h_cp = self.h_cp_dim * self.geo.L_x / self.lambda_eff_dim self.h_tab_n = self.h_tab_n_dim * self.geo.L_x / self.lambda_eff_dim self.h_tab_p = self.h_tab_p_dim * self.geo.L_x / self.lambda_eff_dim self.h_edge = self.h_edge_dim * self.geo.L_x / self.lambda_eff_dim self.h_total = self.h_total_dim * self.geo.L_x / self.lambda_eff_dim # Initial temperature self.T_init = (self.T_init_dim - self.T_ref) / self.Delta_T
def setUp(self): c_e_n = pybamm.Variable("concentration", domain=["negative electrode"]) c_e_s = pybamm.Variable("concentration", domain=["separator"]) c_e_p = pybamm.Variable("concentration", domain=["positive electrode"]) self.c_e = pybamm.Concatenation(c_e_n, c_e_s, c_e_p) T_n = pybamm.Variable("temperature", domain=["negative electrode"]) T_s = pybamm.Variable("temperature", domain=["separator"]) T_p = pybamm.Variable("temperature", domain=["positive electrode"]) self.T = pybamm.Concatenation(T_n, T_s, T_p) self.variables = { "Negative electrolyte concentration": c_e_n, "Positive electrolyte concentration": c_e_p, "Negative electrode temperature": T_n, "Positive electrode temperature": T_p, }
def test_base_concatenation(self): a = pybamm.Symbol("a") b = pybamm.Symbol("b") c = pybamm.Symbol("c") conc = pybamm.Concatenation(a, b, c) self.assertEqual(conc.name, "concatenation") self.assertIsInstance(conc.children[0], pybamm.Symbol) self.assertEqual(conc.children[0].name, "a") self.assertEqual(conc.children[1].name, "b") self.assertEqual(conc.children[2].name, "c") d = pybamm.Vector(np.array([2])) e = pybamm.Vector(np.array([1])) f = pybamm.Vector(np.array([3])) conc2 = pybamm.Concatenation(d, e, f) with self.assertRaises(TypeError): conc2.evaluate()
def test_adding_2D_external_variable_fail(self): model = pybamm.BaseModel() a = pybamm.Variable( "a", domain=["negative electrode", "separator"], auxiliary_domains={"secondary": "current collector"}, ) b1 = pybamm.Variable( "b", domain="negative electrode", auxiliary_domains={"secondary": "current collector"}, ) b2 = pybamm.Variable( "b", domain="separator", auxiliary_domains={"secondary": "current collector"}, ) b = pybamm.Concatenation(b1, b2) model.rhs = {a: a * b} model.initial_conditions = {a: 0} model.external_variables = [b] model.variables = {"b": b} disc = get_1p1d_discretisation_for_testing() with self.assertRaisesRegex( NotImplementedError, "Cannot create 2D external variable" ): disc.process_model(model)
def test_exceptions(self): c_n = pybamm.Variable("c", domain=["negative electrode"]) N_n = pybamm.grad(c_n) c_s = pybamm.Variable("c", domain=["separator"]) N_s = pybamm.grad(c_s) model = pybamm.BaseModel() model.rhs = {c_n: pybamm.div(N_n), c_s: pybamm.div(N_s)} model.initial_conditions = {c_n: pybamm.Scalar(3), c_s: pybamm.Scalar(1)} model.boundary_conditions = { c_n: {"left": (0, "Neumann"), "right": (0, "Neumann")}, c_s: {"left": (0, "Neumann"), "right": (0, "Neumann")}, } disc = get_discretisation_for_testing() # check raises error if different sized key and output var model.variables = {c_n.name: c_s} with self.assertRaisesRegex(pybamm.ModelError, "variable and its eqn"): disc.process_model(model) # check doesn't raise if concatenation model.variables = {c_n.name: pybamm.Concatenation(c_n, c_s)} disc.process_model(model, inplace=False) # check doesn't raise if broadcast model.variables = { c_n.name: pybamm.PrimaryBroadcast( pybamm.InputParameter("a"), ["negative electrode"] ) } disc.process_model(model)
def test_concatenation_2D(self): disc = get_1p1d_discretisation_for_testing(zpts=3) a = pybamm.Variable("a", domain=["negative electrode"]) b = pybamm.Variable("b", domain=["separator"]) c = pybamm.Variable("c", domain=["positive electrode"]) conc = pybamm.Concatenation(a, b, c) disc.set_variable_slices([conc]) self.assertEqual( disc.y_slices[a.id], [slice(0, 40), slice(100, 140), slice(200, 240)]) self.assertEqual( disc.y_slices[b.id], [slice(40, 65), slice(140, 165), slice(240, 265)]) self.assertEqual( disc.y_slices[c.id], [slice(65, 100), slice(165, 200), slice(265, 300)]) expr = disc.process_symbol(conc) self.assertIsInstance(expr, pybamm.DomainConcatenation) # Evaulate y = np.linspace(0, 1, 300) self.assertEqual(expr.children[0].evaluate(0, y).shape, (120, 1)) self.assertEqual(expr.children[1].evaluate(0, y).shape, (75, 1)) self.assertEqual(expr.children[2].evaluate(0, y).shape, (105, 1)) np.testing.assert_equal(expr.evaluate(0, y), y[:, np.newaxis])
def test_domain_concatenation_2D(self): disc = get_1p1d_discretisation_for_testing() a_dom = ["negative electrode"] b_dom = ["separator"] a = pybamm.Variable("a", domain=a_dom) b = pybamm.Variable("b", domain=b_dom) conc = pybamm.concatenation(2 * a, 3 * b) disc.set_variable_slices([a, b]) expr = disc.process_symbol(conc) self.assertIsInstance(expr, pybamm.DomainConcatenation) y = np.empty((expr._size, 1)) for i in range(len(y)): y[i] = i constant_symbols = OrderedDict() variable_symbols = OrderedDict() pybamm.find_symbols(expr, constant_symbols, variable_symbols) self.assertEqual(len(constant_symbols), 0) evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate(y=y) np.testing.assert_allclose(result, expr.evaluate(y=y)) # check that concatenating a single domain is consistent expr = disc.process_symbol(pybamm.Concatenation(a)) evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate(y=y) np.testing.assert_allclose(result, expr.evaluate(y=y))
def _get_standard_porosity_change_variables( self, deps_n_dt, deps_s_dt, deps_p_dt, set_leading_order=False ): deps_n_dt_av = pybamm.x_average(deps_n_dt) deps_s_dt_av = pybamm.x_average(deps_s_dt) deps_p_dt_av = pybamm.x_average(deps_p_dt) deps_dt = pybamm.Concatenation(deps_n_dt, deps_s_dt, deps_p_dt) variables = { "Porosity change": deps_dt, "Negative electrode porosity change": deps_n_dt, "Separator porosity change": deps_s_dt, "Positive electrode porosity change": deps_p_dt, "X-averaged negative electrode porosity change": deps_n_dt_av, "X-averaged separator porosity change": deps_s_dt_av, "X-averaged positive electrode porosity change": deps_p_dt_av, } if set_leading_order is True: variables.update( { "Leading-order x-averaged " + "negative electrode porosity change": deps_n_dt_av, "Leading-order x-averaged separator porosity change": deps_s_dt_av, "Leading-order x-averaged " + "positive electrode porosity change": deps_p_dt_av, } ) return variables
def set_rhs(self, variables): "Composite reaction-diffusion with source terms from leading order" param = self.param eps_0 = separator_and_positive_only(variables["Leading-order porosity"]) deps_0_dt = separator_and_positive_only( variables["Leading-order porosity change"] ) c_ox = variables["Separator and positive electrode oxygen concentration"] N_ox = variables["Oxygen flux"].orphans[1] if self.extended is False: pos_reactions = sum( reaction["Positive"]["s_ox"] * variables["Leading-order " + reaction["Positive"]["aj"].lower()] for reaction in self.reactions.values() ) else: pos_reactions = sum( reaction["Positive"]["s_ox"] * variables[reaction["Positive"]["aj"]] for reaction in self.reactions.values() ) sep_reactions = pybamm.FullBroadcast(0, "separator", "current collector") source_terms_0 = ( pybamm.Concatenation(sep_reactions, pos_reactions) / param.gamma_e ) self.rhs = { c_ox: (1 / eps_0) * (-pybamm.div(N_ox) / param.C_e + source_terms_0 - c_ox * deps_0_dt) }
def set_rhs(self, variables): "Composite reaction-diffusion with source terms from leading order" param = self.param eps_0 = separator_and_positive_only( variables["Leading-order porosity"]) deps_0_dt = separator_and_positive_only( variables["Leading-order porosity change"]) c_ox = variables[ "Separator and positive electrode oxygen concentration"] N_ox = variables["Oxygen flux"].orphans[1] if self.extended is False: j_ox_0 = variables[ "Leading-order positive electrode oxygen interfacial current density"] pos_reactions = param.s_ox_Ox * j_ox_0 else: j_ox_0 = variables[ "Positive electrode oxygen interfacial current density"] pos_reactions = param.s_ox_Ox * j_ox_0 sep_reactions = pybamm.FullBroadcast(0, "separator", "current collector") source_terms_0 = (pybamm.Concatenation(sep_reactions, pos_reactions) / param.gamma_e) self.rhs = { c_ox: (1 / eps_0) * (-pybamm.div(N_ox) / param.C_e + source_terms_0 - c_ox * deps_0_dt) }
def set_rhs(self, variables): param = self.param eps = variables["Porosity"] deps_dt = variables["Porosity change"] c_e = variables["Electrolyte concentration"] N_e = variables["Electrolyte flux"] # i_e = variables["Electrolyte current density"] # source_term = ((param.s - param.t_plus) / param.gamma_e) * pybamm.div(i_e) # source_term = pybamm.div(i_e) / param.gamma_e # lithium-ion source_terms = sum( pybamm.Concatenation( reaction["Negative"]["s"] * variables[reaction["Negative"]["aj"]], pybamm.FullBroadcast(0, "separator", "current collector"), reaction["Positive"]["s"] * variables[reaction["Positive"]["aj"]], ) / param.gamma_e for reaction in self.reactions.values()) self.rhs = { c_e: (1 / eps) * (-pybamm.div(N_e) / param.C_e + source_terms - c_e * deps_dt) }
def get_coupled_variables(self, variables): param = self.param x_n = pybamm.standard_spatial_vars.x_n x_p = pybamm.standard_spatial_vars.x_p # j_n = variables["Negative electrode interfacial current density"] # j_p = variables["Positive electrode interfacial current density"] # Volume-averaged velocity # v_box_n = param.beta_n * pybamm.IndefiniteIntegral(j_n, x_n) # # Shift v_box_p to be equal to 0 at x_p = 1 # v_box_p = param.beta_p * ( # pybamm.IndefiniteIntegral(j_p, x_p) - pybamm.Integral(j_p, x_p) # ) j_n_av = variables[ "X-averaged negative electrode interfacial current density"] j_p_av = variables[ "X-averaged positive electrode interfacial current density"] # Volume-averaged velocity v_box_n = param.beta_n * pybamm.outer(j_n_av, x_n) v_box_p = param.beta_p * pybamm.outer(j_p_av, x_p - 1) v_box_s, dVbox_dz = self._separator_velocity(variables) v_box = pybamm.Concatenation(v_box_n, v_box_s, v_box_p) variables.update(self._get_standard_velocity_variables(v_box)) variables.update( self._get_standard_vertical_velocity_variables(dVbox_dz)) return variables
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 _get_standard_whole_cell_velocity_variables(self, variables): """ A private function to obtain the standard variables which can be derived from the fluid velocity. Parameters ---------- variables : dict The existing variables in the model Returns ------- variables : dict The variables which can be derived from the volume-averaged velocity. """ vel_scale = self.param.velocity_scale v_box_n = variables["Negative electrode volume-averaged velocity"] v_box_s = variables["Separator volume-averaged velocity"] v_box_p = variables["Positive electrode volume-averaged velocity"] v_box = pybamm.Concatenation(v_box_n, v_box_s, v_box_p) variables = { "Volume-averaged velocity": v_box, "Volume-averaged velocity [m.s-1]": vel_scale * v_box, } return variables
def test_concatenations(self): y = np.linspace(0, 1, 10)[:, np.newaxis] a = pybamm.Vector(y) b = pybamm.Scalar(16) c = pybamm.Scalar(3) conc = pybamm.NumpyConcatenation(a, b, c) self.assert_casadi_equal(conc.to_casadi(), casadi.MX(conc.evaluate()), evalf=True) # Domain concatenation mesh = get_mesh_for_testing() a_dom = ["negative electrode"] b_dom = ["separator"] a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom) b = pybamm.Vector(np.ones_like(mesh[b_dom[0]].nodes), domain=b_dom) conc = pybamm.DomainConcatenation([b, a], mesh) self.assert_casadi_equal(conc.to_casadi(), casadi.MX(conc.evaluate()), evalf=True) # 2d disc = get_1p1d_discretisation_for_testing() a = pybamm.Variable("a", domain=a_dom) b = pybamm.Variable("b", domain=b_dom) conc = pybamm.Concatenation(a, b) disc.set_variable_slices([conc]) expr = disc.process_symbol(conc) y = casadi.SX.sym("y", expr.size) x = expr.to_casadi(None, y) f = casadi.Function("f", [x], [x]) y_eval = np.linspace(0, 1, expr.size) self.assert_casadi_equal(f(y_eval), casadi.SX(expr.evaluate(y=y_eval)))
def test_grad_1plus1d(self): mesh = get_1p1d_mesh_for_testing() spatial_methods = {"macroscale": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) a = pybamm.Variable("a", domain=["negative electrode"]) b = pybamm.Variable("b", domain=["separator"]) c = pybamm.Variable("c", domain=["positive electrode"]) var = pybamm.Concatenation(a, b, c) boundary_conditions = { var.id: { "left": (pybamm.Vector(np.linspace(0, 1, 15)), "Neumann"), "right": (pybamm.Vector(np.linspace(0, 1, 15)), "Neumann"), } } disc.bcs = boundary_conditions disc.set_variable_slices([var]) grad_eqn_disc = disc.process_symbol(pybamm.grad(var)) # Evaulate combined_submesh = mesh.combine_submeshes(*var.domain) linear_y = np.outer(np.linspace(0, 1, 15), combined_submesh[0].nodes).reshape(-1, 1) expected = np.outer(np.linspace(0, 1, 15), np.ones_like(combined_submesh[0].edges)).reshape( -1, 1) np.testing.assert_array_almost_equal( grad_eqn_disc.evaluate(None, linear_y), expected)
def test_symbol_visualise(self): param = pybamm.LithiumIonParameters() zero_n = pybamm.FullBroadcast(0, ["negative electrode"], "current collector") zero_s = pybamm.FullBroadcast(0, ["separator"], "current collector") zero_p = pybamm.FullBroadcast(0, ["positive electrode"], "current collector") zero_nsp = pybamm.Concatenation(zero_n, zero_s, zero_p) v_box = pybamm.Scalar(0) variables = { "Porosity": param.epsilon, "Electrolyte tortuosity": param.epsilon**1.5, "Porosity change": zero_nsp, "Electrolyte current density": zero_nsp, "Volume-averaged velocity": v_box, "Interfacial current density": zero_nsp, "Oxygen interfacial current density": zero_nsp, "Cell temperature": pybamm.Concatenation(zero_n, zero_s, zero_p), "Transverse volume-averaged acceleration": pybamm.Concatenation(zero_n, zero_s, zero_p), "Sum of electrolyte reaction source terms": zero_nsp, } model = pybamm.electrolyte_diffusion.Full(param) variables.update(model.get_fundamental_variables()) variables.update(model.get_coupled_variables(variables)) model.set_rhs(variables) rhs = list(model.rhs.values())[0] rhs.visualise("StefanMaxwell_test.png") self.assertTrue(os.path.exists("StefanMaxwell_test.png")) with self.assertRaises(ValueError): rhs.visualise("StefanMaxwell_test")
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"]) c_e = pybamm.Concatenation(c_e_n, c_e_s, c_e_p) return self._get_standard_concentration_variables(c_e)