Exemple #1
0
    def test_macroscale_parameters(self):
        geo = pybamm.GeometricParameters()
        L_n = geo.L_n
        L_s = geo.L_s
        L_p = geo.L_p
        L_x = geo.L_x
        l_n = geo.l_n
        l_s = geo.l_s
        l_p = geo.l_p

        parameter_values = pybamm.ParameterValues(
            values={
                "Negative electrode thickness [m]": 0.05,
                "Separator thickness [m]": 0.02,
                "Positive electrode thickness [m]": 0.21,
            }
        )
        L_n_eval = parameter_values.process_symbol(L_n)
        L_s_eval = parameter_values.process_symbol(L_s)
        L_p_eval = parameter_values.process_symbol(L_p)
        L_x_eval = parameter_values.process_symbol(L_x)

        self.assertEqual(
            (L_n_eval + L_s_eval + L_p_eval).evaluate(), L_x_eval.evaluate()
        )
        l_n_eval = parameter_values.process_symbol(l_n)
        l_s_eval = parameter_values.process_symbol(l_s)
        l_p_eval = parameter_values.process_symbol(l_p)
        self.assertAlmostEqual((l_n_eval + l_s_eval + l_p_eval).evaluate(), 1)
Exemple #2
0
def yz_average(symbol):
    """convenience function for creating an average in the y-z-direction

    Parameters
    ----------
    symbol : :class:`pybamm.Symbol`
        The function to be averaged

    Returns
    -------
    :class:`Symbol`
        the new averaged symbol
    """
    # Symbol must have domain [] or ["current collector"]
    if symbol.domain not in [[], ["current collector"]]:
        raise pybamm.DomainError(
            """y-z-average only implemented in the 'current collector' domain,
            but symbol has domains {}""".format(symbol.domain))
    # If symbol doesn't have a domain, its average value is itself
    if symbol.domain == []:
        new_symbol = symbol.new_copy()
        new_symbol.parent = None
        return new_symbol
    # If symbol is a Broadcast, its average value is its child
    elif isinstance(symbol, pybamm.Broadcast):
        return symbol.orphans[0]
    # Otherwise, use Integral to calculate average value
    else:
        geo = pybamm.GeometricParameters()
        y = pybamm.standard_spatial_vars.y
        z = pybamm.standard_spatial_vars.z
        l_y = geo.l_y
        l_z = geo.l_z
        return Integral(symbol, [y, z]) / (l_y * l_z)
Exemple #3
0
    def __init__(self):

        # Get geometric parameters
        self.geo = pybamm.GeometricParameters()

        # Set parameters
        self._set_dimensional_parameters()
        self._set_dimensionless_parameters()
    def __init__(self):

        # Get geometric, electrical and thermal parameters
        self.geo = pybamm.GeometricParameters()
        self.elec = pybamm.ElectricalParameters()
        self.therm = pybamm.ThermalParameters()

        # Set parameters and scales
        self._set_dimensional_parameters()
        self._set_scales()
        self._set_dimensionless_parameters()

        # Set input current
        self._set_input_current()
Exemple #5
0
    def test_geometry(self):
        var = pybamm.standard_spatial_vars
        geo = pybamm.GeometricParameters()
        for cc_dimension in [0, 1, 2]:
            geometry = pybamm.battery_geometry(
                current_collector_dimension=cc_dimension)
            self.assertIsInstance(geometry, pybamm.Geometry)
            self.assertIn("negative electrode", geometry)
            self.assertIn("negative particle", geometry)
            self.assertEqual(geometry["negative electrode"][var.x_n]["min"], 0)
            self.assertEqual(geometry["negative electrode"][var.x_n]["max"].id,
                             geo.l_n.id)
            if cc_dimension == 1:
                self.assertIn("tabs", geometry["current collector"])

        geometry = pybamm.battery_geometry(include_particles=False)
        self.assertNotIn("negative particle", geometry)
    def __init__(self, model, param, disc, solution, operating_condition):
        self.model = model
        self.param = param
        self.disc = disc
        self.solution = solution
        self.operating_condition = operating_condition

        # Use dimensional time and space
        self.t = solution.t * model.timescale_eval
        geo = pybamm.GeometricParameters()

        L_x = param.evaluate(geo.L_x)
        self.x_n = disc.mesh["negative electrode"].nodes * L_x
        self.x_s = disc.mesh["separator"].nodes * L_x
        self.x_p = disc.mesh["positive electrode"].nodes * L_x
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        self.x = disc.mesh.combine_submeshes(*whole_cell).nodes * L_x
        self.x_n_edge = disc.mesh["negative electrode"].edges * L_x
        self.x_s_edge = disc.mesh["separator"].edges * L_x
        self.x_p_edge = disc.mesh["positive electrode"].edges * L_x
        self.x_edge = disc.mesh.combine_submeshes(*whole_cell).edges * L_x

        if isinstance(self.model, pybamm.lithium_ion.BaseModel):
            R_n = param.evaluate(geo.R_n)
            R_p = param.evaluate(geo.R_p)
            self.r_n = disc.mesh["negative particle"].nodes * R_n
            self.r_p = disc.mesh["positive particle"].nodes * R_p
            self.r_n_edge = disc.mesh["negative particle"].edges * R_n
            self.r_p_edge = disc.mesh["positive particle"].edges * R_p

        # Useful parameters
        self.l_n = param.evaluate(geo.l_n)
        self.l_p = param.evaluate(geo.l_p)

        current_param = self.model.param.current_with_time

        self.i_cell = param.process_symbol(current_param).evaluate(solution.t)
Exemple #7
0
    def test_read_parameters(self):
        geo = pybamm.GeometricParameters()
        L_n = geo.L_n
        L_s = geo.L_s
        L_p = geo.L_p
        L_y = geo.L_y
        L_z = geo.L_z
        tab_n_y = geo.Centre_y_tab_n
        tab_n_z = geo.Centre_z_tab_n
        L_tab_n = geo.L_tab_n
        tab_p_y = geo.Centre_y_tab_p
        tab_p_z = geo.Centre_z_tab_p
        L_tab_p = geo.L_tab_p

        geometry = pybamm.battery_geometry(current_collector_dimension=2)

        self.assertEqual(
            set([x.name for x in geometry.parameters]),
            set([
                x.name for x in [
                    L_n,
                    L_s,
                    L_p,
                    L_y,
                    L_z,
                    tab_n_y,
                    tab_n_z,
                    L_tab_n,
                    tab_p_y,
                    tab_p_z,
                    L_tab_p,
                ]
            ]),
        )
        self.assertTrue(
            all(isinstance(x, pybamm.Parameter) for x in geometry.parameters))
Exemple #8
0
def battery_geometry(include_particles=True, current_collector_dimension=0):
    """
    A convenience function to create battery geometries.

    Parameters
    ----------
    include_particles : bool
        Whether to include particle domains
    current_collector_dimensions : int, default
        The dimensions of the current collector. Should be 0 (default), 1 or 2

    Returns
    -------
    :class:`pybamm.Geometry`
        A geometry class for the battery

    """
    var = pybamm.standard_spatial_vars
    geo = pybamm.GeometricParameters()
    l_n = geo.l_n
    l_s = geo.l_s

    geometry = {
        "negative electrode": {var.x_n: {"min": 0, "max": l_n}},
        "separator": {var.x_s: {"min": l_n, "max": l_n + l_s}},
        "positive electrode": {var.x_p: {"min": l_n + l_s, "max": 1}},
    }
    # Add particle domains
    if include_particles is True:
        geometry.update(
            {
                "negative particle": {var.r_n: {"min": 0, "max": 1}},
                "positive particle": {var.r_p: {"min": 0, "max": 1}},
            }
        )

    if current_collector_dimension == 0:
        geometry["current collector"] = {var.z: {"position": 1}}
    elif current_collector_dimension == 1:
        geometry["current collector"] = {
            var.z: {"min": 0, "max": 1},
            "tabs": {
                "negative": {"z_centre": geo.centre_z_tab_n},
                "positive": {"z_centre": geo.centre_z_tab_p},
            },
        }
    elif current_collector_dimension == 2:
        geometry["current collector"] = {
            var.y: {"min": 0, "max": geo.l_y},
            var.z: {"min": 0, "max": geo.l_z},
            "tabs": {
                "negative": {
                    "y_centre": geo.centre_y_tab_n,
                    "z_centre": geo.centre_z_tab_n,
                    "width": geo.l_tab_n,
                },
                "positive": {
                    "y_centre": geo.centre_y_tab_p,
                    "z_centre": geo.centre_z_tab_p,
                    "width": geo.l_tab_p,
                },
            },
        }
    else:
        raise pybamm.GeometryError(
            "Invalid current collector dimension '{}' (should be 0, 1 or 2)".format(
                current_collector_dimension
            )
        )

    return pybamm.Geometry(geometry)
Exemple #9
0
        0.334,
        "Lower voltage cut-off [V]":
        3.0,
        "Upper voltage cut-off [V]":
        4.7,
    },
    check_already_exists=False)

param["Negative electrode OCP [V]"] = ecm.neg_OCP
param["Positive electrode OCP [V]"] = ecm.pos_OCP
param["Negative electrode OCP entropic change [V.K-1]"] = ecm.neg_dUdT
param["Positive electrode OCP entropic change [V.K-1]"] = ecm.pos_dUdT

e_width = param["Electrode width [m]"]
z_edges = np.linspace(0, e_height, Nspm + 1)
A_cc = param.evaluate(pybamm.GeometricParameters().A_cc)

param.process_model(model)
param.process_geometry(geometry)

sys.setrecursionlimit(10000)

var = pybamm.standard_spatial_vars
var_pts = {
    var.x_n: 5,
    var.x_s: 5,
    var.x_p: 5,
    var.r_n: 10,
    var.r_p: 10,
    var.z: Nspm,
}
Exemple #10
0
def x_average(symbol):
    """convenience function for creating an average in the x-direction

    Parameters
    ----------
    symbol : :class:`pybamm.Symbol`
        The function to be averaged

    Returns
    -------
    :class:`Symbol`
        the new averaged symbol
    """
    # Can't take average if the symbol evaluates on edges
    if symbol.evaluates_on_edges("primary"):
        raise ValueError(
            "Can't take the x-average of a symbol that evaluates on edges")
    # If symbol doesn't have a domain, its average value is itself
    if symbol.domain in [[], ["current collector"]]:
        new_symbol = symbol.new_copy()
        new_symbol.parent = None
        return new_symbol
    # If symbol is a Broadcast, its average value is its child
    elif isinstance(symbol, pybamm.Broadcast):
        return symbol.orphans[0]
    # If symbol is a concatenation of Broadcasts, its average value is its child
    elif (isinstance(symbol, pybamm.Concatenation) and all(
            isinstance(child, pybamm.Broadcast) for child in symbol.children)
          and symbol.domain
          == ["negative electrode", "separator", "positive electrode"]):
        a, b, c = [orp.orphans[0] for orp in symbol.orphans]
        if a.id == b.id == c.id:
            return a
        else:
            geo = pybamm.GeometricParameters()
            l_n = geo.l_n
            l_s = geo.l_s
            l_p = geo.l_p
            return (l_n * a + l_s * b + l_p * c) / (l_n + l_s + l_p)
    # Otherwise, use Integral to calculate average value
    else:
        geo = pybamm.GeometricParameters()
        if symbol.domain == ["negative electrode"]:
            x = pybamm.standard_spatial_vars.x_n
            l = geo.l_n
        elif symbol.domain == ["separator"]:
            x = pybamm.standard_spatial_vars.x_s
            l = geo.l_s
        elif symbol.domain == ["positive electrode"]:
            x = pybamm.standard_spatial_vars.x_p
            l = geo.l_p
        elif symbol.domain == [
                "negative electrode", "separator", "positive electrode"
        ]:
            x = pybamm.standard_spatial_vars.x
            l = pybamm.Scalar(1)
        elif symbol.domain == ["negative particle"]:
            x = pybamm.standard_spatial_vars.x_n
            l = geo.l_n
        elif symbol.domain == ["positive particle"]:
            x = pybamm.standard_spatial_vars.x_p
            l = geo.l_p
        else:
            x = pybamm.SpatialVariable("x", domain=symbol.domain)
            v = pybamm.ones_like(symbol)
            l = pybamm.Integral(v, x)
        return Integral(symbol, x) / l