Beispiel #1
0
 def test_name(self):
     a = pybamm.Symbol("a")
     x = np.linspace(0, 1, 200)
     interp = pybamm.Interpolant(x, x, a, "name")
     self.assertEqual(interp.name, "name")
     interp = pybamm.Interpolant(x, x, a)
     self.assertEqual(interp.name, "interpolating_function")
    def test_interpolation(self):
        x = np.linspace(0, 1)
        y = pybamm.StateVector(slice(0, 2))
        casadi_y = casadi.MX.sym("y", 2)
        # linear
        y_test = np.array([0.4, 0.6])
        for interpolator in ["linear", "pchip", "cubic spline"]:
            interp = pybamm.Interpolant(x, 2 * x, y, interpolator=interpolator)
            interp_casadi = interp.to_casadi(y=casadi_y)
            f = casadi.Function("f", [casadi_y], [interp_casadi])
            np.testing.assert_array_almost_equal(interp.evaluate(y=y_test),
                                                 f(y_test))
        # square
        y = pybamm.StateVector(slice(0, 1))
        for interpolator in ["pchip", "cubic spline"]:
            interp = pybamm.Interpolant(x, x**2, y, interpolator=interpolator)
            interp_casadi = interp.to_casadi(y=casadi_y)
            f = casadi.Function("f", [casadi_y], [interp_casadi])
            np.testing.assert_array_almost_equal(interp.evaluate(y=y_test),
                                                 f(y_test))

        # len(x)=1 but y is 2d
        y = pybamm.StateVector(slice(0, 1))
        casadi_y = casadi.MX.sym("y", 1)
        data = np.tile(2 * x, (10, 1)).T
        y_test = np.array([0.4])
        for interpolator in ["linear", "pchip", "cubic spline"]:
            interp = pybamm.Interpolant(x, data, y, interpolator=interpolator)
            interp_casadi = interp.to_casadi(y=casadi_y)
            f = casadi.Function("f", [casadi_y], [interp_casadi])
            np.testing.assert_array_almost_equal(interp.evaluate(y=y_test),
                                                 f(y_test))
Beispiel #3
0
 def test_errors(self):
     with self.assertRaisesRegex(ValueError,
                                 "data should have exactly two columns"):
         pybamm.Interpolant(np.ones(10), None)
     with self.assertRaisesRegex(ValueError,
                                 "interpolator 'bla' not recognised"):
         pybamm.Interpolant(np.ones((10, 2)), None, interpolator="bla")
Beispiel #4
0
    def test_interpolation(self):
        x = np.linspace(0, 1)[:, np.newaxis]
        y = pybamm.StateVector(slice(0, 2))
        # linear
        linear = np.hstack([x, 2 * x])
        for interpolator in ["pchip", "cubic spline"]:
            interp = pybamm.Interpolant(linear, y, interpolator=interpolator)
            np.testing.assert_array_almost_equal(
                interp.evaluate(y=np.array([0.397, 1.5]))[:, 0],
                np.array([0.794, 3]))
        # square
        square = np.hstack([x, x**2])
        y = pybamm.StateVector(slice(0, 1))
        for interpolator in ["pchip", "cubic spline"]:
            interp = pybamm.Interpolant(square, y, interpolator=interpolator)
            np.testing.assert_array_almost_equal(
                interp.evaluate(y=np.array([0.397]))[:, 0],
                np.array([0.397**2]))

        # with extrapolation set to False
        for interpolator in ["pchip", "cubic spline"]:
            interp = pybamm.Interpolant(square,
                                        y,
                                        interpolator=interpolator,
                                        extrapolate=False)
            np.testing.assert_array_equal(
                interp.evaluate(y=np.array([2]))[:, 0], np.array([np.nan]))
    def test_process_interpolant(self):
        x = np.linspace(0, 10)[:, np.newaxis]
        data = np.hstack([x, 2 * x])
        parameter_values = pybamm.ParameterValues(
            {"a": 3.01, "Times two": ("times two", data)}
        )

        a = pybamm.Parameter("a")
        func = pybamm.FunctionParameter("Times two", {"a": a})

        processed_func = parameter_values.process_symbol(func)
        self.assertIsInstance(processed_func, pybamm.Interpolant)
        self.assertEqual(processed_func.evaluate(), 6.02)

        # process differentiated function parameter
        diff_func = func.diff(a)
        processed_diff_func = parameter_values.process_symbol(diff_func)
        self.assertEqual(processed_diff_func.evaluate(), 2)

        # interpolant defined up front
        interp2 = pybamm.Interpolant(data[:, 0], data[:, 1], a)
        processed_interp2 = parameter_values.process_symbol(interp2)
        self.assertEqual(processed_interp2.evaluate(), 6.02)

        data3 = np.hstack([x, 3 * x])
        interp3 = pybamm.Interpolant(data3[:, 0], data3[:, 1], a)
        processed_interp3 = parameter_values.process_symbol(interp3)
        self.assertEqual(processed_interp3.evaluate(), 9.03)
def get_interp_fun(variable_name, domain):
    """
    Create a :class:`pybamm.Function` object using the variable, to allow plotting with
    :class:`pybamm.QuickPlot` (interpolate in space to match edges, and then create
    function to interpolate in time)
    """
    variable = comsol_variables[variable_name]
    if domain == ["negative electrode"]:
        comsol_x = comsol_variables["x_n"]
    elif domain == ["positive electrode"]:
        comsol_x = comsol_variables["x_p"]
    elif domain == whole_cell:
        comsol_x = comsol_variables["x"]

    # Make sure to use dimensional space
    pybamm_x = mesh.combine_submeshes(*domain).nodes * L_x
    variable = interp.interp1d(comsol_x, variable, axis=0)(pybamm_x)

    fun = pybamm.Interpolant(
        comsol_t,
        variable.T,
        pybamm.t * pybamm_model.timescale.evaluate(),
    )

    fun.domain = domain
    fun.mesh = mesh.combine_submeshes(*domain)
    fun.secondary_mesh = None
    return fun
Beispiel #7
0
    def test_processing(self):
        x = np.linspace(0, 1)[:, np.newaxis]
        y = pybamm.StateVector(slice(0, 2))
        linear = np.hstack([x, 2 * x])
        interp = pybamm.Interpolant(linear, y)

        self.assertEqual(interp.id, interp.new_copy().id)
        self.assertEqual(interp.id, interp.simplify().id)
Beispiel #8
0
 def _function_new_copy(self, children):
     """ See :meth:`Function._function_new_copy()` """
     return pybamm.Interpolant(self.data,
                               *children,
                               name=self.name,
                               interpolator=self.interpolator,
                               extrapolate=self.extrapolate,
                               entries_string=self.entries_string)
Beispiel #9
0
 def test_errors(self):
     with self.assertRaisesRegex(ValueError, "x1"):
         pybamm.Interpolant(np.ones(10), np.ones(11), pybamm.Symbol("a"))
     with self.assertRaisesRegex(ValueError, "x2"):
         pybamm.Interpolant((np.ones(10), np.ones(11)), np.ones((10, 12)),
                            pybamm.Symbol("a"))
     with self.assertRaisesRegex(ValueError, "y should"):
         pybamm.Interpolant((np.ones(10), np.ones(11)), np.ones(10),
                            pybamm.Symbol("a"))
     with self.assertRaisesRegex(ValueError,
                                 "interpolator 'bla' not recognised"):
         pybamm.Interpolant(np.ones(10),
                            np.ones(10),
                            pybamm.Symbol("a"),
                            interpolator="bla")
     with self.assertRaisesRegex(ValueError, "child should have size 1"):
         pybamm.Interpolant(np.ones(10), np.ones((10, 11)),
                            pybamm.StateVector(slice(0, 2)))
     with self.assertRaisesRegex(ValueError, "should equal"):
         pybamm.Interpolant((np.ones(10), np.ones(12)), np.ones((10, 12)),
                            pybamm.Symbol("a"))
     with self.assertRaisesRegex(ValueError,
                                 "interpolator should be 'linear'"):
         pybamm.Interpolant(
             (np.ones(10), np.ones(12)),
             np.ones((10, 12)),
             (pybamm.Symbol("a"), pybamm.Symbol("b")),
             interpolator="cubic spline",
         )
Beispiel #10
0
 def test_interpolation_1_x_2d_y(self):
     x = np.linspace(0, 1, 200)
     y = np.tile(2 * x, (10, 1)).T
     var = pybamm.StateVector(slice(0, 1))
     # linear
     for interpolator in ["linear", "pchip", "cubic spline"]:
         interp = pybamm.Interpolant(x, y, var, interpolator=interpolator)
         np.testing.assert_array_almost_equal(
             interp.evaluate(y=np.array([0.397])), 0.794 * np.ones((10, 1)))
Beispiel #11
0
 def test_interpolation_2_x_2d_y(self):
     x = (np.arange(-5.01, 5.01, 0.05), np.arange(-5.01, 5.01, 0.05))
     xx, yy = np.meshgrid(x[0], x[1])
     z = np.sin(xx**2 + yy**2)
     var1 = pybamm.StateVector(slice(0, 1))
     var2 = pybamm.StateVector(slice(1, 2))
     # linear
     interp = pybamm.Interpolant(x, z, (var1, var2), interpolator="linear")
     np.testing.assert_array_almost_equal(
         interp.evaluate(y=np.array([0, 0])), 0, decimal=3)
 def test_interpolation(self):
     x = np.linspace(0, 1)[:, np.newaxis]
     y = pybamm.StateVector(slice(0, 2))
     casadi_y = casadi.MX.sym("y", 2)
     # linear
     linear = np.hstack([x, 2 * x])
     y_test = np.array([0.4, 0.6])
     for interpolator in ["pchip", "cubic spline"]:
         interp = pybamm.Interpolant(linear, y, interpolator=interpolator)
         interp_casadi = interp.to_casadi(y=casadi_y)
         f = casadi.Function("f", [casadi_y], [interp_casadi])
         np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test))
     # square
     square = np.hstack([x, x ** 2])
     y = pybamm.StateVector(slice(0, 1))
     for interpolator in ["pchip", "cubic spline"]:
         interp = pybamm.Interpolant(square, y, interpolator=interpolator)
         interp_casadi = interp.to_casadi(y=casadi_y)
         f = casadi.Function("f", [casadi_y], [interp_casadi])
         np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test))
Beispiel #13
0
 def test_diff(self):
     x = np.linspace(0, 1)[:, np.newaxis]
     y = pybamm.StateVector(slice(0, 2))
     # linear (derivative should be 2)
     linear = np.hstack([x, 2 * x])
     for interpolator in ["pchip", "cubic spline"]:
         interp_diff = pybamm.Interpolant(linear,
                                          y,
                                          interpolator=interpolator).diff(y)
         np.testing.assert_array_almost_equal(
             interp_diff.evaluate(y=np.array([0.397, 1.5]))[:, 0],
             np.array([2, 2]))
     # square (derivative should be 2*x)
     square = np.hstack([x, x**2])
     for interpolator in ["pchip", "cubic spline"]:
         interp_diff = pybamm.Interpolant(square,
                                          y,
                                          interpolator=interpolator).diff(y)
         np.testing.assert_array_almost_equal(
             interp_diff.evaluate(y=np.array([0.397, 0.806]))[:, 0],
             np.array([0.794, 1.612]),
             decimal=3,
         )
Beispiel #14
0
 def test_diff(self):
     x = np.linspace(0, 1, 200)
     y = pybamm.StateVector(slice(0, 2))
     # linear (derivative should be 2)
     # linear interpolator cannot be differentiated
     for interpolator in ["pchip", "cubic spline"]:
         interp_diff = pybamm.Interpolant(x,
                                          2 * x,
                                          y,
                                          interpolator=interpolator).diff(y)
         np.testing.assert_array_almost_equal(
             interp_diff.evaluate(y=np.array([0.397, 1.5]))[:, 0],
             np.array([2, 2]))
     # square (derivative should be 2*x)
     for interpolator in ["pchip", "cubic spline"]:
         interp_diff = pybamm.Interpolant(x,
                                          x**2,
                                          y,
                                          interpolator=interpolator).diff(y)
         np.testing.assert_array_almost_equal(
             interp_diff.evaluate(y=np.array([0.397, 0.806]))[:, 0],
             np.array([0.794, 1.612]),
             decimal=3,
         )
    def test_drive_cycle_interpolant(self):
        model = pybamm.lithium_ion.SPM()
        param = model.default_parameter_values
        # Import drive cycle from file
        drive_cycle = pd.read_csv(
            pybamm.get_parameters_filepath(
                os.path.join("input", "drive_cycles", "US06.csv")
            ),
            comment="#",
            skip_blank_lines=True,
            header=None,
        ).to_numpy()

        timescale = param.evaluate(model.timescale)

        current_interpolant = pybamm.Interpolant(
            drive_cycle[:, 0], drive_cycle[:, 1], timescale * pybamm.t
        )

        param["Current function [A]"] = current_interpolant

        time_data = drive_cycle[:, 0]

        sim = pybamm.Simulation(model, parameter_values=param)

        # check solution is returned at the times in the data
        sim.solve()
        tau = sim.model.timescale.evaluate()
        np.testing.assert_array_almost_equal(sim.solution.t, time_data / tau)

        # check warning raised if the largest gap in t_eval is bigger than the
        # smallest gap in the data
        with self.assertWarns(pybamm.SolverWarning):
            sim.solve(t_eval=np.linspace(0, 1, 100))

        # check warning raised if t_eval doesnt contain time_data , but has a finer
        # resolution (can still solve, but good for users to know they dont have
        # the solution returned at the data points)
        with self.assertWarns(pybamm.SolverWarning):
            sim.solve(t_eval=np.linspace(0, time_data[-1], 800))
Beispiel #16
0
os.chdir(pybamm.__path__[0] + "/..")

pybamm.set_logging_level("INFO")

# load model and update parameters so the input current is the US06 drive cycle
model = pybamm.lithium_ion.SPMe({"thermal": "lumped"})
param = model.default_parameter_values

# import drive cycle from file
drive_cycle = pd.read_csv("pybamm/input/drive_cycles/US06.csv",
                          comment="#",
                          header=None).to_numpy()

# create interpolant
timescale = param.evaluate(model.timescale)
current_interpolant = pybamm.Interpolant(drive_cycle[:, 0], drive_cycle[:, 1],
                                         timescale * pybamm.t)

# set drive cycle
param["Current function [A]"] = current_interpolant

# create and run simulation using the CasadiSolver in "fast" mode, remembering to
# pass in the updated parameters
sim = pybamm.Simulation(model,
                        parameter_values=param,
                        solver=pybamm.CasadiSolver(mode="fast"))
sim.solve()
sim.plot([
    "Negative particle surface concentration [mol.m-3]",
    "Electrolyte concentration [mol.m-3]",
    "Positive particle surface concentration [mol.m-3]",
    "Current [A]",
Beispiel #17
0
pybamm.set_logging_level("INFO")

# load model and update parameters so the input current is the US06 drive cycle
model = pybamm.lithium_ion.SPMe({"thermal": "lumped"})
param = model.default_parameter_values


# import drive cycle from file
drive_cycle = pd.read_csv(
    "pybamm/input/drive_cycles/US06.csv", comment="#", header=None
).to_numpy()

# create interpolant
timescale = param.evaluate(model.timescale)
current_interpolant = pybamm.Interpolant(drive_cycle, timescale * pybamm.t)

# set drive cycle
param["Current function [A]"] = current_interpolant


# create and run simulation using the CasadiSolver in "fast" mode, remembering to
# pass in the updated parameters
sim = pybamm.Simulation(
    model, parameter_values=param, solver=pybamm.CasadiSolver(mode="fast")
)
sim.solve()
sim.plot(
    [
        "Negative particle surface concentration [mol.m-3]",
        "Electrolyte concentration [mol.m-3]",
Beispiel #18
0
 def test_name(self):
     a = pybamm.Symbol("a")
     x = np.linspace(0, 1)[:, np.newaxis]
     interp = pybamm.Interpolant(np.hstack([x, x]), a, "name")
     self.assertEqual(interp.name, "interpolating function (name)")
Beispiel #19
0
    def _process_symbol(self, symbol):
        """ See :meth:`ParameterValues.process_symbol()`. """

        if isinstance(symbol, pybamm.Parameter):
            value = self[symbol.name]
            # Scalar inherits name (for updating parameters) and domain (for Broadcast)
            return pybamm.Scalar(value, name=symbol.name, domain=symbol.domain)

        elif isinstance(symbol, pybamm.FunctionParameter):
            new_children = [self.process_symbol(child) for child in symbol.children]
            function_name = self[symbol.name]

            # if current setter, process any parameters that are symbols and
            # store the evaluated symbol in the parameters_eval dict
            if isinstance(function_name, pybamm.GetCurrent):
                for param, sym in function_name.parameters.items():
                    if isinstance(sym, pybamm.Symbol):
                        new_sym = self.process_symbol(sym)
                        function_name.parameters[param] = new_sym
                        function_name.parameters_eval[param] = new_sym.evaluate()
                # If loading data, need to update interpolant with
                # evaluated parameters
                if isinstance(function_name, pybamm.GetCurrentData):
                    function_name.interpolate()

            # Create Function or Interpolant objec
            if isinstance(function_name, tuple):
                # If function_name is a tuple then it should be (name, data) and we need
                # to create an Interpolant
                name, data = function_name
                function = pybamm.Interpolant(data, *new_children, name=name)
            else:
                # otherwise create standard function
                function = pybamm.Function(function_name, *new_children)
            # Differentiate if necessary
            if symbol.diff_variable is None:
                return function
            else:
                # return differentiated function
                new_diff_variable = self.process_symbol(symbol.diff_variable)
                return function.diff(new_diff_variable)

        elif isinstance(symbol, pybamm.BinaryOperator):
            # process children
            new_left = self.process_symbol(symbol.left)
            new_right = self.process_symbol(symbol.right)
            # make new symbol, ensure domain remains the same
            new_symbol = symbol.__class__(new_left, new_right)
            new_symbol.domain = symbol.domain
            return new_symbol

        # Unary operators
        elif isinstance(symbol, pybamm.UnaryOperator):
            new_child = self.process_symbol(symbol.child)
            new_symbol = symbol._unary_new_copy(new_child)
            # ensure domain remains the same
            new_symbol.domain = symbol.domain
            return new_symbol

        # Functions
        elif isinstance(symbol, pybamm.Function):
            new_children = [self.process_symbol(child) for child in symbol.children]
            return symbol._function_new_copy(new_children)

        # Concatenations
        elif isinstance(symbol, pybamm.Concatenation):
            new_children = [self.process_symbol(child) for child in symbol.children]
            return symbol._concatenation_new_copy(new_children)

        else:
            # Backup option: return new copy of the object
            try:
                return symbol.new_copy()
            except NotImplementedError:
                raise NotImplementedError(
                    "Cannot process parameters for symbol of type '{}'".format(
                        type(symbol)
                    )
                )
    fun.domain = domain
    fun.mesh = mesh.combine_submeshes(*domain)
    fun.secondary_mesh = None
    return fun


comsol_c_n_surf = get_interp_fun("c_n_surf", ["negative electrode"])
comsol_c_e = get_interp_fun("c_e", whole_cell)
comsol_c_p_surf = get_interp_fun("c_p_surf", ["positive electrode"])
comsol_phi_n = get_interp_fun("phi_n", ["negative electrode"])
comsol_phi_e = get_interp_fun("phi_e", whole_cell)
comsol_phi_p = get_interp_fun("phi_p", ["positive electrode"])
comsol_voltage = pybamm.Interpolant(
    comsol_t,
    comsol_variables["voltage"],
    pybamm.t * pybamm_model.timescale.evaluate(),
)

comsol_voltage.mesh = None
comsol_voltage.secondary_mesh = None

# Create comsol model with dictionary of Matrix variables
comsol_model = pybamm.lithium_ion.BaseModel()
comsol_model.variables = {
    "Negative particle surface concentration [mol.m-3]": comsol_c_n_surf,
    "Electrolyte concentration [mol.m-3]": comsol_c_e,
    "Positive particle surface concentration [mol.m-3]": comsol_c_p_surf,
    "Current [A]": pybamm_model.variables["Current [A]"],
    "Negative electrode potential [V]": comsol_phi_n,
    "Electrolyte potential [V]": comsol_phi_e,
Beispiel #21
0
    def _process_symbol(self, symbol):
        """ See :meth:`ParameterValues.process_symbol()`. """

        if isinstance(symbol, pybamm.Parameter):
            value = self[symbol.name]
            if isinstance(value, numbers.Number):
                # Scalar inherits name (for updating parameters) and domain (for
                # Broadcast)
                return pybamm.Scalar(value,
                                     name=symbol.name,
                                     domain=symbol.domain)
            elif isinstance(value, pybamm.Symbol):
                new_value = self.process_symbol(value)
                new_value.domain = symbol.domain
                return new_value
            else:
                raise TypeError("Cannot process parameter '{}'".format(value))

        elif isinstance(symbol, pybamm.FunctionParameter):
            new_children = []
            for child in symbol.children:
                if symbol.diff_variable is not None and any(
                        x.id == symbol.diff_variable.id
                        for x in child.pre_order()):
                    # Wrap with NotConstant to avoid simplification,
                    # which would stop symbolic diff from working properly
                    new_child = pybamm.NotConstant(child.new_copy())
                    new_children.append(self.process_symbol(new_child))
                else:
                    new_children.append(self.process_symbol(child))
            function_name = self[symbol.name]

            # Create Function or Interpolant or Scalar object
            if isinstance(function_name, tuple):
                # If function_name is a tuple then it should be (name, data) and we need
                # to create an Interpolant
                name, data = function_name
                function = pybamm.Interpolant(data[:, 0],
                                              data[:, 1],
                                              *new_children,
                                              name=name)
                # Define event to catch extrapolation. In these events the sign is
                # important: it should be positive inside of the range and negative
                # outside of it
                self.parameter_events.append(
                    pybamm.Event(
                        "Interpolant {} lower bound".format(name),
                        pybamm.min(new_children[0] - min(data[:, 0])),
                        pybamm.EventType.INTERPOLANT_EXTRAPOLATION,
                    ))
                self.parameter_events.append(
                    pybamm.Event(
                        "Interpolant {} upper bound".format(name),
                        pybamm.min(max(data[:, 0]) - new_children[0]),
                        pybamm.EventType.INTERPOLANT_EXTRAPOLATION,
                    ))
            elif isinstance(function_name, numbers.Number):
                # If the "function" is provided is actually a scalar, return a Scalar
                # object instead of throwing an error.
                # Also use ones_like so that we get the right shapes
                function = pybamm.Scalar(
                    function_name,
                    name=symbol.name) * pybamm.ones_like(*new_children)
            elif (isinstance(function_name, pybamm.Symbol)
                  and function_name.evaluates_to_number()):
                # If the "function" provided is a pybamm scalar-like, use ones_like to
                # get the right shape
                # This also catches input parameters
                function = function_name * pybamm.ones_like(*new_children)
            elif callable(function_name):
                # otherwise evaluate the function to create a new PyBaMM object
                function = function_name(*new_children)
            elif isinstance(function_name, pybamm.Interpolant):
                function = function_name
            else:
                raise TypeError(
                    "Parameter provided for '{}' ".format(symbol.name) +
                    "is of the wrong type (should either be scalar-like or callable)"
                )
            # Differentiate if necessary
            if symbol.diff_variable is None:
                function_out = function
            else:
                # return differentiated function
                new_diff_variable = self.process_symbol(symbol.diff_variable)
                function_out = function.diff(new_diff_variable)
            # Convert possible float output to a pybamm scalar
            if isinstance(function_out, numbers.Number):
                return pybamm.Scalar(function_out)
            # Process again just to be sure
            return self.process_symbol(function_out)

        elif isinstance(symbol, pybamm.BinaryOperator):
            # process children
            new_left = self.process_symbol(symbol.left)
            new_right = self.process_symbol(symbol.right)
            # Special case for averages, which can appear as "integral of a broadcast"
            # divided by "integral of a broadcast"
            # this construction seems very specific but can appear often when averaging
            if (isinstance(symbol, pybamm.Division)
                    # right is integral(Broadcast(1))
                    and (isinstance(new_right, pybamm.Integral)
                         and isinstance(new_right.child, pybamm.Broadcast)
                         and new_right.child.child.id == pybamm.Scalar(1).id)
                    # left is integral
                    and isinstance(new_left, pybamm.Integral)):
                # left is integral(Broadcast)
                if (isinstance(new_left.child, pybamm.Broadcast)
                        and new_left.child.child.domain == []):
                    integrand = new_left.child
                    if integrand.auxiliary_domains == {}:
                        return integrand.orphans[0]
                    else:
                        domain = integrand.auxiliary_domains["secondary"]
                        if "tertiary" not in integrand.auxiliary_domains:
                            return pybamm.PrimaryBroadcast(
                                integrand.orphans[0], domain)
                        else:
                            auxiliary_domains = {
                                "secondary":
                                integrand.auxiliary_domains["tertiary"]
                            }
                            return pybamm.FullBroadcast(
                                integrand.orphans[0], domain,
                                auxiliary_domains)
                # left is "integral of concatenation of broadcasts"
                elif isinstance(new_left.child, pybamm.Concatenation) and all(
                        isinstance(child, pybamm.Broadcast)
                        for child in new_left.child.children):
                    return self.process_symbol(pybamm.x_average(
                        new_left.child))
            # make new symbol, ensure domain remains the same
            new_symbol = symbol._binary_new_copy(new_left, new_right)
            new_symbol.domain = symbol.domain
            return new_symbol

        # Unary operators
        elif isinstance(symbol, pybamm.UnaryOperator):
            new_child = self.process_symbol(symbol.child)
            new_symbol = symbol._unary_new_copy(new_child)
            # ensure domain remains the same
            new_symbol.domain = symbol.domain
            return new_symbol

        # Functions
        elif isinstance(symbol, pybamm.Function):
            new_children = [
                self.process_symbol(child) for child in symbol.children
            ]
            return symbol._function_new_copy(new_children)

        # Concatenations
        elif isinstance(symbol, pybamm.Concatenation):
            new_children = [
                self.process_symbol(child) for child in symbol.children
            ]
            return symbol._concatenation_new_copy(new_children)

        else:
            # Backup option: return new copy of the object
            try:
                return symbol.new_copy()
            except NotImplementedError:
                raise NotImplementedError(
                    "Cannot process parameters for symbol of type '{}'".format(
                        type(symbol)))
Beispiel #22
0
    def _process_symbol(self, symbol):
        """ See :meth:`ParameterValues.process_symbol()`. """

        if isinstance(symbol, pybamm.Parameter):
            value = self[symbol.name]
            if isinstance(value, numbers.Number):
                # Scalar inherits name (for updating parameters) and domain (for
                # Broadcast)
                return pybamm.Scalar(value,
                                     name=symbol.name,
                                     domain=symbol.domain)
            elif isinstance(value, pybamm.InputParameter):
                value.domain = symbol.domain
                return value

        elif isinstance(symbol, pybamm.FunctionParameter):
            new_children = [
                self.process_symbol(child) for child in symbol.children
            ]
            function_name = self[symbol.name]

            # Create Function or Interpolant or Scalar object
            if isinstance(function_name, tuple):
                # If function_name is a tuple then it should be (name, data) and we need
                # to create an Interpolant
                name, data = function_name
                function = pybamm.Interpolant(data, *new_children, name=name)
            elif isinstance(function_name, numbers.Number):
                # If the "function" is provided is actually a scalar, return a Scalar
                # object instead of throwing an error.
                # Also use ones_like so that we get the right shapes
                function = pybamm.Scalar(
                    function_name,
                    name=symbol.name) * pybamm.ones_like(*new_children)
            elif isinstance(function_name, pybamm.InputParameter):
                # Replace the function with an input parameter
                function = function_name
            elif (isinstance(function_name, pybamm.Symbol)
                  and function_name.evaluates_to_number()):
                # If the "function" provided is a pybamm scalar-like, use ones_like to
                # get the right shape
                function = function_name * pybamm.ones_like(*new_children)
            elif callable(function_name):
                # otherwise evaluate the function to create a new PyBaMM object
                function = function_name(*new_children)
            else:
                raise TypeError(
                    "Parameter provided for '{}' ".format(symbol.name) +
                    "is of the wrong type (should either be scalar-like or callable)"
                )
            # Differentiate if necessary
            if symbol.diff_variable is None:
                function_out = function
            else:
                # return differentiated function
                new_diff_variable = self.process_symbol(symbol.diff_variable)
                function_out = function.diff(new_diff_variable)
            # Convert possible float output to a pybamm scalar
            if isinstance(function_out, numbers.Number):
                return pybamm.Scalar(function_out)
            # Process again just to be sure
            return self.process_symbol(function_out)

        elif isinstance(symbol, pybamm.BinaryOperator):
            # process children
            new_left = self.process_symbol(symbol.left)
            new_right = self.process_symbol(symbol.right)
            # make new symbol, ensure domain remains the same
            new_symbol = symbol._binary_new_copy(new_left, new_right)
            new_symbol.domain = symbol.domain
            return new_symbol

        # Unary operators
        elif isinstance(symbol, pybamm.UnaryOperator):
            new_child = self.process_symbol(symbol.child)
            new_symbol = symbol._unary_new_copy(new_child)
            # ensure domain remains the same
            new_symbol.domain = symbol.domain
            return new_symbol

        # Functions
        elif isinstance(symbol, pybamm.Function):
            new_children = [
                self.process_symbol(child) for child in symbol.children
            ]
            return symbol._function_new_copy(new_children)

        # Concatenations
        elif isinstance(symbol, pybamm.Concatenation):
            new_children = [
                self.process_symbol(child) for child in symbol.children
            ]
            return symbol._concatenation_new_copy(new_children)

        else:
            # Backup option: return new copy of the object
            try:
                return symbol.new_copy()
            except NotImplementedError:
                raise NotImplementedError(
                    "Cannot process parameters for symbol of type '{}'".format(
                        type(symbol)))
Beispiel #23
0
    def test_processing(self):
        x = np.linspace(0, 1, 200)
        y = pybamm.StateVector(slice(0, 2))
        interp = pybamm.Interpolant(x, 2 * x, y)

        self.assertEqual(interp.id, interp.new_copy().id)
Beispiel #24
0
def US06_experiment(reply=False):
    model = pybamm.lithium_ion.DFN()
    # import drive cycle from file
    if reply: 
        drive_cycle = pd.read_csv("drive_cycle.csv", comment="#", header=None).to_numpy()
    elif not reply:
        drive_cycle = pd.read_csv("US06.csv", comment="#", header=None).to_numpy()
    # create interpolant
    param = model.default_parameter_values
    timescale = param.evaluate(model.timescale)
    current_interpolant = pybamm.Interpolant(
        drive_cycle[:, 0], drive_cycle[:, 1], timescale * pybamm.t
    )
    # set drive cycle
    param["Current function [A]"] = current_interpolant

    sim_US06_1 = pybamm.Simulation(
        model, parameter_values=param, solver=pybamm.CasadiSolver(mode="fast")
    )
    sol_US06_1 = sim_US06_1.solve()

    if reply:
        time = foo1.plot_graph(sol_US06_1, sim_US06_1, reply=reply)
        return time

    solved = False
    print("REACHED")
    while not solved:
        try:
            cycle = US06_experiment_cycle()
            # cycle = ["Charge at 1 A until 4.1 V", "Hold at 4.1 V until 50 mA"]

            experiment = pybamm.Experiment(cycle)
            sim_cccv = pybamm.Simulation(model, experiment=experiment)
            sol_cccv = sim_cccv.solve()
            new_model = model.set_initial_conditions_from(sol_cccv, inplace=False)
            sim_US06_2 = pybamm.Simulation(
                new_model,
                parameter_values=param,
                solver=pybamm.CasadiSolver(mode="fast"),
            )
            sol_US06_2 = sim_US06_2.solve()
            # pybamm.dynamic_plot(
            #     [sol_US06_1, sol_US06_2],
            #     labels=["Default initial conditions", "Fully charged"],
            # )
            solution = [sol_US06_1, sol_US06_2]
            sim = [sim_US06_1, sim_US06_2]
            t = solution[0]["Time [s]"]
            final_time = int(t.entries[len(t.entries) - 1])
            time = random.randint(0, final_time)
            plot = pybamm.QuickPlot(
                sim,
                labels=["Default initial conditions", "Fully charged"],
                time_unit="seconds",
            )
            plot.plot(time)
            plot.fig.savefig("foo.png", dpi=300)
            print(sol_US06_1)
            solved = True

        except:
            pass
            # print("Exception")
    return time, cycle