def set_rhs(self, variables): T = variables["Cell temperature"] T_n = variables["Negative electrode temperature"] T_s = variables["Separator temperature"] T_p = variables["Positive electrode temperature"] Q = variables["Total heating"] # Define volumetric heat capacity rho_k = pybamm.concatenation( self.param.rho_n(T_n), self.param.rho_s(T_s), self.param.rho_p(T_p), ) # Devine thermal conductivity lambda_k = pybamm.concatenation( self.param.lambda_n(T_n), self.param.lambda_s(T_s), self.param.lambda_p(T_p), ) # Fourier's law for heat flux q = -lambda_k * pybamm.grad(T) # N.B only y-z surface cooling is implemented for this model self.rhs = { T: (-pybamm.div(q) / self.param.delta**2 + self.param.B * Q) / (self.param.C_th * rho_k) }
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 set_rhs(self, variables): """Composite reaction-diffusion with source terms from leading order.""" param = self.param eps_0_s = variables["Leading-order separator porosity"] eps_0_p = variables["Leading-order positive electrode porosity"] eps_0 = pybamm.concatenation(eps_0_s, eps_0_p) deps_0_dt_s = variables["Leading-order separator porosity change"] deps_0_dt_p = variables[ "Leading-order positive electrode porosity change"] deps_0_dt = pybamm.concatenation(deps_0_dt_s, deps_0_dt_p) 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 test_symbol_replacements(self): a = pybamm.Parameter("a") b = pybamm.Parameter("b") c = pybamm.Parameter("c") d = pybamm.Parameter("d") replacer = pybamm.SymbolReplacer({a: b, c: d}) for symbol_in, symbol_out in [ (a, b), # just the symbol (a + a, b + b), # binary operator (2 * pybamm.sin(a), 2 * pybamm.sin(b)), # function (3 * b, 3 * b), # no replacement (a + c, b + d), # two replacements ]: replaced_symbol = replacer.process_symbol(symbol_in) self.assertEqual(replaced_symbol.id, symbol_out.id) var1 = pybamm.Variable("var 1", domain="dom 1") var2 = pybamm.Variable("var 2", domain="dom 2") var3 = pybamm.Variable("var 3", domain="dom 1") conc = pybamm.concatenation(var1, var2) replacer = pybamm.SymbolReplacer({var1: var3}) replaced_symbol = replacer.process_symbol(conc) self.assertEqual(replaced_symbol.id, pybamm.concatenation(var3, var2).id)
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_typ * L_x) j_p_scale = i_typ / (self.param.a_p_typ * 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 set_rhs(self, variables): param = self.param eps_s = variables["Separator porosity"] eps_p = variables["Positive electrode porosity"] eps = pybamm.concatenation(eps_s, eps_p) deps_dt_s = variables["Separator porosity change"] deps_dt_p = variables["Positive electrode porosity change"] deps_dt = pybamm.concatenation(deps_dt_s, deps_dt_p) c_ox = variables["Separator and positive electrode oxygen concentration"] N_ox = variables["Oxygen flux"].orphans[1] j_ox = variables["Positive electrode oxygen interfacial current density"] source_terms = pybamm.concatenation( pybamm.FullBroadcast(0, "separator", "current collector"), param.s_ox_Ox * j_ox, ) self.rhs = { c_ox: (1 / eps) * (-pybamm.div(N_ox) / param.C_e + source_terms - c_ox * deps_dt) }
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, "Negative electrode porosity": param.epsilon_n, "Separator porosity": param.epsilon_s, "Positive electrode porosity": param.epsilon_p, "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 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_set_initial_condition_errors(self): model = pybamm.BaseModel() var = pybamm.Scalar(1) model.rhs = {var: -var} model.initial_conditions = {var: 1} with self.assertRaisesRegex(NotImplementedError, "Variable must have type"): model.set_initial_conditions_from({}) var = pybamm.Variable( "var", domain="negative particle", auxiliary_domains={ "secondary": "negative electrode", "tertiary": "current collector", }, ) model.rhs = {var: -var} model.initial_conditions = {var: 1} with self.assertRaisesRegex(NotImplementedError, "Variable must be 0D, 1D, or 2D"): model.set_initial_conditions_from({"var": np.ones((5, 6, 7, 8))}) var_concat_neg = pybamm.Variable("var concat neg", domain="negative electrode") var_concat_sep = pybamm.Variable("var concat sep", domain="separator") var_concat = pybamm.concatenation(var_concat_neg, var_concat_sep) model.algebraic = {var_concat: -var_concat} model.initial_conditions = {var_concat: 1} with self.assertRaisesRegex(NotImplementedError, "Variable in concatenation must be 1D"): model.set_initial_conditions_from( {"var concat neg": np.ones((5, 6, 7))}) # Inconsistent model and variable names model = pybamm.BaseModel() var = pybamm.Variable("var") model.rhs = {var: -var} model.initial_conditions = {var: pybamm.Scalar(1)} with self.assertRaisesRegex(pybamm.ModelError, "must appear in the solution"): model.set_initial_conditions_from({"wrong var": 2}) var = pybamm.concatenation(pybamm.Variable("var", "test"), pybamm.Variable("var2", "test2")) model.rhs = {var: -var} model.initial_conditions = {var: pybamm.Scalar(1)} with self.assertRaisesRegex(pybamm.ModelError, "must appear in the solution"): model.set_initial_conditions_from({"wrong var": 2})
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"].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) # Add bounds for compatibility with the discretisation a.bounds = (-np.inf, np.inf) b.bounds = (-np.inf, np.inf) c.bounds = (-np.inf, np.inf) conc = pybamm.concatenation(a, b, c) conc.bounds = (-np.inf, np.inf) 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, auxiliary_domains={"secondary": "current collector"}) b = pybamm.Variable( "b", domain=b_dom, auxiliary_domains={"secondary": "current collector"}) c = pybamm.Variable( "c", domain=c_dom, auxiliary_domains={"secondary": "current collector"}) 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_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 _get_standard_porosity_variables(self, eps_n, eps_s, eps_p, set_leading_order=False): eps_n_av = pybamm.x_average(eps_n) eps_s_av = pybamm.x_average(eps_s) eps_p_av = pybamm.x_average(eps_p) eps = pybamm.concatenation(eps_n, eps_s, eps_p) variables = { "Porosity": eps, "Negative electrode porosity": eps_n, "Separator porosity": eps_s, "Positive electrode porosity": eps_p, "X-averaged negative electrode porosity": eps_n_av, "X-averaged separator porosity": eps_s_av, "X-averaged positive electrode porosity": eps_p_av, } if set_leading_order is True: leading_order_variables = { "Leading-order " + name.lower(): var for name, var in variables.items() } variables.update(leading_order_variables) return variables
def _get_standard_tortuosity_variables(self, tor_n, tor_s, tor_p, set_leading_order=False): tor = pybamm.concatenation(tor_n, tor_s, tor_p) variables = { self.phase + " tortuosity": tor, "Negative " + self.phase.lower() + " tortuosity": tor_n, "Positive " + self.phase.lower() + " tortuosity": tor_p, "X-averaged negative " + self.phase.lower() + " tortuosity": pybamm.x_average(tor_n), "X-averaged positive " + self.phase.lower() + " tortuosity": pybamm.x_average(tor_p), } if self.phase == "Electrolyte": variables.update({ "Separator tortuosity": tor_s, "X-averaged separator tortuosity": pybamm.x_average(tor_s), }) if set_leading_order is True: leading_order_variables = { "Leading-order " + name.lower(): var for name, var in variables.items() } variables.update(leading_order_variables) 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 _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 test_binary_simplifications_concatenations(self): def conc_broad(x, y, z): return pybamm.concatenation( pybamm.PrimaryBroadcast(x, "negative electrode"), pybamm.PrimaryBroadcast(y, "separator"), pybamm.PrimaryBroadcast(z, "positive electrode"), ) # Test that concatenations get simplified correctly a = conc_broad(1, 2, 3) b = conc_broad(11, 12, 13) c = conc_broad( pybamm.InputParameter("x"), pybamm.InputParameter("y"), pybamm.InputParameter("z"), ) self.assertEqual((a + 4).id, conc_broad(5, 6, 7).id) self.assertEqual((4 + a).id, conc_broad(5, 6, 7).id) self.assertEqual((a + b).id, conc_broad(12, 14, 16).id) self.assertIsInstance((a + c), pybamm.Concatenation) # No simplifications if are Variable or StateVector objects v = pybamm.concatenation( pybamm.Variable("x", "negative electrode"), pybamm.Variable("y", "separator"), pybamm.Variable("z", "positive electrode"), ) self.assertIsInstance((v * v), pybamm.Multiplication) self.assertIsInstance((a * v), pybamm.Multiplication)
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 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_discretisation(self): param = pybamm.LithiumIonParameters() model_n = pybamm.interface.ButlerVolmer( param, "Negative", "lithium-ion main", { "SEI film resistance": "none", "total interfacial current density as a state": "false", }, ) j_n = model_n.get_coupled_variables( self.variables)["Negative electrode interfacial current density"] model_p = pybamm.interface.ButlerVolmer( param, "Positive", "lithium-ion main", { "SEI film resistance": "none", "total interfacial current density as a state": "false", }, ) j_p = model_p.get_coupled_variables( self.variables)["Positive electrode interfacial current density"] j = pybamm.concatenation(j_n, pybamm.PrimaryBroadcast(0, ["separator"]), j_p) # Process parameters and discretise parameter_values = pybamm.lithium_ion.BaseModel( ).default_parameter_values disc = get_discretisation_for_testing() mesh = disc.mesh disc.set_variable_slices([ self.c_e_n, self.c_e_p, self.delta_phi_s_n, self.delta_phi_s_p, self.c_s_n_surf, self.c_s_p_surf, ]) j_n = disc.process_symbol(parameter_values.process_symbol(j_n)) j_p = disc.process_symbol(parameter_values.process_symbol(j_p)) j = disc.process_symbol(parameter_values.process_symbol(j)) # test butler-volmer in each electrode submesh = np.concatenate([ mesh["negative electrode"].nodes, mesh["positive electrode"].nodes ]) y = np.concatenate([submesh**2, submesh**3, submesh**4]) self.assertEqual( j_n.evaluate(None, y).shape, (mesh["negative electrode"].npts, 1)) self.assertEqual( j_p.evaluate(None, y).shape, (mesh["positive electrode"].npts, 1)) # test concatenated butler-volmer whole_cell = ["negative electrode", "separator", "positive electrode"] whole_cell_mesh = disc.mesh.combine_submeshes(*whole_cell) self.assertEqual(j.evaluate(None, y).shape, (whole_cell_mesh.npts, 1))
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"]) i_e_n = i_boundary_cc * x_n / l_n i_e_s = pybamm.PrimaryBroadcast(i_boundary_cc, ["separator"]) i_e_p = 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_n, phi_e_s, phi_e_p) ) variables.update(self._get_standard_current_variables(i_e)) # concentration overpotential eta_c_av = pybamm.PrimaryBroadcast(0, "current collector") # ohmic losses delta_phi_e_av = pybamm.PrimaryBroadcast(0, "current collector") variables.update(self._get_split_overpotential(eta_c_av, delta_phi_e_av)) return variables
def test_concatenation_orphans(self): 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) a_new, b_new, c_new = conc.orphans # We should be able to manipulate the children without TreeErrors self.assertIsInstance(2 * a_new, pybamm.Multiplication) self.assertIsInstance(3 + b_new, pybamm.Addition) self.assertIsInstance(4 - c_new, pybamm.Subtraction) # ids should stay the same self.assertEqual(a.id, a_new.id) self.assertEqual(b.id, b_new.id) self.assertEqual(c.id, c_new.id) self.assertEqual(conc.id, pybamm.concatenation(a_new, b_new, c_new).id)
def test_base_concatenation(self): a = pybamm.Symbol("a", domain="test a") b = pybamm.Symbol("b", domain="test b") c = pybamm.Symbol("c", domain="test c") conc = pybamm.concatenation(a, b, c) self.assertEqual(conc.name, "concatenation") self.assertEqual(str(conc), "concatenation(a, b, c)") 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([2], domain="test a") e = pybamm.Vector([1], domain="test b") f = pybamm.Vector([3], domain="test c") conc2 = pybamm.concatenation(d, e, f) with self.assertRaises(TypeError): conc2.evaluate() # trying to concatenate non-pybamm symbols with self.assertRaises(TypeError): pybamm.concatenation(1, 2) # concatenation of length 0 with self.assertRaisesRegex(ValueError, "Cannot create empty concatenation"): pybamm.concatenation() # concatenation of lenght 1 self.assertEqual(pybamm.concatenation(a), a)
def _get_standard_concentration_variables(self, c_e_n, c_e_s, c_e_p): """ A private function to obtain the standard variables which can be derived from the concentration in the electrolyte. Parameters ---------- c_e_n : :class:`pybamm.Symbol` The electrolyte concentration in the negative electrode. c_e_s : :class:`pybamm.Symbol` The electrolyte concentration in the separator. c_e_p : :class:`pybamm.Symbol` The electrolyte concentration in the positive electrode. Returns ------- variables : dict The variables which can be derived from the concentration in the electrolyte. """ c_e_typ = self.param.c_e_typ c_e = pybamm.concatenation(c_e_n, c_e_s, c_e_p) c_e_av = pybamm.x_average(c_e) c_e_n_av = pybamm.x_average(c_e_n) c_e_s_av = pybamm.x_average(c_e_s) c_e_p_av = pybamm.x_average(c_e_p) variables = { "Electrolyte concentration": c_e, "Electrolyte concentration [mol.m-3]": c_e_typ * c_e, "Electrolyte concentration [Molar]": c_e_typ * c_e / 1000, "X-averaged electrolyte concentration": c_e_av, "X-averaged electrolyte concentration [mol.m-3]": c_e_typ * c_e_av, "X-averaged electrolyte concentration [Molar]": c_e_typ * c_e_av / 1000, "Negative electrolyte concentration": c_e_n, "Negative electrolyte concentration [mol.m-3]": c_e_typ * c_e_n, "Negative electrolyte concentration [Molar]": c_e_typ * c_e_n / 1000, "Separator electrolyte concentration": c_e_s, "Separator electrolyte concentration [mol.m-3]": c_e_typ * c_e_s, "Separator electrolyte concentration [Molar]": c_e_typ * c_e_s / 1000, "Positive electrolyte concentration": c_e_p, "Positive electrolyte concentration [mol.m-3]": c_e_typ * c_e_p, "Positive electrolyte concentration [Molar]": c_e_typ * c_e_p / 1000, "X-averaged negative electrolyte concentration": c_e_n_av, "X-averaged negative electrolyte concentration [mol.m-3]": c_e_typ * c_e_n_av, "X-averaged separator electrolyte concentration": c_e_s_av, "X-averaged separator electrolyte concentration [mol.m-3]": c_e_typ * c_e_s_av, "X-averaged positive electrolyte concentration": c_e_p_av, "X-averaged positive electrolyte concentration [mol.m-3]": c_e_typ * c_e_p_av, } return variables
def test_public_functions(self): param = pybamm.LithiumIonParameters() a = pybamm.Scalar(1) surf = "electrode surface area to volume ratio" a_n = pybamm.FullBroadcast(a, "negative electrode", "current collector") a_s = pybamm.FullBroadcast(a, "separator", "current collector") a_p = pybamm.FullBroadcast(a, "positive electrode", "current collector") variables = { "Electrolyte tortuosity": a, "Electrolyte concentration": pybamm.concatenation(a_n, a_s, a_p), "Negative electrolyte concentration": a_n, "Separator electrolyte concentration": a_s, "Positive electrolyte concentration": a_p, "Negative electrode temperature": a_n, "Separator temperature": a_s, "Positive electrode temperature": a_p, "Negative " + surf: pybamm.FullBroadcast(a, "negative electrode", "current collector"), "Positive " + surf: pybamm.FullBroadcast(a, "positive electrode", "current collector"), "Sum of interfacial current densities": pybamm.FullBroadcast( a, ["negative electrode", "separator", "positive electrode"], "current collector", ), "Cell temperature": pybamm.concatenation(a_n, a_s, a_p), } submodel = pybamm.electrolyte_conductivity.Full(param) std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all()
def _get_standard_porosity_times_concentration_variables( self, eps_c_e_n, eps_c_e_s, eps_c_e_p): eps_c_e = pybamm.concatenation(eps_c_e_n, eps_c_e_s, eps_c_e_p) variables = { "Porosity times concentration": eps_c_e, "Negative electrode porosity times concentration": eps_c_e_n, "Separator porosity times concentration": eps_c_e_s, "Positive electrode porosity times concentration": eps_c_e_p, } return variables
def test_symbol_new_copy(self): a = pybamm.Parameter("a") b = pybamm.Parameter("b") v_n = pybamm.Variable("v", "negative electrode") x_n = pybamm.standard_spatial_vars.x_n v_s = pybamm.Variable("v", "separator") vec = pybamm.Vector([1, 2, 3, 4, 5]) mat = pybamm.Matrix([[1, 2], [3, 4]]) mesh = get_mesh_for_testing() for symbol in [ a + b, a - b, a * b, a / b, a**b, -a, abs(a), pybamm.Function(np.sin, a), pybamm.FunctionParameter("function", {"a": a}), pybamm.grad(v_n), pybamm.div(pybamm.grad(v_n)), pybamm.upwind(v_n), pybamm.IndefiniteIntegral(v_n, x_n), pybamm.BackwardIndefiniteIntegral(v_n, x_n), pybamm.BoundaryValue(v_n, "right"), pybamm.BoundaryGradient(v_n, "right"), pybamm.PrimaryBroadcast(a, "domain"), pybamm.SecondaryBroadcast(v_n, "current collector"), pybamm.FullBroadcast(a, "domain", {"secondary": "other domain"}), pybamm.concatenation(v_n, v_s), pybamm.NumpyConcatenation(a, b, v_s), pybamm.DomainConcatenation([v_n, v_s], mesh), pybamm.Parameter("param"), pybamm.InputParameter("param"), pybamm.StateVector(slice(0, 56)), pybamm.Matrix(np.ones((50, 40))), pybamm.SpatialVariable("x", ["negative electrode"]), pybamm.t, pybamm.Index(vec, 1), pybamm.NotConstant(a), pybamm.ExternalVariable( "external variable", 20, domain="test", auxiliary_domains={"secondary": "test2"}, ), pybamm.minimum(a, b), pybamm.maximum(a, b), pybamm.SparseStack(mat, mat), ]: self.assertEqual(symbol.id, symbol.new_copy().id)
def test_concatenation_auxiliary_domains(self): a = pybamm.Symbol( "a", domain=["negative electrode"], auxiliary_domains={"secondary": "current collector"}, ) b = pybamm.Symbol( "b", domain=["separator", "positive electrode"], auxiliary_domains={"secondary": "current collector"}, ) conc = pybamm.concatenation(a, b) self.assertEqual(conc.auxiliary_domains, {"secondary": ["current collector"]}) # Can't concatenate nodes with overlapping domains c = pybamm.Symbol("c", domain=["test"], auxiliary_domains={"secondary": "something else"}) with self.assertRaisesRegex( pybamm.DomainError, "children must have same or empty auxiliary domains"): pybamm.concatenation(a, b, c)
def get_coupled_variables(self, variables): tor_s = variables["Separator tortuosity"] tor_p = variables["Positive electrode tortuosity"] tor = pybamm.concatenation(tor_s, tor_p) c_ox = variables["Separator and positive electrode oxygen concentration"] # TODO: allow charge and convection? v_box = pybamm.Scalar(0) param = self.param N_ox_diffusion = -tor * param.curlyD_ox * pybamm.grad(c_ox) N_ox = N_ox_diffusion + param.C_e * 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_fundamental_variables(self): T_n = pybamm.standard_variables.T_n T_s = pybamm.standard_variables.T_s T_p = pybamm.standard_variables.T_p T_cn = pybamm.BoundaryValue(T_n, "left") T_cp = pybamm.BoundaryValue(T_p, "right") T = pybamm.concatenation(T_n, T_s, T_p) T_x_av = self._x_average(T, T_cn, T_cp) T_vol_av = self._yz_average(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 __neg__(self): """return a :class:`Negate` object.""" if isinstance(self, pybamm.Negate): # Double negative is a positive return self.orphans[0] elif isinstance(self, pybamm.Broadcast): # Move negation inside the broadcast # Apply recursively return self._unary_new_copy(-self.orphans[0]) elif isinstance(self, pybamm.Concatenation) and all( child.is_constant() for child in self.children): return pybamm.concatenation(*[-child for child in self.orphans]) else: return pybamm.simplify_if_constant(pybamm.Negate(self))