def _get_neg_pos_coupled_variables(self, variables):
        """
        A private function to get the coupled variables when the domain is 'Negative'
        or 'Positive'.
        """

        param = self.param

        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) / c_e) * pybamm.grad(c_e) +
            pybamm.grad(delta_phi) + i_boundary_cc / sigma_eff)
        variables.update(self._get_domain_current_variables(i_e))

        # TODO: Expression can be written in a form which does not require phi_s and
        # so avoid this hack.
        phi_s = self.nasty_hack_to_get_phi_s(variables)
        phi_e = phi_s - delta_phi

        variables.update(self._get_domain_potential_variables(phi_e))
        variables.update({"test": pybamm.x_average(phi_s)})
        return variables
Ejemplo n.º 2
0
    def test_adding_1D_external_variable(self):
        model = pybamm.BaseModel()

        a = pybamm.Variable("a", domain=["test"])
        b = pybamm.Variable("b", domain=["test"])

        model.rhs = {a: a * b}
        model.boundary_conditions = {
            a: {
                "left": (0, "Dirichlet"),
                "right": (0, "Dirichlet")
            }
        }
        model.initial_conditions = {a: 0}
        model.external_variables = [b]
        model.variables = {
            "a": a,
            "b": b,
            "c": a * b,
            "grad b": pybamm.grad(b),
            "div grad b": pybamm.div(pybamm.grad(b)),
        }

        x = pybamm.SpatialVariable("x", domain="test", coord_sys="cartesian")
        geometry = {
            "test": {
                "primary": {
                    x: {
                        "min": pybamm.Scalar(0),
                        "max": pybamm.Scalar(1)
                    }
                }
            }
        }

        submesh_types = {"test": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh)}
        var_pts = {x: 10}
        mesh = pybamm.Mesh(geometry, submesh_types, var_pts)

        spatial_methods = {"test": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)
        disc.process_model(model)

        self.assertEqual(disc.y_slices[a.id][0], slice(0, 10, None))

        self.assertEqual(model.y_slices[a][0], slice(0, 10, None))

        b_test = np.ones((10, 1))
        np.testing.assert_array_equal(
            model.variables["b"].evaluate(inputs={"b": b_test}), b_test)

        # check that b is added to the boundary conditions
        model.bcs[b.id]["left"]
        model.bcs[b.id]["right"]

        # check that grad and div(grad ) produce the correct shapes
        self.assertEqual(model.variables["b"].shape_for_testing, (10, 1))
        self.assertEqual(model.variables["grad b"].shape_for_testing, (11, 1))
        self.assertEqual(model.variables["div grad b"].shape_for_testing,
                         (10, 1))
Ejemplo n.º 3
0
    def get_fundamental_variables(self):

        # Electrolyte pressure
        p_n = pybamm.Variable(
            "Negative electrode pressure",
            domain="negative electrode",
            auxiliary_domains={"secondary": "current collector"},
        )
        p_p = pybamm.Variable(
            "Positive electrode pressure",
            domain="positive electrode",
            auxiliary_domains={"secondary": "current collector"},
        )
        variables = self._get_standard_neg_pos_pressure_variables(p_n, p_p)

        # TODO: add permeability and viscosity, and other terms
        v_mass_n = -pybamm.grad(p_n)
        v_mass_p = -pybamm.grad(p_p)
        v_box_n = v_mass_n
        v_box_p = v_mass_p
        variables.update(
            self._get_standard_neg_pos_velocity_variables(v_box_n, v_box_p))

        div_v_box_n = pybamm.div(v_box_n)
        div_v_box_p = pybamm.div(v_box_p)
        variables.update(
            self._get_standard_neg_pos_acceleration_variables(
                div_v_box_n, div_v_box_p))

        return variables
Ejemplo n.º 4
0
    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 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
Ejemplo n.º 6
0
 def _current_collector_heating(self, variables):
     "Compute Ohmic heating in current collectors"
     # TODO: implement grad in 0D to return a scalar zero
     # TODO: implement grad_squared in other spatial methods so that the if
     # statement can be removed
     # In the limit of infinitely large current collector conductivity (i.e.
     # 0D current collectors), the Ohmic heating in the current collectors is
     # zero
     if self.cc_dimension == 0:
         Q_s_cn = pybamm.Scalar(0)
         Q_s_cp = pybamm.Scalar(0)
     # Otherwise we compute the Ohmic heating for 1 or 2D current collectors
     elif self.cc_dimension in [1, 2]:
         phi_s_cn = variables["Negative current collector potential"]
         phi_s_cp = variables["Positive current collector potential"]
         if self.cc_dimension == 1:
             Q_s_cn = self.param.sigma_cn_prime * pybamm.inner(
                 pybamm.grad(phi_s_cn), pybamm.grad(phi_s_cn))
             Q_s_cp = self.param.sigma_cp_prime * pybamm.inner(
                 pybamm.grad(phi_s_cp), pybamm.grad(phi_s_cp))
         elif self.cc_dimension == 2:
             # Inner not implemented in 2D -- have to call grad_squared directly
             Q_s_cn = self.param.sigma_cn_prime * pybamm.grad_squared(
                 phi_s_cn)
             Q_s_cp = self.param.sigma_cp_prime * pybamm.grad_squared(
                 phi_s_cp)
     return Q_s_cn, Q_s_cp
Ejemplo n.º 7
0
    def get_coupled_variables(self, variables):
        c_s = variables[self.domain + " particle concentration"]
        T_k = pybamm.PrimaryBroadcast(
            variables[self.domain + " electrode temperature"],
            [self.domain.lower() + " particle"],
        )

        if self.domain == "Negative":
            N_s = -self.param.D_n(c_s, T_k) * pybamm.grad(c_s)
        elif self.domain == "Positive":
            N_s = -self.param.D_p(c_s, T_k) * pybamm.grad(c_s)

        variables.update(self._get_standard_flux_variables(N_s, N_s))

        if self.domain == "Negative":
            x = pybamm.standard_spatial_vars.x_n
            R = pybamm.FunctionParameter(
                "Negative particle distribution in x",
                {"Dimensionless through-cell position (x_n)": x},
            )
            variables.update({"Negative particle distribution in x": R})

        elif self.domain == "Positive":
            x = pybamm.standard_spatial_vars.x_p
            R = pybamm.FunctionParameter(
                "Positive particle distribution in x",
                {"Dimensionless through-cell position (x_p)": x},
            )
            variables.update({"Positive particle distribution in x": R})

        return variables
Ejemplo n.º 8
0
    def test_grad_div_with_bcs_on_tab(self):
        # 2d macroscale
        mesh = get_1p1d_mesh_for_testing()
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(),
            "negative particle": pybamm.FiniteVolume(),
            "positive particle": pybamm.FiniteVolume(),
            "current collector": pybamm.FiniteVolume(),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)
        y_test = np.ones(mesh["current collector"][0].npts)

        # var
        var = pybamm.Variable("var", domain="current collector")
        disc.set_variable_slices([var])
        # grad
        grad_eqn = pybamm.grad(var)
        # div
        N = pybamm.grad(var)
        div_eqn = pybamm.div(N)

        # bcs (on each tab)
        boundary_conditions = {
            var.id: {
                "negative tab": (pybamm.Scalar(1), "Dirichlet"),
                "positive tab": (pybamm.Scalar(0), "Neumann"),
            }
        }
        disc.bcs = boundary_conditions
        grad_eqn_disc = disc.process_symbol(grad_eqn)
        grad_eqn_disc.evaluate(None, y_test)
        div_eqn_disc = disc.process_symbol(div_eqn)
        div_eqn_disc.evaluate(None, y_test)

        # bcs (one pos, one not tab)
        boundary_conditions = {
            var.id: {
                "no tab": (pybamm.Scalar(1), "Dirichlet"),
                "positive tab": (pybamm.Scalar(0), "Dirichlet"),
            }
        }
        disc.bcs = boundary_conditions
        grad_eqn_disc = disc.process_symbol(grad_eqn)
        grad_eqn_disc.evaluate(None, y_test)
        div_eqn_disc = disc.process_symbol(div_eqn)
        div_eqn_disc.evaluate(None, y_test)

        # bcs (one neg, one not tab)
        boundary_conditions = {
            var.id: {
                "negative tab": (pybamm.Scalar(1), "Neumann"),
                "no tab": (pybamm.Scalar(0), "Neumann"),
            }
        }
        disc.bcs = boundary_conditions
        grad_eqn_disc = disc.process_symbol(grad_eqn)
        grad_eqn_disc.evaluate(None, y_test)
        div_eqn_disc = disc.process_symbol(div_eqn)
        div_eqn_disc.evaluate(None, y_test)
Ejemplo n.º 9
0
    def test_processed_variable_ode_pde_solution(self):
        # without space
        model = pybamm.BaseBatteryModel()
        c = pybamm.Variable("conc")
        model.rhs = {c: -c}
        model.initial_conditions = {c: 1}
        model.variables = {"c": c}
        modeltest = tests.StandardModelTest(model)
        modeltest.test_all()
        t_sol, y_sol = modeltest.solution.t, modeltest.solution.y
        processed_vars = pybamm.post_process_variables(model.variables, t_sol,
                                                       y_sol)
        np.testing.assert_array_almost_equal(processed_vars["c"](t_sol),
                                             np.exp(-t_sol))

        # with space
        # set up and solve model
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        model = pybamm.BaseBatteryModel()
        c = pybamm.Variable("conc", domain=whole_cell)
        c_s = pybamm.Variable(
            "particle conc",
            domain="negative particle",
            auxiliary_domains={"secondary": ["negative electrode"]},
        )
        model.rhs = {c: -c, c_s: 1 - c_s}
        model.initial_conditions = {c: 1, c_s: 0.5}
        model.boundary_conditions = {
            c: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            },
            c_s: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            },
        }
        model.variables = {
            "c": c,
            "N": pybamm.grad(c),
            "c_s": c_s,
            "N_s": pybamm.grad(c_s),
        }
        modeltest = tests.StandardModelTest(model)
        modeltest.test_all()
        # set up testing
        t_sol, y_sol = modeltest.solution.t, modeltest.solution.y
        x = pybamm.SpatialVariable("x", domain=whole_cell)
        x_sol = modeltest.disc.process_symbol(x).entries[:, 0]
        processed_vars = pybamm.post_process_variables(model.variables, t_sol,
                                                       y_sol,
                                                       modeltest.disc.mesh)

        # test
        np.testing.assert_array_almost_equal(
            processed_vars["c"](t_sol, x_sol),
            np.ones_like(x_sol)[:, np.newaxis] * np.exp(-t_sol),
        )
Ejemplo n.º 10
0
    def test_grad_div_shapes_mixed_domain(self):
        """
        Test grad and div with Dirichlet boundary conditions (applied by grad on var)
        """
        # create discretisation
        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.SpectralVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        # grad
        var = pybamm.Variable("var",
                              domain=["negative electrode", "separator"])
        grad_eqn = pybamm.grad(var)
        boundary_conditions = {
            var.id: {
                "left": (pybamm.Scalar(1), "Dirichlet"),
                "right": (pybamm.Scalar(1), "Dirichlet"),
            }
        }
        disc.bcs = boundary_conditions

        disc.set_variable_slices([var])

        grad_eqn_disc = disc.process_symbol(grad_eqn)

        combined_submesh = mesh.combine_submeshes("negative electrode",
                                                  "separator")
        constant_y = np.ones_like(combined_submesh.nodes[:, np.newaxis])
        np.testing.assert_array_almost_equal(
            grad_eqn_disc.evaluate(None, constant_y),
            np.zeros_like(combined_submesh.edges[:, np.newaxis]),
        )

        # div: test on linear y (should have laplacian zero) so change bcs
        linear_y = combined_submesh.nodes
        N = pybamm.grad(var)
        div_eqn = pybamm.div(N)
        boundary_conditions = {
            var.id: {
                "left": (pybamm.Scalar(0), "Dirichlet"),
                "right":
                (pybamm.Scalar(combined_submesh.edges[-1]), "Dirichlet"),
            }
        }
        disc.bcs = boundary_conditions

        grad_eqn_disc = disc.process_symbol(grad_eqn)
        np.testing.assert_array_almost_equal(
            grad_eqn_disc.evaluate(None, linear_y),
            np.ones_like(combined_submesh.edges[:, np.newaxis]),
        )

        div_eqn_disc = disc.process_symbol(div_eqn)
        np.testing.assert_array_almost_equal(
            div_eqn_disc.evaluate(None, linear_y),
            np.zeros_like(combined_submesh.nodes[:, np.newaxis]),
        )
Ejemplo n.º 11
0
 def _current_collector_heating(self, variables):
     """Returns the heat source terms in the 1D current collector"""
     phi_s_cn = variables["Negative current collector potential"]
     phi_s_cp = variables["Positive current collector potential"]
     Q_s_cn = self.param.sigma_cn_prime * pybamm.inner(
         pybamm.grad(phi_s_cn), pybamm.grad(phi_s_cn))
     Q_s_cp = self.param.sigma_cp_prime * pybamm.inner(
         pybamm.grad(phi_s_cp), pybamm.grad(phi_s_cp))
     return Q_s_cn, Q_s_cp
Ejemplo n.º 12
0
 def test_symbol_repr(self):
     """
     test that __repr___ returns the string
     `__class__(id, name, parent expression)`
     """
     a = pybamm.Symbol("a")
     b = pybamm.Symbol("b")
     c = pybamm.Symbol("c", domain=["test"])
     d = pybamm.Symbol("d",
                       domain=["test"],
                       auxiliary_domains={"sec": "other test"})
     hex_regex = r"\-?0x[0-9,a-f]+"
     self.assertRegex(
         a.__repr__(),
         r"Symbol\(" + hex_regex +
         r", a, children\=\[\], domain\=\[\], auxiliary_domains\=\{\}\)",
     )
     self.assertRegex(
         b.__repr__(),
         r"Symbol\(" + hex_regex +
         r", b, children\=\[\], domain\=\[\], auxiliary_domains\=\{\}\)",
     )
     self.assertRegex(
         c.__repr__(),
         r"Symbol\(" + hex_regex +
         r", c, children\=\[\], domain\=\['test'\], auxiliary_domains\=\{\}\)",
     )
     self.assertRegex(
         d.__repr__(),
         r"Symbol\(" + hex_regex +
         r", d, children\=\[\], domain\=\['test'\]" +
         r", auxiliary_domains\=\{'sec': \"\['other test'\]\"\}\)",
     )
     self.assertRegex(
         (a + b).__repr__(),
         r"Addition\(" + hex_regex +
         r", \+, children\=\['a', 'b'\], domain=\[\]",
     )
     self.assertRegex(
         (c * d).__repr__(),
         r"Multiplication\(" + hex_regex +
         r", \*, children\=\['c', 'd'\], domain=\['test'\]" +
         r", auxiliary_domains\=\{'sec': \"\['other test'\]\"\}\)",
     )
     self.assertRegex(
         pybamm.grad(a).__repr__(),
         r"Gradient\(" + hex_regex +
         r", grad, children\=\['a'\], domain=\[\], auxiliary_domains\=\{\}\)",
     )
     self.assertRegex(
         pybamm.grad(c).__repr__(),
         r"Gradient\(" + hex_regex +
         r", grad, children\=\['c'\], domain=\['test'\]" +
         r", auxiliary_domains\=\{\}\)",
     )
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
    def test_p2d_spherical_grad_div_shapes_Neumann_bcs(self):
        """
        Test grad and div with Dirichlet boundary conditions (applied by grad on var)
        in the pseudo 2-dimensional case
        """

        mesh = get_p2d_mesh_for_testing()
        spatial_methods = {"negative particle": pybamm.SpectralVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        n_mesh = mesh["negative particle"]

        mesh.add_ghost_meshes()
        disc.mesh.add_ghost_meshes()

        # test grad
        var = pybamm.Variable(
            "var",
            domain=["negative particle"],
            auxiliary_domains={"secondary": "negative electrode"},
        )
        grad_eqn = pybamm.grad(var)
        disc.set_variable_slices([var])
        grad_eqn_disc = disc.process_symbol(grad_eqn)

        prim_pts = n_mesh.npts
        sec_pts = mesh["negative electrode"].npts
        constant_y = np.kron(np.ones(sec_pts), np.ones(prim_pts))

        grad_eval = grad_eqn_disc.evaluate(None, constant_y)
        grad_eval = np.reshape(grad_eval, [sec_pts, prim_pts + 1])

        np.testing.assert_array_equal(grad_eval,
                                      np.zeros([sec_pts, prim_pts + 1]))

        # div
        # div (grad r^2) = 6, N_left = N_right = 0
        N = pybamm.grad(var)
        div_eqn = pybamm.div(N)
        boundary_conditions = {
            var.id: {
                "left": (pybamm.Scalar(0), "Neumann"),
                "right": (pybamm.Scalar(0), "Neumann"),
            }
        }
        disc.bcs = boundary_conditions
        div_eqn_disc = disc.process_symbol(div_eqn)

        const = 6 * np.ones(sec_pts * prim_pts)
        div_eval = div_eqn_disc.evaluate(None, const)
        div_eval = np.reshape(div_eval, [sec_pts, prim_pts])
        np.testing.assert_array_almost_equal(div_eval,
                                             np.zeros([sec_pts, prim_pts]))
Ejemplo n.º 15
0
    def get_coupled_variables(self, variables):
        param = self.param
        T = variables["Cell temperature"]
        eps = variables["Porosity"]
        c_e = variables["Electrolyte concentration"]
        phi_e = variables["Electrolyte potential"]

        i_e = (param.kappa_e(c_e, T) * (eps**param.b) * param.gamma_e /
               param.C_e) * (param.chi(c_e) * pybamm.grad(c_e) / c_e -
                             pybamm.grad(phi_e))

        variables.update(self._get_standard_current_variables(i_e))

        return variables
Ejemplo n.º 16
0
    def test_check_well_posedness_initial_boundary_conditions(self):
        # Well-posed model - Dirichlet
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        model = pybamm.BaseModel()
        c = pybamm.Variable("c", domain=whole_cell)
        model.rhs = {c: 5 * pybamm.div(pybamm.grad(c)) - 1}
        model.initial_conditions = {c: 1}
        model.boundary_conditions = {
            c: {
                "left": (0, "Dirichlet"),
                "right": (0, "Dirichlet")
            }
        }
        model.check_well_posedness()

        # Well-posed model - Neumann
        model.boundary_conditions = {
            c: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            }
        }
        model.check_well_posedness()

        # Model with bad initial conditions (expect assertion error)
        d = pybamm.Variable("d", domain=whole_cell)
        model.initial_conditions = {d: 3}
        with self.assertRaisesRegex(pybamm.ModelError, "initial condition"):
            model.check_well_posedness()

        # Algebraic well-posed model
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        model = pybamm.BaseModel()
        model.algebraic = {c: 5 * pybamm.div(pybamm.grad(c)) - 1}
        model.boundary_conditions = {
            c: {
                "left": (0, "Dirichlet"),
                "right": (0, "Dirichlet")
            }
        }
        model.check_well_posedness()
        model.boundary_conditions = {
            c: {
                "left": (0, "Neumann"),
                "right": (0, "Neumann")
            }
        }
        model.check_well_posedness()
Ejemplo n.º 17
0
    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
Ejemplo n.º 18
0
    def get_coupled_variables(self, variables):

        tor_0 = variables["Leading-order electrolyte tortuosity"]
        c_e_0_av = variables[
            "Leading-order x-averaged electrolyte concentration"]
        c_e = variables["Electrolyte concentration"]
        # i_e = variables["Electrolyte current density"]
        v_box_0 = variables["Leading-order volume-averaged velocity"]
        T_0 = variables["Leading-order cell temperature"]

        param = self.param

        N_e_diffusion = -tor_0 * param.D_e(c_e_0_av, T_0) * pybamm.grad(c_e)
        # N_e_migration = (param.C_e * param.t_plus) / param.gamma_e * i_e
        # N_e_convection = c_e * v_box_0

        # N_e = N_e_diffusion + N_e_migration + N_e_convection

        if v_box_0.id == pybamm.Scalar(0).id:
            N_e = N_e_diffusion
        else:
            N_e = N_e_diffusion + v_box_0 * c_e

        variables.update(self._get_standard_flux_variables(N_e))

        return variables
Ejemplo n.º 19
0
    def test_gradient(self):
        # gradient of scalar symbol should fail
        a = pybamm.Symbol("a")
        with self.assertRaisesRegex(
                pybamm.DomainError,
                "Cannot take gradient of 'a' since its domain is empty"):
            pybamm.Gradient(a)

        # gradient of variable evaluating on edges should fail
        a = pybamm.PrimaryBroadcastToEdges(pybamm.Scalar(1), "test")
        with self.assertRaisesRegex(TypeError, "evaluates on edges"):
            pybamm.Gradient(a)

        # gradient of broadcast should return broadcasted zero
        a = pybamm.PrimaryBroadcast(pybamm.Variable("a"), "test domain")
        grad = pybamm.grad(a)
        self.assertIsInstance(grad, pybamm.PrimaryBroadcastToEdges)
        self.assertIsInstance(grad.child, pybamm.PrimaryBroadcast)
        self.assertIsInstance(grad.child.child, pybamm.Scalar)
        self.assertEqual(grad.child.child.value, 0)

        # otherwise gradient should work
        a = pybamm.Symbol("a", domain="test domain")
        grad = pybamm.Gradient(a)
        self.assertEqual(grad.children[0].name, a.name)
        self.assertEqual(grad.domain, a.domain)
    def get_coupled_variables(self, variables):

        eps_0 = variables["Leading-order porosity"]
        c_e_0_av = variables[
            "Leading-order x-averaged electrolyte concentration"]
        c_e = variables["Electrolyte concentration"]
        # i_e = variables["Electrolyte current density"]
        v_box_0 = variables["Leading-order volume-averaged velocity"]
        T_0 = variables["Leading-order cell temperature"]

        param = self.param

        whole_cell = ["negative electrode", "separator", "positive electrode"]
        N_e_diffusion = (-(eps_0**param.b) * pybamm.PrimaryBroadcast(
            param.D_e(c_e_0_av, T_0), whole_cell) * pybamm.grad(c_e))
        # N_e_migration = (param.C_e * param.t_plus) / param.gamma_e * i_e
        # N_e_convection = c_e * v_box_0

        # N_e = N_e_diffusion + N_e_migration + N_e_convection

        if v_box_0.id == pybamm.Scalar(0).id:
            N_e = N_e_diffusion
        else:
            N_e = N_e_diffusion + pybamm.outer(v_box_0, c_e)

        variables.update(self._get_standard_flux_variables(N_e))

        return variables
Ejemplo n.º 21
0
    def get_coupled_variables(self, variables):

        phi_s = variables[self.domain + " electrode potential"]
        eps = variables[self.domain + " electrode porosity"]

        if self.domain == "Negative":
            sigma = self.param.sigma_n
            b = self.param.b_n
        elif self.domain == "Positive":
            sigma = self.param.sigma_p
            b = self.param.b_p

        sigma_eff = sigma * (1 - eps)**b
        i_s = -sigma_eff * pybamm.grad(phi_s)

        variables.update(
            {self.domain + " electrode effective conductivity": sigma_eff})

        variables.update(self._get_standard_current_variables(i_s))

        if self.domain == "Positive":
            variables.update(
                self._get_standard_whole_cell_variables(variables))

        return variables
Ejemplo n.º 22
0
    def test_new_copy(self):
        model = pybamm.BaseModel(name="a model")
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        c = pybamm.Variable("c", domain=whole_cell)
        d = pybamm.Variable("d", domain=whole_cell)
        model.rhs = {c: 5 * pybamm.div(pybamm.grad(d)) - 1, d: -c}
        model.initial_conditions = {c: 1, d: 2}
        model.boundary_conditions = {
            c: {
                "left": (0, "Dirichlet"),
                "right": (0, "Dirichlet")
            },
            d: {
                "left": (0, "Dirichlet"),
                "right": (0, "Dirichlet")
            },
        }
        model.use_jacobian = False
        model.use_simplify = False
        model.convert_to_format = "python"

        new_model = model.new_copy()
        self.assertEqual(new_model.name, model.name)
        self.assertEqual(new_model.use_jacobian, model.use_jacobian)
        self.assertEqual(new_model.use_simplify, model.use_simplify)
        self.assertEqual(new_model.convert_to_format, model.convert_to_format)
        self.assertEqual(new_model.timescale, model.timescale)
Ejemplo n.º 23
0
    def test_process_parameters_and_discretise(self):
        model = pybamm.lithium_ion.SPM()
        # Set up geometry and parameters
        geometry = model.default_geometry
        parameter_values = model.default_parameter_values
        parameter_values.process_geometry(geometry)
        # Set up discretisation
        mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        # Process expression
        c = pybamm.Parameter("Negative electrode thickness [m]") * pybamm.Variable(
            "X-averaged negative particle concentration",
            domain="negative particle",
            auxiliary_domains={"secondary": "current collector"},
        )
        processed_c = model.process_parameters_and_discretise(c, parameter_values, disc)
        self.assertIsInstance(processed_c, pybamm.Multiplication)
        self.assertIsInstance(processed_c.left, pybamm.Scalar)
        self.assertIsInstance(processed_c.right, pybamm.StateVector)
        # Process flux manually and check result against flux computed in particle
        # submodel
        c_n = model.variables["X-averaged negative particle concentration"]
        T = pybamm.PrimaryBroadcast(
            model.variables["X-averaged negative electrode temperature"],
            ["negative particle"],
        )
        D = model.param.D_n(c_n, T)
        N = -D * pybamm.grad(c_n)

        flux_1 = model.process_parameters_and_discretise(N, parameter_values, disc)
        flux_2 = model.variables["X-averaged negative particle flux"]
        param_flux_2 = parameter_values.process_symbol(flux_2)
        disc_flux_2 = disc.process_symbol(param_flux_2)
        self.assertEqual(flux_1.id, disc_flux_2.id)
Ejemplo n.º 24
0
    def test_gradient(self):
        mesh = get_unit_2p1D_mesh_for_testing(ypts=32, zpts=32, include_particles=False)
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(),
            "current collector": pybamm.ScikitFiniteElement(),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)

        # test gradient of 5*y + 6*z
        var = pybamm.Variable("var", domain="current collector")
        disc.set_variable_slices([var])

        y = mesh["current collector"].coordinates[0, :]
        z = mesh["current collector"].coordinates[1, :]

        gradient = pybamm.grad(var)
        grad_disc = disc.process_symbol(gradient)
        grad_disc_y, grad_disc_z = grad_disc.children

        np.testing.assert_array_almost_equal(
            grad_disc_y.evaluate(None, 5 * y + 6 * z),
            5 * np.ones_like(y)[:, np.newaxis],
        )
        np.testing.assert_array_almost_equal(
            grad_disc_z.evaluate(None, 5 * y + 6 * z),
            6 * np.ones_like(z)[:, np.newaxis],
        )

        # check grad_squared positive
        eqn = pybamm.grad_squared(var)
        eqn_disc = disc.process_symbol(eqn)
        ans = eqn_disc.evaluate(None, 3 * y ** 2)
        np.testing.assert_array_less(0, ans)
Ejemplo n.º 25
0
    def get_coupled_variables(self, variables):
        param = self.param
        T = variables["Cell temperature"]
        tor = variables["Electrolyte tortuosity"]
        c_e = variables["Electrolyte concentration"]
        phi_e = variables["Electrolyte potential"]

        i_e = (param.kappa_e(c_e, T) * tor * param.gamma_e /
               param.C_e) * (param.chi(c_e, T) *
                             (1 + param.Theta * T) * pybamm.grad(c_e) / c_e -
                             pybamm.grad(phi_e))

        variables.update(self._get_standard_current_variables(i_e))
        variables.update(self._get_electrolyte_overpotentials(variables))

        return variables
Ejemplo n.º 26
0
    def test_grad_div_broadcast(self):
        # create mesh and discretisation
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        mesh = get_mesh_for_testing()
        disc = pybamm.Discretisation(mesh, spatial_methods)

        a = pybamm.PrimaryBroadcast(1, "negative electrode")
        grad_a = disc.process_symbol(pybamm.grad(a))
        np.testing.assert_array_equal(grad_a.evaluate(), 0)

        a_edge = pybamm.PrimaryBroadcastToEdges(1, "negative electrode")
        div_a = disc.process_symbol(pybamm.div(a_edge))
        np.testing.assert_array_equal(div_a.evaluate(), 0)

        div_grad_a = disc.process_symbol(pybamm.div(pybamm.grad(a)))
        np.testing.assert_array_equal(div_grad_a.evaluate(), 0)
Ejemplo n.º 27
0
    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)
        }
Ejemplo n.º 28
0
    def test_process_model_not_inplace(self):
        # concatenation of variables as the key
        c = pybamm.Variable("c", domain=["negative electrode"])
        N = pybamm.grad(c)
        model = pybamm.BaseModel()
        model.rhs = {c: pybamm.div(N)}
        model.initial_conditions = {c: pybamm.Scalar(3)}
        model.boundary_conditions = {
            c: {"left": (0, "Neumann"), "right": (0, "Neumann")}
        }
        model.check_well_posedness()

        # create discretisation
        disc = get_discretisation_for_testing()
        mesh = disc.mesh
        submesh = mesh["negative electrode"]

        discretised_model = disc.process_model(model, inplace=False)
        y0 = discretised_model.concatenated_initial_conditions.evaluate()
        np.testing.assert_array_equal(
            y0, 3 * np.ones_like(submesh.nodes[:, np.newaxis])
        )

        # grad and div are identity operators here
        np.testing.assert_array_equal(
            y0, discretised_model.concatenated_rhs.evaluate(None, y0)
        )
        discretised_model.check_well_posedness()
Ejemplo n.º 29
0
    def test_inner(self):
        model = pybamm.lithium_ion.BaseModel()

        phi_s = pybamm.standard_variables.phi_s_n
        i = pybamm.grad(phi_s)

        model.rhs = {phi_s: pybamm.inner(i, i)}
        model.boundary_conditions = {
            phi_s: {
                "left": (pybamm.Scalar(0), "Neumann"),
                "right": (pybamm.Scalar(0), "Neumann"),
            }
        }
        model.initial_conditions = {phi_s: pybamm.Scalar(0)}

        model.variables = {"inner": pybamm.inner(i, i)}

        # load parameter values and process model and geometry
        param = model.default_parameter_values
        geometry = model.default_geometry
        param.process_model(model)
        param.process_geometry(geometry)

        # set mesh
        mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)

        # discretise model
        disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
        disc.process_model(model)

        # check doesn't evaluate on edges anymore
        self.assertEqual(model.variables["inner"].evaluates_on_edges("primary"), False)
Ejemplo n.º 30
0
    def get_coupled_variables(self, variables):
        c_s = variables[self.domain + " particle concentration"]
        T = pybamm.PrimaryBroadcast(
            variables[self.domain + " electrode temperature"],
            [self.domain.lower() + " particle"],
        )

        if self.domain == "Negative":
            N_s = -self.param.D_n(c_s, T) * pybamm.grad(c_s)

        elif self.domain == "Positive":
            N_s = -self.param.D_p(c_s, T) * pybamm.grad(c_s)

        variables.update(self._get_standard_flux_variables(N_s, N_s))

        return variables