diff --git a/LICENSE b/LICENSE index 989aaa1..74adb19 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2023, PyBaMM Team +Copyright (c) 2024, PyBaMM Team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/LICENSES-bundled.txt b/LICENSES-bundled.txt new file mode 100644 index 0000000..d97adec --- /dev/null +++ b/LICENSES-bundled.txt @@ -0,0 +1,7 @@ +This project and source distributions bundle several libraries that are +compatibly licensed. + + +Name: PyBaMM +Files: src/pybamm_cookiecutter/parameters/* +License: BSD-3-Clause diff --git a/noxfile.py b/noxfile.py index a42638e..f4cb85c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,9 +1,13 @@ import nox +from pathlib import Path +import os # Options to modify nox behaviour nox.options.default_venv_backend = "uv|virtualenv" nox.options.reuse_existing_virtualenvs = True +VENV_DIR = Path("./venv").resolve() + @nox.session(name="docs") def build_docs(session: nox.Session) -> None: """Build the documentation and load it in a browser tab, rebuilding on changes.""" @@ -26,3 +30,11 @@ def run_template_generation(session): session.install("setuptools", silent=False) session.install("-e", ".[dev]", silent=False) session.run("pytest", "tests") + +@nox.session(name="dev") +def set_dev(session): + """Install pybamm-cookiecutter in editable mode""" + session.install("virtualenv") + session.run("virtualenv", os.fsdecode(VENV_DIR), silent=True) + python = os.fsdecode(VENV_DIR.joinpath("bin/python")) + session.run(python, "-m", "pip", "install", "-e", ".[dev]") diff --git a/pyproject.toml b/pyproject.toml index a55dc6a..dc0bda6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,9 @@ Homepage = "https://github.com/pybamm-team/pybamm-cookiecutter" Discussions = "https://github.com/pybamm-team/pybamm-cookiecutter/discussions" Changelog = "https://github.com/pybamm-team/pybamm-cookiecutter/releases" +[project.entry-points."cookie_parameter_sets"] +Chen2020 = "pybamm_cookiecutter.parameters.input.Chen2020:get_parameter_values" + [tool.hatch] version.source = "vcs" build.hooks.vcs.version-file = "src/pybamm_cookiecutter/_version.py" @@ -73,8 +76,8 @@ packages = [ "src/pybamm_cookiecutter", "tests" ] -python_version = "3.8" -strict = true +python_version = "3.11" +strict = false warn_return_any = false show_error_codes = true enable_error_code = [ @@ -84,6 +87,9 @@ enable_error_code = [ ] disallow_untyped_defs = false disallow_untyped_calls = false +ignore_missing_imports = true +allow_redefinition = true +disable_error_code = ["call-overload", "operator"] [tool.coverage] run.source = ["pybamm_cookiecutter"] @@ -92,32 +98,40 @@ port.exclude_lines = [ ] [tool.ruff] -select = [ - "E", "F", "W", # flake8 - "B", # flake8-bugbear - "I", # isort - "ARG", # flake8-unused-arguments - "C4", # flake8-comprehensions - "EM", # flake8-errmsg - "ICN", # flake8-import-conventions - "ISC", # flake8-implicit-str-concat - "G", # flake8-logging-format - "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PL", # pylint - "PT", # flake8-pytest-style - "PTH", # flake8-use-pathlib - "RET", # flake8-return - "RUF", # Ruff-specific - "SIM", # flake8-simplify - "T20", # flake8-print - "UP", # pyupgrade - "YTT", # flake8-2020 - "EXE", # flake8-executable - "NPY", # NumPy specific rules - "PD", # pandas-vet -] +extend-include = ["*.ipynb"] +extend-exclude = ["__init__.py"] src = ["src"] exclude = [] isort.required-imports = ["from __future__ import annotations"] flake8-unused-arguments.ignore-variadic-names = true + + +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "RUF", # Ruff-specific + "UP", # pyupgrade + "YTT", # flake8-2020 + "TID252", # relative-imports +] +ignore = [ + "E741", # Ambiguous variable name + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "SIM108", # Use ternary operator + "ARG001", # Unused function argument: + "ARG002", # Unused method arguments + "PLR2004", # Magic value used in comparison + "PLR0915", # Too many statements + "PLR0913", # Too many arguments + "PLR0912", # Too many branches + "RET504", # Unnecessary assignment + "RET505", # Unnecessary `else` + "RET506", # Unnecessary `elif` + "B018", # Found useless expression + "RUF002", # Docstring contains ambiguous + "UP007", # For pyupgrade +] + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["T20"] +"docs/*" = ["T20"] diff --git a/src/pybamm_cookiecutter/__init__.py b/src/pybamm_cookiecutter/__init__.py index 678b215..6dd71d2 100644 --- a/src/pybamm_cookiecutter/__init__.py +++ b/src/pybamm_cookiecutter/__init__.py @@ -8,8 +8,10 @@ import pybamm from ._version import version as __version__ +from .parameters.parameter_sets import parameter_sets __all__ : list[str] = [ "__version__", "pybamm", + "parameter_sets", ] diff --git a/src/pybamm_cookiecutter/parameters/__init__.py b/src/pybamm_cookiecutter/parameters/__init__.py new file mode 100644 index 0000000..6586100 --- /dev/null +++ b/src/pybamm_cookiecutter/parameters/__init__.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +__all__ = ["parameter_sets",] diff --git a/src/pybamm_cookiecutter/parameters/input/Chen2020.py b/src/pybamm_cookiecutter/parameters/input/Chen2020.py new file mode 100644 index 0000000..0b4a8a4 --- /dev/null +++ b/src/pybamm_cookiecutter/parameters/input/Chen2020.py @@ -0,0 +1,369 @@ +""" +This code is adopted from the PyBaMM project under the BSD-3-Clause + +Copyright (c) 2018-2024, the PyBaMM team. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + + +from __future__ import annotations + +import numpy as np +import pybamm +def graphite_LGM50_ocp_Chen2020(sto): + """ + LG M50 Graphite open-circuit potential as a function of stochiometry, fit taken + from [1]. + + References + ---------- + .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the + Electrochemical Society 167 (2020): 080534. + + Parameters + ---------- + sto: :class:`pybamm.Symbol` + Electrode stochiometry + + Returns + ------- + :class:`pybamm.Symbol` + Open-circuit potential + """ + + u_eq = ( + 1.9793 * np.exp(-39.3631 * sto) + + 0.2482 + - 0.0909 * np.tanh(29.8538 * (sto - 0.1234)) + - 0.04478 * np.tanh(14.9159 * (sto - 0.2769)) + - 0.0205 * np.tanh(30.4444 * (sto - 0.6103)) + ) + + return u_eq + + +def graphite_LGM50_electrolyte_exchange_current_density_Chen2020( + c_e, c_s_surf, c_s_max, T +): + """ + Exchange-current density for Butler-Volmer reactions between graphite and LiPF6 in + EC:DMC. + + References + ---------- + .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the + Electrochemical Society 167 (2020): 080534. + + Parameters + ---------- + c_e : :class:`pybamm.Symbol` + Electrolyte concentration [mol.m-3] + c_s_surf : :class:`pybamm.Symbol` + Particle concentration [mol.m-3] + c_s_max : :class:`pybamm.Symbol` + Maximum particle concentration [mol.m-3] + T : :class:`pybamm.Symbol` + Temperature [K] + + Returns + ------- + :class:`pybamm.Symbol` + Exchange-current density [A.m-2] + """ + m_ref = 6.48e-7 # (A/m2)(m3/mol)**1.5 - includes ref concentrations + E_r = 35000 + arrhenius = np.exp(E_r / pybamm.constants.R * (1 / 298.15 - 1 / T)) + + return m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 + + +def nmc_LGM50_ocp_Chen2020(sto): + """ + LG M50 NMC open-circuit potential as a function of stochiometry, fit taken + from [1]. + + References + ---------- + .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the + Electrochemical Society 167 (2020): 080534. + + Parameters + ---------- + sto: :class:`pybamm.Symbol` + Electrode stochiometry + + Returns + ------- + :class:`pybamm.Symbol` + Open-circuit potential + """ + + u_eq = ( + -0.8090 * sto + + 4.4875 + - 0.0428 * np.tanh(18.5138 * (sto - 0.5542)) + - 17.7326 * np.tanh(15.7890 * (sto - 0.3117)) + + 17.5842 * np.tanh(15.9308 * (sto - 0.3120)) + ) + + return u_eq + + +def nmc_LGM50_electrolyte_exchange_current_density_Chen2020(c_e, c_s_surf, c_s_max, T): + """ + Exchange-current density for Butler-Volmer reactions between NMC and LiPF6 in + EC:DMC. + + References + ---------- + .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the + Electrochemical Society 167 (2020): 080534. + + Parameters + ---------- + c_e : :class:`pybamm.Symbol` + Electrolyte concentration [mol.m-3] + c_s_surf : :class:`pybamm.Symbol` + Particle concentration [mol.m-3] + c_s_max : :class:`pybamm.Symbol` + Maximum particle concentration [mol.m-3] + T : :class:`pybamm.Symbol` + Temperature [K] + + Returns + ------- + :class:`pybamm.Symbol` + Exchange-current density [A.m-2] + """ + m_ref = 3.42e-6 # (A/m2)(m3/mol)**1.5 - includes ref concentrations + E_r = 17800 + arrhenius = np.exp(E_r / pybamm.constants.R * (1 / 298.15 - 1 / T)) + + return m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 + + +def electrolyte_diffusivity_Nyman2008(c_e, T): + """ + Diffusivity of LiPF6 in EC:EMC (3:7) as a function of ion concentration. The data + comes from [1] + + References + ---------- + .. [1] A. Nyman, M. Behm, and G. Lindbergh, "Electrochemical characterisation and + modelling of the mass transport phenomena in LiPF6-EC-EMC electrolyte," + Electrochim. Acta, vol. 53, no. 22, pp. 6356–6365, 2008. + + Parameters + ---------- + c_e: :class:`pybamm.Symbol` + Dimensional electrolyte concentration + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + D_c_e = 8.794e-11 * (c_e / 1000) ** 2 - 3.972e-10 * (c_e / 1000) + 4.862e-10 + + # Nyman et al. (2008) does not provide temperature dependence + + return D_c_e + + +def electrolyte_conductivity_Nyman2008(c_e, T): + """ + Conductivity of LiPF6 in EC:EMC (3:7) as a function of ion concentration. The data + comes from [1]. + + References + ---------- + .. [1] A. Nyman, M. Behm, and G. Lindbergh, "Electrochemical characterisation and + modelling of the mass transport phenomena in LiPF6-EC-EMC electrolyte," + Electrochim. Acta, vol. 53, no. 22, pp. 6356–6365, 2008. + + Parameters + ---------- + c_e: :class:`pybamm.Symbol` + Dimensional electrolyte concentration + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + sigma_e = ( + 0.1297 * (c_e / 1000) ** 3 - 2.51 * (c_e / 1000) ** 1.5 + 3.329 * (c_e / 1000) + ) + + # Nyman et al. (2008) does not provide temperature dependence + + return sigma_e + + +# Call dict via a function to avoid errors when editing in place +def get_parameter_values(): + """ + Parameters for an LG M50 cell, from the paper :footcite:t:`Chen2020` and references + therein. + + SEI parameters are example parameters for SEI growth from the papers + :footcite:t:`Ramadass2004`, :footcite:t:`ploehn2004solvent`, + :footcite:t:`single2018identifying`, :footcite:t:`safari2008multimodal`, and + :footcite:t:`Yang2017` + + .. note:: + This parameter set does not claim to be representative of the true parameter + values. Instead these are parameter values that were used to fit SEI models to + observed experimental data in the referenced papers. + """ + + return { + "chemistry": "lithium_ion", + # sei + "Ratio of lithium moles to SEI moles": 2.0, + "Inner SEI reaction proportion": 0.5, + "Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, + "Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, + "SEI reaction exchange current density [A.m-2]": 1.5e-07, + "SEI resistivity [Ohm.m]": 200000.0, + "Outer SEI solvent diffusivity [m2.s-1]": 2.5000000000000002e-22, + "Bulk solvent concentration [mol.m-3]": 2636.0, + "Inner SEI open-circuit potential [V]": 0.1, + "Outer SEI open-circuit potential [V]": 0.8, + "Inner SEI electron conductivity [S.m-1]": 8.95e-14, + "Inner SEI lithium interstitial diffusivity [m2.s-1]": 1e-20, + "Lithium interstitial reference concentration [mol.m-3]": 15.0, + "Initial inner SEI thickness [m]": 2.5e-09, + "Initial outer SEI thickness [m]": 2.5e-09, + "EC initial concentration in electrolyte [mol.m-3]": 4541.0, + "EC diffusivity [m2.s-1]": 2e-18, + "SEI kinetic rate constant [m.s-1]": 1e-12, + "SEI open-circuit potential [V]": 0.4, + "SEI growth activation energy [J.mol-1]": 0.0, + "Negative electrode reaction-driven LAM factor [m3.mol-1]": 0.0, + "Positive electrode reaction-driven LAM factor [m3.mol-1]": 0.0, + # cell + "Negative current collector thickness [m]": 1.2e-05, + "Negative electrode thickness [m]": 8.52e-05, + "Separator thickness [m]": 1.2e-05, + "Positive electrode thickness [m]": 7.56e-05, + "Positive current collector thickness [m]": 1.6e-05, + "Electrode height [m]": 0.065, + "Electrode width [m]": 1.58, + "Cell cooling surface area [m2]": 0.00531, + "Cell volume [m3]": 2.42e-05, + "Cell thermal expansion coefficient [m.K-1]": 1.1e-06, + "Negative current collector conductivity [S.m-1]": 58411000.0, + "Positive current collector conductivity [S.m-1]": 36914000.0, + "Negative current collector density [kg.m-3]": 8960.0, + "Positive current collector density [kg.m-3]": 2700.0, + "Negative current collector specific heat capacity [J.kg-1.K-1]": 385.0, + "Positive current collector specific heat capacity [J.kg-1.K-1]": 897.0, + "Negative current collector thermal conductivity [W.m-1.K-1]": 401.0, + "Positive current collector thermal conductivity [W.m-1.K-1]": 237.0, + "Nominal cell capacity [A.h]": 5.0, + "Current function [A]": 5.0, + "Contact resistance [Ohm]": 0, + # negative electrode + "Negative electrode conductivity [S.m-1]": 215.0, + "Maximum concentration in negative electrode [mol.m-3]": 33133.0, + "Negative particle diffusivity [m2.s-1]": 3.3e-14, + "Negative electrode OCP [V]": graphite_LGM50_ocp_Chen2020, + "Negative electrode porosity": 0.25, + "Negative electrode active material volume fraction": 0.75, + "Negative particle radius [m]": 5.86e-06, + "Negative electrode Bruggeman coefficient (electrolyte)": 1.5, + "Negative electrode Bruggeman coefficient (electrode)": 0, + "Negative electrode charge transfer coefficient": 0.5, + "Negative electrode double-layer capacity [F.m-2]": 0.2, + "Negative electrode exchange-current density [A.m-2]" + "": graphite_LGM50_electrolyte_exchange_current_density_Chen2020, + "Negative electrode density [kg.m-3]": 1657.0, + "Negative electrode specific heat capacity [J.kg-1.K-1]": 700.0, + "Negative electrode thermal conductivity [W.m-1.K-1]": 1.7, + "Negative electrode OCP entropic change [V.K-1]": 0.0, + # positive electrode + "Positive electrode conductivity [S.m-1]": 0.18, + "Maximum concentration in positive electrode [mol.m-3]": 63104.0, + "Positive particle diffusivity [m2.s-1]": 4e-15, + "Positive electrode OCP [V]": nmc_LGM50_ocp_Chen2020, + "Positive electrode porosity": 0.335, + "Positive electrode active material volume fraction": 0.665, + "Positive particle radius [m]": 5.22e-06, + "Positive electrode Bruggeman coefficient (electrolyte)": 1.5, + "Positive electrode Bruggeman coefficient (electrode)": 0, + "Positive electrode charge transfer coefficient": 0.5, + "Positive electrode double-layer capacity [F.m-2]": 0.2, + "Positive electrode exchange-current density [A.m-2]" + "": nmc_LGM50_electrolyte_exchange_current_density_Chen2020, + "Positive electrode density [kg.m-3]": 3262.0, + "Positive electrode specific heat capacity [J.kg-1.K-1]": 700.0, + "Positive electrode thermal conductivity [W.m-1.K-1]": 2.1, + "Positive electrode OCP entropic change [V.K-1]": 0.0, + # separator + "Separator porosity": 0.47, + "Separator Bruggeman coefficient (electrolyte)": 1.5, + "Separator density [kg.m-3]": 397.0, + "Separator specific heat capacity [J.kg-1.K-1]": 700.0, + "Separator thermal conductivity [W.m-1.K-1]": 0.16, + # electrolyte + "Initial concentration in electrolyte [mol.m-3]": 1000.0, + "Cation transference number": 0.2594, + "Thermodynamic factor": 1.0, + "Electrolyte diffusivity [m2.s-1]": electrolyte_diffusivity_Nyman2008, + "Electrolyte conductivity [S.m-1]": electrolyte_conductivity_Nyman2008, + # experiment + "Reference temperature [K]": 298.15, + "Total heat transfer coefficient [W.m-2.K-1]": 10.0, + "Ambient temperature [K]": 298.15, + "Number of electrodes connected in parallel to make a cell": 1.0, + "Number of cells connected in series to make a battery": 1.0, + "Lower voltage cut-off [V]": 2.5, + "Upper voltage cut-off [V]": 4.2, + "Open-circuit voltage at 0% SOC [V]": 2.5, + "Open-circuit voltage at 100% SOC [V]": 4.2, + "Initial concentration in negative electrode [mol.m-3]": 29866.0, + "Initial concentration in positive electrode [mol.m-3]": 17038.0, + "Initial temperature [K]": 298.15, + # citations + "notcite": ["Chen2020"], + } diff --git a/src/pybamm_cookiecutter/parameters/parameter_sets.py b/src/pybamm_cookiecutter/parameters/parameter_sets.py new file mode 100644 index 0000000..bcca96e --- /dev/null +++ b/src/pybamm_cookiecutter/parameters/parameter_sets.py @@ -0,0 +1,117 @@ +""" +This code is adopted from the PyBaMM project under the BSD-3-Clause + +Copyright (c) 2018-2024, the PyBaMM team. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + + +import importlib.metadata +import sys +import textwrap +from collections.abc import Mapping +from typing import Callable + +class ParameterSets(Mapping): + """ + Dict-like interface for accessing parameter sets through entry points in cookiecutter template. + Access via :py:data:`pybamm_cookiecutter.parameter_sets` + + Examples + -------- + Listing available parameter sets: + >>> import pybamm_cookiecutter + >>> list(pybamm_cookiecutter.parameter_sets) + ['Chen2020', ...] + + Get the docstring for a parameter set: + + + >>> print(pybamm_cookiecutter.parameter_sets.get_docstring("Ai2020")) + + Parameters for the Enertech cell (Ai2020), from the papers :footcite:t:`Ai2019`, + :footcite:t:`rieger2016new` and references therein. + ... + + See also: :ref:`adding-parameter-sets` + + """ + + def __init__(self): + """Dict of entry points for parameter sets, lazily load entry points as""" + self.__all_parameter_sets = dict() + for entry_point in self.get_entries("cookie_parameter_sets"): + self.__all_parameter_sets[entry_point.name] = entry_point + + @staticmethod + def get_entries(group_name): + """Wrapper for the importlib version logic""" + if sys.version_info < (3, 10): # pragma: no cover + return importlib.metadata.entry_points()[group_name] + else: + return importlib.metadata.entry_points(group=group_name) + + def __new__(cls): + """Ensure only one instance of ParameterSets exists""" + if not hasattr(cls, "instance"): + cls.instance = super().__new__(cls) + return cls.instance + + def __getitem__(self, key) -> dict: + return self._load_entry_point(key)() + + def _load_entry_point(self, key) -> Callable: + """Check that ``key`` is a registered ``cookie_parameter_sets``, + and return the entry point for the parameter set, loading it needed.""" + if key not in self.__all_parameter_sets: + raise KeyError(f"Unknown parameter set: {key}") + ps = self.__all_parameter_sets[key] + try: + ps = self.__all_parameter_sets[key] = ps.load() + except AttributeError: + pass + return ps + + def __iter__(self): + return self.__all_parameter_sets.__iter__() + + def __len__(self) -> int: + return len(self.__all_parameter_sets) + + def get_docstring(self, key): + """Return the docstring for the ``key`` parameter set""" + return textwrap.dedent(self._load_entry_point(key).__doc__) + + def __getattribute__(self, name): + try: + return super().__getattribute__(name) + except AttributeError as error: + raise error + +#: Singleton Instance of :class:ParameterSets """ +parameter_sets = ParameterSets() diff --git a/tests/test_entry_points.py b/tests/test_entry_points.py new file mode 100644 index 0000000..6845a43 --- /dev/null +++ b/tests/test_entry_points.py @@ -0,0 +1,27 @@ +import pytest +import pybamm_cookiecutter +import importlib.util +import sys +from pathlib import Path + +def test_entry_points(): + """Test if the entry points are loaded correctly.""" + + entry_points = list(pybamm_cookiecutter.parameter_sets) + parameter_sets = Path("src/pybamm_cookiecutter/parameters/input/").glob("*.py") + # Making a list of parameter sets in the parameters/input directory + parameter_sets = [x.stem for x in parameter_sets] + + assert parameter_sets == entry_points, "Entry points missing either in pyproject.toml or in the input directory" + +def test_entry_point_load(): + """Testing if the values get loaded via parameter entry points and are equal when loaded through entry points""" + # Loading parameter_sets through entry points + parameters = pybamm_cookiecutter.parameter_sets['Chen2020'] + # Loading parameter sets through the source file by dynamically loading Chen2020.py as a module + spec = importlib.util.spec_from_file_location("Chen2020mod", "src/pybamm_cookiecutter/parameters/input/Chen2020.py") + chen_module = importlib.util.module_from_spec(spec) + sys.modules["Chen2020mod"] = chen_module + spec.loader.exec_module(chen_module) + parameters_from_file = chen_module.get_parameter_values() + assert parameters.keys() == parameters_from_file.keys(), f"The keys in the module and local input file are not the same, expected {parameters.keys} got {parameters_from_file.keys()}"