Ejemplo n.º 1
0
    def test_process_function_parameter(self):
        parameter_values = pybamm.ParameterValues({
            "a":
            3,
            "func":
            pybamm.load_function("process_symbol_test_function.py"),
            "const":
            pybamm.load_function("process_symbol_test_constant_function.py"),
        })
        a = pybamm.Parameter("a")

        # process function
        func = pybamm.FunctionParameter("func", a)
        processed_func = parameter_values.process_symbol(func)
        self.assertIsInstance(processed_func, pybamm.Function)
        self.assertEqual(processed_func.evaluate(), 369)

        # process constant function
        const = pybamm.FunctionParameter("const", a)
        processed_const = parameter_values.process_symbol(const)
        self.assertIsInstance(processed_const, pybamm.Function)
        self.assertEqual(processed_const.evaluate(), 254)

        # process differentiated function parameter
        diff_func = func.diff(a)
        processed_diff_func = parameter_values.process_symbol(diff_func)
        self.assertEqual(processed_diff_func.evaluate(), 123)
Ejemplo n.º 2
0
    def test_process_function_parameter(self):
        parameter_values = pybamm.ParameterValues({
            "a":
            3,
            "func":
            pybamm.load_function("process_symbol_test_function.py"),
            "const":
            254,
        })
        a = pybamm.InputParameter("a")

        # process function
        func = pybamm.FunctionParameter("func", a)
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(u={"a": 3}), 369)

        # process constant function
        const = pybamm.FunctionParameter("const", a)
        processed_const = parameter_values.process_symbol(const)
        self.assertIsInstance(processed_const, pybamm.Scalar)
        self.assertEqual(processed_const.evaluate(), 254)

        # process differentiated function parameter
        diff_func = func.diff(a)
        processed_diff_func = parameter_values.process_symbol(diff_func)
        self.assertEqual(processed_diff_func.evaluate(u={"a": 3}), 123)

        # function itself as input (different to the variable being an input)
        parameter_values = pybamm.ParameterValues({"func": "[input]"})
        a = pybamm.Scalar(3)
        func = pybamm.FunctionParameter("func", a)
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(u={"func": 13}), 13)
Ejemplo n.º 3
0
    def test_electrolyte_conductivity(self):
        root = pybamm.root_dir()
        p = "pybamm/input/parameters/lithium-ion/electrolytes/lipf6_Landesfeind2019"
        k_path = os.path.join(root, p)
        files = [
            f for f in os.listdir(k_path)
            if ".py" in f and "_base" not in f and "conductivity" in f
        ]
        files.sort()
        funcs = [pybamm.load_function(os.path.join(k_path, f)) for f in files]
        T_ref = 298.15
        T = T_ref + 30.0
        c = 1000.0
        k = [np.around(f(c, T).value, 6) for f in funcs]
        self.assertEqual(k, [1.839786, 1.361015, 0.750259])
        T += 20
        k = [np.around(f(c, T).value, 6) for f in funcs]
        self.assertEqual(k, [2.292425, 1.664438, 0.880755])

        chemistry = pybamm.parameter_sets.Chen2020
        param = pybamm.ParameterValues(chemistry=chemistry)
        param["Electrolyte conductivity [S.m-1]"] = funcs[0]
        model = pybamm.lithium_ion.SPM()
        sim = pybamm.Simulation(model, parameter_values=param)
        sim.set_parameters()
        sim.build()
Ejemplo n.º 4
0
    def test_electrolyte_diffusivity(self):
        root = pybamm.root_dir()
        p = "pybamm/input/parameters/lithium-ion/electrolytes/lipf6_Landesfeind2019"
        d_path = os.path.join(root, p)
        files = [
            f for f in os.listdir(d_path)
            if ".py" in f and "_base" not in f and "diffusivity" in f
        ]
        files.sort()
        funcs = [pybamm.load_function(os.path.join(d_path, f)) for f in files]
        T_ref = 298.15
        T = T_ref + 30.0
        c = 1000.0
        D = [np.around(f(c, T).value, 16) for f in funcs]
        self.assertEqual(D, [5.796505e-10, 5.417881e-10, 5.608856e-10])
        T += 20
        D = [np.around(f(c, T).value, 16) for f in funcs]
        self.assertEqual(D, [8.5992e-10, 7.752815e-10, 7.907549e-10])

        chemistry = pybamm.parameter_sets.Chen2020
        param = pybamm.ParameterValues(chemistry=chemistry)
        param["Electrolyte diffusivity [m2.s-1]"] = funcs[0]
        model = pybamm.lithium_ion.SPM()
        sim = pybamm.Simulation(model, parameter_values=param)
        sim.set_parameters()
        sim.build()
Ejemplo n.º 5
0
 def update(self, values, check_conflict=False, path=""):
     # check parameter values
     values = self.check_and_update_parameter_values(values)
     # update
     for name, value in values.items():
         # check for conflicts
         if (
             check_conflict is True
             and name in self.keys()
             and not (self[name] == float(value) or self[name] == value)
         ):
             raise ValueError(
                 "parameter '{}' already defined with value '{}'".format(
                     name, self[name]
                 )
             )
         # if no conflicts, update, loading functions and data if they are specified
         else:
             # Functions are flagged with the string "[function]"
             if isinstance(value, str):
                 if value.startswith("[function]"):
                     self[name] = pybamm.load_function(
                         os.path.join(path, value[10:] + ".py")
                     )
                 # Inbuilt functions are flagged with the string "[inbuilt]"
                 elif value.startswith("[inbuilt class]"):
                     # Extra set of brackets at the end makes an instance of the
                     # class
                     self[name] = getattr(pybamm, value[15:])()
                 # Data is flagged with the string "[data]"
                 elif value.startswith("[data]"):
                     data = np.loadtxt(os.path.join(path, value[6:] + ".csv"))
                     # Save name and data
                     self[name] = (value[6:], data)
                 # Anything else should be a converted to a float
                 else:
                     self[name] = float(value)
             else:
                 self[name] = value
     # reset processed symbols
     self._processed_symbols = {}
Ejemplo n.º 6
0
    def test_process_function_parameter(self):
        parameter_values = pybamm.ParameterValues(
            {
                "a": 3,
                "func": pybamm.load_function(
                    os.path.join(
                        "tests",
                        "unit",
                        "test_parameters",
                        "data",
                        "process_symbol_test_function.py",
                    )
                ),
                "const": 254,
                "float_func": lambda x: 42,
                "mult": pybamm.InputParameter("b") * 5,
                "bad type": np.array([1, 2, 3]),
            }
        )
        a = pybamm.InputParameter("a")

        # process function
        func = pybamm.FunctionParameter("func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(inputs={"a": 3}), 369)

        # process constant function
        const = pybamm.FunctionParameter("const", {"a": a})
        processed_const = parameter_values.process_symbol(const)
        self.assertIsInstance(processed_const, pybamm.Scalar)
        self.assertEqual(processed_const.evaluate(), 254)

        # process case where parameter provided is a pybamm symbol
        # (e.g. a multiplication)
        mult = pybamm.FunctionParameter("mult", {"a": a})
        processed_mult = parameter_values.process_symbol(mult)
        self.assertEqual(processed_mult.evaluate(inputs={"a": 14, "b": 63}), 63 * 5)

        # process differentiated function parameter
        diff_func = func.diff(a)
        processed_diff_func = parameter_values.process_symbol(diff_func)
        self.assertEqual(processed_diff_func.evaluate(inputs={"a": 3}), 123)

        # make sure diff works, despite simplifications, when the child is constant
        a_const = pybamm.Scalar(3)
        func_const = pybamm.FunctionParameter("func", {"a": a_const})
        diff_func_const = func_const.diff(a_const)
        processed_diff_func_const = parameter_values.process_symbol(diff_func_const)
        self.assertEqual(processed_diff_func_const.evaluate(), 123)

        # function parameter that returns a python float
        func = pybamm.FunctionParameter("float_func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(), 42)

        # weird type raises error
        func = pybamm.FunctionParameter("bad type", {"a": a})
        with self.assertRaisesRegex(TypeError, "Parameter provided for"):
            parameter_values.process_symbol(func)

        # function itself as input (different to the variable being an input)
        parameter_values = pybamm.ParameterValues(
            {"func": "[input]", "vector func": pybamm.InputParameter("vec", "test")}
        )
        a = pybamm.Scalar(3)
        func = pybamm.FunctionParameter("func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(inputs={"func": 13}), 13)

        func = pybamm.FunctionParameter("vector func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(inputs={"vec": 13}), 13)

        # make sure function keeps the domain of the original function

        def my_func(x):
            return 2 * x

        x = pybamm.standard_spatial_vars.x_n
        func = pybamm.FunctionParameter("func", {"x": x})

        parameter_values = pybamm.ParameterValues({"func": my_func})
        func1 = parameter_values.process_symbol(func)

        parameter_values = pybamm.ParameterValues({"func": pybamm.InputParameter("a")})
        func2 = parameter_values.process_symbol(func)

        parameter_values = pybamm.ParameterValues(
            {"func": pybamm.InputParameter("a", "negative electrode")}
        )
        func3 = parameter_values.process_symbol(func)

        self.assertEqual(func1.domains, func2.domains)
        self.assertEqual(func1.domains, func3.domains)
Ejemplo n.º 7
0
    def test_load_function(self):
        # Test filename ends in '.py'
        with self.assertRaisesRegex(
                ValueError, "Expected filename.py, but got doesnotendindotpy"):
            pybamm.load_function("doesnotendindotpy")

        # Test exception if absolute file not found
        with self.assertRaisesRegex(
                ValueError, "is an absolute path, but the file is not found"):
            nonexistent_abs_file = os.path.join(os.getcwd(), "i_dont_exist.py")
            pybamm.load_function(nonexistent_abs_file)

        # Test exception if relative file not found
        with self.assertRaisesRegex(ValueError,
                                    "cannot be found in the PyBaMM directory"):
            pybamm.load_function("i_dont_exist.py")

        # Test exception if relative file found more than once
        with self.assertRaisesRegex(
                ValueError, "found multiple times in the PyBaMM directory"):
            pybamm.load_function("__init__.py")

        # Test exception if no matching function found in module
        with self.assertRaisesRegex(ValueError,
                                    "No function .+ found in module .+"):
            pybamm.load_function("process_symbol_bad_function.py")

        # Test function load with absolute path
        abs_test_path = os.path.join(
            os.getcwd(),
            "tests",
            "unit",
            "test_parameters",
            "data",
            "process_symbol_test_function.py",
        )
        self.assertTrue(os.path.isfile(abs_test_path))
        func = pybamm.load_function(abs_test_path)
        self.assertEqual(func(2), 246)

        # Test function load with relative path
        func = pybamm.load_function("process_symbol_test_function.py")
        self.assertEqual(func(3), 369)
Ejemplo n.º 8
0
    def update(self,
               values,
               check_conflict=False,
               check_already_exists=True,
               path=""):
        """
        Update parameter dictionary, while also performing some basic checks.

        Parameters
        ----------
        values : dict
            Dictionary of parameter values to update parameter dictionary with
        check_conflict : bool, optional
            Whether to check that a parameter in `values` has not already been defined
            in the parameter class when updating it, and if so that its value does not
            change. This is set to True during initialisation, when parameters are
            combined from different sources, and is False by default otherwise
        check_already_exists : bool, optional
            Whether to check that a parameter in `values` already exists when trying to
            update it. This is to avoid cases where an intended change in the parameters
            is ignored due a typo in the parameter name, and is True by default but can
            be manually overridden.
        path : string, optional
            Path from which to load functions
        """
        # check parameter values
        self.check_parameter_values(values)
        # update
        for name, value in values.items():
            # check for conflicts
            if (check_conflict is True and name in self.keys() and
                    not (self[name] == float(value) or self[name] == value)):
                raise ValueError(
                    "parameter '{}' already defined with value '{}'".format(
                        name, self[name]))
            # check parameter already exists (for updating parameters)
            if check_already_exists is True:
                try:
                    self._dict_items[name]
                except KeyError as err:
                    raise KeyError(
                        "Cannot update parameter '{}' as it does not ".format(
                            name) +
                        "have a default value. ({}). If you are ".format(
                            err.args[0]) +
                        "sure you want to update this parameter, use " +
                        "param.update({{name: value}}, check_already_exists=False)"
                    )
            # if no conflicts, update, loading functions and data if they are specified
            # Functions are flagged with the string "[function]"
            if isinstance(value, str):
                if value.startswith("[function]"):
                    loaded_value = pybamm.load_function(
                        os.path.join(path, value[10:]))
                    self._dict_items[name] = loaded_value
                    values[name] = loaded_value
                # Data is flagged with the string "[data]" or "[current data]"
                elif value.startswith("[current data]") or value.startswith(
                        "[data]"):
                    if value.startswith("[current data]"):
                        data_path = os.path.join(pybamm.root_dir(), "pybamm",
                                                 "input", "drive_cycles")
                        filename = os.path.join(data_path, value[14:] + ".csv")
                        function_name = value[14:]
                    else:
                        filename = os.path.join(path, value[6:] + ".csv")
                        function_name = value[6:]
                    filename = pybamm.get_parameters_filepath(filename)
                    data = pd.read_csv(filename,
                                       comment="#",
                                       skip_blank_lines=True,
                                       header=None).to_numpy()
                    # Save name and data
                    self._dict_items[name] = (function_name, data)
                    values[name] = (function_name, data)
                elif value == "[input]":
                    self._dict_items[name] = pybamm.InputParameter(name)
                # Anything else should be a converted to a float
                else:
                    self._dict_items[name] = float(value)
                    values[name] = float(value)
            else:
                self._dict_items[name] = value
        # reset processed symbols
        self._processed_symbols = {}
Ejemplo n.º 9
0
    def test_process_function_parameter(self):
        parameter_values = pybamm.ParameterValues({
            "a":
            3,
            "func":
            pybamm.load_function("process_symbol_test_function.py"),
            "const":
            254,
            "float_func":
            lambda x: 42,
            "mult":
            pybamm.InputParameter("b") * 5,
            "bad type":
            np.array([1, 2, 3]),
        })
        a = pybamm.InputParameter("a")

        # process function
        func = pybamm.FunctionParameter("func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(inputs={"a": 3}), 369)

        # process constant function
        const = pybamm.FunctionParameter("const", {"a": a})
        processed_const = parameter_values.process_symbol(const)
        self.assertIsInstance(processed_const, pybamm.Scalar)
        self.assertEqual(processed_const.evaluate(), 254)

        # process case where parameter provided is a pybamm symbol
        # (e.g. a multiplication)
        mult = pybamm.FunctionParameter("mult", {"a": a})
        processed_mult = parameter_values.process_symbol(mult)
        self.assertEqual(processed_mult.evaluate(inputs={
            "a": 14,
            "b": 63
        }), 63 * 5)

        # process differentiated function parameter
        diff_func = func.diff(a)
        processed_diff_func = parameter_values.process_symbol(diff_func)
        self.assertEqual(processed_diff_func.evaluate(inputs={"a": 3}), 123)

        # function parameter that returns a python float
        func = pybamm.FunctionParameter("float_func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(), 42)

        # weird type raises error
        func = pybamm.FunctionParameter("bad type", {"a": a})
        with self.assertRaisesRegex(TypeError, "Parameter provided for"):
            parameter_values.process_symbol(func)

        # function itself as input (different to the variable being an input)
        parameter_values = pybamm.ParameterValues({
            "func":
            "[input]",
            "vector func":
            pybamm.InputParameter("vec", "test")
        })
        a = pybamm.Scalar(3)
        func = pybamm.FunctionParameter("func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(inputs={"func": 13}), 13)

        func = pybamm.FunctionParameter("vector func", {"a": a})
        processed_func = parameter_values.process_symbol(func)
        self.assertEqual(processed_func.evaluate(inputs={"vec": 13}), 13)
Ejemplo n.º 10
0
    def test_load_params(self):
        data_D_e = {
            "EC_DMC_1_1": [1.94664e-10, 1.94233e-10],
            "EC_EMC_3_7": [2.01038e-10, 1.78391e-10],
            "EMC_FEC_19_1": [2.16871e-10, 1.8992e-10],
        }
        data_sigma_e = {
            "EC_DMC_1_1": [0.870352, 0.839076],
            "EC_EMC_3_7": [0.695252, 0.668677],
            "EMC_FEC_19_1": [0.454054, 0.632419],
        }
        data_TDF = {
            "EC_DMC_1_1": [1.84644, 4.16915],
            "EC_EMC_3_7": [1.82671, 3.9218],
            "EMC_FEC_19_1": [0.92532, 3.22481],
        }
        data_tplus = {
            "EC_DMC_1_1": [0.17651, 0.241924],
            "EC_EMC_3_7": [0.0118815, 0.151879],
            "EMC_FEC_19_1": [-0.0653014, 0.0416203],
        }

        T = [273.15 + 10.0, 273.15 + 30.0]
        c = [1000.0, 2000.0]

        for solvent in ["EC_DMC_1_1", "EC_EMC_3_7", "EMC_FEC_19_1"]:
            root = pybamm.root_dir()
            p = ("pybamm/input/parameters/lithium-ion/electrolytes/lipf6_" +
                 solvent + "_Landesfeind2019/")
            k_path = os.path.join(root, p)

            sigma_e = pybamm.load_function(
                os.path.join(
                    k_path,
                    "electrolyte_conductivity_" + solvent +
                    "_Landesfeind2019.py",
                ))
            D_e = pybamm.load_function(
                os.path.join(
                    k_path, "electrolyte_diffusivity_" + solvent +
                    "_Landesfeind2019.py"))
            TDF = pybamm.load_function(
                os.path.join(
                    k_path,
                    "electrolyte_TDF_" + solvent + "_Landesfeind2019.py"))
            tplus = pybamm.load_function(
                os.path.join(
                    k_path,
                    "electrolyte_transference_number_" + solvent +
                    "_Landesfeind2019.py",
                ))

            for i, _ in enumerate(T):
                self.assertAlmostEqual(sigma_e(c[i], T[i]).value,
                                       data_sigma_e[solvent][i],
                                       places=5)
                self.assertAlmostEqual(D_e(c[i], T[i]).value,
                                       data_D_e[solvent][i],
                                       places=5)
                self.assertAlmostEqual(TDF(c[i], T[i]),
                                       data_TDF[solvent][i],
                                       places=5)
                self.assertAlmostEqual(tplus(c[i], T[i]),
                                       data_tplus[solvent][i],
                                       places=5)
Ejemplo n.º 11
0
    def test_load_function(self):
        # Test filename ends in '.py'
        with self.assertRaisesRegex(
                ValueError, "Expected filename.py, but got doesnotendindotpy"):
            pybamm.load_function("doesnotendindotpy")

        # Test replace function and deprecation warning for lithium-ion
        with self.assertWarns(Warning):
            warn_path = os.path.join(
                "pybamm",
                "input",
                "parameters",
                "lithium-ion",
                "negative_electrodes",
                "graphite_Chen2020",
                "graphite_LGM50_electrolyte_exchange_current_density_Chen2020.py",
            )
            pybamm.load_function(warn_path)

        # Test replace function and deprecation warning for lead-acid
        with self.assertWarns(Warning):
            warn_path = os.path.join(
                "pybamm",
                "input",
                "parameters",
                "lead-acid",
                "negative_electrodes",
                "lead_Sulzer2019",
                "lead_exchange_current_density_Sulzer2019.py",
            )
            pybamm.load_function(warn_path)

        # Test exception if absolute file not found
        with self.assertRaisesRegex(
                ValueError, "is an absolute path, but the file is not found"):
            nonexistent_abs_file = os.path.join(os.getcwd(), "i_dont_exist.py")
            pybamm.load_function(nonexistent_abs_file)

        # Test exception if relative file not found
        with self.assertRaisesRegex(ValueError,
                                    "cannot be found in the PyBaMM directory"):
            pybamm.load_function("i_dont_exist.py")

        # Test exception if relative file found more than once
        with self.assertRaisesRegex(
                ValueError, "found multiple times in the PyBaMM directory"):
            pybamm.load_function("__init__.py")

        # Test exception if no matching function found in module
        with self.assertRaisesRegex(ValueError,
                                    "No function .+ found in module .+"):
            pybamm.load_function("process_symbol_bad_function.py")

        # Test function load with absolute path
        abs_test_path = os.path.join(
            pybamm.root_dir(),
            "tests",
            "unit",
            "test_parameters",
            "data",
            "process_symbol_test_function.py",
        )
        self.assertTrue(os.path.isfile(abs_test_path))
        func = pybamm.load_function(abs_test_path)
        self.assertEqual(func(2), 246)

        # Test function load with relative path
        func = pybamm.load_function("process_symbol_test_function.py")
        self.assertEqual(func(3), 369)
Ejemplo n.º 12
0
    def test_load_function(self):
        # Test replace function and deprecation warning for lithium-ion
        with self.assertWarns(Warning):
            warn_path = os.path.join(
                "pybamm",
                "input",
                "parameters",
                "lithium-ion",
                "negative_electrodes",
                "graphite_Chen2020",
                "graphite_LGM50_electrolyte_exchange_current_density_Chen2020.py",
            )
            pybamm.load_function(warn_path)

        # Test replace function and deprecation warning for lead-acid
        with self.assertWarns(Warning):
            warn_path = os.path.join(
                "pybamm",
                "input",
                "parameters",
                "lead-acid",
                "negative_electrodes",
                "lead_Sulzer2019",
                "lead_exchange_current_density_Sulzer2019.py",
            )
            pybamm.load_function(warn_path)

        # Test function load with absolute path
        abs_test_path = os.path.join(
            pybamm.root_dir(),
            "pybamm",
            "input",
            "parameters",
            "lithium_ion",
            "negative_electrodes",
            "graphite_Chen2020",
            "graphite_LGM50_electrolyte_exchange_current_density_Chen2020.py",
        )
        func = pybamm.load_function(abs_test_path)
        self.assertEqual(
            func,
            pybamm.input.parameters.lithium_ion.negative_electrodes.
            graphite_Chen2020.
            graphite_LGM50_electrolyte_exchange_current_density_Chen2020.
            graphite_LGM50_electrolyte_exchange_current_density_Chen2020,  # noqa
        )

        # Test function load with relative path
        rel_test_path = os.path.join(
            "pybamm",
            "input",
            "parameters",
            "lithium_ion",
            "negative_electrodes",
            "graphite_Chen2020",
            "graphite_LGM50_electrolyte_exchange_current_density_Chen2020.py",
        )
        func = pybamm.load_function(rel_test_path)
        self.assertEqual(
            func,
            pybamm.input.parameters.lithium_ion.negative_electrodes.
            graphite_Chen2020.
            graphite_LGM50_electrolyte_exchange_current_density_Chen2020.
            graphite_LGM50_electrolyte_exchange_current_density_Chen2020,  # noqa
        )