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 tor = variables[self.domain + " electrode tortuosity"] i_boundary_cc = variables["Current collector current density"] i_e = variables[self.domain + " electrolyte current density"] i_s = i_boundary_cc - i_e if self.domain == "Negative": conductivity = param.sigma_n * tor 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 * tor phi_s = -pybamm.IndefiniteIntegral(i_s / conductivity, x_p) + ( pybamm.boundary_value(phi_e_s, "right") + pybamm.boundary_value(delta_phi_p, "left")) return phi_s
def get_coupled_variables(self, variables): # Set up param = self.param z = pybamm.standard_spatial_vars.z # 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) / param.l_s # Simple formula for velocity in the separator div_Vbox_s = -d_vbox_s_dx variables.update( self._get_standard_transverse_velocity_variables( div_Vbox_s, "acceleration")) Vbox_s = pybamm.IndefiniteIntegral(div_Vbox_s, z) variables.update( self._get_standard_transverse_velocity_variables( Vbox_s, "velocity")) return variables
def get_coupled_variables(self, variables): param = self.param if self.domain in ["Negative", "Positive"]: conductivity, sigma_eff = self._get_conductivities(variables) i_boundary_cc = variables["Current collector current density"] c_e = variables[self.domain + " electrolyte concentration"] delta_phi = variables[ self.domain + " electrode surface potential difference" ] T = variables[self.domain + " electrode temperature"] i_e = conductivity * ( ((1 + param.Theta * T) * param.chi(c_e, T) / c_e) * pybamm.grad(c_e) + pybamm.grad(delta_phi) + i_boundary_cc / sigma_eff ) variables.update(self._get_domain_current_variables(i_e)) phi_s = variables[self.domain + " electrode potential"] phi_e = phi_s - delta_phi variables.update(self._get_domain_potential_variables(phi_e)) elif self.domain == "Separator": x_s = pybamm.standard_spatial_vars.x_s i_boundary_cc = variables["Current collector current density"] c_e_s = variables["Separator electrolyte concentration"] phi_e_n = variables["Negative electrolyte potential"] tor_s = variables["Separator porosity"] T = variables["Separator temperature"] chi_e_s = param.chi(c_e_s, T) kappa_s_eff = param.kappa_e(c_e_s, T) * tor_s phi_e_s = pybamm.boundary_value( phi_e_n, "right" ) + pybamm.IndefiniteIntegral( (1 + param.Theta * T) * chi_e_s / c_e_s * pybamm.grad(c_e_s) - param.C_e * i_boundary_cc / kappa_s_eff, x_s, ) i_e_s = pybamm.PrimaryBroadcast(i_boundary_cc, "separator") variables.update(self._get_domain_potential_variables(phi_e_s)) variables.update(self._get_domain_current_variables(i_e_s)) # Update boundary conditions (for indefinite integral) self.boundary_conditions[c_e_s] = { "left": (pybamm.BoundaryGradient(c_e_s, "left"), "Neumann"), "right": (pybamm.BoundaryGradient(c_e_s, "right"), "Neumann"), } if self.domain == "Positive": variables.update(self._get_whole_cell_variables(variables)) return variables
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 i_boundary_cc = variables["Current collector current density"] i_e = variables[self.domain + " electrolyte current density"] eps = variables[self.domain + " electrode porosity"] phi_s_cn = variables["Negative current collector potential"] 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.PrimaryBroadcast( phi_s_cn, "negative electrode" ) - 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", ) variables.update(self._get_standard_potential_variables(phi_s)) variables.update(self._get_standard_current_variables(i_s)) if ( "Negative electrode current density" in variables and "Positive electrode current density" in variables ): variables.update(self._get_standard_whole_cell_variables(variables)) 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_symbol_new_copy(self): a = pybamm.Scalar(0) b = pybamm.Scalar(1) v_n = pybamm.Variable("v", "negative electrode") x_n = pybamm.standard_spatial_vars.x_n v_s = pybamm.Variable("v", "separator") vec = pybamm.Vector(np.array([1, 2, 3, 4, 5])) 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.Integral(a, pybamm.t), 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), ]: self.assertEqual(symbol.id, symbol.new_copy().id)
def _get_sep_coupled_variables(self, variables): """ A private function to get the coupled variables when the domain is 'Separator'. """ param = self.param x_s = pybamm.standard_spatial_vars.x_s i_boundary_cc = variables["Current collector current density"] c_e_s = variables["Separator electrolyte concentration"] phi_e_n = variables["Negative electrolyte potential"] eps_s = variables["Separator porosity"] T = variables["Separator temperature"] chi_e_s = param.chi(c_e_s) kappa_s_eff = param.kappa_e(c_e_s, T) * (eps_s ** param.b_s) phi_e_s = pybamm.PrimaryBroadcast( pybamm.boundary_value(phi_e_n, "right"), "separator" ) + pybamm.IndefiniteIntegral( chi_e_s / c_e_s * pybamm.grad(c_e_s) - param.C_e * pybamm.PrimaryBroadcast(i_boundary_cc, self.domain_for_broadcast) / kappa_s_eff, x_s, ) i_e_s = pybamm.PrimaryBroadcast(i_boundary_cc, "separator") variables.update(self._get_domain_potential_variables(phi_e_s)) variables.update(self._get_domain_current_variables(i_e_s)) # Update boundary conditions (for indefinite integral) self.boundary_conditions[c_e_s] = { "left": (pybamm.BoundaryGradient(c_e_s, "left"), "Neumann"), "right": (pybamm.BoundaryGradient(c_e_s, "right"), "Neumann"), } return variables
def test_indefinite_integral_on_nodes(self): mesh = get_mesh_for_testing() spatial_methods = {"macroscale": pybamm.FiniteVolume()} disc = pybamm.Discretisation(mesh, spatial_methods) # input a phi, take grad, then integrate to recover phi approximation # (need to test this way as check evaluated on edges using if has grad # and no div) phi = pybamm.Variable("phi", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", ["negative electrode", "separator"]) int_phi = pybamm.IndefiniteIntegral(phi, x) disc.set_variable_slices([phi]) # Set boundary conditions (required for shape but don't matter) int_phi_disc = disc.process_symbol(int_phi) combined_submesh = mesh.combine_submeshes("negative electrode", "separator") # constant case phi_exact = np.ones((combined_submesh[0].npts, 1)) int_phi_exact = combined_submesh[0].edges int_phi_approx = int_phi_disc.evaluate(None, phi_exact)[:, 0] np.testing.assert_array_equal(int_phi_exact, int_phi_approx) # linear case phi_exact = combined_submesh[0].nodes[:, np.newaxis] int_phi_exact = combined_submesh[0].edges[:, np.newaxis]**2 / 2 int_phi_approx = int_phi_disc.evaluate(None, phi_exact) np.testing.assert_array_almost_equal(int_phi_exact, int_phi_approx) # cos case phi_exact = np.cos(combined_submesh[0].nodes[:, np.newaxis]) int_phi_exact = np.sin(combined_submesh[0].edges[:, np.newaxis]) int_phi_approx = int_phi_disc.evaluate(None, phi_exact) np.testing.assert_array_almost_equal(int_phi_exact, int_phi_approx, decimal=5)
def test_integral(self): # time integral a = pybamm.Symbol("a") t = pybamm.t inta = pybamm.Integral(a, t) self.assertEqual(inta.name, "integral dtime") # self.assertTrue(inta.definite) self.assertEqual(inta.children[0].name, a.name) self.assertEqual(inta.integration_variable[0], t) self.assertEqual(inta.domain, []) # space integral a = pybamm.Symbol("a", domain=["negative electrode"]) x = pybamm.SpatialVariable("x", ["negative electrode"]) inta = pybamm.Integral(a, x) self.assertEqual(inta.name, "integral dx ['negative electrode']") self.assertEqual(inta.children[0].name, a.name) self.assertEqual(inta.integration_variable[0], x) self.assertEqual(inta.domain, []) self.assertEqual(inta.auxiliary_domains, {}) # space integral with secondary domain a_sec = pybamm.Symbol( "a", domain=["negative electrode"], auxiliary_domains={"secondary": "current collector"}, ) x = pybamm.SpatialVariable("x", ["negative electrode"]) inta_sec = pybamm.Integral(a_sec, x) self.assertEqual(inta_sec.domain, ["current collector"]) self.assertEqual(inta_sec.auxiliary_domains, {}) # space integral with secondary domain a_tert = pybamm.Symbol( "a", domain=["negative electrode"], auxiliary_domains={ "secondary": "current collector", "tertiary": "some extra domain", }, ) x = pybamm.SpatialVariable("x", ["negative electrode"]) inta_tert = pybamm.Integral(a_tert, x) self.assertEqual(inta_tert.domain, ["current collector"]) self.assertEqual(inta_tert.auxiliary_domains, {"secondary": ["some extra domain"]}) # space integral over two variables b = pybamm.Symbol("b", domain=["current collector"]) y = pybamm.SpatialVariable("y", ["current collector"]) z = pybamm.SpatialVariable("z", ["current collector"]) inta = pybamm.Integral(b, [y, z]) self.assertEqual(inta.name, "integral dy dz ['current collector']") self.assertEqual(inta.children[0].name, b.name) self.assertEqual(inta.integration_variable[0], y) self.assertEqual(inta.integration_variable[1], z) self.assertEqual(inta.domain, []) # Indefinite inta = pybamm.IndefiniteIntegral(a, x) self.assertEqual(inta.name, "a integrated w.r.t x on ['negative electrode']") self.assertEqual(inta.children[0].name, a.name) self.assertEqual(inta.integration_variable[0], x) self.assertEqual(inta.domain, ["negative electrode"]) inta_sec = pybamm.IndefiniteIntegral(a_sec, x) self.assertEqual(inta_sec.domain, ["negative electrode"]) self.assertEqual(inta_sec.auxiliary_domains, {"secondary": ["current collector"]}) # expected errors a = pybamm.Symbol("a", domain=["negative electrode"]) x = pybamm.SpatialVariable("x", ["separator"]) y = pybamm.Variable("y") z = pybamm.SpatialVariable("z", ["negative electrode"]) with self.assertRaises(pybamm.DomainError): pybamm.Integral(a, x) with self.assertRaises(ValueError): pybamm.Integral(a, y)
def test_integral(self): # space integral a = pybamm.Symbol("a", domain=["negative electrode"]) x = pybamm.SpatialVariable("x", ["negative electrode"]) inta = pybamm.Integral(a, x) self.assertEqual(inta.name, "integral dx ['negative electrode']") self.assertEqual(inta.children[0].name, a.name) self.assertEqual(inta.integration_variable[0], x) self.assertEqual(inta.domain, []) self.assertEqual(inta.auxiliary_domains, {}) # space integral with secondary domain a_sec = pybamm.Symbol( "a", domain=["negative electrode"], auxiliary_domains={"secondary": "current collector"}, ) x = pybamm.SpatialVariable("x", ["negative electrode"]) inta_sec = pybamm.Integral(a_sec, x) self.assertEqual(inta_sec.domain, ["current collector"]) self.assertEqual(inta_sec.auxiliary_domains, {}) # space integral with tertiary domain a_tert = pybamm.Symbol( "a", domain=["negative electrode"], auxiliary_domains={ "secondary": "current collector", "tertiary": "some extra domain", }, ) x = pybamm.SpatialVariable("x", ["negative electrode"]) inta_tert = pybamm.Integral(a_tert, x) self.assertEqual(inta_tert.domain, ["current collector"]) self.assertEqual(inta_tert.auxiliary_domains, {"secondary": ["some extra domain"]}) # space integral *in* secondary domain y = pybamm.SpatialVariable("y", ["current collector"]) # without a tertiary domain inta_sec_y = pybamm.Integral(a_sec, y) self.assertEqual(inta_sec_y.domain, ["negative electrode"]) self.assertEqual(inta_sec_y.auxiliary_domains, {}) # with a tertiary domain inta_tert_y = pybamm.Integral(a_tert, y) self.assertEqual(inta_tert_y.domain, ["negative electrode"]) self.assertEqual(inta_tert_y.auxiliary_domains, {"secondary": ["some extra domain"]}) # space integral *in* tertiary domain z = pybamm.SpatialVariable("z", ["some extra domain"]) inta_tert_z = pybamm.Integral(a_tert, z) self.assertEqual(inta_tert_z.domain, ["negative electrode"]) self.assertEqual(inta_tert_z.auxiliary_domains, {"secondary": ["current collector"]}) # space integral over two variables b = pybamm.Symbol("b", domain=["current collector"]) y = pybamm.SpatialVariable("y", ["current collector"]) z = pybamm.SpatialVariable("z", ["current collector"]) inta = pybamm.Integral(b, [y, z]) self.assertEqual(inta.name, "integral dy dz ['current collector']") self.assertEqual(inta.children[0].name, b.name) self.assertEqual(inta.integration_variable[0], y) self.assertEqual(inta.integration_variable[1], z) self.assertEqual(inta.domain, []) # Indefinite inta = pybamm.IndefiniteIntegral(a, x) self.assertEqual(inta.name, "a integrated w.r.t x on ['negative electrode']") self.assertEqual(inta.children[0].name, a.name) self.assertEqual(inta.integration_variable[0], x) self.assertEqual(inta.domain, ["negative electrode"]) inta_sec = pybamm.IndefiniteIntegral(a_sec, x) self.assertEqual(inta_sec.domain, ["negative electrode"]) self.assertEqual(inta_sec.auxiliary_domains, {"secondary": ["current collector"]}) # backward indefinite integral inta = pybamm.BackwardIndefiniteIntegral(a, x) self.assertEqual( inta.name, "a integrated backward w.r.t x on ['negative electrode']") # expected errors a = pybamm.Symbol("a", domain=["negative electrode"]) x = pybamm.SpatialVariable("x", ["separator"]) y = pybamm.Variable("y") z = pybamm.SpatialVariable("z", ["negative electrode"]) with self.assertRaises(pybamm.DomainError): pybamm.Integral(a, x) with self.assertRaisesRegex(TypeError, "integration_variable must be"): pybamm.Integral(a, y) with self.assertRaisesRegex( NotImplementedError, "Indefinite integral only implemeted w.r.t. one variable", ): pybamm.IndefiniteIntegral(a, [x, y])
def get_coupled_variables(self, variables): c_e_av = variables["X-averaged electrolyte concentration"] i_boundary_cc_0 = variables[ "Leading-order current collector current density"] c_e_n = variables["Negative electrolyte concentration"] c_e_s = variables["Separator electrolyte concentration"] c_e_p = variables["Positive electrolyte concentration"] c_e_n0 = pybamm.boundary_value(c_e_n, "left") delta_phi_n_av = variables[ "X-averaged negative electrode surface potential difference"] phi_s_n_av = variables["X-averaged negative electrode potential"] tor_n = variables["Negative electrolyte tortuosity"] tor_s = variables["Separator tortuosity"] tor_p = variables["Positive electrolyte tortuosity"] T_av = variables["X-averaged cell temperature"] T_av_n = pybamm.PrimaryBroadcast(T_av, "negative electrode") T_av_s = pybamm.PrimaryBroadcast(T_av, "separator") T_av_p = pybamm.PrimaryBroadcast(T_av, "positive electrode") param = self.param l_n = param.l_n l_p = param.l_p x_n = pybamm.standard_spatial_vars.x_n x_s = pybamm.standard_spatial_vars.x_s x_p = pybamm.standard_spatial_vars.x_p x_n_edge = pybamm.standard_spatial_vars.x_n_edge x_p_edge = pybamm.standard_spatial_vars.x_p_edge chi_av = param.chi(c_e_av, T_av) chi_av_n = pybamm.PrimaryBroadcast(chi_av, "negative electrode") chi_av_s = pybamm.PrimaryBroadcast(chi_av, "separator") chi_av_p = pybamm.PrimaryBroadcast(chi_av, "positive electrode") # electrolyte current i_e_n = i_boundary_cc_0 * x_n / l_n i_e_s = pybamm.PrimaryBroadcast(i_boundary_cc_0, "separator") i_e_p = i_boundary_cc_0 * (1 - x_p) / l_p i_e = pybamm.Concatenation(i_e_n, i_e_s, i_e_p) i_e_n_edge = i_boundary_cc_0 * x_n_edge / l_n i_e_s_edge = pybamm.PrimaryBroadcastToEdges(i_boundary_cc_0, "separator") i_e_p_edge = i_boundary_cc_0 * (1 - x_p_edge) / l_p # electrolyte potential indef_integral_n = (pybamm.IndefiniteIntegral( i_e_n_edge / (param.kappa_e(c_e_n, T_av_n) * tor_n), x_n) * param.C_e / param.gamma_e) indef_integral_s = (pybamm.IndefiniteIntegral( i_e_s_edge / (param.kappa_e(c_e_s, T_av_s) * tor_s), x_s) * param.C_e / param.gamma_e) indef_integral_p = (pybamm.IndefiniteIntegral( i_e_p_edge / (param.kappa_e(c_e_p, T_av_p) * tor_p), x_p) * param.C_e / param.gamma_e) integral_n = indef_integral_n integral_s = indef_integral_s + pybamm.boundary_value( integral_n, "right") integral_p = indef_integral_p + pybamm.boundary_value( integral_s, "right") phi_e_const = ( -delta_phi_n_av + phi_s_n_av - (chi_av * (1 + param.Theta * T_av) * pybamm.x_average( self._higher_order_macinnes_function(c_e_n / c_e_n0))) + pybamm.x_average(integral_n)) phi_e_n = (phi_e_const + (chi_av_n * (1 + param.Theta * T_av_n) * self._higher_order_macinnes_function(c_e_n / c_e_n0)) - integral_n) phi_e_s = (phi_e_const + (chi_av_s * (1 + param.Theta * T_av_s) * self._higher_order_macinnes_function(c_e_s / c_e_n0)) - integral_s) phi_e_p = (phi_e_const + (chi_av_p * (1 + param.Theta * T_av_p) * self._higher_order_macinnes_function(c_e_p / c_e_n0)) - integral_p) # concentration overpotential eta_c_av = (chi_av * (1 + param.Theta * T_av) * (pybamm.x_average( self._higher_order_macinnes_function( c_e_p / c_e_av)) - pybamm.x_average( self._higher_order_macinnes_function(c_e_n / c_e_av)))) # average electrolyte ohmic losses delta_phi_e_av = -(pybamm.x_average(integral_p) - pybamm.x_average(integral_n)) 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)) variables.update( self._get_split_overpotential(eta_c_av, delta_phi_e_av)) return variables
def test_indefinite_integral(self): # create discretisation mesh = get_mesh_for_testing() spatial_methods = { "macroscale": pybamm.FiniteVolume(), "negative particle": pybamm.FiniteVolume(), "positive particle": pybamm.FiniteVolume(), "current collector": pybamm.ZeroDimensionalMethod(), } disc = pybamm.Discretisation(mesh, spatial_methods) # input a phi, take grad, then integrate to recover phi approximation # (need to test this way as check evaluated on edges using if has grad # and no div) phi = pybamm.Variable("phi", domain=["negative electrode", "separator"]) i = pybamm.grad(phi) # create test current (variable on edges) x = pybamm.SpatialVariable("x", ["negative electrode", "separator"]) int_grad_phi = pybamm.IndefiniteIntegral(i, x) disc.set_variable_slices([phi]) # i is not a fundamental variable # Set boundary conditions (required for shape but don't matter) disc._bcs = { phi.id: { "left": (pybamm.Scalar(0), "Neumann"), "right": (pybamm.Scalar(0), "Neumann"), } } int_grad_phi_disc = disc.process_symbol(int_grad_phi) left_boundary_value = pybamm.BoundaryValue(int_grad_phi, "left") left_boundary_value_disc = disc.process_symbol(left_boundary_value) combined_submesh = mesh.combine_submeshes("negative electrode", "separator") # constant case phi_exact = np.ones((combined_submesh[0].npts, 1)) phi_approx = int_grad_phi_disc.evaluate(None, phi_exact) phi_approx += 1 # add constant of integration np.testing.assert_array_equal(phi_exact, phi_approx) self.assertEqual(left_boundary_value_disc.evaluate(y=phi_exact), 0) # linear case phi_exact = combined_submesh[0].nodes[:, np.newaxis] phi_approx = int_grad_phi_disc.evaluate(None, phi_exact) np.testing.assert_array_almost_equal(phi_exact, phi_approx) self.assertEqual(left_boundary_value_disc.evaluate(y=phi_exact), 0) # sine case phi_exact = np.sin(combined_submesh[0].nodes[:, np.newaxis]) phi_approx = int_grad_phi_disc.evaluate(None, phi_exact) np.testing.assert_array_almost_equal(phi_exact, phi_approx) self.assertEqual(left_boundary_value_disc.evaluate(y=phi_exact), 0) # -------------------------------------------------------------------- # region which doesn't start at zero phi = pybamm.Variable("phi", domain=["separator", "positive electrode"]) i = pybamm.grad(phi) # create test current (variable on edges) x = pybamm.SpatialVariable("x", ["separator", "positive electrode"]) int_grad_phi = pybamm.IndefiniteIntegral(i, x) disc.set_variable_slices([phi]) # i is not a fundamental variable disc._bcs = { phi.id: { "left": (pybamm.Scalar(0), "Neumann"), "right": (pybamm.Scalar(0), "Neumann"), } } int_grad_phi_disc = disc.process_symbol(int_grad_phi) left_boundary_value = pybamm.BoundaryValue(int_grad_phi, "left") left_boundary_value_disc = disc.process_symbol(left_boundary_value) combined_submesh = mesh.combine_submeshes("separator", "positive electrode") # constant case phi_exact = np.ones((combined_submesh[0].npts, 1)) phi_approx = int_grad_phi_disc.evaluate(None, phi_exact) phi_approx += 1 # add constant of integration np.testing.assert_array_equal(phi_exact, phi_approx) self.assertEqual(left_boundary_value_disc.evaluate(y=phi_exact), 0) # linear case phi_exact = (combined_submesh[0].nodes[:, np.newaxis] - combined_submesh[0].edges[0]) phi_approx = int_grad_phi_disc.evaluate(None, phi_exact) np.testing.assert_array_almost_equal(phi_exact, phi_approx) np.testing.assert_array_almost_equal( left_boundary_value_disc.evaluate(y=phi_exact), 0) # sine case phi_exact = np.sin(combined_submesh[0].nodes[:, np.newaxis] - combined_submesh[0].edges[0]) phi_approx = int_grad_phi_disc.evaluate(None, phi_exact) np.testing.assert_array_almost_equal(phi_exact, phi_approx) np.testing.assert_array_almost_equal( left_boundary_value_disc.evaluate(y=phi_exact), 0) # -------------------------------------------------------------------- # micrsoscale case c = pybamm.Variable("c", domain=["negative particle"]) N = pybamm.grad(c) # create test current (variable on edges) r_n = pybamm.SpatialVariable("r_n", ["negative particle"]) c_integral = pybamm.IndefiniteIntegral(N, r_n) disc.set_variable_slices([c]) # N is not a fundamental variable disc._bcs = { c.id: { "left": (pybamm.Scalar(0), "Neumann"), "right": (pybamm.Scalar(0), "Neumann"), } } c_integral_disc = disc.process_symbol(c_integral) left_boundary_value = pybamm.BoundaryValue(c_integral, "left") left_boundary_value_disc = disc.process_symbol(left_boundary_value) combined_submesh = mesh["negative particle"] # constant case c_exact = np.ones((combined_submesh[0].npts, 1)) c_approx = c_integral_disc.evaluate(None, c_exact) c_approx += 1 # add constant of integration np.testing.assert_array_equal(c_exact, c_approx) self.assertEqual(left_boundary_value_disc.evaluate(y=c_exact), 0) # linear case c_exact = combined_submesh[0].nodes[:, np.newaxis] c_approx = c_integral_disc.evaluate(None, c_exact) np.testing.assert_array_almost_equal(c_exact, c_approx) np.testing.assert_array_almost_equal( left_boundary_value_disc.evaluate(y=c_exact), 0) # sine case c_exact = np.sin(combined_submesh[0].nodes[:, np.newaxis]) c_approx = c_integral_disc.evaluate(None, c_exact) np.testing.assert_array_almost_equal(c_exact, c_approx, decimal=3) np.testing.assert_array_almost_equal( left_boundary_value_disc.evaluate(y=c_exact), 0)