diff --git a/.gitignore b/.gitignore index d59641db3c..444611541e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.png /local/ *.DS_Store +*.mat # don't ignore important .txt files !requirements* diff --git a/.requirements-docs.txt b/.requirements-docs.txt index c7bcd6446f..6dbc42b983 100644 --- a/.requirements-docs.txt +++ b/.requirements-docs.txt @@ -8,3 +8,4 @@ scikit-fem>=0.2.0 casadi>=3.5.0 guzzle-sphinx-theme sphinx>=1.5 +python-Levenshtein>=0.12.0 diff --git a/.travis.yml b/.travis.yml index 15976a3a98..f7a04dfbbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,7 +79,8 @@ matrix: env: - PYBAMM_UNIT=true - PYBAMM_SCIKITS_ODES=true - # Unit testing on Python3.7 on Ubuntu without scikit odes + - PYBAMM_KLU=true + # Unit and example testing on Python3.7 on Ubuntu without optional dependencies - python: "3.7" addons: apt: @@ -95,6 +96,7 @@ matrix: - libsuitesparse-dev env: - PYBAMM_UNIT=true + - PYBAMM_EXAMPLES=true if: type != cron # Cover, docs and style checking, latest Python version only - python: "3.7" diff --git a/CHANGELOG.md b/CHANGELOG.md index ca6f825ebb..23a055d752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## Features +- Added capacitance effects to lithium-ion models () +- Added fuzzy string matching for parameters and variables ([#796](https://github.com/pybamm-team/PyBaMM/pull/796)) +- Changed ParameterValues to raise an error when a parameter that wasn't previously defined is updated ([#796](https://github.com/pybamm-team/PyBaMM/pull/796)) +- Added some basic models (BasicSPM and BasicDFN) in order to clearly demonstrate the PyBaMM model structure for battery models ([#795](https://github.com/pybamm-team/PyBaMM/pull/795)) +- Added the harmonic mean to the Finite Volume method, which is now used when computing fluxes ([#783](https://github.com/pybamm-team/PyBaMM/pull/783)) +- Refactored `Solution` to make it a dictionary that contains all of the solution variables. This automatically creates `ProcessedVariable` objects when required, so that the solution can be obtained much more easily. ([#781](https://github.com/pybamm-team/PyBaMM/pull/781)) +- Added notebook to explain broadcasts ([#776](https://github.com/pybamm-team/PyBaMM/pull/776)) +- Added a step to discretisation that automatically compute the inverse of the mass matrix of the differential part of the problem so that the underlying DAEs can be provided in semi-explicit form, as required by the CasADi solver ([#769](https://github.com/pybamm-team/PyBaMM/pull/769)) +- Added the gradient operation for the Finite Element Method ([#767](https://github.com/pybamm-team/PyBaMM/pull/767)) +- Added `InputParameter` node for quickly changing parameter values ([#752](https://github.com/pybamm-team/PyBaMM/pull/752)) +- Added submodels for operating modes other than current-controlled ([#751](https://github.com/pybamm-team/PyBaMM/pull/751)) +- Changed finite volume discretisation to use exact values provided by Neumann boundary conditions when computing the gradient instead of adding ghost nodes([#748](https://github.com/pybamm-team/PyBaMM/pull/748)) +- Added optional R(x) distribution in particle models ([#745](https://github.com/pybamm-team/PyBaMM/pull/745)) - Generalized importing of external variables ([#728](https://github.com/pybamm-team/PyBaMM/pull/728)) - Separated active and inactive material volume fractions ([#726](https://github.com/pybamm-team/PyBaMM/pull/726)) - Added submodels for tortuosity ([#726](https://github.com/pybamm-team/PyBaMM/pull/726)) @@ -24,6 +37,8 @@ ## Optimizations +- Simplified solver interface ([#800](https://github.com/pybamm-team/PyBaMM/pull/800)) +- Added caching for shape evaluation, used during discretisation ([#780](https://github.com/pybamm-team/PyBaMM/pull/780)) - Added an option to skip model checks during discretisation, which could be slow for large models ([#739](https://github.com/pybamm-team/PyBaMM/pull/739)) - Use CasADi's automatic differentation algorithms by default when solving a model ([#714](https://github.com/pybamm-team/PyBaMM/pull/714)) - Avoid re-checking size when making a copy of an `Index` object ([#656](https://github.com/pybamm-team/PyBaMM/pull/656)) @@ -31,6 +46,11 @@ ## Bug fixes +- Fixed examples to run with basic pip installation ([#800](https://github.com/pybamm-team/PyBaMM/pull/800)) +- Added events for CasADi solver when stepping ([#800](https://github.com/pybamm-team/PyBaMM/pull/800)) +- Improved implementation of broadcasts ([#776](https://github.com/pybamm-team/PyBaMM/pull/776)) +- Fixed a bug which meant that the Ohmic heating in the current collectors was incorrect if using the Finite Element Method ([#767](https://github.com/pybamm-team/PyBaMM/pull/767)) +- Improved automatic broadcasting ([#747](https://github.com/pybamm-team/PyBaMM/pull/747)) - Fixed bug with wrong temperature in initial conditions ([#737](https://github.com/pybamm-team/PyBaMM/pull/737)) - Improved flexibility of parameter values so that parameters (such as diffusivity or current) can be set as functions or scalars ([#723](https://github.com/pybamm-team/PyBaMM/pull/723)) - Fixed a bug where boundary conditions were sometimes handled incorrectly in 1+1D models ([#713](https://github.com/pybamm-team/PyBaMM/pull/713)) @@ -43,9 +63,12 @@ ## Breaking changes +- Removed `Outer` and `Kron` nodes as no longer used ([#777](https://github.com/pybamm-team/PyBaMM/pull/777)) +- Moved `results` to separate repositories ([#761](https://github.com/pybamm-team/PyBaMM/pull/761)) - The parameters "Bruggeman coefficient" must now be specified separately as "Bruggeman coefficient (electrolyte)" and "Bruggeman coefficient (electrode)" - The current classes (`GetConstantCurrent`, `GetUserCurrent` and `GetUserData`) have now been removed. Please refer to the [`change-input-current` notebook](https://github.com/pybamm-team/PyBaMM/blob/master/examples/notebooks/change-input-current.ipynb) for information on how to specify an input current - Parameter functions must now use pybamm functions instead of numpy functions (e.g. `pybamm.exp` instead of `numpy.exp`), as these are then used to construct the expression tree directly. Generally, pybamm syntax follows numpy syntax; please get in touch if a function you need is missing. +- The current must now be updated by changing "Current function [A]" or "C-rate" instead of "Typical current [A]" # [v0.1.0](https://github.com/pybamm-team/PyBaMM/tree/v0.1.0) - 2019-10-08 diff --git a/INSTALL-LINUX.md b/INSTALL-LINUX.md index 34ef1f2a57..0e6629712d 100644 --- a/INSTALL-LINUX.md +++ b/INSTALL-LINUX.md @@ -94,7 +94,7 @@ Before installing scikits.odes, you need to have installed: - Fortran compiler (e.g. gfortran) - BLAS/LAPACK install (OpenBLAS is recommended by the scikits.odes developers) - CMake (for building Sundials) -- Sundials 4.1.0 +- Sundials 5.0.0 You can install these on Ubuntu or Debian using apt-get: @@ -102,17 +102,17 @@ You can install these on Ubuntu or Debian using apt-get: sudo apt-get install python3-dev gfortran gcc cmake libopenblas-dev ``` -To install Sundials 4.1.0, on the command-line type: +To install Sundials 5.0.0, on the command-line type: ```bash INSTALL_DIR=`pwd`/sundials -wget https://computation.llnl.gov/projects/sundials/download/sundials-4.1.0.tar.gz -tar -xvf sundials-4.1.0.tar.gz -mkdir build-sundials-4.1.0 -cd build-sundials-4.1.0/ -cmake -DLAPACK_ENABLE=ON -DSUNDIALS_INDEX_TYPE=int32_t -DBUILD_ARKODE:BOOL=OFF -DEXAMPLES_ENABLE:BOOL=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR ../sundials-4.1.0/ +wget https://computation.llnl.gov/projects/sundials/download/sundials-5.0.0.tar.gz +tar -xvf sundials-5.0.0.tar.gz +mkdir build-sundials-5.0.0 +cd build-sundials-5.0.0/ +cmake -DLAPACK_ENABLE=ON -DSUNDIALS_INDEX_TYPE=int32_t -DBUILD_ARKODE:BOOL=OFF -DEXAMPLES_ENABLE:BOOL=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR ../sundials-5.0.0/ make install -rm -r ../sundials-4.1.0 +rm -r ../sundials-5.0.0 ``` Then install [scikits.odes](https://github.com/bmcage/odes), letting it know the sundials install location: diff --git a/LICENSE.txt b/LICENSE.txt index e63d315d14..8f0f2ff964 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017-2019, University of Oxford (University of Oxford means the Chancellor, Masters and Scholars of the University of Oxford, having an administrative office at Wellington Square, Oxford OX1 2JD, UK). +Copyright (c) 2018-2020, University of Oxford (University of Oxford means the Chancellor, Masters and Scholars of the University of Oxford, having an administrative office at Wellington Square, Oxford OX1 2JD, UK). All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index e302eeb08a..789f4eae29 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ hosted on [Read The Docs](readthedocs.io). A set of slides giving an overview of can be found [here](https://github.com/pybamm-team/pybamm_summary_slides/blob/master/pybamm.pdf). +For further examples, see the list of repositories that use PyBaMM [here](https://github.com/pybamm-team/pybamm-example-results) + ## How can I obtain & install PyBaMM? ### Linux diff --git a/docs/source/expression_tree/binary_operator.rst b/docs/source/expression_tree/binary_operator.rst index 822864bbc5..d8082d8c4c 100644 --- a/docs/source/expression_tree/binary_operator.rst +++ b/docs/source/expression_tree/binary_operator.rst @@ -25,15 +25,7 @@ Binary Operators .. autoclass:: pybamm.Inner :members: -.. autoclass:: pybamm.Outer - :members: - -.. autoclass:: pybamm.Kron - :members: - .. autoclass:: pybamm.Heaviside :members: -.. autofunction:: pybamm.outer - .. autofunction:: pybamm.source diff --git a/docs/source/expression_tree/broadcasts.rst b/docs/source/expression_tree/broadcasts.rst index 90a7c9b1e4..743e48ce79 100644 --- a/docs/source/expression_tree/broadcasts.rst +++ b/docs/source/expression_tree/broadcasts.rst @@ -9,3 +9,6 @@ Broadcasting Operators .. autoclass:: pybamm.PrimaryBroadcast :members: + +.. autoclass:: pybamm.SecondaryBroadcast + :members: diff --git a/docs/source/expression_tree/index.rst b/docs/source/expression_tree/index.rst index 3eedd7b622..3efd97fb3f 100644 --- a/docs/source/expression_tree/index.rst +++ b/docs/source/expression_tree/index.rst @@ -16,5 +16,6 @@ Expression Tree concatenations broadcasts functions + input_parameter interpolant operations/index diff --git a/docs/source/expression_tree/input_parameter.rst b/docs/source/expression_tree/input_parameter.rst new file mode 100644 index 0000000000..ebbc13e7e5 --- /dev/null +++ b/docs/source/expression_tree/input_parameter.rst @@ -0,0 +1,5 @@ +Input Parameter +=============== + +.. autoclass:: pybamm.InputParameter + :members: diff --git a/docs/source/expression_tree/variable.rst b/docs/source/expression_tree/variable.rst index 629eb90471..0e610f5f28 100644 --- a/docs/source/expression_tree/variable.rst +++ b/docs/source/expression_tree/variable.rst @@ -3,3 +3,6 @@ Variable .. autoclass:: pybamm.Variable :members: + +.. autoclass:: pybamm.ExternalVariable + :members: diff --git a/docs/source/models/lithium_ion/dfn.rst b/docs/source/models/lithium_ion/dfn.rst index 721c867869..9c30327609 100644 --- a/docs/source/models/lithium_ion/dfn.rst +++ b/docs/source/models/lithium_ion/dfn.rst @@ -3,3 +3,6 @@ Doyle-Fuller-Newman (DFN) .. autoclass:: pybamm.lithium_ion.DFN :members: + +.. autoclass:: pybamm.lithium_ion.BasicDFN + :members: diff --git a/docs/source/models/lithium_ion/spm.rst b/docs/source/models/lithium_ion/spm.rst index 5bf53a00d2..fe891e98b4 100644 --- a/docs/source/models/lithium_ion/spm.rst +++ b/docs/source/models/lithium_ion/spm.rst @@ -3,3 +3,6 @@ Single Particle Model (SPM) .. autoclass:: pybamm.lithium_ion.SPM :members: + +.. autoclass:: pybamm.lithium_ion.BasicSPM + :members: diff --git a/docs/source/models/submodels/current_collector/index.rst b/docs/source/models/submodels/current_collector/index.rst index 3ae2821bfd..e13e842515 100644 --- a/docs/source/models/submodels/current_collector/index.rst +++ b/docs/source/models/submodels/current_collector/index.rst @@ -10,5 +10,4 @@ Current Collector homogeneous_current_collector potential_pair quite_conductive_potential_pair - single_particle_potential_pair set_potential_single_particle diff --git a/docs/source/models/submodels/current_collector/single_particle_potential_pair.rst b/docs/source/models/submodels/current_collector/single_particle_potential_pair.rst deleted file mode 100644 index 6fca366360..0000000000 --- a/docs/source/models/submodels/current_collector/single_particle_potential_pair.rst +++ /dev/null @@ -1,5 +0,0 @@ -Single Particle Potential Pair models -===================================== - -.. autoclass:: pybamm.current_collector.SingleParticlePotentialPair - :members: diff --git a/docs/source/models/submodels/external_circuit/current_control_external_circuit.rst b/docs/source/models/submodels/external_circuit/current_control_external_circuit.rst new file mode 100644 index 0000000000..6f3f0a001e --- /dev/null +++ b/docs/source/models/submodels/external_circuit/current_control_external_circuit.rst @@ -0,0 +1,6 @@ +Current control external circuit +================================ + +.. autoclass:: pybamm.external_circuit.CurrentControl + :members: + diff --git a/docs/source/models/submodels/external_circuit/function_control_external_circuit.rst b/docs/source/models/submodels/external_circuit/function_control_external_circuit.rst new file mode 100644 index 0000000000..1c7b8b5049 --- /dev/null +++ b/docs/source/models/submodels/external_circuit/function_control_external_circuit.rst @@ -0,0 +1,11 @@ +Function control external circuit +================================= + +.. autoclass:: pybamm.external_circuit.FunctionControl + :members: + +.. autoclass:: pybamm.external_circuit.VoltageFunctionControl + :members: + +.. autoclass:: pybamm.external_circuit.PowerFunctionControl + :members: \ No newline at end of file diff --git a/docs/source/models/submodels/external_circuit/index.rst b/docs/source/models/submodels/external_circuit/index.rst new file mode 100644 index 0000000000..600c7718ba --- /dev/null +++ b/docs/source/models/submodels/external_circuit/index.rst @@ -0,0 +1,15 @@ +External circuit +================ + +Models to enforce different boundary conditions (as imposed by an imaginary external +circuit) such as constant current, constant voltage, constant power, or any other +relationship between the current and voltage. "Current control" enforces these directly +through boundary conditions, while "Function control" +submodels add an algebraic equation (for the current) and hence can be used to set any +variable to be constant. + +.. toctree:: + :maxdepth: 1 + + current_control_external_circuit + function_control_external_circuit diff --git a/docs/source/models/submodels/index.rst b/docs/source/models/submodels/index.rst index 5377dcd868..d5a1e98721 100644 --- a/docs/source/models/submodels/index.rst +++ b/docs/source/models/submodels/index.rst @@ -9,6 +9,7 @@ Submodels convection/index electrode/index electrolyte/index + external_circuit/index interface/index oxygen_diffusion/index particle/index diff --git a/docs/source/processed_variable.rst b/docs/source/processed_variable.rst index b2cc384289..9f1d62f2a7 100644 --- a/docs/source/processed_variable.rst +++ b/docs/source/processed_variable.rst @@ -1,7 +1,5 @@ Post-Process Variables ====================== -.. autofunction:: pybamm.post_process_variables - .. autoclass:: pybamm.ProcessedVariable :members: diff --git a/docs/source/solvers/base_solvers.rst b/docs/source/solvers/base_solvers.rst index 7694279f0d..9b1d73f48b 100644 --- a/docs/source/solvers/base_solvers.rst +++ b/docs/source/solvers/base_solvers.rst @@ -3,9 +3,3 @@ Base Solvers .. autoclass:: pybamm.BaseSolver :members: - -.. autoclass:: pybamm.OdeSolver - :members: - -.. autoclass:: pybamm.DaeSolver - :members: diff --git a/docs/tutorials/add-parameter-values.rst b/docs/tutorials/add-parameter-values.rst index 3cc8ed090e..9472e9a872 100644 --- a/docs/tutorials/add-parameter-values.rst +++ b/docs/tutorials/add-parameter-values.rst @@ -21,56 +21,36 @@ For an example of how the parameter values work, see the Adding a set of parameters values --------------------------------- -There are two ways to add parameter sets: +Parameter sets are split by material into ``anodes``, ``separators``, ``cathodes``, ``electrolytes``, ``cells`` (for cell geometries and thermal properties) and ``experiments`` (for initial conditions and charge/discharge rates). +To add a new parameter set in one of these subcategories, first create a new folder in the appropriate chemistry folder: for example, to add a new anode chemistry for lithium-ion, add a subfolder ``input/parameters/lithium-ion/anodes/new_anode_chemistry_AuthorYear``. +This subfolder should then contain: -1. **Complete parameter file**: Parameter sets should be added as csv files in the appropriate chemistry folder in ``input/parameters/`` (add a new folder if no parameters exist for that chemistry yet). -The expected structure of the csv file is +- a csv file ``parameters.csv`` with all the relevant scalar parameters. The expected structure of the csv file is: +-------------------------+----------------------+-----------------------+-------------+ | Name [Units] | Value | Reference | Notes | +=========================+======================+=======================+=============+ -| Example [m] | 13 | bloggs2019 | an example | +| Example [m] | 13 | AuthorYear | an example | +-------------------------+----------------------+-----------------------+-------------+ Empty lines, and lines starting with ``#``, will be ignored. -2. **Parameters for a single material**: It's possible to add parameters for a single material (e.g. anode) and then re-use existing parameters for the other materials. To do this, add a new subfolder with a ``README.md`` for references and csv file of parameters (e.g. ``input/parameters/lithium-ion/anodes/new_anode_chemistry_Bloggs2019/``). Then this file can be referenced using the ``chemistry`` option in ``ParameterValues``, e.g. +- a ``README.md`` file with information on where these parameters came from +- python files for any functions, which should be referenced from the ``parameters.csv`` file (see ``Adding a Function`` below) +- csv files for any data to be interpolated, which should be referenced from the ``parameters.csv`` file (see ``Adding data for interpolation`` below) -.. code-block:: python - - param = pybamm.ParameterValues( - chemistry={ - "chemistry": "lithium-ion", - "cell": "kokam_Marquis2019", - "anode": "new_anode_chemistry_Bloggs2019", - "separator": "separator_Marquis2019", - "cathode": "lico2_Marquis2019", - "electrolyte": "lipf6_Marquis2019", - "experiment": "1C_discharge_from_full_Marquis2019", - } - ) - -or, equivalently in this case (since the only difference from the standard parameters from Marquis et al. is the set of anode parameters), - -.. code-block:: python - - param = pybamm.ParameterValues( - chemistry={ - **pybamm.parameter_sets.Marquis2019, - "anode": "new_anode_chemistry_Bloggs2019", - } - ) +The easiest way to start is to copy an existing file (e.g. ````input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019``) and replace all entries in all files as appropriate Adding a function ----------------- Functions should be added as Python functions under a file with the same name in the appropriate chemistry folder in ``input/parameters/``. These Python functions should be documented with references explaining where they were obtained. -For example, we would put the following Python function in a file ``input/parameters/lead-acid/diffusivity_Bloggs2019.py`` +For example, we would put the following Python function in a file ``input/parameters/lithium_ion/anodes/new_anode_chemistry_AuthorYear/diffusivity_AuthorYear.py`` .. code-block:: python - def diffusivity_Bloggs2019(c_e): + def diffusivity_AuthorYear(c_e): """ Dimensional Fickian diffusivity in the electrolyte [m2.s-1], from [1]_, as a function of the electrolyte concentration c_e [mol.m-3]. @@ -89,10 +69,92 @@ called (must be in the same folder), with the tag ``[function]``, for example: +---------------------+--------------------------------------+--------------+-------------+ | Name [Units] | Value | Reference | Notes | +=====================+======================================+==============+=============+ -| Example [m2.s-1] | [function]diffusivity_Bloggs2019 | bloggs2019 | a function | +| Example [m2.s-1] | [function]diffusivity_AuthorYear | AuthorYear | a function | +---------------------+--------------------------------------+--------------+-------------+ +Adding data for interpolation +----------------------------- + +Data should be added as as csv file in the appropriate chemistry folder in ``input/parameters/``. +For example, we would put the following data in a file ``input/parameters/lithium_ion/anodes/new_anode_chemistry_AuthorYear/diffusivity_AuthorYear.csv`` + ++--------------------------+--------------------------+ +| # concentration [mol/m3] | Diffusivity [m2/s] | ++==========================+==========================+ +| 0.000000000000000000e+00 | 4.714135898019971016e+00 | +| 2.040816326530612082e-02 | 4.708899441575220557e+00 | +| 4.081632653061224164e-02 | 4.702448345762175741e+00 | +| 6.122448979591836593e-02 | 4.694558534379876136e+00 | +| 8.163265306122448328e-02 | 4.684994372928071193e+00 | +| 1.020408163265306006e-01 | 4.673523893805322516e+00 | +| 1.224489795918367319e-01 | 4.659941254449398329e+00 | +| 1.428571428571428492e-01 | 4.644096031712390271e+00 | ++--------------------------+--------------------------+ + +Empty lines, and lines starting with ``#``, will be ignored. + +Then, this data should be added to the parameter file from which it will be +called (must be in the same folder), with the tag ``[data]``, for example: + ++---------------------+----------------------------------+--------------+-------------+ +| Name [Units] | Value | Reference | Notes | ++=====================+==================================+==============+=============+ +| Example [m2.s-1] | [data]diffusivity_AuthorYear | AuthorYear | some data | ++---------------------+----------------------------------+--------------+-------------+ + +Using new parameters +-------------------- + +If you have added a whole new set of parameters, then you can create a new parameter set in ``pybamm/parameters/parameter_sets.py``, by just adding a new dictionary to that file, for example + +.. code-block:: python + + AuthorYear = { + "chemistry": "lithium-ion", + "cell": "new_cell_AuthorYear", + "anode": "new_anode_AuthorYear", + "separator": "new_separator_AuthorYear", + "cathode": "new_cathode_AuthorYear", + "electrolyte": "new_electrolyte_AuthorYear", + "experiment": "new_experiment_AuthorYear", + } + +Then, to use these new parameters, use: + +.. code-block:: python + + param = pybamm.ParameterValues(chemistry=pybamm.parameter_sets.AuthorYear) + +Note that you can re-use existing parameter subsets instead of creating new ones (for example, you could just replace "experiment": "new_experiment_AuthorYear" with "experiment": "1C_discharge_from_full_Marquis2019" in the above dictionary). + +It's also possible to add parameters for a single material (e.g. anode) and then re-use existing parameters for the other materials, without adding a parameter set to ``pybamm/parameters/parameter_sets.py``. + +.. code-block:: python + + param = pybamm.ParameterValues( + chemistry={ + "chemistry": "lithium-ion", + "cell": "kokam_Marquis2019", + "anode": "new_anode_chemistry_AuthorYear", + "separator": "separator_Marquis2019", + "cathode": "lico2_Marquis2019", + "electrolyte": "lipf6_Marquis2019", + "experiment": "1C_discharge_from_full_Marquis2019", + } + ) + +or, equivalently in this case (since the only difference from the standard parameters from Marquis et al. is the set of anode parameters), + +.. code-block:: python + + param = pybamm.ParameterValues( + chemistry={ + **pybamm.parameter_sets.Marquis2019, + "anode": "new_anode_chemistry_AuthorYear", + } + ) +See the `"Getting Started" tutorial `_ for examples of setting parameters in action. Unit tests for the new class ---------------------------- @@ -105,13 +167,13 @@ Test on the models In theory, any existing model can now be solved using the new parameters instead of their default parameters, with no extra work from here. To test this, add something like the following test to one of the model test files -(e.g. `DFN `_): +(e.g. `DFN `_): .. code-block:: python def test_my_new_parameters(self): model = pybamm.lithium_ion.DFN() - parameter_values = pybamm.ParameterValues("path/to/parameter/file.csv") + parameter_values = pybamm.ParameterValues(chemistry=pybamm.parameter_sets.AuthorYear) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all() diff --git a/docs/tutorials/add-solver.rst b/docs/tutorials/add-solver.rst index 1ee8e4f328..efd31cd4c3 100644 --- a/docs/tutorials/add-solver.rst +++ b/docs/tutorials/add-solver.rst @@ -27,21 +27,21 @@ The role of solvers is to solve a model at a given set of time points, returning Base solver classes vs specific solver classes ---------------------------------------------- -There is one general base solver class, :class:`pybamm.BaseSolver`, and two specialised base classes, :class:`pybamm.OdeSolver` and :class:`pybamm.DaeSolver`. The general base class simply sets up some useful solver properties such as tolerances. The specialised base classes implement a method :meth:`self.solve()` that solves a model at a given set of time points. +There is one general base solver class, :class:`pybamm.BaseSolver`, which sets up some useful solver properties such as tolerances and implement a method :meth:`self.solve()` that solves a model at a given set of time points. The ``solve`` method unpacks the model, simplifies it by removing extraneous operations, (optionally) creates or calls the mass matrix and/or jacobian, and passes the appropriate attributes to another method, called ``integrate``, which does the time-stepping. The role of specific solver classes is simply to implement this ``integrate`` method for an arbitrary set of derivative function, initial conditions etc. -The base DAE solver class also computes a consistent set of initial conditions for the algebraic equations, using ``model.concatenated_initial_conditions`` as an initial guess. +The base solver class also computes a consistent set of initial conditions for the algebraic equations, using ``model.concatenated_initial_conditions`` as an initial guess. Implementing a new solver ------------------------- To add a new solver (e.g. My Fast DAE Solver), first create a new file (``my_fast_dae_solver.py``) in ``pybamm/solvers/``, -with a single class that inherits from either :class:`pybamm.OdeSolver` or :class:`pybamm.DaeSolver`, depending on whether the new solver can solve DAE systems. For example: +with a single class that inherits from :class:`pybamm.BaseSolver`. For example: .. code-block:: python - def MyFastDaeSolver(pybamm.DaeSolver): + def MyFastDaeSolver(pybamm.BaseSolver): Also add the class to ``pybamm/__init__.py``: @@ -49,7 +49,7 @@ Also add the class to ``pybamm/__init__.py``: from .solvers.my_fast_dae_solver import MyFastDaeSolver -You can then start implementing the solver by adding the ``integrate`` function to the class (the interfaces are slightly different for an ODE Solver and a DAE Solver, see :meth:`pybamm.OdeSolver.integrate` vs :meth:`pybamm.DaeSolver.integrate`) +You can then start implementing the solver by adding the ``integrate`` function to the class. For an example of an existing solver implementation, see the Scikits DAE solver `API docs `_ @@ -74,7 +74,7 @@ Test on the models In theory, any existing model can now be solved using `MyFastDaeSolver` instead of their default solvers, with no extra work from here. To test this, add something like the following test to one of the model test files -(e.g. `DFN `_): +(e.g. `DFN `_): .. code-block:: python diff --git a/docs/tutorials/add-spatial-method.rst b/docs/tutorials/add-spatial-method.rst index 2d6ad71c31..00cf2ae928 100644 --- a/docs/tutorials/add-spatial-method.rst +++ b/docs/tutorials/add-spatial-method.rst @@ -87,7 +87,7 @@ Test on the models In theory, any existing model can now be discretised using ``MyFastMethod`` instead of their default spatial methods, with no extra work from here. To test this, add something like the following test to one of the model test files -(e.g. `DFN `_): +(e.g. `DFN `_): .. code-block:: python diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..c717c5d98a --- /dev/null +++ b/examples/README.md @@ -0,0 +1,4 @@ +# Examples + +A collection of Python scripts and Jupyter notebooks that demonstrate how to use PyBaMM. +For further examples, see the list of repositories that use PyBaMM [here](https://github.com/pybamm-team/pybamm-example-results) diff --git a/examples/notebooks/Getting Started/Tutorial 3 - Basic Plotting.ipynb b/examples/notebooks/Getting Started/Tutorial 3 - Basic Plotting.ipynb index 5263289889..0d6843f6c7 100644 --- a/examples/notebooks/Getting Started/Tutorial 3 - Basic Plotting.ipynb +++ b/examples/notebooks/Getting Started/Tutorial 3 - Basic Plotting.ipynb @@ -340,8 +340,8 @@ " 'Volume-averaged total heating',\n", " 'Volume-averaged total heating [W.m-3]',\n", " 'Positive current collector potential [V]',\n", - " 'Local current collector potential difference',\n", - " 'Local current collector potential difference [V]',\n", + " 'Local voltage',\n", + " 'Local voltage [V]',\n", " 'X-averaged open circuit voltage',\n", " 'Measured open circuit voltage',\n", " 'X-averaged open circuit voltage [V]',\n", diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md index 6aff7646ea..873737cd95 100644 --- a/examples/notebooks/README.md +++ b/examples/notebooks/README.md @@ -40,14 +40,17 @@ For more advanced usage, new sets of parameters, spatial methods and solvers can ## Expression tree structure PyBaMM is built around an expression tree structure. -[This](expression_tree/expression-tree.ipynb) notebook explains how this works, from -model creation to solution. + +- [The expression tree notebook](expression_tree/expression-tree.ipynb) explains how this works, from model creation to solution. +- [The broadcast notebook](expression_tree/broadcasts.ipynb) explains the different types of broadcast. The following notebooks are specific to different stages of the PyBaMM pipeline, such as choosing a model, spatial method, or solver. ### Models -The following models are implemented and can easily be used or [compared](./models/lead-acid.ipynb). We always welcome [new models](https://pybamm.readthedocs.io/en/latest/tutorials/add-model.html)! +Several battery models are implemented and can easily be used or [compared](./models/lead-acid.ipynb). The notebooks below show the solution of each individual model. We always welcome [new models](https://pybamm.readthedocs.io/en/latest/tutorials/add-model.html)! + +Once you are comfortable with the expression tree structure, a good starting point to understand the models in PyBaMM is to take a look at the [basic SPM](https://github.com/pybamm-team/PyBaMM/blob/master/pybamm/models/full_battery_models/lithium_ion/basic_spm.py) and [basic DFN](https://github.com/pybamm-team/PyBaMM/blob/master/pybamm/models/full_battery_models/lithium_ion/basic_dfn.py), since these define the entire model (variables, equations, initial and boundary conditions, events) in a single class and so are easier to understand. However, we recommend that you subsequently use the full models as they offer much greater flexibility for coupling different physical effects and visualising a greater range of variables. #### Lithium-ion models @@ -71,10 +74,9 @@ See [here](https://pybamm.readthedocs.io/en/latest/tutorials/add-spatial-method. ### Solvers -The following solvers are implemented -- Scipy ODE solver -- [Scikits ODE solver](./solvers/scikits-ode-solver.ipynb) -- [Scikits DAE solver](./solvers/scikits-dae-solver.ipynb) -- CasADi DAE solver +The following notebooks show examples for generic ODE and DAE solvers. Several solvers are implemented in PyBaMM and we encourage users to try different ones to find the most appropriate one for their models. + +- [ODE solver](./solvers/ode-solver.ipynb) +- [DAE solver](./solvers/dae-solver.ipynb) See [here](https://pybamm.readthedocs.io/en/latest/tutorials/add-solver.html) for instructions on adding new solvers. diff --git a/examples/notebooks/change-input-current.ipynb b/examples/notebooks/change-input-current.ipynb index 0b5b1549f7..a9c17e3443 100644 --- a/examples/notebooks/change-input-current.ipynb +++ b/examples/notebooks/change-input-current.ipynb @@ -22,7 +22,7 @@ "\n", "In this notebook we will use the SPM as the example model, and change the input current from the default option. If you are not familiar with running a model in PyBaMM, please see [this](./models/SPM.ipynb) notebook for more details.\n", "\n", - "In PyBaMM, the current function is set using the parameter \"Current function\". This can be a scalar, but only accepts values 0 and 1. By default this is set to be a constant current by setting it to '1'. The size of a constant current input is changed by changing the parameter \"Typical current [A]\". Below we load the SPM with the default parameters, and then change the the typical current 16A. We then explicitly set the current function to be a constant current." + "In PyBaMM, the current function is set using the parameter \"Current function [A]\". Below we load the SPM with the default parameters, and then change the the current function to 16A." ] }, { @@ -45,9 +45,8 @@ "# set the default model parameters\n", "param = model.default_parameter_values\n", "\n", - "# change the typical current and set a constant discharge using the typical current value\n", - "param[\"Typical current [A]\"] = 16\n", - "param[\"Current function\"] = \"[constant]\"\n" + "# change the current function\n", + "param[\"Current function [A]\"] = 16" ] }, { @@ -67,12 +66,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "26ae7952fb10438ebbf83297fe284014", + "model_id": "000da8e81b0b4e91984553ba15ba179c", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.02, step=0.005), Output()), _dom_classes=(…" + "interactive(children=(FloatSlider(value=0.0, description='t', max=0.0036363636363636364, step=0.005), Output()…" ] }, "metadata": {}, @@ -98,7 +97,7 @@ "solution = solver.solve(model, t_eval)\n", "\n", "# plot\n", - "quick_plot = pybamm.QuickPlot(model, mesh, solution)\n", + "quick_plot = pybamm.QuickPlot(solution)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=solution.t[-1],step=0.005,value=0));" @@ -108,7 +107,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The one case where we would change \"Current function\" instead of \"Typical current [A]\" is for a simulation at zero-current, since \"Typical current [A]\" cannot be zero (as it is used for non-dimensionalisation). In this case, the current function should be set to zero instead:" + "PyBaMM can also simulate rest behaviour by setting the current function to zero:" ] }, { @@ -117,7 +116,7 @@ "metadata": {}, "outputs": [], "source": [ - "param[\"Current function\"] = \"[zero]\"" + "param[\"Current function [A]\"] = 0" ] }, { @@ -131,31 +130,11 @@ "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "param[\"Current function\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "630250e18bf6416089e4bed2a883c10e", + "model_id": "cc4fd2e7c9ea4129a7acf0e519fea268", "version_major": 2, "version_minor": 0 }, @@ -175,7 +154,7 @@ "solution = solver.solve(model, t_eval)\n", "\n", "# plot\n", - "quick_plot = pybamm.QuickPlot(model, mesh, solution)\n", + "quick_plot = pybamm.QuickPlot(solution)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=solution.t[-1],step=0.005,value=0));" @@ -187,39 +166,23 @@ "source": [ "## Loading in current data \n", "\n", - "Data can be loaded in from a csv file by specifying the path to that file and using the prefix \"[current data]\"." + "Data can be loaded in from a csv file by putting the file in the folder 'input/drive_cycles' and using the prefix \"[current data]\". As an example, we show how to solve the SPM using the US06 drive cycle" ] }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "param[\"Current function\"] = \"[current data]US06\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As an example, we show how to solve the SPM using the US06 drive cycle" - ] - }, - { - "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "18118e75c01e4fb1ba9a00134298b5fd", + "model_id": "84f6cac9b14f43a1907322e650f3c9f8", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.026526276390989537, step=0.001), Output())…" + "interactive(children=(FloatSlider(value=0.0, description='t', max=0.026550306076661374, step=0.001), Output())…" ] }, "metadata": {}, @@ -234,7 +197,7 @@ "\n", "# load parameter values and process model and geometry\n", "param = model.default_parameter_values\n", - "param[\"Current function\"] = \"[current data]US06\"\n", + "param[\"Current function [A]\"] = \"[current data]US06\"\n", "param.process_model(model)\n", "param.process_geometry(geometry)\n", "\n", @@ -253,7 +216,7 @@ "solution = solver.solve(model, t_eval)\n", "\n", "# plot\n", - "quick_plot = pybamm.QuickPlot(model, mesh, solution)\n", + "quick_plot = pybamm.QuickPlot(solution)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=solution.t[-1],step=0.001,value=0));" @@ -279,7 +242,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -301,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -316,7 +279,7 @@ "# set user defined current function\n", "A = pybamm.electrical_parameters.I_typ\n", "omega = 0.1\n", - "param[\"Current function\"] = my_fun(A,omega)\n", + "param[\"Current function [A]\"] = my_fun(A,omega)\n", "\n", "# process model and geometry\n", "param.process_model(model)\n", @@ -332,18 +295,18 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b6fae116be424e22955be046e20a6211", + "model_id": "09ced8a9e10b4e769a9b4cc8573b7fa2", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.0013263138195494769, step=6.63156909774738…" + "interactive(children=(FloatSlider(value=0.0, description='t', max=0.0013275153038330688, step=6.63757651916534…" ] }, "metadata": {}, @@ -361,14 +324,14 @@ "# Example: simulate for 30 seconds\n", "simulation_time = 30 # end time in seconds\n", "tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge).evaluate(0)\n", - "npts = 50 * simulation_time * omega # need enough timesteps to resolve output\n", + "npts = int(50 * simulation_time * omega) # need enough timesteps to resolve output\n", "t_eval = np.linspace(0, simulation_time / tau, npts)\n", "solution = model.default_solver.solve(model, t_eval)\n", "label = [\"Frequency: {} Hz\".format(omega)]\n", "\n", "# plot current and voltage\n", "output_variables = [\"Current [A]\", \"Terminal voltage [V]\"]\n", - "quick_plot = pybamm.QuickPlot(model, mesh, solution, output_variables, label)\n", + "quick_plot = pybamm.QuickPlot(solution, output_variables, label)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=solution.t[-1],step=solution.t[-1]/20,value=0));" @@ -398,9 +361,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/examples/notebooks/change-settings.ipynb b/examples/notebooks/change-settings.ipynb index b494ce30ea..5ff3129cc7 100644 --- a/examples/notebooks/change-settings.ipynb +++ b/examples/notebooks/change-settings.ipynb @@ -33,9 +33,9 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/scott/Projects/PyBaMM/venv/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:146: RuntimeWarning: invalid value encountered in greater_equal\n", + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:146: RuntimeWarning: invalid value encountered in greater_equal\n", " up = (g <= 0) & (g_new >= 0)\n", - "/home/scott/Projects/PyBaMM/venv/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:147: RuntimeWarning: invalid value encountered in less_equal\n", + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:147: RuntimeWarning: invalid value encountered in less_equal\n", " down = (g >= 0) & (g_new <= 0)\n" ] } @@ -88,7 +88,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAELCAYAAAAybErdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8li6FKAAAgAElEQVR4nO3deZTddX3/8ef7LrNPMklmQsJkGQKEfZNoQSuCij9QC7Vu2FoF6Y/foWrdTq1LVdzaelTqsVgpFQRbilhbNShUrYDgAhhkDWsCgSRkmSyzZba7vH9/fL935jLrd2buOvN6nHPP/W73+31/WfLOZzd3R0REJF+s3AGIiEjlUXIQEZFxlBxERGQcJQcRERlHyUFERMZJlDuAQmhtbfWOjo5yhyEiUlXuv//+fe7eNtG5eZEcOjo62LRpU7nDEBGpKmb23GTnVK0kIiLjKDmIiMg4Sg4iIjKOkoOIiIyj5CAiIuMoOYiIyDhKDiIiMs68GOcwWzf8Zhs/fvgFjl+5iOMPX8TxKxezrq2RxtoF/Y9FRGRhJ4dNzx3kd9uCT77m2gSHLa5jxaI62ppraW2qCb9rWdpYQ2tTLcuaaljaWENtIl6m6EVEimdBJ4fP/NHxvPX0VTy2q4fHXujhsV09bD/QT+9Qmt69fWzZ2zftPZpqEyxtrGFJYw3LGmtY0lDDsqYaWhqSLG2ooaWhhiUNSZY0BseWNNSQjKs2T0Qq24JODq1NtZy1vo2z1o9OLeLudA+k2N0zyO7uQfb1DbOvb4i9PUPsPzTEgUPD7OsbZn9fsN03lKZvKM3zB/ojP7epNsHi+iQtDcFncX3uU8Pi+iSL6hMjxxbVJVlUn2RRXYLmuiQ1CSUWESm+BZ0cJmJmtIR/4z92xaIpr3V3egbTI4niYH+KA4eG2H9omK7+FAcPDXOwPzh+sH+Y7v4UXQOpkYSys2tgxvHVJWM01yVpDpNFc22C5roETbUJmuoSNNcmaAy3m2oTNNaE+7UJGmrjwXdNnIaaBPGYzfYfk4jMc0oOc2BmI3/DXzfhvIbjZbNO33Ca7jBhdPWn6B4IkkZP+OnO+/QOpukZHN0eTGUZTA3R2Ts05/jrkjEaaxLU18RHvhtq4tQn42O2E9Qng/26ZIy68HxdIvxOxqhNxKlLjp6vS8apS8RIqApNpCopOZRYLGZBVVFdktVLG2b0W3dnIJWhZyBN72CK3qE0fYNpegfT9A0FyePQUIbewRSHhtP0DWXoG0xxaDjDoaE0h4aCYwPDaQ4NZ8JEMwyHivSyQDxm1CVGE0ZtIkZt+D2aVEa/8xNLbTJITkEyio0kqlzSyi8N1SfjmKkkJFIoSg5VxMxoqEnQUJNgxeK6Od0rm3UG0xkODWUYGM7Qn0qPbg+nGUhl6B/OMJjKMJAKjo9uZxlI5UoxwbHcdu4zlA72M1kPktNwpkD/FCYWs6AtJ1flFrTVBN+LG5K01NeMtPHkOgm01NfQ0hhUzSmxiLyYksMCFYuNJppicXdSGWcoHSSPcd95SST3PZjKMJgeTTpDqSwDw2FSSo0mr/7hTPgJ2m8GU1l6BtP0DKZnHGciZmM6BoSdAeqTYXtOcqRdp7E2QWNNnIba0aq22lyJJxEnGTclGpkXlBykaMyMmoRRk4jRPLeCzrTSmSx9Q+mRNprewfRIu03PQIqu/hRdA0HngPz2noP9w/QPZ8JeacNzjiNmUJsIEkZtIkZNIkZNPEZNIh5uG8l4bORTkzASsfHbyYSRjMVIhNcnYkYiHiMZD64JjueuN+K5Y+F37vrgO9iPx0b347Hgt/GYhb8P9mOGkpsASg4yTyTisZFeZjM1lM6M9CTrHgiSR/dAaiTJ9A4GPcwODWXCdpv0SCkmv8ptMJ0lk/WRUk61isdyycKImxEPk0vMwmPx4Hgslnc8TFBxYyTp5Ceh/CSVS3IvTpJB4qwNk2htXjtVXTIWlnKDktpI77satTMVk5KDLHi1iTjLF8VZvmjuxZt0JstgOstwOqg+G0plSWWyDKWzDGeC4+mMk8oE+/nbqbz9VMZJZ7Kksh4eD49lc9fkbwdJKZUNfpPO+05lnGw2vDbrpDNOJuvBdjb43ch+JkvWGTk293JUceXamRbVB4NLc4NMW5tqaWsOPoctqqW9pZ7DW+qpS2o2g5lQchApoEQ8RlM8BrXljmR23EeTRe47m3VS2SzZLGQ8SCKZrJN1J5OFdHguP9nkJ6B0xsNEFZxPZzxMjEHCyiXNVCbLUCpIpLl2qdEOD2HniVQmGCc0GJTecu1MOw5OP2ZoeXMtR7Q2sv6wZtYf1sQxKxZxUvti6muUNCai5CAiIyysIqqGKcNSmSyHhoK2pYP9Kbr6g0Gn+3qH6ewLxgK90DXAzq4BdnUPsrd3iL29Q9z77IGReyRixgnti9mwdgmvPLqVlx/ZqlkIQubupXuYWR1wF8HfqxLA9939M2OuuRj4MrAzPHSVu39rqvtu2LDBN23aVPiARWReSGey7OoeZGtnMGfaU3t6eXRnD0/s7iGb90fgoroE5x6/gjecvIJXrV8+72cRMLP73X3DhOdKnBwMaHT3PjNLAr8CPuDu9+RdczGwwd3fF/W+Sg4iMhu9gyke3N7Ffc8e4Geb9/Dknt6Rc+vaGrn8VUfyx6e1z9vJMismObzowWYNBMnhcne/N+/4xSg5iEgZbO3s47ZHdvHd320facc4fHEdHzx3PW89fdW86x1VUcnBzOLA/cBRwDfc/W/GnL8Y+HugE3gK+JC7b5/gPpcBlwGsWbPm9Oeee67IkYvIQpHKZLnloRf45zu3jkzd/7rjD+Mf3nwySxtn3l26UlVUchh5sFkL8APg/e7+aN7xZUCfuw+Z2f8D3u7ur57qXio5iEgxZLPODx7YyRUbN9M7lKatuZavvPUUXrU+4kybFW6q5FC2ijR37wLuAM4bc3y/u+emHP0WcHqpYxMRgWCamTefvorbPvhKXtaxlM7eId593X3822+3lTu0oitpcjCztrDEgJnVA+cCT4y5ZmXe7gXA46WLUERkvFVLGrjpsjP4yLnrAfjUjzbzX/fvKHNUxVXqcQ4rgRvCdocY8D13/7GZfQ7Y5O4bgb8yswuANHAAuLjEMYqIjBOPGe9/zdHUJmP83a1P8Nfff4jG2jjnnbhy+h9XobK1ORSS2hxEpJSu/PlTfP0XT5OMG9e++6UvWmq4mlRkm4OISLX60GuP5j2vOIJUxnnvjb9nT89guUMqOCUHEZEZMjM+9cbjePWxy+kdSvPZWzaXO6SCm7TNwczeM8t7/tDdD0x/mYhI9TIzPv/HJ3LPlb/k1kd284vH9/Ca4w4rd1gFM1WD9JTzGU3CgQcJGpJFROa19pZ6Pnzuer7wk8f59I82c8a6ZTTWzo/5TKerVjoLqI/4aQbm19hyEZFpXPzyDk5sX8TOrgGu/PlT5Q6nYKZKDvcC+919KMoHGAh/01eSyEVEKkAiHuMf/uRkYgbf/vWzPLqzu9whFcSkycHdz3T3yAPQ3D0b/mb+pE4RkQhObF/Mu1/eQdbhm3duLXc4BTFpcjCz95vZ8lIGIyJSrS47ax3xmPE/m3ezu7v6u7ZOVa30NWCnmf3czC4xs8WlCkpEpNqsXFzPeSesIJN1bry3+meJnio5rAU+DrQA1wK7zeyHZva2cF4kERHJ864z1wJw033PM5TOlDmauZmqzWGHu3/F3V8KHA18ETgS+C6w18xuNLM3mNn86LclIjJHLztiKceuaGZf3zC3PrKr3OHMSaQR0u6+1d2/4O4nAScRVDltADYCe8zsX4oYo4hIVTAzLn55BwA3/Ka6q5ZmPH2Gu292908BLwO+SVDt9BeFDkxEpBpdeGo7i+uTPLi9i4e2d5U7nFmbUXIwswYzu8jMfgjsBi4H7gb+shjBiYhUm/qaOG9/6WoAbvjttrLGMhfTJgczqzGzN5nZzcBe4D+AduCTwBp3P9vdVa0kIhJ65x+sxQx+/NAu+obS5Q5nVqYa53C+md1AkBD+i6Ct4UvAend/qbtf6e47SxSniEjVWLOsgdNWtzCcyfLrLfvKHc6sTFVy+AnwKuBq4DR3P97dP+/uW0oTmohI9Xr1scEY4jue2FvmSGZnquTwh+7e4e4fc/eHShaRiMg8cE4uOTy5l2pccXOq5DDjdTfNrGYOsYiIzBvHr1zEikV17OkZYvMLPeUOZ8amSg4DZvayqDcys3j4m5dMcU2dmd1nZg+Z2WYz++wE19Sa2c1mtsXM7jWzjqgxiIhUCjPjnGODtaWrsWppqtHNBmwws6aI94ox/XoOQ8Cr3b3PzJLAr8zsNne/J++aS4GD7n6UmV1E0Aj+9ogxiIhUjLOPWc5N923njif38v7XHF3ucGZkuqkvrprh/aasWPOg4i233kMy/Iz9zYXAFeH294GrzMy8GivtRGRB+8OjWqmJx3hgexcHDg2ztLF6at6nSg7HzfKe26Y6GVY/3Q8cBXzD3e8dc0k7sB3A3dNm1g0sA/aNuc9lwGUAa9asmWWoIiLF01ib4A/WLeXup/fxy6f28qbTVpU7pMgmTQ7u/mQxHujuGeBUM2sBfmBmJ7r7o7O4zzXANQAbNmxQqUJEKtI5xyzn7qf3cfsTnVWVHGY8t1KhuHsXcAdw3phTO4HVAOGMr4uB/aWNTkSkMHLjHX755F7SmWyZo4mupMnBzNrCEgPhmhDnAk+MuWwj8O5w+y3A7WpvEJFq1dHayBGtjfQMpnmgiibiK3XJYSVwh5k9DPwO+Lm7/9jMPmdmF4TXXAssM7MtwIeBj5U4RhGRgjrnmFzpobPMkURX0oV63P1h4LQJjn86b3sQeGsp4xIRKaYz1i3lul8/ywPbD5Y7lMhmVXIws6VmVrb2ChGRanLK6hYAHt7eTTZbHbXkkf+AN7PXmNkvzayPYKbWU8Pj3zAzDVITEZnEYYvqWLGojt6hNM/uP1TucCKJlBzM7B3AzwgW+PnImN89TzjeQEREJnbK6sUAVbM6XNSSw6eBf3T3twPfGnPuUeCEgkYlIjLPnLwqqFqab8nhCODWSc71E4xFEBGRSZwatjs8uKO7zJFEEzU57AROnuTcS4BnChOOiMj8dNKq4O/Qj7/Qw3C68gfDRU0O1wNXmNlbCCbLA3AzewXwNwRjE0REZBKL6pIc2dbIcCbLE7srf32HqMnhiwTrSH8POBAe+xVwF3CLu19ZhNhEROaVU6qo3SFScnD3rLtfCpxEMGL5C8AnCdaWvrSI8YmIzBu58Q4PVUG7w4xGSLv7ZmBzkWIREZnXRpJDFZQcIiWHaZYLzQI9wNZwOm4REZnAcSubScaNLZ199A6maK5LTv+jMolacriHaVZ5A/rM7Grg4+5e+U3xIiIlVpuIc9zKRTy8o5tHdnbz8iNbyx3SpKI2SJ9PsDrb9cCfAK8Mv28AdgB/BvwT8AGCAXMiIjKBXKP0wxXe7hC15HAp8O/u/rdjjv/IzL4IvN3d32RmEKzFcEXhQhQRmT9OXlUd02jMpORw5yTn7iRYtAeCld0On1tIIiLz16lV0igdNTl0EySIiZwP5N6yjqBxWkREJrCurYnGmjgvdA+yr2+o3OFMKmq10pXAV8xsNXAL0Am0ARcStD18JLzuVcD9hQ5SRGS+iMeMow9r5sHtXWzZ20drU225Q5pQpOTg7lea2S7g4wTrOuc8CvyZu98U7n8NGCxsiCIi88tRy5tGksMZ65aVO5wJRR4EFyaAm8ysDjgM2BMu6Zl/zQsFjk9EZN45ankTAFv29pU5ksnNeKlPdx909+fGJoYozGy1md1hZo+Z2WYz+8AE15xtZt1m9mD4UddYEZlXjmoLksPWzspNDpFLDmbWDrwDWE/Q8Pwi7v6uCLdJAx9x99+bWTNwv5n93N0fG3Pd3e7+xqixiYhUk2ooOUSdPuMU4G5gH7AWeAJYAqwAdgHPRbmPu+8Kr8fde83scaAdGJscRETmrdVLG6hJxNjVPUjfUJqm2hlNc1cSUauVvkLQS2k9YMCfu/vhwGuBDPCpmT7YzDqA04B7Jzh9ppk9ZGa3mZmWIBWReSUeM9a1NgKwtUJLD1GTw2nAvxFMsgdhtZK73w58HvjyTB5qZk0E60N80N3Hjov4PbDW3U8hmJLjh5Pc4zIz22Rmmzo7O2fyeBGRsjuywquWoiaHGDAYTqjXCazOO/cscEzUB5pZkiAx3Oju/z32vLv3uHtfuH0rkDSzcbNTufs17r7B3Te0tbVFfbyISEXINUpvqdBG6ajJ4XFgXbh9L/CBsOfRYcCHgG1RbmLB5EvXAo9Ptnqcma0Ir8tNFR4D9keMU0SkKlR6o3TUVpBrgTXh9ieBnzKaEAaBt0W8zyuAPwceMbMHw2OfyN3b3a8mGGR3uZmlgQHgInefbrpwEZGqkksOldrmEHWE9HV524+Y2fEE03bXA792950R7/Mrggbtqa65Crgqyv1ERKrVEa2NxAyeO9DPcDpLTWLGw86KKlI0ZvY2M1uS23f3Lne/xd2/Bxwys6glBxERAeqScVYvbSCTdbbtP1TucMaJmqpuAo6e5NyR4XkREZmBkUbpCqxaipocpqoKWgL0FiAWEZEFpZIbpSdtczCzNwBvyDv0UTPbO+ayOuAcNE23iMiMVfJYh6kapNcQNDrnnErQeyjfMPAbtCyoiMiMVWXJwd2/CXwTwMx+C1w6wQR5IiIyS7nk8My+PrJZJxabsjNnSUVqc3D3M5UYREQKa1FdkuXNtQymsuzsGlsxU15TtTm8ZyY3yh8LISIi0Ry1vIm9vUNs2dvH6qUN5Q5nxFRtDt+awX0cUHIQEZmho5Y38Zut+9myt49zjl1e7nBGTJUc6ksWhYjIAlWpjdJTNUgPlTIQEZGFqGNZsK7D8wf6yxzJi81kmdBm4D3AHwJLgQMEq8Ndl5tiW0REZmZN2M5Qackh6txKHcDDBCvCtQM94fdXgYfNbG2R4hMRmdcOb6knZrCre4DhdHb6H5RI1OkzrgSGgPXu/nJ3f5O7v5xg2dDB8LyIiMxQTSLGysX1ZB1eqKDurFGTw2uAT7r7s/kHw/3PEKwlLSIis7B6adD/p5KqlmYy8d5kC+5kmWaNBhERmVwltjtETQ6/BD5rZofnHzSzlQQlhzsLHJeIyIKRSw7bD1ZOcojaW+lDwB3AM2Z2D7AHWA6cCewNz4uIyCzkRkZvr7aSg7tvIVjs52PATqANeAH4KHCMu28tWoQiIvNcJVYrRR7n4O6DwNeKGIuIyII0khz2V05yiDrO4WdmdomZtczlYWa22szuMLPHzGyzmX1ggmvMzL5uZlvM7GEze8lcnikiUumWNtbQWBOnZzBNd3+q3OEA0RukhwjWdthtZreY2Z+aWdMsnpcGPuLuxwNnAO81s+PHXHM+QRXW0cBl4XNFROYtMxtpd6iUqqWobQ5/BBwGXE5QFXU9sMfMvm9mbzWzuoj32eXuvw+3e4HHCUZa57sQ+I4H7gFawl5RIiLzVqW1O0QtOeDu3e7+bXc/H1hJ0EOpBbiRoPfSjIRTcpwG3DvmVDuwPW9/B+MTCGZ2mZltMrNNnZ2dM328iEhFqdrkkM/d9wP3Aw8QTMA3oyqmsErqv4APunvPLGO4xt03uPuGtra22dxCRKRirFlWWckhcm8lADM7GXg78DZgHbAV+FfguzO4R5IgMdzo7v89wSU7gdV5+6vCYyIi81aljXWIlBzM7LMECWE98DzwPeDmXPtBVGZmwLXA4+4+2WR9G4H3mdl3gT8Aut1910yeIyJSbSqtWilqyeEvgP8ELgkbiWfrFcCfA4+Y2YPhsU8AawDc/WrgVuD1wBagH7hkDs8TEakK7S31mMHOrgHSmSyJ+Kxq/QsmanJY5e6TTbwXmbv/imkm6Quf8965PktEpJrUJeMc1lzH7p5BdnUPjlQzlUvUrqxzTgwiIjK1SqpaKm+5RURERlTSQDglBxGRCqGSg4iIjLNmWeWsCKfkICJSIdZU0FiHSXsrmdmrZ3Ijd7997uGIiCxcldTmMFVX1v8lWDc6yvrQDsQLEpGIyALV1lRLXTJGV3+KnsEUi+qSZYtlquRwXMmiEBGRYOruJQ08vbeP7Qf6OeHwxWWLZdLk4O5PljIQERGB9iX1PL23j50HByozOUwknBtpJTBu/QZ3f6ZQQYmILFTtLUGPpZ1dA2WNI+rEewngy8B7mHx6brU5iIjMUfuSMDkcLG9yiNqV9RMEU3V/kKCB+sPAXwK/BrYBby5GcCIiC02llByiJoc/Ba4AvhPu/8rd/8XdzyJYye3cIsQmIrLgrFpSXclhDcEaDBlgiGB50JwbCNZ6EBGROWpvCcY6VEu10m4g12y+jWBdhpy1M7iPiIhMYXlzLcm4sf/QMAPDmbLFEfUP9bsYTQjXAX9rZteZ2TeBrwI/LkZwIiILTSxmrFxc/qqlqF1Z/xZYHm5/JfzdW4B6wmRR+NBERBam9pZ6nj/Qz86uAY5aPlkH0eKKlBzcfQewI9x24O/Dj4iIFFgldGdVW4GISIUZ7c5avgn4IiUHM4ub2fvM7HYze8rMnh/7iXif68xsr5k9Osn5s82s28weDD+fnsnLiIjMB5VQcoja5vBV4P3Az4BbgOFZPu964CpGx0tM5G53f+Ms7y8iUvVWVcBAuKjJ4SLgE+7+pbk8zN3vMrOOudxDRGS+q4SSQ9Q2hwRwfzEDyXOmmT1kZreZ2QmTXWRml5nZJjPb1NnZWaLQRESKb+Xiesxgd88gqUy2LDFETQ7XAW8tZiCh3wNr3f0U4J+AH052obtf4+4b3H1DW1tbCUITESmNmkSM5c21ZB12dw+WJYao1UpbgY+b2W3Az4GusRe4+3VzDcbde/K2bzWzfzazVnffN9d7i4hUk/aWevb0DLGza2Bk+dBSipocvhl+rwH+zwTnnaB0MSdmtgLY4+5uZi8jKNnsn+t9RUSqTfuSBn7/fFfZ2h2iJof6QjzMzG4CzgZazWwH8BkgCeDuVxOMur7czNLAAHBROOhORGRBKffU3VFHSA8V4mHu/o5pzl9F0NVVRGRBK3ePpUmTg5mtA7a7eyrcnpKWCRURKZxyj3WYquSwBTgDuC/cnqx6x8JzWiZURKRA2su86M9UyeF84PG8bRERKZH8Nods1onFrKTPnzQ5uPtPJ9oWEZHia6xN0NKQpKs/xf5Dw7Q115b0+bOaldXMYmM/hQ5MRGShK2ePpaizsjaZ2ZVm9qyZDQGpCT4iIlJAI8mhDD2Woo5z+DbwOoJZVbcw+1lZRUQkotFG6dKv6xA1ObwOeK+7/3sxgxERkVHlLDlEbSvYCXQXMxAREXmxVWHJYUcFJ4ePA580s5XFDEZEREa1twQT7pWjQTrq9Bk/MrNXAs+a2VNMPCvrWYUOTkRkIVuVN4WGu2NWurEOkZKDmf0d8GHgEdQgLSJSEi0NSRpq4vQOpekZSLO4IVmyZ0dtkL4c+Ky7f7aYwYiIyCgzY9WSep7a08eOrn4WNywu2bOjtjkMAb8pZiAiIjJersdSqRuloyaHq4D3FDMQEREZb9WSsFG6xMkharVSHfAKM3sEuIPxDdLu7p8paGQiIjIyEK7UJYeoyeHS8LsVeOsE551gVTcRESmgVWUaJR21K6vGN4iIlEHFtjmYWZ2ZbTQzjWMQESmxkTaHEg+EmzY5uPsgcBbRq6AmZWbXmdleM3t0kvNmZl83sy1m9rCZvWSuzxQRqWatTTXUJmJ09afoG0qX7LlReyv9BHhjAZ53PXDeFOfPB44OP5cB3yzAM0VEqpaZjc7OWsKqpailgR8C/2hmy4FbgT2MWVPa3W+f7ibufpeZdUxxyYXAd9zdgXvMrMXMVrr7rohxiojMO+0t9TzTeYidXf0cs6K5JM+MmhxuDr//NPyM5UC8APG0A9vz9neEx8YlBzO7jKB0wZo1awrwaBGRypRrdyhlo3TU5HBcUaOYBXe/BrgGYMOGDT7N5SIiVWtVpVYrufuTxQ4ktBNYnbe/KjwmIrJglWNdh6gN0phZwswuMbNvhF1bjwyPv8nMji5QPBuBd4W9ls4AutXeICIL3chYhxJ2Z406Zfc64KfACuAh4EzgivD0ucAFwCUR7nMTcDbQamY7CEZVJwHc/WqCxu7XE0wL3h/lniIi891ob6XSjZKO2ubwdWA/QVLo4sXrOdwJ/H2Um7j7O6Y578B7I8YkIrIgLG+uIxk39vUNM5jKUJcsRP+fqUWtVjob+IK772NMF1ZgN6DpNUREiiQeM1YuLm27Q9TkkCKs/pnASqCnMOGIiMhERifgq6zk8L/Ax8ysMe+Ym1mCoBrofwoemYiIjBidgK807Q5R2xz+mmAluKcJEoEDHwNOAFqAKdsSRERkbkq96E+kkoO7bwNOAW4ETiUYe3AMQQ+m091dYxFERIqo1Iv+TFpyCKfo/r279wG4eydBCUJEREqsktoc7gCOL0kUIiIypVK3OUyVHKwkEYiIyLRWLq4jHjP29g4xlM4U/XmRp88QEZHyScRjrFhUhzvs6hos/vOmOf96Mzs2yo3c/TsFiEdERCbRvqSenV0DbD/YT0dr4/Q/mIPpksOnI97HASUHEZEiWru0gfuePcBz+/t5ZaGmO53EdMnhHGBTcUMQEZEocqWF5w8Uv1F6uuQw4O6Hih6FiIhMa83SYCDctn3F/2NZDdIiIlWiY1npSg5KDiIiVWLNsqDk8Nz+foIVDopn0uTg7jF3v6+oTxcRkcgW1ydZ0pBkIJWhs3eoqM9SyUFEpIqsCauWtu0vbtWSkoOISBXpGKlaKm6jtJKDiEgVWbt0tN2hmEqeHMzsPDN70sy2mNnHJjh/sZl1mtmD4ecvSh2jiEilWhtWKz1X5B5LURf7KQgziwPfAM4FdgC/M7ON7v7YmEtvdvf3lTI2EZFqsHaeViu9DNji7s+4+zDwXeDCEscgIlK1RkoO86xaqR3Ynre/Izw21pvN7GEz+76ZrS5NaCIila+1qYaGmjjdAym6+oeL9pxKbJC+Behw95OBnwM3THSRmV1mZpvMbFNnZ2dJAxQRKRczK0npodTJYSeQXxJYFR4b4e773T03uuNbwOkT3cjdr3H3De6+oa2trSeSTfMAAAeOSURBVCjBiohUolyPpW1FbHcodXL4HXC0mR1hZjXARcDG/AvMbGXe7gXA4yWMT0Sk4q1tDZLD80UsOZS0t5K7p83sfcBPgThwnbtvNrPPAZvcfSPwV2Z2AZAGDgAXlzJGEZFKt3Zp8UdJlzQ5ALj7rcCtY459Om/748DHSx2XiEi1yI2Sfv7A/KlWEhGROcrNzlrMkoOSg4hIlVm5uJ6aeIzO3iH6h9NFeYaSg4hIlYnHjFVL64HidWdVchARqUIdRR7roOQgIlKF1iwt7hxLSg4iIlVoZF2HIs3OquQgIlKFRqfQUMlBRERCa5c1UJeMEY8V54/xkg+CExGRuTuitZHHP3ceZlaU+ys5iIhUoWIlhRxVK4mIyDhKDiIiMo6Sg4iIjKPkICIi4yg5iIjIOEoOIiIyjpKDiIiMY+5e7hjmzMw6gedm+fNWYF8Bw6k0er/qpverXtXwbmvdvW2iE/MiOcyFmW1y9w3ljqNY9H7VTe9Xvar93VStJCIi4yg5iIjIOEoOcE25AygyvV910/tVr6p+twXf5iAiIuOp5CAiIuMoOYiIyDgLJjmY2Xlm9qSZbTGzj01wvtbMbg7P32tmHaWPcvYivN+HzewxM3vYzH5hZmvLEedsTfd+ede92czczKqqC2GU9zOzt4X/Djeb2X+UOsbZivDf5hozu8PMHgj/+3x9OeKcLTO7zsz2mtmjk5w3M/t6+P4Pm9lLSh3jrLj7vP8AcWArsA6oAR4Cjh9zzV8CV4fbFwE3lzvuAr/fOUBDuH35fHu/8Lpm4C7gHmBDueMu8L+/o4EHgCXh/vJyx13Ad7sGuDzcPh7YVu64Z/iOZwEvAR6d5PzrgdsAA84A7i13zFE+C6Xk8DJgi7s/4+7DwHeBC8dccyFwQ7j9feA1Vuyllgpn2vdz9zvcvT/cvQdYVeIY5yLKvz+AzwNfAgZLGVwBRHm//wt8w90PArj73hLHOFtR3s2BReH2YuCFEsY3Z+5+F3BgiksuBL7jgXuAFjNbWZroZm+hJId2YHve/o7w2ITXuHsa6AaWlSS6uYvyfvkuJfibTLWY9v3Covpqd/9JKQMrkCj//tYD683s12Z2j5mdV7Lo5ibKu10BvNPMdgC3Au8vTWglM9P/PyuC1pBeYMzsncAG4FXljqVQzCwGXAlcXOZQiilBULV0NkGp7y4zO8ndu8oaVWG8A7je3b9qZmcC/2ZmJ7p7ttyBLWQLpeSwE1idt78qPDbhNWaWICje7i9JdHMX5f0ws9cCnwQucPehEsVWCNO9XzNwInCnmW0jqNfdWEWN0lH+/e0ANrp7yt2fBZ4iSBaVLsq7XQp8D8DdfwvUEUxaN19E+v+z0iyU5PA74GgzO8LMagganDeOuWYj8O5w+y3A7R62JlWBad/PzE4D/oUgMVRLfXXOlO/n7t3u3uruHe7eQdCmcoG7bypPuDMW5b/PHxKUGjCzVoJqpmdKGeQsRXm354HXAJjZcQTJobOkURbXRuBdYa+lM4Bud99V7qCmsyCqldw9bWbvA35K0HviOnffbGafAza5+0bgWoLi7BaCxqWLyhfxzER8vy8DTcB/hu3sz7v7BWULegYivl/Vivh+PwVeZ2aPARngr9294ku2Ed/tI8C/mtmHCBqnL66iv5hhZjcRJO7WsN3kM0ASwN2vJmhHeT2wBegHLilPpDOj6TNERGSchVKtJCIiM6DkICIi4yg5iIjIOEoOIiIyjpKDiIiMo+Qg8144S+t0n7PN7OJwu6mMsV6fF9PXxhyfdtyGmW3L+/0bixutzGcLYpyDLHhn5m3XA7cDXwDy52F6DNgcXttPeT1B0Bd+NgOl3gR0AP9dyIBk4VFykHkvnAkTgLxSwdb843kqYWTuoUlim5a7P2BmBwsdkCw8qlYSCY2tVjKzjnD/IjP7tpn1mNmOcPJCzOyjZvaCmXWa2ZfCCQDz73eimf3EzHrDz3+a2Yo5xnhuuGDMITP7lZmdMJf7iUxGyUFkel8iqOJ5M3A3cIOZfZVgrYL3AF8DPgq8LfcDMzsK+DXBPEHvJJgx9gTgljmsE7KGYBqULxLMZLocuLmK1h2RKqJqJZHp3e7unwAws3sJJma8ADjW3TPA/5jZhQT1/d8Nf/MZYDdwfrjIDWb2MEF7wut5cXtHVEuBV7j70+H9YsAPgGPC+4oUjEoOItP7RW7D3XsI2iV+GSaGnC28eAGX1xL8wZ01s0Q4DfyzwDaC9TRmY1suMYQeC7+raVU/qRJKDiLTG7ugzvAkx+ry9luBvwFSYz7rePHc/nONgzHPFSkIVSuJFMcBgpLDtyY4t6/EsYjMmJKDSHH8gqAB+v5qWptAJEfJQaQ4rgDuA35iZtcRlBbagXMJ1ku+s3yhiUxPbQ4iReDuTxGsZd0PXAPcBnwWGCJovBapaFoJTqSCmNn1wIkEiSXr7tkZ/j5OMH3GFuCP3P3HhY5RFgaVHEQqz+kEPZuunMVvt6KSiRSASg4iFcTMOgi6wQLscfftM/z9SUBtuPu0u3cXLjpZSJQcRERkHFUriYjIOEoOIiIyjpKDiIiMo+QgIiLjKDmIiMg4/x+c57/IB4Jx6wAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] @@ -100,9 +100,8 @@ } ], "source": [ - "t, y = solution.t, solution.y\n", - "time = pybamm.ProcessedVariable(model.variables[\"Time [h]\"], t, y, mesh)(t)\n", - "voltage = pybamm.ProcessedVariable(model.variables[\"Terminal voltage [V]\"], t, y, mesh)(t)\n", + "time = solution[\"Time [h]\"].entries\n", + "voltage = solution[\"Terminal voltage [V]\"].entries\n", "plt.plot(time, voltage, lw=2, label=model.name)\n", "plt.xlabel(\"Time [h]\", fontsize=15)\n", "plt.ylabel(\"Terminal voltage [V]\", fontsize=15)\n", @@ -124,6 +123,26 @@ "cell_type": "code", "execution_count": 3, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + ">" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.default_parameter_values.items" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -152,15 +171,19 @@ "Positive current collector specific heat capacity [J.kg-1.K-1] 897.0\n", "Negative current collector thermal conductivity [W.m-1.K-1] 401.0\n", "Positive current collector thermal conductivity [W.m-1.K-1] 237.0\n", - "Cell capacity [A.h] 0.68\n", + "Cell capacity [A.h] 0.680616\n", + "Typical current [A] 0.680616\n", "Negative electrode conductivity [S.m-1] 100.0\n", "Maximum concentration in negative electrode [mol.m-3] 24983.2619938437\n", - "Negative electrode diffusivity [m2.s-1] \n", - "Negative electrode OCP [V] \n", + "Negative electrode diffusivity [m2.s-1] \n", + "Negative electrode OCP [V] \n", "Negative electrode porosity 0.3\n", + "Negative electrode active material volume fraction 0.7\n", "Negative particle radius [m] 1e-05\n", + "Negative particle distribution in x 1.0\n", "Negative electrode surface area density [m-1] 180000.0\n", - "Negative electrode Bruggeman coefficient 1.5\n", + "Negative electrode Bruggeman coefficient (electrolyte) 1.5\n", + "Negative electrode Bruggeman coefficient (electrode) 1.5\n", "Negative electrode cation signed stoichiometry -1.0\n", "Negative electrode electrons in reaction 1.0\n", "Negative electrode reference exchange-current density [A.m-2(m3.mol)1.5] 2e-05\n", @@ -170,19 +193,22 @@ "Negative electrode density [kg.m-3] 1657.0\n", "Negative electrode specific heat capacity [J.kg-1.K-1] 700.0\n", "Negative electrode thermal conductivity [W.m-1.K-1] 1.7\n", - "Negative electrode OCP entropic change [V.K-1] \n", + "Negative electrode OCP entropic change [V.K-1] \n", "Reference temperature [K] 298.15\n", - "Negative electrode reaction rate \n", + "Negative electrode reaction rate \n", "Negative reaction rate activation energy [J.mol-1] 37480.0\n", "Negative solid diffusion activation energy [J.mol-1] 42770.0\n", "Positive electrode conductivity [S.m-1] 10.0\n", "Maximum concentration in positive electrode [mol.m-3] 51217.9257309275\n", - "Positive electrode diffusivity [m2.s-1] \n", - "Positive electrode OCP [V] \n", + "Positive electrode diffusivity [m2.s-1] \n", + "Positive electrode OCP [V] \n", "Positive electrode porosity 0.3\n", + "Positive electrode active material volume fraction 0.7\n", "Positive particle radius [m] 1e-05\n", + "Positive particle distribution in x 1.0\n", "Positive electrode surface area density [m-1] 150000.0\n", - "Positive electrode Bruggeman coefficient 1.5\n", + "Positive electrode Bruggeman coefficient (electrolyte) 1.5\n", + "Positive electrode Bruggeman coefficient (electrode) 1.5\n", "Positive electrode cation signed stoichiometry -1.0\n", "Positive electrode electrons in reaction 1.0\n", "Positive electrode reference exchange-current density [A.m-2(m3.mol)1.5] 6e-07\n", @@ -192,19 +218,20 @@ "Positive electrode density [kg.m-3] 3262.0\n", "Positive electrode specific heat capacity [J.kg-1.K-1] 700.0\n", "Positive electrode thermal conductivity [W.m-1.K-1] 2.1\n", - "Positive electrode OCP entropic change [V.K-1] \n", - "Positive electrode reaction rate \n", + "Positive electrode OCP entropic change [V.K-1] \n", + "Positive electrode reaction rate \n", "Positive reaction rate activation energy [J.mol-1] 39570.0\n", "Positive solid diffusion activation energy [J.mol-1] 18550.0\n", "Separator porosity 1.0\n", - "Separator Bruggeman coefficient 1.5\n", + "Separator Bruggeman coefficient (electrolyte) 1.5\n", + "Separator Bruggeman coefficient (electrode) 1.5\n", "Separator density [kg.m-3] 397.0\n", "Separator specific heat capacity [J.kg-1.K-1] 700.0\n", "Separator thermal conductivity [W.m-1.K-1] 0.16\n", "Typical electrolyte concentration [mol.m-3] 1000.0\n", "Cation transference number 0.4\n", - "Electrolyte diffusivity [m2.s-1] \n", - "Electrolyte conductivity [S.m-1] \n", + "Electrolyte diffusivity [m2.s-1] \n", + "Electrolyte conductivity [S.m-1] \n", "Electrolyte diffusion activation energy [J.mol-1] 37040.0\n", "Electrolyte conductivity activation energy [J.mol-1] 34700.0\n", "Heat transfer coefficient [W.m-2.K-1] 10.0\n", @@ -213,12 +240,11 @@ "Lower voltage cut-off [V] 3.105\n", "Upper voltage cut-off [V] 4.7\n", "C-rate 1.0\n", - "Current function Constant current\n", "Initial concentration in negative electrode [mol.m-3] 19986.609595075\n", - "Initial concentration in positive electrode [mol.m-3] 30730.7554385565\n", + "Initial concentration in positive electrode [mol.m-3] 30730.755438556498\n", "Initial concentration in electrolyte [mol.m-3] 1000.0\n", "Initial temperature [K] 298.15\n", - "Typical current [A] 0.68\n" + "Current function [A] 0.680616\n" ] } ], @@ -244,20 +270,20 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Typical current [A] was 0.68\n", - "Typical current [A] now is 1.4\n" + "Current function [A] was 0.680616\n", + "Current function [A] now is 1.4\n" ] } ], "source": [ - "variable = \"Typical current [A]\"\n", + "variable = \"Current function [A]\"\n", "old_value = param[variable]\n", "param[variable] = 1.4\n", "new_value = param[variable]\n", @@ -276,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -292,7 +318,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -308,12 +334,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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g+2tNAGYpqq7RKRpr6TtQMxNr1Hw+zciev8Nfzwl6HNLSkqoqakJea+skpIS3n77bQ477LBmx6688kquuuqqwOclS5bwxhtvsHTpUo499lgATjzxRNauXctdd93Fyy+/HDjX7XYzb968wG/Sq1at4oUXXuDRRx8lPT2dgw46CIDDDz+83VLD3Llzueiii9p9loY/rJvas2dP4Df5hrKystizZ0+r39u+fTsAv/71r7n00ku58847effdd7npppvIyMjgsssuCzzHggULOPvss/n9738POEngzTffJDExMRAD0CyOrKyswPHc3Fy2b9/O559/zurVq3niiSeIj4/npptuYvLkyXzzzTckJSW1+2fRMP5QXctElitOGJiRzKbdlWzeU8WB/VLb/1IXBZscPEBrSxANBMpbOWZCKNQDlPLy8lpMDACnnNI4eb399tsMGDCA8ePHN/pNfeLEic16yhx33HGNqlhGjx4dmPTQ7e7YSlZTpkzh008/7dB3QqU+4Zx88sn88Y9/BJxn27x5M/fcc08gOaxcuZLzzz+fM844g/PPP599+/bxxz/+kZ/97Gd8+OGHHZqnSFXZt28fr776KqNGjQJgzJgxjBw5kueee44ZM2a0c4XuuZaJvPwsJzlsKY2u5PA28HsRWQzUd+9QEYkHLgf+0eo3o1RHfnOPtL59+5KYmMimTZtCet3+/fsHfWzXrl1s3769xR/uLlfjwmTT344TEhJQVWpqajqcHLKzs8nI6NrqV1lZWZSVlTXbv2fPnsBv7q19D5yE0NDxxx/PM888Q3l5Oenp6dx8882MGDGCp556KnDOT37yE/Lz83nyySeZOXNm4FplZWWN/nzqSxT1x7Oysujfv3/ghznAsGHDKCgoYNWqVR1+7lBdy0Te/gn4wtPuEGybw+9wuph+i9NQrMDvgRXAMOAP3RKdAZxqmvHjx/Pmm2+2e259VUFtbeNlvluqPmmrJNL0WHZ2Nnl5eXz66afNto8//jiYx+iUuXPn4na7293acvDBB1NUVMS+ffsa7V+zZk2zNoCG6n+oNq2yqv9c3yi8Zs2aZiWwrKwshgwZwvr16wMx1J/bNIbs7Gzqx+qMGjWqxSoyVQ2qEbpp/KG6lom8cC/6E+w4hw04vZSeAw7DGXtwEPAmcISq2liEbnb11VezfPly5s6d2+yYz+cLDMrKz88H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\n", "text/plain": [ "
" ] @@ -325,11 +351,10 @@ } ], "source": [ - "t, y = solution.t, solution.y\n", - "new_voltage = pybamm.ProcessedVariable(model.variables[\"Terminal voltage [V]\"], t, y, mesh=mesh)(t)\n", - "new_time = pybamm.ProcessedVariable(model.variables[\"Time [h]\"], t, y, mesh=mesh)(t)\n", - "plt.plot(time, voltage, lw=2, label=\"Typical current = {}\".format(old_value))\n", - "plt.plot(new_time, new_voltage, lw=2, label=\"Typical current = {}\".format(new_value))\n", + "new_voltage = solution[\"Terminal voltage [V]\"].entries\n", + "new_time = solution[\"Time [h]\"].entries\n", + "plt.plot(time, voltage, lw=2, label=\"Current = {}\".format(old_value))\n", + "plt.plot(new_time, new_voltage, lw=2, label=\"Current = {}\".format(new_value))\n", "plt.xlabel(\"Time [h]\", fontsize=15)\n", "plt.ylabel(\"Terminal voltage [V]\", fontsize=15)\n", "plt.legend(fontsize=15)\n", @@ -347,7 +372,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -379,7 +404,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -403,12 +428,12 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { - "image/png": "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/t3vfte1vsd56aWXOo4bN25gvHXqi6QYAAAAAABAFYsWLWq/cOHCLmvWrFm3fv36dUuWLFk/YMCALyRp7ty5H4wePfrzRMXSunXrivfee2/dhg0bilu2bOn33HNPVn3blpeX65FHHskuKyv7ew5o2bJlG3v06HGkMWMcNGjQoWeeeaZb5fqzzz7bbciQIYca8xyNjaQYAAAAAABIbmajm6TUYtu2bS27detW3rZtW5eknJyc8n79+n0pSWPGjBny+uuvt5Okdu3anfSTn/yk15AhQ/JGjRo19KOPPsqUpOLi4tajRo0aOnjw4Lzp06cf365du5OqnqO8vFxXXnll7ogRI4YNHjw4b8aMGT3quhTf/va3yzZu3NhakiZMmHDC8OHDhw0cOHD43Xff/fe27dq1O+nyyy/PHTJkSN6NN96Ys2vXrpann3764LFjxw6WpF69eo3cvn17piQ98MAD3QcPHpw3ZMiQvEmTJvWver7i4uLWp5566qDhw4cPGz169JCVK1dWe4fc2LFjy1auXNn+8OHDtm/fvhZbtmxpPXz48IOV+1944YWOw4YNyxs8eHDe5MmT+x06dMgkad68eZ369+8/PC8vb9i8efO6VNbfv39/i8mTJ/cbOXLksGHDhuU9+eSTXao7bzxIigEAAAAAAFQxadKk/R9//HGrfv36jfjhD3/Y5+WXX+5QXb1Dhw61OOWUU8pKSkrWnXLKKWX3339/liRdc801vX/84x/vWr9+/brc3Nwvq2s7c+bMHp07dz6ydu3ad4uKit6dM2dO1nvvvdeqppi+/PJLLVy4sNPIkSMPSdJTTz21pbi4+N1Vq1ate+SRR7J37NiRURnT2LFjPyspKVl39913bz/uuOO+XLZs2frly5evjz1eYWFhm7vvvjtn2bJl60tKStY98sgjH1Y957Rp0/o+9NBDHxYXF787Y8aMrVdffXWf6mIzM5122mn7f//733d6+umnu0ycOPHTyn0HDx60K6+8sv/cuXM3rV+/fl15eblmzJiRdfDgQbvmmmv6zZ8/f+PatWvf3bVrV8vKNr/4xS9yxo0bt3/NmjXvvvHGGyX//u//nrt///5GzWNlNubBAAAAAAAAGp37ikSfsnPnzhVr165d9+c//7nj4sWLO06ZMuWEW265Zev06dP3xNZr2bKlX3zxxfskafTo0Z8tWrSokyStXLmywyuvvLJRkqZNm7bn1ltvza16jkWLFnV677332s2fP7+rJB04cCBj3bp1bYYOHfpFbL3Dhw+3GDp0aJ4kjR079sC11167W5Luuuuu7JdffrmLJO3YsaNlcXFxm549e36WkZGhqVOn7q3rNS5cuLDT97///b05OTnlkpSdnX3UI5X79u1rsXLlyg6TJ08+oXLbF198YTUd75JLLvlk5syZ2QcOHMiYOXPmR7fddluOJBUVFbXJzc09fOKJJx6WpKlTp+558MEHj5swYcKB3NzcwyNHjjwctt8ze/bsLElaunRpp4ULF3a57777eobXwDZu3FhjwvBYkBQDAAAAAACoRmZmps4555wD55xzzoETTzzx0BNPPNG9alIsMzPTW7Ro8ff65eXlNSaNqnJ3u+eeez684IIL9tdWr3JOsdhtL730Usdly5Z1LCwsfK9jx44VY8aMGXLo0KEWktSqVauKzMz4Uz5HjhxRx44dy6ueuybjxo07eNVVV7Vt27ZtRWUC7Fi5u+bNm7dx1KhRRx3n448/bllTm4bi8UkAAAAAAIAqioqKWq9Zs6Z15frKlSvb5ubmflFbm1j5+flljz32WFdJevTRR7tVV+fMM8/c95vf/Cbr8OHDJkmrV69uXd9HBD/99NOMzp07H+nYsWPFypUr2xQVFbWvqW779u2P7Nu372vHPeuss/a/+OKLXSsfu9y5c+dR30jZrVu3itzc3C8effTRrpJUUVGht956q21tcd1xxx1b77jjjm2x20aNGvX5tm3bWq1du7a1JD3++OPdTz311AP5+fmfb9u2rVVxcXFrKZicv7LNuHHj9t9zzz3ZFRUVkqQ333yz1vMeC+4UAwAAAAAAqGL//v0Z06dP77N///6MjIwM79ev3+E5c+Z8UN/2999//0eXXHJJ/xkzZuSMHz9+f4cOHb72bY8//elPd2/ZsqX1yJEjh7m7devW7csFCxZsqs/xL7jggn2zZs3KGjBgwPABAwZ8PmrUqM9qqjtlypTdEydOHJydnf1F7LxiBQUFn//sZz/bfuqppw5t0aKFjxgx4uDzzz+/JbbtM888s/nyyy/ve9ddd+WUl5fb+eef/8kpp5xS47dKXnjhhV+7661du3b+8MMPb5k8efIJR44c0ahRow5ef/31pW3btvX777//g3POOWdg27ZtK8aOHVtWVlaWIUl33nnnx1dccUWfoUOH5lVUVFjv3r0PL1myZGN9rk19mbs35vEAAAAAAADiVlRUtGXUqFG7o47jWB04cKBF+/btK1q0aKFZs2Z1nTt3brfFixfXK+GFY1NUVNRj1KhR/epbnzvFAAAAAAAAGtmbb77Z7tprr+3j7urUqdORxx57bEvUMeFoJMUAAAAAAAAa2cSJE8tKSkrqNUE9osFE+wAAAAAAIBlVVFRU1PubHJHewt+Vioa0ISkGAAAAAACS0drS0tLOJMZQl4qKCistLe0saW1D2vH4JAAAAAAASDrl5eXTduzYMXvHjh0jxE09qF2FpLXl5eXTGtKIb58EAAAAAABA2iHTCgAAAAAAgLRDUgwAAAAAAABph6QYAAAAAAAA0g5JMQAAAAAAAKQdkmIAAAAAAABIO/8fkTHHQQhC7qoAAAAASUVORK5CYII=\n", + "image/png": "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hLKysr0kxNCakpKSKCsr26P6uwrTr5eleAAAAAAAADJRQkIM6UreK23Kc5EUAwAAAAAAQNEhKQYAAAAAANCEa6+9dtjIkSPHjx49etyYMWPG/epXv+otSZdddtlJL774Yo9jaXPjxo3dRo0aNb4tdbp06TJ5zJgx40aNGjX+4x//+Cn79u1rUz7nxhtvPC61zllnnTXyrbfe6tJc+WuuueYDN9xww9B027/zzjsH257805/+tG/DvgcffHCA7ck//OEPB6bbzuLFi/tOmzZtZKZl0kVSDAAAAAAAoJGlS5f2XrJkyYB169Zt2LRp04bly5dvOuWUUw5L0oIFC16dPHnyu7mKpXv37nUvv/zyhurq6qquXbvG7bffXpZu3draWt1zzz1D9+/f/+cc0IoVKzYPGTLkSHvGOGrUqIOPPPLIoIbtRx99dNBpp512sD3P0d5IigEAAAAAgMJmT87K0oJt27Z1HTRoUG3Pnj1DksrLy2tHjBjxniRNmTLltF//+te9JKlXr16nf/nLXz7+tNNOG1dRUTHmtddeK5Wkqqqq7hUVFWNGjx497qqrrvpAr169Tm98jtraWn3+858fPmHChLGjR48ed+uttw5p7VJ85CMf2b958+bukjR9+vRTx48fP3bkyJHjb7vttj/X7dWr1+mf+9znhp922mnjrrvuuvKdO3d2Peuss0ZPnTp1tCQdf/zxE7dv314qSd/97ncHjx49etxpp502bubMmSc3Pl9VVVX3M888c9T48ePHTp48+bRVq1Y1eYfc1KlT969atar3oUOHvGfPnpKtW7d2Hz9+/IGG40888UTfsWPHjhs9evS4WbNmjTh48KAlaeHChf1OPvnk8ePGjRu7cOHCAQ3l9+7dWzJr1qwREydOHDt27NhxDz300ICmzpsJkmIAAAAAAACNzJw5c+8bb7zRbcSIERM+/elPn/jkk0/2aarcwYMHS84444z9Gzdu3HDGGWfsv+uuu8ok6corrzzhS1/60s5NmzZtGD58+HtN1b3jjjuG9O/f/8j69etfWrNmzUvz588ve/nll7s1F9N7772nJUuW9Js4ceJBSXr44Ye3VlVVvbR69eoN99xzz9AdO3Z0aYhp6tSp72zcuHHDbbfdtv244457b8WKFZuef/75TantrVy5ssdtt91WvmLFik0bN27ccM899/yx8Tnnzp170ve+970/VlVVvXTrrbe+/sUvfvHEpmKzrY9+9KN7H3vssX4/+tGPBsyYMePthmMHDhzw5z//+ZMXLFiwZdOmTRtqa2t16623lh04cMBXXnnliEWLFm1ev379Szt37uzaUOdf/uVfyqdNm7Z33bp1L/3mN7/Z+I1vfGP43r172zWPVdqejQEAAAAAALS7iBdzfcr+/fvXrV+/fsPPf/7zvsuWLes7e/bsU2+44YbXr7rqql2p5bp27Rqf+tSn9kjS5MmT31m6dGk/SVq1alWfX/ziF5slae7cubu++c1vDm98jqVLl/Z7+eWXey1atGigJO3bt6/Lhg0beowZM+ZwarlDhw6VjBkzZpwkTZ06dd/VV1/9liTdcsstQ5988skBkrRjx46uVVVVPYYNG/ZOly5dNGfOnN2tvcYlS5b0++QnP7m7vLy8VpKGDh161COVe/bsKVm1alWfWbNmndqw7/Dhw26uvcsvv/xPd9xxx9B9+/Z1ueOOO1771re+VS5Ja9as6TF8+PBDH/zgBw9J0pw5c3bdfffdx02fPn3f8OHDD02cOPFQUn/XvffeWyZJTz/9dL8lS5YMuPPOO4cl18CbN29uNmF4LEiKAQAAAAAANKG0tFTnn3/+vvPPP3/fBz/4wYMPPvjg4MZJsdLS0igpKflz+dra2maTRo1FhG+//fY/XnLJJXtbKtcwp1jqvsWLF/ddsWJF35UrV77ct2/fuilTppx28ODBEknq1q1bXWlp5imfI0eOqG/fvrWNz92cadOmHfjCF77Qs2fPnnUNCbBjFRFauHDh5oqKiqPaeeONN7o2V6eteHwSAAAAAACgkTVr1nRft25d94btVatW9Rw+fPjhluqkmjRp0v77779/oCTdd999g5oqc+655+75/ve/X3bo0CFL0tq1a7un+4jg22+/3aV///5H+vbtW7dq1aoea9as6d1c2d69ex/Zs2fP+9o977zz9v7Xf/3XwIbHLt98882jfpFy0KBBdcOHDz983333DZSkuro6Pfvssz1biuumm256/aabbtqWuq+iouLdbdu2dVu/fn13SXrggQcGn3nmmfsmTZr07rZt27pVVVV1l+on52+oM23atL2333770Lq6OknSM8880+J5jwV3igEAAAAAADSyd+/eLlddddWJe/fu7dKlS5cYMWLEofnz57+abv277rrrtcsvv/zkW2+9tfzss8/e26dPn/f92uNXvvKVt7Zu3dp94sSJYyPCgwYNeu+pp57akk77l1xyyZ558+aVnXLKKeNPOeWUdysqKt5pruzs2bPfmjFjxuihQ4ceTp1XrLKy8t2vfvWr288888wxJSUlMWHChAM/+clPtqbWfeSRR1753Oc+d9Itt9xSXltb64suuuhPZ5xxRrO/KnnppZe+7663Xr16xQ9+8IOts2bNOvXIkSOqqKg48LWvfa2mZ8+ecdddd716/vnnj+zZs2fd1KlT9+/fv7+LJN18881vXHHFFSeOGTNmXF1dnU844YRDy5cv35zOtUmXI6I92wMAAAAAAMjYmjVrtlZUVLyV7ziO1b59+0p69+5dV1JSonnz5g1csGDBoGXLlqWV8MKxWbNmzZCKiooR6ZbnTjEAAAAAAIB29swzz/S6+uqrT4wI9evX78j999+/Nd8x4WgkxQAAAAAAANrZjBkz9m/cuDGtCeqRH0y0DwAAAAAAClFdXV1d2r/kiOKWvFfq2lKHpBgAAAAAAChE62tqavqTGENr6urqXFNT01/S+rbU4/FJAAAAAABQcGpra+fu2LHj3h07dkwQN/WgZXWS1tfW1s5tSyV+fRIAAAAAAABFh0wrAAAAAAAAig5JMQAAAAAAABQdkmIAAAAAAAAoOiTFAAAAAAAAUHRIigEAAAAAAKDo/H/QuErv6py/3wAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] @@ -430,7 +455,7 @@ "\n", "# ... and finally solve it\n", "solution = solver.solve(model, t_eval)\n", - "pybamm.QuickPlot(model, mesh, solution).plot(0)" + "pybamm.QuickPlot(solution).plot(0)" ] }, { @@ -448,7 +473,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -472,12 +497,12 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { - "image/png": "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/t3vfte1vsd56aWXOo4bN25gvHXqi6QYAAAAAABAFYsWLWq/cOHCLmvWrFm3fv36dUuWLFk/YMCALyRp7ty5H4wePfrzRMXSunXrivfee2/dhg0bilu2bOn33HNPVn3blpeX65FHHskuKyv7ew5o2bJlG3v06HGkMWMcNGjQoWeeeaZb5fqzzz7bbciQIYca8xyNjaQYAAAAAABIbmajm6TUYtu2bS27detW3rZtW5eknJyc8n79+n0pSWPGjBny+uuvt5Okdu3anfSTn/yk15AhQ/JGjRo19KOPPsqUpOLi4tajRo0aOnjw4Lzp06cf365du5OqnqO8vFxXXnll7ogRI4YNHjw4b8aMGT3quhTf/va3yzZu3NhakiZMmHDC8OHDhw0cOHD43Xff/fe27dq1O+nyyy/PHTJkSN6NN96Ys2vXrpann3764LFjxw6WpF69eo3cvn17piQ98MAD3QcPHpw3ZMiQvEmTJvWver7i4uLWp5566qDhw4cPGz169JCVK1dWe4fc2LFjy1auXNn+8OHDtm/fvhZbtmxpPXz48IOV+1944YWOw4YNyxs8eHDe5MmT+x06dMgkad68eZ369+8/PC8vb9i8efO6VNbfv39/i8mTJ/cbOXLksGHDhuU9+eSTXao7bzxIigEAAAAAAFQxadKk/R9//HGrfv36jfjhD3/Y5+WXX+5QXb1Dhw61OOWUU8pKSkrWnXLKKWX3339/liRdc801vX/84x/vWr9+/brc3Nwvq2s7c+bMHp07dz6ydu3ad4uKit6dM2dO1nvvvdeqppi+/PJLLVy4sNPIkSMPSdJTTz21pbi4+N1Vq1ate+SRR7J37NiRURnT2LFjPyspKVl39913bz/uuOO+XLZs2frly5evjz1eYWFhm7vvvjtn2bJl60tKStY98sgjH1Y957Rp0/o+9NBDHxYXF787Y8aMrVdffXWf6mIzM5122mn7f//733d6+umnu0ycOPHTyn0HDx60K6+8sv/cuXM3rV+/fl15eblmzJiRdfDgQbvmmmv6zZ8/f+PatWvf3bVrV8vKNr/4xS9yxo0bt3/NmjXvvvHGGyX//u//nrt///5GzWNlNubBAAAAAAAAGp37ikSfsnPnzhVr165d9+c//7nj4sWLO06ZMuWEW265Zev06dP3xNZr2bKlX3zxxfskafTo0Z8tWrSokyStXLmywyuvvLJRkqZNm7bn1ltvza16jkWLFnV677332s2fP7+rJB04cCBj3bp1bYYOHfpFbL3Dhw+3GDp0aJ4kjR079sC11167W5Luuuuu7JdffrmLJO3YsaNlcXFxm549e36WkZGhqVOn7q3rNS5cuLDT97///b05OTnlkpSdnX3UI5X79u1rsXLlyg6TJ08+oXLbF198YTUd75JLLvlk5syZ2QcOHMiYOXPmR7fddluOJBUVFbXJzc09fOKJJx6WpKlTp+558MEHj5swYcKB3NzcwyNHjjwctt8ze/bsLElaunRpp4ULF3a57777eobXwDZu3FhjwvBYkBQDAAAAAACoRmZmps4555wD55xzzoETTzzx0BNPPNG9alIsMzPTW7Ro8ff65eXlNSaNqnJ3u+eeez684IIL9tdWr3JOsdhtL730Usdly5Z1LCwsfK9jx44VY8aMGXLo0KEWktSqVauKzMz4Uz5HjhxRx44dy6ueuybjxo07eNVVV7Vt27ZtRWUC7Fi5u+bNm7dx1KhRRx3n448/bllTm4bi8UkAAAAAAIAqioqKWq9Zs6Z15frKlSvb5ubmflFbm1j5+flljz32WFdJevTRR7tVV+fMM8/c95vf/Cbr8OHDJkmrV69uXd9HBD/99NOMzp07H+nYsWPFypUr2xQVFbWvqW779u2P7Nu372vHPeuss/a/+OKLXSsfu9y5c+dR30jZrVu3itzc3C8effTRrpJUUVGht956q21tcd1xxx1b77jjjm2x20aNGvX5tm3bWq1du7a1JD3++OPdTz311AP5+fmfb9u2rVVxcXFrKZicv7LNuHHj9t9zzz3ZFRUVkqQ333yz1vMeC+4UAwAAAAAAqGL//v0Z06dP77N///6MjIwM79ev3+E5c+Z8UN/2999//0eXXHJJ/xkzZuSMHz9+f4cOHb72bY8//elPd2/ZsqX1yJEjh7m7devW7csFCxZsqs/xL7jggn2zZs3KGjBgwPABAwZ8PmrUqM9qqjtlypTdEydOHJydnf1F7LxiBQUFn//sZz/bfuqppw5t0aKFjxgx4uDzzz+/JbbtM888s/nyyy/ve9ddd+WUl5fb+eef/8kpp5xS47dKXnjhhV+7661du3b+8MMPb5k8efIJR44c0ahRow5ef/31pW3btvX777//g3POOWdg27ZtK8aOHVtWVlaWIUl33nnnx1dccUWfoUOH5lVUVFjv3r0PL1myZGN9rk19mbs35vEAAAAAAADiVlRUtGXUqFG7o47jWB04cKBF+/btK1q0aKFZs2Z1nTt3brfFixfXK+GFY1NUVNRj1KhR/epbnzvFAAAAAAAAGtmbb77Z7tprr+3j7urUqdORxx57bEvUMeFoJMUAAAAAAAAa2cSJE8tKSkrqNUE9osFE+wAAAAAAIBlVVFRU1PubHJHewt+Vioa0ISkGAAAAAACS0drS0tLOJMZQl4qKCistLe0saW1D2vH4JAAAAAAASDrl5eXTduzYMXvHjh0jxE09qF2FpLXl5eXTGtKIb58EAAAAAABA2iHTCgAAAAAAgLRDUgwAAAAAAABph6QYAAAAAAAA0g5JMQAAAAAAAKQdkmIAAAAAAABIO/8fkTHHQQhC7qoAAAAASUVORK5CYII=\n", + "image/png": "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hLKysr0kxNCakpKSKCsr26P6uwrTr5eleAAAAAAAADJRQkIM6UreK23Kc5EUAwAAAAAAQNEhKQYAAAAAANCEa6+9dtjIkSPHjx49etyYMWPG/epXv+otSZdddtlJL774Yo9jaXPjxo3dRo0aNb4tdbp06TJ5zJgx40aNGjX+4x//+Cn79u1rUz7nxhtvPC61zllnnTXyrbfe6tJc+WuuueYDN9xww9B027/zzjsH257805/+tG/DvgcffHCA7ck//OEPB6bbzuLFi/tOmzZtZKZl0kVSDAAAAAAAoJGlS5f2XrJkyYB169Zt2LRp04bly5dvOuWUUw5L0oIFC16dPHnyu7mKpXv37nUvv/zyhurq6qquXbvG7bffXpZu3draWt1zzz1D9+/f/+cc0IoVKzYPGTLkSHvGOGrUqIOPPPLIoIbtRx99dNBpp512sD3P0d5IigEAAAAAgMJmT87K0oJt27Z1HTRoUG3Pnj1DksrLy2tHjBjxniRNmTLltF//+te9JKlXr16nf/nLXz7+tNNOG1dRUTHmtddeK5Wkqqqq7hUVFWNGjx497qqrrvpAr169Tm98jtraWn3+858fPmHChLGjR48ed+uttw5p7VJ85CMf2b958+bukjR9+vRTx48fP3bkyJHjb7vttj/X7dWr1+mf+9znhp922mnjrrvuuvKdO3d2Peuss0ZPnTp1tCQdf/zxE7dv314qSd/97ncHjx49etxpp502bubMmSc3Pl9VVVX3M888c9T48ePHTp48+bRVq1Y1eYfc1KlT969atar3oUOHvGfPnpKtW7d2Hz9+/IGG40888UTfsWPHjhs9evS4WbNmjTh48KAlaeHChf1OPvnk8ePGjRu7cOHCAQ3l9+7dWzJr1qwREydOHDt27NhxDz300ICmzpsJkmIAAAAAAACNzJw5c+8bb7zRbcSIERM+/elPn/jkk0/2aarcwYMHS84444z9Gzdu3HDGGWfsv+uuu8ok6corrzzhS1/60s5NmzZtGD58+HtN1b3jjjuG9O/f/8j69etfWrNmzUvz588ve/nll7s1F9N7772nJUuW9Js4ceJBSXr44Ye3VlVVvbR69eoN99xzz9AdO3Z0aYhp6tSp72zcuHHDbbfdtv244457b8WKFZuef/75TantrVy5ssdtt91WvmLFik0bN27ccM899/yx8Tnnzp170ve+970/VlVVvXTrrbe+/sUvfvHEpmKzrY9+9KN7H3vssX4/+tGPBsyYMePthmMHDhzw5z//+ZMXLFiwZdOmTRtqa2t16623lh04cMBXXnnliEWLFm1ev379Szt37uzaUOdf/uVfyqdNm7Z33bp1L/3mN7/Z+I1vfGP43r172zWPVdqejQEAAAAAALS7iBdzfcr+/fvXrV+/fsPPf/7zvsuWLes7e/bsU2+44YbXr7rqql2p5bp27Rqf+tSn9kjS5MmT31m6dGk/SVq1alWfX/ziF5slae7cubu++c1vDm98jqVLl/Z7+eWXey1atGigJO3bt6/Lhg0beowZM+ZwarlDhw6VjBkzZpwkTZ06dd/VV1/9liTdcsstQ5988skBkrRjx46uVVVVPYYNG/ZOly5dNGfOnN2tvcYlS5b0++QnP7m7vLy8VpKGDh161COVe/bsKVm1alWfWbNmndqw7/Dhw26uvcsvv/xPd9xxx9B9+/Z1ueOOO1771re+VS5Ja9as6TF8+PBDH/zgBw9J0pw5c3bdfffdx02fPn3f8OHDD02cOPFQUn/XvffeWyZJTz/9dL8lS5YMuPPOO4cl18CbN29uNmF4LEiKAQAAAAAANKG0tFTnn3/+vvPPP3/fBz/4wYMPPvjg4MZJsdLS0igpKflz+dra2maTRo1FhG+//fY/XnLJJXtbKtcwp1jqvsWLF/ddsWJF35UrV77ct2/fuilTppx28ODBEknq1q1bXWlp5imfI0eOqG/fvrWNz92cadOmHfjCF77Qs2fPnnUNCbBjFRFauHDh5oqKiqPaeeONN7o2V6eteHwSAAAAAACgkTVr1nRft25d94btVatW9Rw+fPjhluqkmjRp0v77779/oCTdd999g5oqc+655+75/ve/X3bo0CFL0tq1a7un+4jg22+/3aV///5H+vbtW7dq1aoea9as6d1c2d69ex/Zs2fP+9o977zz9v7Xf/3XwIbHLt98882jfpFy0KBBdcOHDz983333DZSkuro6Pfvssz1biuumm256/aabbtqWuq+iouLdbdu2dVu/fn13SXrggQcGn3nmmfsmTZr07rZt27pVVVV1l+on52+oM23atL2333770Lq6OknSM8880+J5jwV3igEAAAAAADSyd+/eLlddddWJe/fu7dKlS5cYMWLEofnz57+abv277rrrtcsvv/zkW2+9tfzss8/e26dPn/f92uNXvvKVt7Zu3dp94sSJYyPCgwYNeu+pp57akk77l1xyyZ558+aVnXLKKeNPOeWUdysqKt5pruzs2bPfmjFjxuihQ4ceTp1XrLKy8t2vfvWr288888wxJSUlMWHChAM/+clPtqbWfeSRR1753Oc+d9Itt9xSXltb64suuuhPZ5xxRrO/KnnppZe+7663Xr16xQ9+8IOts2bNOvXIkSOqqKg48LWvfa2mZ8+ecdddd716/vnnj+zZs2fd1KlT9+/fv7+LJN18881vXHHFFSeOGTNmXF1dnU844YRDy5cv35zOtUmXI6I92wMAAAAAAMjYmjVrtlZUVLyV7ziO1b59+0p69+5dV1JSonnz5g1csGDBoGXLlqWV8MKxWbNmzZCKiooR6ZbnTjEAAAAAAIB29swzz/S6+uqrT4wI9evX78j999+/Nd8x4WgkxQAAAAAAANrZjBkz9m/cuDGtCeqRH0y0DwAAAAAAClFdXV1d2r/kiOKWvFfq2lKHpBgAAAAAAChE62tqavqTGENr6urqXFNT01/S+rbU4/FJAAAAAABQcGpra+fu2LHj3h07dkwQN/WgZXWS1tfW1s5tSyV+fRIAAAAAAABFh0wrAAAAAAAAig5JMQAAAAAAABQdkmIAAAAAAAAoOiTFAAAAAAAAUHRIigEAAAAAAKDo/H/QuErv6py/3wAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] @@ -491,7 +516,7 @@ "source": [ "new_solver = pybamm.ScipySolver()\n", "new_solution = new_solver.solve(model, t_eval)\n", - "pybamm.QuickPlot(model, mesh, new_solution).plot(0)" + "pybamm.QuickPlot(new_solution).plot(0)" ] }, { diff --git a/examples/notebooks/compare-comsol-discharge-curve.ipynb b/examples/notebooks/compare-comsol-discharge-curve.ipynb index 81dec1294e..6c9e882bb0 100644 --- a/examples/notebooks/compare-comsol-discharge-curve.ipynb +++ b/examples/notebooks/compare-comsol-discharge-curve.ipynb @@ -62,18 +62,7 @@ "cell_type": "code", "execution_count": 3, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# load model and geometry\n", "model = pybamm.lithium_ion.DFN()\n", @@ -93,7 +82,7 @@ "\n", "# discretise model\n", "disc = pybamm.Discretisation(mesh, model.default_spatial_methods)\n", - "disc.process_model(model)" + "disc.process_model(model);" ] }, { @@ -108,9 +97,26 @@ "execution_count": 4, "metadata": {}, "outputs": [ + { + "ename": "KeyError", + "evalue": "'Discharge capacity [A.h]'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/solvers/solution.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 180\u001b[0m \u001b[0;31m# return it if it exists\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 181\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_variables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 182\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyError\u001b[0m: 'Discharge capacity [A.h]'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0;31m# discharge capacity\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 41\u001b[0;31m \u001b[0mdischarge_capacity\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolution\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"Discharge capacity [A.h]\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 42\u001b[0m \u001b[0mdischarge_capacity_sol\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdischarge_capacity\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msolution\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 43\u001b[0m \u001b[0mcomsol_discharge_capacity\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcomsol_time\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mparam\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"Current function [A]\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m3600\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/solvers/solution.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 182\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 183\u001b[0m \u001b[0;31m# otherwise create it, save it and then return it\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 184\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 185\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_variables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/solvers/solution.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, variables)\u001b[0m\n\u001b[1;32m 149\u001b[0m \u001b[0mpybamm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Post-processing {}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 150\u001b[0m var = pybamm.ProcessedVariable(\n\u001b[0;32m--> 151\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvariables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mknown_evals\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 152\u001b[0m )\n\u001b[1;32m 153\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyError\u001b[0m: 'Discharge capacity [A.h]'" + ] + }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -148,7 +154,7 @@ " comsol_voltage = comsol_variables[\"voltage\"]\n", "\n", " # update current density\n", - " param[\"Typical current [A]\"] = 24 * C_rate\n", + " param[\"Current function [A]\"] = 24 * C_rate\n", " param.update_model(model, disc)\n", "\n", " # discharge timescale\n", @@ -157,21 +163,17 @@ " ).evaluate(0, 0)\n", "\n", " # solve model at comsol times\n", - " solver = model.default_solver\n", + " solver = pybamm.CasadiSolver(mode=\"fast\")\n", " t = comsol_time / tau\n", " solution = solver.solve(model, t)\n", "\n", " # discharge capacity\n", - " discharge_capacity = pybamm.ProcessedVariable(\n", - " model.variables[\"Discharge capacity [A.h]\"], solution.t, solution.y, mesh=mesh\n", - " )\n", + " discharge_capacity = solution[\"Discharge capacity [A.h]\"]\n", " discharge_capacity_sol = discharge_capacity(solution.t)\n", - " comsol_discharge_capacity = comsol_time * param[\"Typical current [A]\"] / 3600\n", + " comsol_discharge_capacity = comsol_time * param[\"Current function [A]\"] / 3600\n", "\n", " # extract the voltage\n", - " voltage = pybamm.ProcessedVariable(\n", - " model.variables[\"Terminal voltage [V]\"], solution.t, solution.y, mesh=mesh\n", - " )\n", + " voltage = solution[\"Terminal voltage [V]\"]\n", " voltage_sol = voltage(solution.t)\n", "\n", " # calculate the difference between the two solution methods\n", @@ -225,7 +227,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/examples/notebooks/create-model.ipynb b/examples/notebooks/create-model.ipynb index 4ebf8c56d3..0ae441220b 100644 --- a/examples/notebooks/create-model.ipynb +++ b/examples/notebooks/create-model.ipynb @@ -576,12 +576,8 @@ "solution = solver.solve(model, t)\n", "\n", "# Extract output variables\n", - "L_out = pybamm.ProcessedVariable(\n", - " model.variables[\"SEI thickness\"], solution.t, solution.y, mesh\n", - ")\n", - "c_out = pybamm.ProcessedVariable(\n", - " model.variables[\"Solvent concentration\"], solution.t, solution.y, mesh\n", - ")\n", + "L_out = solution[\"SEI thickness\"]\n", + "c_out = solution[\"Solvent concentration\"]\n", "x = np.linspace(0, 1, 100)" ] }, @@ -600,7 +596,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "521ab96b2c074ac0a709cb3ae87b051e", + "model_id": "0f7f4e8850354ea3a745351e85077a75", "version_major": 2, "version_minor": 0 }, diff --git a/examples/notebooks/expression_tree/broadcasts.ipynb b/examples/notebooks/expression_tree/broadcasts.ipynb new file mode 100644 index 0000000000..41580784c6 --- /dev/null +++ b/examples/notebooks/expression_tree/broadcasts.ipynb @@ -0,0 +1,269 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Broadcasts\n", + "\n", + "This notebook explains the different types of broadcast available in PyBaMM.\n", + "Understanding of the [expression_tree](./expression-tree.ipynb) and [discretisation](../spatial_methods/finite-volumes.ipynb) notebooks is assumed." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pybamm\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We also explicitly set up the discretisation that is used for this notebook. We use a small number of points in each domain, in order to easily visualise the results." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "var = pybamm.standard_spatial_vars\n", + "geometry = {\n", + " \"negative electrode\": {\"primary\": {var.x_n: {\"min\": pybamm.Scalar(0), \"max\": pybamm.Scalar(1)}}},\n", + " \"negative particle\": {\"primary\": {var.r_n: {\"min\": pybamm.Scalar(0), \"max\": pybamm.Scalar(1)}}},\n", + "\n", + "}\n", + "\n", + "submesh_types = {\n", + " \"negative electrode\": pybamm.Uniform1DSubMesh,\n", + " \"negative particle\": pybamm.Uniform1DSubMesh,\n", + "}\n", + "\n", + "var_pts = {var.x_n: 5, var.r_n: 3}\n", + "mesh = pybamm.Mesh(geometry, submesh_types, var_pts)\n", + "\n", + "spatial_methods = {\n", + " \"negative electrode\": pybamm.FiniteVolume(),\n", + " \"negative particle\": pybamm.FiniteVolume(),\n", + "}\n", + "disc = pybamm.Discretisation(mesh, spatial_methods)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Primary broadcasts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Primary broadcasts are used to broadcast from a \"larger\" scale to a \"smaller\" scale, for example broadcasting temperature T(x) from the electrode to the particles, or broadcasting current collector current i(y, z) from the current collector to the electrodes.\n", + "To demonstrate this, we first create a variable `T` on the negative electrode domain, discretise it, and evaluate it with a simple linear vector" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0. ],\n", + " [0.25],\n", + " [0.5 ],\n", + " [0.75],\n", + " [1. ]])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "T = pybamm.Variable(\"T\", domain=\"negative electrode\")\n", + "disc.set_variable_slices([T])\n", + "disc_T = disc.process_symbol(T)\n", + "disc_T.evaluate(y=np.linspace(0,1,5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then broadcast `T` onto the \"negative particle\" domain (using primary broadcast as we are going from the larger electrode scale to the smaller particle scale), and discretise and evaluate the resulting object." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0. ],\n", + " [0. ],\n", + " [0. ],\n", + " [0.25],\n", + " [0.25],\n", + " [0.25],\n", + " [0.5 ],\n", + " [0.5 ],\n", + " [0.5 ],\n", + " [0.75],\n", + " [0.75],\n", + " [0.75],\n", + " [1. ],\n", + " [1. ],\n", + " [1. ]])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "primary_broad_T = pybamm.PrimaryBroadcast(T, \"negative particle\")\n", + "disc_T = disc.process_symbol(primary_broad_T)\n", + "disc_T.evaluate(y=np.linspace(0,1,5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The broadcasted object makes 3 (since the r-grid has 3 points) copies of each element of `T` and stacks them all up to give an object with size 3x5=15. In the resulting vector, the first 3 entries correspond to the 3 points in the r-domain at the first x-grid point (where T=0 uniformly in r), the next 3 entries correspond to the next 3 points in the r-domain at the second x-grid point (where T=0.25 uniformly in r), etc" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Secondary broadcasts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Secondary broadcasts are used to broadcast from a \"smaller\" scale to a \"larger\" scale, for example broadcasting SPM particle concentrations c_s(r) from the particles to the electrodes. Note that this wouldn't be used to broadcast particle concentrations in the DFN, since these already depend on both x and r.\n", + "To demonstrate this, we first create a variable `c_s` on the negative particle domain, discretise it, and evaluate it with a simple linear vector" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0. ],\n", + " [0.5],\n", + " [1. ]])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c_s = pybamm.Variable(\"c_s\", domain=\"negative particle\")\n", + "disc.set_variable_slices([c_s])\n", + "disc_c_s = disc.process_symbol(c_s)\n", + "disc_c_s.evaluate(y=np.linspace(0,1,3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then broadcast `c_s` onto the \"negative electrode\" domain (using secondary broadcast as we are going from the smaller particle scale to the large electrode scale), and discretise and evaluate the resulting object." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0. ],\n", + " [0.5],\n", + " [1. ],\n", + " [0. ],\n", + " [0.5],\n", + " [1. ],\n", + " [0. ],\n", + " [0.5],\n", + " [1. ],\n", + " [0. ],\n", + " [0.5],\n", + " [1. ],\n", + " [0. ],\n", + " [0.5],\n", + " [1. ]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "secondary_broad_c_s = pybamm.SecondaryBroadcast(c_s, \"negative electrode\")\n", + "disc_broad_c_s = disc.process_symbol(secondary_broad_c_s)\n", + "disc_broad_c_s.evaluate(y=np.linspace(0,1,3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The broadcasted object makes 5 (since the x-grid has 5 points) identical copies of the whole variable `c_s` to give an object with size 5x3=15. In the resulting vector, the first 3 entries correspond to the 3 points in the r-domain at the first x-grid point (where c_s varies in r), the next 3 entries correspond to the next 3 points in the r-domain at the second x-grid point (where c_s varies in r), etc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/notebooks/models/DFN.ipynb b/examples/notebooks/models/DFN.ipynb index eac6beef92..e59424b1d5 100644 --- a/examples/notebooks/models/DFN.ipynb +++ b/examples/notebooks/models/DFN.ipynb @@ -219,7 +219,7 @@ } ], "source": [ - "quick_plot = pybamm.QuickPlot(model, mesh, solution)\n", + "quick_plot = pybamm.QuickPlot(solution)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=solution.t[-1],step=0.05,value=0));" diff --git a/examples/notebooks/models/SPM.ipynb b/examples/notebooks/models/SPM.ipynb index 4f61b697ee..38adbca976 100644 --- a/examples/notebooks/models/SPM.ipynb +++ b/examples/notebooks/models/SPM.ipynb @@ -96,7 +96,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "rhs equation for variable ' X-averaged negative particle concentration ' is:\n" + "rhs equation for variable ' Discharge capacity [A.h] ' is:\n" ] } ], @@ -309,7 +309,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 11, @@ -364,9 +364,9 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/scott/Projects/PyBaMM/venv/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:146: RuntimeWarning: invalid value encountered in greater_equal\n", + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:146: RuntimeWarning: invalid value encountered in greater_equal\n", " up = (g <= 0) & (g_new >= 0)\n", - "/home/scott/Projects/PyBaMM/venv/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:147: RuntimeWarning: invalid value encountered in less_equal\n", + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:147: RuntimeWarning: invalid value encountered in less_equal\n", " down = (g >= 0) & (g_new <= 0)\n" ] } @@ -398,14 +398,10 @@ "output_type": "stream", "text": [ "SPM model variables:\n", - "\t- Total current density\n", - "\t- Total current density [A.m-2]\n", - "\t- Current [A]\n", "\t- Time\n", "\t- Time [s]\n", "\t- Time [min]\n", "\t- Time [h]\n", - "\t- Discharge capacity [A.h]\n", "\t- x\n", "\t- x [m]\n", "\t- x_n\n", @@ -426,6 +422,11 @@ "\t- r_n [m]\n", "\t- r_p\n", "\t- r_p [m]\n", + "\t- Total current density\n", + "\t- Total current density [A.m-2]\n", + "\t- Current [A]\n", + "\t- C-rate\n", + "\t- Discharge capacity [A.h]\n", "\t- Porosity\n", "\t- Negative electrode porosity\n", "\t- Separator porosity\n", @@ -433,12 +434,27 @@ "\t- X-averaged negative electrode porosity\n", "\t- X-averaged separator porosity\n", "\t- X-averaged positive electrode porosity\n", + "\t- Active material volume fraction\n", + "\t- Negative electrode active material volume fraction\n", + "\t- Separator active material volume fraction\n", + "\t- Positive electrode active material volume fraction\n", + "\t- X-averaged negative electrode active material volume fraction\n", + "\t- X-averaged separator active material volume fraction\n", + "\t- X-averaged positive electrode active material volume fraction\n", + "\t- Leading-order porosity\n", "\t- Leading-order negative electrode porosity\n", "\t- Leading-order separator porosity\n", "\t- Leading-order positive electrode porosity\n", "\t- Leading-order x-averaged negative electrode porosity\n", "\t- Leading-order x-averaged separator porosity\n", "\t- Leading-order x-averaged positive electrode porosity\n", + "\t- Leading-order active material volume fraction\n", + "\t- Leading-order negative electrode active material volume fraction\n", + "\t- Leading-order separator active material volume fraction\n", + "\t- Leading-order positive electrode active material volume fraction\n", + "\t- Leading-order x-averaged negative electrode active material volume fraction\n", + "\t- Leading-order x-averaged separator active material volume fraction\n", + "\t- Leading-order x-averaged positive electrode active material volume fraction\n", "\t- Porosity change\n", "\t- Negative electrode porosity change\n", "\t- Separator porosity change\n", @@ -516,6 +532,34 @@ "\t- Volume-averaged cell temperature [K]\n", "\t- Heat flux\n", "\t- Heat flux [W.m-2]\n", + "\t- Electrolyte tortuosity\n", + "\t- Negative electrolyte tortuosity\n", + "\t- Positive electrolyte tortuosity\n", + "\t- X-averaged negative electrolyte tortuosity\n", + "\t- X-averaged positive electrolyte tortuosity\n", + "\t- Separator tortuosity\n", + "\t- X-averaged separator tortuosity\n", + "\t- Electrode tortuosity\n", + "\t- Negative electrode tortuosity\n", + "\t- Positive electrode tortuosity\n", + "\t- X-averaged negative electrode tortuosity\n", + "\t- X-averaged positive electrode tortuosity\n", + "\t- Negative particle flux\n", + "\t- X-averaged negative particle flux\n", + "\t- Positive particle flux\n", + "\t- X-averaged positive particle flux\n", + "\t- Ohmic heating\n", + "\t- Ohmic heating [W.m-3]\n", + "\t- Irreversible electrochemical heating\n", + "\t- Irreversible electrochemical heating [W.m-3]\n", + "\t- Reversible heating\n", + "\t- Reversible heating [W.m-3]\n", + "\t- Total heating\n", + "\t- Total heating [W.m-3]\n", + "\t- X-averaged total heating\n", + "\t- X-averaged total heating [W.m-3]\n", + "\t- Volume-averaged total heating\n", + "\t- Volume-averaged total heating [W.m-3]\n", "\t- Negative current collector potential\n", "\t- Negative current collector potential [V]\n", "\t- Current collector current density\n", @@ -555,7 +599,13 @@ "\t- Positive electrode interfacial current density [A.m-2]\n", "\t- X-averaged positive electrode interfacial current density [A.m-2]\n", "\t- Positive electrode interfacial current density per volume [A.m-3]\n", - "\t- X-averaged positive electrode interfacial current density per volume [A.m-3]\n", + "\t- X-averaged positive electrode interfacial current density per volume [A.m-3]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\t- X-averaged positive electrode total interfacial current density\n", "\t- X-averaged positive electrode total interfacial current density [A.m-2]\n", "\t- X-averaged positive electrode total interfacial current density per volume [A.m-3]\n", @@ -585,10 +635,6 @@ "\t- Exchange current density\n", "\t- Exchange current density [A.m-2]\n", "\t- Exchange current density per volume [A.m-3]\n", - "\t- Negative particle flux\n", - "\t- X-averaged negative particle flux\n", - "\t- Positive particle flux\n", - "\t- X-averaged positive particle flux\n", "\t- Negative electrode potential\n", "\t- Negative electrode potential [V]\n", "\t- X-averaged negative electrode potential\n", @@ -645,21 +691,11 @@ "\t- Positive electrode current density [A.m-2]\n", "\t- Electrode current density\n", "\t- Positive current collector potential\n", - "\t- Ohmic heating\n", - "\t- Ohmic heating [W.m-3]\n", - "\t- Irreversible electrochemical heating\n", - "\t- Irreversible electrochemical heating [W.m-3]\n", - "\t- Reversible heating\n", - "\t- Reversible heating [W.m-3]\n", - "\t- Total heating\n", - "\t- Total heating [W.m-3]\n", - "\t- X-averaged total heating\n", - "\t- X-averaged total heating [W.m-3]\n", - "\t- Volume-averaged total heating\n", - "\t- Volume-averaged total heating [W.m-3]\n", "\t- Positive current collector potential [V]\n", - "\t- Local current collector potential difference\n", - "\t- Local current collector potential difference [V]\n", + "\t- Local voltage\n", + "\t- Local voltage [V]\n", + "\t- Terminal voltage\n", + "\t- Terminal voltage [V]\n", "\t- X-averaged open circuit voltage\n", "\t- Measured open circuit voltage\n", "\t- X-averaged open circuit voltage [V]\n", @@ -668,15 +704,14 @@ "\t- X-averaged reaction overpotential [V]\n", "\t- X-averaged solid phase ohmic losses\n", "\t- X-averaged solid phase ohmic losses [V]\n", - "\t- Terminal voltage\n", - "\t- Terminal voltage [V]\n", "\t- X-averaged battery open circuit voltage [V]\n", "\t- Measured battery open circuit voltage [V]\n", "\t- X-averaged battery reaction overpotential [V]\n", "\t- X-averaged battery solid phase ohmic losses [V]\n", "\t- X-averaged battery electrolyte ohmic losses [V]\n", "\t- X-averaged battery concentration overpotential [V]\n", - "\t- Battery voltage [V]\n" + "\t- Battery voltage [V]\n", + "\t- Terminal power [W]\n" ] } ], @@ -690,7 +725,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To help visualise the results, pybamm provides the `pybamm.ProcessedVariable` class, which takes the output of a solver and a variable, and allows the user to evaluate the value of that variable at any given time or $x$ value. For example, we can create a `pybamm.ProcessedVariable` using the SPM voltage variable, and plot the voltage versus time, along with the surface particle concentration." + "To help visualise the results, pybamm provides the `pybamm.ProcessedVariable` class, which takes the output of a solver and a variable, and allows the user to evaluate the value of that variable at any given time or $x$ value. These processed variables are automatically created by the solution dictionary." ] }, { @@ -699,13 +734,9 @@ "metadata": {}, "outputs": [], "source": [ - "voltage = pybamm.ProcessedVariable(model.variables['Terminal voltage [V]'], solution.t, solution.y, mesh=mesh)#\n", - "c_s_n_surf = pybamm.ProcessedVariable(\n", - " model.variables['Negative particle surface concentration'], solution.t, solution.y, mesh=mesh\n", - ")\n", - "c_s_p_surf = pybamm.ProcessedVariable(\n", - " model.variables['Positive particle surface concentration'], solution.t, solution.y, mesh=mesh\n", - ")" + "voltage = solution['Terminal voltage [V]']\n", + "c_s_n_surf = solution['Negative particle surface concentration']\n", + "c_s_p_surf = solution['Positive particle surface concentration']" ] }, { @@ -722,7 +753,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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ws/kGGBqtf2l6lj0ItZmfiOxb5LmxW17cZBvEbHmGHU6FvXJAfwI2F7td6Nb8NqrwJwgy2NAUQpzlAfvNSu4vyRun52Cz3BR8Jnmp/Az2Z/YP8BG1fZZ62ORCbd6/h0lpWyrawaysFxLwmdyDFlUttU1A1lYriXhM9mfBlK21fNvSOdsOmfTOVsDPGezYEMRERERERGRUGRSH1MRERERERHJQqqYioiIiIiISKhUMRUREREREZFQqWIqIiIiIiIioVLFVEREREREREKliqmIiIiIiIiEShVTERERERERCZUqppIxzKytmZ0Tdg4RkWRS2SYi2UblmtSFKqaSSb4D9Ag7hIhIkqlsE5Fso3JNas3cPewMIjUys28AfwE+B9YD/dx9SbipRETqR2WbiGQblWtSV6qYSsYws5eAn7n73LCziIgki8o2Eck2KtekLtSUVzLJwcD8sEOIiCSZyjYRyTYq16TWVDGVjGBmpcA6d68MO4uISLKobBORbKNyTepKFVPJFPsDH4cdQkQkyfZHZZuIZJf9UbkmdaCKqWSK+UCpmc01s/Kww4iIJInKNhHJNirXpE40+JGIiIiIiIiESldMRUREREREJFSqmIqIiIiIiEioVDEVERERERGRUKliKiIiIiIiIqFSxVRERERERERCpYqpiIiIiIiIhEoVUxEREREREQmVKqYiIiIiIiISqv8H+0hmrwpaz9oAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] @@ -768,7 +799,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0ff3a1563e0f45bdbe67f6838ecaab48", + "model_id": "589cbb6815244b4487846913c0baf528", "version_major": 2, "version_minor": 0 }, @@ -781,8 +812,8 @@ } ], "source": [ - "c_s_n = pybamm.ProcessedVariable(model.variables['Negative particle concentration'], solution.t, solution.y, mesh=mesh)\n", - "c_s_p = pybamm.ProcessedVariable(model.variables['Positive particle concentration'], solution.t, solution.y, mesh=mesh)\n", + "c_s_n = solution['Negative particle concentration']\n", + "c_s_p = solution['Positive particle concentration']\n", "r = np.linspace(0,1,100)\n", "\n", "def plot_concentrations(t):\n", @@ -816,12 +847,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ba068df354c2496d95915645646fbb20", + "model_id": "e19993690e8d4a5b88a9876f38a82491", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=1.0850295540089985, step=0.05), Output()), _…" + "interactive(children=(FloatSlider(value=0.0, description='t', max=1.0840475344777656, step=0.05), Output()), _…" ] }, "metadata": {}, @@ -829,7 +860,7 @@ } ], "source": [ - "quick_plot = pybamm.QuickPlot(model, mesh, solution)\n", + "quick_plot = pybamm.QuickPlot(solution)\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=quick_plot.max_t,step=0.05,value=0));" ] }, diff --git a/examples/notebooks/models/SPMe.ipynb b/examples/notebooks/models/SPMe.ipynb index 09fefdc450..e3a23b62d1 100644 --- a/examples/notebooks/models/SPMe.ipynb +++ b/examples/notebooks/models/SPMe.ipynb @@ -217,7 +217,7 @@ } ], "source": [ - "quick_plot = pybamm.QuickPlot(model, mesh, solution)\n", + "quick_plot = pybamm.QuickPlot(solution)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=solution.t[-1],step=0.05,value=0));" @@ -273,7 +273,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/examples/notebooks/models/compare-lithium-ion.ipynb b/examples/notebooks/models/compare-lithium-ion.ipynb index 076b1eb468..34f506f792 100644 --- a/examples/notebooks/models/compare-lithium-ion.ipynb +++ b/examples/notebooks/models/compare-lithium-ion.ipynb @@ -104,7 +104,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -250,9 +250,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "Solved the Single Particle Model with electrolyte in 0.676 seconds\n", - "Solved the Doyle-Fuller-Newman model in 27.964 seconds\n", - "Solved the Single Particle Model in 0.320 seconds\n" + "Solved the Doyle-Fuller-Newman model in 2.032 seconds\n", + "Solved the Single Particle Model in 0.121 seconds\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:146: RuntimeWarning: invalid value encountered in greater_equal\n", + " up = (g <= 0) & (g_new >= 0)\n", + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:147: RuntimeWarning: invalid value encountered in less_equal\n", + " down = (g >= 0) & (g_new <= 0)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Solved the Single Particle Model with electrolyte in 0.243 seconds\n" ] } ], @@ -279,7 +295,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To plot results, we create a `ProcessedVariable` which can be evaluated at any time. Matplotlib can then be used to plot the voltage predictions of each models as follows:" + "To plot results, we extract the variables from the solutions dictionary. Matplotlib can then be used to plot the voltage predictions of each models as follows:" ] }, { @@ -289,7 +305,7 @@ "outputs": [ { "data": { - "image/png": "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pUuX0qtXL5ydnXFzc6NHjx48/fTTletjY2MRQrB2ba3G8KyzBx54gN696zz99BW2bduGEKLy5ebmxsCBA9m8eXMD1FLzn//8h23btl2xXAjBggULan2cV199lZYtW15zfSp+NkIIdu3adcX6N954AyEEAQEB11wWwPz58+t8LHN9fgICApg/f37l+/DwcJYuXXrFdkOHDmXq1KkNWnYFc53b/v37efXVV2u/gxDg0gYAXWEank5as+G03Ev/Vm+tb813X35HsaG41oeu7jNvDjVdoZxBGxql4vuqpv09fdE6pRG89dZbPPjgg4wePZoff/yRr7/+mttvv501ay4Me926dWv27NnDwIEDG7Gmtbd8+XL27NnDt99+i729PWPGjOHw4cNX37EWqvvPtWfPHqZNm9YgZdSHXq8nLCzsiuVhYWHo9foq9mj+Vq9ezd///vfK99UFSnO0f/9+XnvttbrtZKfXZnyUJjw5j04IcovLKCq9chyvIkNRrQ9ryUCp6aH8WLTBGyu+V5qgBQsW8PDDD/Pvf/+7ctmECRP417/+Vfnezs6OW265pTGqVy9du3alSxdtNNYhQ4bg5+fHF198waefflrvYxYVFdXYoq+x/30mTJjAypUr+fjjj7Gy0v46PXbsGJGRkdx5553s2bOnUetnDj169Lj6Rtc5KSUlJSXY29trC1zaQHouusJMvBxdSSmQpOaWENDywq9qIQQGk4EyUxk2OptGqnnVqr1CkVJuqBhFuPz7Gl+Wq7JysezsbFq1urIV38UtQKq6rK+43fDhhx/i6+uLm5sb06dPJzv70hbhR48epX///tjb29O5c2d+/fVXevfuzQMPPFBjveLj45k+fTru7u44OjoyevRoTp262oDSV9Lr9QQFBREbGwtAcnIys2fPpn379jg4OBAUFMTLL79MaemF+bcrznf58uXcf//9tGjRggkTJhAQEEBmZiavvfZa5W2mir/cqrrltXr1avr27YuDgwMeHh6MGzeOuLgr56+okJWVxdy5c/H29sbe3p7+/fuzb1/txj6dOHEieXl5bN26tXJZWFgYAwcOxMfnyjFQz549y6RJk3BxccHZ2ZkJEyZw5syZS7bJzs5mxowZ6PV6WrduzZtvvlll2Q3xsxoyZAhz586tfL9hwwaEEJfcel21ahW2trYUFmr3/y++5fXAAw+watUq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4+l2a1pERUVnG85D40WuzZB/YMiBItV/7733iI2NJSEhAaUUd911F3379mXUqFGFKu95eHiwevVqqwTs4cOHWb58Of/6178AWLBgAZcuXWLfvn3Wp+SQkBAiIiIK7HfWrFlcv36d/fv3W6Vq27ZtS926dfnPf/7D008/XWD7rKwsTCYTly9f5vXXX+fs2bNWieLGjRszc+ZMa922bdvi6+vLsGHDeOedd2zEvFq1amWXft/d3Z1atWrRqlWrfPefnZ3N2LFj6dOnj80f+F69euXbZsmSJRw8eJBDhw5Rv359ADp37kxoaChvvfWWVXmxIOLi4hg+fDhz5sxh//79nDp1igcffJA333zTpt6CBQs4deoUx48fp169eoBZYbNevXrMmzePcePGcejQIVavXs3y5cvp378/APfddx+1a9e2uS9u9Vppyg4Goy9kXsPddAN/z+o2sxSlZkOp71FTojRp0oQjR46wdu1annrqKUSE1157jfDwcH7//fcC29533302euKNGjXi/PnzZGZmArB3716aN29uM+XSsmXLQmMNmzdvJjo6moCAAEwmEyaTCX9/f5o3b05+Yme5ueee3vQ/9QAADutJREFUe/Dw8KBatWrMnz+fadOmWeMIIsLbb79No0aN8Pb2xsPDg4EDB5KRkWE3crr//vsL3Zcjjh07xpkzZxg6dKjTbTZv3kzz5s254447rMcM0KFDB6eOGaBHjx5kZWXx2WefsXz5cqKiohyuJNuzZw/NmjWzOhOAWrVq0bZtW+tKsb179wLQu3dvax0/Pz8bjfYcu2/lWmnKDm6WwLwXGWSYSv+lRtAjFDuKOkIoCxiNRnr27EnPnj0BmD9/Po888gjz58/n2Wefzbdd3iC9p6cnIkJGRgYeHh6cO3eOKlWq2LVzVJabixcvsnv3bj766CO7bVFRdjpqdixfvpyQkBACAwOpU6eOjdN7++23efHFFxkzZgwdOnQgMDCQvXv38vTTT5OebruqpbhB9pypw+DgYKfb5Byzh4eH3baQkBCn+jAajcTGxrJs2TJ27Nhh1ZjPy9mzZx0eW7Vq1awxnnPnzuHv74+Xl5dNnapVqzq0u7jXSlOG8MwJzN/k6k0TXh5lL329phwyfPhwRo8ezdGjR2+pn+rVqzsMzuYXR8ghKCiIXr168fLLL9ttc0avPCwsjLvvvtvhthUr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\n", + "image/png": "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/cBIH8888ww33HADZ511Fu+99x5nn302N9xwA9OnTw8cs3LlSsaNG0fPnj1ZsGABp556KmPGjCl3/wcffJBrr72Wc845hw8++IDLL7+cO+64g2effTao+BuriOLOjY0ooQQ72nA8cC9wFnB4JecFVaYiIm8Ag4BkEdmC1XLrIWCeiEwENuKfo15EMoArVfUyVd0rItOAVf5L3auqZSv36yz99g9CfckayXzo9JBdKzo6muTkZLKysgD4+OOP+eabb1i2bBknnXQSAKeccgrp6ek8+uijzJw5ky5dutC/f39eeuklBg0aBMBff/3FV199xaJFiyq8j6pyyy23cPHFF/Of//wnsD0qKoprrrmGO+64g5YtW1YZ6+OPP87jjz8eeN+6dWu2bNlSl49fzowZM8jPz2fNmjW0aNECgP79+5Oens6LL77INddcEzi2X79+PP300zW6/h133MErr7zCnDlzuOSS8q3aP/nkExYvXszXX39N//7WLNrDhg1j3bp1PPDAA7zxxhuccMIJ2Gw2vv76a0aPHs1XX33FmWeeydKlS1m+fDlDhgzhq6++olevXsTGHmitv3fvXr799lvS09MB+N///seMGTOYM2cOF1xwAQAej4dRo0axfv16OnbsCMD//d//Ba7h9XoZOnQoKSkpvPfee4HzAPLy8vjggw9o1syaHXDbtm3ceuutFBUVERkZWeW/y2233caFF15YKoaTTz6ZadOmAZCRkcH8+fN5//33+cc//oHH4+Gee+5h4sSJPProo4F/p3379nH//fdz3XXXERkZyUMPPUS3bt2YO3cuIsKIESMoLCxk6tSpgXtnZ2czbdo0pkyZwuTJkwEYMmQIeXl5TJs2jSuuuKLJ9kU70Fse8PnAFv7ZSIKN4CVgItaTxCTg6gqWoKjqOFVNU9UIVW2jqi+o6h5VHayqHVV1SHGiUNXVqnpZiXNfVNWj/EvVZQsGQKmiq5UrV9KqVatAMgGIi4vjjDPO4Ouvvw5smzhxIvPnzycvLw+wirtSU1MZMaLiRnW///47mzZt4vzzzy/1lHHKKadQWFjIzz9bXY6qegK56KKLWLVqVWD58MMPQ/ZvUGzJkiUMHTqUxMTEQBwJCQn06tWLsgOInn566cTu8/lKxV/RoKqdOnXivPPO48EHH8TnK/+tccmSJbRp04a+ffuWutaQIUMC909MTOS4447jq6++AqynkRNPPJGBAweW2jZw4MBS1+7QoUMgmcCBBg2nnHJKuW1btx4oKf7vf//LkCFDaNmyJQ6Hg7i4OAoKCsoVHfXt2zeQTAC6du2KqlZb7AUwePDgKuNKSkqiZcuWgbg2bdpEVlYW551XerSnMWPGkJ2dHSi2W7lyJWeeeWaphHD22WeXOuebb77B6XRy3nnnlfo3Hzx4MNu2bQsq/saquHOjRwT1No6WXsG2aRwGXKOqNWuL1wSF8gkh3AoLC9mzZw+pqVajue3bt9OqVatyx6WmprJ374GHvfPPP5/rr7+eefPmMWHCBF5++WUuvvjiSpvAFhfPnHbaaRXu37x5M5mZmRx55JGBbe3atSMzM7NUDPXdxHf37t2sWLGCN998s9y+kn/0iuMp6V//+hf3339/4P20adO4667yM1/feeed9OjRg7fffrvC+2/ZsoWIiIhy+0pW5Bcnj8LCQlatWsV//vMf3G43ixYtYv/+/fz000+Bb/fFmjdvXup98VNDye3F2woLCwGrmGz48OGccMIJzJo1i7S0NCIjIxk+fHjgmOquX/a4ilQUQ0XXK75WcdFX2Z9B8fvi39WsrKxyv89l3xf/bnbu3LnC2DZv3kzr1k2zsahNbDgAD+DxFhIRUW6aqgYXbELZCuyvz0CM0Fu6dCkej4e//e1vgNWSaufO8u0dsrKyAkVAYD21jB07ltmzZ9OuXTs2bdrEhAkTKr1P8bmzZs3i+OOPL7f/yCOPJCEhgVWrVgW21aQlVHR0NEVFpb+B7du3L+jzS8Z55plncvfdd5fbl5CQUOp92WKQq6++mtGjD3SPquyPUPfu3Rk5ciQPPPBAuWKvFi1aBOoNyip5v4EDB/L000/z+eefExcXx7HHHovb7Wby5MksXboUVQ0UmdXFRx99hMvlYuHChYFWbEVFRWRnlxuqr0GlpaUBlPtdLS66Lf59S01NLXdM2ffFx3700UcVNuzo0qVLaIIOE4fY8KgPt8dF+a8pDS/YhHIHMFlEVpfoL2I0YtnZ2dx2220cddRRDBkyBLCKLaZMmRIoRgEoKCjggw8+4Kyzzip1/sSJE+nXrx9Tp06lX79+Vf7H69y5M61btyYzM5N//OMflR5X2yeQNm3akJuby9atWwN/yD/55JMaX2fw4MHMmzePbt261XhQ0MMPP5zDDz88qGPvuusu+vTpw3vvvVfu/k8++SSJiYl06tSp0vMHDhyIx+Ph4YcfZsCAAYgIPXr0wG63M2PGDLp161bqC0BtOZ1O7HZ7qSfPuXPnVlhc15DatWtHamoqb731FkOHDg1snzdvHklJSXTr1g2A3r17s2jRIqZNmxZIyO+8806pa51wwglER0ezffv2Sotsm7IIsVOoPtzextFbPqiEoqrvishAYIOI/E7Fow2fGOrgjOB4PJ5AS67c3Fy+++47nnnmGQoKCvj4448DfVCKizfGjBnDQw89RMuWLZk+fTpOp5Nbbrml1DX79u1Lt27d+Prrr5k5c2aV97fZbDz22GP8/e9/Jycnh1NPPZXIyEj++usvFi5cyNtvv12qArmmRowYQUxMDJdeeik333wzGzZsqFULnZtuuonXXnuNU045hWuvvZbWrVuTlZXFF198wYABAxg3blytYyypd+/eDB06lE8//bRU/59TTz2VwYMHM3ToUG677Ta6du0a6Ijq9Xq57777AKvYpnPnznz55ZeBSmm73c4JJ5zA4sWLueqqq0IS5+DBg7n11luZMGECEyZM4KeffmLGjBkkJiaG5Pq1ZbfbmTJlCtdccw1JSUkMHjyYpUuX8txzz/HII48Eis1uu+02TjjhBMaNG8f48eP58ccfmT17dqlrtWzZkrvvvpt//vOfbNiwgQEDBuDz+Vi3bh1ffvllhU+LTUlx50ZPI+ncGGwrrweAm4CfsHrNN44aIAOwesf/7W9/Q0RITEzkqKOO4qKLLuLaa6/lsMNKjzS9cOFCbr75Zm644QYKCwvp06cPn3/+eYW90kePHs1ff/1VrqlpRcaMGUNiYiIPPPAAL774Ina7nfbt23PGGWdU2wqoOsnJycyfP59JkyYxevRoevXqxeuvv16qv0aw11mxYgWTJ0/mxhtvJDs7m7S0NAYMGED37t3rFGNZd911V6n+P2AVa7377rtMmzaNxx57jM2bN9OyZUt69OjBddddV+rYgQMHsm7dusCTZPG2xYsXM2DAgJDE2KNHD1544QXuvfde5s+fz/HHH8/8+fNLFe2Fy1VXXUVRURFPPvkkM2bMoG3btsyYMYPrr78+cEy/fv14/fXXmTx5MgsXLqRPnz7MnTu3XAfZO++8kzZt2vDEE0/wyCOPEBsbS6dOnUL2BSKcHLYIGlPnxmCnAN4HPKGq99R/SKFV1RTAa9eu5eijQz/b2cGiT58+dO7cmVdffTXcoRiGUYHs/Cy2OneTiHBEcs2+YFXl559/LjjmmGPWVrTvhx9+SD7uuOPSK9oXbB2KC/hvLWMzmpjVq1fz+eefs2rVqhr3xTAMo+EUT7TVWHrLB5tQngIuxRrh1zjI9e7dm+bNm/Pggw/Su3fvcIdjGEYlIkp2blQf1HnQkroJNqFEA/1F5CdgKeUr5VVVK51n2GhagikGNQwj/Bx2q7Gw1bnRjTjCOzBpsAllov81GTivgv2KNYSKYRiG0UBsYsMOeAGPp5CIppBQVDWtvgMxDMMwai4CwYtaCYVm1Z9Qj6otcBORaBFZJCKmn4lhGEYjUzxzo7sRjOdVbUJR1ULgRIIvHjMMwzAaSPEgkW5f+Ds3Btsk4APgjPoMxDAMw6i5CJu/Yt4X/s6NwT51LARmiEgr4EMgizJzzKvq5yGOzTAMw6hGhD0S3ODW0E9KV1PBJpTi8b4v8C9lKUFOsGUYhmGETvFEW42hc2OwRV5HV7OErs+/UWOzZ8+mV69eJCQkkJSUxPHHH89NN90U2F88T/r7779fL/cfP358SOYyWbZsGSISWJKSkhgwYACfffZZCKK0PPLIIyxbtqzc9prOT1/ZPPc1VfyzEZFSk5wVu++++xCRUpNn1cWkSZNqfK36/v0x6uZA50aFMPchCyqhqOq66pb6DtSo2IMPPshll13G8OHDeeedd3jllVcYNWpUqel609LSWL58ecgGFaxvc+bMYfny5bz22mtER0czYsQI1qxZE5JrV5ZQli9fXm6GwIYUHx/P3Llzy22fO3cu8fHxYYjIaCoc/joUt4CGuWI+6H76IuIQkQki8rS/GXEH//azRKRjXYIQkc4isqbEkiMiN5Q5ZpCI7C9xzL/qcs+DxVNPPcUVV1zBAw88wNChQxk5ciRTp05l/fr1gWOioqLo169fuVnyGqvu3bvTr18/Tj/9dBYuXEh8fDzPPfdcna7pdDqr3N+vX79yMwQ2pJEjR/L222+Xmhr5p59+Yu3atZxxhmkPY1TObrNjAxTB6wnvvChBJRQRaQ+sBZ4EjgNOh0APmqHAnXUJwv+U00NVewC9gAJgQQWHflV8nKreW5d7Hiyys7PLDVEPpWcArKjIIj09nUmTJjFjxgzatGlDUlISY8eOLTdb348//hiYpKhbt258+OGHZGRkMH78+Crj2rRpE2PHjqVFixbExsYyfPhw1q2r+YNsfHw8nTp1CkwXvH37di699FLat29PTEwMnTp14q677io1o2Px550zZw4XX3wxzZs3Z+TIkaSnp7Nnzx7uueeeQDFT8dNKRUVeCxYsoE+fPsTExNCyZUtOO+00Nm7cWGmse/fu5fLLLyc1NZXo6GhOOOEEvv3226A+55lnnklubi5Lly4NbJs7dy4DBgyocHbIDRs2MHr0aBITE0lISGDkyJH88ccfpY7Jzs7mggsuID4+nrS0tFJTGJcUqp+VET4RWP/fPZ7qp2SuT8E+oTwJ7AGOBAYBJedHXYbVTyVUBgN/qmrl/3ONgJ49e/Lvf/+bl19+mT179tTo3Hnz5vHZZ58xa9YsHn74Yd5//33uvPPAd4OCggKGDx+O0+nkjTfe4K677uLGG29k06ZNVV537969DBgwgHXr1vHss88yb9488vPzGTJkSLVPCmV5vV42b94cSJq7d++mRYsWPP7443z88cfccsstvPTSS1x77bXlzp00aRIJCQm89dZb3HnnnSxYsIBmzZoxceJEli9fzvLly+nZs2eF93311Vc5++yz6dChA/PmzeOll16iU6dO7Nq1q8LjXS4XQ4YMYcmSJTz66KMsXLiQlJQUhgwZwo4dO6r9nPHx8Zxxxhm88cYbgW1z586tcM4Ol8vF4MGDWbt2Lc899xyzZ89mw4YNnHTSSYH51gEmTJjARx99xIwZM5g1axaffPJJuWK1UP6sjPA50LkxvE8owbbyGgSMVdXdIlK2NdcOIJRDs4wF3qhk399E5AdgGzBJVX+p6CARuRy4HKBt27Y1u/vU8A5dwNT9NTr86aefZvTo0YwfPx4R4eijj+acc85h0qRJ1c68FxERwcKFCwNTwP7666/MnTuX//znPwC89NJL7Nmzh9WrVwe+JXfo0IG+fftWed0ZM2aQn5/PmjVrAlPV9u/fn/T0dF588UWuueaaKs/3er14PB727t3L/fffz/bt2wNTFB977LFMnz49cGz//v2Ji4vj0ksv5d///nepybz69etXbvh9h8NBmzZtyk3CVJLP5+P222/nrLPOKvUH/swzz6z0nNdee42ff/6ZX375hY4drRLgIUOG0LlzZx577LHAzItVGTt2LBMnTuSZZ55hzZo1bNq0iXPPPZeHHnqo1HEv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8cUXuX79509kFfmuWLGCcePG0bJlS4YNG0a7du24ePEis2fPtjQzVXxys9eUsG7dOnr27Imfnx/BwcEMHjzYMiOiPZcuXWLy5Mm0bt0aX19fevfuTWZmplN5Dh8+nMLCQrZs2WJZt2rVKvr06WMzBXOF48ePM3LkSAIDA2nevDnDhg2r8hj//Px8HnnkEQICAggLC7OZlMyaO8oqPj6eyZMnW5Y3bdqEUsqm6XXt2rV4e3tTVFQE2DZ5paSksHbtWr7++mtL2VRuMlq5ciV33HEHgYGBDBo0iOzs7BrjcjW3d999l86dO+Pj40NERASvv/56lX22bdtGv379CAgIoEWLFiQkJLB3716WL1/OlClTACy5JCQkAD83lW7fvp0ePXrg6+vLmjWmGc+dKVNrb731FgEBATZTfRs8vdm1YxchwVHs37+/2mu+vLyc1157jTvuuAMfHx+ioqJIT0+v8XdTHf1wyEYuJiaGhQsXcvvttzN06FDLbIPOWL16NV27dmXp0qVkZ2fz7LPP8sILL/DWW28BUFRUxAMPPEBoaCgffPAB165d45lnnuGnn35yOKMimP6w9unTh+DgYJYsWYK/vz+vvfYaAwYMICsrq1YTOZWVlXH69GnLz8vLyyMoKIg33ngDo9FIVlYWs2bNIjc3l7ffftvm2OnTp/OrX/2KNWvW4OnpidFopF+/fjz44INMmjQJgOho+zM0vP/++4wbN47k5GRSU1MRETZv3kxubi4RERFV9i8uLmbAgAHk5+czd+5cQkJCWLx4MQMGDODo0aN2K31rAQEBDB06lA8++IABAwYApgplxowZVf6oFBcXk5iYiMFg4J133sHLy4u0tDTi4+M5cOCA5RH2jz32GFu3bmXBggWEhoYyb948jh07ZjOlsrvKKi4ujrVr11qWt23bhq+vLxkZGTbrYmJiqszJApCamsqpU6fIz8+3XH9t27a1bM/MzOTs2bPMnz+fq1evMnXqVCZPnsxnn33mMCZXc5s7dy4vvPACM2fOJCEhgX/961+kpqbi7+/PU089BZj6+5KSkujXrx/p6ek0a9aMHTt2cObMGYYMGcK0adOYP38+O3fuBLB59l1RURHjx49n5syZREVFER4e7nSZWnvkkUeYNm0a//jHPywf8AyePqxfuZ4uXaPp1q0b69atc3jNT5kyhfT0dF566SViYmL4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\n", "text/plain": [ "
" ] @@ -302,10 +318,10 @@ ], "source": [ "for model_name, model in models.items():\n", - " t, y = solutions[model_name].t, solutions[model_name].y\n", - " time = pybamm.ProcessedVariable(model.variables[\"Time [h]\"], t, y, discs[model_name].mesh)(t)\n", - " voltage = pybamm.ProcessedVariable(model.variables[\"Terminal voltage [V]\"], t, y, discs[model_name].mesh)\n", - " plt.plot(time, voltage(t) * 6, lw=2, label=model.name)\n", + " t = solutions[model_name].t\n", + " time = solutions[model_name][\"Time [h]\"](t)\n", + " voltage = solutions[model_name][\"Terminal voltage [V]\"](t)\n", + " plt.plot(time, voltage * 6, lw=2, label=model.name)\n", "plt.xlabel(\"Time [h]\", fontsize=15)\n", "plt.ylabel(\"Terminal voltage [V]\", fontsize=15)\n", "plt.legend(fontsize=15)\n", @@ -345,12 +361,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "e1d24900c48b4577b053e16b64aa0300", + "model_id": "0b39d0725e264a34898185182a4c4865", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.6689256902312558, step=0.05), Output()), _…" + "interactive(children=(FloatSlider(value=0.0, description='t', max=1.077935920470844, step=0.05), Output()), _d…" ] }, "metadata": {}, @@ -358,7 +374,7 @@ } ], "source": [ - "quick_plot = pybamm.QuickPlot(list_of_models, a_mesh, list_of_solutions)\n", + "quick_plot = pybamm.QuickPlot(list_of_solutions)\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=quick_plot.max_t,step=0.05,value=0));" ] @@ -385,7 +401,7 @@ { "data": { "text/plain": [ - "1" + "0.680616" ] }, "execution_count": 14, @@ -412,12 +428,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "62b45618739c453ab2341df06132e80a", + "model_id": "8eed9b3c754c4f8f89721c96c8d5844c", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.20858973136243464, step=0.05), Output()), …" + "interactive(children=(FloatSlider(value=0.0, description='t', max=0.7120821863752079, step=0.05), Output()), _…" ] }, "metadata": {}, @@ -435,7 +451,7 @@ "list_of_models = list(models.values())\n", "list_of_solutions = list(solutions.values())\n", "\n", - "quick_plot = pybamm.QuickPlot(list_of_models, a_mesh, list_of_solutions)\n", + "quick_plot = pybamm.QuickPlot(list_of_solutions)\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=quick_plot.max_t,step=0.05,value=0));" ] }, diff --git a/examples/notebooks/models/lead-acid.ipynb b/examples/notebooks/models/lead-acid.ipynb index 174cddace2..09e42c6b13 100644 --- a/examples/notebooks/models/lead-acid.ipynb +++ b/examples/notebooks/models/lead-acid.ipynb @@ -262,13 +262,23 @@ "execution_count": 7, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:146: RuntimeWarning: invalid value encountered in greater_equal\n", + " up = (g <= 0) & (g_new >= 0)\n", + "/Users/vsulzer/Documents/Energy_storage/PyBaMM/PyBaMM-env/lib/python3.7/site-packages/scipy/integrate/_ivp/ivp.py:147: RuntimeWarning: invalid value encountered in less_equal\n", + " down = (g >= 0) & (g_new <= 0)\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "Solved the LOQS model in 0.092 seconds\n", - "Solved the Composite model in 10.736 seconds\n", - "Solved the Full model in 9.238 seconds\n" + "Solved the LOQS model in 0.038 seconds\n", + "Solved the Composite model in 0.273 seconds\n", + "Solved the Full model in 1.584 seconds\n" ] } ], @@ -295,7 +305,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To plot the results, we create `ProcessedVariable` objects, which can be evaluated at any time and position. For example, we can compare the voltages:" + "To plot the results, the variables are extracted from the solutions dictionary. For example, we can compare the voltages:" ] }, { @@ -303,9 +313,26 @@ "execution_count": 8, "metadata": {}, "outputs": [ + { + "ename": "KeyError", + "evalue": "'Time [h]'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/solvers/solution.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 180\u001b[0m \u001b[0;31m# return it if it exists\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 181\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_variables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 182\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyError\u001b[0m: 'Time [h]'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mmodel\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmodels\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolutions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mtime\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolutions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"Time [h]\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0mvoltage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolutions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"Terminal voltage [V]\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvoltage\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlw\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/solvers/solution.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 182\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 183\u001b[0m \u001b[0;31m# otherwise create it, save it and then return it\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 184\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 185\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_variables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/solvers/solution.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, variables)\u001b[0m\n\u001b[1;32m 149\u001b[0m \u001b[0mpybamm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Post-processing {}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 150\u001b[0m var = pybamm.ProcessedVariable(\n\u001b[0;32m--> 151\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvariables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mknown_evals\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 152\u001b[0m )\n\u001b[1;32m 153\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyError\u001b[0m: 'Time [h]'" + ] + }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -318,10 +345,10 @@ ], "source": [ "for model in models:\n", - " t, y = solutions[model].t, solutions[model].y\n", - " time = pybamm.ProcessedVariable(model.variables[\"Time [h]\"], t, y, discs[model].mesh)(t)\n", - " voltage = pybamm.ProcessedVariable(model.variables[\"Terminal voltage [V]\"], t, y, discs[model].mesh)\n", - " plt.plot(time, voltage(t), lw=2, label=model.name)\n", + " t = solutions[model].t\n", + " time = solutions[model][\"Time [h]\"](t)\n", + " voltage = solutions[model][\"Terminal voltage [V]\"](t)\n", + " plt.plot(time, voltage, lw=2, label=model.name)\n", "plt.xlabel(\"Time [h]\", fontsize=15)\n", "plt.ylabel(\"Terminal voltage [V]\", fontsize=15)\n", "plt.legend(fontsize=15)\n", @@ -337,28 +364,13 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d65f7a348b604f668e484e7fd5a7f31b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.5, step=0.05), Output()), _dom_classes=('w…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "mesh = list(discs.values())[0].mesh\n", "solution_values = [solutions[model] for model in models]\n", - "quick_plot = pybamm.QuickPlot(models, mesh, solution_values)\n", + "quick_plot = pybamm.QuickPlot(solution_values)\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=quick_plot.max_t,step=0.05,value=0));" ] @@ -372,24 +384,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "1c612c4f2a684f82b2fecf0bab61fb2d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "interactive(children=(FloatSlider(value=0.0, description='t', max=0.5, step=0.05), Output()), _dom_classes=('w…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# update parameter values and solve again\n", "param.update({\"Typical current [A]\": 20})\n", @@ -399,7 +396,7 @@ "\n", "# Plot\n", "solution_values = [solutions[model] for model in models]\n", - "quick_plot = pybamm.QuickPlot(models, mesh, solution_values)\n", + "quick_plot = pybamm.QuickPlot(solution_values)\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=quick_plot.max_t,step=0.05,value=0));" ] }, @@ -427,7 +424,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/examples/notebooks/models/spm1.png b/examples/notebooks/models/spm1.png index 7344d99b12..7fb2ae3614 100644 Binary files a/examples/notebooks/models/spm1.png and b/examples/notebooks/models/spm1.png differ diff --git a/examples/notebooks/models/spm2.png b/examples/notebooks/models/spm2.png index 896bc3aca7..309352dadd 100644 Binary files a/examples/notebooks/models/spm2.png and b/examples/notebooks/models/spm2.png differ diff --git a/examples/notebooks/parameter-values.ipynb b/examples/notebooks/parameter-values.ipynb index 1a2e2b798a..d4a12a04c0 100644 --- a/examples/notebooks/parameter-values.ipynb +++ b/examples/notebooks/parameter-values.ipynb @@ -47,7 +47,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are {'a': 1, 'b': 2, 'c': 3}\n" + "parameter values are \n" ] } ], @@ -73,7 +73,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are {'a': 4, 'b': 5, 'c': 6}\n" + "parameter values are \n" ] } ], @@ -126,7 +126,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We can input functions into the parameter values, either directly" + "We can input functions into the parameter values, either directly (note we bypass the check that the parameter already exists)" ] }, { @@ -138,14 +138,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are {'a': 4, 'b': 5, 'c': 6, 'cube function': }\n" + "parameter values are \n" ] } ], "source": [ "def cubed(x):\n", " return x ** 3\n", - "parameter_values.update({\"cube function\": cubed})\n", + "parameter_values.update({\"cube function\": cubed}, check_already_exists=False)\n", "print(\"parameter values are {}\".format(parameter_values))" ] }, @@ -165,7 +165,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are {'a': 4, 'b': 5, 'c': 6, 'cube function': , 'square function': }\n" + "parameter values are \n" ] } ], @@ -178,7 +178,7 @@ "\"\"\"\n", ")\n", "f.close()\n", - "parameter_values.update({\"square function\": pybamm.load_function(\"squared.py\")})\n", + "parameter_values.update({\"square function\": pybamm.load_function(\"squared.py\")}, check_already_exists=False)\n", "print(\"parameter values are {}\".format(parameter_values))" ] }, @@ -261,7 +261,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "function (squared) = 16.0\n" + "a ** 2.0 = 16.0\n" ] } ], @@ -287,7 +287,7 @@ "output_type": "stream", "text": [ "a + b * c = 32.0\n", - "function (squared) = 4.0\n" + "a ** 2.0 = 4.0\n" ] } ], @@ -331,7 +331,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -363,19 +363,19 @@ "\n", "# Solve\n", "t_eval = np.linspace(0, 2, 30)\n", - "ode_solver = pybamm.ScikitsOdeSolver()\n", + "ode_solver = pybamm.ScipySolver()\n", "solution = ode_solver.solve(model, t_eval)\n", "\n", "# Post-process, so that u1 can be called at any time t (using interpolation)\n", - "t_sol1, y_sol1 = solution.t, solution.y\n", - "u1 = pybamm.ProcessedVariable(model.variables[\"u\"], t_sol1, y_sol1)\n", + "t_sol1 = solution.t\n", + "u1 = solution[\"u\"]\n", "\n", "# Update parameters and solve again ###############################\n", "new_parameter_values = pybamm.ParameterValues({\"a\": 4, \"b\": -1})\n", "new_parameter_values.update_model(model, disc) # no need to re-discretise\n", "solution = ode_solver.solve(model, t_eval)\n", - "t_sol2, y_sol2 = solution.t, solution.y\n", - "u2 = pybamm.ProcessedVariable(model.variables[\"u\"], t_sol2, y_sol2)\n", + "t_sol2 = solution.t\n", + "u2 = solution[\"u\"]\n", "###################################################################\n", "\n", "# Plot\n", @@ -405,7 +405,7 @@ { "data": { "text/plain": [ - "{Variable(-0xa8c503144feff0a, u, children=[], domain=[], auxiliary_domains={}): Multiplication(0x1f74a8084187b70a, *, children=['-a', 'y[0:1]'], domain=[], auxiliary_domains={})}" + "{Variable(0x1b4aef6f668c631, u, children=[], domain=[], auxiliary_domains={}): Multiplication(-0x36caade7fdf02dac, *, children=['-a', 'y[0:1]'], domain=[], auxiliary_domains={})}" ] }, "execution_count": 12, @@ -482,7 +482,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/examples/notebooks/solution-data-and-processed-variables.ipynb b/examples/notebooks/solution-data-and-processed-variables.ipynb new file mode 100644 index 0000000000..5b7156238c --- /dev/null +++ b/examples/notebooks/solution-data-and-processed-variables.ipynb @@ -0,0 +1,534 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# A look at solution data and processed variables" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once you have run a simulation the first thing you want to do is have a look at the data. Most of the examples so far have made use of PyBaMM's handy QuickPlot function but there are other ways to access the data and this notebook will explore them. First off we will generate a standard SPMe model and use QuickPlot to view the default variables." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a4a696fe41c840e194e4d1bca8282bfb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(FloatSlider(value=0.0, description='t', max=0.9353313389920834, step=0.05), Output()), _…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pybamm\n", + "import numpy as np\n", + "import os\n", + "import matplotlib.pyplot as plt\n", + "os.chdir(pybamm.__path__[0]+'/..')\n", + "\n", + "# load model\n", + "model = pybamm.lithium_ion.SPMe()\n", + "\n", + "# create geometry\n", + "geometry = model.default_geometry\n", + "\n", + "# load parameter values and process model and geometry\n", + "param = model.default_parameter_values\n", + "param.process_model(model)\n", + "param.process_geometry(geometry)\n", + "\n", + "# set mesh\n", + "mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)\n", + "\n", + "# discretise model\n", + "disc = pybamm.Discretisation(mesh, model.default_spatial_methods)\n", + "disc.process_model(model)\n", + "\n", + "# solve model\n", + "solver = model.default_solver\n", + "dt = 1e-3\n", + "t_eval = np.arange(0, 0.15, dt)\n", + "solution = solver.solve(model, t_eval)\n", + "\n", + "quick_plot = pybamm.QuickPlot(solution)\n", + "\n", + "import ipywidgets as widgets\n", + "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=quick_plot.max_t,step=0.05,value=0));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Behind the scenes the QuickPlot classed has created some processed variables which can interpolate the model variables for our solution and has also stored the results for the solution steps" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['Negative particle surface concentration', 'Electrolyte concentration', 'Positive particle surface concentration', 'Current [A]', 'Negative electrode potential [V]', 'Electrolyte potential [V]', 'Positive electrode potential [V]', 'Terminal voltage [V]'])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.data.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(20, 150)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.data['Negative particle surface concentration'].shape" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(150,)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.t.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that the dictionary keys are in the same order as the subplots in the QuickPlot figure. We can add new processed variables to the solution by simply using it like a dictionary. First lets find a few more variables to look at. As you will see there are quite a few:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Active material volume fraction', 'Battery voltage [V]', 'C-rate', 'Cell temperature', 'Cell temperature [K]', 'Current [A]', 'Current collector current density', 'Current collector current density [A.m-2]', 'Discharge capacity [A.h]', 'Electrode current density', 'Electrode tortuosity', 'Electrolyte concentration', 'Electrolyte concentration [Molar]', 'Electrolyte concentration [mol.m-3]', 'Electrolyte current density', 'Electrolyte current density [A.m-2]', 'Electrolyte flux', 'Electrolyte flux [mol.m-2.s-1]', 'Electrolyte potential', 'Electrolyte potential [V]', 'Electrolyte pressure', 'Electrolyte tortuosity', 'Exchange current density', 'Exchange current density [A.m-2]', 'Exchange current density per volume [A.m-3]', 'Gradient of electrolyte potential', 'Gradient of negative electrode potential', 'Gradient of negative electrolyte potential', 'Gradient of positive electrode potential', 'Gradient of positive electrolyte potential', 'Gradient of separator electrolyte potential', 'Heat flux', 'Heat flux [W.m-2]', 'Interfacial current density', 'Interfacial current density [A.m-2]', 'Interfacial current density per volume [A.m-3]', 'Irreversible electrochemical heating', 'Irreversible electrochemical heating [W.m-3]', 'Leading-order active material volume fraction', 'Leading-order current collector current density', 'Leading-order electrode tortuosity', 'Leading-order electrolyte tortuosity', 'Leading-order negative electrode active material volume fraction', 'Leading-order negative electrode porosity', 'Leading-order negative electrode tortuosity', 'Leading-order negative electrolyte tortuosity', 'Leading-order porosity', 'Leading-order positive electrode active material volume fraction', 'Leading-order positive electrode porosity', 'Leading-order positive electrode tortuosity', 'Leading-order positive electrolyte tortuosity', 'Leading-order separator active material volume fraction', 'Leading-order separator porosity', 'Leading-order separator tortuosity', 'Leading-order x-averaged negative electrode active material volume fraction', 'Leading-order x-averaged negative electrode porosity', 'Leading-order x-averaged negative electrode porosity change', 'Leading-order x-averaged negative electrode tortuosity', 'Leading-order x-averaged negative electrolyte tortuosity', 'Leading-order x-averaged positive electrode active material volume fraction', 'Leading-order x-averaged positive electrode porosity', 'Leading-order x-averaged positive electrode porosity change', 'Leading-order x-averaged positive electrode tortuosity', 'Leading-order x-averaged positive electrolyte tortuosity', 'Leading-order x-averaged separator active material volume fraction', 'Leading-order x-averaged separator porosity', 'Leading-order x-averaged separator porosity change', 'Leading-order x-averaged separator tortuosity', 'Local voltage', 'Local voltage [V]', 'Measured battery open circuit voltage [V]', 'Measured open circuit voltage', 'Measured open circuit voltage [V]', 'Negative current collector potential', 'Negative current collector potential [V]', 'Negative current collector temperature', 'Negative current collector temperature [K]', 'Negative electrode active material volume fraction', 'Negative electrode active volume fraction', 'Negative electrode average extent of lithiation', 'Negative electrode current density', 'Negative electrode current density [A.m-2]', 'Negative electrode entropic change', 'Negative electrode exchange current density', 'Negative electrode exchange current density [A.m-2]', 'Negative electrode exchange current density per volume [A.m-3]', 'Negative electrode interfacial current density', 'Negative electrode interfacial current density [A.m-2]', 'Negative electrode interfacial current density per volume [A.m-3]', 'Negative electrode ohmic losses', 'Negative electrode ohmic losses [V]', 'Negative electrode open circuit potential', 'Negative electrode open circuit potential [V]', 'Negative electrode porosity', 'Negative electrode porosity change', 'Negative electrode potential', 'Negative electrode potential [V]', 'Negative electrode reaction overpotential', 'Negative electrode reaction overpotential [V]', 'Negative electrode surface potential difference', 'Negative electrode surface potential difference [V]', 'Negative electrode temperature', 'Negative electrode temperature [K]', 'Negative electrode tortuosity', 'Negative electrode volume-averaged concentration', 'Negative electrode volume-averaged concentration [mol.m-3]', 'Negative electrolyte concentration', 'Negative electrolyte concentration [Molar]', 'Negative electrolyte concentration [mol.m-3]', 'Negative electrolyte current density', 'Negative electrolyte current density [A.m-2]', 'Negative electrolyte potential', 'Negative electrolyte potential [V]', 'Negative electrolyte tortuosity', 'Negative particle concentration', 'Negative particle concentration [mol.m-3]', 'Negative particle flux', 'Negative particle surface concentration', 'Negative particle surface concentration [mol.m-3]', 'Ohmic heating', 'Ohmic heating [W.m-3]', 'Porosity', 'Porosity change', 'Positive current collector potential', 'Positive current collector potential [V]', 'Positive current collector temperature', 'Positive current collector temperature [K]', 'Positive electrode active material volume fraction', 'Positive electrode active volume fraction', 'Positive electrode average extent of lithiation', 'Positive electrode current density', 'Positive electrode current density [A.m-2]', 'Positive electrode entropic change', 'Positive electrode exchange current density', 'Positive electrode exchange current density [A.m-2]', 'Positive electrode exchange current density per volume [A.m-3]', 'Positive electrode interfacial current density', 'Positive electrode interfacial current density [A.m-2]', 'Positive electrode interfacial current density per volume [A.m-3]', 'Positive electrode ohmic losses', 'Positive electrode ohmic losses [V]', 'Positive electrode open circuit potential', 'Positive electrode open circuit potential [V]', 'Positive electrode porosity', 'Positive electrode porosity change', 'Positive electrode potential', 'Positive electrode potential [V]', 'Positive electrode reaction overpotential', 'Positive electrode reaction overpotential [V]', 'Positive electrode surface potential difference', 'Positive electrode surface potential difference [V]', 'Positive electrode temperature', 'Positive electrode temperature [K]', 'Positive electrode tortuosity', 'Positive electrode volume-averaged concentration', 'Positive electrode volume-averaged concentration [mol.m-3]', 'Positive electrolyte concentration', 'Positive electrolyte concentration [Molar]', 'Positive electrolyte concentration [mol.m-3]', 'Positive electrolyte current density', 'Positive electrolyte current density [A.m-2]', 'Positive electrolyte potential', 'Positive electrolyte potential [V]', 'Positive electrolyte tortuosity', 'Positive particle concentration', 'Positive particle concentration [mol.m-3]', 'Positive particle flux', 'Positive particle surface concentration', 'Positive particle surface concentration [mol.m-3]', 'Reversible heating', 'Reversible heating [W.m-3]', 'Separator active material volume fraction', 'Separator electrolyte concentration', 'Separator electrolyte concentration [Molar]', 'Separator electrolyte concentration [mol.m-3]', 'Separator electrolyte potential', 'Separator electrolyte potential [V]', 'Separator porosity', 'Separator porosity change', 'Separator temperature', 'Separator temperature [K]', 'Separator tortuosity', 'Terminal power [W]', 'Terminal voltage', 'Terminal voltage [V]', 'Time', 'Time [h]', 'Time [min]', 'Time [s]', 'Total current density', 'Total current density [A.m-2]', 'Total heating', 'Total heating [W.m-3]', 'Volume-averaged cell temperature', 'Volume-averaged cell temperature [K]', 'Volume-averaged total heating', 'Volume-averaged total heating [W.m-3]', 'Volume-averaged velocity', 'Volume-averaged velocity [m.s-1]', 'X-averaged battery concentration overpotential [V]', 'X-averaged battery electrolyte ohmic losses [V]', 'X-averaged battery open circuit voltage [V]', 'X-averaged battery reaction overpotential [V]', 'X-averaged battery solid phase ohmic losses [V]', 'X-averaged cell temperature', 'X-averaged cell temperature [K]', 'X-averaged concentration overpotential', 'X-averaged concentration overpotential [V]', 'X-averaged electrolyte concentration', 'X-averaged electrolyte concentration [Molar]', 'X-averaged electrolyte concentration [mol.m-3]', 'X-averaged electrolyte ohmic losses', 'X-averaged electrolyte ohmic losses [V]', 'X-averaged electrolyte overpotential', 'X-averaged electrolyte overpotential [V]', 'X-averaged electrolyte potential', 'X-averaged electrolyte potential [V]', 'X-averaged negative electrode active material volume fraction', 'X-averaged negative electrode entropic change', 'X-averaged negative electrode exchange current density', 'X-averaged negative electrode exchange current density [A.m-2]', 'X-averaged negative electrode exchange current density per volume [A.m-3]', 'X-averaged negative electrode interfacial current density', 'X-averaged negative electrode interfacial current density [A.m-2]', 'X-averaged negative electrode interfacial current density per volume [A.m-3]', 'X-averaged negative electrode ohmic losses', 'X-averaged negative electrode ohmic losses [V]', 'X-averaged negative electrode open circuit potential', 'X-averaged negative electrode open circuit potential [V]', 'X-averaged negative electrode porosity', 'X-averaged negative electrode porosity change', 'X-averaged negative electrode potential', 'X-averaged negative electrode potential [V]', 'X-averaged negative electrode reaction overpotential', 'X-averaged negative electrode reaction overpotential [V]', 'X-averaged negative electrode surface potential difference', 'X-averaged negative electrode surface potential difference [V]', 'X-averaged negative electrode temperature', 'X-averaged negative electrode temperature [K]', 'X-averaged negative electrode tortuosity', 'X-averaged negative electrode total interfacial current density', 'X-averaged negative electrode total interfacial current density [A.m-2]', 'X-averaged negative electrode total interfacial current density per volume [A.m-3]', 'X-averaged negative electrolyte concentration', 'X-averaged negative electrolyte concentration [mol.m-3]', 'X-averaged negative electrolyte potential', 'X-averaged negative electrolyte potential [V]', 'X-averaged negative electrolyte tortuosity', 'X-averaged negative particle concentration', 'X-averaged negative particle concentration [mol.m-3]', 'X-averaged negative particle flux', 'X-averaged negative particle surface concentration', 'X-averaged negative particle surface concentration [mol.m-3]', 'X-averaged open circuit voltage', 'X-averaged open circuit voltage [V]', 'X-averaged porosity change', 'X-averaged positive electrode active material volume fraction', 'X-averaged positive electrode entropic change', 'X-averaged positive electrode exchange current density', 'X-averaged positive electrode exchange current density [A.m-2]', 'X-averaged positive electrode exchange current density per volume [A.m-3]', 'X-averaged positive electrode interfacial current density', 'X-averaged positive electrode interfacial current density [A.m-2]', 'X-averaged positive electrode interfacial current density per volume [A.m-3]', 'X-averaged positive electrode ohmic losses', 'X-averaged positive electrode ohmic losses [V]', 'X-averaged positive electrode open circuit potential', 'X-averaged positive electrode open circuit potential [V]', 'X-averaged positive electrode porosity', 'X-averaged positive electrode porosity change', 'X-averaged positive electrode potential', 'X-averaged positive electrode potential [V]', 'X-averaged positive electrode reaction overpotential', 'X-averaged positive electrode reaction overpotential [V]', 'X-averaged positive electrode surface potential difference', 'X-averaged positive electrode surface potential difference [V]', 'X-averaged positive electrode temperature', 'X-averaged positive electrode temperature [K]', 'X-averaged positive electrode tortuosity', 'X-averaged positive electrode total interfacial current density', 'X-averaged positive electrode total interfacial current density [A.m-2]', 'X-averaged positive electrode total interfacial current density per volume [A.m-3]', 'X-averaged positive electrolyte concentration', 'X-averaged positive electrolyte concentration [mol.m-3]', 'X-averaged positive electrolyte potential', 'X-averaged positive electrolyte potential [V]', 'X-averaged positive electrolyte tortuosity', 'X-averaged positive particle concentration', 'X-averaged positive particle concentration [mol.m-3]', 'X-averaged positive particle flux', 'X-averaged positive particle surface concentration', 'X-averaged positive particle surface concentration [mol.m-3]', 'X-averaged reaction overpotential', 'X-averaged reaction overpotential [V]', 'X-averaged separator active material volume fraction', 'X-averaged separator electrolyte concentration', 'X-averaged separator electrolyte concentration [mol.m-3]', 'X-averaged separator electrolyte potential', 'X-averaged separator electrolyte potential [V]', 'X-averaged separator porosity', 'X-averaged separator porosity change', 'X-averaged separator temperature', 'X-averaged separator temperature [K]', 'X-averaged separator tortuosity', 'X-averaged solid phase ohmic losses', 'X-averaged solid phase ohmic losses [V]', 'X-averaged total heating', 'X-averaged total heating [W.m-3]', 'r_n', 'r_n [m]', 'r_p', 'r_p [m]', 'x', 'x [m]', 'x_n', 'x_n [m]', 'x_p', 'x_p [m]', 'x_s', 'x_s [m]']\n" + ] + } + ], + "source": [ + "keys = list(model.variables.keys())\n", + "keys.sort()\n", + "print(keys)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution['Time [h]']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This created a new processed variable and stored it on the solution object" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['Negative particle surface concentration', 'Electrolyte concentration', 'Positive particle surface concentration', 'Current [A]', 'Negative electrode potential [V]', 'Electrolyte potential [V]', 'Positive electrode potential [V]', 'Terminal voltage [V]', 'Time [h]'])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.data.keys()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see the actual data in one of two ways, first by simply accessing the entries attribute of the processed variable" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0. , 0.00627739, 0.01255478, 0.01883217, 0.02510957,\n", + " 0.03138696, 0.03766435, 0.04394174, 0.05021913, 0.05649652,\n", + " 0.06277392, 0.06905131, 0.0753287 , 0.08160609, 0.08788348,\n", + " 0.09416087, 0.10043826, 0.10671566, 0.11299305, 0.11927044,\n", + " 0.12554783, 0.13182522, 0.13810261, 0.14438001, 0.1506574 ,\n", + " 0.15693479, 0.16321218, 0.16948957, 0.17576696, 0.18204435,\n", + " 0.18832175, 0.19459914, 0.20087653, 0.20715392, 0.21343131,\n", + " 0.2197087 , 0.2259861 , 0.23226349, 0.23854088, 0.24481827,\n", + " 0.25109566, 0.25737305, 0.26365044, 0.26992784, 0.27620523,\n", + " 0.28248262, 0.28876001, 0.2950374 , 0.30131479, 0.30759219,\n", + " 0.31386958, 0.32014697, 0.32642436, 0.33270175, 0.33897914,\n", + " 0.34525653, 0.35153393, 0.35781132, 0.36408871, 0.3703661 ,\n", + " 0.37664349, 0.38292088, 0.38919828, 0.39547567, 0.40175306,\n", + " 0.40803045, 0.41430784, 0.42058523, 0.42686262, 0.43314002,\n", + " 0.43941741, 0.4456948 , 0.45197219, 0.45824958, 0.46452697,\n", + " 0.47080437, 0.47708176, 0.48335915, 0.48963654, 0.49591393,\n", + " 0.50219132, 0.50846871, 0.51474611, 0.5210235 , 0.52730089,\n", + " 0.53357828, 0.53985567, 0.54613306, 0.55241046, 0.55868785,\n", + " 0.56496524, 0.57124263, 0.57752002, 0.58379741, 0.5900748 ,\n", + " 0.5963522 , 0.60262959, 0.60890698, 0.61518437, 0.62146176,\n", + " 0.62773915, 0.63401655, 0.64029394, 0.64657133, 0.65284872,\n", + " 0.65912611, 0.6654035 , 0.67168089, 0.67795829, 0.68423568,\n", + " 0.69051307, 0.69679046, 0.70306785, 0.70934524, 0.71562264,\n", + " 0.72190003, 0.72817742, 0.73445481, 0.7407322 , 0.74700959,\n", + " 0.75328698, 0.75956438, 0.76584177, 0.77211916, 0.77839655,\n", + " 0.78467394, 0.79095133, 0.79722873, 0.80350612, 0.80978351,\n", + " 0.8160609 , 0.82233829, 0.82861568, 0.83489307, 0.84117047,\n", + " 0.84744786, 0.85372525, 0.86000264, 0.86628003, 0.87255742,\n", + " 0.87883482, 0.88511221, 0.8913896 , 0.89766699, 0.90394438,\n", + " 0.91022177, 0.91649916, 0.92277656, 0.92905395, 0.93533134])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution['Time [h]'].entries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Secondly by calling the method with a specific solution time, which is non-dimensional" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0. , 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008,\n", + " 0.009, 0.01 , 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017,\n", + " 0.018, 0.019, 0.02 , 0.021, 0.022, 0.023, 0.024, 0.025, 0.026,\n", + " 0.027, 0.028, 0.029, 0.03 , 0.031, 0.032, 0.033, 0.034, 0.035,\n", + " 0.036, 0.037, 0.038, 0.039, 0.04 , 0.041, 0.042, 0.043, 0.044,\n", + " 0.045, 0.046, 0.047, 0.048, 0.049, 0.05 , 0.051, 0.052, 0.053,\n", + " 0.054, 0.055, 0.056, 0.057, 0.058, 0.059, 0.06 , 0.061, 0.062,\n", + " 0.063, 0.064, 0.065, 0.066, 0.067, 0.068, 0.069, 0.07 , 0.071,\n", + " 0.072, 0.073, 0.074, 0.075, 0.076, 0.077, 0.078, 0.079, 0.08 ,\n", + " 0.081, 0.082, 0.083, 0.084, 0.085, 0.086, 0.087, 0.088, 0.089,\n", + " 0.09 , 0.091, 0.092, 0.093, 0.094, 0.095, 0.096, 0.097, 0.098,\n", + " 0.099, 0.1 , 0.101, 0.102, 0.103, 0.104, 0.105, 0.106, 0.107,\n", + " 0.108, 0.109, 0.11 , 0.111, 0.112, 0.113, 0.114, 0.115, 0.116,\n", + " 0.117, 0.118, 0.119, 0.12 , 0.121, 0.122, 0.123, 0.124, 0.125,\n", + " 0.126, 0.127, 0.128, 0.129, 0.13 , 0.131, 0.132, 0.133, 0.134,\n", + " 0.135, 0.136, 0.137, 0.138, 0.139, 0.14 , 0.141, 0.142, 0.143,\n", + " 0.144, 0.145, 0.146, 0.147, 0.148, 0.149])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.t" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0. , 0.00627739, 0.01255478, 0.01883217, 0.02510957,\n", + " 0.03138696, 0.03766435, 0.04394174, 0.05021913, 0.05649652,\n", + " 0.06277392, 0.06905131, 0.0753287 , 0.08160609, 0.08788348,\n", + " 0.09416087, 0.10043826, 0.10671566, 0.11299305, 0.11927044,\n", + " 0.12554783, 0.13182522, 0.13810261, 0.14438001, 0.1506574 ,\n", + " 0.15693479, 0.16321218, 0.16948957, 0.17576696, 0.18204435,\n", + " 0.18832175, 0.19459914, 0.20087653, 0.20715392, 0.21343131,\n", + " 0.2197087 , 0.2259861 , 0.23226349, 0.23854088, 0.24481827,\n", + " 0.25109566, 0.25737305, 0.26365044, 0.26992784, 0.27620523,\n", + " 0.28248262, 0.28876001, 0.2950374 , 0.30131479, 0.30759219,\n", + " 0.31386958, 0.32014697, 0.32642436, 0.33270175, 0.33897914,\n", + " 0.34525653, 0.35153393, 0.35781132, 0.36408871, 0.3703661 ,\n", + " 0.37664349, 0.38292088, 0.38919828, 0.39547567, 0.40175306,\n", + " 0.40803045, 0.41430784, 0.42058523, 0.42686262, 0.43314002,\n", + " 0.43941741, 0.4456948 , 0.45197219, 0.45824958, 0.46452697,\n", + " 0.47080437, 0.47708176, 0.48335915, 0.48963654, 0.49591393,\n", + " 0.50219132, 0.50846871, 0.51474611, 0.5210235 , 0.52730089,\n", + " 0.53357828, 0.53985567, 0.54613306, 0.55241046, 0.55868785,\n", + " 0.56496524, 0.57124263, 0.57752002, 0.58379741, 0.5900748 ,\n", + " 0.5963522 , 0.60262959, 0.60890698, 0.61518437, 0.62146176,\n", + " 0.62773915, 0.63401655, 0.64029394, 0.64657133, 0.65284872,\n", + " 0.65912611, 0.6654035 , 0.67168089, 0.67795829, 0.68423568,\n", + " 0.69051307, 0.69679046, 0.70306785, 0.70934524, 0.71562264,\n", + " 0.72190003, 0.72817742, 0.73445481, 0.7407322 , 0.74700959,\n", + " 0.75328698, 0.75956438, 0.76584177, 0.77211916, 0.77839655,\n", + " 0.78467394, 0.79095133, 0.79722873, 0.80350612, 0.80978351,\n", + " 0.8160609 , 0.82233829, 0.82861568, 0.83489307, 0.84117047,\n", + " 0.84744786, 0.85372525, 0.86000264, 0.86628003, 0.87255742,\n", + " 0.87883482, 0.88511221, 0.8913896 , 0.89766699, 0.90394438,\n", + " 0.91022177, 0.91649916, 0.92277656, 0.92905395, 0.93533134])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution['Time [h]'](solution.t)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And interpolated" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(0.0031387)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interp_t = (solution.t[0] + solution.t[1])/2\n", + "solution['Time [h]'](interp_t)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the variable has not already been processed it will be created behind the scenes" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(298.15)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "var = 'X-averaged negative electrode temperature [K]'\n", + "solution[var](interp_t)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stepping the solver\n", + "\n", + "This solution was created in one go with the solver's solve method but it is also possible to step the solution and look at the results as we go. In doing so, the results are automatically updated at each step." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time 0\n", + "[3.77057107 3.7384485 3.72393304 3.71095299 3.69929584 3.68896714\n", + " 3.67990351 3.67198691 3.66505857 3.65889901]\n", + "Time 0.05\n", + "[3.77057107 3.7384485 3.72393304 3.71095299 3.69929584 3.68896714\n", + " 3.67990351 3.67198691 3.66505857 3.65889901 3.6531217 3.64686147\n", + " 3.63836533 3.62648194 3.61471462 3.60677738 3.60170497 3.59781764\n", + " 3.59438503]\n", + "Time 0.1\n", + "[3.77057107 3.7384485 3.72393304 3.71095299 3.69929584 3.68896714\n", + " 3.67990351 3.67198691 3.66505857 3.65889901 3.6531217 3.64686147\n", + " 3.63836533 3.62648194 3.61471462 3.60677738 3.60170497 3.59781764\n", + " 3.59438503 3.59123681 3.58840351 3.58588895 3.58343102 3.58026052\n", + " 3.57495718 3.56514501 3.54544289 3.49856266]\n" + ] + } + ], + "source": [ + "dt = 0.05\n", + "time = 0\n", + "end_time = solution.t[-1]\n", + "step_solver = model.default_solver\n", + "step_solution = None\n", + "while time < end_time:\n", + " step_solution = step_solver.step(step_solution, model, dt=dt, npts=10)\n", + " print('Time', time)\n", + " print(step_solution[\"Terminal voltage [V]\"].entries)\n", + " time += dt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can plot the voltages and see that the solutions are the same" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAD4CAYAAADiry33AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8li6FKAAAgAElEQVR4nO3deXxU9dX48c/JIiEsgrIvSaQKihBQIqIWBETAYhUXIBo04BKXKtpWrJr+FJC0blWrtkK0CkosCFofRSqogOACGPIEFERQDPsjEDbDZpbz++PexEmY7JOZyeS8X6/7ysy93zv3TAhn7nxXUVWMMcaErrBAB2CMMaZuWaI3xpgQZ4neGGNCnCV6Y4wJcZbojTEmxEUEOoCyWrVqpXFxcYEOwxhj6pXVq1fvVdXW3o4FXaKPi4sjMzMz0GEYY0y9IiJbyjtmVTfGGBPiLNEbY0yIs0RvjDEhLujq6I2pT/Lz89m+fTvHjh0LdCimgYiKiqJTp05ERkZW+RxL9MbUwvbt22nWrBlxcXGISKDDMSFOVcnNzWX79u2cdtppVT4vdKpuMjIgLg7CwpyfGRmBjsg0AMeOHePUU0+1JG/8QkQ49dRTq/0NMjTu6DMyICUFjhxxnm/Z4jwHSEoKXFymQbAkb/ypJn9voXFHn5r6S5IvduSIs98YYxq40Ej0W7dWb78xxjQgoZHoY2Kqt9+YEJKWlsbZZ59NfHw8vXv3ZuXKlQAMHDiQbt260atXLy666CK+/fbbkv0xMTF4Ljo0cuRImjZtWq3rqiqDBw/m0KFDPnsvOTk5vPHGGyXPMzMzmTBhgs9e39/GjRvHvHnzanRuYmIimzZt8kkcoZHo09IgOrr0vuhoZ78xIeyLL75g/vz5ZGVlsXbtWj766CM6d+5ccjwjI4M1a9aQnJzMxIkTS/a3aNGCzz77DIADBw6wa9eual97wYIF9OrVi+bNm9f+jbjKJvqEhASee+45n71+fXLHHXfwxBNP+OS1QiPRJyVBejo/nRJLEUJ+h1hIT7eGWONX994LAwf6drv33oqvuWvXLlq1akWjRo0AaNWqFR06dDih3IABA/juu+9KnicmJjJ79mwA3n77ba6++upS5Z988knOO+884uPjeeSRR7xeOyMjgyuvvLLk+WuvvUZ8fDy9evXihhtuAJzEPXjwYOLj47nkkkvY6lanjhs3jgkTJnDhhRfSpUuXkrveBx54gOXLl9O7d2+eeeYZli5dyuWXXw7ApEmTuOmmmxg4cCBdunQp+QDIycmhR48eJXE89dRTTJo0CYDs7Gz69etHfHw8V111Ffv37wecbzXFc2rt3buX4okU161bR9++fenduzfx8fEn3FEXFhYybtw4evToQc+ePXnmmWcqvE6xDz74gFGjRpU893xfixYt4oILLuDcc89l1KhR5OXlAdC/f38++ugjCgoKvP7+qyM0Ej1AUhI5S3MIp4g5j+dYkjcNwtChQ9m2bRtdu3blzjvv5JNPPvFa7r333qNnz54lzy+55BKWLVtGYWEhs2fPZsyYMSXHFi1axKZNm1i1ahXZ2dmsXr2aZcuWnfCan332GX369AGcBDl16lQWL17MmjVr+Pvf/w7A3XffTXJyMmvXriUpKalUNcyuXbv49NNPmT9/Pg888AAAjz32GP379yc7O5vf//73J1xzw4YNLFy4kFWrVjF58mTy8/Mr/P3ceOONPP7446xdu5aePXsyefLkCstPmzaNe+65h+zsbDIzM+nUqVOp49nZ2ezYsYOvv/6ar776ivHjx1fpOkOGDGHlypUcPnwYgDlz5pCYmMjevXuZOnUqH330EVlZWSQkJPD0008DEBYWxumnn86aNWsqjLkqQqN7pat7d2jaFFasgLFjAx2NaWiefdb/12zatCmrV69m+fLlLFmyhDFjxvDYY48xbtw4AJKSkmjcuDFxcXE8//zzJeeFh4fz61//mtmzZ3P06FE8pwZftGgRixYt4pxzzgEgLy+PTZs2MWDAgFLX3rdvH82aNQNg8eLFjBo1ilatWgFwyimnAE7V0ttvvw3ADTfcwP33319y/siRIwkLC6N79+78+OOPVXq/I0aMoFGjRjRq1Ig2bdpUeN7Bgwc5cOAAF198MQDJycml7qq9ueCCC0hLS2P79u1cffXVnHHGGaWOd+nShc2bN3P33XczYsQIhg4dWqXrREREMHz4cN577z2uvfZa3n//fZ544gk++eQT1q9fz0UXXQTAzz//zAUXXFByXps2bdi5c2fJB2pNhVSiDw+H884Dty3KmAYhPDycgQMHMnDgQHr27MnMmTNLEn1GRgYJCQlez0tMTOSqq64qqeYopqo8+OCD3HbbbRVeNyIigqKiIsLCalYx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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "voltage = solution[\"Terminal voltage [V]\"]\n", + "step_voltage = step_solution[\"Terminal voltage [V]\"]\n", + "plt.figure()\n", + "plt.plot(solution.t, voltage(solution.t), \"b-\", label=\"SPMe (continuous solve)\")\n", + "plt.plot(\n", + " step_solution.t, step_voltage(step_solution.t), \"ro\", label=\"SPMe (stepped solve)\"\n", + ")\n", + "plt.legend()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/notebooks/solvers/dae-solver.ipynb b/examples/notebooks/solvers/dae-solver.ipynb new file mode 100644 index 0000000000..c45868bb78 --- /dev/null +++ b/examples/notebooks/solvers/dae-solver.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DAE solver\n", + "\n", + "In this notebook, we show some examples of solving a DAE model. For the purposes of this example, we use the CasADi solver, but the syntax remains the same for other solvers" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup\n", + "import pybamm\n", + "import tests\n", + "import numpy as np\n", + "import os\n", + "import matplotlib.pyplot as plt\n", + "from pprint import pprint\n", + "os.chdir(pybamm.__path__[0]+'/..')\n", + "\n", + "# Create solver\n", + "dae_solver = pybamm.CasadiSolver(mode=\"safe\") # use safe mode so that solver stops at events" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Integrating DAEs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In PyBaMM, a model is solved by calling a solver with solve. This sets up the model to be solved, and then calls the method `_integrate`, which is specific to each solver. We begin by setting up and discretising a model" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Create model\n", + "model = pybamm.BaseModel()\n", + "u = pybamm.Variable(\"u\")\n", + "v = pybamm.Variable(\"v\")\n", + "model.rhs = {u: -v} # du/dt = -v\n", + "model.algebraic = {v: 2 * u - v} # 2*v = u\n", + "model.initial_conditions = {u: 1, v: 2}\n", + "model.variables = {\"u\": u, \"v\": v}\n", + "\n", + "# Discretise using default discretisation\n", + "disc = pybamm.Discretisation()\n", + "disc.process_model(model);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the model can be solved by calling `solver.solve` with a specific time vector at which to evaluate the solution" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzdd3RVVfrG8e+bHlqAJCCQUEV6aBELojAooIhYEMVekLEN6lhH/TEMDsrYRbEgYh8VcVRQVJAiiCIE6dWAlABSAoSWhJT9+yMBE0gggSTn3uT5rJWV3H3KfZI1w+t7zzl7m3MOERERERERkdIW4HUAERERERERqRjUgIqIiIiIiEiZUAMqIiIiIiIiZUINqIiIiIiIiJQJNaAiIiIiIiJSJoK8euOoqCjXsGFDr95eRETkmObPn7/DORftdY7CqI6KiIgvK6yOetaANmzYkISEBK/eXkRE5JjMbL3XGY5FdVRERHxZYXVUt+CKiIiIiIhImVADKiIiIiIiImVCDaiIiIiIiIiUCc+eARURkZKRkZFBUlISaWlpXkfxS2FhYcTExBAcHOx1FBERKQGqi2WruHVUDaiIiJ9LSkqiatWqNGzYEDPzOo5fcc6RnJxMUlISjRo18jqOiIiUANXFsnMidVS34IqI+Lm0tDQiIyNVZE+AmREZGalPyUVEyhHVxbJzInX0uA2omY01s21mtrSQ7WZmI80s0cwWm1mHYmQ+OYvHwQutYWj1nO+Lx5XZW4uI+BIV2RNXUn87M4s1s+lmttzMlpnZPQXsU2jNNLMbzey33K8bSyTU8aiOikg5pbpYdor7ty7KFdB3gF7H2H4h0DT3axDwWrESnKjF42DiYEjZCLic7xMHq3iKiIhXMoH7nXMtgTOBu8ys5RH7FFgzzawm8E/gDKAT8E8zq1GqaVVHRUTEA8dtQJ1zM4Gdx9ilL/CeyzEHqG5mdUoqYKGmDoOM1PxjGak54yIi4pf69evH2rVrjxqfMmUKHTt2pE2bNnTs2JFp06YBOc+eAAwdOjTf6yeffPLwsQcPHuTcc88lMzOzVLM757Y4537N/XkvsAKod8RuhdXMnsAU59xO59wuYArH/vD35KmOioiUmo0bN9KtWzdatmxJq1ateOmllw5ve+edd1i3bt3hmlWSvvjiC4YNO/rf8QMHDtC7d2+aN29Oq1ateOSRRw5vc84xY8YMZsyYcTjTF198wfLlyw/v88ADDxyuvSerJJ4BrQdszPM6iaMLLgBmNsjMEswsYfv27Sf3rilJxRsXERGftmzZMrKysmjcuPFR26Kiopg4cSJLlizh3Xff5frrrwdg8uTJPPbYYxw4cIAxY8bw4osvAvkb0JCQELp3784nn3xSNr8IYGYNgfbAL0dsKqxmFqmWqo6KiPiHoKAgnnvuOZYvX86cOXMYNWoUs2fPZuDAgWzcuJEff/yR22+/vcTf9+mnn+bOO+8scNsDDzzAypUrWbBgAbNnz+abb74hNTWVm266iWXLlrF06VJuuukmUlNTj2pA//a3vzFixIgSyVimkxA550Y75+Kdc/HR0dEnd7KImOKNi4hIqfnggw/o1KkT7dq1469//Svr16+nadOm7Nixg+zsbLp06cLkyZNZt24dzZs359prr6VFixb069ePAwcOAPDhhx/St2/fAs/fvn176tatC0CrVq1ITU0lPT2dnj170rNnT1566SWSk5O57777eOSRR0hNTaVdu3Zce+21AFx66aV8+OGHZfK3MLMqwGfAvc65PSV5btVRERH/UKdOHTp0yHnMv2rVqrRo0YIDBw4wfPhw3nrrLT7++GNeey3nycU1a9bQq1cvOnbsSJcuXVi5ciUAffv25b333gPgjTfeOFzTunbtyj333EO7du1o3bo1c+fOBWD16tWEhoYSFRV1VJ5KlSrRrVs3IOeD2Q4dOpCUlER4eDivvfYaY8eO5e233+a1115jwYIFTJgwgQcffJB27dqxZs0aGjRoQHJyMn/88cdJ/21KYhmWTUBsntcxuWOlq/uQnGdV8tw+lBkYRlD3IaX+1iIivupfE5exfHOJ9jy0rFuNf/ZpVej2FStW8MknnzB79myCg4O58847+eGHH3j44Ye544476NSpEy1btqRHjx6sW7eOVatW8dZbb9G5c2duueUWXn31VR544AFmz57NgAEDjpvns88+o0OHDoSGhjJlyhRmzJjB4MGDiYyM5KWXXmLEiBG88sorLFy48PAxrVu3Zt68eSXy9zgWMwsmp/n80Dn3vwJ2KaxmbgK6HjE+o3RS5iqgjmYHhROgOioi5YgXdfFI69atY8GCBTRr1ozHH3+cW265hUaNGnHXXXfx2muvMWjQIF5//XWaNm3KL7/8wp133sm0adMYPXo0nTt3plGjRjz33HPMmTPn8DkPHDjAwoULmTlzJrfccgtLly5l9uzZh5veY9m9ezcTJ07knnvuITU1lbvuuoubb74ZgLvuuotXX32VSy65hIsvvph+/fodPq5Dhw7Mnj2bK664ohh/raOVRAM6AbjbzD4mZ/KEFOfclhI477HF9c/5PnUYLiWJrRbF+2E38ECbK9GcVyIiZWfq1KnMnz+f008/HYDU1FRq1arF0KFD+fTTT3n99dfzNYOxsbF07twZgOuuu46RI0fywAMPsGXLFo53VW/ZsmU8/PDDTJ48GYDzzz+fCy64gKFDhzJw4MBCn6cJDAwkJCSEvXv3UrVq1ZL4tY9iOdMAvgWscM49X8huBdZMM/sOeDLPxEM9gH+UStBDjqijW1wk30f/lRsOjYuIyEnbt28fV1xxBS+++CL169fnzTff5J133qFLly5cd9117Nu3j59++okrr7zy8DHp6ekA1K5dm2HDhtGtWzc+//xzataseXifQx/YnnvuuezZs4fdu3cXqY5mZmYyYMAABg8efPiRl7Fjx/LDDz8AOQ1oYbPa1qpVi82bN5/4HyPXcRtQM/uInE9lo8wsiZxZ+oIBnHOvA5OAi4BE4ABw80mnKqq4/hDXHwOm/bKBUZ8vofPaZM5ucvRlZxGRiqA4n8iWFOccN954I0899VS+8QMHDpCUlPM84b59+w43fkcWtkOvw8PDD68j9vnnn/Ovf/0LgDFjxhAfH09SUhKXXXYZ7733Hk2aNMl37KFJiI41FXx6ejphYWEn86seT2fgemCJmR3quB8F6sOxa6ZzbqeZPQEcukw7zDl3rAkAS0aeOvr218sZO3sdf9l1gJgalUr9rUVEyoIXdfGQjIwMrrjiCq699louv/zyw+M33XTT4Z+zs7OpXr16vg9q81qyZAmRkZFHNX4F1dLw8HBSUlIAyMrKomPHjgBccsklhycmGjRoEE2bNuXee+/Nd2zXrl2P+/ukpaURHh5+3P2Opyiz4A5wztVxzgU752Kcc285517PLaTkzuR3l3OuiXOujXMu4aRTnYDLO9QjqkoIb848evZEEREpPd27d2f8+PFs27YNgJ07d7J+/Xoefvhhrr32WoYNG8Ztt912eP8NGzbw888/A/Df//6Xc845B4AWLVqQmJgIwGWXXcbChQtZuHAh8fHx7N69m969ezNixIjDV0+PJTg4mIyMjMOvk5OTiYqKIjg4uMR+7yM55350zplzLs451y73a1JRa6Zzbqxz7tTcr7dLLWghbu7cKKcRnb2urN9aRKTccc5x66230qJFC/7+978Xul+1atVo1KgRn3766eHjFi1aBMDcuXP55ptvWLBgAc8++yy///774eMOTaz3448/EhERQURERL46GhgYeLiOHmo+H3/8cVJSUg5P2HcsVatWZe/evfnGVq9eTevWrYvxVyhYmU5CVJrCggO54ayGTF+1ndVb9x7/ABERKREtW7bk3//+Nz169CAuLo4LLriAdevWMW/evMNNaEhICG+/ndNTNWvWjFGjRtGiRQt27drFHXfcAUDv3r2ZMWNGge/xyiuvkJiYyLBhw2jXrh3t2rU73PAWZNCgQcTFxR2esGH69On07t27ZH/xcqZu9XD6tK3Lx3M3kJKacfwDRESkULNnz+b9999n2rRph+vWpEmTCtz3ww8/5K233qJt27a0atWKL7/8kvT0dG677TbGjh1L3bp1ee6557jlllsOP2oSFhZG+/btuf3223nrrbeAnNtxFyxYUODjKElJSQwfPpzly5fToUMH2rVrx5gxYwrNf/XVV/PMM8/Qvn171qxZQ0ZGBomJicTHx5/038ZKY/2ZooiPj3cJCSV7sXTX/oOcNWIqfeLq8syVbUv03CIivmrFihW0aNHC6xhFsm7dOi6++GKWLl161LbU1FS6devG7NmzCQwMLNH3vfzyyxkxYgSnnXZagdsL+hua2Xzn3MlX2lJSGnV0+eY9XDRyFg/3as4dXZuU6LlFRMqKP9XFE9G1a1eeffbZApvBe+65hz59+nD++eeX6Ht+/vnn/PrrrzzxxBMFbi9OHS03V0ABalQOoX98LF8s3MTWPWlexxERkWIIDw/nX//6F5s2lexE6gcPHuTSSy8ttPmUP7WsW40uTaN4e/bvpGdmeR1HRESK6dFHHz28vFlJyszM5P777y+Rc5WrBhRg4DmNycp2eoZFRMQHNWzYsMCrn4f07NmT+vXrl+h7hoSEcMMNN5ToOcuz27o0ZtvedL5cePIzHYqISMmbMWNGobfC1q5dm0suuaTE3/PKK6+kevXqJXKucteA1o+sxIWt6/DhL+vZl57pdRwRERG/0qVpFM1PqcqbM9eSne3NYzoiIlJ+lbsG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czu9f7cCT5FurSi0YZfBOdeYUfcvgdPznbcARlLDZQw4ZSIg22A4uZYxCOs/L2a2kPRn52LnXGPv8Iz1vxe4l8Kf/pcX59xB59wh7/kfgWIzm0qItoFnqO+Ar9vAzIpJh+adzrl7skwS+u/BBFGO4vtvuHIU37fBcEL/+6EcDQzlaD6cjxfEDvcgfYR2K+nTOXovPD5pwDRfo3/nCb/znp9E/84TtlL4zhPyqf+DpC+unj9geCUQ955PBd7En4uu81mGGRnPPws8745ctPy2tyyV3vOqoNXvTXcC6YvELWjbwHv/OeS+cP9T9L9o/MWgrP88659N+tqyMwcMLwXKM56vBs73o/48lmF672eHdLC8422PvD5/ftfvjU+Rvr6lNGjbwFuXvwF+MsQ0gf8e+LTdlaNOOTrR9XvTKUf9q1856nP93njlaL71+PUhHMEKu5B0T01vATd4w24m3coJkAB+733xXgTmZsx7gzffG8AFAa3/T8Ae4BXvsdIbfiaw3vuirQeWBngbfB/Y6NX6JHBCxrxf9LbNFuDvgli/93o5cMuA+QKxDUi3pO0Cukifd78U+CrwVW+8AT/zlm89sChg63+4+n8J7M/4Dqzxhs/11v2r3ufrBj/qz3MZrs74DjxPxn8Csn3+gla/N80VpDucyZwvENuA9OlkDliX8Tm5MEzfAz8fw/0GohwNwjIoRye2fuWocnRC6/emuQLlaF6P3pYGERERERERkQkV9GtARUREREREZJLQDqiIiIiIiIgUhHZARUREREREpCC0AyoiIiIiIiIFoR1QERERERERKQjtgIqEnJlVmNlVftchIiISRspRkcLSDqhI+FUACk4REZHRUY6KFJB2QEXC7xbgODN7xcx+6HcxIiIiIaMcFSkgc875XYOIjIGZzQFWOedO9rkUERGR0FGOihSWjoCKiIiIiIhIQWgHVERERERERApCO6Ai4dcMlPtdhIiISEgpR0UKSDugIiHnnGsEnjOzDeo8QUREZGSUoyKFpU6IREREREREpCB0BFREREREREQKQjugIiIiIiIiUhDaARUREREREZGC0A6oiIiIiIiIFIR2QEVERERERKQgtAMqIiIiIiIiBaEdUBERERERESmI/weLkB7PdBETzQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Solve #################################\n", + "t_eval = np.linspace(0, 2, 30)\n", + "solution = dae_solver.solve(model, t_eval)\n", + "#########################################\n", + "\n", + "# Extract u and v\n", + "t_sol = solution.t\n", + "u = solution[\"u\"]\n", + "v = solution[\"v\"]\n", + "\n", + "# Plot\n", + "t_fine = np.linspace(0,t_eval[-1],1000)\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", + "ax1.plot(t_fine, np.exp(-2 * t_fine), t_sol, u(t_sol), \"o\")\n", + "ax1.set_xlabel(\"t\")\n", + "ax1.legend([\"exp(-2*t)\", \"u\"], loc=\"best\")\n", + "\n", + "ax2.plot(t_fine, 2 * np.exp(-2 * t_fine), t_sol, v(t_sol), \"o\")\n", + "ax2.set_xlabel(\"t\")\n", + "ax2.legend([\"2*exp(-2*t)\", \"v\"], loc=\"best\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that, where possible, the solver makes use of the mass matrix and jacobian for the model. However, the discretisation or solver will have created the mass matrix and jacobian algorithmically, using the expression tree, so we do not need to calculate and input these manually." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The solution terminates at the final simulation time:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'final time'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.termination" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Events\n", + "\n", + "It is possible to specify events at which a solution should terminate. This is done by adding events to the `model.events` dictionary. In the following example, we solve the same model as before but add a termination event when `v=-0.2`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 2 dimension(s)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;31m# Solve #################################\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mt_eval\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlinspace\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m30\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0msolution\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdae_solver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mt_eval\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;31m#########################################\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/mnt/c/Users/vsulzer/Documents/Energy_Storage/PyBaMM/pybamm/solvers/casadi_solver.py\u001b[0m in \u001b[0;36msolve\u001b[0;34m(self, model, t_eval, external_variables, inputs)\u001b[0m\n\u001b[1;32m 178\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 179\u001b[0m \u001b[0;31m# Calculate more exact termination reason\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 180\u001b[0;31m \u001b[0msolution\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtermination\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_termination_reason\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msolution\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevents\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 181\u001b[0m pybamm.logger.info(\n\u001b[1;32m 182\u001b[0m \u001b[0;34m\"Finish solving {} ({})\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msolution\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtermination\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/mnt/c/Users/vsulzer/Documents/Energy_Storage/PyBaMM/pybamm/solvers/base_solver.py\u001b[0m in \u001b[0;36mget_termination_reason\u001b[0;34m(self, solution, events)\u001b[0m\n\u001b[1;32m 571\u001b[0m \u001b[0mfinal_event_values\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 572\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mevent\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mevents\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 573\u001b[0;31m \u001b[0my_event\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0madd_external\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msolution\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my_event\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my_pad\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0my_ext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 574\u001b[0m final_event_values[name] = abs(\n\u001b[1;32m 575\u001b[0m \u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevaluate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msolution\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mt_event\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_event\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/mnt/c/Users/vsulzer/Documents/Energy_Storage/PyBaMM/pybamm/solvers/base_solver.py\u001b[0m in \u001b[0;36madd_external\u001b[0;34m(y, y_pad, y_ext)\u001b[0m\n\u001b[1;32m 587\u001b[0m \"\"\"\n\u001b[1;32m 588\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0my_pad\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0my_ext\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 589\u001b[0;31m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconcatenate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_pad\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my_ext\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 590\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 591\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m<__array_function__ internals>\u001b[0m in \u001b[0;36mconcatenate\u001b[0;34m(*args, **kwargs)\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 2 dimension(s)" + ] + } + ], + "source": [ + "# Create model\n", + "model = pybamm.BaseModel()\n", + "u = pybamm.Variable(\"u\")\n", + "v = pybamm.Variable(\"v\")\n", + "model.rhs = {u: -v} # du/dt = -v\n", + "model.algebraic = {v: 2 * u - v} # 2*v = u\n", + "model.initial_conditions = {u: 1, v: 2}\n", + "model.events['v=0.2'] = v - 0.2 # adding event here\n", + "model.variables = {\"u\": u, \"v\": v}\n", + "\n", + "# Discretise using default discretisation\n", + "disc = pybamm.Discretisation()\n", + "disc.process_model(model)\n", + "\n", + "# Solve #################################\n", + "t_eval = np.linspace(0, 2, 30)\n", + "solution = dae_solver.solve(model, t_eval)\n", + "#########################################\n", + "\n", + "# Extract u and v\n", + "t_sol = solution.t\n", + "u = solution[\"u\"]\n", + "v = solution[\"v\"]\n", + "\n", + "# Plot\n", + "t_fine = np.linspace(0,t_eval[-1],1000)\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", + "ax1.plot(t_fine, np.exp(-2 * t_fine), t_sol, u(t_sol), \"o\")\n", + "ax1.set_xlabel(\"t\")\n", + "ax1.legend([\"exp(-2*t)\", \"u\"], loc=\"best\")\n", + "\n", + "ax2.plot(t_fine, 2 * np.exp(-2 * t_fine), t_sol, v(t_sol), \"o\", t_fine, 0.2 * np.ones_like(t_fine), \"k\")\n", + "ax2.set_xlabel(\"t\")\n", + "ax2.legend([\"2*exp(-2*t)\", \"v\", \"v = 0.2\"], loc=\"best\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the solution terminates because the event has been reached" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "solution.termination" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Finding consistent initial conditions\n", + "\n", + "The solver will fail if initial conditions that are inconsistent with the algebraic equations are provided. However, during set up the DAE solvers automatically use `calculate_consistent_initial_conditions` to obtain consistent initial conditions, starting from a guess of bad initial conditions, using a simple root-finding algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "y0_guess=[1. 1.]\n", + "y0_fixed=[1. 2.]\n" + ] + } + ], + "source": [ + "# Create model\n", + "model = pybamm.BaseModel()\n", + "u = pybamm.Variable(\"u\")\n", + "v = pybamm.Variable(\"v\")\n", + "model.rhs = {u: -v} # du/dt = -v\n", + "model.algebraic = {v: 2 * u - v} # 2*v = u\n", + "model.initial_conditions = {u: 1, v: 1} # bad initial conditions, solver fixes\n", + "model.events['v=0.2'] = v - 0.2\n", + "model.variables = {\"u\": u, \"v\": v}\n", + "\n", + "# Discretise using default discretisation\n", + "disc = pybamm.Discretisation()\n", + "disc.process_model(model)\n", + "\n", + "print(f\"y0_guess={model.concatenated_initial_conditions.flatten()}\")\n", + "dae_solver.set_up(model)\n", + "print(f\"y0_fixed={model.y0}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/notebooks/solvers/ode-solver.ipynb b/examples/notebooks/solvers/ode-solver.ipynb new file mode 100644 index 0000000000..834332249f --- /dev/null +++ b/examples/notebooks/solvers/ode-solver.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ODE solver\n", + "\n", + "In this notebook, we show some examples of solving an ODE model. For the purposes of this example, we use the Scipy solver, but the syntax remains the same for other solvers" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup\n", + "import pybamm\n", + "import tests\n", + "import numpy as np\n", + "import os\n", + "import matplotlib.pyplot as plt\n", + "from pprint import pprint\n", + "os.chdir(pybamm.__path__[0]+'/..')\n", + "\n", + "# Create solver\n", + "ode_solver = pybamm.ScipySolver()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Integrating ODEs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In PyBaMM, a model is solved by calling a solver with `solve`. This sets up the model to be solved, and then calls the method `_integrate`, which is specific to each solver. We begin by setting up and discretising a model" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Create model\n", + "model = pybamm.BaseModel()\n", + "u = pybamm.Variable(\"u\")\n", + "v = pybamm.Variable(\"v\")\n", + "model.rhs = {u: -v, v: u}\n", + "model.initial_conditions = {u: 2, v: 1}\n", + "model.variables = {\"u\": u, \"v\": v}\n", + "\n", + "# Discretise using default discretisation\n", + "disc = pybamm.Discretisation()\n", + "disc.process_model(model);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the model can be solved by calling `solver.solve` with a specific time vector at which to evaluate the solution" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzdd3gV1drG4d+kJ6TRWwJEihBCIBBARY8ons+KgkIQQUVB1KMHxYoNEMUK0lFRFBVFItjAegQUUSkBktB7SaghkBDSy3x/bEACiZTsZHZ57uvyipnJnv1gZM+8a9a8yzBNExEREREREZHK5mF1ABEREREREXEPKkBFRERERESkSqgAFRERERERkSqhAlRERERERESqhApQERERERERqRJeVrxprVq1zCZNmljx1iIiIudt5cqVh0zTrG1lBp07RUTEmZR37rSkAG3SpAkJCQlWvLWIiMh5Mwxjl9UZdO4UERFnUt65U1NwRUREREREpEqoABUREREREZEqoQJUREREREREqoQlz4CKiIiIiIh7KiwsJDU1lby8PKujiB34+fkRFhaGt7f3Of28ClAREREREakyqampBAUF0aRJEwzDsDqOVIBpmqSnp5OamkpERMQ5vUZTcEVEREREpMrk5eVRs2ZNFZ8uwDAMataseV53sytcgBqGEW4YxiLDMNYbhrHOMIxHKnrMc5IcD+OiYGSo7WtyfJW8rYiIiLgxXX+I2IWKT9dxvr9Le0zBLQIeN01zlWEYQcBKwzD+Z5rmejscu2zJ8TBvCBTm2r7PTLF9DxAdV2lvKyIiIm7sQq8/kuNhwSjITIWQMOg2XNcrIuK2KlyAmqa5D9h3/N+zDMPYADQEKq8AXTDq7w//Ewpzbdv1gS4iIiKVoOSXF/Eo4/rjyLznGb3pYkzT9jyUp4dBkJ83gX5etDvyM//a9BJexcenp2nQXETcnF2fATUMowkQAywrY99gwzASDMNISEtLq9gbZaaWudksZ7uIiIjIGcqZTmuaJtvTjvHlqlRenr+evtOWEjPq53KvP0IKD/Ln1kMs3Z7Osh2HWbwljfiEFCYu2EKLteP+Lj5POF60Tl+yg1W7j1BYXFLZf1IROUVKSgpXXXUVkZGRtG7dmgkTJpTaP2PGDHbu3IlpmuUe49tvv+W1114763vt27ePm266CYDExES+//77k/vmz5/P8OHDL/BPYX9VldX4p/+w53UgwwgEfgNGm6b55T/9bGxsrJmQkHDhbzYuyjaCeJqDHnXI+U8iTWpVu/Bji4iInMYwjJWmacZamaHC504p7fTptECRhx+f1nmcKentOZiVD4Cvlwct6wXRqn4wz22OIyh/35nHCgmHoWvP2FxcYuIxqjoGZ15rlWBwUd6nAPh7e9K+cSiXRNTkqpZ1aN0g2PZMlabuiovasGEDrVq1suz99+3bx759+2jfvj1ZWVl06NCBr7/+mpCQEEaMGEHjxo2JiIjg999/5913363Qez355JNcfvnl3HLLLcyYMYOEhAQmT54M2Aa72rdvzx9//EFAQEC5xxgwYAADBgyga9euFcpSmVnL+p2Wd+60yzIshmF4A3OBT89WfNpFt+FnnjQ8/Rhb3IdvJ/zOqFta0zs2vNJjiIiIiJMq43Eer5I8/m//u6y8eA6XXFST2CbVuahWNbw8j08YSx51xvUH3v6265IyeHoYtsKxjEFzj5Awlj/WjZW7jrBsx2FW7DzMW79sZuz/NlM/xI+hdRK5be+beBar34W4thfnrWP93qN2PWZkg2BGdG9d7v769etTv359AIKCgmjVqhV79uwhMjKS0aNH07lzZ6Kiovj2228BmDhxIu+88w5eXl5ERkby+eeflyrQBgwYQHBwMAkJCezfv5833niDXr16ATB37lxefvllCgoKGD58OLm5uSxZsoRnnnmGPn360LVrV+bPn09cXMX+Xv/44488++yzFBcXU6tWLRYsWMDhw4e599572b59OwEBAUybNo3o6Gh+++03HnnE1jfWMAwWL15MUFBQlWWtcAFq2NoeTQc2mKb5VkWPd05OfPCeMiro1W04jzbuzq7ZiTw5J5m/tqfz0i1RVPPVUqciIiJiczSvkHlJe+mbmVrmc0j1SGdi35iyX1zG9cdZ70qWMWh+omitE+zH9W3qc30b24XwoWP5LNx4kAUbDtBl61Q8DfW7EKlsO3fuZPXq1XTu3Jm9e/cyYsQI7r33XiIiInjooYd4++23ee2119ixYwe+vr5kZGSUeZx9+/axZMkSNm7cyM0330yvXr3YsWMH1atXx9fXF4BRo0aVuqsIEBsby++//16hoi4tLY377ruPxYsXExERweHDhwEYMWIEMTExfP311yxcuJC77rqL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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Solve ########################\n", + "t_eval = np.linspace(0, 5, 30)\n", + "solution = ode_solver.solve(model, t_eval)\n", + "################################\n", + "\n", + "# Extract u and v\n", + "t_sol = solution.t\n", + "u = solution[\"u\"]\n", + "v = solution[\"v\"]\n", + "\n", + "# Plot\n", + "t_fine = np.linspace(0,t_eval[-1],1000)\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", + "ax1.plot(t_fine, 2 * np.cos(t_fine) - np.sin(t_fine), t_sol, u(t_sol), \"o\")\n", + "ax1.set_xlabel(\"t\")\n", + "ax1.legend([\"2*cos(t) - sin(t)\", \"u\"], loc=\"best\")\n", + "\n", + "ax2.plot(t_fine, 2 * np.sin(t_fine) + np.cos(t_fine), t_sol, v(t_sol), \"o\")\n", + "ax2.set_xlabel(\"t\")\n", + "ax2.legend([\"2*sin(t) + cos(t)\", \"v\"], loc=\"best\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that, where possible, the solver makes use of the mass matrix and jacobian for the model. However, the discretisation or solver will have created the mass matrix and jacobian algorithmically, using the expression tree, so we do not need to calculate and input these manually." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The solution terminates at the final simulation time:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'final time'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.termination" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Events\n", + "\n", + "It is possible to specify events at which a solution should terminate. This is done by adding events to the `model.events` dictionary. In the following example, we solve the same model as before but add a termination event when `v=-2`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzde3yP9f/H8ce188YOzqcNy9lmthkqHZT6dVQok6gU6azopMMXKR0VKRURlZJFKjqHQgnDNuezbI4zNjsfr98foxy2HPbZrs/heb/d3GbXZ5/reqI+1/V6X+/r9TZM00RERERERESksrlZHUBERERERERcgwpQERERERERqRIqQEVERERERKRKqAAVERERERGRKqECVERERERERKqEhxUHrV27ttm0aVMrDi0iInLOVq1adcg0zTpWZtC5U0REHEl5505LCtCmTZsSHx9vxaFFRETOmWEYf1udQedOERFxJOWdOzUFV0RERERERKqEClARERERERGpEipARUREREREpEpY8gyoiIiIiIi4psLCQlJSUsjLy7M6itiAj48PwcHBeHp6ntXPqwAVEREREZEqk5KSgr+/P02bNsUwDKvjSAWYpklaWhopKSmEhoae1Xs0BVdERERERKpMXl4etWrVUvHpBAzDoFatWud0N7vCBahhGCGGYSwyDGODYRjrDcN4tKL7PCtJcTAuHEYFlX5NiquSw4qIiIgL0/WHiE2o+HQe5/pvaYspuEXA46ZprjYMwx9YZRjGL6ZpbrDBvsuWFAfzhkBhbun3Gcml3wNExFbaYUVERMSFne/1R1IcLBgNGSkQGAzdRuh6RURcVoULUNM09wH7jv0+0zCMjUAjoPIK0AWj//3wP64wt3S7PtBFRESkEpT8+gJuZVx/HJn3PGM2t8I0S5+Hcncz8PfxpLqPB5FHfuayzS/iUXxsepoGzUXExdn0GVDDMJoCUcDyMl4bbBhGvGEY8ampqRU7UEZKmZvNcraLiIiInKac6bSmabIjNYuvVqfw0vwN9J38F1Gjfy73+iOw8CB/bjvEXzvSWL7zMIu3phIXn8yEBVtpuW7cv8XncceK1qlLd7J69xEKi0sq+08qIidITk7miiuuoG3btoSFhfH222+f9Pr06dPZtWsXpmmWu49vv/2WV1999YzH2rdvHzfeeCMA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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Create model\n", + "model = pybamm.BaseModel()\n", + "u = pybamm.Variable(\"u\")\n", + "v = pybamm.Variable(\"v\")\n", + "model.rhs = {u: -v, v: u}\n", + "model.initial_conditions = {u: 2, v: 1}\n", + "model.events['v=-2'] = v + 2 # New termination event\n", + "model.variables = {\"u\": u, \"v\": v}\n", + "\n", + "# Discretise using default discretisation\n", + "disc = pybamm.Discretisation()\n", + "disc.process_model(model)\n", + "\n", + "# Solve ########################\n", + "t_eval = np.linspace(0, 5, 30)\n", + "solution = ode_solver.solve(model, t_eval)\n", + "################################\n", + "\n", + "# Extract u and v\n", + "t_sol = solution.t\n", + "u = solution[\"u\"]\n", + "v = solution[\"v\"]\n", + "\n", + "# Plot\n", + "t_fine = np.linspace(0,t_eval[-1],1000)\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", + "ax1.plot(t_fine, 2 * np.cos(t_fine) - np.sin(t_fine), t_sol, u(t_sol), \"o\")\n", + "ax1.set_xlabel(\"t\")\n", + "ax1.legend([\"2*cos(t) - sin(t)\", \"u\"], loc=\"best\")\n", + "\n", + "ax2.plot(t_fine, 2 * np.sin(t_fine) + np.cos(t_fine), t_sol, v(t_sol), \"o\", t_fine, -2 * np.ones_like(t_fine), \"k\")\n", + "ax2.set_xlabel(\"t\")\n", + "ax2.legend([\"2*sin(t) + cos(t)\", \"v\", \"v = -2\"], loc=\"best\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the solution terminates because the event has been reached" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'event: v=-2'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solution.termination" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "event time: [3.78510677] \n", + "event state [-0.99995359 -2. ]\n" + ] + } + ], + "source": [ + "print(\"event time: \", solution.t_event, \"\\nevent state\", solution.y_event.flatten())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/notebooks/solvers/scikits-dae-solver.ipynb b/examples/notebooks/solvers/scikits-dae-solver.ipynb deleted file mode 100644 index c53888e251..0000000000 --- a/examples/notebooks/solvers/scikits-dae-solver.ipynb +++ /dev/null @@ -1,357 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Scikits DAE solver\n", - "\n", - "In this notebook, we show some examples of solving a DAE model using the Scikits DAE solver, which interfaces with the [SUNDIALS](https://computation.llnl.gov/projects/sundials) library via the [scikits-odes](https://scikits-odes.readthedocs.io/en/latest/) Python interface" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Setup\n", - "import pybamm\n", - "import tests\n", - "import numpy as np\n", - "import os\n", - "import matplotlib.pyplot as plt\n", - "from pprint import pprint\n", - "os.chdir(pybamm.__path__[0]+'/..')\n", - "\n", - "# Create solver\n", - "dae_solver = pybamm.ScikitsDaeSolver()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Integrating DAEs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the simplest case, the `integrate` method of the DAE solver needs to be passed a function that returns residuals given `(t,y,ydot)`, initial conditions `y0`, and a time `t_eval` at which to return the solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOzdd3RVVfrG8e+bHiAECKGYAqE3qQFxQIqMgAVRUQbEioh1dHSK/kYHlZlxGBV7G0fsSrGLYqUJKCVCpEoRAgmghBY6afv3RwITICEJ3OTkJs9nrazcs8++9zxhzfKd95xz9zHnHCIiIiIiIiJlLcDrACIiIiIiIlI1qAEVERERERGRcqEGVERERERERMqFGlAREREREREpF2pARUREREREpFwEeXXgunXrusaNG3t1eBERkZP64Ycftjvnor3OURTVURERqciKqqOeNaCNGzcmKSnJq8OLiIiclJlt9DrDyaiOiohIRVZUHdUtuCIiIiIiIlIu1ICKiIiIiIhIuVADKiIiIiIiIuXCs++AiohI2crKyiItLY1Dhw55HaVCCwsLIzY2luDgYK+jiIiIB1QvT09p66gaUBGRSiotLY2IiAgaN26MmXkdp0JyzrFjxw7S0tJISEjwOo6IiHhA9fLUnUod1S24IiKV1KFDh4iKilIxPQkzIyoqSme9RUSqMNXLU3cqdbTYBtTMXjGzbWa2vIj9ZmZPm9k6M1tqZp1Lkfn0LJ0CT7SDB2vl/V46pdwOLSLiD1RMi+erfyMzizOzmWa20sxWmNmdhcwpsmaa2bVmtjb/51qfhCqO6qiICKB6eTpK+29XkiugrwEDT7L/fKB5/s9o4IVSJThVS6fA1DsgIxVweb+n3qHiKSIiXskG/uicawN0B24zszbHzSm0ZppZHeAB4CygG/CAmdUu07SqoyIi4oFiG1Dn3LfAzpNMGQy84fLMB2qZWUNfBSzS9LGQdfDYsayDeeMiIlKpXH755axfv/6E8eTkZKZNm3Z0+9NPP2XMmDHlGe0o59xW59zi/Nd7gVVAzHHTiqqZA4CvnXM7nXO7gK85+cnf06c6KiJSIaSmptK3b1/atGlD27Zteeqpp47ue+2110hJScE55/PjfvTRR4wdW/h/8x9++OGjrzMzM+nVqxfZ2dk+Oa4vvgMaA6QW2E7jxIILgJmNNrMkM0tKT08/vaNmpJVuXERE/NKKFSvIycmhSZMmJ+w7vgG98MILmTp1KgcOHCjPiCcws8ZAJ2DBcbuKqpklqqWqoyIilU9QUBDjx49n5cqVzJ8/n+eee4558+YxatQoUlNTmTt3LjfffLPPj/vII49w6623FrqvYAMaEhJCv379mDx5sk+OW66LEDnnXnLOJTrnEqOjo0/vwyJjSzcuIiLl7q233qJbt2507NiRm266iY0bN9K8eXO2b99Obm4u55xzDl999RUpKSm0atWKESNG0Lp1ay6//PKjTeTbb7/N4MGDT/jszMxMxowZw+TJk+nYsSOTJ0/GzOjTpw+ffvppef+pR5lZDeB94A/OuT2+/GzVURGRyqdhw4Z07py3JEBERAStW7fmwIED/POf/2TChAlMmjSJF17I+5bjzz//zMCBA+nSpQvnnHMOP/30EwCDBw/mjTfeAOA///kPI0aMAKBPnz7ceeeddOzYkXbt2rFw4UIA1qxZQ2hoKHXr1j0hz7333svBgwfp2LHj0c+55JJLePvtt33y9/riMSybgbgC27H5Y2Wr35i876oUuH0oNyicgH7e3HolIlKRPTR1BSu3+LQXos0ZNXlgUNsi969atYrJkyczb948goODufXWW5k9ezb33HMPt9xyC926daNNmzb079+flJQUVq9ezYQJE+jRowcjR47k+eef509/+hPz5s1j+PDhJ3x+SEgIY8eOJSkpiWefffboeGJiInPmzGHo0KE+/XtLwsyCyWs+33bOfVDIlKJq5magz3Hjs8omZb5C6qgLDsdUR0WkCvOiXhaUkpLCkiVLaNmyJffffz8jR44kISGB2267jRdeeIHRo0fz4osv0rx5cxYsWMCtt97KjBkzeOmll+jRowcJCQmMHz+e+fPnH/3MAwcOkJyczLfffsvIkSNZvnw58+bNO9r0Hm/cuHE8++yzJCcnHx1r164dixYtOr1/iHy+uAL6CXBN/sp+3YEM59xWH3zuybUfCoOehsg4HEZabl2+aXZf3riIiHhu+vTp/PDDD3Tt2pWOHTsyffp01q9fz6hRo9izZw8vvvgijz322NH5cXFx9OjRA4CrrrqKuXPnArB161ZKc7WvXr16bNmyxbd/TAlY3jKAE4BVzrnHi5hWVM38EuhvZrXzFx/qnz9Wdgqpo8kdx6qOioh4ZN++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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def exponential_decay_dae(t, y, ydot):\n", - " return [-y[0] - ydot[0], 2 * y[0] - y[1]]\n", - "\n", - "# Solve\n", - "y0 = np.array([1, 2])\n", - "t_eval = np.linspace(0, 5, 20)\n", - "solution = dae_solver.integrate(exponential_decay_dae, y0, t_eval)\n", - "\n", - "# Plot\n", - "t_fine = np.linspace(0,t_eval[-1],1000)\n", - "\n", - "def plot(t_sol, y_sol): \n", - " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", - " ax1.plot(t_fine, np.exp(-t_fine), t_sol, y_sol[0], \"o\")\n", - " ax1.set_xlabel(\"t\")\n", - " ax1.legend([\"exp(-t)\", \"y_sol[0]\"], loc=\"best\")\n", - "\n", - " ax2.plot(t_fine, 2*np.exp(-t_fine), t_sol, y_sol[1], \"o\")\n", - " ax2.set_xlabel(\"t\")\n", - " ax2.legend([\"2*exp(-t)\", \"y_sol[1]\"], loc=\"best\")\n", - "\n", - " plt.tight_layout()\n", - " plt.show()\n", - " \n", - "plot(solution.t, solution.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also provide the mass matrix and Jacobian (both must be provided together)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOzdd3RVVfrG8e+bHiAECKGYAqE3qQFxQIqMgAVRUQbEioh1dHSK/kYHlZlxGBV7G0fsSrGLYqUJKCVCpEoRAgmghBY6afv3RwITICEJ3OTkJs9nrazcs8++9zxhzfKd95xz9zHnHCIiIiIiIiJlLcDrACIiIiIiIlI1qAEVERERERGRcqEGVERERERERMqFGlAREREREREpF2pARUREREREpFwEeXXgunXrusaNG3t1eBERkZP64Ycftjvnor3OURTVURERqciKqqOeNaCNGzcmKSnJq8OLiIiclJlt9DrDyaiOiohIRVZUHdUtuCIiIiIiIlIu1ICKiIiIiIhIuVADKiIiIiIiIuXCs++AiohI2crKyiItLY1Dhw55HaVCCwsLIzY2luDgYK+jiIiIB1QvT09p66gaUBGRSiotLY2IiAgaN26MmXkdp0JyzrFjxw7S0tJISEjwOo6IiHhA9fLUnUod1S24IiKV1KFDh4iKilIxPQkzIyoqSme9RUSqMNXLU3cqdbTYBtTMXjGzbWa2vIj9ZmZPm9k6M1tqZp1Lkfn0LJ0CT7SDB2vl/V46pdwOLSLiD1RMi+erfyMzizOzmWa20sxWmNmdhcwpsmaa2bVmtjb/51qfhCqO6qiICKB6eTpK+29XkiugrwEDT7L/fKB5/s9o4IVSJThVS6fA1DsgIxVweb+n3qHiKSIiXskG/uicawN0B24zszbHzSm0ZppZHeAB4CygG/CAmdUu07SqoyIi4oFiG1Dn3LfAzpNMGQy84fLMB2qZWUNfBSzS9LGQdfDYsayDeeMiIlKpXH755axfv/6E8eTkZKZNm3Z0+9NPP2XMmDHlGe0o59xW59zi/Nd7gVVAzHHTiqqZA4CvnXM7nXO7gK85+cnf06c6KiJSIaSmptK3b1/atGlD27Zteeqpp47ue+2110hJScE55/PjfvTRR4wdW/h/8x9++OGjrzMzM+nVqxfZ2dk+Oa4vvgMaA6QW2E7jxIILgJmNNrMkM0tKT08/vaNmpJVuXERE/NKKFSvIycmhSZMmJ+w7vgG98MILmTp1KgcOHCjPiCcws8ZAJ2DBcbuKqpklqqWqoyIilU9QUBDjx49n5cqVzJ8/n+eee4558+YxatQoUlNTmTt3LjfffLPPj/vII49w6623FrqvYAMaEhJCv379mDx5sk+OW66LEDnnXnLOJTrnEqOjo0/vwyJjSzcuIiLl7q233qJbt2507NiRm266iY0bN9K8eXO2b99Obm4u55xzDl999RUpKSm0atWKESNG0Lp1ay6//PKjTeTbb7/N4MGDT/jszMxMxowZw+TJk+nYsSOTJ0/GzOjTpw+ffvppef+pR5lZDeB94A/OuT2+/GzVURGRyqdhw4Z07py3JEBERAStW7fmwIED/POf/2TChAlMmjSJF17I+5bjzz//zMCBA+nSpQvnnHMOP/30EwCDBw/mjTfeAOA///kPI0aMAKBPnz7ceeeddOzYkXbt2rFw4UIA1qxZQ2hoKHXr1j0hz7333svBgwfp2LHj0c+55JJLePvtt33y9/riMSybgbgC27H5Y2Wr35i876oUuH0oNyicgH7e3HolIlKRPTR1BSu3+LQXos0ZNXlgUNsi969atYrJkyczb948goODufXWW5k9ezb33HMPt9xyC926daNNmzb079+flJQUVq9ezYQJE+jRowcjR47k+eef509/+hPz5s1j+PDhJ3x+SEgIY8eOJSkpiWefffboeGJiInPmzGHo0KE+/XtLwsyCyWs+33bOfVDIlKJq5magz3Hjs8omZb5C6qgLDsdUR0WkCvOiXhaUkpLCkiVLaNmyJffffz8jR44kISGB2267jRdeeIHRo0fz4osv0rx5cxYsWMCtt97KjBkzeOmll+jRowcJCQmMHz+e+fPnH/3MAwcOkJyczLfffsvIkSNZvnw58+bNO9r0Hm/cuHE8++yzJCcnHx1r164dixYtOr1/iHy+uAL6CXBN/sp+3YEM59xWH3zuybUfCoOehsg4HEZabl2+aXZf3riIiHhu+vTp/PDDD3Tt2pWOHTsyffp01q9fz6hRo9izZw8vvvgijz322NH5cXFx9OjRA4CrrrqKuXPnArB161ZKc7WvXr16bNmyxbd/TAlY3jKAE4BVzrnHi5hWVM38EuhvZrXzFx/qnz9Wdgqpo8kdx6qOioh4ZN++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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def jacobian(t, y):\n", - " return np.array([[-1.0, 0.0], [2, -1]])\n", - "\n", - "mass_matrix = np.array([[1, 0], [0, 0]])\n", - "\n", - "solution = dae_solver.integrate(exponential_decay_dae, y0, t_eval, jacobian=jacobian, mass_matrix=mass_matrix)\n", - "plot(solution.t, solution.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can specify events at which the solver should terminate" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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nXP/1ez2tBPn3ASwH7iv2W1pcXcBH/Tvw3gE+FUz+4/mNqk9OOcG/eUw8N94l5bK5SlHpWGUo/VTji6bcrROjczRdi9AnwO2m9kWM4sADwL7mtwmWYX8iQC+Ahxzzn2+2e1pB2bWa2YZfz2ON8nJz5vbqtbmnPukc67fObcZ79+zHzjnPtLkZrU8M0v6k5pgZkng/YBmJ10e5ag0hHK0dsrR2ilH69PoHG3rAtQ5VwQ+DjyGdzP7N5xzR5rbqtZnZl8HngF2mNmAmT3U7Da1gbuA38MbSTvkv+5vdqNa3Hpgv5m9hPef3O855zQdulwNa4EnzexF4FngO865/2tym9qCcrQ+ytG6KEdrpxyVRmlojrb1Y1hERERERESkfbT1GVARERERERFpHypARUREREREpCFUgIqIiIiIiEhDqAAVERERERGRhlABKiIiIiIiIg2hAlSkzZlZxsz+oNntEBERaUfKUZHGUgEq0v4ygIJTRESkPspRkQZSASrS/j4DbPUf6v3ZZjdGRESkzShHRRrInHPNboOIXAEz2wx82zm3s8lNERERaTvKUZHG0hlQERERERERaQgVoCIiIiIiItIQKkBF2t95oKPZjRAREWlTylGRBlIBKtLmnHNjwFNmdliTJ4iIiNRGOSrSWJqESERERERERBpCZ0BFRERERESkIVSAioiIiIiISEOoABUREREREZGGUAEqIiIiIiIiDaECVERERERERBpCBaiIiIiIiIg0hApQERERERERaYj/B/8ytpH/xIx8AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def y1_equal_0pt2(t, y):\n", - " return y[1] - 0.2\n", - "\n", - "# Solve\n", - "solution = dae_solver.integrate(exponential_decay_dae, y0, t_eval, events=[y1_equal_0pt2])\n", - "\n", - "# Plot\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", - "ax1.plot(t_fine, np.exp(-t_fine), solution.t, solution.y[0], \"o\")\n", - "ax1.set_xlabel(\"t\")\n", - "ax1.legend([\"exp(-t)\", \"y_sol[0]\"], loc=\"best\")\n", - "\n", - "ax2.plot(t_fine, 2*np.exp(-t_fine), solution.t, solution.y[1], \"o\", t_fine, 0.2 * np.ones_like(t_fine), \"k\")\n", - "ax2.set_xlabel(\"t\")\n", - "ax2.legend([\"2*exp(-t)\", \"y_sol[1]\", \"y=0.2\"], loc=\"best\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Finding consistent initial conditions\n", - "\n", - "The solver will fail if initial conditions that are inconsistent with the algebraic equations are provided." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "algebraic residual at (0, y0_bad, [0]) is -1\n", - "Error test failures occurred too many times during one internal time step or minimum step size was reached.\n" - ] - } - ], - "source": [ - "y0_bad = np.array([1, 3])\n", - "print(\"algebraic residual at (0, y0_bad, [0]) is {}\".format(exponential_decay_dae(0, y0_bad, [0])[1]))\n", - "try:\n", - " solution = dae_solver.integrate(exponential_decay_dae, y0_bad, t_eval)\n", - "except pybamm.SolverError as e:\n", - " print(e)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, we can use `calculate_consistent_initial_conditions` to obtain consistent initial conditions, starting from a guess of bad initial conditions, using a simple root-finding algorithm." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "y0_fixed = [1. 2.]\n", - "\n", - "algebraic residual at (0, y0_fixed, [0]) is 0.0\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOzdd3RVVfrG8e+bHiAECKGYAqE3qQFxQIqMgAVRUQbEioh1dHSK/kYHlZlxGBV7G0fsSrGLYqUJKCVCpEoRAgmghBY6afv3RwITICEJ3OTkJs9nrazcs8++9zxhzfKd95xz9zHnHCIiIiIiIiJlLcDrACIiIiIiIlI1qAEVERERERGRcqEGVERERERERMqFGlAREREREREpF2pARUREREREpFwEeXXgunXrusaNG3t1eBERkZP64Ycftjvnor3OURTVURERqciKqqOeNaCNGzcmKSnJq8OLiIiclJlt9DrDyaiOiohIRVZUHdUtuCIiIiIiIlIu1ICKiIiIiIhIuVADKiIiIiIiIuXCs++AiohI2crKyiItLY1Dhw55HaVCCwsLIzY2luDgYK+jiIiIB1QvT09p66gaUBGRSiotLY2IiAgaN26MmXkdp0JyzrFjxw7S0tJISEjwOo6IiHhA9fLUnUod1S24IiKV1KFDh4iKilIxPQkzIyoqSme9RUSqMNXLU3cqdbTYBtTMXjGzbWa2vIj9ZmZPm9k6M1tqZp1Lkfn0LJ0CT7SDB2vl/V46pdwOLSLiD1RMi+erfyMzizOzmWa20sxWmNmdhcwpsmaa2bVmtjb/51qfhCqO6qiICKB6eTpK+29XkiugrwEDT7L/fKB5/s9o4IVSJThVS6fA1DsgIxVweb+n3qHiKSIiXskG/uicawN0B24zszbHzSm0ZppZHeAB4CygG/CAmdUu07SqoyIi4oFiG1Dn3LfAzpNMGQy84fLMB2qZWUNfBSzS9LGQdfDYsayDeeMiIlKpXH755axfv/6E8eTkZKZNm3Z0+9NPP2XMmDHlGe0o59xW59zi/Nd7gVVAzHHTiqqZA4CvnXM7nXO7gK85+cnf06c6KiJSIaSmptK3b1/atGlD27Zteeqpp47ue+2110hJScE55/PjfvTRR4wdW/h/8x9++OGjrzMzM+nVqxfZ2dk+Oa4vvgMaA6QW2E7jxIILgJmNNrMkM0tKT08/vaNmpJVuXERE/NKKFSvIycmhSZMmJ+w7vgG98MILmTp1KgcOHCjPiCcws8ZAJ2DBcbuKqpklqqWqoyIilU9QUBDjx49n5cqVzJ8/n+eee4558+YxatQoUlNTmTt3LjfffLPPj/vII49w6623FrqvYAMaEhJCv379mDx5sk+OW66LEDnnXnLOJTrnEqOjo0/vwyJjSzcuIiLl7q233qJbt2507NiRm266iY0bN9K8eXO2b99Obm4u55xzDl999RUpKSm0atWKESNG0Lp1ay6//PKjTeTbb7/N4MGDT/jszMxMxowZw+TJk+nYsSOTJ0/GzOjTpw+ffvppef+pR5lZDeB94A/OuT2+/GzVURGRyqdhw4Z07py3JEBERAStW7fmwIED/POf/2TChAlMmjSJF17I+5bjzz//zMCBA+nSpQvnnHMOP/30EwCDBw/mjTfeAOA///kPI0aMAKBPnz7ceeeddOzYkXbt2rFw4UIA1qxZQ2hoKHXr1j0hz7333svBgwfp2LHj0c+55JJLePvtt33y9/riMSybgbgC27H5Y2Wr35i876oUuH0oNyicgH7e3HolIlKRPTR1BSu3+LQXos0ZNXlgUNsi969atYrJkyczb948goODufXWW5k9ezb33HMPt9xyC926daNNmzb079+flJQUVq9ezYQJE+jRowcjR47k+eef509/+hPz5s1j+PDhJ3x+SEgIY8eOJSkpiWefffboeGJiInPmzGHo0KE+/XtLwsyCyWs+33bOfVDIlKJq5magz3Hjs8omZb5C6qgLDsdUR0WkCvOiXhaUkpLCkiVLaNmyJffffz8jR44kISGB2267jRdeeIHRo0fz4osv0rx5cxYsWMCtt97KjBkzeOmll+jRowcJCQmMHz+e+fPnH/3MAwcOkJyczLfffsvIkSNZvnw58+bNO9r0Hm/cuHE8++yzJCcnHx1r164dixYtOr1/iHy+uAL6CXBN/sp+3YEM59xWH3zuybUfCoOehsg4HEZabl2+aXZf3riIiHhu+vTp/PDDD3Tt2pWOHTsyffp01q9fz6hRo9izZw8vvvgijz322NH5cXFx9OjRA4CrrrqKuXPnArB161ZKc7WvXr16bNmyxbd/TAlY3jKAE4BVzrnHi5hWVM38EuhvZrXzFx/qnz9Wdgqpo8kdx6qOioh4ZN++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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def exponential_decay_dae_rhs(t, y):\n", - " return np.array([exponential_decay_dae(t, y, [0])[0]])\n", - "\n", - "def exponential_decay_dae_algebraic(t, y):\n", - " return np.array([exponential_decay_dae(t, y, [0])[1]])\n", - "\n", - "y0_fixed = dae_solver.calculate_consistent_initial_conditions(\n", - " exponential_decay_dae_rhs, exponential_decay_dae_algebraic, y0_bad\n", - ")\n", - "print(\"y0_fixed = {}\\n\".format(y0_fixed))\n", - "print(\"algebraic residual at (0, y0_fixed, [0]) is {}\".format(exponential_decay_dae(0, y0_fixed, [0])[1]))\n", - "\n", - "solution = dae_solver.integrate(exponential_decay_dae, y0_fixed, t_eval)\n", - "plot(solution.t, solution.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Solving a model" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `solve` method is common to all DAE solvers. It takes a model, which contains all of the above information (residuals function, initial conditions and optionally jacobian, mass matrix, events), and a time to evaluate `t_eval`, and calls `integrate` to solve this model. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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PXfeeSdz585l0qRJdO3alcaNG/PMM8+waNGi3HMeOXKEFStWsGDBAm6++WbWrFlDXFxcbtN7Kvv372fmzJncfffdpKSkMGLECG666SYARowYwcsvv0y/fv244oorGDhwYO7zOnbsSFxcHFdffXUR/lonK4kGdAZwl5m9T/biCcnOuZ9L4Lyn1m5w9vc5Y3HJifxqEbwTcj0PtB2E1rwSESk7c+bMYdmyZZxzzjkApKSkULt2bcaMGcOHH37Iq6++elwzGB0dTdeuXQG49tprmTBhAg888AA///wzp7uqt3btWh5++GFmz54NwMUXX8wll1zCmDFjuPXWWwu8n8bf35+goCAOHjxItWrVSuLXPollLwM4BVjvnHu2gGn51kwz+wJ4Ms/CQ72Av5RK0GNOqKM/u3C+ivwT1x8bFxGRYjt06BBXX301zz//PA0aNOD111/nzTffpFu3blx77bUcOnSIb7/9lkGDBuU+Jy0tDYA6deowduxYevTowUcffUStWrVy5xx7w/bCCy/kwIED7N+/v1B1NCMjg6FDhzJy5MjcW16mTp3K119/DWQ3oAWtalu7dm127tz5+/8YOU7bgJrZe2S/KxthZolkr9IXCOCcexWYBVwOxANHgJuKnaqw2g2GdoMxYO7325j40Wq6bkni/KYnX3YWEakMivKObElxznHDDTfwj3/847jxI0eOkJiYfT/hoUOHchu/EwvbscehoaG5+4h99NFH/O1vfwNg8uTJxMbGkpiYyFVXXcXbb79N06ZNj3vusUWITrUUfFpaGiEhIcX5VU+nK3AdsNrMjnXcjwAN4NQ10zm318weB45dph3rnDvVAoAlI08dfePTdUyNS+AP+44QVbNKqb+0iEhZ8KIuHpOens7VV1/NsGHDGDBgQO74jTfemPtzVlYWNWrUOO6N2rxWr15NeHj4SY1ffrU0NDSU5ORkADIzM+nUqRMA/fr1y12YaPjw4TRr1ox77rnnuOd27979tL9PamoqoaGhp513OoVZBXeoc66ucy7QORflnJvinHs1p5CSs5LfCOdcU+dcW+fc0mKn+h0GdKxPRNUgXl9w8uqJIiJSenr27Mn06dPZtWsXAHv37mXr1q08/PDDDBs2jLFjx3Lbbbflzt+2bRvfffcdAP/+97+54IILAGjZsiXx8fEAXHXVVaxYsYIVK1YQGxvL/v37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POI/7vanvwI46H/RDgK3BHgdfAx4wq/1x8ALyl77J/666QfeGcT6/fGPALcvel0g1gHelrRTQB7vuPtbgPcA7/HnG/Apf/kOAvsD9v6vVv/ngdGy78ABf3qv/94/6n++bq1F/RUuw/vLvgMPUPZPwHKfv6DV77e5Ge+CM+WvC8Q6wDuczAGPlX1Orq+n70EtH6v9DUQ5GoRlUI6ub/3KUeXoutbvt7kZ5WhFj+KWBhEREREREZF1FfRzQEVERERERGSDUAdUREREREREqkIdUBEREREREakKdUBFRERERESkKtQBFRERERERkapQB1SkzplZi5m9t9Z1iIiI1CPlqEh1qQMqUv9aAAWniIjIc6McFakidUBF6t/twPPM7BEz+0StixEREakzylGRKjLnXK1rEJGLYGY7ge86515Y41JERETqjnJUpLq0B1RERERERESqQh1QERERERERqQp1QEXq3ySQrnURIiIidUo5KlJF6oCK1Dnn3DBwn5k9rosniIiIXBjlqEh16SJEIiIiIiIiUhXaAyoiIiIiIiJVoQ6oiIiIiIiIVIU6oCIiIiIiIlIV6oCKiIiIiIhIVagDKiIiIiIiIlWhDqiIiIiIiIhUhTqgIiIiIiIiUhX/DwPrDHs/oyXrAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# Create model\n", - "model = pybamm.BaseModel()\n", - "u = pybamm.Variable(\"u\")\n", - "v = pybamm.Variable(\"v\")\n", - "model.rhs = {u: -v} # du/dt = -v\n", - "model.algebraic = {v: 2 * u - v} # 2*v = u\n", - "model.initial_conditions = {u: 1, v: 1} # bad initial conditions, solver fixes\n", - "model.events['v=0.2'] = v - 0.2\n", - "model.variables = {\"u\": u, \"v\": v}\n", - "\n", - "# Discretise using default discretisation\n", - "disc = pybamm.Discretisation()\n", - "disc.process_model(model)\n", - "\n", - "# Solve #################################\n", - "t_eval = np.linspace(0, 2, 30)\n", - "solution = dae_solver.solve(model, t_eval)\n", - "#########################################\n", - "\n", - "# Post-process, so that u and v can be called at any time t (using interpolation)\n", - "t_sol, y_sol = solution.t, solution.y\n", - "u = pybamm.ProcessedVariable(model.variables[\"u\"], t_sol, y_sol)\n", - "v = pybamm.ProcessedVariable(model.variables[\"v\"], t_sol, y_sol)\n", - "\n", - "# Plot\n", - "t_fine = np.linspace(0,t_eval[-1],1000)\n", - "\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", - "ax1.plot(t_fine, np.exp(-2 * t_fine), t_sol, u(t_sol), \"o\")\n", - "ax1.set_xlabel(\"t\")\n", - "ax1.legend([\"exp(-2*t)\", \"u\"], loc=\"best\")\n", - "\n", - "ax2.plot(t_fine, 2 * np.exp(-2 * t_fine), t_sol, v(t_sol), \"o\", t_fine, 0.2 * np.ones_like(t_fine), \"k\")\n", - "ax2.set_xlabel(\"t\")\n", - "ax2.legend([\"2*exp(-2*t)\", \"v\", \"v = 0.2\"], loc=\"best\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the discretisation or solver will have created the mass matrix and jacobian algorithmically, using the expression tree, so we do not need to calculate and input these manually." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/notebooks/solvers/scikits-ode-solver.ipynb b/examples/notebooks/solvers/scikits-ode-solver.ipynb deleted file mode 100644 index 53d65b879b..0000000000 --- a/examples/notebooks/solvers/scikits-ode-solver.ipynb +++ /dev/null @@ -1,277 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Scikits ODE solver\n", - "\n", - "In this notebook, we show some examples of solving an ODE model using the Scikits ODE solver, which interfaces with the [SUNDIALS](https://computation.llnl.gov/projects/sundials) library via the [scikits-odes](https://scikits-odes.readthedocs.io/en/latest/) Python interface" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Setup\n", - "import pybamm\n", - "import tests\n", - "import numpy as np\n", - "import os\n", - "import matplotlib.pyplot as plt\n", - "from pprint import pprint\n", - "os.chdir(pybamm.__path__[0]+'/..')\n", - "\n", - "# Create solver\n", - "ode_solver = pybamm.ScikitsOdeSolver()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Integrating ODEs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the simplest case, the `integrate` method of the ODE solver needs to be passed a function that returns `dydt` at `(t,y)`, initial conditions `y0`, and a time `t_eval` at which to return the solution:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOzdeXxV1bn/8c+TiQQIEEgYE0iYCRCCBBxAhIsFlCqOVIooRaQ4FKttf9raaxVr663zbFWGWhFE64SCM4iiDEHCFGQOJIwhQAAJQ5L1+yMhDZBAhpPsDN/365VXctZe55xvuL2uPGfv/SxzziEiIiIiIiJS0fy8DiAiIiIiIiK1gwpQERERERERqRQqQEVERERERKRSqAAVERERERGRSqECVERERERERCpFgNcBihIeHu6io6O9jiEiInKGZcuW7XXORXidQ2uliIhUZcWtl1WyAI2OjiYxMdHrGCIiImcws61eZwCtlSIiUrUVt17qElwRERERERGpFCpARUREREREpFKoABUREREREZFKUSXvARURkfI7ceIEaWlpHD161Oso1VJwcDCRkZEEBgZ6HUVExFNaT+RsSrteqgAVEamh0tLSCA0NJTo6GjPzOk614pwjIyODtLQ0YmJivI4jIuIprSdSnLKsl7oEV0Skhjp69ChNmjTRHwtlYGY0adJEn/aLiKD1RIpXlvXynAWomUWZ2TwzSzazNWZ2VxFzzMyeNbONZrbSzM4rdOxmM9uQ/3VziZOV18pZ8FQ3eLBR3veVsyrtrUVEqgr9sVB2vvq3M7MpZrbHzFYXc7zYNbTCaa0UkRLSeiLFKe3/NkpyBjQb+J1zLha4ALjDzGJPm3MZ0CH/azzwUn6YxsBfgPOBPsBfzCysVAnLYuUsmD0RMlMBl/d99kQtrCIi4oVpwNCzHC9yDa1wWitFRMQD5yxAnXM7nXM/5P98CFgLtDpt2nDgdZdnEdDIzFoAQ4DPnXP7nHP7gc85+yLsG19OghNZp46dyMobFxGRSpOamsrAgQOJjY2la9euPPPMMwXHpk2bRkpKCs45n7/v+++/z6RJRf83/29/+1vBz8ePH6d///5kZ2f7PMNJzrkFwL6zTCluDa1YWitFpJa67rrr2Lx5M0eOHGHYsGF07tyZrl27ct999xXMcc4xf/585s+fXyHr1PLly7nllluKPPb0009z5MiRgseXXnop+/fvL9Xrp6Sk8Oabb5Y61/Tp04mLi6N79+5cdNFFrFixAvDtelmqe0DNLBroCSw+7VArILXQ47T8seLGi3rt8WaWaGaJ6enppYl1psy00o2LiEiFCAgI4IknniA5OZlFixbxwgsvsHDhQsaNG0dqairffvstEyZM8Pn7/uMf/+D2228v8ljhAjQoKIhBgwbx1ltv+TxDKWitFBGpJGvWrCEnJ4e2bdsC8Pvf/54ff/yR5cuXs3DhQubOnUtWVhZjxoxhzZo1rF69mjFjxpCVlXWOVy6dv/3tb0ycOLHIY6cXoKNHj+bFF18s1euXtQCNiYnh66+/ZtWqVfzv//4v48ePB3y7Xpa4ADWz+sB/gN865w6W+51P45x7xTmX4JxLiIiIKN+LNYws3biIiFSIFi1acN55ebc0hoaG0qVLF44cOcIjjzzC5MmTmTlzJi+9lHfF6aZNmxg6dCi9evXi4osv5scffwRg+PDhvP766wD885//ZNSoUQAMGDCAu+66i/j4eLp168aSJUsAWL9+PXXq1CE8PPyMPPfddx9ZWVnEx8cXvM5VV13F9OnTK/Yfwke0VopIbfXGG2/Qp08f4uPj+fWvf01OTg5bt26lQ4cO7N27l9zcXC6++GI+++wzUlJS6Ny5M6NGjaJLly5cd911BQXd9OnTGT58OAB169Zl4MCBQF6Bdd5555GWlkZISAgvvfQSU6ZMYerUqbz00kuEhISQnp7OtddeS+/evenduzcLFy4E4K677iq46ubTTz+lf//+5ObmMmbMGCZMmEBCQgIdO3bko48+AuDQoUOsXLmSHj16nPF7Pvvss+zYsYOBAwcWZLvyyiuZMWNGqf697rvvPr755hvi4+N56qmnSvy8iy66iLCwvDsmL7jgAtLS/vuhpK/WyxJtw2JmgeQVn9Odc+8WMWU7EFXocWT+2HZgwGnj88sStFQGPZB3H0vhS4sCQ/LGRURqoYdmryF5h28/O4xt2YC/XNG1xPNTUlJYvnw5nTp14s9//jNjx44lJiaGO+64g5deeonx48fz8ssv06FDBxYvXsztt9/OV199xSuvvELfvn2JiYnhiSeeYNGiRQWveeTIEZKSkliwYAFjx45l9erVLFy4sKDoPd2jjz7K888/T1JSUsFYt27dWLp0adn/IcqvuDW0YmmtFJEy8GI9Wbt2LW+99RYLFy4kMDCQ22+/nenTp3PTTTdx7733ctttt9GnTx9iY2MZPHgwKSkprFu3jsmTJ9O3b1/Gjh3Liy++yO9//3sWLlzIyJEjz3iPAwcOMHv2bO666y6ysrK44447+NWvfgXAHXfcwYsvvshdd93F3XffTb9+/di2bRtDhgxh7dq1/P3vf6d3795cfPHFTJw4kTlz5uDnl3eeLyUlhSVLlrBp0yYGDhzIxo0bSUxMpFu3bkX+rhMnTuTJJ59k3rx5BR+khoWFcezYMTIyMmjSpEmJ/k0fffRRHn/88YKitywmT57MZZddVvDYV+vlOQtQy2trNBlY65x7sphpHwJ3mtlM8hoOZTrndprZp8DfCjUeGgz8sdypzyVuRN73LyfhMtPY6ZoQfvkjBJ0cFxGRSnX48GGuvfZann76aVq3bs2rr77KtGnTuPjii7nxxhs5fPgw3333Hddff33Bc44dOwZAs2bNmDRpEgMHDuS9996jcePGBXNO/hHRv39/Dh48yIEDB9i5cyelOTvo7+9PUFAQhw4dIjQ01Ee/cakUuYZW+Lvmr4m5XzwEmdv5Kbg5ocMm/XcNFRGpIr788kuWLVtG7969AcjKyqJp06YAjBs3jrfffpuXX375lA8Xo6Ki6Nu3LwA33ngjzz77LL///e+LXCOys7MZOXIkEydOLLg0d8qUKXz99ddAXgFqZnzxxRckJycXPO/gwYMcPnyY+vXr8+qrr9K/f3+eeuop2rVrVzBnxIgR+Pn50aFDB9q2bcuPP/5Y6nUKoGnTpuzYsaPEBWh5zZs3j8mTJ/Ptt98WjPlqvSzJGdC+wGhglZmd/L/qn4DWAM65l4E5wOXARuAI8Kv8Y/vM7GHgZKk8yTl3tkYMvhM3AuJG8P2mvfzy1cU8Y/EMr5Q3FhGpekpzptLXTpw4wbXXXsuoUaO45pprCsbHjBlT8HNubi6NGjU65Y+HwlatWkWTJk3YsWPHKeOnt343M0JCQsjMzAQgJyeHXr16AXmXMBXXmOjYsWMEBweX+ncrCTObQd7VQOFmlkZed/hAOPsaWiniRuAXN4L4SZ/x8y4t+Gtc90p7axGpnrxYT5xz3Hzzzfz9738/49iRI0cKLhM9fPhwQWFU1PoAEBIScsaelePHj6dDhw789re/PWX+gAEDTpmXm5vLokWLilwvSrtOFc4wZMgQdu/eTUJCAq+99lqR/wZHjx4lJCTklLH33nuPhx56CIDXXnuNhISEIp97uvvvv5+PP/4YoMh1d+XKlYwbN465c+eeUfD6Yr0sSRfcb51z5pyLc87F53/Ncc69nL9wkt+57w7nXDvnXHfnXGKh509xzrXP/5parrRlcEFME1o1CuHdHyr+aiYRETmVc45bbrmFLl26cM899xQ7r0GDBsTExPD2228XPO9k570lS5Ywd+5cli9fzuOPP86WLVsKnneyGcK3335Lw4YNadiwIV26dGHjxo1A3qe1SUlJJCUlFRSfgYGBnDhxouA1MjIyCA8PJzAw0Le/fD7n3EjnXAvnXKBzLtI5N7mka2hliQwLIXWfbxtsiIj4yqBBg3jnnXfYs2cPAPv27WPr1q0A3HvvvYwaNYpJkyZx6623Fjxn27ZtfP/99wC8+eab9OvXD+CUNQLgz3/+M5mZmTz99NPnzDF48GCee+65gscni7etW7fyxBNPsHz5cubOncvixf/t1/r222+Tm5vLpk2b2Lx5M506dTojw6effkpSUlJB8RkaGsqhQ4cKjjvn2LVrF9HR0afkufrqqwvWuNOLz9Nfo7BHHnmk4Hmn27ZtG9dccw3//ve/6dix4ynHfLVelqoLbnXk52dc3bMV32xIZ/fBo+d+goiI+MzChQv597//zVdffUV8fDzx8fHMmTOnyLnTp09n8uTJ9OjRg65du/LBBx9w7Ngxbr31VqZMmULLli154oknGDt2bEFL/ODgYHr27MmECROYPHkykHc57vLly4ttmz9+/Hji4uIKmhDNmzePYcOGVcBvX31EhdUlbf+Rc08UEfFAbGwsf/3rXxk8eDBxcXH87Gc/Y+fOnXz99dcsXbq0oAgNCgpi6tS8812dOnXihRdeoEuXLuzfv5/bbrsNgGHDhjF//nwA0tLSeOSRR0hOTua8884jPj6+2DOQkNcgKDExkbi4OGJjY3n55ZcLPmh9/PHHadmyJZMnT2bcuHEFZzhbt25Nnz59uOyyy3j55ZcJDg6mc+fOZGZmFlsgjh8/nqFDhxY0IVq2bBkXXHABAQElat8DQFxcHP7+/vTo0aNUTYgmTZpERkYGt99+O/Hx8acUtj5bL51zVe6rV69ezpc27Tnk2tz7kfvn1xt9+roiIlVZcnKy1xEq1CWXXOKWLl1a5LGJEye6zz//vESvc/XVV7t169YVeayof0Mg0dWwtfKRj5Ndx/vnuNzcXJ+9pojUHNVtPdmyZYvr2rVrkceOHDnizj//fJednV3hOW6++Wb39ttvF3nsySefdK+++mqJXmfixInuiy++8GW0MvHVelnjz4ACtI2oT8/WjfjPsu0VspGsiIhULX/6059O2UOtOMePH+eqq6464zKj2iYyLIRj2bmkHzrmdRQRkQoVEhLCQw89xPbt3t6ed9ttt1GnTp0Sze3WrRuDBg2q4ERn58v1suTncau5a8+L5M/vr2bNjoN0a9XQ6zgiIlJOJy+hKkqzZs248sorz/kaQUFB3HTTTT5MVT1FhdUFIHV/Fk0bVEwzJhGRyhIdHc3q1auLPT5kyJBKyTFt2rRijwUHBzN69OgSvU7he1u94sv1slacAQX4eVwLgvz9+M8PaeeeLCIiUotEhuV1VtR9oCIiUtFqTQHaqG4Ql8Y25YOkHRzPzvU6joiISJURmX8GNG2/OuGKiEjFqjUFKMD1CVHs++k4X67d7XUUERGRKiMkyJ/w+kGk7tMZUBERqVi1qgDt3yGCFg2Dmbk01esoIiIiVUpkWF2dARURkQpXqwpQfz/j+l6RLNiQzvYDWmRFREROimpcl1TdAyoiIhWsVhWgkHcZLsA7iWpGJCJyipWz4Klu8GCjvO8rZ3mdqFjR0dHs3buXlJQUQkJCiI+PLzj2ySef0KlTJ9q3b8+jjz5aMD5q1CgaN27MO++840XkKi8yLIQdB7LIydV2ZSJSTtVkPTnbWjJ27FiaNm1Kt27dTnnOH/7wB5o3b87jjz9e2XFrjFpXgEY1rku/9uHMSkzVIisictLKWTB7ImSmAi7v++yJVfaPhsLatWtHUlISADk5Odxxxx3MnTuX5ORkZsyYQXJyMgDTp08v0dYstVVUWF1O5Dh2HzzqdRQRqc6q6XpSeC0BGDNmDJ988skZ8x577DEmTJhQmdFqnFpXgAL8oncU2w9ksXDjXq+jiIhUDV9OghOn3ZpwIitvvBweeOABnn766YLH999/P88888wZ83bu3En//v2Jj4+nW7dufPPNNwDMmDGD7t27061bN+69995zvt+SJUto3749bdu2JSgoiBtuuIEPPvigXL9DbfHfrVh0i4qIlEMFrCeVvZYA9O/fn8aNG5c5sxSvVhagP4ttRljdQN5SMyIRkTyZxdyWUNx4CY0dO5bXX38dgNzcXGbOnMmNN954xrw333yTIUOGkJSUxIoVK4iPj2fHjh3ce++9fPXVVyQlJbF06VLef//9s77f9u3biYqKKngcGRnJ9u3by/U71BZRjfO2Ytma8ZPHSUSkWquA9aSy1xKpWLWyAK0T4M8150XyWfIuMg4f8zqOiIj3GkaWbryEoqOjadKkCcuXL+ezzz6jZ8+eNGnS5Ix5vXv3ZurUqTz44IOsWrWK0NBQli5dyoABA4iIiCAgIIBRo0axYMGCcuWR4kWGheDvZ2zTViwiUh4VsJ5oLalZamUBCnmX4Z7Icby3XJ+Mi4gw6AEIDDl1LDAkb7ycxo0bx7Rp05g6dSpjx44tck7//v1ZsGABrVq1YsyYMQWfdJdWq1atSE3979UtaWlptGrVqkyvVdsE+vsRGRZCSoYKUBEphwpaTypzLZGKVWsL0I7NQunZuhFvLU3FOTUjEpFaLm4EXPEsNIwCLO/7Fc/mjZfT1VdfzSeffMLSpUsZMmRIkXO2bt1Ks2bNuPXWWxk3bhw//PADffr04euvv2bv3r3k5OQwY8YMLrnkkrO+V+/evdmwYQNbtmzh+PHjzJw5U42HSqFNk3qk7NUluCJSDhW0nlTmWiIVK8DrAF66oXcU9/5nFcu27ichWjcZi0gtFzfCJwXn6YKCghg4cCCNGjXC39+/yDnz58/nscceIzAwkPr16/P666/TokULHn30UQYOHIhzjmHDhjF8+PCzvldAQADPP/88Q4YMIScnh7Fjx9K1a1ef/041VXSTuizfth/nHGbmdRwRqa4qYD2pzLUEYOTIkcyfP5+9e/cSGRnJQw89xC233OLT36m2qtUF6BU9WvLXj9byxqKtKkBFRCpIbm4uixYt4u233y52zs0338zNN998xvjIkSMZOXLkGeMpKSkAHD58+Ixjl19+OZdffnnZA9dibZrU49DRbPYfOUHjekFexxERKVDZa8mMGTPKHlbO6pyX4JrZFDPbY2arizn+BzNLyv9abWY5ZtY4/1iKma3KP5bo6/DlVTcogGt7RTJnlZoRiYhUhOTkZNq3b8+gQYPo0KGDz1/f39+fzMzMUzYPL86oUaP4+uuvCQ4O9nmOmiImPK8Tboo64YpIFVKV1pI//OEPvPHGG9SrV8/nOWqLkpwBnQY8DxR5F69z7jHgMQAzuwK42zm3r9CUgc65Krvh5qjzWzPtuxRmJaZx24B2XscREalRYmNj2bx5c8HjVatWMXr06FPm1KlTh8WLF5fp9aOiok5pOnQ206dPL9N71CZtmuT9QZWy9yfOax3mcRoRkTxVaS157LHHeOyxx8r0PpLnnAWoc26BmUWX8PVGAtXqfHWHZqFc0LYx0xdvZXz/tvj76Z4XEak5qtq9fN27dycpKcnrGCVSGxvURYaF4GeoE66InKEqrSfVaS2pDUq7XvqsC66Z1QWGAv8pnAf4zMyWmdn4czx/vJklmllienq6r2KVyI0XtCFtfxYL1lfu+4qIVKTg4GAyMjJqZSFVXs45MjIyat3lunUC/GnZKIStugRXRArReiLFKct66csmRFcAC0+7/Lafc267mTUFPjezH51zRe786px7BXgFICEhoVL/1z04tjkRoXX496KtDOzctDLfWkSkwkRGRpKWlkZlf6hXUwQHBxMZWfaN06urmHBtxSIip9J6ImdT2vXSlwXoDZx2+a1zbnv+9z1m9h7QByiyAPVSUIAfN/SO4vl5G0ndd4SoxnW9jiQiUm6BgYHExMR4HUOqmTZN6jJ7xU6vY4hIFaL1RHzJJ5fgmllD4BLgg0Jj9cws9OTPwGCgyE66VcHIPq0x4M0l27yOIiIi4pnoJvXIzDrBgSPHvY4iIiI1UEm2YZkBfA90MrM0M7vFzCaY2YRC064GPnPOFb5mpxnwrZmtAJYAHzvnPvFleF9q2SiEQV2a8dbSVI5l53gdR0RExBMnO+Fu0WW4IiJSAUrSBffMXVvPnDONvO1aCo9tBnqUNZgXRl/Qhs+TdzN31S6u6tnK6zgiIiKV7uReoFszjtBTW7GIiIiP+awLbk3Qr304bcPrMXXhFnX5EhGRWikyrC5mkKJOuCIiUgFUgBbi52eM6RvNirRMfth2wOs4IiIilS440J+WDUPUCVdERCqECtDTXHteJKHBAUxduMXrKCIiIp5oG1GPzSpARUSkAqgAPU29OgHc0DuKuat3sTMzy+s4IiIila5dRH027Tms21FERMTnVIAW4aYLo3HO8fr3W72OIiIiUunaNa3PT8dz2HXwqNdRRESkhlEBWoSoxnUZHNucGUu2kXVcW7KIiEjt0i4ibyuWTXt0Ga6IiPiWCtBijO0Xw4EjJ3hv+Xavo4iIiFSq9hH1AdiUftjjJCIiUtOoAC1G7+gwurZsoC1ZRESk3MxsqJmtM7ONZnZfEcdbm9k8M1tuZivN7HIvcp4UEVqH0OAANu5RASoiIr6lArQYZsbYvjFs2HOYbzfu9TqOiIhUU2bmD7wAXAbEAiPNLPa0aX8GZjnnegI3AC9WbspTmVleIyKdARURER9TAXoWP+/RgvD6dZj8rbZkERGRMusDbHTObXbOHQdmAsNPm+OABvk/NwR2VGK+IqkAFRGRiqAC9CzqBPhz84VtmL8unXW7DnkdR0REqqdWQGqhx2n5Y4U9CNxoZmnAHOA3Rb2QmY03s0QzS0xPT6+IrAXaN63P7oPHOHT0RIW+j4iI1C4qQM9h9IVtCAn055UFm72OIiIiNddIYJpzLhK4HPi3mZ2xRjvnXnHOJTjnEiIiIio0UEEn3HR1whUREd9RAXoOjeoGcUOfKD5I2s7OzCyv44iISPWzHYgq9Dgyf6ywW4BZAM6574FgILxS0hWjfdP8TrhqRCQiIj6kArQEbukXgwOm6F5QEREpvaVABzOLMbMg8poMfXjanG3AIAAz60JeAVqx19ieQ1TjugT6m+4DFRERn1IBWgKRYXX5eVwL3ly8jcws3QsjIiIl55zLBu4EPgXWktftdo2ZTTKzK/On/Q641cxWADOAMc7jPcAC/f1o06SetmIRERGfCvA6QHUxvn9bPkjawfTFW7l9QHuv44iISDXinJtDXnOhwmMPFPo5Gehb2bnOpX1EfTbsURM+ERHxHZ0BLaGuLRtycYdwpi5M4Vh2jtdxREREKly7pvXYmnGE49m5XkcREZEaQgVoKfy6fzvSDx3j/eWn944QERGpeTo2CyU717F5ry7DFRER3zhnAWpmU8xsj5mtLub4ADPLNLOk/K8HCh0bambrzGyjmd3ny+Be6Nu+CV1bNuCfCzaTm+vprTkiIiIVrnPzBgDaC1tERHymJGdApwFDzzHnG+dcfP7XJAAz8wdeAC4DYoGRZhZbnrBeMzNuG9COzek/MXf1Lq/jiIiIVKiY8HoE+JkKUBER8ZlzFqDOuQXAvjK8dh9go3Nus3PuODATGF6G16lSLuvWgrYR9Xjuqw143KBQRESkQgUF+NEuor4KUBER8Rlf3QN6oZmtMLO5ZtY1f6wVkFpoTlr+WJHMbLyZJZpZYnq6p1ufnZW/n3HHgPb8uOsQX6zd43UcERGRCtWpeSg/qgAVEREf8UUB+gPQxjnXA3gOeL8sL+Kce8U5l+CcS4iIiPBBrIozPL4lrRvX5XmdBRURkRquU/NQth/I4tBR7YMtIiLlV+4C1Dl30Dl3OP/nOUCgmYUD24GoQlMj88eqvQB/P24f0I4VaZl8s2Gv13FEREQqTKdmoQCs362zoCIiUn7lLkDNrLmZWf7PffJfMwNYCnQwsxgzCwJuAD4s7/tVFdecF0nLhsG6F1RERGq0Ts3zCtB1u7QVi4iIlF9JtmGZAXwPdDKzNDO7xcwmmNmE/CnXAavNbAXwLHCDy5MN3Al8CqwFZjnn1lTMr1H5ggL8mDCgHUtT9rN4S1l6NImIiFR9kWEh1AvyZ92ug15HERGRGiDgXBOccyPPcfx54Plijs0B5pQtWtU3IiGK577ayHNfbeCCtk28jiMiIuJzZkZHNSISEREf8VUX3FopONCfX/dvy8KNGSzbqrOgIiJSM3VuHsq63Yd0y4mIiJSbCtBy+uX5rQmvH8QTn633OoqIiEiF6NQslANHTrDn0DGvo4iISDWnArSc6gYFcPuA9ny3KYPvNqkjroiI1DydmjcA0GW4IiJSbipAfeCX57emeYNgnvxsvS5PEhGRGqdLi7xOuMk71IhIRETKRwWoDwQH+nPn/7Qncet+FmhfUBERqWEa1Q0iMiyE1TsyvY4iIiLVnApQHxmREEVkWAhPfLZOZ0FFRKTG6dqygc6AiohIuakA9ZGgAD8mDurAyrRMPk/e7XUcERERn+rWsiFb9v7EoaMnvI4iIiLVmApQH7qmZytiwuvx5Ofryc3VWVAREak5urVqCMDanWpEJCIiZacC1IcC/P347aUd+HHXIT5etdPrOCIiIj7TtWVeJ9zV23UfqIiIlJ0KUB/7eVxLOjUL5YnP1nEiJ9frOCIiIj7RtEEwEaF1WKP7QEVEpBxUgPqYv59x32WdSck4wowl27yOIyIi4jPdWjZgjTrhiohIOagArQADOkVwQdvGPPPFBjVrEBGRGqNry4Zs2HOYoydyvI4iIiLVlArQCmBm/PGyLmT8dJxXF2z2Oo6IiIhPdGvVgJxcx7pdakQkIiJlowK0gvSIasSwuBa8+s0W9hw86nUcERGRcuvaMq8T7mpdhisiImWkArQC/b8hncjOzeWpLzZ4HUVERKTcIsNCaBAcwOrtakQkIiJlowK0ArVpUo9R57dhVmIqG/cc9jqOiIhIuZgZcZGNWJF6wOsoIiJSTakArWC/+Z/2hAT6849PfvQ6ioiISLnFRzVi3e5DZB1XIyIRESk9FaAVrEn9Oky4pC2fJe9m8eYMr+OIiIiUS3xUI3Jyne4DFRGRMjlnAWpmU8xsj5mtLub4KDNbaWarzOw7M+tR6FhK/niSmSX6Mnh1cku/trRsGMxDs5PJyXVexxERESmz+NaNAEjapstwRUSk9EpyBnQaMPQsx7cAlzjnugMPA6+cdnygcy7eOZdQtojVX0iQP3+8vAvJOw8yKzHV6xeMgHEAACAASURBVDgiIiJlFl6/DpFhISTpPlARESmDcxagzrkFwL6zHP/OObc//+EiINJH2WqUn8e1oHd0GI9/uo6DR094HUdERKTM4qMaqQAVEZEy8fU9oLcAcws9dsBnZrbMzMaf7YlmNt7MEs0sMT093cexvGdm/OWKruw7cpznvtS2LCIiUn3FRzVi+4Es9hzSPtciIlI6PitAzWwgeQXovYWG+znnzgMuA+4ws/7FPd8594pzLsE5lxAREeGrWFVKt1YNGdEriqkLU9iUrm1ZRESkeoqPyrsPdEWqGhGJiEjp+KQANbM44DVguHOuoNWrc257/vc9wHtAH1+8X3X2+yGdCA7055GP13odRUREKomZDTWzdWa20czuK2bOCDNLNrM1ZvZmZWcsjW6tGhLgZySl7j/3ZBERkULKXYCaWWvgXWC0c259ofF6ZhZ68mdgMFBkJ93aJCK0DhMHteerH/cwb90er+OIiEgFMzN/4AXyrgaKBUaaWexpczoAfwT6Oue6Ar+t9KClEBzoT+cWoboPVERESq0k27DMAL4HOplZmpndYmYTzGxC/pQHgCbAi6dtt9IM+NbMVgBLgI+dc59UwO9Q7Yy5KIaY8Ho8PDuZY9nayFtEpIbrA2x0zm12zh0HZgLDT5tzK/DCyaZ++VcOVWnxUY1YkZqp7cVERKRUStIFd6RzroVzLtA5F+mcm+yce9k593L+8XHOubD8rVYKtlvJX2h75H91dc49UtG/THURFODHX66IZfPen3jl681exxERkYrVCii8B1da/lhhHYGOZrbQzBaZWZHbn1Wlhn0JbRpz+Fg263Yd8jSHiIhUL77ugislNKBTU4Z1b8Hz8zayLeOI13FERMRbAUAHYAAwEnjVzBqdPqkqNexLiA4DYGlKsTu1iYiInEEFqIf+9+exBPgZD3y4Gud0CZOISA21HYgq9Dgyf6ywNOBD59wJ59wWYD15BWmVFRlWl5YNg1WAiohIqagA9VDzhsHcM7gTDTa8x9F/xMKDjeCpbrByltfRRETEd5YCHcwsxsyCgBuAD0+b8z55Zz8xs3DyLsmt8vdoJEQ3ZmnKPn2IKiIiJaYC1GNj6i/hH0GTCcnaATjITIXZE1WEiojUEM65bOBO4FNgLTDLObfGzCaZ2ZX50z4FMswsGZgH/KHwtmZVVe+Yxuw+eIzUfVleRxERkWoiwOsAtZ3/vIfx59ipgyey4MtJEDfCm1AiIuJTzrk5wJzTxh4o9LMD7sn/qjb6RDcG8u4Dbd2krsdpRESkOtAZUK9lppVuXEREpIro0LQ+DUMCdR+oiIiUmApQrzWMLN24iIhIFeHnZyS0CVMBKiIiJaYC1GuDHoDAkFOGTvgF542LiIhUcQnRjdmU/hMZh4+de7KIiNR6KkC9FjcCrngWGkbhMDICmnLfiXFsbnG518lERETOqU+M9gMVEZGSUwFaFcSNgLtXYw8eIGfiKj737899/1lFbq7a2ouISNUWF9mIukH+fLepyjftFRGRKkAFaBXTtEEwf/55LEtS9jF98Vav44iIiJxVoL8ffWIas3DjXq+jiIhINaACtAq6vlckF3cI59G5P5K2/4jXcURERM6qb7twNqX/xK7Mo15HERGRKk4FaBVkZvzt6u444E/vrSZvezgREZGq6aL2TQD4bpPOgoqIyNmpAK2iohrX5d6hnVmwPp2ZS1O9jiMiIlKsLs0bEFY3kIUbdR+oiIicnQrQKmz0BW3o274JD3+UzNaMn7yOIyIiUiQ/P+PCdk34ftNeXbUjIiJnpQK0CvPzMx67rgf+fsY9s1aQo664IiJSRV3ULpwdmUdJyVDvAhERKZ4K0CquZaMQHh7ejWVb9/Py15u8jiMiIlKkvu3DAdQNV0REzkoFaDUwPL4lw7q34Okv1rNmR6bXcURERM4Q3aQuLRsG8+0GFaAiIlK8EhWgZjbFzPaY2epijpuZPWtmG81spZmdV+jYzWa2If/rZl8Fr03MjL9e1Y2wukHc/VYSR0/keB1JRETkFGbGJZ0i+HbjXk7k5HodR0REqqiSngGdBgw9y/HLgA75X+OBlwDMrDHwF+B8oA/wFzMLK2vY2iysXhD/d10c63cf5rFP13kdR0RE5AwDOjXl8LFsElP2ex1FRESqqBIVoM65BcC+s0wZDrzu8iwCGplZC2AI8Llzbp9zbj/wOWcvZOUsBnZqyugL2jD52y3MW7fH6zgiIiKn6Ns+nEB/Y/56rVEiIlI0X90D2goovFllWv5YceNnMLPxZpZoZonp6ek+ilXz3D+sC52bh/K7WSvYffCo13FEREQK1K8TQEKbxsz/Ueu4iIgUrco0IXLOveKcS3DOJURERHgdp8oKDvTn+V/2JOt4Dne/laStWUREpEoZ2DmCdbsPseNAltdRRESkCvJVAbodiCr0ODJ/rLhxKYf2TUN56MqufLcpg5fmb/Q6joiISIEBnZoCMH+dzoKKiMiZfFWAfgjclN8N9wIg0zm3E/gUGGxmYfnNhwbnj0k5XZ8QyfD4ljz1xQYSU852e66IiEjl6dC0Pq0ahTBfvQpERKQIJd2GZQbwPdDJzNLM7BYzm2BmE/KnzAE2AxuBV4HbAZxz+4CHgaX5X5Pyx6ScTm7NEhkWwsQZyzlw5LjXkURERDAzBuRvx6Jtw0RE5HQl7YI70jnXwjkX6JyLdM5Nds697Jx7Of+4c87d4Zxr55zr7pxLLPTcKc659vlfUyvqF6mNQoMDeW5kT9IPH+Put5LI1f2gIiJSBVwa24wjx3P4btNer6OIiEgVU2WaEEnZxEU24oErujJvXTrPfaX7QUVExHsXtWtC/ToBfLZmt9dRRESkilEBWgPceH5rrunZiqe/XK97bkRExHN1AvwZ2LkpnyfvVrd2ERE5hQrQGsDMeOTq7nRqFspdM5NI3XfE60giIlLLDenajIyfjrNs636vo4iISBWiArSGCAny55+je5HrHLdNX6bGDyIi4qlLOkYQ5O/HZ2t2eR1FRESqEBWgNUibJvV4akQ8q7cf5H/fX41zuuxJRES8ERocSN/2Tfg0eZfWIxERKaACtIa5NLYZv/mf9ry9LI1/fZfidRwREanFhnRtTuq+LNbuPOR1FBERqSJUgNZAd1/akZ/FNmPSR8ksWJ/udRwREamlfhbbDH8/46OVO7yOIiIiVYQK0BrIz8946hfxdGwWyh1v/sCm9MNeRxIRkVqoSf069G0fzuyVO3QZroiIACpAa6z6dQJ49aYEAv39uPVfiWQeOeF1JBERqYWu7NGS1H1ZJKUe8DqKiIhUASpAa7CoxnV5+cZepO4/wp0zfiA7J9frSCIiUssM7tqMIH8/Plyhy3BFREQFaI3XJ6Yxf72qG99s2Mukj5J1CZSIiFSqBsGBDOgUwUcrd5KTqzVIRKS2UwFaC/yid2tuvTiG17/fyqvfbPY6johIrWNmQ81snZltNLP7zjLvWjNzZpZQmfkq2pXxLUk/dIzFWzK8jiIiIh5TAVpL/PGyLgzr3oK/zfmR2boMSkSk0piZP/ACcBkQC4w0s9gi5oUCdwGLKzdhxRvUuRl1g/z5YLnWHxGR2k4FaC3h52c8MaIHvaPD+N2sFSzerE+hRUQqSR9go3Nus3PuODATGF7EvIeB/wOOVma4yhAS5M9l3Vrw8aqdHDme7XUcERHxkArQWiQ40J9Xb0ogqnEIt76eyIbd2hhcRKQStAJSCz1Oyx8rYGbnAVHOuY8rM1hluj4hksPHsvlk9S6vo4iIiIdUgNYyjeoGMe1XfQgK8GfM1KXsPljjPmgXEalWzMwPeBL4XQnmjjezRDNLTE9Pr/hwPnR+TGNaN67L24lpXkcREREPqQCthaIa12XqmN4cOHKc0ZMXs/+n415HEhGpybYDUYUeR+aPnRQKdAPmm1kKcAHwYVGNiJxzrzjnEpxzCRERERUY2ffMjOt6RfL95gxS9x3xOo6IiHhEBWgt1T2yIa/elEBKxhHGTFvK4WO6J0dEpIIsBTqYWYyZBQE3AB+ePOicy3TOhTvnop1z0cAi4ErnXKI3cSvOtb0iMYP//KCzoCIitVWJCtBztY83s6fMLCn/a72ZHSh0LKfQsQ9Pf65456L24Tw/siert2dy678SOXoix+tIIiI1jnMuG7gT+BRYC8xyzq0xs0lmdqW36SpXq0Yh9G0XztuJadoTVESkljpnAVqS9vHOubudc/HOuXjgOeDdQoezTh5zztWqhbY6GNy1OY9fH8f3mzO4883lnMjJ9TqSiEiN45yb45zr6Jxr55x7JH/sAefcGR/MOucG1MSznyeN7NOa7Qey+Hr9Hq+jiIiIB0pyBrSk7eNPGgnM8EU4qRxX94xk0vCufLF2N//vnZXk6lNpERGpIIO7NqNZgzq8/v1Wr6OIiIgHSlKAnrN9/Elm1gaIAb4qNByc37FvkZldVdybVOfOfjXBTRdG8/vBHXlv+Xb+9N4qFaEiIlIhAv39GNmnNV+vT2drxk9exxERkUrm6yZENwDvOOcK30zYxjmXAPwSeNrM2hX1xOrc2a+muGNge+4c2J6ZS1O5/30VoSIiUjFG9mmNvxlvLNJZUBGR2qYkBei52scXdgOnXX7rnNue/30zMB/oWeqUUinMjN8N7sidA9szY4mKUBERqRjNGgQzpGtzZiWmkXVcDfBERGqTkhSgZ20ff5KZdQbCgO8LjYWZWZ38n8OBvkCyL4JLxVARKiIilWH0hW3IzDrB7BU7vI4iIiKVKOBcE5xz2WZ2sn28PzDlZPt4ILFQB78bgJnOucLVShfgn2aWS16x+6hzTgVoFXeyCAV4ft5GAB65qjt+fuZlLBERqUHOj2lMp2ahTFm4hesTIjHTGiMiUhucswCFvPbxwJzTxh447fGDRTzvO6B7OfKJR04vQk/kOB69pjsB/r6+bVhERGojM2N8/7b87u0VzF+fzsBOTb2OJCIilUDVhBTrZBF696UdeWdZGne+uZxj2bpXR0REfOOKHi1p0TCYf369yesoIiJSSVSAylmZGXdd2oEHfh7LJ2t2MfnF/yP3ya7wYCN4qhusnOV1RBERqaaCAvy4pV8MizbvIyn1gNdxRESkEqgAlRIZ2y+Gty5MZUzGU/gdTAMcZKbC7IkqQkVEpMxu6NOaBsEBOgsqIlJLqACVEjt/8/PUteOnDp7Igi8neRNIRESqvfp1Ahh9YRs+WbOLzemHvY4jIiIVTAWolFxmWunGRURESmDMRTHUCfDj+a82eh1FREQqmApQKbmGkUUOH6vXspKDiIhITRIRWoebLozm/aTtbNyjs6AiIjWZClApuUEPQGDIKUNHqcMfD17Nxyt3ehRKRERqgl/3b0twoD9Pf7He6ygiIlKBVIBKycWNgCuehYZRgEHDKLKHPc22lsO4480feO2bzTjnvE4pIiLVUJP6dRhzUTQfrdzJj7sOeh1HREQqSIDXAaSaiRuR95WvPvBGfA53v5XEXz9ey9aMIzxwRSyB/vpsQ0RESmd8/7a8/v1Wnv58Ay+P7uV1HBERqQCqEqTcggP9eeGX5/Hr/m3596Kt3DR5Cft/On7uJ4qIiBTSqG4Qt/SL4ZM1u/hh236v44iISAVQASo+4edn/PHyLjxxfQ+Wbd3P8BcWsn73Ia9jiYhINXNr/7ZEhNbhrx8l67YOEZEaSAWo+NS1vSKZ+esLyDqRwzUvfseXa3d7HUlERKqR+nUC+P3gjvyw7QAfr1KDOxGRmkYFqPjcea3D+PDOvsSE12Pc64k89+UGcnP1KbaIiJTMdb2i6Nw8lEfn/sjREzlexxERER9SASoVokXDEGb9+kKu7NGSJz5fzy3/WsqBI7ovVEREzs3fz/jfn8eStj+LqQtTvI4jIiI+pAJUKkxIkD9P/yKeh4d35duNexn27LesTDvgdSwREakG+rYP59IuzXjuqw1sP5DldRwREfERFaBSocyM0RdG8/aEiwC47qXvmb54qxpLiIjIOf3lilhynePBD9d4HUVERHxEBahUivioRnz0m35c2K4J97+3mntmreDwsWyvY4mISBUW1bguv720I58n7+azNbu8jiMiIj5QogLUzIaa2Toz22hm9xVxfIyZpZtZUv7XuELHbjazDflfN/syvFQvYfWCmDqmN/f8rCMfJG1n2LPfkJSqS3JFRKR4t/SLoXPzUP7y4Rp9cCkiUgOcswA1M3/gBeAyIBYYaWaxRUx9yzkXn//1Wv5zGwN/Ac4H+gB/MbMwn6WXasfPz5g4qANv/fpCsnMc1730HS/M20iOuuSKiEgRAv39eOTq7vQ+9AXZj8fCg43gqW6wcpbX0UREpAxKcga0D7DRObfZOXccmAkML+HrDwE+d87tc87tBz4HhpYtqtQkvaMbM+euixnarTmPfbqOX766iB1qMiEiIkXolfk5j9eZTKMTuwEHmakwe6KKUBGRaqgkBWgrILXQ47T8sdNda2YrzewdM4sq5XOlFmoYEshzI3vy+PU9WLU9k8ue+YYPkrarQZGIiJzqy0kEuWOnjp3Igi8neZNHRETKzFdNiGYD0c65OPLOcv6rtC9gZuPNLNHMEtPT030US6o6M+O6XpHMmXgxMeH1uGtmEre98QPph46d+8kiIlI7ZKaVblxERKqskhSg24GoQo8j88cKOOcynCv4aPI1oFdJn1voNV5xziU45xIiIiJKkl1qkOjwevzntou477LOfLVuD4Of+poPV+zQ2VAREYGGkaUbFxGRKqskBehSoIOZxZhZEHAD8GHhCWbWotDDK4G1+T9/Cgw2s7D85kOD88dEzuDvZ0y4pB1zJvajdZN6TJyxnNve+IG9h3U2VESkVhv0AASGnDKU5YJIP/9ejwKJiEhZnbMAdc5lA3eSVziuBWY559aY2SQzuzJ/2kQzW2NmK4CJwJj85+4DHiaviF0KTMofEylW+6ah/GfChdw7tDNf/biHS5/8mreWbiNXnXJFRGqnuBFwxbPQMAowskMjecgm8Ktl0RzLzvE6nYiIlIJVxUscExISXGJiotcxpArYuOcQf3p3NUtS9tE7Ooy/Xd2dDs1CvY4lIrWYmS1zziV4naO2r5WfJ+/m1tcTGX1BGx6+qpvXcURE5DTFrZe+akIkUiHaNw1l5vgL+Me1cWzYc5jLn/2Gxz79kaMn9Im3iEht9rPYZtx6cQz/XrSVt5Zu8zqOiIiUkApQqfL8/IwRvaP48p5LuKJHS16Yt4nBTy1g3o97vI4mIlIiZjbUzNaZ2UYzu6+I4/eYWXL+dmZfmlkbL3JWN/cO7czFHcL58/urWbJFd/iIiFQHKkCl2mhSvw5PjojnzVvPJ8Df+NW0pYyZuoSNew57HU1EpFhm5g+8AFwGxAIjzSz2tGnLgYT87czeAf5RuSmrpwB/P57/5XlEhdVlwhvLSN13xOtIIiJyDipApdq5qF04n9zVnz8P68KylP0MfXoBk2Ynk5l1wutoIiJF6QNsdM5tds4dB2YCwwtPcM7Nc86drJ4WkbdtmZRAw5BAXrs5geycXMb9K1FrgYhIFacCVKqloAA/xl3clnl/GMD1CZFM/W4LAx+fz/TFW8lRt1wRqVpaAamFHqfljxXnFmBuUQfMbLyZJZpZYnp6ug8jVm9tI+rz0o292Lz3MLf+K1F9AkREqjAVoFKthdevw9+viWP2nf1o37Q+97+3miFPL+DTNbtwK2fBU93gwUZ531fO8jquiMhZmdmNQALwWFHHnXOvOOcSnHMJERERlRuuiuvbPpynfhHP0q37uPPN5WTn5HodSUREiqACVGqEbq0a8tb4C3j5xvPIdY6Ppz/DsXfvhMxUwOV9nz1RRaiIeGE7EFXocWT+2CnM7FLgfuBK59yxSspWo/w8riWTruzKF2t3c9+7q7R/tIhIFRTgdQARXzEzhnZrwaVdmnHssQkEHz3t77cTWfDlpLwNzUVEKs9SoIOZxZBXeN4A/LLwBDPrCfwTGOqcU4vvchh9YTQZPx3n6S82EOhvPHJVd/z8zOtYIiKSTwWo1DgB/n4EHN1V5DGXmYb+DBGRyuScyzazO4FPAX9ginNujZlNAhKdcx+Sd8ltfeBtMwPY5py70rPQ1dxdgzqQneN4ft5GsnMcj14bh7+KUBGRKkEFqNRMDSPzL7891fbcJjzyxjJ+8z8diG3ZwINgIlIbOefmAHNOG3ug0M+XVnqoGszM+N3gjvj7Gc98uYEc5/jHtXEE+OvOIxERr+m/xFIzDXoAAkNOGXIBIazoNJFvN+zl8me/YfzriazenulRQBERqUhmxt0/68g9P+vIuz9s57bpP6g7rohIFaAzoFIznbzP88tJkJkGDSOxQQ8wLG4E/Y6cYOp3W5jy7RY+S97NwE4RjO/fjgvaNib/0jcREakhJg7qQMOQQB6cvYZRry1m8s0JNKob5HUsEZFay5yreh3iEhISXGJiotcxpIY7ePQE/1qYwrTvUsj46TjdWzXk1v5tubxbc12mJSLFMrNlzrkEr3NorSydOat28tuZSUQ1DmHar/oQ1biu15FERGq04tZL/ZUttVaD4EB+M6gDC+/7H/52dXd+OpbNxBnLueSx+Uz5dgs/Hcv2OqKIiPjI5d1b8PotfUg/dIwrnv+WhRv3eh1JRKRWUgEqtV5woD+/PL81X9xzCa+M7kXLRsFM+iiZC//+JY98nEzK3p+8jigiIj5wQdsmfHhnP5qG1uGmKUuY/O0WquKVYCIiNZkKUJF8fn7G4K7NeXvCRbx7+0Vc3CGCqQtTGPD4fG6esoQvkneTo03NRUSqtejwerx7e18u7dKUhz9K5rdvJXFYV7yIiFQaNSESKcJ5rcM4b1QYuw8eZcaSbby5eBvjXk8kMiyEUee3YURCJE3q1/E6poiIlEH9OgG8NKoXL8zbyFNfrCcp9QDPjexJXGQjr6OJiNR4akIkUgIncnL5bM1u/r0ohUWb9xHk78elsU25PiGK/h0itMG5SC2iJkQ1y5It+/jtzOXsOXSMPwzpxK0Xt8VP/00XESm34tbLEhWgZjYUeAbwB15zzj162vF7gHFANpAOjHXObc0/lgOsyp+6zTl35bneT4uqVGXrdx9i5pJU3luexv4jJ2jeIJhre7Xi+l5RRIfX8zqeiFQwFaA1T+aRE9z37krmrt5FQpsw/u+6ONpF1Pc6lohItVbmAtTM/IH1wM+ANGApMNI5l1xozkBgsXPuiJndBgxwzv0i/9hh51yp/iuuRVWqg+PZuXy5djdvL0tj/ro95DroE9OY63pFMrRbcxoEB3odUUQqgArQmsk5x39+2M7DHyWTdSKHuwZ1YHz/tgRqWy4RkTIpTwF6IfCgc25I/uM/Ajjn/l7M/J7A8865vvmPVYBKjbf74FHe/WE7byemsnnvTwQF+DGwUwRX9mjFoC5NCQ709zqiiPiICtCabc+hozzw/ho+WbOL2BYNePiqrvRq09jrWCIi1U5x62VJmhC1AlILPU4Dzj/L/FuAuYUeB5tZInmX5z7qnHu/BO8pUq00axDMbQPaMeGStqxIy+TDpB3MXrmDT9fspl6QP4O7NufKHi3p1yFcn6aLiFRhTUODeXl0L+as2slDs9dw7Uvfc1V8S+67rAvNGwZ7HU9EpNrzaRdcM7sRSAAuKTTcxjm33czaAl+Z2Srn3KYinjseGA/QunVrX8YSqTRmRnxUI+KjGnH/sC4s3pzBhyt2MHf1Lt5bvp2wuoH8LLYZQ7o2p2/78P+eGV05C76cBJlp0DASBj0AcSO8/WVERGqxy7u34JKOEbw4fyOvLtjCZ8m7uX1AO8b2i6FukDYREBEpK59dgmtmlwLPAZc45/YU81rTgI+cc++c7T11WZHUNMezc1mwPp3ZK3fw1do9HDqWTb0gfwZ0asqvGiyl14q/YNlZ/31CYAhc8ayKUJEqSJfg1j7bMo7w14+T+Sx5N+H163DHwHaM7NNat1eIiJxFee4BDSCvCdEgYDt5TYh+6ZxbU2hOT+AdYKhzbkOh8TDgiHPumJmFA98Dwws3MCqKFlWpyY5n5/L95gw+XbOLz5N3896xXxPpt/fMiQ2j4O7VlR9QRM5KBWjtlZiyj8c/W8eizfto0TCYiYM6cO15kQQF6NYKEZHTlXcblsuBp8nbhmWKc+4RM5sEJDrnPjSzL4DuwM78p2xzzl1pZhcB/wRyAT/gaefc5HO9nxZVqS1ycx02KQzjzP8/dBjf3biRhOgw6gToU3aRqkIFaO3mnOO7TRk89uk6klIP0LxBMGP7RTOyT2tC1f1cRKRAuQrQyqZFVWqVp7pBZuoZw9tdOH2PPUvdIH8uahfOwM4RXNw+gqj/3969B9lZ13ccf3/Pde9nb9lNshuymAAKIRVBqojaEbWoWHuxgE5bp+OUdtRRsR2nilLUsVU7LbZjZ6pFx8EbUlAGEAEvKI2AJCCEhEAusCEbkmz2fj/XX/94nj17yZ7dPSe755w9+bzGM8/lPM+zv/xGnu/v+zy/3+80V2OmH0kXKRUloAJeIvrwgT6+/utDPHKon/poiPe/7iw+8PouNr50j8b1i8gZ73RmwRWR1XTFjXDPRyE5dwxoyzu+yC1Vl/Cr/b089NxJfr7vBAAdjdW8YWsLl21p5bItLbQ1aFZGEZFiMzPefO463nzuOp7pGebrDx/ifx5+gRM7buXLkW8SdXHvwOEj3j0elISKiKA3oCLlYYlZcJ1zHDo5xiOH+vnNwT4ee2GA4ckkAFvb6rhsi5eQXnp2M821kVL9K0TOCHoDKrkcGZig4b8vIpY4fuqXGtcvImcYvQEVKWfbr170ybiZsbWtnq1t9fzV67tIZxz7jo3wyKE+HjnUzx1P9HDro4cB2LKultd2NXPx5iYu6Wqmq6VGXXZFRIpgU3MNJE4s+F1muIc7dh3hym3radBYURE5gykBFVmDggFjW0eMbR0xrnvTFhKp8lgY/gAAE1tJREFUDLt7htjZPciu7gF+uuc4t+30xpW21kW4eHNTNil91YYG/XSAiMhqiXUuOK6/11r55B27+exde3jrq9q5ctt6/uC8dZq4SETOOEpARSpAJBTgkq5mLulqBraQyXhddnd2D7Lr8AC7ugd5YK/3VD4cNF65voHtnTF+r7OR7ZtinNNWTzCgt6QiIqctx7j+9nf/Mz9uvIy7fneUe3cf4yfPHCMSDHDZ1hbefv563np+G231GtMvIpVPY0BFzhC9I1M8+dIgT/cM8/SRIZ7pGWY0ngKgOhxkW0cD2zsb2d4Z44KNMc5urV04KV1ivKpIpdMYUFnSEvfJdMbx5EuDPLDnOA8+e4KXBiYwg+0dMd54zjreeE4rF53VpN8XFZE1TT/DIiJzZDKOF/vH2d0zxNNHhtndM8Tel0eIpzIAVIUDnNdez6s2NGQ/2wYeoOb+6095ss+7/1NJqJwxlIDKSnLO8fyJUR7ce4Jf7z/JU0eGSGcctZEgr9/SwuVbW3ndlhbObasnoJ4qIrKGKAEVkSUl0xn2nxhl37FR9h0byX4GJ7wZd3dEPkpnoO+U8zINnQQ+sbfYxRUpCSWgspqGJ5M8eqifHQdP8n8H+jjcPwFAQ1WIS7qaeW1XM6/tauLCzhjRkMbzi0j50iy4IrKkcDDABRu9LrjTnHOcGImz79gIHbf1L3zi8FHe9JWH2NpW533W1bG13VvXbI8iIssXqw5z5bb1XLltPeD9tMtvXxxgV/cAj3cP8MvnegFv7P+2jd7QiQs7YmzvjPGKdXUazy8iZU8JqIgsysxYH6tifawq5+yOY1XtbO+McbB3jB0H+0j43XgB2uqjnN1aS1dLLZtba7xlSw2bW2qpi+oWJCKymE3NNWxqruG9F3cC0D8WZ2f3IDu7B9jdM8QPdx7h2490A1ATCbJtY4wLO2Ocv6GB89bXs7WtbvGZzzWuX0SKTK0/EVm+HLM7NrzrC3xt+2sAb3KNIwMTHOwd40DvGAd7xzjcP84vnuulbyw+53KtdVG6/GS0q6WGza21dDZV09lYTWtdVOOdRETmaamLznlDmvZnPX+mZ5hnjnrj+b/72OHseH4z6Gqp5dz2Os5tr+fc9nrOW1/P5pYaos/eOfeePnzE2wYloSKyajQGVETycxpPy8fiKQ73j3O4f4Lu/nEO9/nL/gmOj0zNOTYSDLCxsYqOpmo6GqvpaKzJrnc2VbM+VkU4uMQMkXqyL6tAY0Cl3KXSGQ4PTLD/+CjPnxhl/4lRnj8+Snf/BOmM1+4zg0ejH2M9J08538U6ses1rl9ETo8mIRKRsjaVTPPSwAQ9gxMcHZykZ2iSo4OTHPWXvaNz354GDNobvK7B7fVVtDdEaffX18eq2HL8Ptp/9UkspRl7ZWUpAZW1aiqZ5oWT4xzoHeWFk+N8/DeXYpzaDsw4443VP6KjsTr7IHBj4/TDwGo6mqqpiagTnYgsTpMQiUhZqwoHs93DFhJPpTk2NJVNSHuyiekUh06O8cihPkamUtnjd0Q+hwUm514kOcnQvZ/lh4MX01oXpaUukl221Eb1m3siUtGqwkHO39jA+RsbvB17co/rv/TsZo4OTbKze5B7dh/Lvjmd1lgTZmOsmvaGKG31VbQ1RGmrj7Juznp0ZWfqVa8WkYqgBFRE1oRoKEhXay1drbU5j5lIpOgdiXN8ZIqOWxeesbchfoJ/+elzC39XFVowMW2ti9BSF6WxJkxjdYTGmjBNNRGqwgHMVmicqhpWIlJsi4zrv3n7q7O70hnHiZEpXh7ye6X4DwBfHvJ6p+x5eYT+sTiZBTrVNdaEaauP0loXpak2QnNNhOZa7zO93VQb9rZrIrknTNp9u8arilQIJaAiUjFqIiG6WkNekppjxl5r7GTPh/6Q/rE4fWMJ+sbi9I8l/O04fePe+sHeMR57IZ79DdSFREIBmmYlpdkEtdZbNvn7YtUR6qtCNFSFqa8KUV8VIjR7/KoaViJSCtP3lyUefgUDxsZGrxturr7n6YyjfyxO72ic3tEpekfmrg+MJ7zflR5PMDSZJNcIsJpIkKYa757aUBWmodq7d95w4LM0Jk/t1ZJ48CZ6N11FfVWY+miofCav00NFkZyUgIpIZcrxZN+uuJG6aIi6aIjNLbnfpk5LpTMMTCToH0swNJFkaMJrPA1OJBie8JZDE0mGJpO82DfO0MQQQxNJEunMotetDgezyegPxm+gLXNqw2rsvhu5P3kZDVUh6qvC1EaD1ERC2WVNJLj0REyrQQ0rkcqx/eoV+e83GDDaGqpoa6gCYosem0pnGPbvowPjSQbGE/56gsFxbzk8mWRkKkl33wQjU0ka4idggdwyNPoyl3/5IcCbWKnWvzfWRmeWtZEgNdNL/x7q7Z97bF00RFU46H8CM+uhwNyHhktZaw8VdU+XIlMCKiKVaZlP9pcSCga88U31Vcs+xznHZDLNoJ+wDk8kGZlKMTqVZHQq5X/89XiSdQdOnYUSoGbyOP/wv08v+rciwQA10WC2ITXTyJqbrM5ugFVHQl7jKhSkOuI1tKKhZTa61LAqiJldCfwHEARucc59ad73UeBW4GKgH7jGOddd7HKKFEMoGKClLkpLXXT5J928cK+WeO0GvnLVdkamkoxMJhmLp5lIpBhPpJmIpxiLp+gbSzA+MMFEPM14IsV4PLVgd+HFhINGVShIdM590ruPTq9Hw0GqQkE+c+AzNC3wtnbsvhu5Z+p1RIIBwqEAkWCASMgIBwPz9s3b9veFg5ZfIrwcuqevHpU1p2XNgns6gdPMPgV8EEgDH3XOPbDU39PMfiJyRrl524INq3RDJz0feJzRqRQjU0kmE+lso2r2cnK6sZVIMR6ft/SPm0imc3Z5W8z8RtedU9fR7k5NmAfC7fzb+XdmG05zlqHpxlOA6Pz9sxpZ0XnHRmY1vgrqVje/YQUrMhNyvrPgmlkQ2A+8DegBdgLvc849O+uYDwHbnXN/Z2bXAn/inLtmsesqVsoZZQX/e3bOEU9lGI+nmEhMJ6Uz9854Ks1UMs1UMjOznLUvnkz729Pfp5mc3p9MsyP+ZwRyzC78ivj3TqsaAkY2YQ0GjVDACAUCBANGOGj+0tsOBbyEdea7AOGAd0wo6J1306FraE6dOOXvDEfWc8sldxMKBAj51w2aYea97Q4GjIB5n2AAf+l9zLxjp/dPfxfwrxGYdfzMcu41vPOYc0zNcz8i9vO/nzO7vQtVE3/nV0lf8F4CfvnMwPDON5tZFtUqxZ9VsYplLXgWXD9w/hezAqeZ3T07cOIlmIPOua1+4PwycI2ZnQ9cC1wAbAR+bmbnOufSp/WvERGpJDm6Cwff+k/L6ia8HM45ppKZ7NP/2Q2nqdSs9UUaXVPJNG37+ha8fmOyl/v3HCeRyhBPZ0ikFu+CnK9w0IgEvTey4aDXwAoFjXAgkF0PBb3G1fT2zS/fQGv61LcQ/OLzxW4AXAocdM69AGBmtwHvAWbH0fcAN/nrdwBfMzNz5fhbaSKlsEK9WsBLRqZ7erSscDGBnG9rXayDRz/4FpIpRyKdJpFyJNIZkv49M+Evk/OWibQ7ZV8y7UhlMqQyjnTakcxkSGccqen9aed9l/HOjSczJDNp0vO+a0z1LvhPqI+f4GsPHSzoweVq2RG5kcZ5s9tbapK+u27g8tsXnkF/zrHm9eKeSVQtuz2dpGaPCVjOY81PlgECgbnJ7vT53x/7NO3u1PjTe9en+ZuHN8L038yWzbv+dPLs/4/pvNmYm1zPzqfnnjuzTfZ8m/Wdf/3pdYMvvPgZWlLFjZXL6YJbcOD099/mnIsDL5rZQf96j65M8UVEKsAKNqxyMTOqI16X29Z8ur3Nl6NhFYh18sT1b8tuO+c1cBKpuQ2r7DLXPn97upEVn3dsKp0hmXHe0m+UpfzlzP4Mk0lHc3rhrs0M9xT+7y9MBzC70nqA3891jHMuZWbDQAswJ+M3s+uA6wDOOuus1SqvSHlaofGqq26Rh4obYtWlK9dCct3TGzt58fp3kcnMJLcZ5002lck40s5bZhzZde8Y75POMG97Zn/GzVwjnXE4/7rpWfszjuw1087hnKPjJwvPbt8R6OdT73glDu/aznkxyDnIOHB418O/7vR29jjI/lscM+cveKzz/4b/t5izPXNM24GFH9auy/TRVBuZcyzM/ruz1r0igwNHZtY+l32/Pr0Pf9/0+dnvZp0zvW/O3wOacjyEWM1YuZwE9HQCZwfw2LxzOwourYhIpVrjDSuuuHHOYWaWfVNZexr57mnJ0bAi1ln8sqwQ59w3gG+A1wW3xMURkYUU4aHiilninh4IGNHACv6W6+nYkWN2+1gnf/vmLSUo0CJyxB+LdfLtv760BAVaRAliZdn86rqZXWdmu8xs18mTOZ5ai4hIaW2/2hsXEtsEmLcsxzEt4DWgwvPeNiyQLBfBUWDTrO1Of9+Cx5hZCG8a0YUf94tI+dt+NVy/B24a8pbleI8E3dNXi8q6qOW8Ac0ncPbMC5zLORfQU10RkTVjrbytLZ+3EDuBc8zsbLwYeC3w/nnH3A18AG+IynuBX2r8p4gUhe7pK09lXdSSs+D6CeV+4Aq8wLkTeL9zbu+sYz4MXDhr9r4/dc5dbWYXAN/HG/e5EfgFcM5SkxBpZj8RESlX+c6C65/zTuCreLPJf8s590Uz+zywyzl3t5lVAd8BLgIGgGun517IRbFSRETKWcGz4PpjOj8CPMBM4Nw7O3AC3wS+408yNID3dBf/uNvxJixKAR/WDLgiInKmcc7dB9w3b9+Ns9angD8vdrlERESKbTldcE8rcDrnvgh88TTKKCIiIiIiIhWgbCYhEhERERERkcqmBFRERERERESKQgmoiIiIiIiIFIUSUBERERERESmKJX+GpRTM7CRweIUu1wr0rdC1zhSqs8Ko3vKnOiuM6i1/K1lnm51z61boWgVTrCwLqrf8qc7ypzorjOotfytdZwvGy7JMQFeSme3K9/faznSqs8Ko3vKnOiuM6i1/qrPFqX4Ko3rLn+osf6qzwqje8lesOlMXXBERERERESkKJaAiIiIiIiJSFGdCAvqNUhdgDVKdFUb1lj/VWWFUb/lTnS1O9VMY1Vv+VGf5U50VRvWWv6LUWcWPARUREREREZHycCa8ARUREREREZEyoARUREREREREiqJiE1Azu9LMnjezg2b2j6Uuz1pgZt8ys14z21PqsqwVZrbJzB4ys2fNbK+ZfazUZVoLzKzKzB43s6f9evtcqcu0VphZ0Mx+Z2b3lrosa4WZdZvZM2b2lJntKnV5yo3iZf4UL/OneJk/xcrCKVbmr5ixsiLHgJpZENgPvA3oAXYC73POPVvSgpU5M3sTMAbc6pzbVuryrAVmtgHY4Jx70szqgSeAP9b/1xZnZgbUOufGzCwM7AA+5px7rMRFK3tm9gngEqDBOXdVqcuzFphZN3CJc04/SD6P4mVhFC/zp3iZP8XKwilW5q+YsbJS34BeChx0zr3gnEsAtwHvKXGZyp5z7mFgoNTlWEucc8ecc0/666PAPqCjtKUqf84z5m+G/U/lPQ1bYWbWCbwLuKXUZZGKoXhZAMXL/Cle5k+xsjCKleWvUhPQDuDIrO0edJOTVWZmXcBFwG9LW5K1we8e8xTQC/zMOad6W9pXgU8CmVIXZI1xwINm9oSZXVfqwpQZxUspOsXL5VOsLIhiZWGKFisrNQEVKSozqwPuBD7unBspdXnWAudc2jn3aqATuNTM1I1tEWZ2FdDrnHui1GVZgy53zr0GeAfwYb/7pIiUgOJlfhQr86NYeVqKFisrNQE9Cmyatd3p7xNZcf64jDuB7znnflTq8qw1zrkh4CHgylKXpcy9Afgjf4zGbcBbzOy7pS3S2uCcO+ove4Ef43U7FY/ipRSN4mXhFCuXTbGyQMWMlZWagO4EzjGzs80sAlwL3F3iMkkF8icI+Cawzzn376Uuz1phZuvMrNFfr8abAOW50paqvDnnPuWc63TOdeHd037pnPuLEher7JlZrT/hCWZWC7wd0MylMxQvpSgUL/OnWJk/xcrCFDtWVmQC6pxLAR8BHsAb5H67c25vaUtV/szsB8CjwHlm1mNmHyx1mdaANwB/ifeE7Sn/885SF2oN2AA8ZGa78RrAP3POaap0WQ3twA4zexp4HPiJc+7+EpepbCheFkbxsiCKl/lTrJRiKWqsrMifYREREREREZHyU5FvQEVERERERKT8KAEVERERERGRolACKiIiIiIiIkWhBFRERERERESKQgmoiIiIiIiIFIUSUJEKZmaNZvahUpdDRESkXClWihSXElCRytYIKKiKiIjkplgpUkRKQEUq25eALf4Pfv9rqQsjIiJShhQrRYrInHOlLoOIrBIz6wLudc5tK3FRREREypJipUhx6Q2oiIiIiIiIFIUSUBERERERESkKJaAilW0UqC91IURERMqYYqVIESkBFalgzrl+4DdmtkcTK4iIiJxKsVKkuDQJkYiIiIiIiBSF3oCKiIiIiIhIUSgBFRERERERkaJQAioiIiIiIiJFoQRUREREREREikIJqIiIiIiIiBSFElAREREREREpCiWgIiIiIiIiUhT/DygvQNsrrCy2AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def exponential_decay(t, y):\n", - " return np.array([-y[0], - (1.0 + y[0]) * y[1]])\n", - "\n", - "# Solve\n", - "y0 = np.array([2., 1.])\n", - "t_eval = np.linspace(0, 5, 10)\n", - "solution = ode_solver.integrate(exponential_decay, y0, t_eval)\n", - "\n", - "# Plot\n", - "def plot(t_sol, y_sol):\n", - " t_fine = np.linspace(0,t_eval[-1],1000)\n", - "\n", - " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", - " ax1.plot(t_fine, 2 * np.exp(-t_fine), t_sol, y_sol[0], \"o\")\n", - " ax1.set_xlabel(\"t\")\n", - " ax1.legend([\"2*exp(-t)\", \"y_sol[0]\"], loc=\"best\")\n", - "\n", - " ax2.plot(t_fine, np.exp(2 * np.exp(-t_fine) - t_fine - 2), t_sol, y_sol[1], \"o\")\n", - " ax2.set_xlabel(\"t\")\n", - " ax2.legend([\"exp(2*exp(-t) - t - 2)\", \"y_sol[1]\"], loc=\"best\")\n", - "\n", - " plt.tight_layout()\n", - " plt.show()\n", - " \n", - "plot(solution.t, solution.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also provide the Jacobian" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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c0/666PAPqCjtKUqf84z5m+G/U/lPQ1bYWbWCbwLuKXUZZGKoXhZAMXL/Cle5k+xsjCKleWvUhPQDuDIrO0edJOTVWZmXcBFwG9LW5K1we8e8xTQC/zMOad6W9pXgU8CmVIXZI1xwINm9oSZXVfqwpQZxUspOsXL5VOsLIhiZWGKFisrNQEVKSozqwPuBD7unBspdXnWAudc2jn3aqATuNTM1I1tEWZ2FdDrnHui1GVZgy53zr0GeAfwYb/7pIiUgOJlfhQr86NYeVqKFisrNQE9Cmyatd3p7xNZcf64jDuB7znnflTq8qw1zrkh4CHgylKXpcy9Afgjf4zGbcBbzOy7pS3S2uCcO+ove4Ef43U7FY/ipRSN4mXhFCuXTbGyQMWMlZWagO4EzjGzs80sAlwL3F3iMkkF8icI+Cawzzn376Uuz1phZuvMrNFfr8abAOW50paqvDnnPuWc63TOdeHd037pnPuLEher7JlZrT/hCWZWC7wd0MylMxQvpSgUL/OnWJk/xcrCFDtWVmQC6pxLAR8BHsAb5H67c25vaUtV/szsB8CjwHlm1mNmHyx1mdaANwB/ifeE7Sn/885SF2oN2AA8ZGa78RrAP3POaap0WQ3twA4zexp4HPiJc+7+EpepbCheFkbxsiCKl/lTrJRiKWqsrMifYREREREREZHyU5FvQEVERERERKT8KAEVERERERGRolACKiIiIiIiIkWhBFRERERERESKQgmoiIiIiIiIFIUSUJEKZmaNZvahUpdDRESkXClWihSXElCRytYIKKiKiIjkplgpUkRKQEUq25eALf4Pfv9rqQsjIiJShhQrRYrInHOlLoOIrBIz6wLudc5tK3FRREREypJipUhx6Q2oiIiIiIiIFIUSUBERERERESkKJaAilW0UqC91IURERMqYYqVIESkBFalgzrl+4DdmtkcTK4iIiJxKsVKkuDQJkYiIiIiIiBSF3oCKiIiIiIhIUSgBFRERERERkaJQAioiIiIiIiJFoQRUREREREREikIJqIiIiIiIiBSFElAREREREREpCiWgIiIiIiIiUhT/DygvQNsrrCy2AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def jacobian(t, y):\n", - " return np.array([[-1.0, 0.0], [-y[1], -(1 - y[0])]])\n", - "\n", - "solution = ode_solver.integrate(exponential_decay, y0, t_eval, jacobian=jacobian)\n", - "\n", - "plot(solution.t, solution.y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is also possible to provide a mass matrix to the `integrate` method by using the key-word argument `mass_matrix`, but this is currently not used by the Scikits ODE solver. \n", - "\n", - "Finally, we can specify events at which the solver should terminate" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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a05KfETfQkYBVEREIsrgsIePjrfwRmUjmw56w6grIYYVs3JYNSeXm8syg/bJsQKoiIhcK2stnX1DnGrr9QXSPtztfdR29FHb3k9T9xlGR7OYKENBqjeMFl4koGY4Y8P6VjLjfRsWERGRgIqNdnD7zBxun5nDwPAcPjrWyhv7GvjdwUZ+u8uNMzaK22flsHJ2LrddlxXR9xkVEZGJzxhDmjOWNGfsp3boBRgYHqGuox93R/+5UFrb4Q2p79Q30t47+InxCTFRFKUnUJiWSH5qPHmuBApSE8hPTSA/NZ6clHhiwnBzJHVrEREJe3HRUSyflcPyWTkMDnvYcqKNt/c3sOlAExv31hMX7eDWGVmsqsjl9pk52ipfREQiTlx0FFOzkpialXTR93sHhr3hdNRZU3dHH+6OfnbXdNDRN/SJ8Q4D2cnx5KfG+0JpAvmuUY9TE0hLjAn6WVQFUBERiSixvrB564ws/vHzlu2n2nl7fyNv729k08EmYqIMN03L5Osrylh4kU+YRUREIpEzLprrcpO5Ljf5ou/3DQ5T33mG+s5+Grr6qfM9ru/s50D9aTYdbGJw2POJ3xMf4yA/1XvmtCw7mSfvLh/3v4cCqIiIRKwoh2Hp1AyWTs3gybvK2ePu5O39jby1v4FQ7XEgIiISComx0UzPTmJ69sXPoJ7dIKmh8wx1vmDqDave58eau4NSpwKoiIhMCA6HYWFxGguL0/j2qpmhLkdERCSsGGPITIojMymOikJXyOpQABURkQknnHcFFBERmczCb1skERERERERmZAUQEVERERERCQoFEBFREREREQkKBRARUREREREJCgUQEVERERERCQoFEBFREREREQkKBRARUREREREJCgUQEVERERERCQojLU2NH+wMS1AdQAPmQm0BvB4k4XmzT+aN/9o3vyjefNPIOetxFqbFaBj+S3AvVPfV/7RvPlH8+YfzZt/NG/+CfS8XbR3hiyABpoxZoe1dlGo64g0mjf/aN78o3nzj+bNP5q3y9P8+Efz5h/Nm380b/7RvPknWPOmS3BFREREREQkKBRARUREREREJCgmUgB9JtQFRCjNm380b/7RvPlH8+YfzdvlaX78o3nzj+bNP5o3/2je/BOUeZswa0BFREREREQkvE2kM6AiIiIiIiISxhRARUREREREJCgiKoAaY1YaY44YY44bY/72Iu/HGWNe8L3/sTGmNPhVhp8xzNs3jTEHjTGVxph3jTEloagz3Fxp3kaN+6IxxhpjtN03Y5s3Y8z9vu+5A8aY3wS7xnA0hn+nxcaY940xu33/VleHos5wY4z5hTGm2Riz/xLvG2PM0755rTTGLAx2jaGm3ukf9U7/qHf6R73TP+qdVy8s+qa1NiK+gCigCpgKxAJ7gfILxvwZ8FPf43XAC6GuO9RfY5y3zwCJvsdf07yNbd5845KBD4CtwKJQ1x3qrzF+v5UBu4E03/PsUNcd6q8xztszwNd8j8uBU6GuOxy+gFuAhcD+S7y/GngLMMBS4ONQ1xzk+VHvHL95U+/0Y95849Q7r3Le1Dv9njf1zk/PW8j7ZiSdAV0CHLfWnrDWDgLrgbUXjFkL/Mr3+GVguTHGBLHGcHTFebPWvm+t7fM93QoUBrnGcDSW7zeAfwB+AJwJZnFhbCzz9l+Af7HWdgBYa5uDXGM4Gsu8WSDF99gF1AexvrBlrf0AaL/MkLXAs9ZrK5BqjMkLTnVhQb3TP+qd/lHv9I96p3/UO/0QDn0zkgJoAVA76rnb99pFx1hrh4EuICMo1YWvsczbaF/B+6nHZHfFefNdklBkrX0jmIWFubF8v80AZhhj/miM2WqMWRm06sLXWObt74GHjTFu4E3gz4NTWsS72v8HTjTqnf5R7/SPeqd/1Dv9o945Psa9b0YH8mAS2YwxDwOLgFtDXUu4M8Y4gB8BfxriUiJRNN5LiW7De8bgA2NMhbW2M6RVhb8HgV9aa//ZGHMj8GtjzBxrrSfUhYlMZuqdY6feeU3UO/2j3hmGIukMaB1QNOp5oe+1i44xxkTjPdXeFpTqwtdY5g1jzArg74A11tqBINUWzq40b8nAHOD3xphTeK+R36DNFMb0/eYGNlhrh6y1J4GjeJvqZDaWefsK8CKAtXYLEA9kBqW6yDam/wdOYOqd/lHv9I96p3/UO/2j3jk+xr1vRlIA3Q6UGWOmGGNi8W6UsOGCMRuAR32P7wXes77VtJPYFefNGLMA+BneBqo1BV6XnTdrbZe1NtNaW2qtLcW7/meNtXZHaMoNG2P5d/rveD/BxRiTifeyohPBLDIMjWXeaoDlAMaYWXibaEtQq4xMG4A/8e3qtxTostY2hLqoIFLv9I96p3/UO/2j3ukf9c7xMe59M2IuwbXWDhtjHgfewbvr1S+stQeMMd8FdlhrNwD/D++p9eN4F9euC13F4WGM8/ZDIAl4ybfvRI21dk3Iig4DY5w3ucAY5+0d4A5jzEFgBPhra+2kPtsyxnn7S+Dnxpi/wLupwp8qJIAx5nm8P5Rl+tb4fAeIAbDW/hTvmp/VwHGgD/hyaCoNDfVO/6h3+ke90z/qnf5R7/RPOPRNM8n/G4iIiIiIiEiQRNIluCIiIiIiIhLBFEBFREREREQkKBRARUREREREJCgUQEVERERERCQoFEBFREREREQkKBRARSYIY0yqMebPQl2HiIhIJFDfFAkNBVCRiSMVUCMVEREZG/VNkRBQABWZOL4PTDPG7DHG/DDUxYiIiIQ59U2REDDW2lDXICIBYIwpBV631s4JcSkiIiJhT31TJDR0BlRERERERESCQgFUREREREREgkIBVGTi6AaSQ12EiIhIhFDfFAkBBVCRCcJa2wb80RizX5spiIiIXJ76pkhoaBMiERERERERCQqdARUREREREZGgUAAVERERERGRoFAAFRERERERkaBQABUREREREZGgUAAVERERERGRoFAAFRERERERkaBQABUREREREZGg+P8pTONUEkrFowAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "def y0_equal_1(t, y):\n", - " return y[0] - 1\n", - "\n", - "# Solve\n", - "t_eval = np.linspace(0, 1, 10)\n", - "solution = ode_solver.integrate(exponential_decay, y0, t_eval, events=[y0_equal_1])\n", - "\n", - "# Plot\n", - "t_fine = np.linspace(0,t_eval[-1],1000)\n", - "\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", - "ax1.plot(t_fine, 2 * np.exp(-t_fine), solution.t, solution.y[0], \"o\", t_fine, np.ones_like(t_fine), \"k\")\n", - "ax1.set_xlabel(\"t\")\n", - "ax1.legend([\"2*exp(-t)\", \"y_sol[0]\", \"y=1\"], loc=\"best\")\n", - "\n", - "ax2.plot(t_fine, np.exp(2 * np.exp(-t_fine) - t_fine - 2), solution.t, solution.y[1], \"o\")\n", - "ax2.set_xlabel(\"t\")\n", - "ax2.legend([\"exp(2*exp(-t) - t - 2)\", \"y_sol[1]\"], loc=\"best\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Solving a model" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `solve` method is common to all ODE solvers. It takes a model, which contains all of the above information (derivatives function, initial conditions and optionally jacobian, mass matrix, events), and a time to evaluate `t_eval`, and calls `integrate` to solve this model. " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6AAAAEYCAYAAABCw5uAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOzde3yP9f/H8ce188YOzqcNy9lmthkqHZT6dVQok6gU6azopMMXKR0VKRURlZJFKjqHQgnDNuezbI4zNjsfr98foxy2HPbZrs/heb/d3GbXZ5/reqI+1/V6X+/r9TZM00RERERERESksrlZHUBERERERERcgwpQERERERERqRIqQEVERERERKRKqAAVERERERGRKqECVERERERERKqEhxUHrV27ttm0aVMrDi0iInLOVq1adcg0zTpWZtC5U0REHEl5505LCtCmTZsSHx9vxaFFRETOmWEYf1udQedOERFxJOWdOzUFV0RERERERKqEClARERERERGpEipARUREREREpEpY8gyoiIiIiIi4psLCQlJSUsjLy7M6itiAj48PwcHBeHp6ntXPqwAVEREREZEqk5KSgr+/P02bNsUwDKvjSAWYpklaWhopKSmEhoae1Xs0BVdERERERKpMXl4etWrVUvHpBAzDoFatWud0N7vCBahhGCGGYSwyDGODYRjrDcN4tKL7PCtJcTAuHEYFlX5NiquSw4qIiIgL0/WHiE2o+HQe5/pvaYspuEXA46ZprjYMwx9YZRjGL6ZpbrDBvsuWFAfzhkBhbun3Gcml3wNExFbaYUVERMSFne/1R1IcLBgNGSkQGAzdRuh6RURcVoULUNM09wH7jv0+0zCMjUAjoPIK0AWj//3wP64wt3S7PtBFRESkEpT8+gJuZVx/HJn3PGM2t8I0S5+Hcncz8PfxpLqPB5FHfuayzS/iUXxsepoGzUXExdn0GVDDMJoCUcDyMl4bbBhGvGEY8ampqRU7UEZKmZvNcraLiIiInKac6bSmabIjNYuvVqfw0vwN9J38F1Gjfy73+iOw8CB/bjvEXzvSWL7zMIu3phIXn8yEBVtpuW7cv8XncceK1qlLd7J69xEKi0sq+08qIidITk7miiuuoG3btoSFhfH222+f9Pr06dPZtWsXpmmWu49vv/2WV1999YzH2rdvHzfeeCMA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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# Create model\n", - "model = pybamm.BaseModel()\n", - "u = pybamm.Variable(\"u\")\n", - "v = pybamm.Variable(\"v\")\n", - "model.rhs = {u: -v, v: u}\n", - "model.initial_conditions = {u: 2, v: 1}\n", - "model.events['v=-2'] = v + 2\n", - "model.variables = {\"u\": u, \"v\": v}\n", - "\n", - "# Discretise using default discretisation\n", - "disc = pybamm.Discretisation()\n", - "disc.process_model(model)\n", - "\n", - "# Solve ########################\n", - "t_eval = np.linspace(0, 5, 30)\n", - "solution = ode_solver.solve(model, t_eval)\n", - "################################\n", - "\n", - "# Post-process, so that u and v can be called at any time t (using interpolation)\n", - "t_sol, y_sol = solution.t, solution.y\n", - "u = pybamm.ProcessedVariable(model.variables[\"u\"], t_sol, y_sol)\n", - "v = pybamm.ProcessedVariable(model.variables[\"v\"], t_sol, y_sol)\n", - "\n", - "# Plot\n", - "t_fine = np.linspace(0,t_eval[-1],1000)\n", - "\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13,4))\n", - "ax1.plot(t_fine, 2 * np.cos(t_fine) - np.sin(t_fine), t_sol, u(t_sol), \"o\")\n", - "ax1.set_xlabel(\"t\")\n", - "ax1.legend([\"2*cos(t) - sin(t)\", \"u\"], loc=\"best\")\n", - "\n", - "ax2.plot(t_fine, 2 * np.sin(t_fine) + np.cos(t_fine), t_sol, v(t_sol), \"o\", t_fine, -2 * np.ones_like(t_fine), \"k\")\n", - "ax2.set_xlabel(\"t\")\n", - "ax2.legend([\"2*sin(t) + cos(t)\", \"v\", \"v = -2\"], loc=\"best\")\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the discretisation or solver will have created the mass matrix and jacobian algorithmically, using the expression tree, so we do not need to calculate and input these manually." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/notebooks/unsteady_heat_equation.ipynb b/examples/notebooks/unsteady_heat_equation.ipynb index 5582dc8231..927d3e6eb1 100644 --- a/examples/notebooks/unsteady_heat_equation.ipynb +++ b/examples/notebooks/unsteady_heat_equation.ipynb @@ -267,7 +267,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "After solving, we can process variables using the class `pybamm.ProcessedVariable`. This returns a callable object which can be evaluated at any time and position by means of interpolating the solution. Processed variables provide a convinient way of comparing the solution to solutions from different models, or to exact solutions. Since all of the variables are names with informative strings, the user doesn't need to keep track of where they are stored in the state vector `solution.y`. This is particularly useful in complex models with lots of variables. Here we create `T_out` which is the processed temperature. In order to do so, we pass the variable, solution times, solution states, and the mesh to `pybamm.ProcessedVariable`." + "After solving, we can process variables using the class `pybamm.ProcessedVariable`. This returns a callable object which can be evaluated at any time and position by means of interpolating the solution. Processed variables provide a convinient way of comparing the solution to solutions from different models, or to exact solutions. Since all of the variables are names with informative strings, the user doesn't need to keep track of where they are stored in the state vector `solution.y`. This is particularly useful in complex models with lots of variables, and is automatically handled by the solution dictionary." ] }, { @@ -276,7 +276,7 @@ "metadata": {}, "outputs": [], "source": [ - "T_out = pybamm.ProcessedVariable(model.variables[\"Temperature\"], solution.t, solution.y, mesh)" + "T_out = solution[\"Temperature\"]" ] }, { diff --git a/examples/notebooks/using-model-options_thermal-example.ipynb b/examples/notebooks/using-model-options_thermal-example.ipynb index a1d6dee7c1..9f9ff48dda 100644 --- a/examples/notebooks/using-model-options_thermal-example.ipynb +++ b/examples/notebooks/using-model-options_thermal-example.ipynb @@ -159,7 +159,7 @@ " \"X-averaged cell temperature [K]\",\n", " \"Cell temperature [K]\",\n", "]\n", - "quick_plot = pybamm.QuickPlot(model, mesh, solution, output_variables)\n", + "quick_plot = pybamm.QuickPlot(solution, output_variables)\n", "\n", "import ipywidgets as widgets\n", "widgets.interact(quick_plot.plot, t=widgets.FloatSlider(min=0,max=1,step=0.05,value=0));" @@ -209,4 +209,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/examples/notebooks/using-submodels.ipynb b/examples/notebooks/using-submodels.ipynb index dd53cb4097..86939d2417 100644 --- a/examples/notebooks/using-submodels.ipynb +++ b/examples/notebooks/using-submodels.ipynb @@ -40,7 +40,26 @@ "cell_type": "code", "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "DomainError", + "evalue": "Primary broadcast from current collector domain must be to electrode\n or separator", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDomainError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpybamm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlithium_ion\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSPM\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/models/full_battery_models/lithium_ion/spm.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, options, name, build)\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 47\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mbuild\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 48\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuild_model\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 49\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mset_porosity_submodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/models/full_battery_models/base_battery_model.py\u001b[0m in \u001b[0;36mbuild_model\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 493\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuild_fundamental_and_external\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 494\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 495\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuild_coupled_variables\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 496\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 497\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuild_model_equations\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/models/full_battery_models/base_battery_model.py\u001b[0m in \u001b[0;36mbuild_coupled_variables\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 425\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 426\u001b[0m self.variables.update(\n\u001b[0;32m--> 427\u001b[0;31m \u001b[0msubmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_coupled_variables\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvariables\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 428\u001b[0m )\n\u001b[1;32m 429\u001b[0m \u001b[0msubmodels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msubmodel_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/models/submodels/particle/fickian/fickian_single_particle.py\u001b[0m in \u001b[0;36mget_coupled_variables\u001b[0;34m(self, variables)\u001b[0m\n\u001b[1;32m 44\u001b[0m T_k_xav = pybamm.PrimaryBroadcast(\n\u001b[1;32m 45\u001b[0m \u001b[0mvariables\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"X-averaged \"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdomain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\" electrode temperature\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 46\u001b[0;31m \u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdomain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\" particle\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 47\u001b[0m )\n\u001b[1;32m 48\u001b[0m \u001b[0mN_s_xav\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_flux_law\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mc_s_xav\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mT_k_xav\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/expression_tree/broadcasts.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, child, broadcast_domain, name)\u001b[0m\n\u001b[1;32m 85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchild\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbroadcast_domain\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 87\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchild\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbroadcast_domain\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbroadcast_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"primary\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 88\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 89\u001b[0m def check_and_set_domains(\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/expression_tree/broadcasts.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, child, broadcast_domain, broadcast_auxiliary_domains, broadcast_type, name)\u001b[0m\n\u001b[1;32m 53\u001b[0m \u001b[0;31m# perform some basic checks and set attributes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 54\u001b[0m domain, auxiliary_domains = self.check_and_set_domains(\n\u001b[0;32m---> 55\u001b[0;31m \u001b[0mchild\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbroadcast_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbroadcast_domain\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbroadcast_auxiliary_domains\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 56\u001b[0m )\n\u001b[1;32m 57\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbroadcast_type\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbroadcast_type\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/Energy_storage/PyBaMM/pybamm/expression_tree/broadcasts.py\u001b[0m in \u001b[0;36mcheck_and_set_domains\u001b[0;34m(self, child, broadcast_type, broadcast_domain, broadcast_auxiliary_domains)\u001b[0m\n\u001b[1;32m 102\u001b[0m raise pybamm.DomainError(\n\u001b[1;32m 103\u001b[0m \"\"\"Primary broadcast from current collector domain must be to electrode\n\u001b[0;32m--> 104\u001b[0;31m or separator\"\"\"\n\u001b[0m\u001b[1;32m 105\u001b[0m )\n\u001b[1;32m 106\u001b[0m elif child.domain[0] in [\n", + "\u001b[0;31mDomainError\u001b[0m: Primary broadcast from current collector domain must be to electrode\n or separator" + ] + } + ], "source": [ "model = pybamm.lithium_ion.SPM()" ] @@ -54,28 +73,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "porosity \n", - "convection \n", - "negative interface \n", - "positive interface \n", - "negative particle \n", - "positive particle \n", - "negative electrode \n", - "electrolyte conductivity \n", - "electrolyte diffusion \n", - "positive electrode \n", - "thermal \n", - "current collector \n" - ] - } - ], + "outputs": [], "source": [ "for name, submodel in model.submodels.items():\n", " print(name, submodel)" @@ -90,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -106,7 +106,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -122,28 +122,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "porosity \n", - "convection \n", - "negative interface \n", - "positive interface \n", - "negative particle \n", - "positive particle \n", - "negative electrode \n", - "electrolyte conductivity \n", - "electrolyte diffusion \n", - "positive electrode \n", - "thermal \n", - "current collector \n" - ] - } - ], + "outputs": [], "source": [ "for name, submodel in model.submodels.items():\n", " print(name, submodel)" @@ -158,20 +139,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{}" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "model.rhs" ] @@ -185,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -201,21 +171,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{Variable(-0x3cdc128c884d9c10, X-averaged negative particle surface concentration, children=[], domain=['current collector'], auxiliary_domains={}): Division(0x5bb7507a231a7556, /, children=['-3.0 * broadcast(Current function / Typical current [A] * function (sign)) / Negative electrode thickness [m] / Negative electrode thickness [m] + Separator thickness [m] + Positive electrode thickness [m]', 'Negative electrode surface area density [m-1] * Negative particle radius [m]'], domain=['current collector'], auxiliary_domains={}),\n", - " Variable(-0x5815563c2dff222c, X-averaged positive particle concentration, children=[], domain=['positive particle'], auxiliary_domains={'secondary': \"['current collector']\"}): Multiplication(-0x2939bd9275c629cb, *, children=['-1.0 / Positive particle radius [m] ** 2.0 / Positive electrode diffusivity / 96485.33289 * Maximum concentration in negative electrode [mol.m-3] * Negative electrode thickness [m] + Separator thickness [m] + Positive electrode thickness [m] / function (abs_non_zero)', 'div(-Positive electrode diffusivity / Positive electrode diffusivity * grad(X-averaged positive particle concentration))'], domain=['positive particle'], auxiliary_domains={'secondary': \"['current collector']\"})}" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "model.rhs" ] @@ -229,22 +187,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# create geometry\n", "geometry = model.default_geometry\n", @@ -265,8 +210,8 @@ "t_eval = np.linspace(0, 0.15, 100)\n", "solution = model.default_solver.solve(model, t_eval)\n", "\n", - "# post-process voltage for plotting\n", - "voltage = pybamm.ProcessedVariable(model.variables['Terminal voltage [V]'], solution.t, solution.y, mesh=mesh)\n", + "# extract voltage\n", + "voltage = solution['Terminal voltage [V]']\n", "\n", "# plot\n", "plt.plot(solution.t, voltage(solution.t))\n", @@ -287,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -300,12 +245,28 @@ "source": [ "Submodels can be added to the `model.submodels` dictionary in the same way that we changed the submodels earlier. \n", "\n", + "We use the simplest model for the external circuit, which is the \"current control\" submodel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.submodels[\"external circuit\"] = pybamm.external_circuit.CurrentControl(model.param)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "We want to build a 1D model, so select the `Uniform` current collector model (if the current collectors are behaving uniformly, then a 1D model is appropriate). We also want the model to be isothermal, so slect the thermal model accordingly. " ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -322,7 +283,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -343,7 +304,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -364,7 +325,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -385,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -407,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -423,7 +384,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -439,22 +400,9 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# process model and geometry\n", "param = model.default_parameter_values\n", @@ -473,8 +421,8 @@ "solver = pybamm.ScipySolver()\n", "solution = solver.solve(model, t_eval)\n", "\n", - "# post-process voltage for plotting\n", - "voltage = pybamm.ProcessedVariable(model.variables['Terminal voltage [V]'], solution.t, solution.y, mesh=mesh)\n", + "# extract voltage\n", + "voltage = solution['Terminal voltage [V]']\n", "\n", "# plot\n", "plt.plot(solution.t, voltage(solution.t))\n", @@ -517,7 +465,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/examples/scripts/DFN.py b/examples/scripts/DFN.py index 8697cfc539..4811df8a19 100644 --- a/examples/scripts/DFN.py +++ b/examples/scripts/DFN.py @@ -8,13 +8,14 @@ pybamm.set_logging_level("INFO") # load model -model = pybamm.lithium_ion.DFN() +model = pybamm.lithium_ion.DFN({"operating mode": "voltage"}) # create geometry geometry = model.default_geometry # load parameter values and process model and geometry param = model.default_parameter_values +param.update({"Voltage function [V]": 4.1}, check_already_exists=False) param.process_model(model) param.process_geometry(geometry) @@ -35,5 +36,5 @@ solution = solver.solve(model, t_eval) # plot -plot = pybamm.QuickPlot(model, mesh, solution) +plot = pybamm.QuickPlot(solution) plot.dynamic_plot() diff --git a/examples/scripts/SPM_compare_particle_grid.py b/examples/scripts/SPM_compare_particle_grid.py index 8190e02875..eb0d8f2a2d 100644 --- a/examples/scripts/SPM_compare_particle_grid.py +++ b/examples/scripts/SPM_compare_particle_grid.py @@ -49,25 +49,15 @@ # solve model solutions = [None] * len(models) -t_eval = np.linspace(0, 0.17, 100) +t_eval = np.linspace(0, 0.25, 100) for i, model in enumerate(models): solutions[i] = model.default_solver.solve(model, t_eval) # process particle concentration variables processed_variables = [None] * len(models) for i, solution in enumerate(solutions): - c_n = pybamm.ProcessedVariable( - models[i].variables["X-averaged negative particle concentration [mol.m-3]"], - solution.t, - solution.y, - mesh=meshes[i], - ) - c_p = pybamm.ProcessedVariable( - models[i].variables["X-averaged positive particle concentration [mol.m-3]"], - solution.t, - solution.y, - mesh=meshes[i], - ) + c_n = solution["X-averaged negative particle concentration [mol.m-3]"] + c_p = solution["X-averaged positive particle concentration [mol.m-3]"] processed_variables[i] = {"c_n": c_n, "c_p": c_p} diff --git a/examples/scripts/SPMe.py b/examples/scripts/SPMe.py index d96d9bec9d..422a338d23 100644 --- a/examples/scripts/SPMe.py +++ b/examples/scripts/SPMe.py @@ -30,5 +30,5 @@ solution = pybamm.CasadiSolver(method="cvodes", mode="fast").solve(model, t_eval) # plot -plot = pybamm.QuickPlot(model, mesh, solution) +plot = pybamm.QuickPlot(solution) plot.dynamic_plot() diff --git a/examples/scripts/SPMe_SOC.py b/examples/scripts/SPMe_SOC.py index 411eb50f67..1d9942cfc3 100644 --- a/examples/scripts/SPMe_SOC.py +++ b/examples/scripts/SPMe_SOC.py @@ -27,14 +27,14 @@ w = 0.207 / factor A = h * w l_s = 2.5e-5 - l1d = (l_n + l_p + l_s) + l1d = l_n + l_p + l_s vol = h * w * l1d vol_cm3 = vol * 1e6 tot_cap = 0.0 tot_time = 0.0 fig, axes = plt.subplots(1, 2, sharey=True) I_mag = 1.01 / factor - print('*' * 30) + print("*" * 30) for enum, I_app in enumerate([-1.0, 1.0]): I_app *= I_mag # load model @@ -44,27 +44,33 @@ # load parameter values and process model and geometry param = model.default_parameter_values param.update( - {"Electrode height [m]": h, - "Electrode width [m]": w, - "Negative electrode thickness [m]": l_n, - "Positive electrode thickness [m]": l_p, - "Separator thickness [m]": l_s, - "Lower voltage cut-off [V]": 2.8, - "Upper voltage cut-off [V]": 4.7, - "Maximum concentration in negative electrode [mol.m-3]": 25000, - "Maximum concentration in positive electrode [mol.m-3]": 50000, - "Initial concentration in negative electrode [mol.m-3]": 12500, - "Initial concentration in positive electrode [mol.m-3]": 25000, - "Negative electrode surface area density [m-1]": 180000.0, - "Positive electrode surface area density [m-1]": 150000.0, - "Typical current [A]": I_app, - } + { + "Electrode height [m]": h, + "Electrode width [m]": w, + "Negative electrode thickness [m]": l_n, + "Positive electrode thickness [m]": l_p, + "Separator thickness [m]": l_s, + "Lower voltage cut-off [V]": 2.8, + "Upper voltage cut-off [V]": 4.7, + "Maximum concentration in negative electrode [mol.m-3]": 25000, + "Maximum concentration in positive electrode [mol.m-3]": 50000, + "Initial concentration in negative electrode [mol.m-3]": 12500, + "Initial concentration in positive electrode [mol.m-3]": 25000, + "Negative electrode surface area density [m-1]": 180000.0, + "Positive electrode surface area density [m-1]": 150000.0, + "Current function [A]": I_app, + } ) param.process_model(model) param.process_geometry(geometry) s_var = pybamm.standard_spatial_vars - var_pts = {s_var.x_n: 5, s_var.x_s: 5, s_var.x_p: 5, - s_var.r_n: 5, s_var.r_p: 10} + var_pts = { + s_var.x_n: 5, + s_var.x_s: 5, + s_var.x_p: 5, + s_var.r_n: 5, + s_var.r_p: 10, + } # set mesh mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) # discretise model @@ -73,59 +79,56 @@ # solve model t_eval = np.linspace(0, 0.2, 100) sol = model.default_solver.solve(model, t_eval) - var = "Positive electrode average extent of lithiation" - xpext = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) - var = "Negative electrode average extent of lithiation" - xnext = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) - var = "X-averaged positive particle surface concentration" - xpsurf = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) - var = "X-averaged negative particle surface concentration" - xnsurf = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) - time = pybamm.ProcessedVariable(model.variables["Time [h]"], - sol.t, sol.y, mesh=mesh) + xpext = sol["Positive electrode average extent of lithiation"] + xnext = sol["Negative electrode average extent of lithiation"] + xpsurf = sol["X-averaged positive particle surface concentration"] + xnsurf = sol["X-averaged negative particle surface concentration"] + time = sol["Time [h]"] # Coulomb counting time_hours = time(sol.t) dc_time = np.around(time_hours[-1], 3) # Capacity mAh cap = np.absolute(I_app * 1000 * dc_time) cap_time = np.absolute(I_app * 1000 * time_hours) - axes[enum].plot(cap_time, - xnext(sol.t), 'r-', label='Average Neg') - axes[enum].plot(cap_time, - xpext(sol.t), 'b-', label='Average Pos') - axes[enum].plot(cap_time, - xnsurf(sol.t), 'r--', label='Surface Neg') - axes[enum].plot(cap_time, - xpsurf(sol.t), 'b--', label='Surface Pos') - axes[enum].set_xlabel('Capacity [mAh]') + axes[enum].plot(cap_time, xnext(sol.t), "r-", label="Average Neg") + axes[enum].plot(cap_time, xpext(sol.t), "b-", label="Average Pos") + axes[enum].plot(cap_time, xnsurf(sol.t), "r--", label="Surface Neg") + axes[enum].plot(cap_time, xpsurf(sol.t), "b--", label="Surface Pos") + axes[enum].set_xlabel("Capacity [mAh]") handles, labels = axes[enum].get_legend_handles_labels() axes[enum].legend(handles, labels) if I_app < 0.0: - axes[enum].set_ylabel('Extent of Lithiation, Elecrode Ratio: ' - + str(e_ratio)) - axes[enum].title.set_text('Charge') + axes[enum].set_ylabel( + "Extent of Lithiation, Elecrode Ratio: " + str(e_ratio) + ) + axes[enum].title.set_text("Charge") else: - axes[enum].title.set_text('Discharge') - print('Applied Current', I_app, 'A', 'Time', - dc_time, 'hrs', 'Capacity', cap, 'mAh') + axes[enum].title.set_text("Discharge") + print( + "Applied Current", + I_app, + "A", + "Time", + dc_time, + "hrs", + "Capacity", + cap, + "mAh", + ) tot_cap += cap tot_time += dc_time - print('Anode : Cathode thickness', e_ratio) - print('Total Charge/Discharge Time', tot_time, 'hrs') - print('Total Capacity', np.around(tot_cap, 3), 'mAh') + print("Anode : Cathode thickness", e_ratio) + print("Total Charge/Discharge Time", tot_time, "hrs") + print("Total Capacity", np.around(tot_cap, 3), "mAh") specific_cap = np.around(tot_cap, 3) / vol_cm3 - print('Total Capacity', specific_cap, 'mAh.cm-3') + print("Total Capacity", specific_cap, "mAh.cm-3") capacities.append(tot_cap) specific_capacities.append(specific_cap) fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) ax1.plot(thicknesses / l_p, capacities) ax2.plot(thicknesses / l_p, specific_capacities) -ax1.set_ylabel('Capacity [mAh]') -ax2.set_ylabel('Specific Capacity [mAh.cm-3]') -ax2.set_xlabel('Anode : Cathode thickness') +ax1.set_ylabel("Capacity [mAh]") +ax2.set_ylabel("Specific Capacity [mAh.cm-3]") +ax2.set_xlabel("Anode : Cathode thickness") diff --git a/examples/scripts/SPMe_step.py b/examples/scripts/SPMe_step.py index 666976c61d..e1cf9a3e1f 100644 --- a/examples/scripts/SPMe_step.py +++ b/examples/scripts/SPMe_step.py @@ -37,26 +37,15 @@ step_solver = model.default_solver step_solution = None while time < end_time: - current_step_sol = step_solver.step(model, dt=dt, npts=10) - if not step_solution: - # create solution object on first step - step_solution = current_step_sol - else: - # append solution from the current step to step_solution - step_solution.append(current_step_sol) + step_solution = step_solver.step(step_solution, model, dt=dt, npts=10) time += dt # plot -voltage = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=mesh - -) -step_voltage = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], step_solution.t, step_solution.y, mesh=mesh -) +voltage = solution["Terminal voltage [V]"] +step_voltage = step_solution["Terminal voltage [V]"] plt.plot(solution.t, voltage(solution.t), "b-", label="SPMe (continuous solve)") plt.plot( - step_solution.t, step_voltage(step_solution.t), "ro", label="SPMe (steppped solve)" + step_solution.t, step_voltage(step_solution.t), "ro", label="SPMe (stepped solve)" ) plt.xlabel(r"$t$") plt.ylabel("Terminal voltage [V]") diff --git a/examples/scripts/compare-dae-solver.py b/examples/scripts/compare-dae-solver.py index a092a78652..9bf4dcabb3 100644 --- a/examples/scripts/compare-dae-solver.py +++ b/examples/scripts/compare-dae-solver.py @@ -24,14 +24,32 @@ disc.process_model(model) # solve model -t_eval = np.linspace(0, 0.17, 100) +t_eval = np.linspace(0, 0.25, 100) casadi_sol = pybamm.CasadiSolver(atol=1e-8, rtol=1e-8).solve(model, t_eval) -klu_sol = pybamm.IDAKLUSolver(atol=1e-8, rtol=1e-8).solve(model, t_eval) -scikits_sol = pybamm.ScikitsDaeSolver(atol=1e-8, rtol=1e-8).solve(model, t_eval) +solutions = [casadi_sol] + +if pybamm.have_idaklu(): + klu_sol = pybamm.IDAKLUSolver(atol=1e-8, rtol=1e-8).solve(model, t_eval) + solutions.append(klu_sol) +else: + pybamm.logger.error( + """ + Cannot solve model with IDA KLU solver as solver is not installed. + Please consult installation instructions on GitHub. + """ + ) +if pybamm.have_scikits_odes(): + scikits_sol = pybamm.ScikitsDaeSolver(atol=1e-8, rtol=1e-8).solve(model, t_eval) + solutions.append(scikits_sol) +else: + pybamm.logger.error( + """ + Cannot solve model with Scikits DAE solver as solver is not installed. + Please consult installation instructions on GitHub. + """ + ) # plot -models = [model, model, model] -solutions = [casadi_sol, klu_sol, casadi_sol] -plot = pybamm.QuickPlot(models, mesh, solutions) +plot = pybamm.QuickPlot(solutions) plot.dynamic_plot() diff --git a/examples/scripts/compare_SPM_diffusion_models.py b/examples/scripts/compare_SPM_diffusion_models.py index 12aa8cd190..bac771b7a2 100644 --- a/examples/scripts/compare_SPM_diffusion_models.py +++ b/examples/scripts/compare_SPM_diffusion_models.py @@ -23,7 +23,7 @@ # load parameter values and process models and geometry param = models[0].default_parameter_values -param.update({"Typical current [A]": 1}) +param.update({"Current function [A]": 1}) for model in models: param.process_model(model) @@ -42,7 +42,7 @@ # solve model solutions = [None] * len(models) -t_eval = np.linspace(0, 0.17, 100) +t_eval = np.linspace(0, 0.25, 100) for i, model in enumerate(models): solutions[i] = model.default_solver.solve(model, t_eval) @@ -52,5 +52,5 @@ "X-averaged positive particle surface concentration [mol.m-3]", "Terminal voltage [V]", ] -plot = pybamm.QuickPlot(models, mesh, solutions, variables) +plot = pybamm.QuickPlot(solutions, variables) plot.dynamic_plot() diff --git a/examples/scripts/compare_comsol/compare_comsol_DFN.py b/examples/scripts/compare_comsol/compare_comsol_DFN.py index b2f757bf09..da6121c4be 100644 --- a/examples/scripts/compare_comsol/compare_comsol_DFN.py +++ b/examples/scripts/compare_comsol/compare_comsol_DFN.py @@ -33,7 +33,7 @@ param = pybamm_model.default_parameter_values param["Electrode width [m]"] = 1 param["Electrode height [m]"] = 1 -param["Typical current [A]"] = 24 * C_rates[C_rate] +param["Current function [A]"] = 24 * C_rates[C_rate] param.process_model(pybamm_model) param.process_geometry(geometry) @@ -51,7 +51,7 @@ # solve model at comsol times time = comsol_variables["time"] / tau.evaluate(0) -solution = pybamm.CasadiSolver(mode="fast").solve(pybamm_model, time) +pybamm_solution = pybamm.CasadiSolver(mode="fast").solve(pybamm_model, time) # Make Comsol 'model' for comparison @@ -63,7 +63,7 @@ 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 + :class:`pybamm.QuickPlot` (interpolate in space to match edges, and then create function to interpolate in time) """ variable = comsol_variables[variable_name] @@ -78,11 +78,23 @@ def get_interp_fun(variable_name, domain): variable = interp.interp1d(comsol_x, variable, axis=0)(pybamm_x) def myinterp(t): - return interp.interp1d(comsol_t, variable)(t)[:, np.newaxis] + try: + return interp.interp1d( + comsol_t, variable, fill_value="extrapolate", bounds_error=False + )(t)[:, np.newaxis] + except ValueError as err: + raise ValueError( + """Failed to interpolate '{}' with time range [{}, {}] at time {}. + Original error: {}""".format( + variable_name, comsol_t[0], comsol_t[-1], t, err + ) + ) # Make sure to use dimensional time fun = pybamm.Function(myinterp, pybamm.t * tau, name=variable_name + "_comsol") fun.domain = domain + fun.mesh = mesh.combine_submeshes(*domain) + fun.secondary_mesh = None return fun @@ -92,7 +104,17 @@ def myinterp(t): 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 = interp.interp1d(comsol_t, comsol_variables["voltage"]) +comsol_voltage = pybamm.Function( + interp.interp1d( + comsol_t, + comsol_variables["voltage"], + fill_value="extrapolate", + bounds_error=False, + ), + pybamm.t * tau, +) +comsol_voltage.mesh = None +comsol_voltage.secondary_mesh = None # Create comsol model with dictionary of Matrix variables comsol_model = pybamm.BaseModel() @@ -104,14 +126,13 @@ def myinterp(t): "Negative electrode potential [V]": comsol_phi_n, "Electrolyte potential [V]": comsol_phi_e, "Positive electrode potential [V]": comsol_phi_p, - "Terminal voltage [V]": pybamm.Function(comsol_voltage, pybamm.t * tau), + "Terminal voltage [V]": comsol_voltage, } - +comsol_solution = pybamm.CasadiSolver(mode="fast").solve(pybamm_model, time) +comsol_solution.model = comsol_model # plot plot = pybamm.QuickPlot( - [pybamm_model, comsol_model], - mesh, - [solution, solution], + [pybamm_solution, comsol_solution], output_variables=comsol_model.variables.keys(), labels=["PyBaMM", "Comsol"], ) diff --git a/examples/scripts/compare_comsol/discharge_curve.py b/examples/scripts/compare_comsol/discharge_curve.py index e8ecb43502..33e3b14c56 100644 --- a/examples/scripts/compare_comsol/discharge_curve.py +++ b/examples/scripts/compare_comsol/discharge_curve.py @@ -57,7 +57,7 @@ comsol_voltage = comsol_variables["voltage"] # update current density - param["Typical current [A]"] = 24 * C_rate + param["Current function [A]"] = 24 * C_rate param.update_model(model, disc) # discharge timescale @@ -67,19 +67,15 @@ # solve model at comsol times t = comsol_time / tau - solution = model.default_solver.solve(model, t) + solution = pybamm.CasadiSolver(mode="fast").solve(model, t) # discharge capacity - discharge_capacity = pybamm.ProcessedVariable( - model.variables["Discharge capacity [A.h]"], solution.t, solution.y, mesh=mesh - ) + discharge_capacity = solution["Discharge capacity [A.h]"] discharge_capacity_sol = discharge_capacity(solution.t) - comsol_discharge_capacity = comsol_time * param["Typical current [A]"] / 3600 + comsol_discharge_capacity = comsol_time * param["Current function [A]"] / 3600 # extract the voltage - voltage = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=mesh - ) + voltage = solution["Terminal voltage [V]"] voltage_sol = voltage(solution.t) # calculate the difference between the two solution methods diff --git a/examples/scripts/compare_extrapolations.py b/examples/scripts/compare_extrapolations.py index defcf557f0..725d48f61c 100644 --- a/examples/scripts/compare_extrapolations.py +++ b/examples/scripts/compare_extrapolations.py @@ -25,8 +25,6 @@ # plot the two sols -models = [sim_lin.built_model, sim_quad.built_model] solutions = [sim_lin.solution, sim_quad.solution] -plot = pybamm.QuickPlot(models, sim_lin.mesh, solutions) +plot = pybamm.QuickPlot(solutions) plot.dynamic_plot() - diff --git a/examples/scripts/compare_lead_acid.py b/examples/scripts/compare_lead_acid.py index 028a2a1629..cac469e749 100644 --- a/examples/scripts/compare_lead_acid.py +++ b/examples/scripts/compare_lead_acid.py @@ -18,14 +18,14 @@ # load models models = [ pybamm.lead_acid.LOQS(), - pybamm.lead_acid.FOQS(), + # pybamm.lead_acid.FOQS(), pybamm.lead_acid.Composite(), pybamm.lead_acid.Full(), ] # load parameter values and process models and geometry param = models[0].default_parameter_values -param.update({"Typical current [A]": 10, "Initial State of Charge": 1}) +param.update({"Current function [A]": 10, "Initial State of Charge": 1}) for model in models: param.process_model(model) @@ -55,5 +55,5 @@ "Electrolyte potential [V]", "Terminal voltage [V]", ] -plot = pybamm.QuickPlot(models, mesh, solutions, output_variables) +plot = pybamm.QuickPlot(solutions, output_variables) plot.dynamic_plot() diff --git a/examples/scripts/compare_lead_acid_3D.py b/examples/scripts/compare_lead_acid_3D.py index a472d828a2..5b3f0e93bc 100644 --- a/examples/scripts/compare_lead_acid_3D.py +++ b/examples/scripts/compare_lead_acid_3D.py @@ -45,8 +45,7 @@ param = models[0].default_parameter_values param.update( { - "Typical current [A]": 1, - "Bruggeman coefficient": 0.001, + "Current function [A]": 1, "Initial State of Charge": 1, "Typical electrolyte concentration [mol.m-3]": 5600, "Negative electrode reference exchange-current density [A.m-2]": 0.08, @@ -82,11 +81,10 @@ for i, model in enumerate(models): solution = model.default_solver.solve(model, t_eval) solutions[i] = solution - pybamm.post_process_variables(model.variables, solution.t, solution.y, mesh=mesh) # plot output_variables = [ - "Local current collector potential difference [V]", + "Local voltage [V]", "Negative current collector potential [V]", "Positive current collector potential [V]", "X-averaged electrolyte concentration", @@ -94,5 +92,5 @@ "Current collector current density", "Terminal voltage [V]", ] -plot = pybamm.QuickPlot(models, mesh, solutions, output_variables) +plot = pybamm.QuickPlot(solutions, output_variables) plot.dynamic_plot() diff --git a/examples/scripts/compare_lithium_ion.py b/examples/scripts/compare_lithium_ion.py index d92fb46f84..507ed5dc5d 100644 --- a/examples/scripts/compare_lithium_ion.py +++ b/examples/scripts/compare_lithium_ion.py @@ -16,7 +16,7 @@ pybamm.set_logging_level("INFO") # load models -options = {"thermal": "isothermal"} +options = {"thermal": "isothermal", "surface form": "differential"} models = [ pybamm.lithium_ion.SPM(options), pybamm.lithium_ion.SPMe(options), @@ -26,7 +26,8 @@ # load parameter values and process models and geometry param = models[0].default_parameter_values -param["Typical current [A]"] = 1.0 +param["Current function [A]"] = 1.0 + for model in models: param.process_model(model) @@ -50,5 +51,5 @@ solutions[i] = model.default_solver.solve(model, t_eval) # plot -plot = pybamm.QuickPlot(models, mesh, solutions) +plot = pybamm.QuickPlot(solutions) plot.dynamic_plot() diff --git a/examples/scripts/compare_lithium_ion_3D.py b/examples/scripts/compare_lithium_ion_3D.py index f271b63cf1..1ef238e541 100644 --- a/examples/scripts/compare_lithium_ion_3D.py +++ b/examples/scripts/compare_lithium_ion_3D.py @@ -58,5 +58,5 @@ # plot # TO DO: plotting 3D variables output_variables = ["Terminal voltage [V]"] -plot = pybamm.QuickPlot(models, mesh, solutions, output_variables) +plot = pybamm.QuickPlot(solutions, output_variables) plot.dynamic_plot() diff --git a/examples/scripts/compare_lithium_ion_particle_distribution.py b/examples/scripts/compare_lithium_ion_particle_distribution.py new file mode 100644 index 0000000000..cbe9158a25 --- /dev/null +++ b/examples/scripts/compare_lithium_ion_particle_distribution.py @@ -0,0 +1,82 @@ +# +# Compare lithium-ion battery models +# +import argparse +import numpy as np +import pybamm + +parser = argparse.ArgumentParser() +parser.add_argument( + "--debug", action="store_true", help="Set logging level to 'DEBUG'." +) +args = parser.parse_args() +if args.debug: + pybamm.set_logging_level("DEBUG") +else: + pybamm.set_logging_level("INFO") + +# load models +options = {"thermal": "isothermal"} +models = [ + pybamm.lithium_ion.DFN(options, name="standard DFN"), + pybamm.lithium_ion.DFN(options, name="particle DFN"), +] + + +# load parameter values and process models and geometry +params = [models[0].default_parameter_values, models[1].default_parameter_values] +params[0]["Typical current [A]"] = 1.0 +params[0].process_model(models[0]) + + +params[1]["Typical current [A]"] = 1.0 + + +def negative_distribution(x): + return 1 + x + + +def positive_distribution(x): + return 1 + (x - (1 - models[1].param.l_p)) + + +params[1]["Negative particle distribution in x"] = negative_distribution +params[1]["Positive particle distribution in x"] = positive_distribution +params[1].process_model(models[1]) + +# set mesh +var = pybamm.standard_spatial_vars +var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10, var.r_n: 5, var.r_p: 5} + +# discretise models +for param, model in zip(params, models): + # create geometry + geometry = model.default_geometry + param.process_geometry(geometry) + mesh = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts) + disc = pybamm.Discretisation(mesh, model.default_spatial_methods) + disc.process_model(model) + +# solve model +solutions = [None] * len(models) +t_eval = np.linspace(0, 0.3, 100) +for i, model in enumerate(models): + solutions[i] = model.default_solver.solve(model, t_eval) + + +output_variables = [ + "Negative particle surface concentration", + "Electrolyte concentration", + "Positive particle surface concentration", + "Current [A]", + "Negative electrode potential [V]", + "Electrolyte potential [V]", + "Positive electrode potential [V]", + "Terminal voltage [V]", + "Negative particle distribution in x", + "Positive particle distribution in x", +] + +# plot +plot = pybamm.QuickPlot(solutions, output_variables=output_variables) +plot.dynamic_plot() diff --git a/examples/scripts/create-model.py b/examples/scripts/create-model.py index 73b7d33e58..8c2d702835 100644 --- a/examples/scripts/create-model.py +++ b/examples/scripts/create-model.py @@ -115,9 +115,7 @@ def Diffusivity(cc): solution = solver.solve(model, t) # Extract output variables -L_out = pybamm.ProcessedVariable( - model.variables["SEI thickness"], solution.t, solution.y, mesh -) +L_out = solution["SEI thickness"] # plot plt.plot(solution.t, L_out(solution.t)) diff --git a/examples/scripts/custom_model.py b/examples/scripts/custom_model.py index ac6c5343e5..a32894ebc3 100644 --- a/examples/scripts/custom_model.py +++ b/examples/scripts/custom_model.py @@ -11,6 +11,9 @@ model = pybamm.lithium_ion.BaseModel(name="my li-ion model") # set choice of submodels +model.submodels["external circuit"] = pybamm.external_circuit.CurrentControl( + model.param +) model.submodels["current collector"] = pybamm.current_collector.Uniform(model.param) model.submodels["thermal"] = pybamm.thermal.isothermal.Isothermal(model.param) model.submodels["negative electrode"] = pybamm.electrode.ohm.LeadingOrder( @@ -65,5 +68,5 @@ solution = solver.solve(model, t_eval) # plot -plot = pybamm.QuickPlot(model, mesh, solution) +plot = pybamm.QuickPlot(solution) plot.dynamic_plot() diff --git a/examples/scripts/heat_equation.py b/examples/scripts/heat_equation.py index 5bccd4fd9d..35d4963851 100644 --- a/examples/scripts/heat_equation.py +++ b/examples/scripts/heat_equation.py @@ -61,9 +61,7 @@ solution = solver.solve(model, t) # Extract output variables -T_out = pybamm.ProcessedVariable( - model.variables["Temperature"], solution.t, solution.y, mesh -) +T_out = solution["Temperature"] # Exact solution ------------------------------------------------------- N = 100 # number of Fourier modes to sum @@ -122,11 +120,7 @@ def T_exact(x, t): label="Numerical" if i == 0 else "", ) plt.plot( - xx, - T_exact(xx, t), - "-", - color=color, - label="Exact (t={})".format(plot_times[i]), + xx, T_exact(xx, t), "-", color=color, label="Exact (t={})".format(plot_times[i]) ) plt.xlabel("x", fontsize=16) plt.ylabel("T", fontsize=16) diff --git a/examples/scripts/thermal_lithium_ion.py b/examples/scripts/thermal_lithium_ion.py index 79747040d8..eb55ca07b7 100644 --- a/examples/scripts/thermal_lithium_ion.py +++ b/examples/scripts/thermal_lithium_ion.py @@ -38,7 +38,7 @@ # solve model solutions = [None] * len(models) -t_eval = np.linspace(0, 0.17, 100) +t_eval = np.linspace(0, 0.25, 100) for i, model in enumerate(models): solver = pybamm.ScipySolver(atol=1e-8, rtol=1e-8) solution = solver.solve(model, t_eval) @@ -51,5 +51,5 @@ "Cell temperature [K]", ] labels = ["Full thermal model", "Lumped thermal model"] -plot = pybamm.QuickPlot(models, mesh, solutions, output_variables, labels) +plot = pybamm.QuickPlot(solutions, output_variables, labels) plot.dynamic_plot() diff --git a/input/parameters/lead-acid/cells/BBOXX_Sulzer2019/parameters.csv b/input/parameters/lead-acid/cells/BBOXX_Sulzer2019/parameters.csv index 02cf1b92a0..85bebe1816 100644 --- a/input/parameters/lead-acid/cells/BBOXX_Sulzer2019/parameters.csv +++ b/input/parameters/lead-acid/cells/BBOXX_Sulzer2019/parameters.csv @@ -16,3 +16,4 @@ Positive tab centre z-coordinate [m],0.114,Tab at top, ,,, # Electrical,,, Cell capacity [A.h],17,Manufacturer, +Typical current [A],1,, diff --git a/input/parameters/lead-acid/experiments/1C_discharge_from_full/parameters.csv b/input/parameters/lead-acid/experiments/1C_discharge_from_full/parameters.csv index 791171664f..c1b130f248 100644 --- a/input/parameters/lead-acid/experiments/1C_discharge_from_full/parameters.csv +++ b/input/parameters/lead-acid/experiments/1C_discharge_from_full/parameters.csv @@ -10,8 +10,7 @@ Number of electrodes connected in parallel to make a cell,8,Manufacturer, Number of cells connected in series to make a battery,6,Manufacturer, Lower voltage cut-off [V],1.73,,(just under) 10.5V across 6-cell battery Upper voltage cut-off [V],2.44,,(just over) 14.5V across 6-cell battery -C-rate,1,, -Current function,[constant],, +C-rate,0.1,, ,,, # Initial conditions Initial State of Charge,1,-, diff --git a/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv b/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv index 3d1469d95c..9a5fcbcd21 100644 --- a/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv +++ b/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv @@ -11,6 +11,7 @@ Negative electrode OCP [V],[function]graphite_mcmb2528_ocp_Dualfoil1998, Negative electrode porosity,0.3,Scott Moura FastDFN,electrolyte volume fraction Negative electrode active material volume fraction,0.7,,assuming zero binder volume fraction Negative particle radius [m],1E-05,Scott Moura FastDFN, +Negative particle distribution in x,1,, Negative electrode surface area density [m-1],180000,Scott Moura FastDFN, Negative electrode Bruggeman coefficient (electrolyte),1.5,Scott Moura FastDFN, Negative electrode Bruggeman coefficient (electrode),1.5,Scott Moura FastDFN, diff --git a/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv b/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv index d1fabddca7..1eafb9b658 100644 --- a/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv +++ b/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv @@ -11,6 +11,7 @@ Positive electrode OCP [V],[function]lico2_ocp_Dualfoil1998, Positive electrode porosity,0.3,Scott Moura FastDFN,electrolyte volume fraction Positive electrode active material volume fraction,0.7,,assuming zero binder volume fraction Positive particle radius [m],1E-05,Scott Moura FastDFN, +Positive particle distribution in x,1,, Positive electrode surface area density [m-1],150000,Scott Moura FastDFN, Positive electrode Bruggeman coefficient (electrolyte),1.5,Scott Moura FastDFN, Positive electrode Bruggeman coefficient (electrode),1.5,Scott Moura FastDFN, diff --git a/input/parameters/lithium-ion/cells/kokam_Marquis2019/parameters.csv b/input/parameters/lithium-ion/cells/kokam_Marquis2019/parameters.csv index f98187ecd0..eddde7ffb5 100644 --- a/input/parameters/lithium-ion/cells/kokam_Marquis2019/parameters.csv +++ b/input/parameters/lithium-ion/cells/kokam_Marquis2019/parameters.csv @@ -34,3 +34,4 @@ Positive current collector thermal conductivity [W.m-1.K-1],237,, ,,, # Electrical,,, Cell capacity [A.h],0.680616,,24 Ah/m2 * 0.137m * 0.207m +Typical current [A],0.680616,,1C current diff --git a/input/parameters/lithium-ion/experiments/1C_discharge_from_full_Marquis2019/parameters.csv b/input/parameters/lithium-ion/experiments/1C_discharge_from_full_Marquis2019/parameters.csv index a7a9bb24ee..80a2746b6a 100644 --- a/input/parameters/lithium-ion/experiments/1C_discharge_from_full_Marquis2019/parameters.csv +++ b/input/parameters/lithium-ion/experiments/1C_discharge_from_full_Marquis2019/parameters.csv @@ -11,7 +11,6 @@ Number of cells connected in series to make a battery,1,, Lower voltage cut-off [V],3.105,, Upper voltage cut-off [V],4.7,, C-rate,1,, -Current function,[constant],, ,,, # Initial conditions Initial concentration in negative electrode [mol.m-3],19986.609595075,Scott Moura FastDFN, diff --git a/pybamm/__init__.py b/pybamm/__init__.py index 23f55aa76f..8140fdeedb 100644 --- a/pybamm/__init__.py +++ b/pybamm/__init__.py @@ -55,8 +55,8 @@ def version(formatted=False): # # Utility classes and methods # -from .util import Timer -from .util import root_dir, load_function, rmse, get_infinite_nested_dict +from .util import Timer, FuzzyDict +from .util import root_dir, load_function, rmse, get_infinite_nested_dict, load from .logger import logger, set_logging_level from .settings import settings @@ -81,10 +81,7 @@ def version(formatted=False): Division, Inner, inner, - Outer, - Kron, Heaviside, - outer, source, ) from .expression_tree.concatenations import ( @@ -98,10 +95,17 @@ def version(formatted=False): from .expression_tree.unary_operators import * from .expression_tree.functions import * from .expression_tree.interpolant import Interpolant +from .expression_tree.input_parameter import InputParameter from .expression_tree.parameter import Parameter, FunctionParameter -from .expression_tree.broadcasts import Broadcast, PrimaryBroadcast, FullBroadcast +from .expression_tree.broadcasts import ( + Broadcast, + PrimaryBroadcast, + SecondaryBroadcast, + FullBroadcast, + ones_like, +) from .expression_tree.scalar import Scalar -from .expression_tree.variable import Variable +from .expression_tree.variable import Variable, ExternalVariable from .expression_tree.independent_variable import ( IndependentVariable, Time, @@ -160,6 +164,7 @@ def version(formatted=False): current_collector, electrolyte, electrode, + external_circuit, interface, oxygen_diffusion, particle, @@ -232,8 +237,6 @@ def version(formatted=False): # from .solvers.solution import Solution from .solvers.base_solver import BaseSolver -from .solvers.ode_solver import OdeSolver -from .solvers.dae_solver import DaeSolver from .solvers.algebraic_solver import AlgebraicSolver from .solvers.casadi_solver import CasadiSolver from .solvers.scikits_dae_solver import ScikitsDaeSolver @@ -244,7 +247,7 @@ def version(formatted=False): # # other # -from .processed_variable import post_process_variables, ProcessedVariable +from .processed_variable import ProcessedVariable from .quick_plot import QuickPlot, ax_min, ax_max from .simulation import Simulation, load_sim diff --git a/pybamm/discretisations/discretisation.py b/pybamm/discretisations/discretisation.py index 24dcecb714..584606af17 100644 --- a/pybamm/discretisations/discretisation.py +++ b/pybamm/discretisations/discretisation.py @@ -4,7 +4,8 @@ import pybamm import numpy as np from collections import defaultdict, OrderedDict -from scipy.sparse import block_diag, csr_matrix +from scipy.sparse import block_diag, csc_matrix, csr_matrix +from scipy.sparse.linalg import inv def has_bc_of_form(symbol, side, bcs, form): @@ -52,7 +53,7 @@ def __init__(self, mesh=None, spatial_methods=None): self.bcs = {} self.y_slices = {} self._discretised_symbols = {} - self.external_variables = [] + self.external_variables = {} @property def mesh(self): @@ -126,11 +127,7 @@ def process_model(self, model, inplace=True, check_model=True): # Prepare discretisation # set variables (we require the full variable not just id) - variables = ( - list(model.rhs.keys()) - + list(model.algebraic.keys()) - + model.external_variables - ) + variables = list(model.rhs.keys()) + list(model.algebraic.keys()) # Set the y split for variables pybamm.logger.info("Set variable slices for {}".format(model.name)) @@ -139,6 +136,7 @@ def process_model(self, model, inplace=True, check_model=True): # now add extrapolated external variables to the boundary conditions # if required by the spatial method self._preprocess_external_variables(model) + self.set_external_variables(model) # set boundary conditions (only need key ids for boundary_conditions) pybamm.logger.info("Discretise boundary conditions for {}".format(model.name)) @@ -157,28 +155,6 @@ def process_model(self, model, inplace=True, check_model=True): model_disc.bcs = self.bcs - self.external_variables = model.external_variables - # find where external variables begin in state vector - # we always append external variables to the end, so - # it is sufficient to only know the starting location - start_vals = [] - for var in self.external_variables: - if isinstance(var, pybamm.Concatenation): - for child in var.children: - start_vals += [self.y_slices[child.id][0].start] - elif isinstance(var, pybamm.Variable): - start_vals += [self.y_slices[var.id][0].start] - - # attach properties of the state vector so that it - # can be divided correctly during the solving stage - model_disc.external_variables = model.external_variables - model_disc.y_length = self.y_length - model_disc.y_slices = self.y_slices - if start_vals: - model_disc.external_start = min(start_vals) - else: - model_disc.external_start = self.y_length - pybamm.logger.info("Discretise initial conditions for {}".format(model.name)) ics, concat_ics = self.process_initial_conditions(model) model_disc.initial_conditions = ics @@ -206,7 +182,9 @@ def process_model(self, model, inplace=True, check_model=True): # Create mass matrix pybamm.logger.info("Create mass matrix for {}".format(model.name)) - model_disc.mass_matrix = self.create_mass_matrix(model_disc) + model_disc.mass_matrix, model_disc.mass_matrix_inv = self.create_mass_matrix( + model_disc + ) # Check that resulting model makes sense if check_model: @@ -232,13 +210,8 @@ def set_variable_slices(self, variables): end = 0 # Iterate through unpacked variables, adding appropriate slices to y_slices for variable in variables: - # If domain is empty then variable has size 1 - if variable.domain == []: - end += 1 - y_slices[variable.id].append(slice(start, end)) - start = end - # Otherwise, add up the size of all the domains in variable.domain - elif isinstance(variable, pybamm.Concatenation): + # Add up the size of all the domains in variable.domain + if isinstance(variable, pybamm.Concatenation): children = variable.children meshes = OrderedDict() for child in children: @@ -254,18 +227,27 @@ def set_variable_slices(self, variables): y_slices[child.id].append(slice(start, end)) start = end else: - for dom in variable.domain: - for submesh in self.spatial_methods[dom].mesh[dom]: - end += submesh.npts_for_broadcast + end += self._get_variable_size(variable) y_slices[variable.id].append(slice(start, end)) start = end self.y_slices = y_slices - self.y_length = end # reset discretised_symbols self._discretised_symbols = {} + def _get_variable_size(self, variable): + "Helper function to determine what size a variable should be" + # If domain is empty then variable has size 1 + if variable.domain == []: + return 1 + else: + size = 0 + for dom in variable.domain: + for submesh in self.spatial_methods[dom].mesh[dom]: + size += submesh.npts_for_broadcast + return size + def _preprocess_external_variables(self, model): """ A method to preprocess external variables so that they are @@ -288,6 +270,43 @@ def _preprocess_external_variables(self, model): model.boundary_conditions.update(new_bcs) + def set_external_variables(self, model): + """ + Add external variables to the list of variables to account for, being careful + about concatenations + """ + for var in model.external_variables: + # Find the name in the model variables + # Look up dictionary key based on value + try: + idx = [x.id for x in model.variables.values()].index(var.id) + except ValueError: + raise ValueError( + """ + Variable {} must be in the model.variables dictionary to be set + as an external variable + """.format( + var + ) + ) + name = list(model.variables.keys())[idx] + if isinstance(var, pybamm.Variable): + # No need to keep track of the parent + self.external_variables[(name, None)] = var + elif isinstance(var, pybamm.Concatenation): + start = 0 + end = 0 + for child in var.children: + dom = child.domain[0] + if len(self.spatial_methods[dom].mesh[dom]) > 1: + raise NotImplementedError( + "Cannot create 2D external variable with concatenations" + ) + end += self._get_variable_size(child) + # Keep a record of the parent + self.external_variables[(name, (var, start, end))] = child + start = end + def set_internal_boundary_conditions(self, model): """ A method to set the internal boundary conditions for the submodel. @@ -532,10 +551,14 @@ def create_mass_matrix(self, model): ------- :class:`pybamm.Matrix` The mass matrix + :class:`pybamm.Matrix` + The inverse of the ode part of the mass matrix (required by solvers + which only accept the ODEs in explicit form) """ # Create list of mass matrices for each equation to be put into block # diagonal mass matrix for the model mass_list = [] + mass_inv_list = [] # get a list of model rhs variables that are sorted according to # where they are in the state vector @@ -560,12 +583,26 @@ def create_mass_matrix(self, model): if var.domain == []: # If variable domain empty then mass matrix is just 1 mass_list.append(1.0) + mass_inv_list.append(1.0) else: - mass_list.append( + mass = ( self.spatial_methods[var.domain[0]] .mass_matrix(var, self.bcs) .entries ) + mass_list.append(mass) + if isinstance( + self.spatial_methods[var.domain[0]], + (pybamm.ZeroDimensionalMethod, pybamm.FiniteVolume), + ): + # for 0D methods the mass matrix is just a scalar 1 and for + # finite volumes the mass matrix is identity, so no need to + # compute the inverse + mass_inv_list.append(mass) + else: + # inverse is more efficient in csc format + mass_inv = inv(csc_matrix(mass)) + mass_inv_list.append(mass_inv) # Create lumped mass matrix (of zeros) of the correct shape for the # discretised algebraic equations @@ -574,10 +611,14 @@ def create_mass_matrix(self, model): mass_algebraic = csr_matrix((mass_algebraic_size, mass_algebraic_size)) mass_list.append(mass_algebraic) - # Create block diagonal (sparse) mass matrix - mass_matrix = block_diag(mass_list, format="csr") + # Create block diagonal (sparse) mass matrix and inverse (if model has odes) + mass_matrix = pybamm.Matrix(block_diag(mass_list, format="csr")) + if model.rhs.keys(): + mass_matrix_inv = pybamm.Matrix(block_diag(mass_inv_list, format="csr")) + else: + mass_matrix_inv = None - return pybamm.Matrix(mass_matrix) + return mass_matrix, mass_matrix_inv def create_jacobian(self, model): """Creates Jacobian of the discretised model. @@ -657,13 +698,17 @@ def process_dict(self, var_eqn_dict): for eqn_key, eqn in var_eqn_dict.items(): # Broadcast if the equation evaluates to a number(e.g. Scalar) if eqn.evaluates_to_number() and not isinstance(eqn_key, str): - eqn = pybamm.Broadcast(eqn, eqn_key.domain) + eqn = pybamm.FullBroadcast( + eqn, eqn_key.domain, eqn_key.auxiliary_domains + ) # note we are sending in the key.id here so we don't have to # keep calling .id pybamm.logger.debug("Discretise {!r}".format(eqn_key)) - new_var_eqn_dict[eqn_key] = self.process_symbol(eqn) + processed_eqn = self.process_symbol(eqn) + + new_var_eqn_dict[eqn_key] = processed_eqn return new_var_eqn_dict @@ -688,6 +733,18 @@ def process_symbol(self, symbol): discretised_symbol = self._process_symbol(symbol) self._discretised_symbols[symbol.id] = discretised_symbol discretised_symbol.test_shape() + # Assign mesh as an attribute to the processed variable + if symbol.domain != []: + discretised_symbol.mesh = self.mesh.combine_submeshes(*symbol.domain) + else: + discretised_symbol.mesh = None + # Assign secondary mesh + if "secondary" in symbol.auxiliary_domains: + discretised_symbol.secondary_mesh = self.mesh.combine_submeshes( + *symbol.auxiliary_domains["secondary"] + ) + else: + discretised_symbol.secondary_mesh = None return discretised_symbol def _process_symbol(self, symbol): @@ -746,8 +803,7 @@ def _process_symbol(self, symbol): elif isinstance(symbol, pybamm.Integral): out = child_spatial_method.integral(child, disc_child) - out.domain = symbol.domain - out.auxiliary_domains = symbol.auxiliary_domains + out.copy_domains(symbol) return out elif isinstance(symbol, pybamm.DefiniteIntegralVector): @@ -797,11 +853,40 @@ def _process_symbol(self, symbol): return symbol._function_new_copy(disc_children) elif isinstance(symbol, pybamm.Variable): - return pybamm.StateVector( - *self.y_slices[symbol.id], - domain=symbol.domain, - auxiliary_domains=symbol.auxiliary_domains - ) + # Check if variable is a standard variable or an external variable + if any(symbol.id == var.id for var in self.external_variables.values()): + # Look up dictionary key based on value + idx = [x.id for x in self.external_variables.values()].index(symbol.id) + name, parent_and_slice = list(self.external_variables.keys())[idx] + if parent_and_slice is None: + # Variable didn't come from a concatenation so we can just create a + # normal external variable using the symbol's name + return pybamm.ExternalVariable( + symbol.name, + size=self._get_variable_size(symbol), + domain=symbol.domain, + auxiliary_domains=symbol.auxiliary_domains, + ) + else: + # We have to use a special name since the concatenation doesn't have + # a very informative name. Needs improving + parent, start, end = parent_and_slice + ext = pybamm.ExternalVariable( + name, + size=self._get_variable_size(parent), + domain=parent.domain, + auxiliary_domains=parent.auxiliary_domains, + ) + out = ext[slice(start, end)] + out.domain = symbol.domain + return out + + else: + return pybamm.StateVector( + *self.y_slices[symbol.id], + domain=symbol.domain, + auxiliary_domains=symbol.auxiliary_domains + ) elif isinstance(symbol, pybamm.SpatialVariable): return spatial_method.spatial_variable(symbol) @@ -875,8 +960,8 @@ def _concatenate_in_order(self, var_eqn_dict, check_complete=False, sparse=False if check_complete: # Check keys from the given var_eqn_dict against self.y_slices ids = {v.id for v in unpacked_variables} - external_id = {v.id for v in self.external_variables} - for var in self.external_variables: + external_id = {v.id for v in self.external_variables.values()} + for var in self.external_variables.values(): child_ids = {child.id for child in var.children} external_id = external_id.union(child_ids) y_slices_with_external_removed = set(self.y_slices.keys()).difference( @@ -961,7 +1046,7 @@ def check_variables(self, model): """ Check variables in variable list against rhs Be lenient with size check if the variable in model.variables is broadcasted, or - a concatenation, or an outer product + a concatenation (if broadcasted, variable is a multiplication with a vector of ones) """ for rhs_var in model.rhs.keys(): @@ -973,7 +1058,6 @@ def check_variables(self, model): ) not_concatenation = not isinstance(var, pybamm.Concatenation) - not_outer = not isinstance(var, pybamm.Outer) not_mult_by_one_vec = not ( isinstance(var, pybamm.Multiplication) @@ -981,12 +1065,7 @@ def check_variables(self, model): and np.all(var.right.entries == 1) ) - if ( - different_shapes - and not_concatenation - and not_outer - and not_mult_by_one_vec - ): + if different_shapes and not_concatenation and not_mult_by_one_vec: raise pybamm.ModelError( """ variable and its eqn must have the same shape after discretisation diff --git a/pybamm/expression_tree/array.py b/pybamm/expression_tree/array.py index 5341795326..348fbed616 100644 --- a/pybamm/expression_tree/array.py +++ b/pybamm/expression_tree/array.py @@ -98,6 +98,6 @@ def new_copy(self): self.entries_string, ) - def _base_evaluate(self, t=None, y=None): + def _base_evaluate(self, t=None, y=None, u=None): """ See :meth:`pybamm.Symbol._base_evaluate()`. """ return self._entries diff --git a/pybamm/expression_tree/binary_operators.py b/pybamm/expression_tree/binary_operators.py index eafece3c57..6ac3cbc729 100644 --- a/pybamm/expression_tree/binary_operators.py +++ b/pybamm/expression_tree/binary_operators.py @@ -5,7 +5,7 @@ import numpy as np import numbers -from scipy.sparse import issparse, csr_matrix, kron +from scipy.sparse import issparse, csr_matrix def is_scalar_zero(expr): @@ -79,15 +79,8 @@ class BinaryOperator(pybamm.Symbol): def __init__(self, name, left, right): left, right = self.format(left, right) - # Check and process domains, except for Outer symbol which takes the outer - # product of two smbols in different domains, and gives it the domain of the - # right child. - if isinstance(self, (pybamm.Outer, pybamm.Kron)): - domain = right.domain - auxiliary_domains = {"secondary": left.domain} - else: - domain = self.get_children_domains(left.domain, right.domain) - auxiliary_domains = self.get_children_auxiliary_domains([left, right]) + domain = self.get_children_domains(left.domain, right.domain) + auxiliary_domains = self.get_children_auxiliary_domains([left, right]) super().__init__( name, children=[left, right], @@ -114,11 +107,7 @@ def format(self, left, right): ) # Do some broadcasting in special cases, to avoid having to do this manually - if ( - not isinstance(self, (Outer, Kron)) - and left.domain != [] - and right.domain != [] - ): + if left.domain != [] and right.domain != []: if ( left.domain != right.domain and "secondary" in right.auxiliary_domains @@ -165,8 +154,7 @@ def new_copy(self): # make new symbol, ensure domain(s) remain the same out = self._binary_new_copy(new_left, new_right) - out.domain = self.domain - out.auxiliary_domains = self.auxiliary_domains + out.copy_domains(self) return out @@ -174,24 +162,24 @@ def _binary_new_copy(self, left, right): "Default behaviour for new_copy" return self.__class__(left, right) - def evaluate(self, t=None, y=None, known_evals=None): + def evaluate(self, t=None, y=None, u=None, known_evals=None): """ See :meth:`pybamm.Symbol.evaluate()`. """ if known_evals is not None: id = self.id try: return known_evals[id], known_evals except KeyError: - left, known_evals = self.left.evaluate(t, y, known_evals) - right, known_evals = self.right.evaluate(t, y, known_evals) + left, known_evals = self.left.evaluate(t, y, u, known_evals) + right, known_evals = self.right.evaluate(t, y, u, known_evals) value = self._binary_evaluate(left, right) known_evals[id] = value return value, known_evals else: - left = self.left.evaluate(t, y) - right = self.right.evaluate(t, y) + left = self.left.evaluate(t, y, u) + right = self.right.evaluate(t, y, u) return self._binary_evaluate(left, right) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape()`. """ left = self.children[0].evaluate_for_shape() right = self.children[1].evaluate_for_shape() @@ -653,86 +641,6 @@ def inner(left, right): return pybamm.Inner(left, right) -class Outer(BinaryOperator): - """A node in the expression tree representing an outer product. - This takes a 1D vector in the current collector domain of size (n,1) and a 1D - variable of size (m,1), takes their outer product, and reshapes this into a vector - of size (nm,1). It can also take in a vector in a single particle and a vector - of the electrolyte domain to repeat that particle. - Note: this class might be a bit dangerous, so at the moment it is very restrictive - in what symbols can be passed to it - - **Extends:** :class:`BinaryOperator` - """ - - def __init__(self, left, right): - """ See :meth:`pybamm.BinaryOperator.__init__()`. """ - # cannot have certain types of objects in the right symbol, as these - # can already be 2D objects (so we can't take an outer product with them) - if right.has_symbol_of_classes( - (pybamm.Variable, pybamm.StateVector, pybamm.Matrix, pybamm.SpatialVariable) - ): - raise TypeError("right child must only contain Vectors and Scalars" "") - - super().__init__("outer product", left, right) - - def __str__(self): - """ See :meth:`pybamm.Symbol.__str__()`. """ - return "outer({!s}, {!s})".format(self.left, self.right) - - def diff(self, variable): - """ See :meth:`pybamm.Symbol.diff()`. """ - raise NotImplementedError("diff not implemented for symbol of type 'Outer'") - - def _outer_jac(self, left_jac, right_jac, variable): - """ - Calculate jacobian of outer product. - See :meth:`pybamm.Jacobian._jac()`. - """ - # right cannot be a StateVector, so no need for product rule - left, right = self.orphans - if left.evaluates_to_number(): - # Return zeros of correct size - return pybamm.Matrix( - csr_matrix((self.size, variable.evaluation_array.count(True))) - ) - else: - return pybamm.Kron(left_jac, right) - - def _binary_evaluate(self, left, right): - """ See :meth:`pybamm.BinaryOperator._binary_evaluate()`. """ - - return np.outer(left, right).reshape(-1, 1) - - -class Kron(BinaryOperator): - """A node in the expression tree representing a (sparse) kronecker product operator - - **Extends:** :class:`BinaryOperator` - """ - - def __init__(self, left, right): - """ See :meth:`pybamm.BinaryOperator.__init__()`. """ - - super().__init__("kronecker product", left, right) - - def __str__(self): - """ See :meth:`pybamm.Symbol.__str__()`. """ - return "kron({!s}, {!s})".format(self.left, self.right) - - def diff(self, variable): - """ See :meth:`pybamm.Symbol.diff()`. """ - raise NotImplementedError("diff not implemented for symbol of type 'Kron'") - - def _binary_jac(self, left_jac, right_jac): - """ See :meth:`pybamm.BinaryOperator._binary_jac()`. """ - raise NotImplementedError("jac not implemented for symbol of type 'Kron'") - - def _binary_evaluate(self, left, right): - """ See :meth:`pybamm.BinaryOperator._binary_evaluate()`. """ - return csr_matrix(kron(left, right)) - - class Heaviside(BinaryOperator): """A node in the expression tree representing a heaviside step function. @@ -782,18 +690,6 @@ def _binary_new_copy(self, left, right): return Heaviside(left, right, self.equal) -def outer(left, right): - """ - Return outer product of two symbols. If the symbols have the same domain, the outer - product is just a multiplication. If they have different domains, make a copy of the - left child with same domain as right child, and then take outer product. - """ - try: - return left * right - except pybamm.DomainError: - return pybamm.Outer(left, right) - - def source(left, right, boundary=False): """A convinience function for creating (part of) an expression tree representing a source term. This is necessary for spatial methods where the mass matrix @@ -823,7 +719,7 @@ def source(left, right, boundary=False): """ # Broadcast if left is number if isinstance(left, numbers.Number): - left = pybamm.Broadcast(left, "current collector") + left = pybamm.PrimaryBroadcast(left, "current collector") if left.domain != ["current collector"] or right.domain != ["current collector"]: raise pybamm.DomainError( diff --git a/pybamm/expression_tree/broadcasts.py b/pybamm/expression_tree/broadcasts.py index 5395b880c8..31228d8085 100644 --- a/pybamm/expression_tree/broadcasts.py +++ b/pybamm/expression_tree/broadcasts.py @@ -11,15 +11,18 @@ class Broadcast(pybamm.SpatialOperator): Broadcasts a child to a specified domain. After discretisation, this will evaluate to an array of the right shape for the specified domain. + For an example of broadcasts in action, see + `this example notebook + `_ + Parameters ---------- child : :class:`Symbol` child node broadcast_domain : iterable of str Primary domain for broadcast. This will become the domain of the symbol - auxiliary_domain : iterable of str - Secondary domain for broadcast. Currently, this is only used for testing that - symbols have the right shape. + broadcast_auxiliary_domains : dict of str + Auxiliary domains for broadcast. broadcast_type : str, optional Whether to broadcast to the full domain (primary and secondary) or only in the primary direction. Default is "full". @@ -33,90 +36,118 @@ def __init__( self, child, broadcast_domain, - auxiliary_domains=None, + broadcast_auxiliary_domains=None, broadcast_type="full", name=None, ): # Convert child to scalar if it is a number if isinstance(child, numbers.Number): child = pybamm.Scalar(child) + # Convert domain to list if it's a string + if isinstance(broadcast_domain, str): + broadcast_domain = [broadcast_domain] if name is None: name = "broadcast" # perform some basic checks and set attributes - domain = self.check_and_set_domain_and_broadcast_type( - child, broadcast_domain, broadcast_type + domain, auxiliary_domains = self.check_and_set_domains( + child, broadcast_type, broadcast_domain, broadcast_auxiliary_domains ) self.broadcast_type = broadcast_type self.broadcast_domain = broadcast_domain - if auxiliary_domains is None: - if child.domain != []: - auxiliary_domains = {"secondary": child.domain} - else: - auxiliary_domains = {} super().__init__(name, child, domain, auxiliary_domains) - def check_and_set_domain_and_broadcast_type( - self, child, broadcast_domain, broadcast_type + def _unary_simplify(self, simplified_child): + """ See :meth:`pybamm.UnaryOperator.simplify()`. """ + return self._unary_new_copy(simplified_child) + + +class PrimaryBroadcast(Broadcast): + """A node in the expression tree representing a primary broadcasting operator. + Broadcasts in a `primary` dimension only. That is, makes explicit copies of the + symbol in the domain specified by `broadcast_domain`. This should be used for + broadcasting from a "larger" scale to a "smaller" scale, for example broadcasting + temperature T(x) from the electrode to the particles, or broadcasting current + collector current i(y, z) from the current collector to the electrodes. + + Parameters + ---------- + child : :class:`Symbol` + child node + broadcast_domain : iterable of str + Primary domain for broadcast. This will become the domain of the symbol + name : str + name of the node + + **Extends:** :class:`SpatialOperator` + """ + + def __init__(self, child, broadcast_domain, name=None): + super().__init__(child, broadcast_domain, broadcast_type="primary", name=name) + + def check_and_set_domains( + self, child, broadcast_type, broadcast_domain, broadcast_auxiliary_domains ): - """ - Set broadcast domain and broadcast type, performing basic checks to make sure - it is compatible with the child - """ - # Acceptable broadcast types - if broadcast_type not in ["primary", "secondary", "full"]: - raise KeyError( - """Broadcast type must be either: 'primary', 'secondary', or 'full' and - not {}""".format( - broadcast_type - ) + "See :meth:`Broadcast.check_and_set_domains`" + # Can only do primary broadcast from current collector to electrode or particle + # or from electrode to particle. Note current collector to particle *is* allowed + if child.domain == []: + pass + elif child.domain == ["current collector"] and broadcast_domain[0] not in [ + "negative electrode", + "separator", + "positive electrode", + "negative particle", + "positive particle", + ]: + raise pybamm.DomainError( + """Primary broadcast from current collector domain must be to electrode + or separator or particle domains""" ) + elif child.domain[0] in [ + "negative electrode", + "separator", + "positive electrode", + ] and broadcast_domain[0] not in ["negative particle", "positive particle"]: + raise pybamm.DomainError( + """Primary broadcast from electrode or separator must be to particle + domains""" + ) + elif child.domain[0] in ["negative particle", "positive particle"]: + raise pybamm.DomainError("Cannot do primary broadcast from particle domain") domain = broadcast_domain + auxiliary_domains = {} + if child.domain != []: + auxiliary_domains["secondary"] = child.domain + if "secondary" in child.auxiliary_domains: + auxiliary_domains["tertiary"] = child.auxiliary_domains["secondary"] - # Variables on the current collector can only be broadcast to 'primary' - if broadcast_type == "full": - if child.domain == ["current collector"]: - raise ValueError( - """ - Variables on the current collector must be broadcast to 'primary' - only - """ - ) - return domain - - def _unary_simplify(self, child): - """ See :meth:`pybamm.UnaryOperator.simplify()`. """ - - return Broadcast( - child, self.broadcast_domain, self.auxiliary_domains, self.broadcast_type - ) + return domain, auxiliary_domains def _unary_new_copy(self, child): """ See :meth:`pybamm.UnaryOperator.simplify()`. """ + return PrimaryBroadcast(child, self.broadcast_domain) - return Broadcast( - child, self.broadcast_domain, self.auxiliary_domains, self.broadcast_type - ) - - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a Broadcast. See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ child_eval = self.children[0].evaluate_for_shape() vec = pybamm.evaluate_for_shape_using_domain(self.domain) - - if self.broadcast_type == "primary": - return np.outer(child_eval, vec).reshape(-1, 1) - elif self.broadcast_type == "full": - return child_eval * vec + return np.outer(child_eval, vec).reshape(-1, 1) -class PrimaryBroadcast(Broadcast): +class SecondaryBroadcast(Broadcast): """A node in the expression tree representing a primary broadcasting operator. - Broadcasts in a `primary` dimension only. That is, makes explicit copies + Broadcasts in a `secondary` dimension only. That is, makes explicit copies of the + symbol in the domain specified by `broadcast_domain`. This should be used for + broadcasting from a "smaller" scale to a "larger" scale, for example broadcasting + SPM particle concentrations c_s(r) from the particles to the electrodes. Note that + this wouldn't be used to broadcast particle concentrations in the DFN, since these + already depend on both x and r. Parameters ---------- @@ -131,24 +162,62 @@ class PrimaryBroadcast(Broadcast): """ def __init__(self, child, broadcast_domain, name=None): - super().__init__(child, broadcast_domain, broadcast_type="primary", name=name) + super().__init__(child, broadcast_domain, broadcast_type="secondary", name=name) - def _unary_simplify(self, child): - """ See :meth:`pybamm.UnaryOperator.simplify()`. """ - return PrimaryBroadcast(child, self.broadcast_domain) + def check_and_set_domains( + self, child, broadcast_type, broadcast_domain, broadcast_auxiliary_domains + ): + "See :meth:`Broadcast.check_and_set_domains`" + + # Can only do secondary broadcast from particle to electrode or from + # electrode to current collector + if child.domain[0] in [ + "negative particle", + "positive particle", + ] and broadcast_domain[0] not in [ + "negative electrode", + "separator", + "positive electrode", + ]: + raise pybamm.DomainError( + """Secondary broadcast from particle domain must be to electrode or + separator domains""" + ) + elif child.domain[0] in [ + "negative electrode", + "separator", + "positive electrode", + ] and broadcast_domain != ["current collector"]: + raise pybamm.DomainError( + """Secondary broadcast from electrode or separator must be to + current collector domains""" + ) + elif child.domain == ["current collector"]: + raise pybamm.DomainError( + "Cannot do secondary broadcast from current collector domain" + ) + # Domain stays the same as child domain and broadcast domain is secondary + # domain + domain = child.domain + auxiliary_domains = {"secondary": broadcast_domain} + # Child's secondary domain becomes tertiary domain + if "secondary" in child.auxiliary_domains: + auxiliary_domains["tertiary"] = child.auxiliary_domains["secondary"] + + return domain, auxiliary_domains def _unary_new_copy(self, child): """ See :meth:`pybamm.UnaryOperator.simplify()`. """ - return PrimaryBroadcast(child, self.broadcast_domain) + return SecondaryBroadcast(child, self.broadcast_domain) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a Broadcast. See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ child_eval = self.children[0].evaluate_for_shape() vec = pybamm.evaluate_for_shape_using_domain(self.domain) - return np.outer(child_eval, vec).reshape(-1, 1) + return np.outer(vec, child_eval).reshape(-1, 1) class FullBroadcast(Broadcast): @@ -160,20 +229,31 @@ def __init__(self, child, broadcast_domain, auxiliary_domains, name=None): super().__init__( child, broadcast_domain, - auxiliary_domains=auxiliary_domains, + broadcast_auxiliary_domains=auxiliary_domains, broadcast_type="full", name=name, ) - def _unary_simplify(self, child): - """ See :meth:`pybamm.UnaryOperator.simplify()`. """ - return FullBroadcast(child, self.broadcast_domain, self.auxiliary_domains) + def check_and_set_domains( + self, child, broadcast_type, broadcast_domain, broadcast_auxiliary_domains + ): + "See :meth:`Broadcast.check_and_set_domains`" + + # Variables on the current collector can only be broadcast to 'primary' + if child.domain == ["current collector"]: + raise pybamm.DomainError( + "Cannot do full broadcast from current collector domain" + ) + domain = broadcast_domain + auxiliary_domains = broadcast_auxiliary_domains or {} + + return domain, auxiliary_domains def _unary_new_copy(self, child): """ See :meth:`pybamm.UnaryOperator.simplify()`. """ return FullBroadcast(child, self.broadcast_domain, self.auxiliary_domains) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a Broadcast. See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` @@ -184,3 +264,26 @@ def evaluate_for_shape(self): ) return child_eval * vec + + +def ones_like(*symbols): + """ + Create a symbol with the same shape as the input symbol and with constant value '1', + using `FullBroadcast`. + + Parameters + ---------- + symbols : :class:`Symbol` + Symbols whose shape to copy + """ + # Make a symbol that combines all the children, to get the right domain + # that takes all the child symbols into account + sum_symbol = symbols[0] + for sym in symbols: + sum_symbol += sym + + # Just return scalar 1 if symbol has no domain (no broadcasting necessary) + if sum_symbol.domain == []: + return pybamm.Scalar(1) + else: + return FullBroadcast(1, sum_symbol.domain, sum_symbol.auxiliary_domains) diff --git a/pybamm/expression_tree/concatenations.py b/pybamm/expression_tree/concatenations.py index c90d2874e0..6bac3d5d60 100644 --- a/pybamm/expression_tree/concatenations.py +++ b/pybamm/expression_tree/concatenations.py @@ -52,20 +52,22 @@ def _concatenation_evaluate(self, children_eval): else: return self.concatenation_function(children_eval) - def evaluate(self, t=None, y=None, known_evals=None): + def evaluate(self, t=None, y=None, u=None, known_evals=None): """ See :meth:`pybamm.Symbol.evaluate()`. """ children = self.cached_children if known_evals is not None: if self.id not in known_evals: children_eval = [None] * len(children) for idx, child in enumerate(children): - children_eval[idx], known_evals = child.evaluate(t, y, known_evals) + children_eval[idx], known_evals = child.evaluate( + t, y, u, known_evals + ) known_evals[self.id] = self._concatenation_evaluate(children_eval) return known_evals[self.id], known_evals else: children_eval = [None] * len(children) for idx, child in enumerate(children): - children_eval[idx] = child.evaluate(t, y) + children_eval[idx] = child.evaluate(t, y, u) return self._concatenation_evaluate(children_eval) def new_copy(self): @@ -85,10 +87,10 @@ def _concatenation_jac(self, children_jacs): def _concatenation_simplify(self, children): """ See :meth:`pybamm.Symbol.simplify()`. """ new_symbol = self.__class__(*children) - new_symbol.domain = [] + new_symbol.clear_domains() return new_symbol - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape` """ if len(self.children) == 0: return np.array([]) @@ -150,7 +152,7 @@ def _concatenation_simplify(self, children): else: new_children.append(child) new_symbol = NumpyConcatenation(*new_children) - new_symbol.domain = [] + new_symbol.clear_domains() return new_symbol @@ -169,7 +171,7 @@ class DomainConcatenation(Concatenation): children : iterable of :class:`pybamm.Symbol` The symbols to concatenate - mesh : :class:`pybamm.BaseMesh` + full_mesh : :class:`pybamm.BaseMesh` The underlying mesh for discretisation, used to obtain the number of mesh points in each domain. @@ -179,7 +181,7 @@ class DomainConcatenation(Concatenation): """ - def __init__(self, children, mesh, copy_this=None): + def __init__(self, children, full_mesh, copy_this=None): # Convert any constant symbols in children to a Vector of the right size for # concatenation children = list(children) @@ -188,12 +190,12 @@ def __init__(self, children, mesh, copy_this=None): super().__init__(*children, name="domain concatenation") # ensure domain is sorted according to mesh keys - domain_dict = {d: mesh.domain_order.index(d) for d in self.domain} + domain_dict = {d: full_mesh.domain_order.index(d) for d in self.domain} self.domain = sorted(domain_dict, key=domain_dict.__getitem__) if copy_this is None: # store mesh - self._mesh = mesh + self._full_mesh = full_mesh # Check that there is a domain, otherwise the functionality won't work # and we should raise a DomainError @@ -206,7 +208,7 @@ def __init__(self, children, mesh, copy_this=None): ) # create dict of domain => slice of final vector - self.secondary_dimensions_npts = len(self.mesh[self.domain[0]]) + self.secondary_dimensions_npts = len(self.full_mesh[self.domain[0]]) self._slices = self.create_slices(self) # store size of final vector @@ -217,21 +219,21 @@ def __init__(self, children, mesh, copy_this=None): self.create_slices(child) for child in self.cached_children ] else: - self._mesh = copy.copy(copy_this._mesh) + self._full_mesh = copy.copy(copy_this._full_mesh) self._slices = copy.copy(copy_this._slices) self._size = copy.copy(copy_this._size) self._children_slices = copy.copy(copy_this._children_slices) self.secondary_dimensions_npts = copy_this.secondary_dimensions_npts @property - def mesh(self): - return self._mesh + def full_mesh(self): + return self._full_mesh def create_slices(self, node): slices = defaultdict(list) start = 0 end = 0 - second_pts = len(self.mesh[node.domain[0]]) + second_pts = len(self.full_mesh[node.domain[0]]) if second_pts != self.secondary_dimensions_npts: raise ValueError( """Concatenation and children must have the same number of @@ -239,7 +241,7 @@ def create_slices(self, node): ) for i in range(second_pts): for dom in node.domain: - end += self.mesh[dom][i].npts + end += self.full_mesh[dom][i].npts slices[dom].append(slice(start, end)) start = end return slices @@ -275,7 +277,7 @@ def _concatenation_jac(self, children_jacs): def _concatenation_new_copy(self, children): """ See :meth:`pybamm.Symbol.new_copy()`. """ - new_symbol = self.__class__(children, self.mesh, self) + new_symbol = self.__class__(children, self.full_mesh, self) return new_symbol def _concatenation_simplify(self, children): @@ -297,11 +299,11 @@ def _concatenation_simplify(self, children): slice(children[0].y_slices[0].start, children[-1].y_slices[-1].stop) ) - new_symbol = self.__class__(children, self.mesh, self) + new_symbol = self.__class__(children, self.full_mesh, self) # TODO: this should not be needed, but somehow we are still getting domains in # the simplified children - new_symbol.domain = [] + new_symbol.clear_domains() return new_symbol diff --git a/pybamm/expression_tree/functions.py b/pybamm/expression_tree/functions.py index 8ce840815f..5be8e15a1a 100644 --- a/pybamm/expression_tree/functions.py +++ b/pybamm/expression_tree/functions.py @@ -14,8 +14,8 @@ class Function(pybamm.Symbol): ---------- function : method A function can have 0 or many inputs. If no inputs are given, self.evaluate() - simply returns func(). Otherwise, self.evaluate(t, y) returns - func(child0.evaluate(t, y), child1.evaluate(t, y), etc). + simply returns func(). Otherwise, self.evaluate(t, y, u) returns + func(child0.evaluate(t, y, u), child1.evaluate(t, y, u), etc). children : :class:`pybamm.Symbol` The children nodes to apply the function to derivative : str, optional @@ -144,7 +144,7 @@ def _function_jac(self, children_jacs): for i, child in enumerate(children): if not child.evaluates_to_number(): jac_fun = self._function_diff(children, i) * children_jacs[i] - jac_fun.domain = [] + jac_fun.clear_domains() if jacobian is None: jacobian = jac_fun else: @@ -152,22 +152,22 @@ def _function_jac(self, children_jacs): return jacobian - def evaluate(self, t=None, y=None, known_evals=None): + def evaluate(self, t=None, y=None, u=None, known_evals=None): """ See :meth:`pybamm.Symbol.evaluate()`. """ if known_evals is not None: if self.id not in known_evals: evaluated_children = [None] * len(self.children) for i, child in enumerate(self.children): evaluated_children[i], known_evals = child.evaluate( - t, y, known_evals + t, y, u, known_evals=known_evals ) known_evals[self.id] = self._function_evaluate(evaluated_children) return known_evals[self.id], known_evals else: - evaluated_children = [child.evaluate(t, y) for child in self.children] + evaluated_children = [child.evaluate(t, y, u) for child in self.children] return self._function_evaluate(evaluated_children) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Default behaviour: has same shape as all child See :meth:`pybamm.Symbol.evaluate_for_shape()` diff --git a/pybamm/expression_tree/independent_variable.py b/pybamm/expression_tree/independent_variable.py index dceb84ea5b..9dea22f5a7 100644 --- a/pybamm/expression_tree/independent_variable.py +++ b/pybamm/expression_tree/independent_variable.py @@ -27,7 +27,7 @@ class IndependentVariable(pybamm.Symbol): def __init__(self, name, domain=None, auxiliary_domains=None): super().__init__(name, domain=domain, auxiliary_domains=auxiliary_domains) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain( self.domain, self.auxiliary_domains @@ -51,13 +51,13 @@ def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ return Time() - def _base_evaluate(self, t, y=None): + def _base_evaluate(self, t, y=None, u=None): """ See :meth:`pybamm.Symbol._base_evaluate()`. """ if t is None: raise ValueError("t must be provided") return t - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Return the scalar '0' to represent the shape of the independent variable `Time`. See :meth:`pybamm.Symbol.evaluate_for_shape()` @@ -85,9 +85,7 @@ def __init__(self, name, domain=None, auxiliary_domains=None, coord_sys=None): domain = self.domain if name not in KNOWN_SPATIAL_VARS: - raise ValueError( - "name must be KNOWN_SPATIAL_VARS but is '{}'".format(name) - ) + raise ValueError(f"name must be in {KNOWN_SPATIAL_VARS} but is '{name}'") if domain == []: raise ValueError("domain must be provided") diff --git a/pybamm/expression_tree/input_parameter.py b/pybamm/expression_tree/input_parameter.py new file mode 100644 index 0000000000..148c062b0c --- /dev/null +++ b/pybamm/expression_tree/input_parameter.py @@ -0,0 +1,53 @@ +# +# Parameter classes +# +import numpy as np +import pybamm + + +class InputParameter(pybamm.Symbol): + """A node in the expression tree representing an input parameter + + This node's value can be set at the point of solving, allowing parameter estimation + and control + + Parameters + ---------- + name : str + name of the node + + """ + + def __init__(self, name): + super().__init__(name) + + def new_copy(self): + """ See :meth:`pybamm.Symbol.new_copy()`. """ + return InputParameter(self.name) + + def _evaluate_for_shape(self): + """ + Returns the scalar 'NaN' to represent the shape of a parameter. + See :meth:`pybamm.Symbol.evaluate_for_shape()` + """ + return np.nan + + def _jac(self, variable): + """ See :meth:`pybamm.Symbol._jac()`. """ + return pybamm.Scalar(0) + + def _base_evaluate(self, t=None, y=None, u=None): + # u should be a dictionary + # convert 'None' to empty dictionary for more informative error + if u is None: + u = {} + if not isinstance(u, dict): + # if the special input "shape test" is passed, just return 1 + if u == "shape test": + return 1 + raise TypeError("inputs u should be a dictionary") + try: + return u[self.name] + # raise more informative error if can't find name in dict + except KeyError: + raise KeyError("Input parameter '{}' not found".format(self.name)) diff --git a/pybamm/expression_tree/operations/convert_to_casadi.py b/pybamm/expression_tree/operations/convert_to_casadi.py index e6e0758410..a04aec5b0a 100644 --- a/pybamm/expression_tree/operations/convert_to_casadi.py +++ b/pybamm/expression_tree/operations/convert_to_casadi.py @@ -11,36 +11,50 @@ class CasadiConverter(object): def __init__(self, casadi_symbols=None): self._casadi_symbols = casadi_symbols or {} - def convert(self, symbol, t=None, y=None): + def convert(self, symbol, t=None, y=None, u=None): """ - This function recurses down the tree, applying any simplifications defined in - classes derived from pybamm.Symbol. E.g. any expression multiplied by a - pybamm.Scalar(0) will be simplified to a pybamm.Scalar(0). - If a symbol has already been simplified, the stored value is returned. + This function recurses down the tree, converting the PyBaMM expression tree to + a CasADi expression tree Parameters ---------- symbol : :class:`pybamm.Symbol` The symbol to convert + t : :class:`casadi.MX` + A casadi symbol representing time + y : :class:`casadi.MX` + A casadi symbol representing state vectors + u : dict + A dictionary of casadi symbols representing inputs Returns ------- - CasADi symbol - The convert symbol + :class:`casadi.MX` + The converted symbol """ - try: return self._casadi_symbols[symbol.id] except KeyError: - casadi_symbol = self._convert(symbol, t, y) + # Change u to empty dictionary if it's None + u = u or {} + casadi_symbol = self._convert(symbol, t, y, u) self._casadi_symbols[symbol.id] = casadi_symbol return casadi_symbol - def _convert(self, symbol, t, y): + def _convert(self, symbol, t=None, y=None, u=None): """ See :meth:`CasadiConverter.convert()`. """ - if isinstance(symbol, (pybamm.Scalar, pybamm.Array, pybamm.Time)): - return casadi.MX(symbol.evaluate(t, y)) + if isinstance( + symbol, + ( + pybamm.Scalar, + pybamm.Array, + pybamm.Time, + pybamm.InputParameter, + pybamm.ExternalVariable, + ), + ): + return casadi.MX(symbol.evaluate(t, y, u)) elif isinstance(symbol, pybamm.StateVector): if y is None: @@ -50,23 +64,20 @@ def _convert(self, symbol, t, y): elif isinstance(symbol, pybamm.BinaryOperator): left, right = symbol.children # process children - converted_left = self.convert(left, t, y) - converted_right = self.convert(right, t, y) - if isinstance(symbol, pybamm.Outer): - return casadi.kron(converted_left, converted_right) - else: - # _binary_evaluate defined in derived classes for specific rules - return symbol._binary_evaluate(converted_left, converted_right) + converted_left = self.convert(left, t, y, u) + converted_right = self.convert(right, t, y, u) + # _binary_evaluate defined in derived classes for specific rules + return symbol._binary_evaluate(converted_left, converted_right) elif isinstance(symbol, pybamm.UnaryOperator): - converted_child = self.convert(symbol.child, t, y) + converted_child = self.convert(symbol.child, t, y, u) if isinstance(symbol, pybamm.AbsoluteValue): return casadi.fabs(converted_child) return symbol._unary_evaluate(converted_child) elif isinstance(symbol, pybamm.Function): converted_children = [ - self.convert(child, t, y) for child in symbol.children + self.convert(child, t, y, u) for child in symbol.children ] # Special functions if symbol.function == np.min: @@ -97,7 +108,7 @@ def _convert(self, symbol, t, y): return symbol._function_evaluate(converted_children) elif isinstance(symbol, pybamm.Concatenation): converted_children = [ - self.convert(child, t, y) for child in symbol.children + self.convert(child, t, y, u) for child in symbol.children ] if isinstance(symbol, (pybamm.NumpyConcatenation, pybamm.SparseStack)): return casadi.vertcat(*converted_children) diff --git a/pybamm/expression_tree/operations/evaluate.py b/pybamm/expression_tree/operations/evaluate.py index 43065a91b8..a11769b065 100644 --- a/pybamm/expression_tree/operations/evaluate.py +++ b/pybamm/expression_tree/operations/evaluate.py @@ -92,14 +92,6 @@ def find_symbols(symbol, constant_symbols, variable_symbols): "if scipy.sparse.issparse({1}) else " "{0} * {1}".format(children_vars[0], children_vars[1]) ) - elif isinstance(symbol, pybamm.Outer): - symbol_str = "np.outer({}, {}).reshape(-1, 1)".format( - children_vars[0], children_vars[1] - ) - elif isinstance(symbol, pybamm.Kron): - symbol_str = "scipy.sparse.csr_matrix(scipy.sparse.kron({}, {}))".format( - children_vars[0], children_vars[1] - ) else: symbol_str = children_vars[0] + " " + symbol.name + " " + children_vars[1] @@ -175,6 +167,9 @@ def find_symbols(symbol, constant_symbols, variable_symbols): elif isinstance(symbol, pybamm.Time): symbol_str = "t" + elif isinstance(symbol, pybamm.InputParameter): + symbol_str = "u['{}']".format(symbol.name) + else: raise NotImplementedError( "Not implemented for a symbol of type '{}'".format(type(symbol)) @@ -270,7 +265,7 @@ def __init__(self, symbol): self._result_var, "return" + self._result_var, "eval" ) - def evaluate(self, t=None, y=None, known_evals=None): + def evaluate(self, t=None, y=None, u=None, known_evals=None): """ Acts as a drop-in replacement for :func:`pybamm.Symbol.evaluate` """ diff --git a/pybamm/expression_tree/operations/jacobian.py b/pybamm/expression_tree/operations/jacobian.py index 324401e552..42bd3683da 100644 --- a/pybamm/expression_tree/operations/jacobian.py +++ b/pybamm/expression_tree/operations/jacobian.py @@ -46,15 +46,8 @@ def _jac(self, symbol, variable): # process children left_jac = self.jac(left, variable) right_jac = self.jac(right, variable) - # Need to treat outer differently. If the left child of an Outer - # evaluates to number then we need to return a matrix of zeros - # of the correct size, which requires variable.evaluation_array - if isinstance(symbol, pybamm.Outer): - # _outer_jac defined in pybamm.Outer - jac = symbol._outer_jac(left_jac, right_jac, variable) - else: - # _binary_jac defined in derived classes for specific rules - jac = symbol._binary_jac(left_jac, right_jac) + # _binary_jac defined in derived classes for specific rules + jac = symbol._binary_jac(left_jac, right_jac) elif isinstance(symbol, pybamm.UnaryOperator): child_jac = self.jac(symbol.child, variable) @@ -83,6 +76,5 @@ def _jac(self, symbol, variable): ) # jacobian removes the domain(s) - jac.domain = [] - jac.auxiliary_domains = {} + jac.clear_domains() return jac diff --git a/pybamm/expression_tree/operations/simplify.py b/pybamm/expression_tree/operations/simplify.py index f30769853e..2079379991 100644 --- a/pybamm/expression_tree/operations/simplify.py +++ b/pybamm/expression_tree/operations/simplify.py @@ -83,8 +83,8 @@ def flatten(this_class, left_child, right_child, in_subtraction): (1 + 2) - (2 + 3) -> [1, 2, 2, 3] and [None, Addition, Subtraction, Subtraction] """ - left_child.domain = [] - right_child.domain = [] + left_child.clear_domains() + right_child.clear_domains() for side, child in [("left", left_child), ("right", right_child)]: if isinstance(child, (pybamm.Addition, pybamm.Subtraction)): left, right = child.orphans @@ -284,8 +284,8 @@ def flatten( 1 / (c / 2) -> [1, 2] [c] [None, Multiplication] """ - left_child.domain = [] - right_child.domain = [] + left_child.clear_domains() + right_child.clear_domains() for side, child in [("left", left_child), ("right", right_child)]: if side == "left": @@ -581,8 +581,7 @@ def simplify(self, symbol): def _simplify(self, symbol): """ See :meth:`Simplification.simplify()`. """ - symbol.domain = [] - symbol.auxiliary_domains = {} + symbol.clear_domains() if isinstance(symbol, pybamm.BinaryOperator): left, right = symbol.children diff --git a/pybamm/expression_tree/parameter.py b/pybamm/expression_tree/parameter.py index f99bb09915..7000bca24b 100644 --- a/pybamm/expression_tree/parameter.py +++ b/pybamm/expression_tree/parameter.py @@ -27,7 +27,7 @@ def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ return Parameter(self.name, self.domain) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns the scalar 'NaN' to represent the shape of a parameter. See :meth:`pybamm.Symbol.evaluate_for_shape()` @@ -118,7 +118,7 @@ def _function_parameter_new_copy(self, children): """ return FunctionParameter(self.name, *children, diff_variable=self.diff_variable) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns the sum of the evaluated children See :meth:`pybamm.Symbol.evaluate_for_shape()` diff --git a/pybamm/expression_tree/scalar.py b/pybamm/expression_tree/scalar.py index 1c70228bef..975c770879 100644 --- a/pybamm/expression_tree/scalar.py +++ b/pybamm/expression_tree/scalar.py @@ -50,7 +50,7 @@ def set_id(self): (self.__class__, self.name) + tuple(self.domain) + tuple(str(self._value)) ) - def _base_evaluate(self, t=None, y=None): + def _base_evaluate(self, t=None, y=None, u=None): """ See :meth:`pybamm.Symbol._base_evaluate()`. """ return self._value diff --git a/pybamm/expression_tree/state_vector.py b/pybamm/expression_tree/state_vector.py index 688f07173a..d99079b085 100644 --- a/pybamm/expression_tree/state_vector.py +++ b/pybamm/expression_tree/state_vector.py @@ -103,7 +103,7 @@ def set_id(self): + tuple(self.domain) ) - def _base_evaluate(self, t=None, y=None): + def _base_evaluate(self, t=None, y=None, u=None): """ See :meth:`pybamm.Symbol._base_evaluate()`. """ if y is None: raise TypeError("StateVector cannot evaluate input 'y=None'") @@ -173,7 +173,7 @@ def new_copy(self): evaluation_array=self.evaluation_array, ) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a StateVector. The size of a StateVector is the number of True elements in its evaluation_array diff --git a/pybamm/expression_tree/symbol.py b/pybamm/expression_tree/symbol.py index a5af685ab2..49468dcc01 100644 --- a/pybamm/expression_tree/symbol.py +++ b/pybamm/expression_tree/symbol.py @@ -91,15 +91,6 @@ def __init__(self, name, children=None, domain=None, auxiliary_domains=None): if children is None: children = [] - if domain is None: - domain = [] - elif isinstance(domain, str): - domain = [domain] - if auxiliary_domains is None: - auxiliary_domains = {} - for level, dom in auxiliary_domains.items(): - if isinstance(dom, str): - auxiliary_domains[level] = [dom] for child in children: # copy child before adding @@ -110,6 +101,7 @@ def __init__(self, name, children=None, domain=None, auxiliary_domains=None): self.cached_children = super(Symbol, self).children # Set auxiliary domains + self._domain = None self.auxiliary_domains = auxiliary_domains # Set domain (and hence id) self.domain = domain @@ -157,8 +149,16 @@ def domain(self): @domain.setter def domain(self, domain): - if isinstance(domain, str): + if domain is None: + domain = [] + elif isinstance(domain, str): domain = [domain] + if domain == [] and self.auxiliary_domains != {}: + raise pybamm.DomainError( + "Domain cannot be empty if auxiliary domains are not empty" + ) + if domain in self.auxiliary_domains.values(): + raise pybamm.DomainError("Domain cannot be the same as an auxiliary domain") try: iter(domain) except TypeError: @@ -168,6 +168,43 @@ def domain(self, domain): # Update id since domain has changed self.set_id() + @property + def auxiliary_domains(self): + return self._auxiliary_domains + + @auxiliary_domains.setter + def auxiliary_domains(self, auxiliary_domains): + # Turn dictionary into appropriate form + if auxiliary_domains is None: + auxiliary_domains = {} + for level, dom in auxiliary_domains.items(): + if isinstance(dom, str): + auxiliary_domains[level] = [dom] + + # Check domains don't clash + if self.domain in auxiliary_domains.values(): + raise pybamm.DomainError("Domain cannot be the same as an auxiliary domain") + values = [tuple(val) for val in auxiliary_domains.values()] + if len(set(values)) != len(values): + raise pybamm.DomainError("All auxiliary domains must be different") + + self._auxiliary_domains = auxiliary_domains + + @property + def secondary_domain(self): + "Helper function to get the secondary domain of a symbol" + return self.auxiliary_domains["secondary"] + + def copy_domains(self, symbol): + "Copy the domains from a given symbol, bypassing checks" + self._domain = symbol.domain + self._auxiliary_domains = symbol.auxiliary_domains + + def clear_domains(self): + "Clear domains, bypassing checks" + self._domain = [] + self._auxiliary_domains = {} + def get_children_auxiliary_domains(self, children): "Combine auxiliary domains from children, at all levels" aux_domains = {} @@ -435,7 +472,7 @@ def _jac(self, variable): """ raise NotImplementedError - def _base_evaluate(self, t=None, y=None): + def _base_evaluate(self, t=None, y=None, u=None): """evaluate expression tree will raise a ``NotImplementedError`` if this member function has not @@ -459,7 +496,7 @@ def _base_evaluate(self, t=None, y=None): ) ) - def evaluate(self, t=None, y=None, known_evals=None): + def evaluate(self, t=None, y=None, u=None, known_evals=None): """Evaluate expression tree (wrapper to allow using dict of known values). If the dict 'known_evals' is provided, the dict is searched for self.id; if self.id is in the keys, return that value; otherwise, evaluate using @@ -471,6 +508,8 @@ def evaluate(self, t=None, y=None, known_evals=None): time at which to evaluate (default None) y : numpy.array, optional array to evaluate when solving (default None) + u : dict, optional + dictionary of inputs to use when solving (default None) known_evals : dict, optional dictionary containing known values (default None) @@ -483,10 +522,10 @@ def evaluate(self, t=None, y=None, known_evals=None): """ if known_evals is not None: if self.id not in known_evals: - known_evals[self.id] = self._base_evaluate(t, y) + known_evals[self.id] = self._base_evaluate(t, y, u) return known_evals[self.id], known_evals else: - return self._base_evaluate(t, y) + return self._base_evaluate(t, y, u) def evaluate_for_shape(self): """Evaluate expression tree to find its shape. For symbols that cannot be @@ -494,10 +533,19 @@ def evaluate_for_shape(self): shape is returned instead, using the symbol's domain. See :meth:`pybamm.Symbol.evaluate()` """ + try: + return self._saved_evaluate_for_shape + except AttributeError: + self._saved_evaluate_for_shape = self._evaluate_for_shape() + return self._saved_evaluate_for_shape + + def _evaluate_for_shape(self): + "See :meth:`Symbol.evaluate_for_shape`" return self.evaluate() def is_constant(self): """returns true if evaluating the expression is not dependent on `t` or `y` + or `u` See Also -------- @@ -505,8 +553,13 @@ def is_constant(self): """ # if any of the nodes are instances of any of these types, then the whole - # expression depends on either t or y - search_types = (pybamm.Variable, pybamm.StateVector, pybamm.IndependentVariable) + # expression depends on either t or y or u + search_types = ( + pybamm.Variable, + pybamm.StateVector, + pybamm.Time, + pybamm.InputParameter, + ) # do the search, return true if no relevent nodes are found return not any((isinstance(n, search_types)) for n in self.pre_order()) @@ -514,8 +567,8 @@ def is_constant(self): def evaluate_ignoring_errors(self): """ Evaluates the expression. If a node exists in the tree that cannot be evaluated - as a scalar or vectr (e.g. Parameter, Variable, StateVector), then None is - returned. Otherwise the result of the evaluation is given + as a scalar or vector (e.g. Parameter, Variable, StateVector, InputParameter), + then None is returned. Otherwise the result of the evaluation is given See Also -------- @@ -523,19 +576,18 @@ def evaluate_ignoring_errors(self): """ try: - result = self.evaluate(t=0) + result = self.evaluate(t=0, u="shape test") except NotImplementedError: - # return false if NotImplementedError is raised + # return None if NotImplementedError is raised # (there is a e.g. Parameter, Variable, ... in the tree) return None except TypeError as error: - # return false if specific TypeError is raised + # return None if specific TypeError is raised # (there is a e.g. StateVector in the tree) if error.args[0] == "StateVector cannot evaluate input 'y=None'": return None else: raise error - return result def evaluates_to_number(self): @@ -579,12 +631,12 @@ def simplify(self, simplified_symbols=None): """ Simplify the expression tree. See :class:`pybamm.Simplification`. """ return pybamm.Simplification(simplified_symbols).simplify(self) - def to_casadi(self, t=None, y=None, casadi_symbols=None): + def to_casadi(self, t=None, y=None, u=None, casadi_symbols=None): """ Convert the expression tree to a CasADi expression tree. See :class:`pybamm.CasadiConverter`. """ - return pybamm.CasadiConverter(casadi_symbols).convert(self, t, y) + return pybamm.CasadiConverter(casadi_symbols).convert(self, t, y, u) def new_copy(self): """ @@ -614,7 +666,7 @@ def shape(self): # Try with some large y, to avoid having to use pre_order (slow) try: y = np.linspace(0.1, 0.9, int(1e4)) - evaluated_self = self.evaluate(0, y) + evaluated_self = self.evaluate(0, y, u="shape test") # If that fails, fall back to calculating how big y should really be except ValueError: state_vectors_in_node = [ diff --git a/pybamm/expression_tree/unary_operators.py b/pybamm/expression_tree/unary_operators.py index 5ce164498a..0b4f68e22d 100644 --- a/pybamm/expression_tree/unary_operators.py +++ b/pybamm/expression_tree/unary_operators.py @@ -63,18 +63,18 @@ def _unary_evaluate(self, child): """Perform unary operation on a child. """ raise NotImplementedError - def evaluate(self, t=None, y=None, known_evals=None): + def evaluate(self, t=None, y=None, u=None, known_evals=None): """ See :meth:`pybamm.Symbol.evaluate()`. """ if known_evals is not None: if self.id not in known_evals: - child, known_evals = self.child.evaluate(t, y, known_evals) + child, known_evals = self.child.evaluate(t, y, u, known_evals) known_evals[self.id] = self._unary_evaluate(child) return known_evals[self.id], known_evals else: - child = self.child.evaluate(t, y) + child = self.child.evaluate(t, y, u) return self._unary_evaluate(child) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Default behaviour: unary operator has same shape as child See :meth:`pybamm.Symbol.evaluate_for_shape()` @@ -189,9 +189,9 @@ def __init__(self, child, index, name=None, check_size=True): super().__init__(name, child) - # no domain for integer value + # no domain for integer value key if isinstance(index, int): - self.domain = [] + self.clear_domains() def _unary_jac(self, child_jac): """ See :meth:`pybamm.UnaryOperator._unary_jac()`. """ @@ -228,7 +228,7 @@ def _unary_new_copy(self, child): return self.__class__(child, self.index, check_size=False) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return self._unary_evaluate(self.children[0].evaluate_for_shape()) def evaluates_on_edges(self): @@ -265,7 +265,7 @@ def diff(self, variable): # We shouldn't need this raise NotImplementedError - def _unary_simplify(self, child): + def _unary_simplify(self, simplified_child): """ See :meth:`pybamm.UnaryOperator.simplify()`. """ # if there are none of these nodes in the child tree, then this expression @@ -276,7 +276,7 @@ def _unary_simplify(self, child): if all([not (isinstance(n, search_types)) for n in self.pre_order()]): return pybamm.Scalar(0) else: - return self.__class__(child) + return self.__class__(simplified_child) class Gradient(SpatialOperator): @@ -327,7 +327,6 @@ class Gradient_Squared(SpatialOperator): operator with itself. In particular, this is useful in the finite element formualtion where we only require the (sclar valued) square of the gradient, and not the gradient itself. - **Extends:** :class:`SpatialOperator` """ @@ -348,7 +347,7 @@ class Mass(SpatialOperator): def __init__(self, child): super().__init__("mass", child) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return pybamm.evaluate_for_shape_using_domain(self.domain, typ="matrix") @@ -362,7 +361,7 @@ class BoundaryMass(SpatialOperator): def __init__(self, child): super().__init__("boundary mass", child) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return pybamm.evaluate_for_shape_using_domain(self.domain, typ="matrix") @@ -456,7 +455,7 @@ def _unary_new_copy(self, child): return self.__class__(child, self.integration_variable) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain(self.domain) @@ -494,8 +493,7 @@ def __init__(self, child, integration_variable): integration_variable = integration_variable[0] super().__init__(child, integration_variable) # overwrite domains with child domains - self.auxiliary_domains = child.auxiliary_domains - self.domain = child.domain + self.copy_domains(child) # Overwrite the name self.name = "{} integrated w.r.t {}".format( child.name, integration_variable.name @@ -503,7 +501,7 @@ def __init__(self, child, integration_variable): if isinstance(integration_variable, pybamm.SpatialVariable): self.name += " on {}".format(integration_variable.domain) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return self.children[0].evaluate_for_shape() @@ -532,7 +530,7 @@ def __init__(self, child, vector_type="row"): self.vector_type = vector_type super().__init__(name, child) # integrating removes the domain - self.domain = [] + self.clear_domains() def set_id(self): """ See :meth:`pybamm.Symbol.set_id()` """ @@ -552,7 +550,7 @@ def _unary_new_copy(self, child): return self.__class__(child, vector_type=self.vector_type) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain(self.domain) @@ -613,7 +611,7 @@ def _unary_new_copy(self, child): return self.__class__(child, region=self.region) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain(self.domain) @@ -637,6 +635,8 @@ class DeltaFunction(SpatialOperator): def __init__(self, child, side, domain): self.side = side + if domain is None: + raise pybamm.DomainError("Delta function domain cannot be None") if child.domain != []: auxiliary_domains = {"secondary": child.domain} else: @@ -694,7 +694,7 @@ def __init__(self, name, child, side): # boundary value of a child takes the domain from auxiliary domain of the child if child.auxiliary_domains != {}: domain = child.auxiliary_domains["secondary"] - # if child has no auxiliary domain, integral removes domain + # if child has no auxiliary domain, boundary operator removes domain else: domain = [] # tertiary auxiliary domain shift down to secondary @@ -722,7 +722,7 @@ def _unary_new_copy(self, child): """ See :meth:`UnaryOperator._unary_new_copy()`. """ return self.__class__(child, self.side) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain( self.domain, self.auxiliary_domains @@ -850,7 +850,7 @@ def grad_squared(expression): # -def surf(symbol, set_domain=False): +def surf(symbol): """convenience function for creating a right :class:`BoundaryValue`, usually in the spherical geometry @@ -865,19 +865,7 @@ def surf(symbol, set_domain=False): :class:`pybamm.BoundaryValue` the surface value of ``symbol`` """ - if symbol.domain in [["negative electrode"], ["positive electrode"]] and isinstance( - symbol, pybamm.PrimaryBroadcast - ): - child_surf = boundary_value(symbol.orphans[0], "right") - out = pybamm.PrimaryBroadcast(child_surf, symbol.domain) - else: - out = boundary_value(symbol, "right") - if set_domain: - if symbol.domain == ["negative particle"]: - out.domain = ["negative electrode"] - elif symbol.domain == ["positive particle"]: - out.domain = ["positive electrode"] - return out + return boundary_value(symbol, "right") def x_average(symbol): @@ -1035,9 +1023,18 @@ def boundary_value(symbol, side): new_symbol = symbol.new_copy() new_symbol.parent = None return new_symbol - # If symbol is a Broadcast, its boundary value is its child - if isinstance(symbol, pybamm.Broadcast): + # If symbol is a primary or full broadcast, its boundary value is its child + if isinstance(symbol, (pybamm.PrimaryBroadcast, pybamm.FullBroadcast)): return symbol.orphans[0] + # If symbol is a secondary broadcast, its boundary value is a primary broadcast of + # the boundary value of its child + if isinstance(symbol, pybamm.SecondaryBroadcast): + # Read child (making copy) + child = symbol.orphans[0] + # Take boundary value + boundary_child = boundary_value(child, side) + # Broadcast back to the original symbol's secondary domain + return pybamm.PrimaryBroadcast(boundary_child, symbol.secondary_domain) # Otherwise, calculate boundary value else: return BoundaryValue(symbol, side) @@ -1066,5 +1063,7 @@ def r_average(symbol): return symbol.orphans[0] else: r = pybamm.SpatialVariable("r", symbol.domain) - v = pybamm.Broadcast(pybamm.Scalar(1), symbol.domain) + v = pybamm.FullBroadcast( + pybamm.Scalar(1), symbol.domain, symbol.auxiliary_domains + ) return Integral(symbol, r) / Integral(v, r) diff --git a/pybamm/expression_tree/variable.py b/pybamm/expression_tree/variable.py index c0a350717d..935305c924 100644 --- a/pybamm/expression_tree/variable.py +++ b/pybamm/expression_tree/variable.py @@ -2,13 +2,15 @@ # Variable class # import pybamm +import numbers +import numpy as np class Variable(pybamm.Symbol): """A node in the expression tree represending a dependent variable This node will be discretised by :class:`.Discretisation` and converted - to a :class:`.Vector` node. + to a :class:`pybamm.StateVector` node. Parameters ---------- @@ -37,10 +39,73 @@ def __init__(self, name, domain=None, auxiliary_domains=None): def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ - return Variable(self.name, self.domain, self.auxiliary_domains) + return self.__class__(self.name, self.domain, self.auxiliary_domains) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain( self.domain, self.auxiliary_domains ) + + +class ExternalVariable(Variable): + """A node in the expression tree represending an external variable variable + + This node will be discretised by :class:`.Discretisation` and converted + to a :class:`.Vector` node. + + Parameters + ---------- + + name : str + name of the node + domain : iterable of str + list of domains that this variable is valid over + auxiliary_domains : dict + dictionary of auxiliary domains ({'secondary': ..., 'tertiary': ...}). For + example, for the single particle model, the particle concentration would be a + Variable with domain 'negative particle' and secondary auxiliary domain 'current + collector'. For the DFN, the particle concentration would be a Variable with + domain 'negative particle', secondary domain 'negative electrode' and tertiary + domain 'current collector' + + *Extends:* :class:`pybamm.Variable` + """ + + def __init__(self, name, size, domain=None, auxiliary_domains=None): + self._size = size + super().__init__(name, domain, auxiliary_domains) + + @property + def size(self): + return self._size + + def _evaluate_for_shape(self): + """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ + return np.nan * np.ones((self.size, 1)) + + def _base_evaluate(self, t=None, y=None, u=None): + # u should be a dictionary + # convert 'None' to empty dictionary for more informative error + if u is None: + u = {} + if not isinstance(u, dict): + # if the special input "shape test" is passed, just return 1 + if u == "shape test": + return self.evaluate_for_shape() + raise TypeError("inputs u should be a dictionary") + try: + out = u[self.name] + if isinstance(out, numbers.Number) or out.shape[0] == 1: + return out * np.ones((self.size, 1)) + elif out.shape[0] != self.size: + raise ValueError( + "External variable input has size {} but should be {}".format( + out.shape[0], self.size + ) + ) + else: + return out + # raise more informative error if can't find name in dict + except KeyError: + raise KeyError("External variable '{}' not found".format(self.name)) diff --git a/pybamm/logger.py b/pybamm/logger.py index 27853fb298..2be256297f 100644 --- a/pybamm/logger.py +++ b/pybamm/logger.py @@ -18,4 +18,3 @@ def set_logging_level(level): # Create a custom logger logger = logging.getLogger(__name__) set_logging_level("WARNING") - diff --git a/pybamm/meshes/meshes.py b/pybamm/meshes/meshes.py index 084b159689..f6da5633c7 100644 --- a/pybamm/meshes/meshes.py +++ b/pybamm/meshes/meshes.py @@ -159,6 +159,11 @@ def combine_submeshes(self, *submeshnames): "trying to combine two meshes in different coordinate systems" ) submeshes = [None] * len(self[submeshnames[0]]) + # Hack for the special case of current collector + if submeshnames == ("current collector",) and isinstance( + self[submeshnames[0]][0].edges, dict + ): + return self[submeshnames[0]] for i in range(len(self[submeshnames[0]])): combined_submesh_edges = np.concatenate( [self[submeshnames[0]][i].edges] @@ -235,6 +240,4 @@ def __call__(self, lims, npts, tabs=None): return self.submesh_type(lims, npts, tabs, **self.submesh_params) def __repr__(self): - return "Generator for {}".format( - self.submesh_type.__name__ - ) + return "Generator for {}".format(self.submesh_type.__name__) diff --git a/pybamm/models/base_model.py b/pybamm/models/base_model.py index 78625704d4..0f5ff41aa5 100644 --- a/pybamm/models/base_model.py +++ b/pybamm/models/base_model.py @@ -60,6 +60,9 @@ class BaseModel(object): mass_matrix : :class:`pybamm.Matrix` After discretisation, contains the mass matrix for the model. This is computed automatically + mass_matrix_inv : :class:`pybamm.Matrix` + After discretisation, contains the inverse mass matrix for the differential + (rhs) part of model. This is computed automatically jacobian : :class:`pybamm.Concatenation` Contains the Jacobian for the model. If model.use_jacobian is True, the Jacobian is computed automatically during solver set up @@ -88,7 +91,7 @@ class BaseModel(object): - "casadi": convert into CasADi expression tree, which then uses CasADi's \ algorithm to calculate the Jacobian. - Default is "python". + Default is "casadi". """ @@ -101,12 +104,13 @@ def __init__(self, name="Unnamed model"): self._algebraic = {} self._initial_conditions = {} self._boundary_conditions = {} - self._variables = {} + self._variables = pybamm.FuzzyDict() self._events = {} self._concatenated_rhs = None self._concatenated_algebraic = None self._concatenated_initial_conditions = None self._mass_matrix = None + self._mass_matrix_inv = None self._jacobian = None self._jacobian_algebraic = None self.external_variables = [] @@ -204,7 +208,7 @@ def variables(self): @variables.setter def variables(self, variables): - self._variables = variables + self._variables = pybamm.FuzzyDict(variables) def variable_names(self): return list(self._variables.keys()) @@ -249,6 +253,14 @@ def mass_matrix(self): def mass_matrix(self, mass_matrix): self._mass_matrix = mass_matrix + @property + def mass_matrix_inv(self): + return self._mass_matrix_inv + + @mass_matrix_inv.setter + def mass_matrix_inv(self, mass_matrix_inv): + self._mass_matrix_inv = mass_matrix_inv + @property def jacobian(self): return self._jacobian @@ -332,7 +344,10 @@ def check_and_combine_dict(self, dict1, dict2): ids1 = set(x.id for x in dict1.keys()) ids2 = set(x.id for x in dict2.keys()) if len(ids1.intersection(ids2)) != 0: - raise pybamm.ModelError("Submodel incompatible: duplicate variables") + variables = [x for x in dict1.keys() if x.id in ids1.intersection(ids2)] + raise pybamm.ModelError( + "Submodel incompatible: duplicate variables '{}'".format(variables) + ) dict1.update(dict2) def check_well_posedness(self, post_discretisation=False): @@ -368,7 +383,8 @@ def check_well_determined(self, post_discretisation): vars_in_rhs_keys = set() vars_in_algebraic_keys = set() vars_in_eqns = set() - # Get all variables ids from rhs and algebraic keys and equations + # Get all variables ids from rhs and algebraic keys and equations, and + # from boundary conditions # For equations we look through the whole expression tree. # "Variables" can be Concatenations so we also have to look in the whole # expression tree @@ -386,11 +402,16 @@ def check_well_determined(self, post_discretisation): vars_in_eqns.update( [x.id for x in eqn.pre_order() if isinstance(x, pybamm.Variable)] ) + for var, side_eqn in self.boundary_conditions.items(): + for side, (eqn, typ) in side_eqn.items(): + vars_in_eqns.update( + [x.id for x in eqn.pre_order() if isinstance(x, pybamm.Variable)] + ) # If any keys are repeated between rhs and algebraic then the model is # overdetermined if not set(vars_in_rhs_keys).isdisjoint(vars_in_algebraic_keys): raise pybamm.ModelError("model is overdetermined (repeated keys)") - # If any algebraic keys don't appear in the eqns then the model is + # If any algebraic keys don't appear in the eqns (or bcs) then the model is # overdetermined (but rhs keys can be absent from the eqns, e.g. dcdt = -1 is # fine) # Skip this step after discretisation, as any variables in the equations will @@ -422,13 +443,21 @@ def check_algebraic_equations(self, post_discretisation): After discretisation, there must be at least one StateVector in each algebraic equation """ + vars_in_bcs = set() + for var, side_eqn in self.boundary_conditions.items(): + for eqn, _ in side_eqn.values(): + vars_in_bcs.update( + [x.id for x in eqn.pre_order() if isinstance(x, pybamm.Variable)] + ) if not post_discretisation: # After the model has been defined, each algebraic equation key should - # appear in that algebraic equation + # appear in that algebraic equation, or in the boundary conditions # this has been relaxed for concatenations for now for var, eqn in self.algebraic.items(): - if not any(x.id == var.id for x in eqn.pre_order()) and not isinstance( - var, pybamm.Concatenation + if not ( + any(x.id == var.id for x in eqn.pre_order()) + or var.id in vars_in_bcs + or isinstance(var, pybamm.Concatenation) ): raise pybamm.ModelError( "each variable in the algebraic eqn keys must appear in the eqn" @@ -538,4 +567,3 @@ def default_solver(self): return pybamm.IDAKLUSolver() else: return pybamm.CasadiSolver(mode="safe") - diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index 380309e990..434dfb7450 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -37,10 +37,8 @@ class BaseBatteryModel(pybamm.BaseModel): (default) or "varying". Not currently implemented in any of the models. * "current collector" : str, optional Sets the current collector model to use. Can be "uniform" (default), - "potential pair", "potential pair quite conductive", "single particle - potential pair" or "set external potential". The submodel - "single particle potential pair" can only be used with lithium-ion - single particle models. The submodel "set external potential" can only + "potential pair", "potential pair quite conductive", or + "set external potential". The submodel "set external potential" can only be used with the SPM. * "particle" : str, optional Sets the submodel to use to describe behaviour within the particle. @@ -147,6 +145,7 @@ def options(self): @options.setter def options(self, extra_options): default_options = { + "operating mode": "current", "dimensionality": 0, "surface form": False, "convection": False, @@ -168,6 +167,13 @@ def options(self, extra_options): raise pybamm.OptionError("option {} not recognised".format(name)) # Some standard checks to make sure options are compatible + if not ( + options["operating mode"] in ["current", "voltage", "power"] + or callable(options["operating mode"]) + ): + raise pybamm.OptionError( + "operating mode '{}' not recognised".format(options["operating mode"]) + ) if ( isinstance(self, (pybamm.lead_acid.LOQS, pybamm.lead_acid.Composite)) and options["surface form"] is False @@ -188,7 +194,6 @@ def options(self, extra_options): "uniform", "potential pair", "potential pair quite conductive", - "single particle potential pair", "set external potential", ]: raise pybamm.OptionError( @@ -237,16 +242,6 @@ def options(self, extra_options): raise pybamm.OptionError( "thermal effects not implemented for lead-acid models" ) - if options[ - "current collector" - ] == "single particle potential pair" and not isinstance( - self, (pybamm.lithium_ion.SPM, pybamm.lithium_ion.SPMe) - ): - raise pybamm.OptionError( - "option {} only compatible with SPM or SPMe".format( - options["current collector"] - ) - ) if options["current collector"] == "set external potential" and not isinstance( self, pybamm.lithium_ion.SPM ): @@ -348,18 +343,6 @@ def set_standard_output_variables(self): self.variables = {} - # Current - i_cell = pybamm.electrical_parameters.current_with_time - i_cell_dim = pybamm.electrical_parameters.dimensional_current_density_with_time - I = pybamm.electrical_parameters.dimensional_current_with_time - self.variables.update( - { - "Total current density": i_cell, - "Total current density [A.m-2]": i_cell_dim, - "Current [A]": I, - } - ) - # Time time_scale = pybamm.electrical_parameters.timescale self.variables.update( @@ -368,7 +351,6 @@ def set_standard_output_variables(self): "Time [s]": pybamm.t * time_scale, "Time [min]": pybamm.t * time_scale / 60, "Time [h]": pybamm.t * time_scale / 3600, - "Discharge capacity [A.h]": I * pybamm.t * time_scale / 3600, } ) @@ -457,7 +439,11 @@ def build_coupled_variables(self): ) else: # try setting coupled variables on next loop through - pass + pybamm.logger.debug( + "Can't find {}, trying other submodels first".format( + key + ) + ) def build_model_equations(self): # Set model equations @@ -510,10 +496,10 @@ def build_model(self): self.build_model_equations() - pybamm.logger.debug("Setting voltage variables") + pybamm.logger.debug("Setting voltage variables ({})".format(self.name)) self.set_voltage_variables() - pybamm.logger.debug("Setting SoC variables") + pybamm.logger.debug("Setting SoC variables ({})".format(self.name)) self.set_soc_variables() # Massive hack for consistent delta_phi = phi_s - phi_e with SPMe @@ -529,6 +515,30 @@ def build_model(self): self._built = True + def set_external_circuit_submodel(self): + """ + Define how the external circuit defines the boundary conditions for the model, + e.g. (not necessarily constant-) current, voltage, etc + """ + if self.options["operating mode"] == "current": + self.submodels["external circuit"] = pybamm.external_circuit.CurrentControl( + self.param + ) + elif self.options["operating mode"] == "voltage": + self.submodels[ + "external circuit" + ] = pybamm.external_circuit.VoltageFunctionControl(self.param) + elif self.options["operating mode"] == "power": + self.submodels[ + "external circuit" + ] = pybamm.external_circuit.PowerFunctionControl(self.param) + elif callable(self.options["operating mode"]): + self.submodels[ + "external circuit" + ] = pybamm.external_circuit.FunctionControl( + self.param, self.options["operating mode"] + ) + def set_tortuosity_submodels(self): self.submodels["electrolyte tortuosity"] = pybamm.tortuosity.Bruggeman( self.param, "Electrolyte" @@ -639,8 +649,6 @@ def set_current_collector_submodel(self): submodel = pybamm.current_collector.PotentialPair1plus1D(self.param) elif self.options["dimensionality"] == 2: submodel = pybamm.current_collector.PotentialPair2plus1D(self.param) - elif self.options["current collector"] == "single particle potential pair": - submodel = pybamm.current_collector.SingleParticlePotentialPair(self.param) elif self.options["current collector"] == "set external potential": if self.options["dimensionality"] == 1: submodel = pybamm.current_collector.SetPotentialSingleParticle1plus1D( @@ -716,21 +724,6 @@ def set_voltage_variables(self): eta_r_av = eta_r_p_av - eta_r_n_av eta_r_av_dim = eta_r_p_av_dim - eta_r_n_av_dim - # terminal voltage (Note: phi_s_cn is zero at the negative tab) - phi_s_cp = self.variables["Positive current collector potential"] - phi_s_cp_dim = self.variables["Positive current collector potential [V]"] - if self.options["dimensionality"] == 0: - V = phi_s_cp - V_dim = phi_s_cp_dim - elif self.options["dimensionality"] in [1, 2]: - V = pybamm.BoundaryValue(phi_s_cp, "positive tab") - V_dim = pybamm.BoundaryValue(phi_s_cp_dim, "positive tab") - - phi_s_cn = self.variables["Negative current collector potential"] - phi_s_cn_dim = self.variables["Negative current collector potential [V]"] - V_local = phi_s_cp - phi_s_cn - V_local_dim = phi_s_cp_dim - phi_s_cn_dim - # TODO: add current collector losses to the voltage in 3D self.variables.update( @@ -743,14 +736,11 @@ def set_voltage_variables(self): "X-averaged reaction overpotential [V]": eta_r_av_dim, "X-averaged solid phase ohmic losses": delta_phi_s_av, "X-averaged solid phase ohmic losses [V]": delta_phi_s_av_dim, - "Local voltage": V_local, - "Local voltage [V]": V_local_dim, - "Terminal voltage": V, - "Terminal voltage [V]": V_dim, } ) # Battery-wide variables + V_dim = self.variables["Terminal voltage [V]"] eta_e_av_dim = self.variables.get("X-averaged electrolyte ohmic losses [V]", 0) eta_c_av_dim = self.variables.get( "X-averaged concentration overpotential [V]", 0 @@ -780,6 +770,10 @@ def set_voltage_variables(self): self.events["Minimum voltage"] = voltage - self.param.voltage_low_cut self.events["Maximum voltage"] = voltage - self.param.voltage_high_cut + # Power + I_dim = self.variables["Current [A]"] + self.variables.update({"Terminal power [W]": I_dim * V_dim}) + def set_soc_variables(self): """ Set variables relating to the state of charge. diff --git a/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py b/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py index f233d13c40..b2e10213e9 100644 --- a/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py +++ b/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py @@ -53,19 +53,6 @@ def default_solver(self): def set_standard_output_variables(self): super().set_standard_output_variables() - # Current - i_cell = pybamm.standard_parameters_lead_acid.current_with_time - i_cell_dim = ( - pybamm.standard_parameters_lead_acid.dimensional_current_density_with_time - ) - I = pybamm.standard_parameters_lead_acid.dimensional_current_with_time - self.variables.update( - { - "Total current density": i_cell, - "Total current density [A.m-2]": i_cell_dim, - "Current [A]": I, - } - ) # Time time_scale = pybamm.standard_parameters_lead_acid.tau_discharge @@ -74,7 +61,6 @@ def set_standard_output_variables(self): "Time [s]": pybamm.t * time_scale, "Time [min]": pybamm.t * time_scale / 60, "Time [h]": pybamm.t * time_scale / 3600, - "Discharge capacity [A.h]": I * pybamm.t * time_scale / 3600, } ) @@ -116,5 +102,5 @@ def set_soc_variables(self): if "Fractional Charge Input" not in self.variables: fci = pybamm.Variable("Fractional Charge Input", domain="current collector") self.variables["Fractional Charge Input"] = fci - self.rhs[fci] = -self.param.current_with_time * 100 + self.rhs[fci] = -self.variables["Total current density"] * 100 self.initial_conditions[fci] = self.param.q_init * 100 diff --git a/pybamm/models/full_battery_models/lead_acid/full.py b/pybamm/models/full_battery_models/lead_acid/full.py index a93339cb46..4e0a508558 100644 --- a/pybamm/models/full_battery_models/lead_acid/full.py +++ b/pybamm/models/full_battery_models/lead_acid/full.py @@ -34,6 +34,7 @@ class Full(BaseModel): def __init__(self, options=None, name="Full model", build=True): super().__init__(options, name) + self.set_external_circuit_submodel() self.set_reactions() self.set_interfacial_submodel() self.set_porosity_submodel() @@ -123,3 +124,4 @@ def set_side_reaction_submodels(self): self.submodels[ "negative oxygen interface" ] = pybamm.interface.lead_acid_oxygen.NoReaction(self.param, "Negative") + diff --git a/pybamm/models/full_battery_models/lead_acid/higher_order.py b/pybamm/models/full_battery_models/lead_acid/higher_order.py index 8151cfe031..7154583a02 100644 --- a/pybamm/models/full_battery_models/lead_acid/higher_order.py +++ b/pybamm/models/full_battery_models/lead_acid/higher_order.py @@ -34,6 +34,7 @@ class BaseHigherOrderModel(BaseModel): def __init__(self, options=None, name="Composite model", build=True): super().__init__(options, name) + self.set_external_circuit_submodel() self.set_leading_order_model() self.set_reactions() # Electrolyte submodel to get first-order concentrations diff --git a/pybamm/models/full_battery_models/lead_acid/loqs.py b/pybamm/models/full_battery_models/lead_acid/loqs.py index 14f6bf591c..fc5b0d66b4 100644 --- a/pybamm/models/full_battery_models/lead_acid/loqs.py +++ b/pybamm/models/full_battery_models/lead_acid/loqs.py @@ -33,6 +33,7 @@ class LOQS(BaseModel): def __init__(self, options=None, name="LOQS model", build=True): super().__init__(options, name) + self.set_external_circuit_submodel() self.set_reactions() self.set_interfacial_submodel() self.set_convection_submodel() @@ -51,6 +52,30 @@ def __init__(self, options=None, name="LOQS model", build=True): if self.options["dimensionality"] == 0: self.use_jacobian = False + def set_external_circuit_submodel(self): + """ + Define how the external circuit defines the boundary conditions for the model, + e.g. (not necessarily constant-) current, voltage, etc + """ + if self.options["operating mode"] == "current": + self.submodels[ + "leading order external circuit" + ] = pybamm.external_circuit.LeadingOrderCurrentControl(self.param) + elif self.options["operating mode"] == "voltage": + self.submodels[ + "leading order external circuit" + ] = pybamm.external_circuit.LeadingOrderVoltageFunctionControl(self.param) + elif self.options["operating mode"] == "power": + self.submodels[ + "leading order external circuit" + ] = pybamm.external_circuit.LeadingOrderPowerFunctionControl(self.param) + elif callable(self.options["operating mode"]): + self.submodels[ + "leading order external circuit" + ] = pybamm.external_circuit.LeadingOrderFunctionControl( + self.param, self.options["operating mode"] + ) + def set_current_collector_submodel(self): if self.options["current collector"] in [ diff --git a/pybamm/models/full_battery_models/lithium_ion/__init__.py b/pybamm/models/full_battery_models/lithium_ion/__init__.py index a807d22fc3..167a38f3b5 100644 --- a/pybamm/models/full_battery_models/lithium_ion/__init__.py +++ b/pybamm/models/full_battery_models/lithium_ion/__init__.py @@ -5,3 +5,5 @@ from .spm import SPM from .spme import SPMe from .dfn import DFN +from .basic_dfn import BasicDFN +from .basic_spm import BasicSPM diff --git a/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py b/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py index d2b71f6560..908e131b12 100644 --- a/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py +++ b/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py @@ -19,19 +19,6 @@ def __init__(self, options=None, name="Unnamed lithium-ion model"): def set_standard_output_variables(self): super().set_standard_output_variables() - # Current - i_cell = pybamm.standard_parameters_lithium_ion.current_with_time - i_cell_dim = ( - pybamm.standard_parameters_lithium_ion.dimensional_current_density_with_time - ) - I = pybamm.standard_parameters_lithium_ion.dimensional_current_with_time - self.variables.update( - { - "Total current density": i_cell, - "Total current density [A.m-2]": i_cell_dim, - "Current [A]": I, - } - ) # Time time_scale = pybamm.standard_parameters_lithium_ion.tau_discharge @@ -40,7 +27,6 @@ def set_standard_output_variables(self): "Time [s]": pybamm.t * time_scale, "Time [min]": pybamm.t * time_scale / 60, "Time [h]": pybamm.t * time_scale / 3600, - "Discharge capacity [A.h]": I * pybamm.t * time_scale / 3600, } ) diff --git a/pybamm/models/full_battery_models/lithium_ion/basic_dfn.py b/pybamm/models/full_battery_models/lithium_ion/basic_dfn.py new file mode 100644 index 0000000000..f395877b0d --- /dev/null +++ b/pybamm/models/full_battery_models/lithium_ion/basic_dfn.py @@ -0,0 +1,271 @@ +# +# Basic Doyle-Fuller-Newman (DFN) Model +# +import pybamm +from .base_lithium_ion_model import BaseModel + + +class BasicDFN(BaseModel): + """Doyle-Fuller-Newman (DFN) model of a lithium-ion battery, from [2]_. + + This class differs from the :class:`pybamm.lithium_ion.DFN` model class in that it + shows the whole model in a single class. This comes at the cost of flexibility in + comparing different physical effects, and in general the main DFN class should be + used instead. + + Parameters + ---------- + name : str, optional + The name of the model. + + References + ---------- + .. [2] SG Marquis, V Sulzer, R Timms, CP Please and SJ Chapman. “An asymptotic + derivation of a single particle model with electrolyte”. In: arXiv preprint + arXiv:1905.12553 (2019). + + + **Extends:** :class:`pybamm.lithium_ion.BaseModel` + """ + + def __init__(self, name="Doyle-Fuller-Newman model"): + super().__init__({}, name) + # `param` is a class containing all the relevant parameters and functions for + # this model. These are purely symbolic at this stage, and will be set by the + # `ParameterValues` class when the model is processed. + param = self.param + + ###################### + # Variables + ###################### + # Variables that depend on time only are created without a domain + Q = pybamm.Variable("Discharge capacity [A.h]") + # Variables that vary spatially are created with a domain + c_e_n = pybamm.Variable( + "Negative electrolyte concentration", domain="negative electrode", + ) + c_e_s = pybamm.Variable( + "Separator electrolyte concentration", domain="separator", + ) + c_e_p = pybamm.Variable( + "Positive electrolyte concentration", domain="positive electrode", + ) + # Concatenations combine several variables into a single variable, to simplify + # implementing equations that hold over several domains + c_e = pybamm.Concatenation(c_e_n, c_e_s, c_e_p) + + # Electrolyte potential + phi_e_n = pybamm.Variable( + "Negative electrolyte potential", domain="negative electrode", + ) + phi_e_s = pybamm.Variable( + "Separator electrolyte potential", domain="separator", + ) + phi_e_p = pybamm.Variable( + "Positive electrolyte potential", domain="positive electrode", + ) + phi_e = pybamm.Concatenation(phi_e_n, phi_e_s, phi_e_p) + + # Electrode potential + phi_s_n = pybamm.Variable( + "Negative electrode potential", domain="negative electrode", + ) + phi_s_p = pybamm.Variable( + "Positive electrode potential", domain="positive electrode", + ) + # Particle concentrations are variables on the particle domain, but also vary in + # the x-direction (electrode domain) and so must be provided with auxiliary + # domains + c_s_n = pybamm.Variable( + "Negative particle concentration", + domain="negative particle", + auxiliary_domains={"secondary": "negative electrode"}, + ) + c_s_p = pybamm.Variable( + "Positive particle concentration", + domain="positive particle", + auxiliary_domains={"secondary": "positive electrode"}, + ) + + # Constant temperature + T = param.T_init + + ###################### + # Other set-up + ###################### + + # Current density + i_cell = param.current_with_time + + # Porosity + # Primary broadcasts are used to broadcast scalar quantities across a domain + # into a vector of the right shape, for multiplying with other vectors + eps_n = pybamm.PrimaryBroadcast(param.epsilon_n, "negative electrode") + eps_s = pybamm.PrimaryBroadcast(param.epsilon_s, "separator") + eps_p = pybamm.PrimaryBroadcast(param.epsilon_p, "positive electrode") + eps = pybamm.Concatenation(eps_n, eps_s, eps_p) + + # Tortuosity + tor = pybamm.Concatenation( + eps_n ** param.b_e_n, eps_s ** param.b_e_s, eps_p ** param.b_e_p + ) + + # Interfacial reactions + # Surf takes the surface value of a variable, i.e. its boundary value on the + # right side. This is also accessible via `boundary_value(x, "right")`, with + # "left" providing the boundary value of the left side + c_s_surf_n = pybamm.surf(c_s_n) + j0_n = ( + param.m_n(T) + / param.C_r_n + * c_e_n ** (1 / 2) + * c_s_surf_n ** (1 / 2) + * (1 - c_s_surf_n) ** (1 / 2) + ) + j_n = ( + 2 + * j0_n + * pybamm.sinh( + param.ne_n / 2 * (phi_s_n - phi_e_n - param.U_n(c_s_surf_n, T)) + ) + ) + c_s_surf_p = pybamm.surf(c_s_p) + j0_p = ( + param.gamma_p + * param.m_p(T) + / param.C_r_p + * c_e_p ** (1 / 2) + * c_s_surf_p ** (1 / 2) + * (1 - c_s_surf_p) ** (1 / 2) + ) + j_s = pybamm.PrimaryBroadcast(0, "separator") + j_p = ( + 2 + * j0_p + * pybamm.sinh( + param.ne_p / 2 * (phi_s_p - phi_e_p - param.U_p(c_s_surf_p, T)) + ) + ) + j = pybamm.Concatenation(j_n, j_s, j_p) + + ###################### + # State of Charge + ###################### + I = param.dimensional_current_with_time + # The `rhs` dictionary contains differential equations, with the key being the + # variable in the d/dt + self.rhs[Q] = I * param.timescale / 3600 + # Initial conditions must be provided for the ODEs + self.initial_conditions[Q] = pybamm.Scalar(0) + + ###################### + # Particles + ###################### + + # The div and grad operators will be converted to the appropriate matrix + # multiplication at the discretisation stage + N_s_n = -param.D_n(c_s_n, T) * pybamm.grad(c_s_n) + N_s_p = -param.D_p(c_s_p, T) * pybamm.grad(c_s_p) + self.rhs[c_s_n] = -(1 / param.C_n) * pybamm.div(N_s_n) + self.rhs[c_s_p] = -(1 / param.C_p) * pybamm.div(N_s_p) + # Boundary conditions must be provided for equations with spatial derivatives + self.boundary_conditions[c_s_n] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (-param.C_n * j_n / param.a_n, "Neumann"), + } + self.boundary_conditions[c_s_p] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (-param.C_p * j_p / param.a_p / param.gamma_p, "Neumann"), + } + self.initial_conditions[c_s_n] = param.c_n_init + self.initial_conditions[c_s_p] = param.c_p_init + # Events specify points at which a solution should terminate + self.events.update( + { + "Minimum negative particle surface concentration": ( + pybamm.min(c_s_surf_n) - 0.01 + ), + "Maximum negative particle surface concentration": (1 - 0.01) + - pybamm.max(c_s_surf_n), + "Minimum positive particle surface concentration": ( + pybamm.min(c_s_surf_p) - 0.01 + ), + "Maximum positive particle surface concentration": (1 - 0.01) + - pybamm.max(c_s_surf_p), + } + ) + ###################### + # Current in the solid + ###################### + i_s_n = -param.sigma_n * (1 - eps_n) ** param.b_s_n * pybamm.grad(phi_s_n) + sigma_eff_p = param.sigma_p * (1 - eps_p) ** param.b_s_p + i_s_p = -sigma_eff_p * pybamm.grad(phi_s_p) + # The `algebraic` dictionary contains differential equations, with the key being + # the main scalar variable of interest in the equation + self.algebraic[phi_s_n] = pybamm.div(i_s_n) + j_n + self.algebraic[phi_s_p] = pybamm.div(i_s_p) + j_p + self.boundary_conditions[phi_s_n] = { + "left": (pybamm.Scalar(0), "Dirichlet"), + "right": (pybamm.Scalar(0), "Neumann"), + } + self.boundary_conditions[phi_s_p] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (i_cell / pybamm.boundary_value(-sigma_eff_p, "right"), "Neumann"), + } + # Initial conditions must also be provided for algebraic equations, as an + # initial guess for a root-finding algorithm which calculates consistent initial + # conditions + self.initial_conditions[phi_s_n] = pybamm.Scalar(0) + self.initial_conditions[phi_s_p] = param.U_p( + param.c_p_init, param.T_init + ) - param.U_n(param.c_n_init, param.T_init) + + ###################### + # Current in the electrolyte + ###################### + i_e = (param.kappa_e(c_e, T) * tor * param.gamma_e / param.C_e) * ( + param.chi(c_e) * pybamm.grad(c_e) / c_e - pybamm.grad(phi_e) + ) + self.algebraic[phi_e] = pybamm.div(i_e) - j + self.boundary_conditions[phi_e] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (pybamm.Scalar(0), "Neumann"), + } + self.initial_conditions[phi_e] = -param.U_n(param.c_n_init, param.T_init) + + ###################### + # Electrolyte concentration + ###################### + N_e = -tor * param.D_e(c_e, T) * pybamm.grad(c_e) + self.rhs[c_e] = (1 / eps) * ( + -pybamm.div(N_e) / param.C_e + (1 - param.t_plus) * j / param.gamma_e + ) + self.boundary_conditions[c_e] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (pybamm.Scalar(0), "Neumann"), + } + self.initial_conditions[c_e] = param.c_e_init + self.events["Zero electrolyte concentration cut-off"] = pybamm.min(c_e) - 0.002 + + ###################### + # (Some) variables + ###################### + voltage = pybamm.boundary_value(phi_s_p, "right") + # The `variables` dictionary contains all variables that might be useful for + # visualising the solution of the model + self.variables = { + "Negative particle surface concentration": c_s_surf_n, + "Electrolyte concentration": c_e, + "Positive particle surface concentration": c_s_surf_p, + "Current [A]": I, + "Negative electrode potential": phi_s_n, + "Electrolyte potential": phi_e, + "Positive electrode potential": phi_s_p, + "Terminal voltage": voltage, + } + self.events["Minimum voltage"] = voltage - param.voltage_low_cut + self.events["Maximum voltage"] = voltage - param.voltage_high_cut + + @property + def default_geometry(self): + return pybamm.Geometry("1D macro", "1+1D micro") diff --git a/pybamm/models/full_battery_models/lithium_ion/basic_spm.py b/pybamm/models/full_battery_models/lithium_ion/basic_spm.py new file mode 100644 index 0000000000..b907f56593 --- /dev/null +++ b/pybamm/models/full_battery_models/lithium_ion/basic_spm.py @@ -0,0 +1,172 @@ +# +# Basic Single Particle Model (SPM) +# +import pybamm +from .base_lithium_ion_model import BaseModel + + +class BasicSPM(BaseModel): + """Single Particle Model (SPM) model of a lithium-ion battery, from [2]_. + + This class differs from the :class:`pybamm.lithium_ion.SPM` model class in that it + shows the whole model in a single class. This comes at the cost of flexibility in + combining different physical effects, and in general the main SPM class should be + used instead. + + Parameters + ---------- + name : str, optional + The name of the model. + + References + ---------- + .. [2] SG Marquis, V Sulzer, R Timms, CP Please and SJ Chapman. “An asymptotic + derivation of a single particle model with electrolyte”. In: arXiv preprint + arXiv:1905.12553 (2019). + + + **Extends:** :class:`pybamm.lithium_ion.BaseModel` + """ + + def __init__(self, name="Single Particle Model"): + super().__init__({}, name) + # `param` is a class containing all the relevant parameters and functions for + # this model. These are purely symbolic at this stage, and will be set by the + # `ParameterValues` class when the model is processed. + param = self.param + + ###################### + # Variables + ###################### + # Variables that depend on time only are created without a domain + Q = pybamm.Variable("Discharge capacity [A.h]") + # Variables that vary spatially are created with a domain + c_s_n = pybamm.Variable( + "X-averaged negative particle concentration", domain="negative particle", + ) + c_s_p = pybamm.Variable( + "X-averaged positive particle concentration", domain="positive particle", + ) + + # Constant temperature + T = param.T_init + + ###################### + # Other set-up + ###################### + + # Current density + i_cell = param.current_with_time + j_n = i_cell / param.l_n + j_p = -i_cell / param.l_p + + ###################### + # State of Charge + ###################### + I = param.dimensional_current_with_time + # The `rhs` dictionary contains differential equations, with the key being the + # variable in the d/dt + self.rhs[Q] = I * param.timescale / 3600 + # Initial conditions must be provided for the ODEs + self.initial_conditions[Q] = pybamm.Scalar(0) + + ###################### + # Particles + ###################### + + # The div and grad operators will be converted to the appropriate matrix + # multiplication at the discretisation stage + N_s_n = -param.D_n(c_s_n, T) * pybamm.grad(c_s_n) + N_s_p = -param.D_p(c_s_p, T) * pybamm.grad(c_s_p) + self.rhs[c_s_n] = -(1 / param.C_n) * pybamm.div(N_s_n) + self.rhs[c_s_p] = -(1 / param.C_p) * pybamm.div(N_s_p) + # Boundary conditions must be provided for equations with spatial derivatives + self.boundary_conditions[c_s_n] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (-param.C_n * j_n / param.a_n, "Neumann"), + } + self.boundary_conditions[c_s_p] = { + "left": (pybamm.Scalar(0), "Neumann"), + "right": (-param.C_p * j_p / param.a_p / param.gamma_p, "Neumann"), + } + self.initial_conditions[c_s_n] = param.c_n_init + self.initial_conditions[c_s_p] = param.c_p_init + # Surf takes the surface value of a variable, i.e. its boundary value on the + # right side. This is also accessible via `boundary_value(x, "right")`, with + # "left" providing the boundary value of the left side + c_s_surf_n = pybamm.surf(c_s_n) + c_s_surf_p = pybamm.surf(c_s_p) + # Events specify points at which a solution should terminate + self.events.update( + { + "Minimum negative particle surface concentration": ( + pybamm.min(c_s_surf_n) - 0.01 + ), + "Maximum negative particle surface concentration": (1 - 0.01) + - pybamm.max(c_s_surf_n), + "Minimum positive particle surface concentration": ( + pybamm.min(c_s_surf_p) - 0.01 + ), + "Maximum positive particle surface concentration": (1 - 0.01) + - pybamm.max(c_s_surf_p), + } + ) + + # Note that the SPM does not have any algebraic equations, so the `algebraic` + # dictionary remains empty + + ###################### + # (Some) variables + ###################### + # Interfacial reactions + j0_n = ( + param.m_n(T) + / param.C_r_n + * 1 ** (1 / 2) + * c_s_surf_n ** (1 / 2) + * (1 - c_s_surf_n) ** (1 / 2) + ) + j0_p = ( + param.gamma_p + * param.m_p(T) + / param.C_r_p + * 1 ** (1 / 2) + * c_s_surf_p ** (1 / 2) + * (1 - c_s_surf_p) ** (1 / 2) + ) + eta_n = (2 / param.ne_n) * pybamm.arcsinh(j_n / (2 * j0_n)) + eta_p = (2 / param.ne_p) * pybamm.arcsinh(j_p / (2 * j0_p)) + phi_s_n = 0 + phi_e = -eta_n - param.U_n(c_s_surf_n, T) + phi_s_p = eta_p + phi_e + param.U_p(c_s_surf_p, T) + V = phi_s_p + + whole_cell = ["negative electrode", "separator", "positive electrode"] + # The `variables` dictionary contains all variables that might be useful for + # visualising the solution of the model + # Primary broadcasts are used to broadcast scalar quantities across a domain + # into a vector of the right shape, for multiplying with other vectors + self.variables = { + "Negative particle surface concentration": pybamm.PrimaryBroadcast( + c_s_surf_n, "negative electrode" + ), + "Electrolyte concentration": pybamm.PrimaryBroadcast(1, whole_cell), + "Positive particle surface concentration": pybamm.PrimaryBroadcast( + c_s_surf_p, "positive electrode" + ), + "Current [A]": I, + "Negative electrode potential": pybamm.PrimaryBroadcast( + phi_s_n, "negative electrode" + ), + "Electrolyte potential": pybamm.PrimaryBroadcast(phi_e, whole_cell), + "Positive electrode potential": pybamm.PrimaryBroadcast( + phi_s_p, "positive electrode" + ), + "Terminal voltage": V, + } + self.events["Minimum voltage"] = V - param.voltage_low_cut + self.events["Maximum voltage"] = V - param.voltage_high_cut + + @property + def default_geometry(self): + return pybamm.Geometry("1D macro", "1D micro") diff --git a/pybamm/models/full_battery_models/lithium_ion/dfn.py b/pybamm/models/full_battery_models/lithium_ion/dfn.py index 7df85ef67c..582629c921 100644 --- a/pybamm/models/full_battery_models/lithium_ion/dfn.py +++ b/pybamm/models/full_battery_models/lithium_ion/dfn.py @@ -33,6 +33,7 @@ class DFN(BaseModel): def __init__(self, options=None, name="Doyle-Fuller-Newman model", build=True): super().__init__(options, name) + self.set_external_circuit_submodel() self.set_reactions() self.set_porosity_submodel() self.set_tortuosity_submodels() diff --git a/pybamm/models/full_battery_models/lithium_ion/spm.py b/pybamm/models/full_battery_models/lithium_ion/spm.py index 7ad95b1697..023b64bc76 100644 --- a/pybamm/models/full_battery_models/lithium_ion/spm.py +++ b/pybamm/models/full_battery_models/lithium_ion/spm.py @@ -33,6 +33,7 @@ def __init__(self, options=None, name="Single Particle Model", build=True): super().__init__(options, name) self.set_reactions() + self.set_external_circuit_submodel() self.set_porosity_submodel() self.set_tortuosity_submodels() self.set_convection_submodel() @@ -98,14 +99,8 @@ def set_negative_electrode_submodel(self): def set_positive_electrode_submodel(self): - if self.options["current collector"] == "set external potential": - # Potentials are set by external model - set_positive_potential = False - else: - # Potential determined by 1D model - set_positive_potential = True self.submodels["positive electrode"] = pybamm.electrode.ohm.LeadingOrder( - self.param, "Positive", set_positive_potential=set_positive_potential + self.param, "Positive" ) def set_electrolyte_submodel(self): diff --git a/pybamm/models/full_battery_models/lithium_ion/spme.py b/pybamm/models/full_battery_models/lithium_ion/spme.py index 13084f91ae..a400d0dff2 100644 --- a/pybamm/models/full_battery_models/lithium_ion/spme.py +++ b/pybamm/models/full_battery_models/lithium_ion/spme.py @@ -35,6 +35,7 @@ def __init__( ): super().__init__(options, name) + self.set_external_circuit_submodel() self.set_reactions() self.set_porosity_submodel() self.set_tortuosity_submodels() diff --git a/pybamm/models/standard_variables.py b/pybamm/models/standard_variables.py index 64636416c5..1d30f5d83b 100644 --- a/pybamm/models/standard_variables.py +++ b/pybamm/models/standard_variables.py @@ -100,7 +100,7 @@ # Particle concentration c_s_n = pybamm.Variable( "Negative particle concentration", - "negative particle", + domain="negative particle", auxiliary_domains={ "secondary": "negative electrode", "tertiary": "current collector", @@ -108,7 +108,7 @@ ) c_s_p = pybamm.Variable( "Positive particle concentration", - "positive particle", + domain="positive particle", auxiliary_domains={ "secondary": "positive electrode", "tertiary": "current collector", @@ -116,29 +116,29 @@ ) c_s_n_xav = pybamm.Variable( "X-averaged negative particle concentration", - "negative particle", + domain="negative particle", auxiliary_domains={"secondary": "current collector"}, ) c_s_p_xav = pybamm.Variable( "X-averaged positive particle concentration", - "positive particle", + domain="positive particle", auxiliary_domains={"secondary": "current collector"}, ) c_s_n_surf = pybamm.Variable( "Negative particle surface concentration", - "negative electrode", + domain="negative electrode", auxiliary_domains={"secondary": "current collector"}, ) c_s_p_surf = pybamm.Variable( "Positive particle surface concentration", - "positive electrode", + domain="positive electrode", auxiliary_domains={"secondary": "current collector"}, ) c_s_n_surf_xav = pybamm.Variable( - "X-averaged negative particle surface concentration", "current collector" + "X-averaged negative particle surface concentration", domain="current collector" ) c_s_p_surf_xav = pybamm.Variable( - "X-averaged positive particle surface concentration", "current collector" + "X-averaged positive particle surface concentration", domain="current collector" ) diff --git a/pybamm/models/submodels/base_submodel.py b/pybamm/models/submodels/base_submodel.py index 47291a4124..99f8568773 100644 --- a/pybamm/models/submodels/base_submodel.py +++ b/pybamm/models/submodels/base_submodel.py @@ -46,7 +46,14 @@ class BaseSubModel: symbols. """ - def __init__(self, param, domain=None, reactions=None, external=False): + def __init__( + self, + param, + domain=None, + reactions=None, + name="Unnamed submodel", + external=False, + ): super().__init__() self.param = param # Initialise empty variables (to avoid overwriting with 'None') @@ -61,6 +68,7 @@ def __init__(self, param, domain=None, reactions=None, external=False): self.domain = domain self.set_domain_for_broadcast() self.reactions = reactions + self.name = name self.external = external diff --git a/pybamm/models/submodels/current_collector/__init__.py b/pybamm/models/submodels/current_collector/__init__.py index 2b575b750d..e4a9774255 100644 --- a/pybamm/models/submodels/current_collector/__init__.py +++ b/pybamm/models/submodels/current_collector/__init__.py @@ -2,7 +2,6 @@ from .homogeneous_current_collector import Uniform from .effective_resistance_current_collector import EffectiveResistance2D -from .single_particle_potential_pair import SingleParticlePotentialPair from .potential_pair import ( BasePotentialPair, PotentialPair1plus1D, diff --git a/pybamm/models/submodels/current_collector/base_current_collector.py b/pybamm/models/submodels/current_collector/base_current_collector.py index fb1af13345..f9c8c788f9 100644 --- a/pybamm/models/submodels/current_collector/base_current_collector.py +++ b/pybamm/models/submodels/current_collector/base_current_collector.py @@ -19,15 +19,6 @@ class BaseModel(pybamm.BaseSubModel): def __init__(self, param): super().__init__(param) - def get_coupled_variables(self, variables): - - # 1D models determine phi_s_cp - phi_s_cn = variables["Negative current collector potential"] - phi_s_cp = variables["Positive current collector potential"] - - variables = self._get_standard_potential_variables(phi_s_cn, phi_s_cp) - return variables - def _get_standard_negative_potential_variables(self, phi_s_cn): """ A private function to obtain the standard variables which @@ -35,8 +26,8 @@ def _get_standard_negative_potential_variables(self, phi_s_cn): Parameters ---------- - phi_cc : :class:`pybamm.Symbol` - The potential in the current collector. + phi_s_cn : :class:`pybamm.Symbol` + The potential in the negative current collector. Returns ------- @@ -54,40 +45,6 @@ def _get_standard_negative_potential_variables(self, phi_s_cn): return variables - def _get_standard_potential_variables(self, phi_s_cn, phi_s_cp): - """ - A private function to obtain the standard variables which - can be derived from the potentials in the current collector. - - Parameters - ---------- - phi_cc : :class:`pybamm.Symbol` - The potential in the current collector. - - Returns - ------- - variables : dict - The variables which can be derived from the potential in the - current collector. - """ - - pot_scale = self.param.potential_scale - U_ref = self.param.U_p_ref - self.param.U_n_ref - - # Local potential difference - V_cc = phi_s_cp - phi_s_cn - - variables = { - "Positive current collector potential": phi_s_cp, - "Positive current collector potential [V]": U_ref + phi_s_cp * pot_scale, - "Local current collector potential difference": V_cc, - "Local current collector potential difference [V]": U_ref - + V_cc * pot_scale, - } - variables.update(self._get_standard_negative_potential_variables(phi_s_cn)) - - return variables - def _get_standard_current_variables(self, i_cc, i_boundary_cc): """ A private function to obtain the standard variables which diff --git a/pybamm/models/submodels/current_collector/effective_resistance_current_collector.py b/pybamm/models/submodels/current_collector/effective_resistance_current_collector.py index 49df45dc19..26819f11fc 100644 --- a/pybamm/models/submodels/current_collector/effective_resistance_current_collector.py +++ b/pybamm/models/submodels/current_collector/effective_resistance_current_collector.py @@ -113,7 +113,7 @@ def __init__(self): } ) - def get_processed_potentials(self, solution, mesh, param_values, V_av, I_av): + def get_processed_potentials(self, solution, param_values, V_av, I_av): """ Calculates the potentials in the current collector given the average voltage and current. @@ -134,18 +134,8 @@ def get_processed_potentials(self, solution, mesh, param_values, V_av, I_av): U_ref = param_values.evaluate(param.U_p_ref - param.U_n_ref) # Process psi and W, and their (average) values at the negative tab - psi = pybamm.ProcessedVariable( - self.variables["Current collector potential weighted sum"], - solution.t, - solution.y, - mesh, - ) - W = pybamm.ProcessedVariable( - self.variables["Perturbation to current collector potential difference"], - solution.t, - solution.y, - mesh, - ) + psi = solution["Current collector potential weighted sum"] + W = solution["Perturbation to current collector potential difference"] psi_neg_tab = self.variables[ "Current collector potential weighted sum (negative tab)" ].evaluate(y=solution.y[:, 0])[0][0] @@ -197,8 +187,8 @@ def phi_s_cp_dim(t, y, z): "Negative current collector potential [V]": phi_s_cn_dim, "Positive current collector potential": phi_s_cp, "Positive current collector potential [V]": phi_s_cp_dim, - "Local current collector potential difference": V_cc, - "Local current collector potential difference [V]": V_cc_dim, + "Local voltage": V_cc, + "Local voltage [V]": V_cc_dim, } return potentials diff --git a/pybamm/models/submodels/current_collector/homogeneous_current_collector.py b/pybamm/models/submodels/current_collector/homogeneous_current_collector.py index 8315ed881b..4d65f29cd1 100644 --- a/pybamm/models/submodels/current_collector/homogeneous_current_collector.py +++ b/pybamm/models/submodels/current_collector/homogeneous_current_collector.py @@ -21,12 +21,12 @@ class Uniform(BaseModel): def __init__(self, param): super().__init__(param) - def get_fundamental_variables(self): + def get_coupled_variables(self, variables): # TODO: grad not implemented for 2D yet i_cc = pybamm.Scalar(0) i_boundary_cc = pybamm.PrimaryBroadcast( - self.param.current_with_time, "current collector" + variables["Total current density"], "current collector" ) phi_s_cn = pybamm.PrimaryBroadcast(0, "current collector") @@ -40,4 +40,5 @@ def get_fundamental_variables(self): variables["Leading-order current collector current density"] = variables[ "Current collector current density" ] + return variables diff --git a/pybamm/models/submodels/current_collector/potential_pair.py b/pybamm/models/submodels/current_collector/potential_pair.py index 79b1382a78..14513b6f29 100644 --- a/pybamm/models/submodels/current_collector/potential_pair.py +++ b/pybamm/models/submodels/current_collector/potential_pair.py @@ -60,8 +60,7 @@ def set_algebraic(self, variables): def set_initial_conditions(self, variables): - param = self.param - applied_current = param.current_with_time + applied_current = variables["Total current density"] cc_area = self._get_effective_current_collector_area() phi_s_cn = variables["Negative current collector potential"] i_boundary_cc = variables["Current collector current density"] @@ -84,7 +83,7 @@ def set_boundary_conditions(self, variables): phi_s_cp = variables["Positive current collector potential"] param = self.param - applied_current = param.current_with_time + applied_current = variables["Total current density"] cc_area = self._get_effective_current_collector_area() # cc_area appears here due to choice of non-dimensionalisation @@ -124,7 +123,7 @@ def set_boundary_conditions(self, variables): phi_s_cp = variables["Positive current collector potential"] param = self.param - applied_current = param.current_with_time + applied_current = variables["Total current density"] cc_area = self._get_effective_current_collector_area() # Note: we divide by the *numerical* tab area so that the correct total diff --git a/pybamm/models/submodels/current_collector/quite_conductive_potential_pair.py b/pybamm/models/submodels/current_collector/quite_conductive_potential_pair.py index e71b1307f4..5f84c22470 100644 --- a/pybamm/models/submodels/current_collector/quite_conductive_potential_pair.py +++ b/pybamm/models/submodels/current_collector/quite_conductive_potential_pair.py @@ -47,7 +47,7 @@ def get_fundamental_variables(self): def set_algebraic(self, variables): param = self.param - applied_current = param.current_with_time + applied_current = variables["Total current density"] cc_area = self._get_effective_current_collector_area() z = pybamm.standard_spatial_vars.z diff --git a/pybamm/models/submodels/current_collector/set_potential_single_particle.py b/pybamm/models/submodels/current_collector/set_potential_single_particle.py index bc7eafe28a..e519bd57f1 100644 --- a/pybamm/models/submodels/current_collector/set_potential_single_particle.py +++ b/pybamm/models/submodels/current_collector/set_potential_single_particle.py @@ -32,9 +32,8 @@ def __init__(self, param): def get_fundamental_variables(self): phi_s_cn = pybamm.standard_variables.phi_s_cn - phi_s_cp = pybamm.standard_variables.phi_s_cp - variables = self._get_standard_potential_variables(phi_s_cn, phi_s_cp) + variables = self._get_standard_negative_potential_variables(phi_s_cn) # TO DO: grad not implemented for 2D yet i_cc = pybamm.Scalar(0) @@ -53,11 +52,10 @@ def get_fundamental_variables(self): def set_rhs(self, variables): phi_s_cn = variables["Negative current collector potential"] - phi_s_cp = variables["Positive current collector potential"] # Dummy equations so that PyBaMM doesn't change the potentials during solve # i.e. d_phi/d_t = 0. Potentials are set externally between steps. - self.rhs = {phi_s_cn: pybamm.Scalar(0), phi_s_cp: pybamm.Scalar(0)} + self.rhs = {phi_s_cn: pybamm.Scalar(0)} def set_algebraic(self, variables): ocp_p_av = variables["X-averaged positive electrode open circuit potential"] @@ -69,7 +67,7 @@ def set_algebraic(self, variables): delta_phi_s_p_av = variables["X-averaged positive electrode ohmic losses"] i_boundary_cc = variables["Current collector current density"] - v_boundary_cc = variables["Local current collector potential difference"] + v_boundary_cc = variables["Local voltage"] # The voltage-current expression from the SPM(e) local_voltage_expression = ( ocp_p_av @@ -84,17 +82,13 @@ def set_algebraic(self, variables): def set_initial_conditions(self, variables): - param = self.param - applied_current = param.current_with_time + applied_current = variables["Total current density"] cc_area = self._get_effective_current_collector_area() phi_s_cn = variables["Negative current collector potential"] - phi_s_cp = variables["Positive current collector potential"] i_boundary_cc = variables["Current collector current density"] self.initial_conditions = { phi_s_cn: pybamm.Scalar(0), - phi_s_cp: param.U_p(param.c_p_init, param.T_init) - - param.U_n(param.c_n_init, param.T_init), i_boundary_cc: applied_current / cc_area, } diff --git a/pybamm/models/submodels/current_collector/single_particle_potential_pair.py b/pybamm/models/submodels/current_collector/single_particle_potential_pair.py deleted file mode 100644 index 615c2c71aa..0000000000 --- a/pybamm/models/submodels/current_collector/single_particle_potential_pair.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Class for two-dimensional current collectors - Single-Particle formulation -# -from .potential_pair import PotentialPair2plus1D - - -class SingleParticlePotentialPair(PotentialPair2plus1D): - """A submodel for Ohm's law plus conservation of current in the current collectors, - which uses the voltage-current relationship from the SPM(e). - - Parameters - ---------- - param : parameter class - The parameters to use for this submodel - - - **Extends:** :class:`pybamm.current_collector.PotentialPair2plus1D` - """ - - def __init__(self, param): - super().__init__(param) - - def get_coupled_variables(self, variables): - ocp_p_av = variables["X-averaged positive electrode open circuit potential"] - ocp_n_av = variables["X-averaged negative electrode open circuit potential"] - eta_r_n_av = variables["X-averaged negative electrode reaction overpotential"] - eta_r_p_av = variables["X-averaged positive electrode reaction overpotential"] - eta_e_av = variables["X-averaged electrolyte overpotential"] - delta_phi_s_n_av = variables["X-averaged negative electrode ohmic losses"] - delta_phi_s_p_av = variables["X-averaged positive electrode ohmic losses"] - - phi_s_cn = variables["Negative current collector potential"] - - local_voltage_expression = ( - ocp_p_av - - ocp_n_av - + eta_r_p_av - - eta_r_n_av - + eta_e_av - + delta_phi_s_p_av - - delta_phi_s_n_av - ) - phi_s_cp = phi_s_cn + local_voltage_expression - variables = self._get_standard_potential_variables(phi_s_cn, phi_s_cp) - return variables diff --git a/pybamm/models/submodels/electrode/base_electrode.py b/pybamm/models/submodels/electrode/base_electrode.py index 1a77f7b713..a4e786f32a 100644 --- a/pybamm/models/submodels/electrode/base_electrode.py +++ b/pybamm/models/submodels/electrode/base_electrode.py @@ -40,24 +40,23 @@ def _get_standard_potential_variables(self, phi_s): electrode. """ param = self.param + pot = param.potential_scale phi_s_av = pybamm.x_average(phi_s) if self.domain == "Negative": - phi_s_dim = param.potential_scale * phi_s - phi_s_av_dim = param.potential_scale * phi_s_av + phi_s_dim = pot * phi_s + phi_s_av_dim = pot * phi_s_av delta_phi_s = phi_s elif self.domain == "Positive": - phi_s_dim = param.U_p_ref - param.U_n_ref + param.potential_scale * phi_s - phi_s_av_dim = ( - param.U_p_ref - param.U_n_ref + param.potential_scale * phi_s_av - ) + phi_s_dim = param.U_p_ref - param.U_n_ref + pot * phi_s + phi_s_av_dim = param.U_p_ref - param.U_n_ref + pot * phi_s_av v = pybamm.boundary_value(phi_s, "right") delta_phi_s = phi_s - v delta_phi_s_av = pybamm.x_average(delta_phi_s) - delta_phi_s_dim = delta_phi_s * param.potential_scale - delta_phi_s_av_dim = delta_phi_s_av * param.potential_scale + delta_phi_s_dim = delta_phi_s * pot + delta_phi_s_av_dim = delta_phi_s_av * pot variables = { self.domain + " electrode potential": phi_s, @@ -108,6 +107,51 @@ def _get_standard_current_variables(self, i_s): return variables + def _get_standard_current_collector_potential_variables(self, phi_s_cn, phi_s_cp): + """ + A private function to obtain the standard variables which + can be derived from the potentials in the current collector. + + Parameters + ---------- + phi_cc : :class:`pybamm.Symbol` + The potential in the current collector. + + Returns + ------- + variables : dict + The variables which can be derived from the potential in the + current collector. + """ + + pot_scale = self.param.potential_scale + U_ref = self.param.U_p_ref - self.param.U_n_ref + phi_s_cp_dim = U_ref + phi_s_cp * pot_scale + + # Local potential difference + V_cc = phi_s_cp - phi_s_cn + + # Terminal voltage + # Note phi_s_cn is always zero at the negative tab + V = pybamm.boundary_value(phi_s_cp, "positive tab") + V_dim = pybamm.boundary_value(phi_s_cp_dim, "positive tab") + + # Voltage is local current collector potential difference at the tabs, in 1D + # this will be equal to the local current collector potential difference + + variables = { + "Negative current collector potential": phi_s_cn, + "Negative current collector potential [V]": phi_s_cn * pot_scale, + "Positive current collector potential": phi_s_cp, + "Positive current collector potential [V]": phi_s_cp_dim, + "Local voltage": V_cc, + "Local voltage [V]": U_ref + V_cc * pot_scale, + "Terminal voltage": V, + "Terminal voltage [V]": V_dim, + } + + return variables + def _get_standard_whole_cell_variables(self, variables): """ A private function to obtain the whole-cell versions of the @@ -131,14 +175,19 @@ def _get_standard_whole_cell_variables(self, variables): i_s = pybamm.Concatenation(i_s_n, i_s_s, i_s_p) + variables.update({"Electrode current density": i_s}) + if self.set_positive_potential: + # Get phi_s_cn from the current collector submodel and phi_s_p from the + # electrode submodel + phi_s_cn = variables["Negative current collector potential"] phi_s_p = variables["Positive electrode potential"] phi_s_cp = pybamm.boundary_value(phi_s_p, "right") - variables = { - "Electrode current density": i_s, - "Positive current collector potential": phi_s_cp, - } - else: - variables = {"Electrode current density": i_s} + variables.update( + self._get_standard_current_collector_potential_variables( + phi_s_cn, phi_s_cp + ) + ) return variables + diff --git a/pybamm/models/submodels/electrode/ohm/full_ohm.py b/pybamm/models/submodels/electrode/ohm/full_ohm.py index 1abb20b2f1..7ed04dc09b 100644 --- a/pybamm/models/submodels/electrode/ohm/full_ohm.py +++ b/pybamm/models/submodels/electrode/ohm/full_ohm.py @@ -71,7 +71,6 @@ def set_boundary_conditions(self, variables): phi_s = variables[self.domain + " electrode potential"] phi_s_cn = variables["Negative current collector potential"] tor = variables[self.domain + " electrode tortuosity"] - i_boundary_cc = variables["Current collector current density"] if self.domain == "Negative": lbc = (phi_s_cn, "Dirichlet") @@ -80,6 +79,7 @@ def set_boundary_conditions(self, variables): elif self.domain == "Positive": lbc = (pybamm.Scalar(0), "Neumann") sigma_eff = self.param.sigma_p * tor + i_boundary_cc = variables["Current collector current density"] rbc = ( i_boundary_cc / pybamm.boundary_value(-sigma_eff, "right"), "Neumann", diff --git a/pybamm/models/submodels/external_circuit/__init__.py b/pybamm/models/submodels/external_circuit/__init__.py new file mode 100644 index 0000000000..21966d10b5 --- /dev/null +++ b/pybamm/models/submodels/external_circuit/__init__.py @@ -0,0 +1,11 @@ +from .base_external_circuit import BaseModel, LeadingOrderBaseModel +from .current_control_external_circuit import CurrentControl, LeadingOrderCurrentControl +from .function_control_external_circuit import ( + FunctionControl, + VoltageFunctionControl, + PowerFunctionControl, + LeadingOrderFunctionControl, + LeadingOrderVoltageFunctionControl, + LeadingOrderPowerFunctionControl, +) + diff --git a/pybamm/models/submodels/external_circuit/base_external_circuit.py b/pybamm/models/submodels/external_circuit/base_external_circuit.py new file mode 100644 index 0000000000..9c69e00eab --- /dev/null +++ b/pybamm/models/submodels/external_circuit/base_external_circuit.py @@ -0,0 +1,53 @@ +# +# Base model for the external circuit +# +import pybamm + + +class BaseModel(pybamm.BaseSubModel): + """Model to represent the behaviour of the external circuit. """ + + def __init__(self, param): + super().__init__(param) + + def _get_current_variables(self, i_cell): + param = self.param + I = i_cell * abs(param.I_typ) + i_cell_dim = I / (param.n_electrodes_parallel * param.A_cc) + + variables = { + "Total current density": i_cell, + "Total current density [A.m-2]": i_cell_dim, + "Current [A]": I, + "C-rate": I / param.Q, + } + + return variables + + def get_fundamental_variables(self): + Q = pybamm.Variable("Discharge capacity [A.h]") + variables = {"Discharge capacity [A.h]": Q} + return variables + + def set_initial_conditions(self, variables): + Q = variables["Discharge capacity [A.h]"] + self.initial_conditions[Q] = pybamm.Scalar(0) + + def set_rhs(self, variables): + # ODE for discharge capacity + Q = variables["Discharge capacity [A.h]"] + I = variables["Current [A]"] + self.rhs[Q] = I * self.param.timescale / 3600 + + +class LeadingOrderBaseModel(BaseModel): + """Model to represent the behaviour of the external circuit, at leading order. """ + + def __init__(self, param): + super().__init__(param) + + def get_fundamental_variables(self): + Q = pybamm.Variable("Leading-order discharge capacity [A.h]") + variables = {"Discharge capacity [A.h]": Q} + return variables + diff --git a/pybamm/models/submodels/external_circuit/current_control_external_circuit.py b/pybamm/models/submodels/external_circuit/current_control_external_circuit.py new file mode 100644 index 0000000000..0368852226 --- /dev/null +++ b/pybamm/models/submodels/external_circuit/current_control_external_circuit.py @@ -0,0 +1,37 @@ +# +# External circuit with current control +# +from .base_external_circuit import BaseModel, LeadingOrderBaseModel + + +class CurrentControl(BaseModel): + """External circuit with current control. """ + + def __init__(self, param): + super().__init__(param) + + def get_fundamental_variables(self): + # Current is given as a function of time + i_cell = self.param.current_with_time + i_cell_dim = self.param.dimensional_current_density_with_time + I = self.param.dimensional_current_with_time + + variables = { + "Total current density": i_cell, + "Total current density [A.m-2]": i_cell_dim, + "Current [A]": I, + "C-rate": I / self.param.Q, + } + + # Add discharge capacity variable + variables.update(super().get_fundamental_variables()) + + return variables + + +class LeadingOrderCurrentControl(CurrentControl, LeadingOrderBaseModel): + """External circuit with current control, for leading order models. """ + + def __init__(self, param): + super().__init__(param) + diff --git a/pybamm/models/submodels/external_circuit/function_control_external_circuit.py b/pybamm/models/submodels/external_circuit/function_control_external_circuit.py new file mode 100644 index 0000000000..fb1dd39f6b --- /dev/null +++ b/pybamm/models/submodels/external_circuit/function_control_external_circuit.py @@ -0,0 +1,108 @@ +# +# External circuit with an arbitrary function +# +import pybamm +from .base_external_circuit import BaseModel, LeadingOrderBaseModel + + +class FunctionControl(BaseModel): + """External circuit with an arbitrary function. """ + + def __init__(self, param, external_circuit_class): + super().__init__(param) + self.external_circuit_class = external_circuit_class + + def _get_current_variable(self): + return pybamm.Variable("Total current density") + + def get_fundamental_variables(self): + # Current is a variable + i_cell = self._get_current_variable() + variables = self._get_current_variables(i_cell) + + # Add discharge capacity variable + variables.update(super().get_fundamental_variables()) + + # Add switches + # These are not implemented yet but can be used later with the Experiment class + # to simulate different external circuit conditions sequentially within a + # single model (for example Constant Current - Constant Voltage) + # for i in range(self.external_circuit_class.num_switches): + # s = pybamm.Parameter("Switch {}".format(i + 1)) + # variables["Switch {}".format(i + 1)] = s + + return variables + + def set_initial_conditions(self, variables): + super().set_initial_conditions(variables) + # Initial condition as a guess for consistent initial conditions + i_cell = variables["Total current density"] + self.initial_conditions[i_cell] = self.param.current_with_time + + def set_algebraic(self, variables): + # External circuit submodels are always equations on the current + # The external circuit function should fix either the current, or the voltage, + # or a combination (e.g. I*V for power control) + i_cell = variables["Total current density"] + self.algebraic[i_cell] = self.external_circuit_class(variables) + + +class VoltageFunctionControl(FunctionControl): + """ + External circuit with voltage control, implemented as an extra algebraic equation. + """ + + def __init__(self, param): + super().__init__(param, ConstantVoltage()) + + +class ConstantVoltage: + num_switches = 0 + + def __call__(self, variables): + V = variables["Terminal voltage [V]"] + return V - pybamm.FunctionParameter("Voltage function [V]", pybamm.t) + + +class PowerFunctionControl(FunctionControl): + """External circuit with power control. """ + + def __init__(self, param): + super().__init__(param, ConstantPower()) + + +class ConstantPower: + num_switches = 0 + + def __call__(self, variables): + I = variables["Current [A]"] + V = variables["Terminal voltage [V]"] + return I * V - pybamm.FunctionParameter("Power function [W]", pybamm.t) + + +class LeadingOrderFunctionControl(FunctionControl, LeadingOrderBaseModel): + """External circuit with an arbitrary function, at leading order. """ + + def __init__(self, param, external_circuit_class): + super().__init__(param, external_circuit_class) + + def _get_current_variable(self): + return pybamm.Variable("Leading-order total current density") + + +class LeadingOrderVoltageFunctionControl(LeadingOrderFunctionControl): + """ + External circuit with voltage control, implemented as an extra algebraic equation, + at leading order. + """ + + def __init__(self, param): + super().__init__(param, ConstantVoltage()) + + +class LeadingOrderPowerFunctionControl(LeadingOrderFunctionControl): + """External circuit with power control, at leading order. """ + + def __init__(self, param): + super().__init__(param, ConstantPower()) + diff --git a/pybamm/models/submodels/particle/base_particle.py b/pybamm/models/submodels/particle/base_particle.py index c8bf288fc1..bbc3cc4878 100644 --- a/pybamm/models/submodels/particle/base_particle.py +++ b/pybamm/models/submodels/particle/base_particle.py @@ -23,7 +23,7 @@ def __init__(self, param, domain): def _get_standard_concentration_variables(self, c_s, c_s_xav): - c_s_surf = pybamm.surf(c_s, set_domain=True) + c_s_surf = pybamm.surf(c_s) c_s_surf_av = pybamm.x_average(c_s_surf) geo_param = pybamm.geometric_parameters @@ -92,7 +92,7 @@ def set_events(self, variables): tol = 0.01 self.events[ - "Minumum " + self.domain.lower() + " particle surface concentration" + "Minimum " + self.domain.lower() + " particle surface concentration" ] = (pybamm.min(c_s_surf) - tol) self.events[ diff --git a/pybamm/models/submodels/particle/fast/fast_single_particle.py b/pybamm/models/submodels/particle/fast/fast_single_particle.py index 1c1d2ecddf..925229086b 100644 --- a/pybamm/models/submodels/particle/fast/fast_single_particle.py +++ b/pybamm/models/submodels/particle/fast/fast_single_particle.py @@ -33,7 +33,7 @@ def get_fundamental_variables(self): if self.domain == "Negative": c_s_surf_xav = pybamm.standard_variables.c_s_n_surf_xav c_s_xav = pybamm.PrimaryBroadcast(c_s_surf_xav, ["negative particle"]) - c_s = pybamm.PrimaryBroadcast(c_s_xav, ["negative electrode"]) + c_s = pybamm.SecondaryBroadcast(c_s_xav, ["negative electrode"]) N_s = pybamm.FullBroadcast( 0, @@ -48,7 +48,7 @@ def get_fundamental_variables(self): elif self.domain == "Positive": c_s_surf_xav = pybamm.standard_variables.c_s_p_surf_xav c_s_xav = pybamm.PrimaryBroadcast(c_s_surf_xav, ["positive particle"]) - c_s = pybamm.PrimaryBroadcast(c_s_xav, ["positive electrode"]) + c_s = pybamm.SecondaryBroadcast(c_s_xav, ["positive electrode"]) N_s = pybamm.FullBroadcast( 0, diff --git a/pybamm/models/submodels/particle/fickian/base_fickian_particle.py b/pybamm/models/submodels/particle/fickian/base_fickian_particle.py index 216164a633..368927c5c1 100644 --- a/pybamm/models/submodels/particle/fickian/base_fickian_particle.py +++ b/pybamm/models/submodels/particle/fickian/base_fickian_particle.py @@ -35,16 +35,6 @@ def _flux_law(self, c, T): def _unpack(self, variables): raise NotImplementedError - def set_rhs(self, variables): - - c, N, _ = self._unpack(variables) - - if self.domain == "Negative": - self.rhs = {c: -(1 / self.param.C_n) * pybamm.div(N)} - - elif self.domain == "Positive": - self.rhs = {c: -(1 / self.param.C_p) * pybamm.div(N)} - def set_boundary_conditions(self, variables): c, _, j = self._unpack(variables) diff --git a/pybamm/models/submodels/particle/fickian/fickian_many_particles.py b/pybamm/models/submodels/particle/fickian/fickian_many_particles.py index 159a0a710f..8b0d9d40cd 100644 --- a/pybamm/models/submodels/particle/fickian/fickian_many_particles.py +++ b/pybamm/models/submodels/particle/fickian/fickian_many_particles.py @@ -46,8 +46,30 @@ def get_coupled_variables(self, variables): N_s = self._flux_law(c_s, T_k) variables.update(self._get_standard_flux_variables(N_s, N_s)) + + if self.domain == "Negative": + x = pybamm.standard_spatial_vars.x_n + R = pybamm.FunctionParameter("Negative particle distribution in x", x) + variables.update({"Negative particle distribution in x": R}) + + elif self.domain == "Positive": + x = pybamm.standard_spatial_vars.x_p + R = pybamm.FunctionParameter("Positive particle distribution in x", x) + variables.update({"Positive particle distribution in x": R}) return variables + def set_rhs(self, variables): + + c, N, _ = self._unpack(variables) + + if self.domain == "Negative": + R = variables["Negative particle distribution in x"] + self.rhs = {c: -(1 / (R ** 2 * self.param.C_n)) * pybamm.div(N)} + + elif self.domain == "Positive": + R = variables["Positive particle distribution in x"] + self.rhs = {c: -(1 / (R ** 2 * self.param.C_p)) * pybamm.div(N)} + def _unpack(self, variables): c_s = variables[self.domain + " particle concentration"] N_s = variables[self.domain + " particle flux"] diff --git a/pybamm/models/submodels/particle/fickian/fickian_single_particle.py b/pybamm/models/submodels/particle/fickian/fickian_single_particle.py index db3486a9da..9715e2e1c4 100644 --- a/pybamm/models/submodels/particle/fickian/fickian_single_particle.py +++ b/pybamm/models/submodels/particle/fickian/fickian_single_particle.py @@ -27,11 +27,11 @@ def __init__(self, param, domain): def get_fundamental_variables(self): if self.domain == "Negative": c_s_xav = pybamm.standard_variables.c_s_n_xav - c_s = pybamm.PrimaryBroadcast(c_s_xav, ["negative electrode"]) + c_s = pybamm.SecondaryBroadcast(c_s_xav, ["negative electrode"]) elif self.domain == "Positive": c_s_xav = pybamm.standard_variables.c_s_p_xav - c_s = pybamm.PrimaryBroadcast(c_s_xav, ["positive electrode"]) + c_s = pybamm.SecondaryBroadcast(c_s_xav, ["positive electrode"]) variables = self._get_standard_concentration_variables(c_s, c_s_xav) @@ -46,12 +46,22 @@ def get_coupled_variables(self, variables): [self.domain.lower() + " particle"], ) N_s_xav = self._flux_law(c_s_xav, T_k_xav) - N_s = pybamm.PrimaryBroadcast(N_s_xav, [self._domain.lower() + " electrode"]) + N_s = pybamm.SecondaryBroadcast(N_s_xav, [self._domain.lower() + " electrode"]) variables.update(self._get_standard_flux_variables(N_s, N_s_xav)) return variables + def set_rhs(self, variables): + + c, N, _ = self._unpack(variables) + + if self.domain == "Negative": + self.rhs = {c: -(1 / self.param.C_n) * pybamm.div(N)} + + elif self.domain == "Positive": + self.rhs = {c: -(1 / self.param.C_p) * pybamm.div(N)} + def _unpack(self, variables): c_s_xav = variables[ "X-averaged " + self.domain.lower() + " particle concentration" diff --git a/pybamm/models/submodels/porosity/base_porosity.py b/pybamm/models/submodels/porosity/base_porosity.py index 72012656ab..72a5082fce 100644 --- a/pybamm/models/submodels/porosity/base_porosity.py +++ b/pybamm/models/submodels/porosity/base_porosity.py @@ -100,4 +100,3 @@ def set_events(self, variables): self.events["Max negative electrode porosity cut-off"] = pybamm.max(eps_n) - 1 self.events["Zero positive electrode porosity cut-off"] = pybamm.min(eps_p) self.events["Max positive electrode porosity cut-off"] = pybamm.max(eps_p) - 1 - diff --git a/pybamm/models/submodels/thermal/isothermal/isothermal.py b/pybamm/models/submodels/thermal/isothermal/isothermal.py index 3c12d58d5e..f4d6a7f446 100644 --- a/pybamm/models/submodels/thermal/isothermal/isothermal.py +++ b/pybamm/models/submodels/thermal/isothermal/isothermal.py @@ -23,7 +23,7 @@ def __init__(self, param): def get_fundamental_variables(self): - T_x_av = pybamm.PrimaryBroadcast(0, "current collector") + T_x_av = pybamm.PrimaryBroadcast(self.param.T_init, "current collector") T_n = pybamm.PrimaryBroadcast(T_x_av, "negative electrode") T_s = pybamm.PrimaryBroadcast(T_x_av, "separator") T_p = pybamm.PrimaryBroadcast(T_x_av, "positive electrode") diff --git a/pybamm/models/submodels/thermal/x_lumped/x_lumped_2D_current_collectors.py b/pybamm/models/submodels/thermal/x_lumped/x_lumped_2D_current_collectors.py index b9d301cc14..811e20c9e1 100644 --- a/pybamm/models/submodels/thermal/x_lumped/x_lumped_2D_current_collectors.py +++ b/pybamm/models/submodels/thermal/x_lumped/x_lumped_2D_current_collectors.py @@ -49,8 +49,7 @@ def _current_collector_heating(self, variables): """Returns the heat source terms in the 2D current collector""" phi_s_cn = variables["Negative current collector potential"] phi_s_cp = variables["Positive current collector potential"] - # Note: grad not implemented in 2D weak form, but can compute grad squared - # directly + Q_s_cn = self.param.sigma_cn_prime * pybamm.grad_squared(phi_s_cn) Q_s_cp = self.param.sigma_cp_prime * pybamm.grad_squared(phi_s_cp) return Q_s_cn, Q_s_cp diff --git a/pybamm/models/submodels/thermal/xyz_lumped/xyz_lumped_2D_current_collector.py b/pybamm/models/submodels/thermal/xyz_lumped/xyz_lumped_2D_current_collector.py index 60fe551a74..da1c3319bf 100644 --- a/pybamm/models/submodels/thermal/xyz_lumped/xyz_lumped_2D_current_collector.py +++ b/pybamm/models/submodels/thermal/xyz_lumped/xyz_lumped_2D_current_collector.py @@ -25,8 +25,7 @@ def _current_collector_heating(self, variables): """Returns the heat source terms in the 2D current collector""" phi_s_cn = variables["Negative current collector potential"] phi_s_cp = variables["Positive current collector potential"] - # Note: grad not implemented in 2D weak form, but can compute grad squared - # directly + Q_s_cn = self.param.sigma_cn_prime * pybamm.grad_squared(phi_s_cn) Q_s_cp = self.param.sigma_cp_prime * pybamm.grad_squared(phi_s_cp) return Q_s_cn, Q_s_cp diff --git a/pybamm/parameters/electrical_parameters.py b/pybamm/parameters/electrical_parameters.py index 8db66dbe29..7e9d1ecfac 100644 --- a/pybamm/parameters/electrical_parameters.py +++ b/pybamm/parameters/electrical_parameters.py @@ -25,7 +25,7 @@ # the user may provide the typical timescale as a parameter. timescale = pybamm.Parameter("Typical timescale [s]") dimensional_current_with_time = pybamm.FunctionParameter( - "Current function", pybamm.t * timescale + "Current function [A]", pybamm.t * timescale ) dimensional_current_density_with_time = dimensional_current_with_time / ( n_electrodes_parallel * pybamm.geometric_parameters.A_cc diff --git a/pybamm/parameters/parameter_values.py b/pybamm/parameters/parameter_values.py index 8566ece726..57a7f4fb35 100644 --- a/pybamm/parameters/parameter_values.py +++ b/pybamm/parameters/parameter_values.py @@ -7,10 +7,13 @@ import numbers -class ParameterValues(dict): +class ParameterValues: """ The parameter values for a simulation. + Note that this class does not inherit directly from the python dictionary class as + this causes issues with saving and loading simulations. + Parameters ---------- values : dict or string @@ -49,6 +52,7 @@ class ParameterValues(dict): """ def __init__(self, values=None, chemistry=None): + self._dict_items = pybamm.FuzzyDict() # Must provide either values or chemistry, not both (nor neither) if values is not None and chemistry is not None: raise ValueError( @@ -66,14 +70,37 @@ def __init__(self, values=None, chemistry=None): self.update_from_chemistry(chemistry) # Then update with values dictionary or file if values is not None: + # If base_parameters is a filename, load from that filename if isinstance(values, str): values = self.read_parameters_csv(values) - # If base_parameters is a filename, load from that filename - self.update(values) + # Don't check parameter already exists when first creating it + self.update(values, check_already_exists=False) # Initialise empty _processed_symbols dict (for caching) self._processed_symbols = {} + def __getitem__(self, key): + return self._dict_items[key] + + def __setitem__(self, key, value): + "Call the update functionality when doing a setitem" + self.update({key: value}) + + def __delitem__(self, key): + del self._dict_items[key] + + def keys(self): + "Get the keys of the dictionary" + return self._dict_items.keys() + + def values(self): + "Get the values of the dictionary" + return self._dict_items.values() + + def items(self): + "Get the items of the dictionary" + return self._dict_items.items() + def update_from_chemistry(self, chemistry): """ Load standard set of components from a 'chemistry' dictionary @@ -105,7 +132,12 @@ def update_from_chemistry(self, chemistry): os.path.join(component_path, "parameters.csv") ) # Update parameters, making sure to check any conflicts - self.update(component_params, check_conflict=True, path=component_path) + self.update( + component_params, + check_conflict=True, + check_already_exists=False, + path=component_path, + ) def read_parameters_csv(self, filename): """Reads parameters from csv file into dict. @@ -126,13 +158,27 @@ def read_parameters_csv(self, filename): df.dropna(how="all", inplace=True) return {k: v for (k, v) in zip(df["Name [units]"], df["Value"])} - def __setitem__(self, key, value): - "Call the update functionality when doing a setitem" - self.update({key: value}) + def update(self, values, check_conflict=False, check_already_exists=True, path=""): + """ + Update parameter dictionary, while also performing some basic checks. - def update(self, values, check_conflict=False, path=""): - # check parameter values - values = self.check_and_update_parameter_values(values) + 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 + """ # update for name, value in values.items(): # check for conflicts @@ -146,92 +192,105 @@ def update(self, values, check_conflict=False, path=""): 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 have a default + value. ({}). If you are sure you want to update this parameter, + use param.update({{name: value}}, check_already_exists=False) + """.format( + name, err.args[0] + ) + ) # 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") + # 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:] + ".py") + ) + 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(), "input", "drive_cycles" ) - # 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(), "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:] - data = pd.read_csv( - filename, comment="#", skip_blank_lines=True - ).to_numpy() - # Save name and data - super().__setitem__(name, (function_name, data)) - # Special case (hacky) for zero current - elif value == "[zero]": - super().__setitem__(name, 0) - # Anything else should be a converted to a float + filename = os.path.join(data_path, value[14:] + ".csv") + function_name = value[14:] else: - super().__setitem__(name, float(value)) + filename = os.path.join(path, value[6:] + ".csv") + function_name = value[6:] + data = pd.read_csv( + filename, comment="#", skip_blank_lines=True + ).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: - super().__setitem__(name, value) + self._dict_items[name] = float(value) + values[name] = float(value) + else: + self._dict_items[name] = value + # check parameter values + self.check_and_update_parameter_values(values) # reset processed symbols self._processed_symbols = {} def check_and_update_parameter_values(self, values): - # Make sure "C-rate" and current are both non-zero - if "C-rate" in values and values["C-rate"] == 0: + # Make sure typical current is non-zero + if "Typical current [A]" in values and values["Typical current [A]"] == 0: raise ValueError( """ - "C-rate" cannot be zero. A possible alternative is to set - "Current function" to `0` instead. + "Typical current [A]" cannot be zero. A possible alternative is to set + "Current function [A]" to `0` instead. """ ) - if "Typical current [A]" in values and values["Typical current [A]"] == 0: + if "C-rate" in values and "Current function [A]" in values: raise ValueError( """ - "Typical current [A]" cannot be zero. A possible alternative is to set - "Current function" to `0` instead. + Cannot provide both "C-rate" and "Current function [A]" simultaneously """ ) # If the capacity of the cell has been provided, make sure "C-rate" and current # match with the stated capacity - if "Cell capacity [A.h]" in values or "Cell capacity [A.h]" in self: + if "Cell capacity [A.h]" in values or "Cell capacity [A.h]" in self._dict_items: # Capacity from values takes precedence if "Cell capacity [A.h]" in values: capacity = values["Cell capacity [A.h]"] else: - capacity = self["Cell capacity [A.h]"] + capacity = self._dict_items["Cell capacity [A.h]"] # Make sure they match if both provided - if "C-rate" in values and "Typical current [A]" in values: - if values["C-rate"] * capacity != values["Typical current [A]"]: - raise ValueError( - """ - "C-rate" ({}C) and Typical current ({} A) provided do not match - given capacity ({} Ah). These can be updated individually - instead. - """.format( - values["C-rate"], values["Typical current [A]"], capacity - ) - ) # Update the other if only one provided - elif "C-rate" in values: - values["Typical current [A]"] = float(values["C-rate"]) * capacity - elif "Typical current [A]" in values: - values["C-rate"] = float(values["Typical current [A]"]) / capacity - - # Update the current function if it is constant - self_and_values = {**self, **values} - if "Current function" in self_and_values and ( - self_and_values["Current function"] == "[constant]" - or isinstance(self_and_values["Current function"], numbers.Number) - ): - values["Current function"] = {**self, **values}["Typical current [A]"] + if "C-rate" in values: + # Can't provide C-rate as a function + if callable(values["C-rate"]): + value = CrateToCurrent(values["C-rate"], capacity) + elif isinstance(values["C-rate"], tuple): + data = values["C-rate"][1] + data[:, 1] = data[:, 1] * capacity + value = (values["C-rate"][0] + "_to_Crate", data) + else: + value = values["C-rate"] * capacity + self._dict_items["Current function [A]"] = value + elif "Current function [A]" in values: + if callable(values["Current function [A]"]): + value = CurrentToCrate(values["Current function [A]"], capacity) + elif isinstance(values["Current function [A]"], tuple): + data = values["Current function [A]"][1] + data[:, 1] = data[:, 1] / capacity + value = (values["Current function [A]"][0] + "_to_current", data) + else: + value = values["Current function [A]"] / capacity + self._dict_items["C-rate"] = value return values @@ -281,13 +340,13 @@ def process_model(self, unprocessed_model, processing="process", inplace=True): elif processing == "update": processing_function = self.update_scalars - for variable, equation in unprocessed_model.rhs.items(): + for variable, equation in model.rhs.items(): pybamm.logger.debug( "{} parameters for {!r} (rhs)".format(processing.capitalize(), variable) ) model.rhs[variable] = processing_function(equation) - for variable, equation in unprocessed_model.algebraic.items(): + for variable, equation in model.algebraic.items(): pybamm.logger.debug( "{} parameters for {!r} (algebraic)".format( processing.capitalize(), variable @@ -295,7 +354,7 @@ def process_model(self, unprocessed_model, processing="process", inplace=True): ) model.algebraic[variable] = processing_function(equation) - for variable, equation in unprocessed_model.initial_conditions.items(): + for variable, equation in model.initial_conditions.items(): pybamm.logger.debug( "{} parameters for {!r} (initial conditions)".format( processing.capitalize(), variable @@ -308,10 +367,11 @@ def process_model(self, unprocessed_model, processing="process", inplace=True): # small number of variables, e.g. {"negative tab": neg. tab bc, # "positive tab": pos. tab bc "no tab": no tab bc}. new_boundary_conditions = {} - for variable, bcs in unprocessed_model.boundary_conditions.items(): + sides = ["left", "right", "negative tab", "positive tab", "no tab"] + for variable, bcs in model.boundary_conditions.items(): processed_variable = processing_function(variable) new_boundary_conditions[processed_variable] = {} - for side in ["left", "right", "negative tab", "positive tab", "no tab"]: + for side in sides: try: bc, typ = bcs[side] pybamm.logger.debug( @@ -321,19 +381,25 @@ def process_model(self, unprocessed_model, processing="process", inplace=True): ) processed_bc = (processing_function(bc), typ) new_boundary_conditions[processed_variable][side] = processed_bc - except KeyError: - pass + except KeyError as err: + # don't raise error if the key error comes from the side not being + # found + if err.args[0] in side: + pass + # do raise error otherwise (e.g. can't process symbol) + else: + raise KeyError(err) model.boundary_conditions = new_boundary_conditions - for variable, equation in unprocessed_model.variables.items(): + for variable, equation in model.variables.items(): pybamm.logger.debug( "{} parameters for {!r} (variables)".format( processing.capitalize(), variable ) ) model.variables[variable] = processing_function(equation) - for event, equation in unprocessed_model.events.items(): + for event, equation in model.events.items(): pybamm.logger.debug( "{} parameters for event '{}''".format(processing.capitalize(), event) ) @@ -419,8 +485,13 @@ def _process_symbol(self, 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) + 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] @@ -434,8 +505,11 @@ def _process_symbol(self, symbol): 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 - function = pybamm.Scalar(function_name, name=symbol.name) + # 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) else: # otherwise evaluate the function to create a new PyBaMM object function = function_name(*new_children) @@ -504,13 +578,10 @@ def update_scalars(self, symbol): for x in symbol.pre_order(): if isinstance(x, pybamm.Scalar): # update any Scalar nodes if their name is in the parameter dict - try: - x.value = self[x.name] + if x.name in self._dict_items.keys(): + x.value = self._dict_items[x.name] # update id x.set_id() - except KeyError: - # KeyError -> name not in parameter dict, don't update - continue return symbol @@ -533,3 +604,25 @@ def evaluate(self, symbol): return processed_symbol.evaluate() else: raise ValueError("symbol must evaluate to a constant scalar") + + +class CurrentToCrate: + "Convert a current function to a C-rate function" + + def __init__(self, function, capacity): + self.function = function + self.capacity = capacity + + def __call__(self, t): + return self.function(t) / self.capacity + + +class CrateToCurrent: + "Convert a C-rate function to a current function" + + def __init__(self, function, capacity): + self.function = function + self.capacity = capacity + + def __call__(self, t): + return self.function(t) * self.capacity diff --git a/pybamm/parameters/print_parameters.py b/pybamm/parameters/print_parameters.py index 775df80043..381c3f369d 100644 --- a/pybamm/parameters/print_parameters.py +++ b/pybamm/parameters/print_parameters.py @@ -61,7 +61,7 @@ def print_parameters(parameters, parameter_values, output_file=None): # Calculate parameters for each C-rate for Crate in [1, 10]: # Update Crate - parameter_values.update({"C-rate": Crate}) + parameter_values.update({"C-rate": Crate}, check_already_exists=False) for name, symbol in parameters.items(): if not callable(symbol): proc_symbol = parameter_values.process_symbol(symbol) diff --git a/pybamm/parameters/standard_parameters_lead_acid.py b/pybamm/parameters/standard_parameters_lead_acid.py index 3f058f4e2b..d0d7bab989 100644 --- a/pybamm/parameters/standard_parameters_lead_acid.py +++ b/pybamm/parameters/standard_parameters_lead_acid.py @@ -277,6 +277,8 @@ def U_p_dimensional(c_e, T): # Electrolyte diffusion timescale tau_diffusion_e = L_x ** 2 / D_e_typ +# Choose discharge timescale +timescale = tau_discharge # -------------------------------------------------------------------------------------- "4. Dimensionless Parameters" @@ -482,14 +484,15 @@ def U_p(c_e_p, T): # -------------------------------------------------------------------------------------- -"6. Input current" +# 6. Input current and voltage + dimensional_current_with_time = pybamm.FunctionParameter( - "Current function", pybamm.t * tau_discharge + "Current function [A]", pybamm.t * timescale ) dimensional_current_density_with_time = dimensional_current_with_time / ( n_electrodes_parallel * pybamm.geometric_parameters.A_cc ) - current_with_time = ( dimensional_current_with_time / I_typ * pybamm.Function(np.sign, I_typ) ) + diff --git a/pybamm/parameters/standard_parameters_lithium_ion.py b/pybamm/parameters/standard_parameters_lithium_ion.py index ebce5d285a..e03a4e1f48 100644 --- a/pybamm/parameters/standard_parameters_lithium_ion.py +++ b/pybamm/parameters/standard_parameters_lithium_ion.py @@ -240,6 +240,9 @@ def U_p_dimensional(sto, T): # Thermal diffusion timescale tau_th_yz = pybamm.thermal_parameters.tau_th_yz +# Choose discharge timescale +timescale = tau_discharge + # -------------------------------------------------------------------------------------- "4. Dimensionless Parameters" # Timescale ratios @@ -442,14 +445,15 @@ def dUdT_p(c_s_p): # -------------------------------------------------------------------------------------- -"6. Input current" +# 6. Input current and voltage + dimensional_current_with_time = pybamm.FunctionParameter( - "Current function", pybamm.t * tau_discharge + "Current function [A]", pybamm.t * timescale ) dimensional_current_density_with_time = dimensional_current_with_time / ( n_electrodes_parallel * pybamm.geometric_parameters.A_cc ) - current_with_time = ( dimensional_current_with_time / I_typ * pybamm.Function(np.sign, I_typ) ) + diff --git a/pybamm/processed_variable.py b/pybamm/processed_variable.py index a4db4a6462..96f97f18eb 100644 --- a/pybamm/processed_variable.py +++ b/pybamm/processed_variable.py @@ -7,43 +7,6 @@ import scipy.interpolate as interp -def post_process_variables(variables, t_sol, u_sol, mesh=None, interp_kind="linear"): - """ - Post-process all variables in a model - - Parameters - ---------- - variables : dict - Dictionary of variables - t_sol : array_like, size (m,) - The time vector returned by the solver - u_sol : array_like, size (m, k) - The solution vector returned by the solver. Can include solution values that - other than those that get read by base_variable.evaluate() (i.e. k>=n) - mesh : :class:`pybamm.Mesh` - The mesh used to solve, used here to calculate the reference x values for - interpolation - interp_kind : str - The method to use for interpolation - - Returns - ------- - dict - Dictionary of processed variables - """ - processed_variables = {} - known_evals = {t: {} for t in t_sol} - for var, eqn in variables.items(): - pybamm.logger.debug("Post-processing {}".format(var)) - processed_variables[var] = ProcessedVariable( - eqn, t_sol, u_sol, mesh, interp_kind, known_evals - ) - - for t in known_evals: - known_evals[t].update(processed_variables[var].known_evals[t]) - return processed_variables - - class ProcessedVariable(object): """ An object that can be evaluated at arbitrary (scalars or vectors) t and x, and @@ -56,72 +19,72 @@ class ProcessedVariable(object): variable. Note that this can be any kind of node in the expression tree, not just a :class:`pybamm.Variable`. When evaluated, returns an array of size (m,n) - t_sol : array_like, size (m,) - The time vector returned by the solver - u_sol : array_like, size (m, k) - The solution vector returned by the solver. Can include solution values that - other than those that get read by base_variable.evaluate() (i.e. k>=n) - mesh : :class:`pybamm.Mesh` - The mesh used to solve, used here to calculate the reference x values for - interpolation + solution : :class:`pybamm.Solution` + The solution object to be used to create the processed variables interp_kind : str The method to use for interpolation + known_evals : dict + Dictionary of known evaluations, to be used to speed up finding the solution """ - def __init__( - self, - base_variable, - t_sol, - u_sol, - mesh=None, - interp_kind="linear", - known_evals=None, - ): + def __init__(self, base_variable, solution, known_evals=None): self.base_variable = base_variable - self.t_sol = t_sol - self.u_sol = u_sol - self.mesh = mesh - self.interp_kind = interp_kind + self.t_sol = solution.t + self.u_sol = solution.y + self.mesh = base_variable.mesh + self.inputs = solution.inputs self.domain = base_variable.domain self.auxiliary_domains = base_variable.auxiliary_domains self.known_evals = known_evals if self.known_evals: - self.base_eval, self.known_evals[t_sol[0]] = base_variable.evaluate( - t_sol[0], u_sol[:, 0], self.known_evals[t_sol[0]] + self.base_eval, self.known_evals[solution.t[0]] = base_variable.evaluate( + solution.t[0], + solution.y[:, 0], + {name: inp[0] for name, inp in solution.inputs.items()}, + known_evals=self.known_evals[solution.t[0]], ) else: - self.base_eval = base_variable.evaluate(t_sol[0], u_sol[:, 0]) + self.base_eval = base_variable.evaluate( + solution.t[0], + solution.y[:, 0], + {name: inp[0] for name, inp in solution.inputs.items()}, + ) # handle 2D (in space) finite element variables differently if ( - mesh + self.mesh and "current collector" in self.domain - and isinstance(self.mesh[self.domain[0]][0], pybamm.ScikitSubMesh2D) + and isinstance(self.mesh[0], pybamm.ScikitSubMesh2D) ): - if len(self.t_sol) == 1: + if len(solution.t) == 1: # space only (steady solution) self.initialise_2Dspace_scikit_fem() else: self.initialise_3D_scikit_fem() # check variable shape - elif ( - isinstance(self.base_eval, numbers.Number) - or len(self.base_eval.shape) == 0 - or self.base_eval.shape[0] == 1 - ): - self.initialise_1D() else: - n = self.mesh.combine_submeshes(*self.domain)[0].npts - base_shape = self.base_eval.shape[0] - if base_shape in [n, n + 1]: - self.initialise_2D() + if len(solution.t) == 1: + raise pybamm.SolverError( + """ + Solution time vector must have length > 1. Check whether simulation + terminated too early. + """ + ) + elif ( + isinstance(self.base_eval, numbers.Number) + or len(self.base_eval.shape) == 0 + or self.base_eval.shape[0] == 1 + ): + self.initialise_1D() else: - self.initialise_3D() - - # Remove base_variable attribute to allow pickling - del self.base_variable + n = self.mesh[0].npts + base_shape = self.base_eval.shape[0] + if base_shape in [n, n + 1]: + self.initialise_2D() + else: + self.initialise_3D() def initialise_1D(self): # initialise empty array of the correct size @@ -129,20 +92,18 @@ def initialise_1D(self): # Evaluate the base_variable index-by-index for idx in range(len(self.t_sol)): t = self.t_sol[idx] + u = self.u_sol[:, idx] + inputs = {name: inp[idx] for name, inp in self.inputs.items()} if self.known_evals: entries[idx], self.known_evals[t] = self.base_variable.evaluate( - t, self.u_sol[:, idx], self.known_evals[t] + t, u, inputs, known_evals=self.known_evals[t] ) else: - entries[idx] = self.base_variable.evaluate(t, self.u_sol[:, idx]) + entries[idx] = self.base_variable.evaluate(t, u, inputs) # No discretisation provided, or variable has no domain (function of t only) self._interpolation_function = interp.interp1d( - self.t_sol, - entries, - kind=self.interp_kind, - fill_value=np.nan, - bounds_error=False, + self.t_sol, entries, kind="linear", fill_value=np.nan, bounds_error=False ) self.entries = entries @@ -156,18 +117,19 @@ def initialise_2D(self): for idx in range(len(self.t_sol)): t = self.t_sol[idx] u = self.u_sol[:, idx] + inputs = {name: inp[idx] for name, inp in self.inputs.items()} if self.known_evals: eval_and_known_evals = self.base_variable.evaluate( - t, u, self.known_evals[t] + t, u, inputs, known_evals=self.known_evals[t] ) entries[:, idx] = eval_and_known_evals[0][:, 0] self.known_evals[t] = eval_and_known_evals[1] else: - entries[:, idx] = self.base_variable.evaluate(t, u)[:, 0] + entries[:, idx] = self.base_variable.evaluate(t, u, inputs)[:, 0] # Process the discretisation to get x values - nodes = self.mesh.combine_submeshes(*self.domain)[0].nodes - edges = self.mesh.combine_submeshes(*self.domain)[0].edges + nodes = self.mesh[0].nodes + edges = self.mesh[0].edges if entries.shape[0] == len(nodes): space = nodes elif entries.shape[0] == len(edges): @@ -179,7 +141,9 @@ def initialise_2D(self): space = np.concatenate([extrap_space_left, space, extrap_space_right]) extrap_entries_left = 2 * entries[0] - entries[1] extrap_entries_right = 2 * entries[-1] - entries[-2] - entries = np.vstack([extrap_entries_left, entries, extrap_entries_right]) + entries_for_interp = np.vstack( + [extrap_entries_left, entries, extrap_entries_right] + ) # assign attributes for reference (either x_sol or r_sol) self.entries = entries @@ -205,74 +169,42 @@ def initialise_2D(self): # note that the order of 't' and 'space' is the reverse of what you'd expect self._interpolation_function = interp.interp2d( - self.t_sol, space, entries, kind=self.interp_kind, fill_value=np.nan + self.t_sol, space, entries_for_interp, kind="linear", fill_value=np.nan ) def initialise_3D(self): """ Initialise a 3D object that depends on x and r, or x and z. - Needs to be generalised to deal with other domains. - - Notes - ----- - There is different behaviour between a variable on an electrode domain - broadcast to a particle (such as temperature) and a variable on a particle - domain broadcast to an electrode (such as particle concentration). We deal with - this by reshaping the former with the Fortran order ("F") and the latter with - the C order ("C"). These are transposes of each other, so this approach simply - avoids having to transpose later. """ - # Dealt with weird particle/electrode case - if self.domain in [ - ["negative electrode"], - ["positive electrode"], - ] and self.auxiliary_domains["secondary"] in [ - ["negative particle"], - ["positive particle"], + first_dim_nodes = self.mesh[0].nodes + first_dim_edges = self.mesh[0].edges + second_dim_pts = self.base_variable.secondary_mesh[0].nodes + if self.base_eval.size // len(second_dim_pts) == len(first_dim_nodes): + first_dim_pts = first_dim_nodes + elif self.base_eval.size // len(second_dim_pts) == len(first_dim_edges): + first_dim_pts = first_dim_edges + + # Process r-x or x-z + if self.domain[0] in [ + "negative particle", + "positive particle", + ] and self.auxiliary_domains["secondary"][0] in [ + "negative electrode", + "positive electrode", ]: - # Switch domain and auxiliary domains and set order to Fortran order ("F") - dom = self.domain - self.domain = self.auxiliary_domains["secondary"] - self.auxiliary_domains["secondary"] = dom - order = "F" - else: - # Set order to C order ("C") - order = "C" - - # Process x-r or x-z - if self.domain == ["negative particle"] and self.auxiliary_domains[ - "secondary" - ] == ["negative electrode"]: - x_sol = self.mesh["negative electrode"][0].nodes - r_nodes = self.mesh["negative particle"][0].nodes - r_edges = self.mesh["negative particle"][0].edges - set_up_r = True - elif self.domain == ["positive particle"] and self.auxiliary_domains[ - "secondary" - ] == ["positive electrode"]: - x_sol = self.mesh["positive electrode"][0].nodes - r_nodes = self.mesh["positive particle"][0].nodes - r_edges = self.mesh["positive particle"][0].edges - set_up_r = True + self.first_dimension = "r" + self.second_dimension = "x" + self.r_sol = first_dim_pts + self.x_sol = second_dim_pts elif self.domain[0] in [ "negative electrode", "separator", "positive electrode", ] and self.auxiliary_domains["secondary"] == ["current collector"]: - x_nodes = self.mesh.combine_submeshes(*self.domain)[0].nodes - x_edges = self.mesh.combine_submeshes(*self.domain)[0].edges - z_sol = self.mesh["current collector"][0].nodes - r_sol = None self.first_dimension = "x" self.second_dimension = "z" - - if self.base_eval.size // len(z_sol) == len(x_nodes): - x_sol = x_nodes - elif self.base_eval.size // len(z_sol) == len(x_edges): - x_sol = x_edges - first_dim_nodes = x_sol - second_dim_nodes = z_sol - set_up_r = False + self.x_sol = first_dim_pts + self.z_sol = second_dim_pts else: raise pybamm.DomainError( """ Cannot process 3D object with domain '{}' @@ -280,65 +212,57 @@ def initialise_3D(self): self.domain, self.auxiliary_domains ) ) - if set_up_r: - z_sol = None - self.first_dimension = "x" - self.second_dimension = "r" - if self.base_eval.size // len(x_sol) == len(r_nodes): - r_sol = r_nodes - elif self.base_eval.size // len(x_sol) == len(r_edges): - r_sol = r_edges - first_dim_nodes = x_sol - second_dim_nodes = r_sol - - first_dim_size = len(first_dim_nodes) - second_dim_size = len(second_dim_nodes) + + first_dim_size = len(first_dim_pts) + second_dim_size = len(second_dim_pts) entries = np.empty((first_dim_size, second_dim_size, len(self.t_sol))) # Evaluate the base_variable index-by-index for idx in range(len(self.t_sol)): t = self.t_sol[idx] u = self.u_sol[:, idx] + inputs = {name: inp[idx] for name, inp in self.inputs.items()} if self.known_evals: eval_and_known_evals = self.base_variable.evaluate( - t, u, self.known_evals[t] + t, u, inputs, known_evals=self.known_evals[t] ) entries[:, :, idx] = np.reshape( eval_and_known_evals[0], [first_dim_size, second_dim_size], - order=order, + order="F", ) self.known_evals[t] = eval_and_known_evals[1] else: entries[:, :, idx] = np.reshape( - self.base_variable.evaluate(t, u), + self.base_variable.evaluate(t, u, inputs), [first_dim_size, second_dim_size], - order=order, + order="F", ) # assign attributes for reference self.entries = entries self.dimensions = 3 - self.x_sol = x_sol - self.r_sol = r_sol - self.z_sol = z_sol # set up interpolation self._interpolation_function = interp.RegularGridInterpolator( - (first_dim_nodes, second_dim_nodes, self.t_sol), + (first_dim_pts, second_dim_pts, self.t_sol), entries, - method=self.interp_kind, + method="linear", fill_value=np.nan, ) def initialise_2Dspace_scikit_fem(self): - y_sol = self.mesh[self.domain[0]][0].edges["y"] + y_sol = self.mesh[0].edges["y"] len_y = len(y_sol) - z_sol = self.mesh[self.domain[0]][0].edges["z"] + z_sol = self.mesh[0].edges["z"] len_z = len(z_sol) # Evaluate the base_variable - entries = np.reshape(self.base_variable.evaluate(0, self.u_sol), [len_y, len_z]) + inputs = {name: inp[0] for name, inp in self.inputs.items()} + + entries = np.reshape( + self.base_variable.evaluate(0, self.u_sol, inputs), [len_y, len_z] + ) # assign attributes for reference self.entries = entries @@ -350,13 +274,13 @@ def initialise_2Dspace_scikit_fem(self): # set up interpolation self._interpolation_function = interp.interp2d( - y_sol, z_sol, entries, kind=self.interp_kind, fill_value=np.nan + y_sol, z_sol, entries, kind="linear", fill_value=np.nan ) def initialise_3D_scikit_fem(self): - y_sol = self.mesh[self.domain[0]][0].edges["y"] + y_sol = self.mesh[0].edges["y"] len_y = len(y_sol) - z_sol = self.mesh[self.domain[0]][0].edges["z"] + z_sol = self.mesh[0].edges["z"] len_z = len(z_sol) entries = np.empty((len_y, len_z, len(self.t_sol))) @@ -364,15 +288,17 @@ def initialise_3D_scikit_fem(self): for idx in range(len(self.t_sol)): t = self.t_sol[idx] u = self.u_sol[:, idx] + inputs = {name: inp[idx] for name, inp in self.inputs.items()} + if self.known_evals: eval_and_known_evals = self.base_variable.evaluate( - t, u, self.known_evals[t] + t, u, inputs, known_evals=self.known_evals[t] ) entries[:, :, idx] = np.reshape(eval_and_known_evals[0], [len_y, len_z]) self.known_evals[t] = eval_and_known_evals[1] else: entries[:, :, idx] = np.reshape( - self.base_variable.evaluate(t, u), [len_y, len_z] + self.base_variable.evaluate(t, u, inputs), [len_y, len_z] ) # assign attributes for reference @@ -385,10 +311,7 @@ def initialise_3D_scikit_fem(self): # set up interpolation self._interpolation_function = interp.RegularGridInterpolator( - (y_sol, z_sol, self.t_sol), - entries, - method=self.interp_kind, - fill_value=np.nan, + (y_sol, z_sol, self.t_sol), entries, method="linear", fill_value=np.nan ) def __call__(self, t=None, x=None, r=None, y=None, z=None, warn=True): @@ -431,6 +354,11 @@ def call_3D(self, t, x, r, y, z): return self._interpolation_function((first_dim, second_dim, t)) + @property + def data(self): + "Same as entries, but different name" + return self.entries + def eval_dimension_name(name, x, r, y, z): if name == "x": diff --git a/pybamm/quick_plot.py b/pybamm/quick_plot.py index 472dc1237f..e320adffd3 100644 --- a/pybamm/quick_plot.py +++ b/pybamm/quick_plot.py @@ -65,33 +65,18 @@ class QuickPlot(object): def __init__( self, - models, - meshes, solutions, output_variables=None, labels=None, colors=None, linestyles=None, ): - # Pre-process models and solutions - if isinstance(models, pybamm.BaseModel): - models = [models] - elif not isinstance(models, list): - raise TypeError("'models' must be 'pybamm.BaseModel' or list") - if isinstance(meshes, pybamm.Mesh): - # If only one mesh is passed but there are multiple models, try to use - # the same mesh for all of them - meshes = [meshes] * len(models) - elif not isinstance(meshes, list): - raise TypeError("'meshes' must be 'pybamm.Mesh' or list") if isinstance(solutions, pybamm.Solution): solutions = [solutions] elif not isinstance(solutions, list): raise TypeError("'solutions' must be 'pybamm.Solution' or list") - if len(models) == len(solutions): - self.num_models = len(models) - else: - raise ValueError("must provide the same number of models and solutions") + + models = [solution.model for solution in solutions] # Set labels self.labels = labels or [model.name for model in models] @@ -158,10 +143,10 @@ def __init__( else: output_variables = models[0].variables - self.set_output_variables(output_variables, solutions, models, meshes) + self.set_output_variables(output_variables, solutions) self.reset_axis() - def set_output_variables(self, output_variables, solutions, models, meshes): + def set_output_variables(self, output_variables, solutions): # Set up output variables self.variables = {} self.spatial_variable = {} @@ -173,19 +158,16 @@ def set_output_variables(self, output_variables, solutions, models, meshes): # Process output variables into a form that can be plotted processed_variables = {} - for i, model in enumerate(models): - variables_to_process = {} + for solution in solutions: + processed_variables[solution] = {} for variable_list in output_variables: # Make sure we always have a list of lists of variables if isinstance(variable_list, str): variable_list = [variable_list] # Add all variables to the list of variables that should be processed - variables_to_process.update( - {var: model.variables[var] for var in variable_list} + processed_variables[solution].update( + {var: solution[var] for var in variable_list} ) - processed_variables[model] = pybamm.post_process_variables( - variables_to_process, solutions[i].t, solutions[i].y, meshes[i] - ) # Prepare dictionary of variables for k, variable_list in enumerate(output_variables): @@ -195,26 +177,30 @@ def set_output_variables(self, output_variables, solutions, models, meshes): # Prepare list of variables key = tuple(variable_list) - self.variables[key] = [None] * len(models) + self.variables[key] = [None] * len(solutions) # process each variable in variable_list for each model - for i, model in enumerate(models): + for i, solution in enumerate(solutions): # self.variables is a dictionary of lists of lists self.variables[key][i] = [ - processed_variables[model][var] for var in variable_list + processed_variables[solution][var] for var in variable_list ] # Make sure variables have the same dimensions and domain - domain = self.variables[key][0][0].domain + first_variable = self.variables[key][0][0] + domain = first_variable.domain for variable in self.variables[key][0]: if variable.domain != domain: raise ValueError("mismatching variable domains") # Set the x variable for any two-dimensional variables - if self.variables[key][0][0].dimensions == 2: - variable_key = self.variables[key][0][0].spatial_var_name - variable_value = meshes[0].combine_submeshes(*domain)[0].edges - self.spatial_variable[key] = (variable_key, variable_value) + if first_variable.dimensions == 2: + spatial_variable_key = first_variable.spatial_var_name + spatial_variable_value = first_variable.mesh[0].edges + self.spatial_variable[key] = ( + spatial_variable_key, + spatial_variable_value, + ) # Don't allow 3D variables elif any(var.dimensions == 3 for var in self.variables[key][0]): @@ -330,7 +316,7 @@ def plot(self, t): spatial_scale = self.spatial_scales["r_p"] else: spatial_scale = self.spatial_scales[spatial_var_name] - self.plots[key][i][j], = ax.plot( + (self.plots[key][i][j],) = ax.plot( spatial_var_value * spatial_scale, variable( t, **{spatial_var_name: spatial_var_value}, warn=False @@ -345,7 +331,7 @@ def plot(self, t): for i, variable_list in enumerate(variable_lists): for j, variable in enumerate(variable_list): full_t = self.ts[i] - self.plots[key][i][j], = ax.plot( + (self.plots[key][i][j],) = ax.plot( full_t * self.time_scale, variable(full_t, warn=False), lw=2, @@ -353,7 +339,7 @@ def plot(self, t): linestyle=linestyles[j], ) y_min, y_max = self.axis[key][2:] - self.time_lines[key], = ax.plot( + (self.time_lines[key],) = ax.plot( [t * self.time_scale, t * self.time_scale], [y_min, y_max], "k--" ) # Set either y label or legend entries diff --git a/pybamm/simulation.py b/pybamm/simulation.py index e9260059eb..d94e47e7c7 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -75,8 +75,6 @@ def __init__( if self.C_rate: self._parameter_values.update({"C-rate": self.C_rate}) - self._made_first_step = False - self.reset(update_model=False) # ignore runtime warnings in notebooks @@ -110,7 +108,6 @@ def reset(self, update_model=True): self._mesh = None self._disc = None self._solution = None - self._made_first_step = False def set_parameters(self): """ @@ -152,7 +149,14 @@ def build(self, check_model=True): self._model, inplace=False, check_model=check_model ) - def solve(self, t_eval=None, solver=None, check_model=True): + def solve( + self, + t_eval=None, + solver=None, + external_variables=None, + inputs=None, + check_model=True, + ): """ A method to solve the model. This method will automatically build and set the model parameters if not already done so. @@ -166,6 +170,13 @@ def solve(self, t_eval=None, solver=None, check_model=True): non-dimensional time of 1. solver : :class:`pybamm.BaseSolver` The solver to use to solve the model. + external_variables : dict + A dictionary of external variables and their corresponding + values at the current time. The variables must correspond to + the variables that would normally be found by solving the + submodels that have been made external. + inputs : dict, optional + Any input parameters to pass to the model when solving check_model : bool, optional If True, model checks are performed after discretisation (see :meth:`pybamm.Discretisation.process_model`). Default is True. @@ -185,9 +196,17 @@ def solve(self, t_eval=None, solver=None, check_model=True): if solver is None: solver = self.solver - self._solution = solver.solve(self.built_model, t_eval) + self.t_eval = t_eval + self._solution = solver.solve( + self.built_model, + t_eval, + external_variables=external_variables, + inputs=inputs, + ) - def step(self, dt, solver=None, external_variables=None, save=True): + def step( + self, dt, solver=None, npts=2, external_variables=None, inputs=None, save=True + ): """ A method to step the model forward one timestep. This method will automatically build and set the model parameters if not already done so. @@ -198,11 +217,16 @@ def step(self, dt, solver=None, external_variables=None, save=True): The timestep over which to step the solution solver : :class:`pybamm.BaseSolver` The solver to use to solve the model. + npts : int, optional + The number of points at which the solution will be returned during + the step dt. default is 2 (returns the solution at t0 and t0 + dt). external_variables : dict A dictionary of external variables and their corresponding values at the current time. The variables must correspond to the variables that would normally be found by solving the submodels that have been made external. + inputs : dict, optional + Any input parameters to pass to the model when solving save : bool Turn on to store the solution of all previous timesteps """ @@ -211,30 +235,25 @@ def step(self, dt, solver=None, external_variables=None, save=True): if solver is None: solver = self.solver - solution = solver.step( - self.built_model, dt, external_variables=external_variables - ) - - if save is False or self._made_first_step is False: - self._solution = solution - elif self._solution.t[-1] == solution.t[-1]: - pass + if save is False: + # Don't pass previous solution + self._solution = solver.step( + None, + self.built_model, + dt, + npts=npts, + external_variables=external_variables, + inputs=inputs, + ) else: - self._update_solution(solution) - - self._made_first_step = True - - def _update_solution(self, solution): - - self._solution.set_up_time += solution.set_up_time - self._solution.solve_time += solution.solve_time - self._solution.t = np.append(self._solution.t, solution.t[-1]) - self._solution.t_event = solution.t_event - self._solution.termination = solution.termination - self._solution.y = np.concatenate( - [self._solution.y, solution.y[:, -1][:, np.newaxis]], axis=1 - ) - self._solution.y_event = solution.y_event + self._solution = solver.step( + self._solution, + self.built_model, + dt, + npts=npts, + external_variables=external_variables, + inputs=inputs, + ) def get_variable_array(self, *variables): """ @@ -285,12 +304,7 @@ def plot(self, quick_plot_vars=None, testing=False): if quick_plot_vars is None: quick_plot_vars = self.quick_plot_vars - plot = pybamm.QuickPlot( - self.built_model, - self._mesh, - self._solution, - output_variables=quick_plot_vars, - ) + plot = pybamm.QuickPlot(self._solution, output_variables=quick_plot_vars) if isnotebook(): import ipywidgets as widgets @@ -462,12 +476,16 @@ def save(self, filename): Set model.convert_to_format = 'casadi' instead. """ ) + # Clear solver problem (not pickle-able, will automatically be recomputed) + if ( + isinstance(self._solver, pybamm.CasadiSolver) + and self._solver.problems != {} + ): + self._solver.problems = {} with open(filename, "wb") as f: pickle.dump(self, f, pickle.HIGHEST_PROTOCOL) def load_sim(filename): """Load a saved simulation""" - with open(filename, "rb") as f: - sim = pickle.load(f) - return sim + return pybamm.load(filename) diff --git a/pybamm/solvers/algebraic_solver.py b/pybamm/solvers/algebraic_solver.py index ff69455ef9..3ea4d1200f 100644 --- a/pybamm/solvers/algebraic_solver.py +++ b/pybamm/solvers/algebraic_solver.py @@ -85,6 +85,7 @@ def jacobian(y): solve_start_time = timer.time() pybamm.logger.info("Calling root finding algorithm") solution = self.root(algebraic, y0_guess, jacobian=jacobian) + solution.model = model # Assign times solution.solve_time = timer.time() - solve_start_time @@ -137,7 +138,7 @@ def root_fun(y0): if sol.success and np.all(sol.fun < self.tol * len(sol.x)): termination = "success" # Return solution object (no events, so pass None to t_event, y_event) - return pybamm.Solution([0], sol.x[:, np.newaxis], None, None, termination) + return pybamm.Solution([0], sol.x[:, np.newaxis], termination=termination) elif not sol.success: raise pybamm.SolverError( "Could not find acceptable solution: {}".format(sol.message) diff --git a/pybamm/solvers/base_solver.py b/pybamm/solvers/base_solver.py index 5ef78b690e..a17ed8b3b9 100644 --- a/pybamm/solvers/base_solver.py +++ b/pybamm/solvers/base_solver.py @@ -1,8 +1,12 @@ # # Base solver class # +import casadi import pybamm +import numbers import numpy as np +from scipy import optimize +from scipy.sparse import issparse class BaseSolver(object): @@ -10,20 +14,42 @@ class BaseSolver(object): Parameters ---------- + method : str, optional + The method to use for integration, specific to each solver rtol : float, optional The relative tolerance for the solver (default is 1e-6). atol : float, optional The absolute tolerance for the solver (default is 1e-6). + root_method : str, optional + The method to use to find initial conditions (default is "lm") + root_tol : float, optional + The tolerance for the initial-condition solver (default is 1e-6). + max_steps: int, optional + The maximum number of steps the solver will take before terminating + (default is 1000). """ - def __init__(self, method=None, rtol=1e-6, atol=1e-6): + def __init__( + self, + method=None, + rtol=1e-6, + atol=1e-6, + root_method="lm", + root_tol=1e-6, + max_steps=1000, + ): self._method = method self._rtol = rtol self._atol = atol - self.name = "Base solver" + self.root_method = root_method + self.root_tol = root_tol + self.max_steps = max_steps + + self.model_step_times = {} - self.y_pad = None - self.y_ext = None + # Defaults, can be overwritten by specific solver + self.name = "Base solver" + self.ode_solver = False @property def method(self): @@ -49,7 +75,295 @@ def atol(self): def atol(self, value): self._atol = value - def solve(self, model, t_eval): + @property + def root_method(self): + return self._root_method + + @root_method.setter + def root_method(self, method): + self._root_method = method + + @property + def root_tol(self): + return self._root_tol + + @root_tol.setter + def root_tol(self, tol): + self._root_tol = tol + + @property + def max_steps(self): + return self._max_steps + + @max_steps.setter + def max_steps(self, max_steps): + self._max_steps = max_steps + + def set_up(self, model, inputs=None): + """Unpack model, perform checks, simplify and calculate jacobian. + + Parameters + ---------- + model : :class:`pybamm.BaseModel` + The model whose solution to calculate. Must have attributes rhs and + initial_conditions + inputs : dict, optional + Any input parameters to pass to the model when solving + + """ + inputs = inputs or {} + y0 = model.concatenated_initial_conditions + + # Check model.algebraic for ode solvers + if self.ode_solver is True and len(model.algebraic) > 0: + raise pybamm.SolverError( + "Cannot use ODE solver '{}' to solve DAE model".format(self.name) + ) + + if ( + isinstance(self, pybamm.CasadiSolver) + and model.convert_to_format != "casadi" + ): + pybamm.logger.warning( + f"Converting {model.name} to CasADi for solving with CasADi solver" + ) + model.convert_to_format = "casadi" + + if model.convert_to_format != "casadi": + simp = pybamm.Simplification() + # Create Jacobian from concatenated rhs and algebraic + y = pybamm.StateVector( + slice(0, np.size(model.concatenated_initial_conditions)) + ) + # set up Jacobian object, for re-use of dict + jacobian = pybamm.Jacobian() + else: + # Convert model attributes to casadi + t_casadi = casadi.MX.sym("t") + y_diff = casadi.MX.sym( + "y_diff", len(model.concatenated_rhs.evaluate(0, y0, inputs)) + ) + y_alg = casadi.MX.sym( + "y_alg", len(model.concatenated_algebraic.evaluate(0, y0, inputs)) + ) + y_casadi = casadi.vertcat(y_diff, y_alg) + u_casadi = {} + for name, value in inputs.items(): + if isinstance(value, numbers.Number): + u_casadi[name] = casadi.MX.sym(name) + else: + u_casadi[name] = casadi.MX.sym(name, value.shape[0]) + u_casadi_stacked = casadi.vertcat(*[u for u in u_casadi.values()]) + + def process(func, name, use_jacobian=None): + def report(string): + # don't log event conversion + if "event" not in string: + pybamm.logger.info(string) + + if use_jacobian is None: + use_jacobian = model.use_jacobian + if model.convert_to_format != "casadi": + # Process with pybamm functions + if model.use_simplify: + report(f"Simplifying {name}") + func = simp.simplify(func) + if use_jacobian: + report(f"Calculating jacobian for {name}") + jac = jacobian.jac(func, y) + if model.use_simplify: + report(f"Simplifying jacobian for {name}") + jac = simp.simplify(jac) + if model.convert_to_format == "python": + report(f"Converting jacobian for {name} to python") + jac = pybamm.EvaluatorPython(jac) + jac = jac.evaluate + else: + jac = None + if model.convert_to_format == "python": + report(f"Converting {name} to python") + func = pybamm.EvaluatorPython(func) + func = func.evaluate + else: + # Process with CasADi + report(f"Converting {name} to CasADi") + func = func.to_casadi(t_casadi, y_casadi, u_casadi) + if use_jacobian: + report(f"Calculating jacobian for {name} using CasADi") + jac_casadi = casadi.jacobian(func, y_casadi) + jac = casadi.Function( + name, [t_casadi, y_casadi, u_casadi_stacked], [jac_casadi] + ) + else: + jac = None + func = casadi.Function( + name, [t_casadi, y_casadi, u_casadi_stacked], [func] + ) + if name == "residuals": + func_call = Residuals(func, name, model) + else: + func_call = SolverCallable(func, name, model) + func_call.set_inputs(inputs) + if jac is not None: + jac_call = SolverCallable(jac, name + "_jac", model) + jac_call.set_inputs(inputs) + else: + jac_call = None + return func, func_call, jac_call + + # Process rhs, algebraic and event expressions + rhs, rhs_eval, jac_rhs = process(model.concatenated_rhs, "RHS") + algebraic, algebraic_eval, jac_algebraic = process( + model.concatenated_algebraic, "algebraic" + ) + events_eval = [ + process(event, "event", use_jacobian=False)[1] + for event in model.events.values() + ] + + # Add the solver attributes + model.rhs_eval = rhs_eval + model.algebraic_eval = algebraic_eval + model.jac_algebraic_eval = jac_algebraic + model.events_eval = events_eval + + # Calculate consistent initial conditions for the algebraic equations + if len(model.algebraic) > 0: + all_states = pybamm.NumpyConcatenation( + model.concatenated_rhs, model.concatenated_algebraic + ) + # Process again, uses caching so should be quick + residuals, residuals_eval, jacobian_eval = process(all_states, "residuals") + model.residuals_eval = residuals_eval + model.jacobian_eval = jacobian_eval + model.y0 = self.calculate_consistent_initial_conditions(model) + else: + # can use DAE solver to solve ODE model + model.residuals_eval = Residuals(rhs, "residuals", model) + model.jacobian_eval = jac_rhs + model.y0 = model.concatenated_initial_conditions[:, 0] + + # Save CasADi functions for the CasADi solver + # Note: when we pass to casadi the ode part of the problem must be in explicit + # form so we pre-multiply by the inverse of the mass matrix + if model.convert_to_format == "casadi" and isinstance( + self, pybamm.CasadiSolver + ): + mass_matrix_inv = casadi.MX(model.mass_matrix_inv.entries) + explicit_rhs = mass_matrix_inv @ rhs(t_casadi, y_casadi, u_casadi_stacked) + model.casadi_rhs = casadi.Function( + "rhs", [t_casadi, y_casadi, u_casadi_stacked], [explicit_rhs] + ) + model.casadi_algebraic = algebraic + + pybamm.logger.info("Finish solver set-up") + + def set_inputs(self, model, ext_and_inputs): + """ + Set values that are controlled externally, such as external variables and input + parameters + + Parameters + ---------- + ext_and_inputs : dict + Any external variables or input parameters to pass to the model when solving + + """ + model.rhs_eval.set_inputs(ext_and_inputs) + model.algebraic_eval.set_inputs(ext_and_inputs) + model.residuals_eval.set_inputs(ext_and_inputs) + for evnt in model.events_eval: + evnt.set_inputs(ext_and_inputs) + if model.jacobian_eval: + model.jacobian_eval.set_inputs(ext_and_inputs) + + def calculate_consistent_initial_conditions(self, model): + """ + Calculate consistent initial conditions for the algebraic equations through + root-finding + + Parameters + ---------- + model : :class:`pybamm.BaseModel` + The model for which to calculate initial conditions. + + Returns + ------- + y0_consistent : array-like, same shape as y0_guess + Initial conditions that are consistent with the algebraic equations (roots + of the algebraic equations) + """ + pybamm.logger.info("Start calculating consistent initial conditions") + rhs = model.rhs_eval + algebraic = model.algebraic_eval + y0_guess = model.concatenated_initial_conditions.flatten() + jac = model.jac_algebraic_eval + + # Split y0_guess into differential and algebraic + len_rhs = rhs(0, y0_guess).shape[0] + y0_diff, y0_alg_guess = np.split(y0_guess, [len_rhs]) + + def root_fun(y0_alg): + "Evaluates algebraic using y0_diff (fixed) and y0_alg (changed by algo)" + y0 = np.concatenate([y0_diff, y0_alg]) + out = algebraic(0, y0) + pybamm.logger.debug( + "Evaluating algebraic equations at t=0, L2-norm is {}".format( + np.linalg.norm(out) + ) + ) + return out + + if jac: + if issparse(jac(0, y0_guess)): + + def jac_fn(y0_alg): + """ + Evaluates jacobian using y0_diff (fixed) and y0_alg (varying) + """ + y0 = np.concatenate([y0_diff, y0_alg]) + return jac(0, y0)[:, len_rhs:].toarray() + + else: + + def jac_fn(y0_alg): + """ + Evaluates jacobian using y0_diff (fixed) and y0_alg (varying) + """ + y0 = np.concatenate([y0_diff, y0_alg]) + return jac(0, y0)[:, len_rhs:] + + else: + jac_fn = None + # Find the values of y0_alg that are roots of the algebraic equations + sol = optimize.root( + root_fun, + y0_alg_guess, + jac=jac_fn, + method=self.root_method, + tol=self.root_tol, + ) + # Return full set of consistent initial conditions (y0_diff unchanged) + y0_consistent = np.concatenate([y0_diff, sol.x]) + + if sol.success and np.all(sol.fun < self.root_tol * len(sol.x)): + pybamm.logger.info("Finish calculating consistent initial conditions") + return y0_consistent + elif not sol.success: + raise pybamm.SolverError( + "Could not find consistent initial conditions: {}".format(sol.message) + ) + else: + raise pybamm.SolverError( + """ + Could not find consistent initial conditions: solver terminated + successfully, but maximum solution error ({}) above tolerance ({}) + """.format( + np.max(sol.fun), self.root_tol * len(sol.x) + ) + ) + + def solve(self, model, t_eval, external_variables=None, inputs=None): """ Execute the solver setup and calculate the solution of the model at specified times. @@ -61,6 +375,11 @@ def solve(self, model, t_eval): initial_conditions t_eval : numeric type The times at which to compute the solution + external_variables : dict + A dictionary of external variables and their corresponding + values at the current time + inputs : dict, optional + Any input parameters to pass to the model when solving Raises ------ @@ -74,21 +393,39 @@ def solve(self, model, t_eval): if len(model.rhs) == 0 and len(model.algebraic) == 0: raise pybamm.ModelError("Cannot solve empty model") + # Make sure t_eval is monotonic + if (np.diff(t_eval) < 0).any(): + raise pybamm.SolverError("t_eval must increase monotonically") + # Set up timer = pybamm.Timer() - start_time = timer.time() - if model.convert_to_format == "casadi" or isinstance(self, pybamm.CasadiSolver): - self.set_up_casadi(model) - else: - self.set_up(model) - set_up_time = timer.time() - start_time + + # Set up external variables and inputs + external_variables = external_variables or {} + inputs = inputs or {} + ext_and_inputs = {**external_variables, **inputs} + + self.set_up(model, ext_and_inputs) + set_up_time = timer.time() # Solve - solution, solve_time, termination = self.compute_solution(model, t_eval) + # Set inputs and external + self.set_inputs(model, ext_and_inputs) + + timer.reset() + pybamm.logger.info("Calling solver") + solution = self._integrate(model, t_eval, ext_and_inputs) # Assign times - solution.solve_time = solve_time solution.set_up_time = set_up_time + solution.solve_time = timer.time() + + # Add model and inputs to solution + solution.model = model + solution.inputs = inputs + + # Identify the event that caused termination + termination = self.get_termination_reason(solution, model.events) pybamm.logger.info("Finish solving {} ({})".format(model.name, termination)) pybamm.logger.info( @@ -100,7 +437,9 @@ def solve(self, model, t_eval): ) return solution - def step(self, model, dt, npts=2, log=True, external_variables=None): + def step( + self, old_solution, model, dt, npts=2, external_variables=None, inputs=None + ): """ Step the solution of the model forward by a given time increment. The first time this method is called it executes the necessary setup by @@ -108,6 +447,8 @@ def step(self, model, dt, npts=2, log=True, external_variables=None): Parameters ---------- + old_solution : :class:`pybamm.Solution` or None + The previous solution to be added to. If `None`, a new solution is created. model : :class:`pybamm.BaseModel` The model whose solution to calculate. Must have attributes rhs and initial_conditions @@ -119,6 +460,9 @@ def step(self, model, dt, npts=2, log=True, external_variables=None): external_variables : dict A dictionary of external variables and their corresponding values at the current time + inputs : dict, optional + Any input parameters to pass to the model when solving + Raises ------ @@ -126,6 +470,10 @@ def step(self, model, dt, npts=2, log=True, external_variables=None): If an empty model is passed (`model.rhs = {}` and `model.algebraic={}`) """ + if old_solution is not None and old_solution.termination != "final time": + # Return same solution as an event has already been triggered + return old_solution + # Make sure model isn't empty if len(model.rhs) == 0 and len(model.algebraic) == 0: raise pybamm.ModelError("Cannot step empty model") @@ -133,52 +481,46 @@ def step(self, model, dt, npts=2, log=True, external_variables=None): # Set timer timer = pybamm.Timer() - if not hasattr(self, "y0"): - # create a y_pad vector of the correct size: - self.y_pad = np.zeros((model.y_length - model.external_start, 1)) - - self.set_external_variables(model, external_variables) + # Set up external variables and inputs + external_variables = external_variables or {} + inputs = inputs or {} + ext_and_inputs = {**external_variables, **inputs} # Run set up on first step - if not hasattr(self, "y0"): + if model not in self.model_step_times: pybamm.logger.info( "Start stepping {} with {}".format(model.name, self.name) ) - - if model.convert_to_format == "casadi" or isinstance( - self, pybamm.CasadiSolver - ): - self.set_up_casadi(model) - else: - pybamm.logger.debug( - "Start stepping {} with {}".format(model.name, self.name) - ) - self.set_up(model) - self.t = 0.0 + self.set_up(model, ext_and_inputs) + self.model_step_times[model] = 0.0 set_up_time = timer.time() - else: set_up_time = 0 # Step - t_eval = np.linspace(self.t, self.t + dt, npts) - solution, solve_time, termination = self.compute_solution(model, t_eval) - - # Set self.t and self.y0 to their values at the final step - self.t = solution.t[-1] - self.y0 = solution.y[:, -1] + t = self.model_step_times[model] + t_eval = np.linspace(t, t + dt, npts) + # Set inputs and external + self.set_inputs(model, ext_and_inputs) - # add the external points onto the solution - full_y = np.zeros((model.y_length, solution.y.shape[1])) - for i in np.arange(solution.y.shape[1]): - sol_y = solution.y[:, i] - sol_y = sol_y[:, np.newaxis] - full_y[:, i] = add_external(sol_y, self.y_pad, self.y_ext)[:, 0] - solution.y = full_y + pybamm.logger.info("Calling solver") + timer.reset() + solution = self._integrate(model, t_eval, ext_and_inputs) # Assign times - solution.solve_time = solve_time solution.set_up_time = set_up_time + solution.solve_time = timer.time() + + # Add model and inputs to solution + solution.model = model + solution.inputs = inputs + + # Identify the event that caused termination + termination = self.get_termination_reason(solution, model.events) + + # Set self.t and self.y0 to their values at the final step + self.model_step_times[model] = solution.t[-1] + model.y0 = solution.y[:, -1] pybamm.logger.debug("Finish stepping {} ({})".format(model.name, termination)) if set_up_time: @@ -193,72 +535,10 @@ def step(self, model, dt, npts=2, log=True, external_variables=None): pybamm.logger.debug( "Step time: {}".format(timer.format(solution.solve_time)) ) - return solution - - def set_external_variables(self, model, external_variables): - if external_variables is None: - external_variables = {} - - # load external variables into a state vector - self.y_ext = np.zeros((model.y_length, 1)) - for var_name, var_vals in external_variables.items(): - var = model.variables[var_name] - if isinstance(var, pybamm.Concatenation): - start = var.children[0].y_slices[0].start - stop = var.children[-1].y_slices[-1].stop - y_slice = slice(start, stop) - - elif isinstance(var, pybamm.StateVector): - start = var.y_slices[0].start - stop = var.y_slices[-1].stop - y_slice = slice(start, stop) - else: - raise pybamm.InputError( - """The variable you have inputted is not a StateVector or Concatenation - of StateVectors. Please check the submodel you have made "external" and - ensure that the variable you - are passing in is the variable that is solved for in that submodel""" - ) - self.y_ext[y_slice] = var_vals - - def compute_solution(self, model, t_eval): - """Calculate the solution of the model at specified times. Note: this - does *not* execute the solver setup. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - t_eval : numeric type - The times at which to compute the solution - - """ - raise NotImplementedError - - def set_up(self, model): - """Unpack model, perform checks, simplify and calculate jacobian. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - - """ - raise NotImplementedError - - def set_up_casadi(self, model): - """Convert model to casadi format and use their inbuilt functionalities. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - - """ - raise NotImplementedError + if old_solution is None: + return solution + else: + return old_solution + solution def get_termination_reason(self, solution, events): """ @@ -281,9 +561,12 @@ def get_termination_reason(self, solution, events): # Get final event value final_event_values = {} for name, event in events.items(): - y_event = add_external(solution.y_event, self.y_pad, self.y_ext) final_event_values[name] = abs( - event.evaluate(solution.t_event, y_event) + event.evaluate( + solution.t_event, + solution.y_event, + {k: v[-1] for k, v in solution.inputs.items()}, + ) ) termination_event = min(final_event_values, key=final_event_values.get) # Add the event to the solution object @@ -291,11 +574,55 @@ def get_termination_reason(self, solution, events): return "the termination event '{}' occurred".format(termination_event) -def add_external(y, y_pad, y_ext): - """ - Pad the state vector and then add the external variables so that - it is of the correct shape for evaluate - """ - if y_pad is not None and y_ext is not None: - y = np.concatenate([y, y_pad]) + y_ext - return y +class SolverCallable: + "A class that will be called by the solver when integrating" + + def __init__(self, function, name, model): + self._function = function + if isinstance(function, casadi.Function): + self.form = "casadi" + self.inputs = casadi.DM() + else: + self.form = "python" + self.inputs = {} + self.name = name + self.model = model + + def set_inputs(self, inputs): + "Set inputs" + if self.form == "python": + self.inputs = inputs + elif self.form == "casadi": + self.inputs = casadi.vertcat(*[x for x in inputs.values()]) + + def __call__(self, t, y): + y = y[:, np.newaxis] + if self.name in ["RHS", "algebraic", "residuals", "event"]: + return self.function(t, y).flatten() + else: + return self.function(t, y) + + def function(self, t, y): + if self.form == "casadi": + if self.name in ["RHS", "algebraic", "residuals", "event"]: + return self._function(t, y, self.inputs).full() + else: + # keep jacobians sparse + return self._function(t, y, self.inputs) + else: + return self._function(t, y, self.inputs, known_evals={})[0] + + +class Residuals(SolverCallable): + "Returns information about residuals at time t and state y" + + def __init__(self, function, name, model): + super().__init__(function, name, model) + self.mass_matrix = model.mass_matrix.entries + + def __call__(self, t, y, ydot): + pybamm.logger.debug( + "Evaluating residuals for {} at t={}".format(self.model.name, t) + ) + states_eval = super().__call__(t, y) + return states_eval - self.mass_matrix @ ydot diff --git a/pybamm/solvers/casadi_solver.py b/pybamm/solvers/casadi_solver.py index b88ceed73c..299b8aa7dd 100644 --- a/pybamm/solvers/casadi_solver.py +++ b/pybamm/solvers/casadi_solver.py @@ -6,10 +6,10 @@ import numpy as np -class CasadiSolver(pybamm.DaeSolver): +class CasadiSolver(pybamm.BaseSolver): """Solve a discretised model, using CasADi. - **Extends**: :class:`pybamm.DaeSolver` + **Extends**: :class:`pybamm.BaseSolver` Parameters ---------- @@ -44,7 +44,6 @@ class CasadiSolver(pybamm.DaeSolver): def __init__( self, - method="idas", mode="safe", rtol=1e-6, atol=1e-6, @@ -53,7 +52,7 @@ def __init__( max_step_decrease_count=5, **extra_options, ): - super().__init__(method, rtol, atol, root_method, root_tol) + super().__init__("problem dependent", rtol, atol, root_method, root_tol) if mode in ["safe", "fast"]: self.mode = mode else: @@ -66,69 +65,77 @@ def __init__( ) self.max_step_decrease_count = max_step_decrease_count self.extra_options = extra_options - self.name = "CasADi solver ({}) with '{}' mode".format(method, mode) + self.name = "CasADi solver with '{}' mode".format(mode) - def solve(self, model, t_eval): + # Initialize + self.problems = {} + self.options = {} + self.methods = {} + + def _integrate(self, model, t_eval, inputs=None): """ - Execute the solver setup and calculate the solution of the model at - specified times. + Solve a DAE model defined by residuals with initial conditions y0. Parameters ---------- model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions + The model whose solution to calculate. t_eval : numeric type The times at which to compute the solution - - Raises - ------ - :class:`pybamm.ValueError` - If an invalid mode is passed. - :class:`pybamm.ModelError` - If an empty model is passed (`model.rhs = {}` and `model.algebraic={}`) - + inputs : dict, optional + Any external variables or input parameters to pass to the model when solving """ + inputs = inputs or {} + + rhs_size = model.rhs_eval(0, model.y0).shape[0] if self.mode == "fast": - # Solve model normally by calling the solve method from parent class - return super().solve(model, t_eval) + integrator = self.get_integrator(model, t_eval, inputs) + y0_diff, y0_alg = np.split(model.y0, [rhs_size]) + solution = self._run_integrator(integrator, y0_diff, y0_alg, inputs, t_eval) + solution.termination = "final time" + return solution elif model.events == {}: pybamm.logger.info("No events found, running fast mode") - # Solve model normally by calling the solve method from parent class - return super().solve(model, t_eval) + integrator = self.get_integrator(model, t_eval, inputs) + y0_diff, y0_alg = np.split(model.y0, [rhs_size]) + solution = self._run_integrator(integrator, y0_diff, y0_alg, inputs, t_eval) + solution.termination = "final time" + return solution elif self.mode == "safe": # Step-and-check - timer = pybamm.Timer() - self.set_up_casadi(model) - set_up_time = timer.time() init_event_signs = np.sign( - np.concatenate([event(0, self.y0) for event in self.event_funs]) + np.concatenate([event(0, model.y0) for event in model.events_eval]) ) - solution = None pybamm.logger.info( "Start solving {} with {} in 'safe' mode".format(model.name, self.name) ) - self.t = 0.0 + t = t_eval[0] + y0 = model.y0 + # Initialize solution + solution = pybamm.Solution(np.array([t]), y0[:, np.newaxis]) + solution.solve_time = 0 for dt in np.diff(t_eval): # Step solved = False count = 0 while not solved: + integrator = self.get_integrator( + model, np.array([t, t + dt]), inputs + ) # Try to solve with the current step, if it fails then halve the # step size and try again. This will make solution.t slightly # different to t_eval, but shouldn't matter too much as it should # only happen near events. try: - current_step_sol = self.step(model, dt) + y0_diff, y0_alg = np.split(y0, [rhs_size]) + current_step_sol = self._run_integrator( + integrator, y0_diff, y0_alg, inputs, np.array([t, t + dt]) + ) solved = True except pybamm.SolverError: dt /= 2 count += 1 if count >= self.max_step_decrease_count: - if solution is None: - t = 0 - else: - t = solution.t[-1] raise pybamm.SolverError( """ Maximum number of decreased steps occurred at t={}. Try @@ -142,7 +149,7 @@ def solve(self, model, t_eval): np.concatenate( [ event(0, current_step_sol.y[:, -1]) - for event in self.event_funs + for event in model.events_eval ] ) ) @@ -153,112 +160,69 @@ def solve(self, model, t_eval): solution.y_event = solution.y[:, -1] break else: - if not solution: - # create solution object on first step - solution = current_step_sol - solution.set_up_time = set_up_time - else: - # append solution from the current step to solution - solution.append(current_step_sol) + # assign temporary solve time + current_step_sol.solve_time = np.nan + # append solution from the current step to solution + solution.append(current_step_sol) + t = solution.t[-1] + y0 = solution.y[:, -1] - # Calculate more exact termination reason - solution.termination = self.get_termination_reason(solution, self.events) - pybamm.logger.info( - "Finish solving {} ({})".format(model.name, solution.termination) - ) - pybamm.logger.info( - "Set-up time: {}, Solve time: {}, Total time: {}".format( - timer.format(solution.set_up_time), - timer.format(solution.solve_time), - timer.format(solution.total_time), - ) - ) return solution - def compute_solution(self, model, t_eval): - """Calculate the solution of the model at specified times. In this class, we - overwrite the behaviour of :class:`pybamm.DaeSolver`, since CasADi requires - slightly different syntax. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - t_eval : numeric type - The times at which to compute the solution - - """ - timer = pybamm.Timer() + def get_integrator(self, model, t_eval, inputs): + # Only set up problem once + if model not in self.problems: + y0 = model.y0 + rhs = model.casadi_rhs + algebraic = model.casadi_algebraic + u_stacked = casadi.vertcat(*[x for x in inputs.values()]) + + options = { + "grid": t_eval, + "reltol": self.rtol, + "abstol": self.atol, + "output_t0": True, + "max_num_steps": self.max_steps, + } - solve_start_time = timer.time() - pybamm.logger.debug("Calling DAE solver") - solution = self.integrate_casadi( - self.casadi_rhs, - self.casadi_algebraic, - self.y0, - t_eval, - mass_matrix=model.mass_matrix.entries, + # set up and solve + t = casadi.MX.sym("t") + u = casadi.MX.sym("u", u_stacked.shape[0]) + y_diff = casadi.MX.sym("y_diff", rhs(0, y0, u).shape[0]) + problem = {"t": t, "x": y_diff, "p": u} + if algebraic(0, y0, u).is_empty(): + method = "cvodes" + problem.update({"ode": rhs(t, y_diff, u)}) + else: + options["calc_ic"] = True + method = "idas" + y_alg = casadi.MX.sym("y_alg", algebraic(0, y0, u).shape[0]) + y_full = casadi.vertcat(y_diff, y_alg) + problem.update( + { + "z": y_alg, + "ode": rhs(t, y_full, u), + "alg": algebraic(t, y_full, u), + } + ) + self.problems[model] = problem + self.options[model] = options + self.methods[model] = method + else: + # problem stays the same + # just update options + self.options[model]["grid"] = t_eval + return casadi.integrator( + "F", self.methods[model], self.problems[model], self.options[model] ) - solve_time = timer.time() - solve_start_time - - # Events not implemented, termination is always 'final time' - termination = "final time" - - return solution, solve_time, termination - - def integrate_casadi(self, rhs, algebraic, y0, t_eval, mass_matrix=None): - """ - Solve a DAE model defined by residuals with initial conditions y0. - Parameters - ---------- - residuals : method - A function that takes in t, y and ydot and returns the residuals of the - equations - y0 : numeric type - The initial conditions - t_eval : numeric type - The times at which to compute the solution - mass_matrix : array_like, optional - The (sparse) mass matrix for the chosen spatial method. This is only passed - to check that the mass matrix is diagonal with 1s for the odes and 0s for - the algebraic equations, as CasADi does not allow to pass mass matrices. - """ - options = { - "grid": t_eval, - "reltol": self.rtol, - "abstol": self.atol, - "output_t0": True, - "max_num_steps": self.max_steps, - } - options.update(self.extra_options) - if self.method == "idas": - options["calc_ic"] = True - - # set up and solve - t = casadi.MX.sym("t") - y_diff = casadi.MX.sym("y_diff", rhs(0, y0).shape[0]) - if algebraic is None: - problem = {"t": t, "x": y_diff, "ode": rhs(t, y_diff)} - else: - y_alg = casadi.MX.sym("y_alg", algebraic(0, y0).shape[0]) - y = casadi.vertcat(y_diff, y_alg) - problem = { - "t": t, - "x": y_diff, - "z": y_alg, - "ode": rhs(t, y), - "alg": algebraic(t, y), - } - integrator = casadi.integrator("F", self.method, problem, options) + def _run_integrator(self, integrator, y0_diff, y0_alg, inputs, t_eval): try: # Try solving - y0_diff, y0_alg = np.split(y0, [y_diff.shape[0]]) - sol = integrator(x0=y0_diff, z0=y0_alg) + u_stacked = casadi.vertcat(*[x for x in inputs.values()]) + sol = integrator(x0=y0_diff, z0=y0_alg, p=u_stacked, **self.extra_options) y_values = np.concatenate([sol["xf"].full(), sol["zf"].full()]) - return pybamm.Solution(t_eval, y_values, None, None, "final time") + return pybamm.Solution(t_eval, y_values) except RuntimeError as e: # If it doesn't work raise error raise pybamm.SolverError(e.args[0]) - diff --git a/pybamm/solvers/dae_solver.py b/pybamm/solvers/dae_solver.py deleted file mode 100644 index 33d312de49..0000000000 --- a/pybamm/solvers/dae_solver.py +++ /dev/null @@ -1,589 +0,0 @@ -# -# Base solver class -# -import casadi -import pybamm -import numpy as np -from scipy import optimize -from scipy.sparse import issparse - -from .base_solver import add_external - - -class DaeSolver(pybamm.BaseSolver): - """Solve a discretised model. - - Parameters - ---------- - rtol : float, optional - The relative tolerance for the solver (default is 1e-6). - atol : float, optional - The absolute tolerance for the solver (default is 1e-6). - root_method : str, optional - The method to use to find initial conditions (default is "lm") - root_tol : float, optional - The tolerance for the initial-condition solver (default is 1e-6). - max_steps: int, optional - The maximum number of steps the solver will take before terminating - (default is 1000). - """ - - def __init__( - self, - method=None, - rtol=1e-6, - atol=1e-6, - root_method="lm", - root_tol=1e-6, - max_steps=1000, - ): - super().__init__(method, rtol, atol) - self.root_method = root_method - self.root_tol = root_tol - self.max_steps = max_steps - self.name = "Base DAE solver" - - @property - def root_method(self): - return self._root_method - - @root_method.setter - def root_method(self, method): - self._root_method = method - - @property - def root_tol(self): - return self._root_tol - - @root_tol.setter - def root_tol(self, tol): - self._root_tol = tol - - @property - def max_steps(self): - return self._max_steps - - @max_steps.setter - def max_steps(self, max_steps): - self._max_steps = max_steps - - def compute_solution(self, model, t_eval): - """Calculate the solution of the model at specified times. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - t_eval : numeric type - The times at which to compute the solution - - """ - timer = pybamm.Timer() - - # update y_pad and y_ext - self.rhs.set_pad_ext(self.y_pad, self.y_ext) - self.algebraic.set_pad_ext(self.y_pad, self.y_ext) - self.residuals.set_pad_ext(self.y_pad, self.y_ext) - for evnt in self.event_funs: - evnt.set_pad_ext(self.y_pad, self.y_ext) - if self.jacobian: - self.jacobian.set_pad_ext(self.y_pad, self.y_ext) - - solve_start_time = timer.time() - pybamm.logger.info("Calling DAE solver") - solution = self.integrate( - self.residuals, - self.y0, - t_eval, - events=self.event_funs, - mass_matrix=model.mass_matrix.entries, - jacobian=self.jacobian, - model=model, - ) - - solve_time = timer.time() - solve_start_time - - # Identify the event that caused termination - termination = self.get_termination_reason(solution, self.events) - - return solution, solve_time, termination - - def set_up(self, model): - """Unpack model, perform checks, simplify and calculate jacobian. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - """ - # create simplified rhs, algebraic and event expressions - concatenated_rhs = model.concatenated_rhs - concatenated_algebraic = model.concatenated_algebraic - events = model.events - - if model.use_simplify: - # set up simplification object, for re-use of dict - simp = pybamm.Simplification() - pybamm.logger.info("Simplifying RHS") - concatenated_rhs = simp.simplify(concatenated_rhs) - pybamm.logger.info("Simplifying algebraic") - concatenated_algebraic = simp.simplify(concatenated_algebraic) - pybamm.logger.info("Simplifying events") - events = {name: simp.simplify(event) for name, event in events.items()} - - if model.use_jacobian: - # Create Jacobian from concatenated rhs and algebraic - y = pybamm.StateVector( - slice(0, np.size(model.concatenated_initial_conditions)) - ) - # set up Jacobian object, for re-use of dict - jacobian = pybamm.Jacobian() - pybamm.logger.info("Calculating jacobian") - jac_rhs = jacobian.jac(concatenated_rhs, y) - jac_algebraic = jacobian.jac(concatenated_algebraic, y) - jac = pybamm.SparseStack(jac_rhs, jac_algebraic) - model.jacobian = jac - model.jacobian_rhs = jac_rhs - model.jacobian_algebraic = jac_algebraic - - if model.use_simplify: - pybamm.logger.info("Simplifying jacobian") - jac_algebraic = simp.simplify(jac_algebraic) - jac = simp.simplify(jac) - - if model.convert_to_format == "python": - pybamm.logger.info("Converting jacobian to python") - jac_algebraic = pybamm.EvaluatorPython(jac_algebraic) - jac = pybamm.EvaluatorPython(jac) - - jacobian = Jacobian(jac.evaluate) - - def jacobian_alg(t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return jac_algebraic.evaluate(t, y, known_evals={})[0] - - else: - jacobian = None - jacobian_alg = None - - if model.convert_to_format == "python": - pybamm.logger.info("Converting RHS to python") - concatenated_rhs = pybamm.EvaluatorPython(concatenated_rhs) - pybamm.logger.info("Converting algebraic to python") - concatenated_algebraic = pybamm.EvaluatorPython(concatenated_algebraic) - pybamm.logger.info("Converting events to python") - events = { - name: pybamm.EvaluatorPython(event) for name, event in events.items() - } - - # Calculate consistent initial conditions for the algebraic equations - rhs = Rhs(concatenated_rhs.evaluate) - algebraic = Algebraic(concatenated_algebraic.evaluate) - - if len(model.algebraic) > 0: - y0 = self.calculate_consistent_initial_conditions( - rhs, - algebraic, - model.concatenated_initial_conditions[:, 0], - jacobian_alg, - ) - else: - # can use DAE solver to solve ODE model - y0 = model.concatenated_initial_conditions[:, 0] - - # Create event-dependent function to evaluate events - def get_event_class(event): - return EvalEvent(event.evaluate) - - # Add the solver attributes - self.y0 = y0 - self.rhs = rhs - self.algebraic = algebraic - self.residuals = Residuals( - model, concatenated_rhs.evaluate, concatenated_algebraic.evaluate - ) - self.events = events - self.event_funs = [get_event_class(event) for event in events.values()] - self.jacobian = jacobian - - def set_up_casadi(self, model): - """Convert model to casadi format and use their inbuilt functionalities. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - """ - # Convert model attributes to casadi - t_casadi = casadi.MX.sym("t") - y0 = model.concatenated_initial_conditions - y0 = add_external(y0, self.y_pad, self.y_ext) - - y_diff = casadi.MX.sym("y_diff", len(model.concatenated_rhs.evaluate(0, y0))) - y_alg = casadi.MX.sym( - "y_alg", len(model.concatenated_algebraic.evaluate(0, y0)) - ) - y_casadi = casadi.vertcat(y_diff, y_alg) - if self.y_pad is not None: - y_ext = casadi.MX.sym("y_ext", len(self.y_pad)) - y_casadi_w_ext = casadi.vertcat(y_casadi, y_ext) - else: - y_casadi_w_ext = y_casadi - - pybamm.logger.info("Converting RHS to CasADi") - concatenated_rhs = model.concatenated_rhs.to_casadi(t_casadi, y_casadi_w_ext) - pybamm.logger.info("Converting algebraic to CasADi") - concatenated_algebraic = model.concatenated_algebraic.to_casadi( - t_casadi, y_casadi_w_ext - ) - all_states = casadi.vertcat(concatenated_rhs, concatenated_algebraic) - pybamm.logger.info("Converting events to CasADi") - casadi_events = { - name: event.to_casadi(t_casadi, y_casadi_w_ext) - for name, event in model.events.items() - } - - # Create functions to evaluate rhs and algebraic - concatenated_rhs_fn = casadi.Function( - "rhs", [t_casadi, y_casadi_w_ext], [concatenated_rhs] - ) - concatenated_algebraic_fn = casadi.Function( - "algebraic", [t_casadi, y_casadi_w_ext], [concatenated_algebraic] - ) - all_states_fn = casadi.Function("all", [t_casadi, y_casadi_w_ext], [all_states]) - - if model.use_jacobian: - - pybamm.logger.info("Calculating jacobian") - casadi_jac = casadi.jacobian(all_states, y_casadi) - casadi_jac_fn = casadi.Function( - "jacobian", [t_casadi, y_casadi_w_ext], [casadi_jac] - ) - casadi_jac_alg = casadi.jacobian(concatenated_algebraic, y_casadi) - casadi_jac_alg_fn = casadi.Function( - "jacobian", [t_casadi, y_casadi_w_ext], [casadi_jac_alg] - ) - - jacobian = JacobianCasadi(casadi_jac_fn) - - def jacobian_alg(t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return casadi_jac_alg_fn(t, y) - - else: - jacobian = None - jacobian_alg = None - - rhs = RhsCasadi(concatenated_rhs_fn) - algebraic = AlgebraicCasadi(concatenated_algebraic_fn) - - rhs.set_pad_ext(self.y_pad, self.y_ext) - algebraic.set_pad_ext(self.y_pad, self.y_ext) - - if len(model.algebraic) > 0: - - y0 = self.calculate_consistent_initial_conditions( - rhs, - algebraic, - model.concatenated_initial_conditions[:, 0], - jacobian_alg, - ) - else: - # can use DAE solver to solve ODE model - y0 = model.concatenated_initial_conditions[:, 0] - - # Create event-dependent function to evaluate events - def get_event_class(event): - casadi_event_fn = casadi.Function( - "event", [t_casadi, y_casadi_w_ext], [event] - ) - return EvalEvent(casadi_event_fn) - - # Add the solver attributes - # Note: these are the (possibly) converted to python version rhs, algebraic - # etc. The expression tree versions of these are attributes of the model - self.y0 = y0 - self.rhs = rhs - self.algebraic = algebraic - self.residuals = ResidualsCasadi(model, all_states_fn) - self.events = model.events - self.event_funs = [get_event_class(event) for event in casadi_events.values()] - self.jacobian = jacobian - - # Save CasADi functions for the CasADi solver - self.casadi_rhs = concatenated_rhs_fn - self.casadi_algebraic = concatenated_algebraic_fn - - def calculate_consistent_initial_conditions( - self, rhs, algebraic, y0_guess, jac=None - ): - """ - Calculate consistent initial conditions for the algebraic equations through - root-finding - - Parameters - ---------- - rhs : method - Function that takes in t and y and returns the value of the differential - equations - algebraic : method - Function that takes in t and y and returns the value of the algebraic - equations - y0_guess : array-like - Array of the user's guess for the initial conditions, used to initialise - the root finding algorithm - jac : method - Function that takes in t and y and returns the value of the jacobian for the - algebraic equations - - Returns - ------- - y0_consistent : array-like, same shape as y0_guess - Initial conditions that are consistent with the algebraic equations (roots - of the algebraic equations) - """ - pybamm.logger.info("Start calculating consistent initial conditions") - - # Split y0_guess into differential and algebraic - len_rhs = rhs(0, y0_guess).shape[0] - y0_diff, y0_alg_guess = np.split(y0_guess, [len_rhs]) - - def root_fun(y0_alg): - "Evaluates algebraic using y0_diff (fixed) and y0_alg (changed by algo)" - y0 = np.concatenate([y0_diff, y0_alg]) - out = algebraic(0, y0) - pybamm.logger.debug( - "Evaluating algebraic equations at t=0, L2-norm is {}".format( - np.linalg.norm(out) - ) - ) - return out - - if jac: - if issparse(jac(0, y0_guess)): - - def jac_fn(y0_alg): - """ - Evaluates jacobian using y0_diff (fixed) and y0_alg (varying) - """ - y0 = np.concatenate([y0_diff, y0_alg]) - return jac(0, y0)[:, len_rhs:].toarray() - - else: - - def jac_fn(y0_alg): - """ - Evaluates jacobian using y0_diff (fixed) and y0_alg (varying) - """ - y0 = np.concatenate([y0_diff, y0_alg]) - return jac(0, y0)[:, len_rhs:] - - else: - jac_fn = None - # Find the values of y0_alg that are roots of the algebraic equations - sol = optimize.root( - root_fun, - y0_alg_guess, - jac=jac_fn, - method=self.root_method, - tol=self.root_tol, - ) - # Return full set of consistent initial conditions (y0_diff unchanged) - y0_consistent = np.concatenate([y0_diff, sol.x]) - - if sol.success and np.all(sol.fun < self.root_tol * len(sol.x)): - pybamm.logger.info("Finish calculating consistent initial conditions") - return y0_consistent - elif not sol.success: - raise pybamm.SolverError( - "Could not find consistent initial conditions: {}".format(sol.message) - ) - else: - raise pybamm.SolverError( - """ - Could not find consistent initial conditions: solver terminated - successfully, but maximum solution error ({}) above tolerance ({}) - """.format( - np.max(sol.fun), self.root_tol * len(sol.x) - ) - ) - - def integrate( - self, residuals, y0, t_eval, events=None, mass_matrix=None, jacobian=None - ): - """ - Solve a DAE model defined by residuals with initial conditions y0. - - Parameters - ---------- - residuals : method - A function that takes in t, y and ydot and returns the residuals of the - equations - y0 : numeric type - The initial conditions - t_eval : numeric type - The times at which to compute the solution - events : method, optional - A function that takes in t and y and returns conditions for the solver to - stop - mass_matrix : array_like, optional - The (sparse) mass matrix for the chosen spatial method. - jacobian : method, optional - A function that takes in t, y and ydot and returns the Jacobian - """ - raise NotImplementedError - - -class Rhs: - "Returns information about rhs at time t and state y" - - def __init__(self, concatenated_rhs_fn): - self.concatenated_rhs_fn = concatenated_rhs_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.concatenated_rhs_fn(t, y, known_evals={})[0][:, 0] - - -class RhsCasadi(Rhs): - "Returns information about rhs at time t and state y, with CasADi" - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.concatenated_rhs_fn(t, y).full()[:, 0] - - -class Algebraic: - "Returns information about algebraic equations at time t and state y" - - def __init__(self, concatenated_algebraic_fn): - self.concatenated_algebraic_fn = concatenated_algebraic_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.concatenated_algebraic_fn(t, y, known_evals={})[0][:, 0] - - -class AlgebraicCasadi(Algebraic): - "Returns information about algebraic equations at time t and state y, with CasADi" - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.concatenated_algebraic_fn(t, y).full()[:, 0] - - -class Residuals: - "Returns information about residuals at time t and state y" - - def __init__(self, model, concatenated_rhs_fn, concatenated_algebraic_fn): - self.model = model - self.concatenated_rhs_fn = concatenated_rhs_fn - self.concatenated_algebraic_fn = concatenated_algebraic_fn - self.mass_matrix = model.mass_matrix.entries - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y, ydot): - pybamm.logger.debug( - "Evaluating residuals for {} at t={}".format(self.model.name, t) - ) - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - rhs_eval, known_evals = self.concatenated_rhs_fn(t, y, known_evals={}) - # reuse known_evals - alg_eval = self.concatenated_algebraic_fn(t, y, known_evals=known_evals)[0] - # turn into 1D arrays - rhs_eval = rhs_eval[:, 0] - alg_eval = alg_eval[:, 0] - return np.concatenate([rhs_eval, alg_eval]) - self.mass_matrix @ ydot - - -class ResidualsCasadi(Residuals): - "Returns information about residuals at time t and state y, with CasADi" - - def __init__(self, model, all_states_fn): - self.model = model - self.all_states_fn = all_states_fn - self.mass_matrix = model.mass_matrix.entries - self.y_pad = None - self.y_ext = None - - def __call__(self, t, y, ydot): - pybamm.logger.debug( - "Evaluating residuals for {} at t={}".format(self.model.name, t) - ) - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - states_eval = self.all_states_fn(t, y).full()[:, 0] - return states_eval - self.mass_matrix @ ydot - - -class EvalEvent: - "Returns information about events at time t and state y" - - def __init__(self, event_fn): - self.event_fn = event_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.event_fn(t, y) - - -class Jacobian: - "Returns information about the jacobian at time t and state y" - - def __init__(self, jac_fn): - self.jac_fn = jac_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.jac_fn(t, y, known_evals={})[0] - - -class JacobianCasadi(Jacobian): - "Returns information about the jacobian at time t and state y, with CasADi" - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.jac_fn(t, y) - diff --git a/pybamm/solvers/idaklu_solver.py b/pybamm/solvers/idaklu_solver.py index 6c16cf0082..2c5950a3f6 100644 --- a/pybamm/solvers/idaklu_solver.py +++ b/pybamm/solvers/idaklu_solver.py @@ -17,7 +17,7 @@ def have_idaklu(): return idaklu_spec is not None -class IDAKLUSolver(pybamm.DaeSolver): +class IDAKLUSolver(pybamm.BaseSolver): """Solve a discretised model, using sundials with the KLU sparse linear solver. Parameters @@ -132,37 +132,20 @@ def _check_atol_type(self, atol, size): return atol - def integrate(self, residuals, y0, t_eval, events, mass_matrix, jacobian, model): + def _integrate(self, model, t_eval, inputs=None): """ Solve a DAE model defined by residuals with initial conditions y0. Parameters ---------- - residuals : method - A function that takes in t, y and ydot and returns the residuals of the - equations - y0 : numeric type - The initial conditions - t_eval : numeric type - The times at which to compute the solution - events : method, - A function that takes in t and y and returns conditions for the solver to - stop - mass_matrix : array_like, - The (sparse) mass matrix for the chosen spatial method. - jacobian : method, - A function that takes in t and y and returns the Jacobian. If - None, the solver will approximate the Jacobian. - (see `SUNDIALS docs. `). model : :class:`pybamm.BaseModel` The model whose solution to calculate. + t_eval : numeric type + The times at which to compute the solution """ - if jacobian is None: - pybamm.SolverError("KLU requires the Jacobian to be provided") - - if events is None: - pybamm.SolverError("KLU requires events to be provided") + if model.jacobian_eval is None: + raise pybamm.SolverError("KLU requires the Jacobian to be provided") try: atol = model.atol @@ -170,20 +153,22 @@ def integrate(self, residuals, y0, t_eval, events, mass_matrix, jacobian, model) atol = self._atol rtol = self._rtol - atol = self._check_atol_type(atol, y0.size) + atol = self._check_atol_type(atol, model.y0.size) + y0 = model.y0 + mass_matrix = model.mass_matrix.entries - if jacobian: - jac_y0_t0 = jacobian(t_eval[0], y0) + if model.jacobian_eval: + jac_y0_t0 = model.jacobian_eval(t_eval[0], y0) if sparse.issparse(jac_y0_t0): def jacfn(t, y, cj): - j = jacobian(t, y) - cj * mass_matrix + j = model.jacobian_eval(t, y) - cj * mass_matrix return j else: def jacfn(t, y, cj): - jac_eval = jacobian(t, y) - cj * mass_matrix + jac_eval = model.jacobian_eval(t, y) - cj * mass_matrix return sparse.csr_matrix(jac_eval) class SundialsJacobian: @@ -214,17 +199,17 @@ def get_jac_col_ptrs(self): jac_class = SundialsJacobian() - num_of_events = len(events) + num_of_events = len(model.events_eval) use_jac = 1 def rootfn(t, y): return_root = np.ones((num_of_events,)) - return_root[:] = [event(t, y) for event in events] + return_root[:] = [event(t, y) for event in model.events_eval] return return_root # get ids of rhs and algebraic variables - rhs_ids = np.ones(self.rhs(0, y0).shape) + rhs_ids = np.ones(model.rhs_eval(0, y0).shape) alg_ids = np.zeros(len(y0) - len(rhs_ids)) ids = np.concatenate((rhs_ids, alg_ids)) @@ -233,7 +218,7 @@ def rootfn(t, y): t_eval, y0, ydot0, - self.residuals, + model.residuals_eval, jac_class.jac_res, jac_class.get_jac_data, jac_class.get_jac_row_vals, @@ -249,7 +234,7 @@ def rootfn(t, y): t = sol.t number_of_timesteps = t.size - number_of_states = y0.size + number_of_states = model.y0.size y_out = sol.y.reshape((number_of_timesteps, number_of_states)) # return solution, we need to tranpose y to match scipy's interface diff --git a/pybamm/solvers/ode_solver.py b/pybamm/solvers/ode_solver.py deleted file mode 100644 index 934e521ef5..0000000000 --- a/pybamm/solvers/ode_solver.py +++ /dev/null @@ -1,322 +0,0 @@ -# -# Base solver class -# -import casadi -import pybamm -import numpy as np - -from .base_solver import add_external - - -class OdeSolver(pybamm.BaseSolver): - """Solve a discretised model. - - Parameters - ---------- - rtol : float, optional - The relative tolerance for the solver (default is 1e-6). - atol : float, optional - The absolute tolerance for the solver (default is 1e-6). - """ - - def __init__(self, method=None, rtol=1e-6, atol=1e-6): - super().__init__(method, rtol, atol) - self.name = "Base ODE solver" - - def compute_solution(self, model, t_eval): - """Calculate the solution of the model at specified times. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - t_eval : numeric type - The times at which to compute the solution - - """ - timer = pybamm.Timer() - - self.dydt.set_pad_ext(self.y_pad, self.y_ext) - for evnt in self.event_funs: - evnt.set_pad_ext(self.y_pad, self.y_ext) - if self.jacobian: - self.jacobian.set_pad_ext(self.y_pad, self.y_ext) - - solve_start_time = timer.time() - pybamm.logger.info("Calling ODE solver") - solution = self.integrate( - self.dydt, - self.y0, - t_eval, - events=self.event_funs, - mass_matrix=model.mass_matrix.entries, - jacobian=self.jacobian, - ) - - solve_time = timer.time() - solve_start_time - - # Identify the event that caused termination - termination = self.get_termination_reason(solution, self.events) - - return solution, solve_time, termination - - def set_up(self, model): - """Unpack model, perform checks, simplify and calculate jacobian. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - - Raises - ------ - :class:`pybamm.SolverError` - If the model contains any algebraic equations (in which case a DAE solver - should be used instead) - - """ - # Check for algebraic equations - if len(model.algebraic) > 0: - raise pybamm.SolverError( - """Cannot use ODE solver to solve model with DAEs""" - ) - - # create simplified rhs and event expressions - concatenated_rhs = model.concatenated_rhs - events = model.events - - if model.use_simplify: - # set up simplification object, for re-use of dict - simp = pybamm.Simplification() - # create simplified rhs and event expressions - pybamm.logger.info("Simplifying RHS") - concatenated_rhs = simp.simplify(concatenated_rhs) - - pybamm.logger.info("Simplifying events") - events = {name: simp.simplify(event) for name, event in events.items()} - - y0 = model.concatenated_initial_conditions[:, 0] - - if model.use_jacobian: - # Create Jacobian from concatenated rhs - y = pybamm.StateVector(slice(0, np.size(y0))) - # set up Jacobian object, for re-use of dict - jacobian = pybamm.Jacobian() - pybamm.logger.info("Calculating jacobian") - jac_rhs = jacobian.jac(concatenated_rhs, y) - model.jacobian = jac_rhs - model.jacobian_rhs = jac_rhs - - if model.use_simplify: - pybamm.logger.info("Simplifying jacobian") - jac_rhs = simp.simplify(jac_rhs) - - if model.convert_to_format == "python": - pybamm.logger.info("Converting jacobian to python") - jac_rhs = pybamm.EvaluatorPython(jac_rhs) - else: - jac_rhs = None - - if model.convert_to_format == "python": - pybamm.logger.info("Converting RHS to python") - concatenated_rhs = pybamm.EvaluatorPython(concatenated_rhs) - pybamm.logger.info("Converting events to python") - events = { - name: pybamm.EvaluatorPython(event) for name, event in events.items() - } - - # Create event-dependent function to evaluate events - def get_event_class(event): - return EvalEvent(event.evaluate) - - # Create function to evaluate jacobian - if jac_rhs is not None: - jacobian = Jacobian(jac_rhs.evaluate) - else: - jacobian = None - - # Add the solver attributes - # Note: these are the (possibly) converted to python version rhs, algebraic - # etc. The expression tree versions of these are attributes of the model - self.y0 = y0 - self.dydt = Dydt(model, concatenated_rhs.evaluate) - self.events = events - self.event_funs = [get_event_class(event) for event in events.values()] - self.jacobian = jacobian - - def set_up_casadi(self, model): - """Convert model to casadi format and use their inbuilt functionalities. - - Parameters - ---------- - model : :class:`pybamm.BaseModel` - The model whose solution to calculate. Must have attributes rhs and - initial_conditions - - Raises - ------ - :class:`pybamm.SolverError` - If the model contains any algebraic equations (in which case a DAE solver - should be used instead) - - """ - # Check for algebraic equations - if len(model.algebraic) > 0: - raise pybamm.SolverError( - """Cannot use ODE solver to solve model with DAEs""" - ) - - y0 = model.concatenated_initial_conditions[:, 0] - - t_casadi = casadi.MX.sym("t") - y_casadi = casadi.MX.sym("y", len(y0)) - - if self.y_pad is not None: - y_ext = casadi.MX.sym("y_ext", len(self.y_pad)) - y_casadi_w_ext = casadi.vertcat(y_casadi, y_ext) - else: - y_casadi_w_ext = y_casadi - - pybamm.logger.info("Converting RHS to CasADi") - concatenated_rhs = model.concatenated_rhs.to_casadi(t_casadi, y_casadi_w_ext) - pybamm.logger.info("Converting events to CasADi") - casadi_events = { - name: event.to_casadi(t_casadi, y_casadi_w_ext) - for name, event in model.events.items() - } - - # Create function to evaluate rhs - concatenated_rhs_fn = casadi.Function( - "rhs", [t_casadi, y_casadi_w_ext], [concatenated_rhs] - ) - - # Create event-dependent function to evaluate events - def get_event_class(event): - casadi_event_fn = casadi.Function( - "event", [t_casadi, y_casadi_w_ext], [event] - ) - return EvalEvent(casadi_event_fn) - - # Create function to evaluate jacobian - if model.use_jacobian: - pybamm.logger.info("Calculating jacobian") - casadi_jac = casadi.jacobian(concatenated_rhs, y_casadi) - casadi_jac_fn = casadi.Function( - "jacobian", [t_casadi, y_casadi_w_ext], [casadi_jac] - ) - - jacobian = JacobianCasadi(casadi_jac_fn) - - else: - jacobian = None - - # Add the solver attributes - self.y0 = y0 - self.dydt = DydtCasadi(model, concatenated_rhs_fn) - self.events = model.events - self.event_funs = [get_event_class(event) for event in casadi_events.values()] - self.jacobian = jacobian - - def integrate( - self, derivs, y0, t_eval, events=None, mass_matrix=None, jacobian=None - ): - """ - Solve a model defined by dydt with initial conditions y0. - - Parameters - ---------- - derivs : method - A function that takes in t and y and returns the time-derivative dydt - y0 : numeric type - The initial conditions - t_eval : numeric type - The times at which to compute the solution - events : method, optional - A function that takes in t and y and returns conditions for the solver to - stop - mass_matrix : array_like, optional - The (sparse) mass matrix for the chosen spatial method. - jacobian : method, optional - A function that takes in t and y and returns the Jacobian - """ - raise NotImplementedError - - -# Set up caller classes outside of the solver object to allow pickling -class Dydt: - "Returns information about time derivatives at time t and state y" - - def __init__(self, model, concatenated_rhs_fn): - self.model = model - self.concatenated_rhs_fn = concatenated_rhs_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - pybamm.logger.debug("Evaluating RHS for {} at t={}".format(self.model.name, t)) - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - dy = self.concatenated_rhs_fn(t, y, known_evals={})[0] - return dy[:, 0] - - -class DydtCasadi(Dydt): - "Returns information about time derivatives at time t and state y, with CasADi" - - def __call__(self, t, y): - pybamm.logger.debug("Evaluating RHS for {} at t={}".format(self.model.name, t)) - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - dy = self.concatenated_rhs_fn(t, y).full() - return dy[:, 0] - - -class EvalEvent: - "Returns information about events at time t and state y" - - def __init__(self, event_fn): - self.event_fn = event_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.event_fn(t, y) - - -class Jacobian: - "Returns information about the jacobian at time t and state y" - - def __init__(self, jac_fn): - self.jac_fn = jac_fn - self.y_pad = None - self.y_ext = None - - def set_pad_ext(self, y_pad, y_ext): - self.y_pad = y_pad - self.y_ext = y_ext - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.jac_fn(t, y, known_evals={})[0] - - -class JacobianCasadi(Jacobian): - "Returns information about the jacobian at time t and state y, with CasADi" - - def __call__(self, t, y): - y = y[:, np.newaxis] - y = add_external(y, self.y_pad, self.y_ext) - return self.jac_fn(t, y) diff --git a/pybamm/solvers/scikits_dae_solver.py b/pybamm/solvers/scikits_dae_solver.py index 9ac0b39799..a461553f54 100644 --- a/pybamm/solvers/scikits_dae_solver.py +++ b/pybamm/solvers/scikits_dae_solver.py @@ -15,7 +15,7 @@ scikits_odes_spec.loader.exec_module(scikits_odes) -class ScikitsDaeSolver(pybamm.DaeSolver): +class ScikitsDaeSolver(pybamm.BaseSolver): """Solve a discretised model, using scikits.odes. Parameters @@ -50,40 +50,25 @@ def __init__( super().__init__(method, rtol, atol, root_method, root_tol, max_steps) self.name = "Scikits DAE solver ({})".format(method) - def integrate( - self, - residuals, - y0, - t_eval, - events=None, - mass_matrix=None, - jacobian=None, - model=None, - ): + def _integrate(self, model, t_eval, inputs=None): """ - Solve a DAE model defined by residuals with initial conditions y0. + Solve a model defined by dydt with initial conditions y0. Parameters ---------- - residuals : method - A function that takes in t, y and ydot and returns the residuals of the - equations - y0 : numeric type - The initial conditions - t_eval : numeric type - The times at which to compute the solution - events : method, optional - A function that takes in t and y and returns conditions for the solver to - stop - mass_matrix : array_like, optional - The (sparse) mass matrix for the chosen spatial method. - jacobian : method, optional - A function that takes in t and y and returns the Jacobian. If - None, the solver will approximate the Jacobian. - (see `SUNDIALS docs. `). model : :class:`pybamm.BaseModel` The model whose solution to calculate. + t_eval : numeric type + The times at which to compute the solution + inputs : dict, optional + Any input parameters to pass to the model when solving + """ + residuals = model.residuals_eval + y0 = model.y0 + events = model.events_eval + jacobian = model.jacobian_eval + mass_matrix = model.mass_matrix.entries def eqsres(t, y, ydot, return_residuals): return_residuals[:] = residuals(t, y, ydot) diff --git a/pybamm/solvers/scikits_ode_solver.py b/pybamm/solvers/scikits_ode_solver.py index 1f8b78b5e8..cda731d4f8 100644 --- a/pybamm/solvers/scikits_ode_solver.py +++ b/pybamm/solvers/scikits_ode_solver.py @@ -19,7 +19,7 @@ def have_scikits_odes(): return scikits_odes_spec is not None -class ScikitsOdeSolver(pybamm.OdeSolver): +class ScikitsOdeSolver(pybamm.BaseSolver): """Solve a discretised model, using scikits.odes. Parameters @@ -40,33 +40,27 @@ def __init__(self, method="cvode", rtol=1e-6, atol=1e-6, linsolver="dense"): super().__init__(method, rtol, atol) self.linsolver = linsolver + self.ode_solver = True self.name = "Scikits ODE solver ({})".format(method) - def integrate( - self, derivs, y0, t_eval, events=None, mass_matrix=None, jacobian=None - ): + def _integrate(self, model, t_eval, inputs=None): """ Solve a model defined by dydt with initial conditions y0. Parameters ---------- - derivs : method - A function that takes in t and y and returns the time-derivative dydt - y0 : numeric type - The initial conditions + model : :class:`pybamm.BaseModel` + The model whose solution to calculate. t_eval : numeric type The times at which to compute the solution - events : method, optional - A function that takes in t and y and returns conditions for the solver to - stop - mass_matrix : array_like, optional - The (sparse) mass matrix for the chosen spatial method. - jacobian : method, optional - A function that takes in t and y and returns the Jacobian. If - None, the solver will approximate the Jacobian. - (see `SUNDIALS docs. `). + inputs : dict, optional + Any input parameters to pass to the model when solving """ + derivs = model.rhs_eval + y0 = model.y0 + events = model.events_eval + jacobian = model.jacobian_eval def eqsydot(t, y, return_ydot): return_ydot[:] = derivs(t, y) diff --git a/pybamm/solvers/scipy_solver.py b/pybamm/solvers/scipy_solver.py index fd2af8d763..8ac16eafba 100644 --- a/pybamm/solvers/scipy_solver.py +++ b/pybamm/solvers/scipy_solver.py @@ -7,8 +7,8 @@ import numpy as np -class ScipySolver(pybamm.OdeSolver): - """Solve a discretised model, using scipy.integrate.solve_ivp. +class ScipySolver(pybamm.BaseSolver): + """Solve a discretised model, using scipy._integrate.solve_ivp. Parameters ---------- @@ -22,31 +22,22 @@ class ScipySolver(pybamm.OdeSolver): def __init__(self, method="BDF", rtol=1e-6, atol=1e-6): super().__init__(method, rtol, atol) + self.ode_solver = True self.name = "Scipy solver ({})".format(method) - def integrate( - self, derivs, y0, t_eval, events=None, mass_matrix=None, jacobian=None - ): + def _integrate(self, model, t_eval, inputs=None): """ Solve a model defined by dydt with initial conditions y0. Parameters ---------- - derivs : method - A function that takes in t (size (1,)), y (size (n,)) - and returns the time-derivative dydt (size (n,)) - y0 : :class:`numpy.array`, size (n,) - The initial conditions + model : :class:`pybamm.BaseModel` + The model whose solution to calculate. t_eval : :class:`numpy.array`, size (k,) The times at which to compute the solution - events : method, optional - A function that takes in t and y and returns conditions for the solver to - stop - mass_matrix : array_like, optional - The (sparse) mass matrix for the chosen spatial method. - jacobian : method, optional - A function that takes in t and y and returns the Jacobian. If - None, the solver will approximate the Jacobian. + inputs : dict, optional + Any input parameters to pass to the model when solving + Returns ------- object @@ -59,19 +50,19 @@ def integrate( # check for user-supplied Jacobian implicit_methods = ["Radau", "BDF", "LSODA"] if np.any([self.method in implicit_methods]): - if jacobian: - extra_options.update({"jac": jacobian}) + if model.jacobian_eval: + extra_options.update({"jac": model.jacobian_eval}) # make events terminal so that the solver stops when they are reached - if events: - for event in events: + if model.events_eval: + for event in model.events_eval: event.terminal = True - extra_options.update({"events": events}) + extra_options.update({"events": model.events_eval}) sol = it.solve_ivp( - derivs, + model.rhs_eval, (t_eval[0], t_eval[-1]), - y0, + model.y0, t_eval=t_eval, method=self.method, dense_output=True, diff --git a/pybamm/solvers/solution.py b/pybamm/solvers/solution.py index a25d53d1a5..6c73438dc0 100644 --- a/pybamm/solvers/solution.py +++ b/pybamm/solvers/solution.py @@ -1,7 +1,11 @@ # # Solution class # +import numbers import numpy as np +import pickle +import pybamm +from collections import defaultdict class Solution(object): @@ -26,12 +30,24 @@ class Solution(object): """ - def __init__(self, t, y, t_event, y_event, termination): + def __init__(self, t, y, t_event=None, y_event=None, termination="final time"): self.t = t self.y = y self.t_event = t_event self.y_event = y_event self.termination = termination + # initialize empty inputs and model, to be populated later + self.inputs = {} + self._model = None + + # initiaize empty variables and data + self._variables = {} + self.data = {} + + # initialize empty known evals + self.known_evals = defaultdict(dict) + for time in t: + self.known_evals[time] = {} @property def t(self): @@ -53,6 +69,31 @@ def y(self, value): "Updates the solution values" self._y = value + @property + def inputs(self): + "Values of the inputs" + return self._inputs + + @inputs.setter + def inputs(self, inputs): + "Updates the input values" + self._inputs = {} + for name, inp in inputs.items(): + if isinstance(inp, numbers.Number): + inp = inp * np.ones_like(self.t) + self._inputs[name] = inp + + @property + def model(self): + "Model used for solution" + return self._model + + @model.setter + def model(self, value): + "Updates the model" + assert isinstance(value, pybamm.BaseModel) + self._model = value + @property def t_event(self): "Time at which the event happens" @@ -83,6 +124,11 @@ def termination(self, value): "Updates the reason for termination" self._termination = value + def __add__(self, other): + "See :meth:`Solution.append`" + self.append(other) + return self + def append(self, solution): """ Appends solution.t and solution.y onto self.t and self.y. @@ -91,10 +137,93 @@ def append(self, solution): and self.y[:, -1] is equal to solution.y[:, 0]). """ + # Update t, y and inputs self.t = np.concatenate((self.t, solution.t[1:])) self.y = np.concatenate((self.y, solution.y[:, 1:]), axis=1) + for name, inp in self.inputs.items(): + solution_inp = solution.inputs[name] + if isinstance(solution_inp, numbers.Number): + solution_inp = solution_inp * np.ones_like(solution.t) + self.inputs[name] = np.concatenate((inp, solution_inp[1:])) + # Update solution time self.solve_time += solution.solve_time + # Update termination + self.termination = solution.termination + self.t_event = solution.t_event + self.y_event = solution.y_event + + # Update known_evals + for t, evals in solution.known_evals.items(): + self.known_evals[t].update(evals) + # Recompute existing variables + for var in self._variables.keys(): + self.update(var) @property def total_time(self): return self.set_up_time + self.solve_time + + def update(self, variables): + """Add ProcessedVariables to the dictionary of variables in the solution""" + # Convert single entry to list + if isinstance(variables, str): + variables = [variables] + # Process + for key in variables: + pybamm.logger.debug("Post-processing {}".format(key)) + var = pybamm.ProcessedVariable( + self.model.variables[key], self, self.known_evals + ) + + # Update known_evals in order to process any other variables faster + for t in var.known_evals: + self.known_evals[t].update(var.known_evals[t]) + + # Save variable and data + self._variables[key] = var + self.data[key] = var.data + + def __getitem__(self, key): + """Read a variable from the solution. Variables are created 'just in time', i.e. + only when they are called. + + Parameters + ---------- + key : str + The name of the variable + + Returns + ------- + :class:`pybamm.ProcessedVariable` + A variable that can be evaluated at any time or spatial point. The + underlying data for this variable is available in its attribute ".data" + """ + + try: + # Try getting item + # return it if it exists + return self._variables[key] + except KeyError: + # otherwise create it, save it and then return it + self.update(key) + return self._variables[key] + + def save(self, filename): + """Save the whole solution using pickle""" + # No warning here if len(self.data)==0 as solution can be loaded + # and used to process new variables + with open(filename, "wb") as f: + pickle.dump(self, f, pickle.HIGHEST_PROTOCOL) + + def save_data(self, filename): + """Save solution data only (raw arrays) using pickle""" + if len(self.data) == 0: + raise ValueError( + """Solution does not have any data. Add variables by calling + 'solution.update', e.g. + 'solution.update(["Terminal voltage [V]", "Current [A]"])' + and then save""" + ) + with open(filename, "wb") as f: + pickle.dump(self.data, f, pickle.HIGHEST_PROTOCOL) + diff --git a/pybamm/spatial_methods/finite_volume.py b/pybamm/spatial_methods/finite_volume.py index 900b4b6e61..3533b1f86e 100644 --- a/pybamm/spatial_methods/finite_volume.py +++ b/pybamm/spatial_methods/finite_volume.py @@ -71,22 +71,27 @@ def gradient(self, symbol, discretised_symbol, boundary_conditions): # Discretise symbol domain = symbol.domain - # Add boundary conditions, if defined + # Add Dirichlet boundary conditions, if defined if symbol.id in boundary_conditions: bcs = boundary_conditions[symbol.id] - # add ghost nodes - discretised_symbol = self.add_ghost_nodes(symbol, discretised_symbol, bcs) - # edit domain - domain = ( - [domain[0] + "_left ghost cell"] - + domain - + [domain[-1] + "_right ghost cell"] - ) + if any(bc[1] == "Dirichlet" for bc in bcs.values()): + # add ghost nodes and update domain + discretised_symbol, domain = self.add_ghost_nodes( + symbol, discretised_symbol, bcs + ) # note in 1D spherical grad and normal grad are the same gradient_matrix = self.gradient_matrix(domain) + # Multiply by gradient matrix out = gradient_matrix @ discretised_symbol + + # Add Neumann boundary conditions, if defined + if symbol.id in boundary_conditions: + bcs = boundary_conditions[symbol.id] + if any(bc[1] == "Neumann" for bc in bcs.values()): + out = self.add_neumann_values(symbol, out, bcs, domain) + return out def preprocess_external_variables(self, var): @@ -303,8 +308,7 @@ def indefinite_integral(self, child, discretised_child): # only change the diveregence (childs here have grad and no div) out = integration_matrix @ discretised_child - out.domain = child.domain - out.auxiliary_domains = child.auxiliary_domains + out.copy_domains(child) return out @@ -406,8 +410,7 @@ def delta_function(self, symbol, discretised_symbol): # Return delta function, keep domains delta_fn = pybamm.Matrix(domain_width / dx * matrix) * discretised_symbol - delta_fn.domain = symbol.domain - delta_fn.auxiliary_domains = symbol.auxiliary_domains + delta_fn.copy_domains(symbol) return delta_fn @@ -451,8 +454,10 @@ def internal_neumann_condition( # Remove domains to avoid clash left_domain = left_symbol_disc.domain right_domain = right_symbol_disc.domain - left_symbol_disc.domain = [] - right_symbol_disc.domain = [] + left_auxiliary_domains = left_symbol_disc.auxiliary_domains + right_auxiliary_domains = right_symbol_disc.auxiliary_domains + left_symbol_disc.clear_domains() + right_symbol_disc.clear_domains() # Finite volume derivative dy = right_matrix @ right_symbol_disc - left_matrix @ left_symbol_disc @@ -461,6 +466,8 @@ def internal_neumann_condition( # Change domains back left_symbol_disc.domain = left_domain right_symbol_disc.domain = right_domain + left_symbol_disc.auxiliary_domains = left_auxiliary_domains + right_symbol_disc.auxiliary_domains = right_auxiliary_domains return dy / dx @@ -508,20 +515,16 @@ def add_ghost_nodes(self, symbol, discretised_symbol, bcs): where y1 is the value of the first node. Similarly for the right-hand boundary condition. - For Dirichlet bcs, for a boundary condition "y = a at the left-hand boundary", - we concatenate a ghost node to the start of the vector y with value "2*a - y1" - where y1 is the value of the first node. - Similarly for the right-hand boundary condition. - - For Neumann bcs, for a boundary condition "dy/dx = b at the left-hand boundary", - we concatenate a ghost node to the start of the vector y with value "b*h + y1" - where y1 is the value of the first node and h is the mesh size. - Similarly for the right-hand boundary condition. + For Neumann bcs no ghost nodes are added. Instead, the exact value provided + by the boundary condition is used at the cell edge when calculating the + gradient (see :meth:`pybamm.FiniteVolume.add_neumann_values`). Parameters ---------- - domain : list of strings - The domain of the symbol for which to add ghost nodes + symbol : :class:`pybamm.SpatialVariable` + The variable to be discretised + discretised_symbol : :class:`pybamm.Vector` + Contains the discretised variable bcs : dict of tuples (:class:`pybamm.Scalar`, str) Dictionary (with keys "left" and "right") of boundary conditions. Each boundary condition consists of a value and a flag indicating its type @@ -529,13 +532,14 @@ def add_ghost_nodes(self, symbol, discretised_symbol, bcs): Returns ------- - :class:`pybamm.Symbol` (shape (n+2, n)) + :class:`pybamm.Symbol` `Matrix @ discretised_symbol + bcs_vector`. When evaluated, this gives the discretised_symbol, with appropriate ghost nodes concatenated at each end. """ # get relevant grid points - submesh_list = self.mesh.combine_submeshes(*symbol.domain) + domain = symbol.domain + submesh_list = self.mesh.combine_submeshes(*domain) # Prepare sizes and empty bcs_vector n = submesh_list[0].npts @@ -546,53 +550,70 @@ def add_ghost_nodes(self, symbol, discretised_symbol, bcs): lbc_value, lbc_type = bcs["left"] rbc_value, rbc_type = bcs["right"] - for i in range(sec_pts): + # Add ghost node(s) to domain where necessary and count number of + # Dirichlet boundary conditions + n_bcs = 0 + if lbc_type == "Dirichlet": + domain = [domain[0] + "_left ghost cell"] + domain + n_bcs += 1 + if rbc_type == "Dirichlet": + domain = domain + [domain[-1] + "_right ghost cell"] + n_bcs += 1 + + # Calculate values for ghost nodes for any Dirichlet boundary conditions + if lbc_type == "Dirichlet": + lbc_sub_matrix = coo_matrix(([1], ([0], [0])), shape=(n + n_bcs, 1)) + lbc_matrix = csr_matrix(kron(eye(sec_pts), lbc_sub_matrix)) if lbc_value.evaluates_to_number(): - lbc_i = lbc_value - else: - lbc_i = lbc_value[i] - if rbc_value.evaluates_to_number(): - rbc_i = rbc_value + left_ghost_constant = 2 * lbc_value * pybamm.Vector(np.ones(sec_pts)) else: - rbc_i = rbc_value[i] - if lbc_type == "Dirichlet": - left_ghost_constant = 2 * lbc_i - elif lbc_type == "Neumann": - dx = 2 * (submesh_list[0].nodes[0] - submesh_list[0].edges[0]) - left_ghost_constant = -dx * lbc_i - else: - raise ValueError( - "boundary condition must be Dirichlet or Neumann, not '{}'".format( - lbc_type - ) + left_ghost_constant = 2 * lbc_value + lbc_vector = pybamm.Matrix(lbc_matrix) @ left_ghost_constant + elif lbc_type == "Neumann": + lbc_vector = pybamm.Vector(np.zeros((n + n_bcs) * sec_pts)) + else: + raise ValueError( + "boundary condition must be Dirichlet or Neumann, not '{}'".format( + lbc_type ) - if rbc_type == "Dirichlet": - right_ghost_constant = 2 * rbc_i - elif rbc_type == "Neumann": - dx = 2 * (submesh_list[0].edges[-1] - submesh_list[0].nodes[-1]) - right_ghost_constant = dx * rbc_i + ) + + if rbc_type == "Dirichlet": + rbc_sub_matrix = coo_matrix( + ([1], ([n + n_bcs - 1], [0])), shape=(n + n_bcs, 1) + ) + rbc_matrix = csr_matrix(kron(eye(sec_pts), rbc_sub_matrix)) + if rbc_value.evaluates_to_number(): + right_ghost_constant = 2 * rbc_value * pybamm.Vector(np.ones(sec_pts)) else: - raise ValueError( - "boundary condition must be Dirichlet or Neumann, not '{}'".format( - rbc_type - ) + right_ghost_constant = 2 * rbc_value + rbc_vector = pybamm.Matrix(rbc_matrix) @ right_ghost_constant + elif rbc_type == "Neumann": + rbc_vector = pybamm.Vector(np.zeros((n + n_bcs) * sec_pts)) + else: + raise ValueError( + "boundary condition must be Dirichlet or Neumann, not '{}'".format( + rbc_type ) - # concatenate - bcs_vector = pybamm.NumpyConcatenation( - bcs_vector, - left_ghost_constant, - pybamm.Vector(np.zeros(n)), - right_ghost_constant, ) + bcs_vector = lbc_vector + rbc_vector + # Need to match the domain. E.g. in the case of the boundary condition + # on the particle, the gradient has domain particle but the bcs_vector + # has domain electrode, since it is a function of the macroscopic variables + bcs_vector.copy_domains(discretised_symbol) + # Make matrix to calculate ghost nodes - bc_factors = {"Dirichlet": -1, "Neumann": 1} - left_factor = bc_factors[lbc_type] - right_factor = bc_factors[rbc_type] # coo_matrix takes inputs (data, (row, col)) and puts data[i] at the point # (row[i], col[i]) for each index of data. - left_ghost_vector = coo_matrix(([left_factor], ([0], [0])), shape=(1, n)) - right_ghost_vector = coo_matrix(([right_factor], ([0], [n - 1])), shape=(1, n)) + if lbc_type == "Dirichlet": + left_ghost_vector = coo_matrix(([-1], ([0], [0])), shape=(1, n)) + else: + left_ghost_vector = None + if rbc_type == "Dirichlet": + right_ghost_vector = coo_matrix(([-1], ([0], [n - 1])), shape=(1, n)) + else: + right_ghost_vector = None sub_matrix = vstack([left_ghost_vector, eye(n), right_ghost_vector]) # repeat matrix for secondary dimensions @@ -602,7 +623,123 @@ def add_ghost_nodes(self, symbol, discretised_symbol, bcs): # issue matrix = csr_matrix(kron(eye(sec_pts), sub_matrix)) - return pybamm.Matrix(matrix) @ discretised_symbol + bcs_vector + new_symbol = pybamm.Matrix(matrix) @ discretised_symbol + bcs_vector + + return new_symbol, domain + + def add_neumann_values(self, symbol, discretised_gradient, bcs, domain): + """ + Add the known values of the gradient from Neumann boundary conditions to + the discretised gradient. + + Dirichlet bcs are implemented using ghost nodes, see + :meth:`pybamm.FiniteVolume.add_ghost_nodes`. + + Parameters + ---------- + symbol : :class:`pybamm.SpatialVariable` + The variable to be discretised + discretised_gradient : :class:`pybamm.Vector` + Contains the discretised gradient of symbol + bcs : dict of tuples (:class:`pybamm.Scalar`, str) + Dictionary (with keys "left" and "right") of boundary conditions. Each + boundary condition consists of a value and a flag indicating its type + (e.g. "Dirichlet") + domain : list of strings + The domain of the gradient of the symbol (may include ghost nodes) + + Returns + ------- + :class:`pybamm.Symbol` + `Matrix @ discretised_gradient + bcs_vector`. When evaluated, this gives the + discretised_gradient, with the values of the Neumann boundary conditions + concatenated at each end (if given). + + """ + # get relevant grid points + submesh_list = self.mesh.combine_submeshes(*domain) + + # Prepare sizes and empty bcs_vector + n = submesh_list[0].npts - 1 + sec_pts = len(submesh_list) + + lbc_value, lbc_type = bcs["left"] + rbc_value, rbc_type = bcs["right"] + + # Count number of Neumann boundary conditions + n_bcs = 0 + if lbc_type == "Neumann": + n_bcs += 1 + if rbc_type == "Neumann": + n_bcs += 1 + + # Add any values from Neumann boundary conditions to the bcs vector + if lbc_type == "Neumann": + lbc_sub_matrix = coo_matrix(([1], ([0], [0])), shape=(n + n_bcs, 1)) + lbc_matrix = csr_matrix(kron(eye(sec_pts), lbc_sub_matrix)) + if lbc_value.evaluates_to_number(): + left_bc = lbc_value * pybamm.Vector(np.ones(sec_pts)) + else: + left_bc = lbc_value + lbc_vector = pybamm.Matrix(lbc_matrix) @ left_bc + elif lbc_type == "Dirichlet": + lbc_vector = pybamm.Vector(np.zeros((n + n_bcs) * sec_pts)) + else: + raise ValueError( + "boundary condition must be Dirichlet or Neumann, not '{}'".format( + lbc_type + ) + ) + if rbc_type == "Neumann": + rbc_sub_matrix = coo_matrix( + ([1], ([n + n_bcs - 1], [0])), shape=(n + n_bcs, 1) + ) + rbc_matrix = csr_matrix(kron(eye(sec_pts), rbc_sub_matrix)) + if rbc_value.evaluates_to_number(): + right_bc = rbc_value * pybamm.Vector(np.ones(sec_pts)) + else: + right_bc = rbc_value + rbc_vector = pybamm.Matrix(rbc_matrix) @ right_bc + elif rbc_type == "Dirichlet": + rbc_vector = pybamm.Vector(np.zeros((n + n_bcs) * sec_pts)) + else: + raise ValueError( + "boundary condition must be Dirichlet or Neumann, not '{}'".format( + rbc_type + ) + ) + + bcs_vector = lbc_vector + rbc_vector + # Need to match the domain. E.g. in the case of the boundary condition + # on the particle, the gradient has domain particle but the bcs_vector + # has domain electrode, since it is a function of the macroscopic variables + bcs_vector.domain = discretised_gradient.domain + bcs_vector.auxiliary_domains = discretised_gradient.auxiliary_domains + + # Make matrix which makes "gaps" in the the discretised gradient into + # which the known Neumann values will be added. E.g. in 1D if the left + # boundary condition is Dirichlet and the right Neumann, this matrix will + # act to append a zero to the end of the discretsied gradient + if lbc_type == "Neumann": + left_vector = csr_matrix((1, n)) + else: + left_vector = None + if rbc_type == "Neumann": + right_vector = csr_matrix((1, n)) + else: + right_vector = None + sub_matrix = vstack([left_vector, eye(n), right_vector]) + + # repeat matrix for secondary dimensions + # Convert to csr_matrix so that we can take the index (row-slicing), which is + # not supported by the default kron format + # Note that this makes column-slicing inefficient, but this should not be an + # issue + matrix = csr_matrix(kron(eye(sec_pts), sub_matrix)) + + new_gradient = pybamm.Matrix(matrix) @ discretised_gradient + bcs_vector + + return new_gradient def boundary_value_or_flux(self, symbol, discretised_child, bcs=None): """ @@ -820,11 +957,9 @@ def boundary_value_or_flux(self, symbol, discretised_child, bcs=None): # Return boundary value with domain given by symbol boundary_value = pybamm.Matrix(matrix) @ discretised_child - boundary_value.domain = symbol.domain - boundary_value.auxiliary_domains = symbol.auxiliary_domains + boundary_value.copy_domains(symbol) - additive.domain = symbol.domain - additive.auxiliary_domains = symbol.auxiliary_domains + additive.copy_domains(symbol) boundary_value += additive return boundary_value @@ -832,7 +967,11 @@ def boundary_value_or_flux(self, symbol, discretised_child, bcs=None): def process_binary_operators(self, bin_op, left, right, disc_left, disc_right): """Discretise binary operators in model equations. Performs appropriate averaging of diffusivities if one of the children is a gradient operator, so - that discretised sizes match up. + that discretised sizes match up. For this averaging we use the harmonic + mean [1]. + + [1] Recktenwald, Gerald. "The control-volume finite-difference approximation to + the diffusion equation." (2012). Parameters ---------- @@ -868,11 +1007,23 @@ def process_binary_operators(self, bin_op, left, right, disc_left, disc_right): elif left_evaluates_on_edges == right_evaluates_on_edges: pass # If only left child evaluates on edges, map right child onto edges + # using the harmonic mean if the left child is a gradient (i.e. this + # binary operator represents a flux) elif left_evaluates_on_edges and not right_evaluates_on_edges: - disc_right = self.node_to_edge(disc_right) + if isinstance(left, pybamm.Gradient): + method = "harmonic" + else: + method = "arithmetic" + disc_right = self.node_to_edge(disc_right, method=method) # If only right child evaluates on edges, map left child onto edges + # using the harmonic mean if the right child is a gradient (i.e. this + # binary operator represents a flux) elif right_evaluates_on_edges and not left_evaluates_on_edges: - disc_left = self.node_to_edge(disc_left) + if isinstance(right, pybamm.Gradient): + method = "harmonic" + else: + method = "arithmetic" + disc_left = self.node_to_edge(disc_left, method=method) # Return new binary operator with appropriate class out = bin_op.__class__(disc_left, disc_right) return out @@ -904,27 +1055,28 @@ def concatenation(self, disc_children): ) return pybamm.DomainConcatenation(disc_children, self.mesh) - def edge_to_node(self, discretised_symbol): + def edge_to_node(self, discretised_symbol, method="arithmetic"): """ Convert a discretised symbol evaluated on the cell edges to a discretised symbol evaluated on the cell nodes. See :meth:`pybamm.FiniteVolume.shift` """ - return self.shift(discretised_symbol, "edge to node") + return self.shift(discretised_symbol, "edge to node", method) - def node_to_edge(self, discretised_symbol): + def node_to_edge(self, discretised_symbol, method="arithmetic"): """ Convert a discretised symbol evaluated on the cell nodes to a discretised symbol evaluated on the cell edges. See :meth:`pybamm.FiniteVolume.shift` """ - return self.shift(discretised_symbol, "node to edge") + return self.shift(discretised_symbol, "node to edge", method) - def shift(self, discretised_symbol, shift_key): + def shift(self, discretised_symbol, shift_key, method): """ Convert a discretised symbol evaluated at edges/nodes, to a discretised symbol - evaluated at nodes/edges. - For now we just take the arithemtic mean, though it may be better to take the + evaluated at nodes/edges. Can be the arithmetic mean or the harmonic mean. + + Note: when computing fluxes at cell edges it is better to take the harmonic mean based on [1]. [1] Recktenwald, Gerald. "The control-volume finite-difference approximation to @@ -939,6 +1091,8 @@ def shift(self, discretised_symbol, shift_key): shift_key : str Whether to shift from nodes to edges ("node to edge"), or from edges to nodes ("edge to node") + method : str + Whether to use the "arithmetic" or "harmonic" mean Returns ------- @@ -986,10 +1140,141 @@ def arithmetic_mean(array): return pybamm.Matrix(matrix) @ array + def harmonic_mean(array): + """ + Calculate the harmonic mean of an array using matrix multiplication. + The harmonic mean is computed as + + .. math:: + D_{eff} = \\frac{D_1 D_2}{\\beta D_2 + (1 - \\beta) D_1}, + + where + + .. math:: + \\beta = \\frac{\\Delta x_1}{\\Delta x_2 + \\Delta x_1} + + accounts for the difference in the control volume widths. This is the + definiton from [1], which is the same as that in [2] but with slightly + different notation. + + [1] Torchio, M et al. "LIONSIMBA: A Matlab Framework Based on a Finite + Volume Model Suitable for Li-Ion Battery Design, Simulation, and Control." + (2016). + [2] Recktenwald, Gerald. "The control-volume finite-difference + approximation to the diffusion equation." (2012). + """ + # Create appropriate submesh by combining submeshes in domain + submesh_list = self.mesh.combine_submeshes(*array.domain) + submesh = submesh_list[0] + + # Get second dimension length for use later + second_dim_len = len(submesh_list) + + # Create 1D matrix using submesh + n = submesh.npts + + if shift_key == "node to edge": + # Matrix to compute values at the exterior edges + edges_sub_matrix_left = csr_matrix( + ([1.5, -0.5], ([0, 0], [0, 1])), shape=(1, n) + ) + edges_sub_matrix_center = csr_matrix((n - 1, n)) + edges_sub_matrix_right = csr_matrix( + ([-0.5, 1.5], ([0, 0], [n - 2, n - 1])), shape=(1, n) + ) + edges_sub_matrix = vstack( + [ + edges_sub_matrix_left, + edges_sub_matrix_center, + edges_sub_matrix_right, + ] + ) + + # Generate full matrix from the submatrix + # Convert to csr_matrix so that we can take the index (row-slicing), + # which is not supported by the default kron format + # Note that this makes column-slicing inefficient, but this should + # not be an issue + edges_matrix = csr_matrix(kron(eye(second_dim_len), edges_sub_matrix)) + + # Matrix to extract the node values running from the first node + # to the penultimate node in the primary dimension (D_1 in the + # definiton of the harmonic mean) + sub_matrix_D1 = hstack([eye(n - 1), csr_matrix((n - 1, 1))]) + matrix_D1 = csr_matrix(kron(eye(second_dim_len), sub_matrix_D1)) + D1 = pybamm.Matrix(matrix_D1) @ array + + # Matrix to extract the node values running from the second node + # to the final node in the primary dimension (D_2 in the + # definiton of the harmonic mean) + sub_matrix_D2 = hstack([csr_matrix((n - 1, 1)), eye(n - 1)]) + matrix_D2 = csr_matrix(kron(eye(second_dim_len), sub_matrix_D2)) + D2 = pybamm.Matrix(matrix_D2) @ array + + # Compute weight beta + dx = submesh.d_edges + sub_beta = (dx[:-1] / (dx[1:] + dx[:-1]))[:, np.newaxis] + beta = pybamm.Array(np.kron(np.ones((second_dim_len, 1)), sub_beta)) + + # Compute harmonic mean on internal edges + # Note: add small number to denominator to regularise D_eff + D_eff = D1 * D2 / (D2 * beta + D1 * (1 - beta) + 1e-16) + + # Matrix to pad zeros at the beginning and end of the array where + # the exterior edge values will be added + sub_matrix = vstack( + [csr_matrix((1, n - 1)), eye(n - 1), csr_matrix((1, n - 1))] + ) + + # Generate full matrix from the submatrix + # Convert to csr_matrix so that we can take the index (row-slicing), + # which is not supported by the default kron format + # Note that this makes column-slicing inefficient, but this should + # not be an issue + matrix = csr_matrix(kron(eye(second_dim_len), sub_matrix)) + + return ( + pybamm.Matrix(edges_matrix) @ array + pybamm.Matrix(matrix) @ D_eff + ) + + elif shift_key == "edge to node": + # Matrix to extract the edge values running from the first edge + # to the penultimate edge in the primary dimension (D_1 in the + # definiton of the harmonic mean) + sub_matrix_D1 = hstack([eye(n), csr_matrix((n, 1))]) + matrix_D1 = csr_matrix(kron(eye(second_dim_len), sub_matrix_D1)) + D1 = pybamm.Matrix(matrix_D1) @ array + + # Matrix to extract the edge values running from the second edge + # to the final edge in the primary dimension (D_2 in the + # definiton of the harmonic mean) + sub_matrix_D2 = hstack([csr_matrix((n, 1)), eye(n)]) + matrix_D2 = csr_matrix(kron(eye(second_dim_len), sub_matrix_D2)) + D2 = pybamm.Matrix(matrix_D2) @ array + + # Compute weight beta + dx0 = submesh.nodes[0] - submesh.edges[0] # first edge to node + dxN = submesh.edges[-1] - submesh.nodes[-1] # last node to edge + dx = np.concatenate(([dx0], submesh.d_nodes, [dxN])) + sub_beta = (dx[:-1] / (dx[1:] + dx[:-1]))[:, np.newaxis] + beta = pybamm.Array(np.kron(np.ones((second_dim_len, 1)), sub_beta)) + + # Compute harmonic mean on nodes + # Note: add small number to denominator to regularise D_eff + D_eff = D1 * D2 / (D2 * beta + D1 * (1 - beta) + 1e-16) + + return D_eff + + else: + raise ValueError("shift key '{}' not recognised".format(shift_key)) + # If discretised_symbol evaluates to number there is no need to average if discretised_symbol.evaluates_to_number(): out = discretised_symbol - else: + elif method == "arithmetic": out = arithmetic_mean(discretised_symbol) - + elif method == "harmonic": + out = harmonic_mean(discretised_symbol) + else: + raise ValueError("method '{}' not recognised".format(method)) return out diff --git a/pybamm/spatial_methods/scikit_finite_element.py b/pybamm/spatial_methods/scikit_finite_element.py index 66124df45a..a4d8a7693e 100644 --- a/pybamm/spatial_methods/scikit_finite_element.py +++ b/pybamm/spatial_methods/scikit_finite_element.py @@ -3,7 +3,8 @@ # import pybamm -from scipy.sparse import csr_matrix +from scipy.sparse import csr_matrix, csc_matrix +from scipy.sparse.linalg import inv import numpy as np import skfem @@ -67,10 +68,104 @@ def spatial_variable(self, symbol): return vector def gradient(self, symbol, discretised_symbol, boundary_conditions): - """Matrix-vector multiplication to implement the gradient operator. - See :meth:`pybamm.SpatialMethod.gradient` + """Matrix-vector multiplication to implement the gradient operator. The + gradient w of the function u is approximated by the finite element method + using the same function space as u, i.e. we solve w = grad(u), which + corresponds to the weak form w*v*dx = grad(u)*v*dx, where v is a suitable + test function. + + Parameters + ---------- + symbol: :class:`pybamm.Symbol` + The symbol that we will take the laplacian of. + discretised_symbol: :class:`pybamm.Symbol` + The discretised symbol of the correct size + boundary_conditions : dict + The boundary conditions of the model + ({symbol.id: {"negative tab": neg. tab bc, "positive tab": pos. tab bc}}) + + Returns + ------- + :class: `pybamm.Concatenation` + A concatenation that contains the result of acting the discretised + gradient on the child discretised_symbol. The first column corresponds + to the y-component of the gradient and the second column corresponds + to the z component of the gradient. """ - raise NotImplementedError + domain = symbol.domain[0] + mesh = self.mesh[domain][0] + + # get gradient matrix + grad_y_matrix, grad_z_matrix = self.gradient_matrix(symbol, boundary_conditions) + + # assemble mass matrix (there is no need to zero out entries here, since + # boundary conditions are already accounted for in the governing pde + # for the symbol we are taking the gradient of. we just want to get the + # correct weights) + @skfem.bilinear_form + def mass_form(u, du, v, dv, w): + return u * v + + mass = skfem.asm(mass_form, mesh.basis) + # we need the inverse + mass_inv = pybamm.Matrix(inv(csc_matrix(mass))) + + # compute gradient + grad_y = mass_inv @ (grad_y_matrix @ discretised_symbol) + grad_z = mass_inv @ (grad_z_matrix @ discretised_symbol) + + # create concatenation + grad = pybamm.Concatenation( + grad_y, grad_z, check_domain=False, concat_fun=np.hstack + ) + grad.domain = domain + + return grad + + def gradient_squared(self, symbol, discretised_symbol, boundary_conditions): + """Multiplication to implement the inner product of the gradient operator + with itself. See :meth:`pybamm.SpatialMethod.gradient_squared` + """ + grad = self.gradient(symbol, discretised_symbol, boundary_conditions) + grad_y, grad_z = grad.orphans + return grad_y ** 2 + grad_z ** 2 + + def gradient_matrix(self, symbol, boundary_conditions): + """ + Gradient matrix for finite elements in the appropriate domain. + + Parameters + ---------- + symbol: :class:`pybamm.Symbol` + The symbol for which we want to calculate the gradient matrix + boundary_conditions : dict + The boundary conditions of the model + ({symbol.id: {"negative tab": neg. tab bc, "positive tab": pos. tab bc}}) + + Returns + ------- + :class:`pybamm.Matrix` + The (sparse) finite element gradient matrix for the domain + """ + # get primary domain mesh + domain = symbol.domain[0] + mesh = self.mesh[domain][0] + + # make form for the gradient in the y direction + @skfem.bilinear_form + def gradient_dy(u, du, v, dv, w): + return du[0] * v[0] + + # make form for the gradient in the z direction + @skfem.bilinear_form + def gradient_dz(u, du, v, dv, w): + return du[1] * v[1] + + # assemble the matrices + grad_y = skfem.asm(gradient_dy, mesh.basis) + grad_z = skfem.asm(gradient_dz, mesh.basis) + + return pybamm.Matrix(grad_y), pybamm.Matrix(grad_z) def divergence(self, symbol, discretised_symbol, boundary_conditions): """Matrix-vector multiplication to implement the divergence operator. @@ -151,15 +246,6 @@ def unit_bc_load_form(v, dv, w): return -stiffness_matrix @ discretised_symbol + boundary_load - def gradient_squared(self, symbol, discretised_symbol, boundary_conditions): - """Matrix-vector multiplication to implement the inner product of the - gradient operator with itself. - See :meth:`pybamm.SpatialMethod.gradient_squared` - """ - stiffness_matrix = self.stiffness_matrix(symbol, boundary_conditions) - - return stiffness_matrix @ (discretised_symbol ** 2) - def stiffness_matrix(self, symbol, boundary_conditions): """ Laplacian (stiffness) matrix for finite elements in the appropriate domain. @@ -274,7 +360,7 @@ def boundary_integral(self, child, discretised_child, region): ) out = integration_vector @ discretised_child - out.domain = [] + out.clear_domains() return out def boundary_integral_vector(self, domain, region): diff --git a/pybamm/spatial_methods/spatial_method.py b/pybamm/spatial_methods/spatial_method.py index 8c2e7c44e0..1976b806c0 100644 --- a/pybamm/spatial_methods/spatial_method.py +++ b/pybamm/spatial_methods/spatial_method.py @@ -3,7 +3,7 @@ # import pybamm import numpy as np -from scipy.sparse import eye, kron, coo_matrix, csr_matrix +from scipy.sparse import eye, kron, coo_matrix, csr_matrix, vstack class SpatialMethod: @@ -89,21 +89,40 @@ def broadcast(self, symbol, domain, auxiliary_domains, broadcast_type): The discretised symbol of the correct size for the spatial method """ - primary_pts_for_broadcast = sum( + primary_domain_size = sum( self.mesh[dom][0].npts_for_broadcast for dom in domain ) - full_pts_for_broadcast = sum( + full_domain_size = sum( subdom.npts_for_broadcast for dom in domain for subdom in self.mesh[dom] ) if broadcast_type == "primary": - out = pybamm.Outer( - symbol, pybamm.Vector(np.ones(primary_pts_for_broadcast), domain=domain) + # Make copies of the child stacked on top of each other + sub_vector = np.ones((primary_domain_size, 1)) + if symbol.shape_for_testing == (): + out = symbol * pybamm.Vector(sub_vector) + else: + # Repeat for secondary points + matrix = csr_matrix(kron(eye(symbol.shape_for_testing[0]), sub_vector)) + out = pybamm.Matrix(matrix) @ symbol + out.domain = domain + elif broadcast_type == "secondary": + secondary_domain_size = sum( + self.mesh[dom][0].npts_for_broadcast + for dom in auxiliary_domains["secondary"] ) - + kron_size = full_domain_size // primary_domain_size + # Symbol may be on edges so need to calculate size carefully + symbol_primary_size = symbol.shape[0] // kron_size + # Make copies of the child stacked on top of each other + identity = eye(symbol_primary_size) + sub_matrix = vstack([identity for _ in range(secondary_domain_size)]) + # Repeat for secondary points + matrix = csr_matrix(kron(eye(kron_size), sub_matrix)) + out = pybamm.Matrix(matrix) @ symbol elif broadcast_type == "full": - out = symbol * pybamm.Vector(np.ones(full_pts_for_broadcast), domain=domain) + out = symbol * pybamm.Vector(np.ones(full_domain_size), domain=domain) out.auxiliary_domains = auxiliary_domains return out @@ -185,7 +204,6 @@ def gradient_squared(self, symbol, discretised_symbol, boundary_conditions): The symbol that we will take the gradient of. discretised_symbol: :class:`pybamm.Symbol` The discretised symbol of the correct size - boundary_conditions : dict The boundary conditions of the model ({symbol.id: {"left": left bc, "right": right bc}}) @@ -343,7 +361,7 @@ def boundary_value_or_flux(self, symbol, discretised_child, bcs=None): out = bv_vector @ discretised_child # boundary value removes domain - out.domain = [] + out.clear_domains() return out def mass_matrix(self, symbol, boundary_conditions): diff --git a/pybamm/spatial_methods/zero_dimensional_method.py b/pybamm/spatial_methods/zero_dimensional_method.py index bf8397be44..abdac2a256 100644 --- a/pybamm/spatial_methods/zero_dimensional_method.py +++ b/pybamm/spatial_methods/zero_dimensional_method.py @@ -23,6 +23,13 @@ def __init__(self, options=None): def build(self, mesh): self._mesh = mesh + def boundary_value_or_flux(self, symbol, discretised_child, bcs=None): + """ + In 0D, the boundary value is the identity operator. + See :meth:`SpatialMethod.boundary_value_or_flux` + """ + return discretised_child + def mass_matrix(self, symbol, boundary_conditions): """ Calculates the mass matrix for a spatial method. Since the spatial method is diff --git a/pybamm/util.py b/pybamm/util.py index eaec238a99..045b85f8ce 100644 --- a/pybamm/util.py +++ b/pybamm/util.py @@ -10,7 +10,9 @@ import sys import timeit import pathlib +import pickle import pybamm +import Levenshtein from collections import defaultdict @@ -19,6 +21,40 @@ def root_dir(): return str(pathlib.Path(pybamm.__path__[0]).parent) +class FuzzyDict(dict): + def get_best_matches(self, key): + "Get best matches from keys" + key = key.lower() + best_three = [] + lowest_score = 0 + for k in self.keys(): + score = Levenshtein.ratio(k.lower(), key) + # Start filling out the list + if len(best_three) < 3: + best_three.append((k, score)) + # Sort once the list has three elements, using scores + if len(best_three) == 3: + best_three.sort(key=lambda x: x[1], reverse=True) + lowest_score = best_three[-1][1] + # Once list is full, start checking new entries + else: + if score > lowest_score: + # Replace last element with new entry + best_three[-1] = (k, score) + # Sort and update lowest score + best_three.sort(key=lambda x: x[1], reverse=True) + lowest_score = best_three[-1][1] + + return [x[0] for x in best_three] + + def __getitem__(self, key): + try: + return super().__getitem__(key) + except KeyError: + best_matches = self.get_best_matches(key) + raise KeyError(f"'{key}' not found. Best matches are {best_matches}") + + class Timer(object): """ Provides accurate timing. @@ -186,3 +222,11 @@ def get_infinite_nested_dict(): True """ return defaultdict(get_infinite_nested_dict) + + +def load(filename): + "Load a saved object" + with open(filename, "rb") as f: + obj = pickle.load(f) + return obj + diff --git a/results/2019_08_sulzer_thesis/README.md b/results/2019_08_sulzer_thesis/README.md deleted file mode 100644 index b0cb9bac69..0000000000 --- a/results/2019_08_sulzer_thesis/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Results: Valentin Sulzer's Thesis - -This folder contains the scripts used to generate results for Valentin Sulzer's thesis -The plots were formatted using a formatting file `matplotlibrc` identical to [this one](_matplotlibrc) (but not included in the GitHub repo to avoid clashes with different formatting files). - -## Chapter 2 - Model - -- (Dimensionless and dimensional) [parameters](print_lead_acid_parameters.py) - - Function to print out all standard parameters to a text file, including dependence on C-rate for dimensionless parameters - -## Chapter 3 - Simplified models for slow discharge - -- [Effect of capacitance](effect_of_capacitance.py): comparison of the one-dimensional model with and without capacitance terms included - - Voltages - - Time taken for full solution -- [Discharge asymptotics results](lead_acid_discharge.py): - - Comparison of voltage curves for the full porous electrode model and a hierarchy of simplified models (Leading-Order Quasi-Static, First-Order Quasi-Static and Composite) - - Comparison of variable profiles at various times for the full and reduced-order models - - Electrolyte concentration - - Electrolyte potential - - Interfacial current density - - Errors compared to full model and time taken to solve each model - - Decomposition of voltage into constituent overpotentials -- [Effect of convection](effect_of_convection.py): - - Voltage at various C-rates with and without convection - - Velocity profiles - - Increasing the volume changes to see more of an effect -- [Effect of side reactions](effect_of_side_reactions.py): - - Voltage at various C-rates with and without side reactions - - Interfacial current densities - - Electrolyte concentrations -- [Charge asymptotics results](lead_acid_charge.py): - - Comparison of voltage curves for the full porous electrode model and a hierarchy of simplified models (Leading-Order Quasi-Static, First-Order Quasi-Static and Composite) - - Comparison of average interfacial current densities for each of the side reactions for the full porous electrode model and a hierarchy of simplified models (Leading-Order Quasi-Static, First-Order Quasi-Static and Composite) - - Comparison of variable profiles at various times for the full and reduced-order models - - Electrolyte concentration - - Oxygen concentration - - Decomposition of voltage into constituent overpotentials -- [Self-discharge](self_discharge.py): - - Self-discharge voltages - -## Chapter 4 - Small aspect ratio cells - -- 2+1D model - - Model and capacitance formulation - - Concentrations and potentials as functions of x, y, z - - Times taken -- Further asymptotics diff --git a/results/2019_08_sulzer_thesis/_matplotlibrc b/results/2019_08_sulzer_thesis/_matplotlibrc deleted file mode 100644 index 7e8738810d..0000000000 --- a/results/2019_08_sulzer_thesis/_matplotlibrc +++ /dev/null @@ -1,27 +0,0 @@ -# Set a 2/1 aspect ratio with automatic layout. -figure.figsize: 6.4, 4. - -# Use LaTeX fonts -font.family: serif -font.serif: ComputerModern -text.usetex: true - -# Backend options (42: use TrueType fonts) -pdf.fonttype: 42 -ps.fonttype: 42 - -# Set large font sizes for paper figures -axes.labelsize: 11 -axes.titlesize: 11 -xtick.labelsize: 11 -ytick.labelsize: 11 -legend.fontsize: 11 -legend.numpoints: 1 -legend.scatterpoints: 1 - -# Set lines and ticks according to the `seaborn-paper` style sheet -grid.linewidth: 0.8 -lines.linewidth: 1.4 -patch.linewidth: 0.24 -lines.markersize: 5.6 -lines.markeredgewidth: 0 diff --git a/results/2019_08_sulzer_thesis/effect_of_capacitance.py b/results/2019_08_sulzer_thesis/effect_of_capacitance.py deleted file mode 100644 index c182b928a9..0000000000 --- a/results/2019_08_sulzer_thesis/effect_of_capacitance.py +++ /dev/null @@ -1,159 +0,0 @@ -# -# Simulations: discharge of a lead-acid battery -# -import argparse -import matplotlib.pyplot as plt -import numpy as np -import pickle -import pybamm -import shared_plotting -from config import OUTPUT_DIR -from mpl_toolkits.axes_grid1.inset_locator import inset_axes -from shared_solutions import model_comparison, convergence_study - -save_folder = "results/2019_08_sulzer_thesis/data/capacitance_results/" - - -def plot_voltages(all_variables, t_eval): - linestyles = ["k-", "b-.", "r--"] - _, axes = shared_plotting.plot_voltages( - all_variables, t_eval, linestyles=linestyles, figsize=(6.4, 4) - ) - - # Add inset plot - for k, (Crate, models_variables) in enumerate(all_variables.items()): - ax = axes.flat[k] - y_min = ax.get_ylim()[0] - ax.set_ylim([y_min, 13.6]) - inset = inset_axes(ax, width="40%", height="30%", loc=1, borderpad=0) - for j, variables in enumerate(models_variables.values()): - time = variables["Time [s]"](t_eval) - capacitance_indices = np.where(time < 50) - time = time[capacitance_indices] - voltage = variables["Battery voltage [V]"](t_eval)[capacitance_indices] - inset.plot(time, voltage, linestyles[j]) - inset.set_xlabel("Time [s]", fontsize=9) - inset.set_xlim([0, 3]) - inset.tick_params(axis="both", which="major", labelsize=9) - - file_name = "capacitance_voltage_comparison.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_errors(all_variables, t_eval, Crates): - # Linestyles - linestyles = ["k-", "b-.", "r--"] - # Only use some Crates - all_variables = {k: v for k, v in all_variables.items() if k in Crates} - # Plot - fig, ax = plt.subplots(1, 1, figsize=(6.4, 4)) - for k, (Crate, models_variables) in enumerate(all_variables.items()): - ax.set_xlabel("Time [h]") - ax.set_ylabel("Error [V]") - - for j, (model, variables) in enumerate(models_variables.items()): - if model == "direct form": - base_model_results = models_variables[model] - continue - error = np.abs( - variables["Battery voltage [V]"](t_eval) - - base_model_results["Battery voltage [V]"](t_eval) - ) - ax.loglog(variables["Time [h]"](t_eval), error, linestyles[j], label=model) - ax.legend(loc="upper right") - fig.tight_layout() - file_name = "capacitance_errors_voltages.eps".format(Crate) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def discharge_states(compute): - savefile = "effect_of_capacitance_data.pickle" - if compute: - models = [ - pybamm.lead_acid.Full(name="direct form"), - pybamm.lead_acid.Full( - {"surface form": "differential"}, - name="capacitance form\n(differential)", - ), - pybamm.lead_acid.Full( - {"surface form": "algebraic"}, name="capacitance form\n(algebraic)" - ), - ] - Crates = [0.1, 1, 5] - t_eval = np.concatenate( - [np.logspace(-6, -3, 50), np.linspace(0.001, 1, 100)[1:]] - ) - all_variables, t_eval = model_comparison(models, Crates, t_eval) - with open(savefile, "wb") as f: - data = (all_variables, t_eval) - pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile, "rb") as f: - (all_variables, t_eval) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables, t_eval) - plot_errors(all_variables, t_eval, [5]) - - -def plot_times(models_times_and_voltages): - shared_plotting.plot_times( - models_times_and_voltages, Crate=1, linestyles=["k-", "b-.", "r--"] - ) - file_name = "capacitance_solver_times.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def discharge_times_and_errors(compute): - savefile = "capacitance_times_and_errors.pickle" - if compute: - try: - with open(savefile, "rb") as f: - models_times_and_voltages = pickle.load(f) - except FileNotFoundError: - models_times_and_voltages = pybamm.get_infinite_nested_dict() - models = [ - pybamm.lead_acid.Full(name="direct form"), - pybamm.lead_acid.Full( - {"surface form": "differential"}, - name="capacitance form\n(differential)", - ), - pybamm.lead_acid.Full( - {"surface form": "algebraic"}, name="capacitance form\n(algebraic)" - ), - ] - Crates = [1] - all_npts = np.linspace(10, 100, 2) - t_eval = np.linspace(0, 1, 100) - new_models_times_and_voltages = convergence_study( - models, Crates, all_npts, t_eval - ) - models_times_and_voltages.update(new_models_times_and_voltages) - with open(savefile, "wb") as f: - pickle.dump(models_times_and_voltages, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile, "rb") as f: - models_times_and_voltages = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_errors(models_times_and_voltages) - # plot_times(models_times_and_voltages) - - -if __name__ == "__main__": - pybamm.set_logging_level("INFO") - parser = argparse.ArgumentParser() - parser.add_argument("--compute", action="store_true", help="(Re)-compute results.") - args = parser.parse_args() - discharge_states(args.compute) - # discharge_times_and_errors(args.compute) - plt.show() diff --git a/results/2019_08_sulzer_thesis/effect_of_convection.py b/results/2019_08_sulzer_thesis/effect_of_convection.py deleted file mode 100644 index ab1a3000e9..0000000000 --- a/results/2019_08_sulzer_thesis/effect_of_convection.py +++ /dev/null @@ -1,125 +0,0 @@ -# -# Simulations: effect of side reactions for charge of a lead-acid battery -# -import argparse -import matplotlib.pyplot as plt -import numpy as np -import pickle -import pybamm -import shared_plotting -from shared_solutions import model_comparison - -try: - from config import OUTPUT_DIR -except ImportError: - OUTPUT_DIR = None - - -def plot_voltages(all_variables, t_eval, bigger_beta=False): - linestyles = ["k-", "b--"] - shared_plotting.plot_voltages(all_variables, t_eval, linestyles, figsize=(6.4, 2.5)) - if bigger_beta: - file_name = "convection_voltage_comparison_bigger_beta.eps" - else: - file_name = "convection_voltage_comparison.eps" - plt.subplots_adjust(bottom=0.4) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_variables(all_variables, t_eval, bigger_beta=False): - # Set up - times = np.array([0.195]) - linestyles = ["k-", "b--"] - if bigger_beta: - var_file_names = { - "Volume-averaged velocity [m.s-1]" - + "": "convection_velocity_comparison_bigger_beta.eps", - "Electrolyte concentration [Molar]" - + "": "convection_electrolyte_concentration_comparison_bigger_beta.eps", - } - else: - var_file_names = { - "Volume-averaged velocity [m.s-1]": "convection_velocity_comparison.eps", - "Electrolyte concentration [Molar]" - + "": "convection_electrolyte_concentration_comparison.eps", - } - for var, file_name in var_file_names.items(): - fig, axes = shared_plotting.plot_variable( - all_variables, times, var, linestyles=linestyles, figsize=(6.4, 3) - ) - for ax in axes.flat: - title = ax.get_title() - ax.set_title(title, y=1.08) - plt.subplots_adjust( - bottom=0.3, top=0.85, left=0.1, right=0.9, hspace=0.08, wspace=0.05 - ) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def charge_states(compute): - savefile = "effect_of_convection_data.pickle" - if compute: - models = [ - pybamm.lead_acid.Full( - {"convection": True}, name="With convection" - ), - pybamm.lead_acid.Full(name="Without convection"), - ] - Crates = [0.5, 1, 5] - t_eval = np.linspace(0, 1, 100) - all_variables, t_eval = model_comparison(models, Crates, t_eval) - with open(savefile, "wb") as f: - data = (all_variables, t_eval) - pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile, "rb") as f: - (all_variables, t_eval) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables, t_eval) - plot_variables(all_variables, t_eval) - - -def charge_states_bigger_volume_change(compute): - savefile = "effect_of_convection_bigger_beta_data.pickle" - if compute: - models = [ - pybamm.lead_acid.Full( - {"convection": True}, name="With convection" - ), - pybamm.lead_acid.Full(name="Without convection"), - ] - Crates = [0.5, 1, 5] - t_eval = np.linspace(0, 1, 100) - extra_parameter_values = {"Volume change factor": 10} - all_variables, t_eval = model_comparison( - models, Crates, t_eval, extra_parameter_values=extra_parameter_values - ) - with open(savefile, "wb") as f: - data = (all_variables, t_eval) - pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile, "rb") as f: - (all_variables, t_eval) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables, t_eval, bigger_beta=True) - plot_variables(all_variables, t_eval, bigger_beta=True) - - -if __name__ == "__main__": - pybamm.set_logging_level("DEBUG") - parser = argparse.ArgumentParser() - parser.add_argument("--compute", action="store_true", help="(Re)-compute results.") - args = parser.parse_args() - charge_states(args.compute) - charge_states_bigger_volume_change(args.compute) - plt.show() diff --git a/results/2019_08_sulzer_thesis/effect_of_side_reactions.py b/results/2019_08_sulzer_thesis/effect_of_side_reactions.py deleted file mode 100644 index 7c7cdbbc1a..0000000000 --- a/results/2019_08_sulzer_thesis/effect_of_side_reactions.py +++ /dev/null @@ -1,111 +0,0 @@ -# -# Simulations: effect of side reactions for charge of a lead-acid battery -# -import argparse -import matplotlib.pyplot as plt -import numpy as np -import pickle -import pybamm -import shared_plotting -from shared_solutions import model_comparison - -try: - from config import OUTPUT_DIR -except ImportError: - OUTPUT_DIR = None - - -def plot_voltages(all_variables, t_eval): - linestyles = ["k-", "b--"] - shared_plotting.plot_voltages(all_variables, t_eval, linestyles, figsize=(6.4, 2.5)) - file_name = "side_reactions_voltage_comparison.eps" - plt.subplots_adjust(bottom=0.4) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_interfacial_currents(all_variables, t_eval): - file_name = "side_reactions_interfacial_current_density_comparison.eps" - output_vars = [ - "Average positive electrode interfacial current density", - "Average positive electrode oxygen interfacial current density", - "Average negative electrode oxygen interfacial current density", - "Average negative electrode interfacial current density", - ] - labels = [ - "Pos electrode\n(main)", - "Pos electrode\n(oxygen)", - "Neg electrode\n(oxygen)", - "Neg electrode\n(main)", - ] - shared_plotting.plot_time_dependent_variables( - all_variables, t_eval, output_vars, labels - ) - plt.subplots_adjust(bottom=0.4, right=0.95, wspace=0.3) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def charge_states(compute): - savefile1 = "effect_of_side_reactions_data.pickle" - savefile2 = "effect_of_side_reactions_loqs_data.pickle" - if compute: - models1 = [ - pybamm.lead_acid.Full( - {"surface form": "algebraic", "side reactions": ["oxygen"]}, - name="With oxygen", - ), - pybamm.lead_acid.Full( - {"surface form": "algebraic"}, name="Without oxygen" - ), - ] - Crates = [-0.1, -1, -5] - t_eval = np.linspace(0, 5, 100) - extra_parameter_values = { - "Positive electrode" - + "reference exchange-current density (oxygen) [A.m-2]": 1e-24, - "Initial State of Charge": 0.5, - } - all_variables1, t_eval1 = model_comparison( - models1, Crates, t_eval, extra_parameter_values=extra_parameter_values - ) - # Use LOQS without voltage cut-off for interfacial current densities, so that - # the current goes all the way - models2 = [ - pybamm.lead_acid.Full( - {"surface form": "algebraic", "side reactions": ["oxygen"]}, - name="With oxygen", - ), - pybamm.lead_acid.LOQS({"surface form": "algebraic"}, name="Without oxygen"), - ] - extra_parameter_values["Upper voltage cut-off [V]"] = 100 - all_variables2, t_eval2 = model_comparison( - models2, Crates, t_eval, extra_parameter_values=extra_parameter_values - ) - with open(savefile1, "wb") as f: - data1 = (all_variables1, t_eval1) - pickle.dump(data1, f, pickle.HIGHEST_PROTOCOL) - with open(savefile2, "wb") as f: - data2 = (all_variables2, t_eval2) - pickle.dump(data2, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile1, "rb") as f: - (all_variables1, t_eval1) = pickle.load(f) - with open(savefile2, "rb") as f: - (all_variables2, t_eval2) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables1, t_eval1) - plot_interfacial_currents(all_variables2, t_eval2) - - -if __name__ == "__main__": - pybamm.set_logging_level("DEBUG") - parser = argparse.ArgumentParser() - parser.add_argument("--compute", action="store_true", help="(Re)-compute results.") - args = parser.parse_args() - charge_states(args.compute) - plt.show() diff --git a/results/2019_08_sulzer_thesis/lead_acid_charge.py b/results/2019_08_sulzer_thesis/lead_acid_charge.py deleted file mode 100644 index c4f59a9010..0000000000 --- a/results/2019_08_sulzer_thesis/lead_acid_charge.py +++ /dev/null @@ -1,145 +0,0 @@ -# -# Simulations: charge of a lead-acid battery -# -import argparse -import matplotlib.pyplot as plt -import numpy as np -import pickle -import pybamm -import shared_plotting -from shared_solutions import model_comparison - -try: - from config import OUTPUT_DIR -except ImportError: - OUTPUT_DIR = None - - -def plot_voltages(all_variables, t_eval): - Crates = [-0.1, -0.2, -0.5, -1, -2, -5] - all_variables = {k: v for k, v in all_variables.items() if k in Crates} - shared_plotting.plot_voltages(all_variables, t_eval) - file_name = "charge_voltage_comparison.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_interfacial_currents(all_variables, t_eval): - Crates = [-0.1, -2, -5] - all_variables = {Crate: v for Crate, v in all_variables.items() if Crate in Crates} - file_name = "charge_interfacial_current_density_comparison.eps" - output_vars = [ - "Average positive electrode interfacial current density", - "Average positive electrode oxygen interfacial current density", - "Average negative electrode oxygen interfacial current density", - "Average negative electrode interfacial current density", - ] - labels = [ - "Pos electrode\n(main)", - "Pos electrode\n(oxygen)", - "Neg electrode\n(oxygen)", - "Neg electrode\n(main)", - ] - shared_plotting.plot_time_dependent_variables( - all_variables, - t_eval, - output_vars, - labels, - colors=["k", "g", "r", "b"], - figsize=(6.4, 6.4), - ) - plt.subplots_adjust( - bottom=0.15, left=0.15, right=0.95, wspace=0.3, hspace=0.4, top=0.95 - ) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_variables(all_variables, t_eval): - # Set up - Crates = [-0.1, -2, -5] - times = np.linspace(0, 2, 4) - var_file_names = { - "Electrolyte concentration [Molar]" - + "": "charge_electrolyte_concentration_comparison.eps", - "Oxygen concentration [Molar]": "charge_oxygen_concentration_comparison.eps", - } - limits_exceptions = {"Electrolyte concentration [Molar]": {"min": 0}} - all_variables = {k: v for k, v in all_variables.items() if k in Crates} - for var, file_name in var_file_names.items(): - if var in limits_exceptions: - exceptions = limits_exceptions[var] - else: - exceptions = {} - shared_plotting.plot_variable( - all_variables, times, var, exceptions, yaxis="FCI" - ) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_voltage_components(all_variables, t_eval): - Crates = [-0.1, -2, -5] - model = "Composite" - shared_plotting.plot_voltage_components(all_variables, t_eval, model, Crates) - file_name = "charge_voltage_components.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def charge_states(compute): - if compute: - models = [ - pybamm.lead_acid.Full( - {"side reactions": ["oxygen"]}, name="Full" - ), - pybamm.lead_acid.LOQS( - {"surface form": "algebraic", "side reactions": ["oxygen"]}, name="LOQS" - ), - pybamm.lead_acid.FOQS( - {"surface form": "algebraic", "side reactions": ["oxygen"]}, name="FOQS" - ), - # pybamm.lead_acid.Composite( - # {"surface form": "algebraic", "side reactions": ["oxygen"]}, - # name="Composite", - # ), - pybamm.lead_acid.CompositeExtended( - {"surface form": "algebraic", "side reactions": ["oxygen"]}, - name="Composite", - ), - ] - Crates = [-0.1, -0.2, -0.5, -1, -2, -5] - t_eval = np.linspace(0, 3, 100) - extra_parameter_values = { - "Positive electrode" - + "reference exchange-current density (oxygen) [A.m-2]": 1e-24, - "Initial State of Charge": 0.5, - } - all_variables, t_eval = model_comparison( - models, Crates, t_eval, extra_parameter_values=extra_parameter_values - ) - with open("charge_asymptotics_data.pickle", "wb") as f: - data = (all_variables, t_eval) - pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open("charge_asymptotics_data.pickle", "rb") as f: - (all_variables, t_eval) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables, t_eval) - plot_interfacial_currents(all_variables, t_eval) - plot_variables(all_variables, t_eval) - plot_voltage_components(all_variables, t_eval) - - -if __name__ == "__main__": - pybamm.set_logging_level("INFO") - parser = argparse.ArgumentParser() - parser.add_argument("--compute", action="store_true", help="(Re)-compute results.") - args = parser.parse_args() - charge_states(args.compute) - # charge_times_and_errors(args.compute) - plt.show() diff --git a/results/2019_08_sulzer_thesis/lead_acid_discharge.py b/results/2019_08_sulzer_thesis/lead_acid_discharge.py deleted file mode 100644 index 887521e0a4..0000000000 --- a/results/2019_08_sulzer_thesis/lead_acid_discharge.py +++ /dev/null @@ -1,169 +0,0 @@ -# -# Simulations: discharge of a lead-acid battery -# -import argparse -import matplotlib.pyplot as plt -import numpy as np -import pickle -import pybamm -import shared_plotting -from collections import defaultdict -from shared_solutions import model_comparison, convergence_study - -try: - from config import OUTPUT_DIR -except ImportError: - OUTPUT_DIR = None - - -def plot_voltages(all_variables, t_eval): - Crates = [0.1, 0.2, 0.5, 1, 2, 5] - all_variables = {k: v for k, v in all_variables.items() if k in Crates} - shared_plotting.plot_voltages(all_variables, t_eval) - file_name = "discharge_voltage_comparison.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_variables(all_variables, t_eval): - # Set up - Crates = [0.1, 1, 4] - times = np.array([0, 0.195, 0.375, 0.545]) - var_file_names = { - "Electrolyte concentration [Molar]" - + "": "discharge_electrolyte_concentration_comparison.eps", - "Electrolyte potential [V]": "discharge_electrolyte_potential_comparison.eps", - "Interfacial current density" - + "": "discharge_interfacial_current_density_comparison.eps", - } - limits_exceptions = {"Electrolyte concentration [Molar]": {"min": 0}} - all_variables = {k: v for k, v in all_variables.items() if k in Crates} - for var, file_name in var_file_names.items(): - if var in limits_exceptions: - exceptions = limits_exceptions[var] - else: - exceptions = {} - shared_plotting.plot_variable(all_variables, times, var, exceptions) - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_voltage_components(all_variables, t_eval): - Crates = [0.1, 2, 5] - model = "Composite" - shared_plotting.plot_voltage_components(all_variables, t_eval, model, Crates) - file_name = "discharge_voltage_components.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def discharge_states(compute): - savefile = "discharge_asymptotics_data.pickle" - if compute: - models = [ - pybamm.lead_acid.Full(name="Full"), - pybamm.lead_acid.LOQS(name="LOQS"), - pybamm.lead_acid.FOQS(name="FOQS"), - pybamm.lead_acid.Composite(name="Composite"), - ] - Crates = [0.1, 0.2, 0.5, 1, 2, 4, 5, 10, 20] - t_eval = np.linspace(0, 1, 100) - extra_parameter_values = {"Bruggeman coefficient": 0.001} - all_variables, t_eval = model_comparison( - models, Crates, t_eval, extra_parameter_values=extra_parameter_values - ) - with open(savefile, "wb") as f: - data = (all_variables, t_eval) - pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile, "rb") as f: - (all_variables, t_eval) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables, t_eval) - plot_variables(all_variables, t_eval) - plot_voltage_components(all_variables, t_eval) - - -def plot_errors(models_times_and_voltages): - npts = 20 - linestyles = ["k-", "g--", "r:", "b-."] - Crates = defaultdict(list) - voltage_errors = defaultdict(list) - fig, ax = plt.subplots(1, 1) - for i, (model, times_and_voltages) in enumerate(models_times_and_voltages.items()): - if model != "Full": - for Crate, variables in times_and_voltages[npts].items(): - Crates[model].append(Crate) - full_voltage = models_times_and_voltages["Full"][npts][Crate][ - "Battery voltage [V]" - ] - reduced_voltage = variables["Battery voltage [V]"] - voltage_errors[model].append(pybamm.rmse(full_voltage, reduced_voltage)) - ax.semilogx( - Crates[model], voltage_errors[model], linestyles[i], label=model - ) - ax.set_xlabel("C-rate") - ax.set_ylabel("RMSE [V]") - ax.legend(loc="best") - fig.tight_layout() - file_name = "discharge_asymptotics_rmse.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def plot_times(models_times_and_voltages): - shared_plotting.plot_times(models_times_and_voltages, Crate=1) - file_name = "discharge_asymptotics_solver_times.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def discharge_times_and_errors(compute): - savefile = "discharge_asymptotics_times_and_errors.pickle" - if compute: - try: - with open(savefile, "rb") as f: - models_times_and_voltages = pickle.load(f) - except FileNotFoundError: - models_times_and_voltages = pybamm.get_infinite_nested_dict() - models = [ - pybamm.lead_acid.Full( - {"surface form": "algebraic"}, name="Full" - ), - pybamm.lead_acid.LOQS(name="LOQS"), - # pybamm.lead_acid.FOQS(name="FOQS"), - # pybamm.lead_acid.Composite(name="Composite"), - ] - Crates = np.linspace(0.01, 5, 2) - all_npts = [20] - t_eval = np.linspace(0, 1, 100) - new_models_times_and_voltages = convergence_study( - models, Crates, all_npts, t_eval - ) - models_times_and_voltages.update(new_models_times_and_voltages) - with open(savefile, "wb") as f: - pickle.dump(models_times_and_voltages, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(savefile, "rb") as f: - models_times_and_voltages = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_errors(models_times_and_voltages) - plot_times(models_times_and_voltages) - - -if __name__ == "__main__": - pybamm.set_logging_level("INFO") - parser = argparse.ArgumentParser() - parser.add_argument("--compute", action="store_true", help="(Re)-compute results.") - args = parser.parse_args() - discharge_states(args.compute) - # discharge_times_and_errors(args.compute) - plt.show() diff --git a/results/2019_08_sulzer_thesis/print_lead_acid_parameters.py b/results/2019_08_sulzer_thesis/print_lead_acid_parameters.py deleted file mode 100644 index be13842e3a..0000000000 --- a/results/2019_08_sulzer_thesis/print_lead_acid_parameters.py +++ /dev/null @@ -1,10 +0,0 @@ -# -# Print parameters for lead-acid models -# -import pybamm - -parameters = pybamm.standard_parameters_lead_acid -parameter_values = pybamm.lead_acid.BaseModel().default_parameter_values -output_file = "results/2019_08_sulzer_thesis/parameters.txt" - -pybamm.print_parameters(parameters, parameter_values, output_file) diff --git a/results/2019_08_sulzer_thesis/self_discharge.py b/results/2019_08_sulzer_thesis/self_discharge.py deleted file mode 100644 index bee360be25..0000000000 --- a/results/2019_08_sulzer_thesis/self_discharge.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# Simulations: self-discharge -# -import argparse -import matplotlib.pyplot as plt -import numpy as np -import pickle -import pybamm -import shared_plotting -from config import OUTPUT_DIR -from shared_solutions import model_comparison - - -def plot_voltages(all_variables, t_eval): - shared_plotting.plot_voltages(all_variables, t_eval) - file_name = "sefl_discharge_voltage_comparison.eps" - if OUTPUT_DIR is not None: - plt.savefig(OUTPUT_DIR + file_name, format="eps", dpi=1000) - - -def self_discharge_states(compute): - save_file = "self_discharge_data.pickle" - if compute: - models = [ - pybamm.lead_acid.Full(name="Full, without oxygen"), - pybamm.lead_acid.Full( - {"side reactions": ["oxygen"]}, name="Full, with oxygen" - ), - pybamm.lead_acid.LOQS( - {"surface form": "algebraic", "side reactions": ["oxygen"]}, - name="LOQS, with oxygen", - ), - ] - extra_parameter_values = { - "Current function": "[zero]" - } - t_eval = np.linspace(0, 1000, 100) - all_variables, t_eval = model_comparison( - models, [1], t_eval, extra_parameter_values=extra_parameter_values - ) - with open(save_file, "wb") as f: - data = (all_variables, t_eval) - pickle.dump(data, f, pickle.HIGHEST_PROTOCOL) - else: - try: - with open(save_file, "rb") as f: - (all_variables, t_eval) = pickle.load(f) - except FileNotFoundError: - raise FileNotFoundError( - "Run script with '--compute' first to generate results" - ) - plot_voltages(all_variables, t_eval) - - -if __name__ == "__main__": - pybamm.set_logging_level("INFO") - parser = argparse.ArgumentParser() - parser.add_argument("--compute", action="store_true", help="(Re)-compute results.") - args = parser.parse_args() - self_discharge_states(args.compute) - plt.show() diff --git a/results/2019_08_sulzer_thesis/shared_plotting.py b/results/2019_08_sulzer_thesis/shared_plotting.py deleted file mode 100644 index 313aa4a185..0000000000 --- a/results/2019_08_sulzer_thesis/shared_plotting.py +++ /dev/null @@ -1,335 +0,0 @@ -# -# Shared plotting -# -import matplotlib.pyplot as plt -import numpy as np -import pybamm -from collections import defaultdict - - -def plot_voltages(all_variables, t_eval, linestyles=None, figsize=(6.4, 4.5)): - # Plot - linestyles = linestyles or ["k-", "g--", "r:", "b-."] - n = int(len(all_variables) // np.sqrt(len(all_variables))) - m = int(np.ceil(len(all_variables) / n)) - fig, axes = plt.subplots(n, m, figsize=figsize) - labels = [model for model in [x for x in all_variables.values()][0].keys()] - y_min = 0.98 * min( - np.nanmin(variables["Battery voltage [V]"](t_eval)) - for models_variables in all_variables.values() - for variables in models_variables.values() - ) - y_max = 1.02 * max( - np.nanmax(variables["Battery voltage [V]"](t_eval)) - for models_variables in all_variables.values() - for variables in models_variables.values() - ) - # Strict voltage cut-offs - y_min = max(y_min, 10.5) - y_max = min(y_max, 14.6) - for k, (Crate, models_variables) in enumerate(all_variables.items()): - if len(all_variables) == 1: - ax = axes - else: - ax = axes.flat[k] - t_max = max( - np.nanmax(var["Time [h]"](t_eval)) for var in models_variables.values() - ) - ax.set_xlim([0, t_max]) - ax.set_ylim([y_min, y_max]) - ax.set_xlabel("Time [h]") - if len(all_variables) > 1: - ax.set_title( - "\\textbf{{({})}} {}C ($\\mathcal{{C}}_e={}$)".format( - chr(97 + k), abs(Crate), abs(Crate) * 0.6 - ) - ) - # # Hide the right and top spines - # ax.spines["right"].set_visible(False) - # ax.spines["top"].set_visible(False) - # - # # Only show ticks on the left and bottom spines - # ax.yaxis.set_ticks_position("left") - # ax.xaxis.set_ticks_position("bottom") - ax.xaxis.set_major_locator(plt.MaxNLocator(3)) - if k % m == 0: - ax.set_ylabel("Voltage [V]") - # else: - # ax.set_yticklabels([]) - - for j, variables in enumerate(models_variables.values()): - ax.plot( - variables["Time [h]"](t_eval), - variables["Battery voltage [V]"](t_eval), - linestyles[j], - ) - if len(all_variables) == 1: - leg = ax.legend(labels, loc="best") - fig.tight_layout() - else: - leg = fig.legend(labels, loc="lower center", ncol=len(labels)) - plt.subplots_adjust(bottom=0.25, right=0.95, hspace=1.1, wspace=0.3) - leg.get_frame().set_edgecolor("k") - return fig, axes - - -def plot_variable( - all_variables, - times, - variable, - limits_exceptions=None, - yaxis="SOC", - linestyles=None, - figsize=(6.4, 5), -): - limits_exceptions = limits_exceptions or {} - linestyles = linestyles or ["k-", "g--", "r:", "b-."] - n = len(times) - m = len(all_variables) - Crates = list(all_variables.keys()) - labels = [model for model in [x for x in all_variables.values()][0].keys()] - x = all_variables[Crates[0]][labels[0]]["x"](0, np.linspace(0, 1))[:, 0] - x_dim = all_variables[Crates[0]][labels[0]]["x [m]"](0, np.linspace(0, 1))[:, 0] - - fig, axes = plt.subplots(n, m, figsize=figsize) - - # Default limits - y_min = pybamm.ax_min( - [ - np.nanmin(variables[variable](times, x)) - for Crate, models_variables in all_variables.items() - for variables in models_variables.values() - ] - ) - y_max = pybamm.ax_max( - [ - np.nanmax(variables[variable](times, x)) - for Crate, models_variables in all_variables.items() - for variables in models_variables.values() - ] - ) - # Exceptions - if "min" in limits_exceptions: - y_min = limits_exceptions["min"] - if "max" in limits_exceptions: - y_max = limits_exceptions["max"] - - # Plot - for i, (Crate, models_variables) in enumerate(all_variables.items()): - for j, time in enumerate(times): - if len(times) == 1: - ax = axes[i] - else: - ax = axes[j, i] - ax.set_xlim([x_dim[0], x_dim[-1]]) - ax.set_ylim([y_min, y_max]) - ax.yaxis.set_major_locator(plt.MaxNLocator(3)) - - # Title - if j == 0: - ax.set_title( - "\\textbf{{({})}} {}C ($\\mathcal{{C}}_e={}$)".format( - chr(97 + i), abs(Crate), abs(Crate) * 0.6 - ) - ) - # x-axis - if j == len(times) - 1: - ax.set_xlabel("x [m]") - else: - ax.set_xticklabels([]) - - # y-axis - if i == 0: - # If we only want to plot one time the y label is the variable - if len(times) == 1: - ax.set_ylabel(variable) - # Otherwise the y label is the time - else: - for variables in models_variables.values(): - try: - if yaxis == "SOC": - soc = variables["State of Charge"](time) - ax.set_ylabel( - "{}\% SoC".format(int(soc)), rotation=0, labelpad=30 - ) - elif yaxis == "FCI": - fci = variables["Fractional Charge Input"](time) - ax.set_ylabel( - "{}\% FCI".format(int(fci)), rotation=0, labelpad=30 - ) - ax.yaxis.get_label().set_verticalalignment("center") - except ValueError: - pass - else: - ax.set_yticklabels([]) - - # Plot - for j, variables in enumerate(models_variables.values()): - ax.plot(x_dim, variables[variable](time, x), linestyles[j]) - leg = fig.legend(labels, loc="lower center", ncol=len(labels), frameon=True) - leg.get_frame().set_edgecolor("k") - plt.subplots_adjust( - bottom=0.17, top=0.95, left=0.18, right=0.97, hspace=0.08, wspace=0.05 - ) - return fig, axes - - -def plot_time_dependent_variables( - all_variables, t_eval, output_vars, labels, colors=None, figsize=(6.4, 3.5) -): - models = list(list(all_variables.values())[0].keys()) - full_model = models[0] - fig, axes = plt.subplots(len(models) - 1, len(all_variables), figsize=figsize) - y_min = pybamm.ax_min( - [ - np.nanmin(variables[var](t_eval)) - for models_variables in all_variables.values() - for variables in models_variables.values() - for var in output_vars - ] - ) - y_max = pybamm.ax_max( - [ - np.nanmax(variables[var](t_eval)) - for models_variables in all_variables.values() - for variables in models_variables.values() - for var in output_vars - ] - ) - linestyles = ["--", ":", "-.", "-"] - colors = colors or ["k", "b"] - for i, (Crate, models_variables) in enumerate(all_variables.items()): - for j, model in enumerate(models[1:]): - full_variables = models_variables[full_model] - variables = models_variables[model] - if len(models) == 2: - ax = axes[i] - else: - ax = axes[j, i] - t_max = max( - np.nanmax(var["Time [h]"](t_eval)) for var in models_variables.values() - ) - ax.set_xlim([0, t_max]) - ax.set_ylim([y_min, y_max]) - if i == 0: - if len(models) == 2: - ax.set_ylabel("Interfacial current densities") - else: - ax.set_ylabel("{}\nvs Full".format(model), rotation=0, labelpad=30) - ax.yaxis.get_label().set_verticalalignment("center") - if j == 0 and len(all_variables) > 1: - ax.set_title( - "\\textbf{{({})}} {}C ($\\mathcal{{C}}_e={}$)".format( - chr(97 + i), abs(Crate), abs(Crate) * 0.6 - ) - ) - if j == len(models) - 2: - ax.set_xlabel("Time [h]") - plots = {} - for k, var in enumerate(output_vars): - plots[(full_model, k)], = ax.plot( - full_variables["Time [h]"](t_eval), - full_variables[var](t_eval), - linestyle=linestyles[k], - color=colors[0], - ) - for k, var in enumerate(output_vars): - plots[(model, k)], = ax.plot( - variables["Time [h]"](t_eval), - variables[var](t_eval), - linestyle=linestyles[k], - color=colors[j + 1], - ) - if len(models) == 2: - leg1 = fig.legend( - [plots[(model, len(linestyles) - 1)] for model in models], - models, - loc="lower center", - ncol=len(models), - bbox_to_anchor=(0.5, 0), - ) - fig.legend( - labels, loc="lower center", ncol=len(labels), bbox_to_anchor=(0.5, 0.1) - ) - fig.add_artist(leg1) - else: - fig.legend(labels, loc="lower center", ncol=len(labels)) - - -def plot_voltage_components(all_variables, t_eval, model, Crates): - n = int(len(Crates) // np.sqrt(len(Crates))) - m = int(np.ceil(len(Crates) / n)) - fig, axes = plt.subplots(n, m, figsize=(6.4, 2.3)) - labels = ["V", "$V_U$", "$V_k$", "$V_c$", "$V_o$"] - overpotentials = [ - "Average battery reaction overpotential [V]", - "Average battery concentration overpotential [V]", - "Average battery electrolyte ohmic losses [V]", - ] - y_min = 0.95 * min( - np.nanmin(models_variables[model]["Battery voltage [V]"](t_eval)) - for models_variables in all_variables.values() - ) - y_max = 1.05 * max( - np.nanmax(models_variables[model]["Battery voltage [V]"](t_eval)) - for models_variables in all_variables.values() - ) - for k, Crate in enumerate(Crates): - variables = all_variables[Crate][model] - ax = axes.flat[k] - - # Set up - t_max = np.nanmax(variables["Time [h]"](t_eval)) - ax.set_xlim([0, t_max]) - ax.set_ylim([y_min, y_max]) - ax.set_xlabel("Time [h]") - ax.set_title( - "\\textbf{{({})}} {}C ($\\mathcal{{C}}_e={}$)".format( - chr(97 + k), abs(Crate), abs(Crate) * 0.6 - ) - ) - ax.xaxis.set_major_locator(plt.MaxNLocator(3)) - if k % m == 0: - ax.set_ylabel("Voltage [V]") - - # Plot - # Initialise - # for lead-acid we multiply everything by 6 to - time = variables["Time [h]"](t_eval) - initial_ocv = variables["Average battery open circuit voltage [V]"](0) - ocv = variables["Average battery open circuit voltage [V]"](t_eval) - ax.fill_between(time, ocv, initial_ocv) - top = ocv - # Plot - for overpotential in overpotentials: - bottom = top + variables[overpotential](t_eval) - ax.fill_between(time, bottom, top) - top = bottom - ax.plot(time, variables["Battery voltage [V]"](t_eval), "k--") - leg = axes.flat[-1].legend( - labels, bbox_to_anchor=(1.05, 0.5), loc="center left", frameon=True - ) - leg.get_frame().set_edgecolor("k") - fig.tight_layout() - - -def plot_times(models_times_and_voltages, Crate=1, linestyles=None): - linestyles = linestyles or ["k-", "g--", "r:", "b-."] - all_npts = defaultdict(list) - solver_times = defaultdict(list) - fig, ax = plt.subplots(1, 1) - for i, (model, times_and_voltages) in enumerate(models_times_and_voltages.items()): - for npts in times_and_voltages.keys(): - try: - solver_time = times_and_voltages[npts][Crate][ - "solution object" - ].solve_time - except KeyError: - continue - all_npts[model].append(npts * 3) - solver_times[model].append(solver_time) - ax.loglog(all_npts[model], solver_times[model], linestyles[i], label=model) - ax.set_xlabel("Number of grid points") - ax.set_ylabel("Solver time [s]") - ax.legend(loc="best") - fig.tight_layout() diff --git a/results/2019_08_sulzer_thesis/shared_solutions.py b/results/2019_08_sulzer_thesis/shared_solutions.py deleted file mode 100644 index 26369949d4..0000000000 --- a/results/2019_08_sulzer_thesis/shared_solutions.py +++ /dev/null @@ -1,147 +0,0 @@ -# -# Simulations -# -import pybamm - - -def model_comparison(models, Crates, t_eval, extra_parameter_values=None): - " Solve models at a range of Crates " - # load parameter values and geometry - geometry = models[0].default_geometry - extra_parameter_values = extra_parameter_values or {} - param = models[0].default_parameter_values - param.update(extra_parameter_values) - - # Process parameters (same parameters for all models) - for model in models: - param.process_model(model) - param.process_geometry(geometry) - - # set mesh - var = pybamm.standard_spatial_vars - var_pts = {var.x_n: 20, var.x_s: 20, var.x_p: 20} - mesh = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts) - - # discretise models - discs = {} - for model in models: - disc = pybamm.Discretisation(mesh, model.default_spatial_methods) - disc.process_model(model) - # Store discretisation - discs[model] = disc - - # solve model for range of Crates - all_variables = {} - for Crate in Crates: - all_variables[Crate] = {} - current = Crate * 17 - pybamm.logger.info("Setting typical current to {} A".format(current)) - param.update({"Typical current [A]": current}) - for model in models: - param.update_model(model, discs[model]) - solution = model.default_solver.solve(model, t_eval) - variables = pybamm.post_process_variables( - model.variables, solution.t, solution.y, mesh - ) - variables["solution"] = solution - all_variables[Crate][model.name] = variables - - return all_variables, t_eval - - -def convergence_study(models, Crates, all_npts, t_eval, extra_parameter_values=None): - " Solve models at a range of number of grid points " - # load parameter values and geometry - geometry = models[0].default_geometry - param = models[0].default_parameter_values - # Update parameters - extra_parameter_values = extra_parameter_values or {} - param.update(extra_parameter_values) - - # Process parameters (same parameters for all models) - for model in models: - param.process_model(model) - param.process_geometry(geometry) - - # set mesh - var = pybamm.standard_spatial_vars - - # solve model for range of Crates and npts - models_times_and_voltages = {model.name: {} for model in models} - for npts in all_npts: - pybamm.logger.info("Setting number of grid points to {}".format(npts)) - var_pts = {var.x_n: npts, var.x_s: npts, var.x_p: npts} - mesh = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts) - - # discretise models, store discretised model and discretisation - models_disc = {} - discs = {} - for model in models: - disc = pybamm.Discretisation(mesh, model.default_spatial_methods) - models_times_and_voltages[model.name][npts] = {} - models_disc[model.name] = disc.process_model(model, inplace=False) - discs[model.name] = disc - - # Solve for a range of C-rates - for Crate in Crates: - current = Crate * 17 - pybamm.logger.info("Setting typical current to {} A".format(current)) - param.update({"Typical current [A]": current}) - for model in models: - model_disc = models_disc[model.name] - disc = discs[model.name] - param.update_model(model_disc, disc) - try: - solution = model.default_solver.solve(model_disc, t_eval) - except pybamm.SolverError: - pybamm.logger.error( - "Could not solve {!s} at {} A with {} points".format( - model.name, current, npts - ) - ) - continue - voltage = pybamm.ProcessedVariable( - model_disc.variables["Battery voltage [V]"], solution.t, solution.y - )(t_eval) - variables = { - "Battery voltage [V]": voltage, - "solution object": solution, - } - models_times_and_voltages[model.name][npts][Crate] = variables - - return models_times_and_voltages - - -def simulation(models, t_eval, extra_parameter_values=None, disc_only=False): - - # create geometry - geometry = models[-1].default_geometry - - # load parameter values and process models and geometry - param = models[0].default_parameter_values - extra_parameter_values = extra_parameter_values or {} - param.update(extra_parameter_values) - for model in models: - param.process_model(model) - param.process_geometry(geometry) - - # set mesh - var = pybamm.standard_spatial_vars - var_pts = {var.x_n: 25, var.x_s: 41, var.x_p: 34} - mesh = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts) - - # discretise models - for model in models: - disc = pybamm.Discretisation(mesh, model.default_spatial_methods) - disc.process_model(model) - - if disc_only: - return model, mesh - - # solve model - solutions = [None] * len(models) - for i, model in enumerate(models): - solution = model.default_solver.solve(model, t_eval) - solutions[i] = solution - - return models, mesh, solutions diff --git a/results/2plus1D/README.md b/results/2plus1D/README.md deleted file mode 100644 index 1bdf963769..0000000000 --- a/results/2plus1D/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Results: "2plus1D" models - -This folder contains the scripts used to generate results for the "2plus1D" papers: - -1. Asymptotic Reduction of a Li-ion Pouch Cell Model: Part 1 - Scott Marquis, Robert Timms, Valentin Sulzer, Colin Please, S Jon Chapman - -2. Asymptotic Reduction of a Li-ion Pouch Cell Model: Part 2 - Robert Timms, Scott Marquis, Valentin Sulzer, Colin Please, S Jon Chapman diff --git a/results/2plus1D/_matplotlibrc b/results/2plus1D/_matplotlibrc deleted file mode 100644 index 7e8738810d..0000000000 --- a/results/2plus1D/_matplotlibrc +++ /dev/null @@ -1,27 +0,0 @@ -# Set a 2/1 aspect ratio with automatic layout. -figure.figsize: 6.4, 4. - -# Use LaTeX fonts -font.family: serif -font.serif: ComputerModern -text.usetex: true - -# Backend options (42: use TrueType fonts) -pdf.fonttype: 42 -ps.fonttype: 42 - -# Set large font sizes for paper figures -axes.labelsize: 11 -axes.titlesize: 11 -xtick.labelsize: 11 -ytick.labelsize: 11 -legend.fontsize: 11 -legend.numpoints: 1 -legend.scatterpoints: 1 - -# Set lines and ticks according to the `seaborn-paper` style sheet -grid.linewidth: 0.8 -lines.linewidth: 1.4 -patch.linewidth: 0.24 -lines.markersize: 5.6 -lines.markeredgewidth: 0 diff --git a/results/2plus1D/compare_lead_acid_1plus1D.py b/results/2plus1D/compare_lead_acid_1plus1D.py deleted file mode 100644 index a6b015437e..0000000000 --- a/results/2plus1D/compare_lead_acid_1plus1D.py +++ /dev/null @@ -1,73 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level and increase recursion limit -pybamm.set_logging_level("INFO") -sys.setrecursionlimit(10000) - -# load models -models = [ - pybamm.lead_acid.Full(name="1D Full"), - pybamm.lead_acid.Composite(name="1D composite"), - pybamm.lead_acid.LOQS(name="1D LOQS"), - pybamm.lead_acid.Full( - {"current collector": "potential pair", "dimensionality": 1}, name="1+1D Full" - ), - pybamm.lead_acid.Composite( - {"current collector": "potential pair", "dimensionality": 1}, - name="1+1D composite", - ), - pybamm.lead_acid.LOQS( - {"current collector": "potential pair", "dimensionality": 1}, name="1+1D LOQS" - ), -] - -# load parameter values and process models -param = models[0].default_parameter_values -for model in models: - param.process_model(model) - -# process geometry and discretise models -meshes = [None] * len(models) -for i, model in enumerate(models): - geometry = model.default_geometry - param.process_geometry(geometry) - var = pybamm.standard_spatial_vars - var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, - } - meshes[i] = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - disc = pybamm.Discretisation(meshes[i], model.default_spatial_methods) - disc.process_model(model) - -# solve models and process time and voltage for plotting on different meshes -solutions = [None] * len(models) -times = [None] * len(models) -voltages = [None] * len(models) -t_eval = np.linspace(0, 1, 1000) -for i, model in enumerate(models): - solution = model.default_solver.solve(model, t_eval) - solutions[i] = solution - times[i] = pybamm.ProcessedVariable( - model.variables["Time [h]"], solution.t, solution.y - ) - voltages[i] = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=meshes[i] - ) - -# plot terminal voltage -t = np.linspace(0, solution.t[-1], 100) -for i, model in enumerate(models): - plt.plot(times[i](t), voltages[i](t), lw=2, label=model.name) -plt.xlabel("Time [h]", fontsize=15) -plt.ylabel("Terminal voltage [V]", fontsize=15) -plt.legend(fontsize=15) -plt.show() diff --git a/results/2plus1D/compare_lead_acid_2plus1D.py b/results/2plus1D/compare_lead_acid_2plus1D.py deleted file mode 100644 index afd7fdc6de..0000000000 --- a/results/2plus1D/compare_lead_acid_2plus1D.py +++ /dev/null @@ -1,75 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level and increase recursion limit -pybamm.set_logging_level("INFO") -sys.setrecursionlimit(10000) - -# load models -models = [ - pybamm.lead_acid.Full(name="1D Full"), - pybamm.lead_acid.Composite(name="1D composite"), - pybamm.lead_acid.LOQS(name="1D LOQS"), - pybamm.lead_acid.Full( - {"current collector": "potential pair", "dimensionality": 2}, name="2+1D Full" - ), - pybamm.lead_acid.Composite( - {"current collector": "potential pair", "dimensionality": 2}, - name="2+1D composite", - ), - pybamm.lead_acid.LOQS( - {"current collector": "potential pair", "dimensionality": 2}, name="2+1D LOQS" - ), -] - -# load parameter values and process models -param = models[0].default_parameter_values -for model in models: - param.process_model(model) - -# process geometry and discretise models -meshes = [None] * len(models) -for i, model in enumerate(models): - geometry = model.default_geometry - param.process_geometry(geometry) - var = pybamm.standard_spatial_vars - var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, - } - meshes[i] = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - disc = pybamm.Discretisation(meshes[i], model.default_spatial_methods) - disc.process_model(model) - -# solve models and process time and voltage for plotting on different meshes -solutions = [None] * len(models) -times = [None] * len(models) -voltages = [None] * len(models) -t_eval = np.linspace(0, 1, 1000) -for i, model in enumerate(models): - if "2+1D" in model.name: - model.use_simplify = False # simplifying jacobian slow for large systems - solution = model.default_solver.solve(model, t_eval) - solutions[i] = solution - times[i] = pybamm.ProcessedVariable( - model.variables["Time [h]"], solution.t, solution.y - ) - voltages[i] = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=meshes[i] - ) - -# plot terminal voltage -t = np.linspace(0, solution.t[-1], 100) -for i, model in enumerate(models): - plt.plot(times[i](t), voltages[i](t), lw=2, label=model.name) -plt.xlabel("Time [h]", fontsize=15) -plt.ylabel("Terminal voltage [V]", fontsize=15) -plt.legend(fontsize=15) -plt.show() diff --git a/results/2plus1D/compare_lithium_ion_2plus1D.py b/results/2plus1D/compare_lithium_ion_2plus1D.py deleted file mode 100644 index dfe33bba10..0000000000 --- a/results/2plus1D/compare_lithium_ion_2plus1D.py +++ /dev/null @@ -1,96 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level and increase recursion limit -pybamm.set_logging_level("INFO") -sys.setrecursionlimit(10000) - -# load models -models = [ - pybamm.lithium_ion.SPM(name="1D SPM"), - pybamm.lithium_ion.SPMe(name="1D SPMe"), - pybamm.lithium_ion.DFN(name="1D DFN"), - pybamm.lithium_ion.SPM( - {"current collector": "potential pair", "dimensionality": 2}, name="2+1D SPM" - ), - pybamm.lithium_ion.SPMe( - {"current collector": "potential pair", "dimensionality": 2}, name="2+1D SPMe" - ), - pybamm.lithium_ion.DFN( - {"current collector": "potential pair", "dimensionality": 2}, name="2+1D DFN" - ), -] - -# load parameter values -param = models[0].default_parameter_values -C_rate = 1 -param.update({"C-rate": C_rate}) -# make current collectors not so conductive, just for illustrative purposes -param.update( - { - "Negative current collector conductivity [S.m-1]": 5.96e6, - "Positive current collector conductivity [S.m-1]": 3.55e6, - } -) - -# process models -for model in models: - param.process_model(model) - -# process geometry and discretise models -meshes = [None] * len(models) -for i, model in enumerate(models): - geometry = model.default_geometry - param.process_geometry(geometry) - var = pybamm.standard_spatial_vars - var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, - } - meshes[i] = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - disc = pybamm.Discretisation(meshes[i], model.default_spatial_methods) - disc.process_model(model) - -# solve models and process time and voltage for plotting on different meshes -solutions = [None] * len(models) -times = [None] * len(models) -voltages = [None] * len(models) -t_eval = np.linspace(0, 1, 1000) -for i, model in enumerate(models): - if "2+1D" in model.name: - model.use_simplify = False # simplifying jacobian slow for large systems - solution = model.default_solver.solve(model, t_eval) - solutions[i] = solution - times[i] = pybamm.ProcessedVariable( - model.variables["Time [h]"], solution.t, solution.y - ) - voltages[i] = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=meshes[i] - ) - -# plot terminal voltage -t = np.linspace(0, solution.t[-1], 100) -for i, model in enumerate(models): - plt.plot(times[i](t), voltages[i](t), label=model.name) -plt.xlabel("Time [h]") -plt.ylabel("Terminal voltage [V]") -plt.legend() -# add C-rate, delta, and alpha to title -delta = param.evaluate(pybamm.standard_parameters_lithium_ion.delta) -alpha = param.evaluate(pybamm.standard_parameters_lithium_ion.alpha) -plt.title( - r"C-rate = {:3d}, $\alpha$ = {:.6f} , $\delta$ = {:.6f}".format( - C_rate, alpha, delta - ) -) -# save and show -file_name = "discharge_curve_2plus1D_comparison.eps" -plt.savefig(file_name, format="eps", dpi=1000) -plt.show() diff --git a/results/2plus1D/compare_spmecc.py b/results/2plus1D/compare_spmecc.py deleted file mode 100644 index 2f4cd2f136..0000000000 --- a/results/2plus1D/compare_spmecc.py +++ /dev/null @@ -1,196 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level and increase recursion limit -pybamm.set_logging_level("INFO") -sys.setrecursionlimit(10000) - -# load current collector and SPMe models -cc_model = pybamm.current_collector.EffectiveResistance2D() -spme_av = pybamm.lithium_ion.SPMe(name="Average SPMe") -spme = pybamm.lithium_ion.SPMe( - {"current collector": "potential pair", "dimensionality": 2}, name="2+1D SPMe" -) -models = {"Current collector": cc_model, "Average SPMe": spme_av, "2+1D SPMe": spme} - -# set parameters based on the spme -param = spme.default_parameter_values - -# set mesh -var = pybamm.standard_spatial_vars -var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, -} - -# process model and geometry, and discretise -meshes = {} -for name, model in models.items(): - param.process_model(model) - geometry = model.default_geometry - param.process_geometry(geometry) - meshes[name] = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - disc = pybamm.Discretisation(meshes[name], model.default_spatial_methods) - disc.process_model(model) - -# solve models -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solutions = {} -for name, model in models.items(): - if name == "Current collector": - solutions[name] = model.default_solver.solve(model) - else: - solutions[name] = model.default_solver.solve(model, t_eval) - -# plot terminal voltage -for name in ["Average SPMe", "2+1D SPMe"]: - t, y = solutions[name].t, solutions[name].y - model = models[name] - time = pybamm.ProcessedVariable(model.variables["Time [h]"], t, y)(t) - voltage = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], t, y, mesh=meshes[name] - )(t) - - # add current collector Ohmic losses to average SPMEe to get SPMeCC voltage - if model.name == "Average SPMe": - current = pybamm.ProcessedVariable(model.variables["Current [A]"], t, y)(t) - delta = param.evaluate(pybamm.standard_parameters_lithium_ion.delta) - R_cc = param.process_symbol( - cc_model.variables["Effective current collector resistance [Ohm]"] - ).evaluate( - t=solutions["Current collector"].t, y=solutions["Current collector"].y - )[ - 0 - ][ - 0 - ] - cc_ohmic_losses = -delta * current * R_cc - voltage = voltage + cc_ohmic_losses - - # plot - plt.plot(time, voltage, label=model.name) -plt.xlabel("Time [h]") -plt.ylabel("Terminal voltage [V]") -plt.legend() - - -# plot potentials in current collector - -# get processed potentials from SPMeCC -V_av = pybamm.ProcessedVariable( - spme_av.variables["Terminal voltage"], - solutions["Average SPMe"].t, - solutions["Average SPMe"].y, - mesh=meshes["Average SPMe"], -) -I_av = pybamm.ProcessedVariable( - spme_av.variables["Total current density"], - solutions["Average SPMe"].t, - solutions["Average SPMe"].y, - mesh=meshes["Average SPMe"], -) -potentials = cc_model.get_processed_potentials( - solutions["Current collector"], meshes["Current collector"], param, V_av, I_av -) -phi_s_cn_spmecc = potentials["Negative current collector potential [V]"] -phi_s_cp_spmecc = potentials["Positive current collector potential [V]"] - -# get processed potentials from 2+1D SPMe -phi_s_cn = pybamm.ProcessedVariable( - model.variables["Negative current collector potential [V]"], - solutions["2+1D SPMe"].t, - solutions["2+1D SPMe"].y, - mesh=meshes["2+1D SPMe"], -) -phi_s_cp = pybamm.ProcessedVariable( - model.variables["Positive current collector potential [V]"], - solutions["2+1D SPMe"].t, - solutions["2+1D SPMe"].y, - mesh=meshes["2+1D SPMe"], -) - -# make plot -l_y = phi_s_cp.y_sol[-1] -l_z = phi_s_cp.z_sol[-1] -y_plot = np.linspace(0, l_y, 21) -z_plot = np.linspace(0, l_z, 21) - - -def plot(t): - plt.subplots(figsize=(15, 8)) - plt.tight_layout() - plt.subplots_adjust(left=-0.1) - - # negative current collector potential - plt.subplot(221) - phi_s_cn_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cn(y=y_plot, z=z_plot, t=t)), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cn}$") - plt.set_cmap("cividis") - plt.colorbar(phi_s_cn_plot) - plt.subplot(222) - phi_s_cn_spmecc_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cn_spmecc(y=y_plot, z=z_plot, t=t)), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cn}$ SPMeCC") - plt.set_cmap("cividis") - plt.colorbar(phi_s_cn_spmecc_plot) - - # positive current collector potential - plt.subplot(223) - phi_s_cp_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cp(y=y_plot, z=z_plot, t=t)), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cp}$") - plt.set_cmap("viridis") - plt.colorbar(phi_s_cp_plot) - plt.subplot(224) - phi_s_cp_spmecc_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cp_spmecc(y=y_plot, z=z_plot, t=t)), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cp}$ SPMeCC") - plt.set_cmap("viridis") - plt.colorbar(phi_s_cp_spmecc_plot) - - plt.subplots_adjust( - top=0.92, bottom=0.15, left=0.10, right=0.9, hspace=0.5, wspace=0.5 - ) - - -plot(solutions["2+1D SPMe"].t[-1] / 2) -plt.show() diff --git a/results/2plus1D/compare_thermal_lithium_ion_2plus1D.py b/results/2plus1D/compare_thermal_lithium_ion_2plus1D.py deleted file mode 100644 index c1ac708e98..0000000000 --- a/results/2plus1D/compare_thermal_lithium_ion_2plus1D.py +++ /dev/null @@ -1,133 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level and increase recursion limit -pybamm.set_logging_level("INFO") -sys.setrecursionlimit(10000) - -# load models -models = [ - pybamm.lithium_ion.SPM({"thermal": "x-lumped"}, name="1D SPM (lumped)"), - pybamm.lithium_ion.SPMe({"thermal": "x-lumped"}, name="1D SPMe (lumped)"), - pybamm.lithium_ion.DFN({"thermal": "x-lumped"}, name="1D DFN (lumped)"), - pybamm.lithium_ion.SPM({"thermal": "x-full"}, name="1D SPM (full)"), - pybamm.lithium_ion.SPMe({"thermal": "x-full"}, name="1D SPMe (full)"), - pybamm.lithium_ion.DFN({"thermal": "x-full"}, name="1D DFN (full)"), - pybamm.lithium_ion.SPM( - { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "xyz-lumped", - }, - name="2+1D SPM (lumped)", - ), - pybamm.lithium_ion.SPMe( - { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "xyz-lumped", - }, - name="2+1D SPMe (lumped)", - ), - pybamm.lithium_ion.DFN( - { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "xyz-lumped", - }, - name="2+1D DFN (lumped)", - ), - pybamm.lithium_ion.SPM( - { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", - }, - name="2+1D SPM (full)", - ), - pybamm.lithium_ion.SPMe( - { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", - }, - name="2+1D SPMe (full)", - ), - pybamm.lithium_ion.DFN( - { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", - }, - name="2+1D DFN (full)", - ), -] - -# load parameter values -param = models[0].default_parameter_values - -# process models -for model in models: - param.process_model(model) - -# process geometry and discretise models -meshes = [None] * len(models) -for i, model in enumerate(models): - geometry = model.default_geometry - param.process_geometry(geometry) - var = pybamm.standard_spatial_vars - var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, - } - meshes[i] = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - disc = pybamm.Discretisation(meshes[i], model.default_spatial_methods) - disc.process_model(model) - -# solve models and process time and voltage for plotting on different meshes -solutions = [None] * len(models) -times = [None] * len(models) -voltages = [None] * len(models) -temperatures = [None] * len(models) - -t_eval = np.linspace(0, 1, 1000) -for i, model in enumerate(models): - if "2+1D" in model.name: - model.use_simplify = False # simplifying jacobian slow for large systems - solution = model.default_solver.solve(model, t_eval) - solutions[i] = solution - times[i] = pybamm.ProcessedVariable( - model.variables["Time [h]"], solution.t, solution.y - ) - voltages[i] = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=meshes[i] - ) - temperatures[i] = pybamm.ProcessedVariable( - model.variables["Volume-averaged cell temperature [K]"], - solution.t, - solution.y, - mesh=meshes[i], - ) - -# plot terminal voltage and temperature -t = np.linspace(0, solution.t[-1], 100) -plt.subplot(121) -for i, model in enumerate(models): - plt.plot(times[i](t), voltages[i](t), label=model.name) -plt.xlabel("Time [h]") -plt.ylabel("Terminal voltage [V]") -plt.legend() -plt.subplot(122) -for i, model in enumerate(models): - plt.plot(times[i](t), temperatures[i](t), label=model.name) -plt.xlabel("Time [h]") -plt.ylabel("Temperature [K]") -plt.tight_layout() -plt.show() diff --git a/results/2plus1D/dfn_2plus1D.py b/results/2plus1D/dfn_2plus1D.py deleted file mode 100644 index 32daf9ed58..0000000000 --- a/results/2plus1D/dfn_2plus1D.py +++ /dev/null @@ -1,137 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level -pybamm.set_logging_level("INFO") - -# load (2+1D) DFN model -options = { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", -} -model = pybamm.lithium_ion.DFN(options) -model.use_simplify = False # simplifying jacobian slow for large systems - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -var = pybamm.standard_spatial_vars -var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, -} -# depending on number of points in y-z plane may need to increase recursion depth... -sys.setrecursionlimit(10000) -mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = pybamm.IDAKLUSolver().solve(model, t_eval) - -# TO DO: 2+1D automated plotting -phi_s_cn = pybamm.ProcessedVariable( - model.variables["Negative current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -phi_s_cp = pybamm.ProcessedVariable( - model.variables["Positive current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -T = pybamm.ProcessedVariable( - model.variables["X-averaged cell temperature [K]"], - solution.t, - solution.y, - mesh=mesh, -) -l_y = phi_s_cp.y_sol[-1] -l_z = phi_s_cp.z_sol[-1] -y_plot = np.linspace(0, l_y, 21) -z_plot = np.linspace(0, l_z, 21) - - -def plot(t): - fig, ax = plt.subplots(figsize=(15, 8)) - plt.tight_layout() - plt.subplots_adjust(left=-0.1) - - # find t index - ind = (np.abs(solution.t - t)).argmin() - - # negative current collector potential - plt.subplot(131) - phi_s_cn_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cn(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cn}$ [V]") - plt.set_cmap("cividis") - plt.colorbar(phi_s_cn_plot) - - # positive current collector potential - plt.subplot(132) - phi_s_cp_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cp(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cp}$ [V]") - plt.set_cmap("viridis") - plt.colorbar(phi_s_cp_plot) - - # temperature - plt.subplot(133) - T_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(T(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$T$ [K]") - plt.set_cmap("inferno") - plt.colorbar(T_plot) - - plt.subplots_adjust( - top=0.92, bottom=0.15, left=0.10, right=0.9, hspace=0.5, wspace=0.5 - ) - plt.show() - - -plot(solution.t[-1] / 2) diff --git a/results/2plus1D/set_temperature_spm_1plus1D.py b/results/2plus1D/set_temperature_spm_1plus1D.py deleted file mode 100644 index c469d76079..0000000000 --- a/results/2plus1D/set_temperature_spm_1plus1D.py +++ /dev/null @@ -1,51 +0,0 @@ -# -# Example of 1+1D SPM where the temperature can be set by the user -# - -import pybamm -import numpy as np - -# set logging level -pybamm.set_logging_level("INFO") - -model_options = { - "current collector": "potential pair", - "dimensionality": 1, - "thermal": "x-lumped", - "external submodels": ["thermal"], -} -model = pybamm.lithium_ion.SPMe(model_options) - -var = pybamm.standard_spatial_vars -z_pts = 20 -var_pts = {var.x_n: 5, var.x_s: 5, var.x_p: 5, var.r_n: 5, var.r_p: 5, var.z: z_pts} - -sim = pybamm.Simulation(model, var_pts=var_pts, C_rate=2) - -# Set the temperature (in dimensionless form) -# T_av = np.linspace(0, 1, z_pts)[:, np.newaxis] - -z = np.linspace(0, 1, z_pts) -t_eval = np.linspace(0, 0.13, 50) -# step through the solver, setting the temperature at each timestep -for i in np.arange(1, len(t_eval) - 1): - dt = t_eval[i + 1] - t_eval[i] - T_av = (np.sin(2 * np.pi * z) * np.sin(2 * np.pi * 100 * t_eval[i]))[ - :, np.newaxis - ] - external_variables = {"X-averaged cell temperature": T_av} - sim.step(dt, external_variables=external_variables) - -sim.plot( - [ - "Terminal voltage [V]", - "X-averaged total heating [W.m-3]", - "X-averaged cell temperature [K]", - "X-averaged negative particle surface concentration [mol.m-3]", - "X-averaged positive particle surface concentration [mol.m-3]", - "Negative current collector potential [V]", - "Positive current collector potential [V]", - "Local voltage [V]", - ] -) - diff --git a/results/2plus1D/spm_1plus1D.py b/results/2plus1D/spm_1plus1D.py deleted file mode 100644 index 993ab68ae9..0000000000 --- a/results/2plus1D/spm_1plus1D.py +++ /dev/null @@ -1,63 +0,0 @@ -import pybamm -import numpy as np -import sys - -# set logging level -pybamm.set_logging_level("INFO") - -# load (1+1D) SPMe model -options = { - "current collector": "potential pair", - "dimensionality": 1, - "thermal": "lumped", -} -model = pybamm.lithium_ion.SPM(options) - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -C_rate = 1 -current_1C = 24 * param.process_symbol(pybamm.geometric_parameters.A_cc).evaluate() -param.update( - { - "Typical current [A]": C_rate * current_1C, - "Initial temperature [K]": 298.15, - "Negative current collector conductivity [S.m-1]": 1e7, - "Positive current collector conductivity [S.m-1]": 1e7, - "Heat transfer coefficient [W.m-2.K-1]": 1, - } -) -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -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: 15} -# depending on number of points in y-z plane may need to increase recursion depth... -sys.setrecursionlimit(10000) -mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = model.default_solver.solve(model, t_eval) - -# plot -output_variables = [ - "X-averaged negative particle surface concentration [mol.m-3]", - "X-averaged positive particle surface concentration [mol.m-3]", - # "X-averaged cell temperature [K]", - "Local potenital difference [V]", - "Current collector current density [A.m-2]", - "Terminal voltage [V]", - "Volume-averaged cell temperature [K]", -] -plot = pybamm.QuickPlot(model, mesh, solution, output_variables) -plot.dynamic_plot() diff --git a/results/2plus1D/spm_2plus1D.py b/results/2plus1D/spm_2plus1D.py deleted file mode 100644 index 5f95cd31d0..0000000000 --- a/results/2plus1D/spm_2plus1D.py +++ /dev/null @@ -1,137 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level -pybamm.set_logging_level("DEBUG") - -# load (2+1D) SPM model -options = { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", -} -model = pybamm.lithium_ion.SPM(options) -model.use_simplify = False # simplifying jacobian slow for large systems - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -var = pybamm.standard_spatial_vars -var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, -} -# depending on number of points in y-z plane may need to increase recursion depth... -sys.setrecursionlimit(10000) -mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = pybamm.IDAKLUSolver().solve(model, t_eval) - -# TO DO: 2+1D automated plotting -phi_s_cn = pybamm.ProcessedVariable( - model.variables["Negative current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -phi_s_cp = pybamm.ProcessedVariable( - model.variables["Positive current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -T = pybamm.ProcessedVariable( - model.variables["X-averaged cell temperature [K]"], - solution.t, - solution.y, - mesh=mesh, -) -l_y = phi_s_cp.y_sol[-1] -l_z = phi_s_cp.z_sol[-1] -y_plot = np.linspace(0, l_y, 21) -z_plot = np.linspace(0, l_z, 21) - - -def plot(t): - fig, ax = plt.subplots(figsize=(15, 8)) - plt.tight_layout() - plt.subplots_adjust(left=-0.1) - - # find t index - ind = (np.abs(solution.t - t)).argmin() - - # negative current collector potential - plt.subplot(131) - phi_s_cn_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cn(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cn}$ [V]") - plt.set_cmap("cividis") - plt.colorbar(phi_s_cn_plot) - - # positive current collector potential - plt.subplot(132) - phi_s_cp_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cp(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cp}$ [V]") - plt.set_cmap("viridis") - plt.colorbar(phi_s_cp_plot) - - # temperature - plt.subplot(133) - T_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(T(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$T$ [K]") - plt.set_cmap("inferno") - plt.colorbar(T_plot) - - plt.subplots_adjust( - top=0.92, bottom=0.15, left=0.10, right=0.9, hspace=0.5, wspace=0.5 - ) - plt.show() - - -plot(solution.t[-1] / 2) diff --git a/results/2plus1D/spm_2plus1D_tab_grid.py b/results/2plus1D/spm_2plus1D_tab_grid.py deleted file mode 100644 index 19835003c5..0000000000 --- a/results/2plus1D/spm_2plus1D_tab_grid.py +++ /dev/null @@ -1,138 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level -pybamm.set_logging_level("INFO") - -# load (2+1D) SPM model -options = { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", -} -model = pybamm.lithium_ion.SPM(options) - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -var = pybamm.standard_spatial_vars -var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, -} -submesh_types = model.default_submesh_types -submesh_types["current collector"] = pybamm.ScikitExponential2DSubMesh -# depnding on number of points in y-z plane may need to increase recursion depth... -sys.setrecursionlimit(10000) -mesh = pybamm.Mesh(geometry, submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = model.default_solver.solve(model, t_eval) - -# TO DO: 2+1D automated plotting -phi_s_cn = pybamm.ProcessedVariable( - model.variables["Negative current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -phi_s_cp = pybamm.ProcessedVariable( - model.variables["Positive current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -T = pybamm.ProcessedVariable( - model.variables["X-averaged cell temperature [K]"], - solution.t, - solution.y, - mesh=mesh, -) -l_y = phi_s_cp.y_sol[-1] -l_z = phi_s_cp.z_sol[-1] -y_plot = np.linspace(0, l_y, 21) -z_plot = np.linspace(0, l_z, 21) - - -def plot(t): - fig, ax = plt.subplots(figsize=(15, 8)) - plt.tight_layout() - plt.subplots_adjust(left=-0.1) - - # find t index - ind = (np.abs(solution.t - t)).argmin() - - # negative current collector potential - plt.subplot(131) - phi_s_cn_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cn(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cn}$ [V]") - plt.set_cmap("cividis") - plt.colorbar(phi_s_cn_plot) - - # positive current collector potential - plt.subplot(132) - phi_s_cp_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cp(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cp}$ [V]") - plt.set_cmap("viridis") - plt.colorbar(phi_s_cp_plot) - - # temperature - plt.subplot(133) - T_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(T(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$T$ [K]") - plt.set_cmap("inferno") - plt.colorbar(T_plot) - - plt.subplots_adjust( - top=0.92, bottom=0.15, left=0.10, right=0.9, hspace=0.5, wspace=0.5 - ) - plt.show() - - -plot(solution.t[-1] / 2) diff --git a/results/2plus1D/spme_2plus1D.py b/results/2plus1D/spme_2plus1D.py deleted file mode 100644 index c7256b993a..0000000000 --- a/results/2plus1D/spme_2plus1D.py +++ /dev/null @@ -1,137 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt -import sys - -# set logging level -pybamm.set_logging_level("INFO") - -# load (2+1D) SPMe model -options = { - "current collector": "potential pair", - "dimensionality": 2, - "thermal": "x-lumped", -} -model = pybamm.lithium_ion.SPMe(options) -model.use_simplify = False # simplifying jacobian slow for large systems - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -var = pybamm.standard_spatial_vars -var_pts = { - var.x_n: 5, - var.x_s: 5, - var.x_p: 5, - var.r_n: 5, - var.r_p: 5, - var.y: 5, - var.z: 5, -} -# depending on number of points in y-z plane may need to increase recursion depth... -sys.setrecursionlimit(10000) -mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = pybamm.IDAKLUSolver().solve(model, t_eval) - -# TO DO: 2+1D automated plotting -phi_s_cn = pybamm.ProcessedVariable( - model.variables["Negative current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -phi_s_cp = pybamm.ProcessedVariable( - model.variables["Positive current collector potential [V]"], - solution.t, - solution.y, - mesh=mesh, -) -T = pybamm.ProcessedVariable( - model.variables["X-averaged cell temperature [K]"], - solution.t, - solution.y, - mesh=mesh, -) -l_y = phi_s_cp.y_sol[-1] -l_z = phi_s_cp.z_sol[-1] -y_plot = np.linspace(0, l_y, 21) -z_plot = np.linspace(0, l_z, 21) - - -def plot(t): - fig, ax = plt.subplots(figsize=(15, 8)) - plt.tight_layout() - plt.subplots_adjust(left=-0.1) - - # find t index - ind = (np.abs(solution.t - t)).argmin() - - # negative current collector potential - plt.subplot(131) - phi_s_cn_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cn(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cn}$ [V]") - plt.set_cmap("cividis") - plt.colorbar(phi_s_cn_plot) - - # positive current collector potential - plt.subplot(132) - phi_s_cp_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(phi_s_cp(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$\phi_{s,cp}$ [V]") - plt.set_cmap("viridis") - plt.colorbar(phi_s_cp_plot) - - # temperature - plt.subplot(133) - T_plot = plt.pcolormesh( - y_plot, - z_plot, - np.transpose(T(y=y_plot, z=z_plot, t=solution.t[ind])), - shading="gouraud", - ) - - plt.axis([0, l_y, 0, l_z]) - plt.xlabel(r"$y$") - plt.ylabel(r"$z$") - plt.title(r"$T$ [K]") - plt.set_cmap("inferno") - plt.colorbar(T_plot) - - plt.subplots_adjust( - top=0.92, bottom=0.15, left=0.10, right=0.9, hspace=0.5, wspace=0.5 - ) - plt.show() - - -plot(solution.t[-1] / 2) diff --git a/results/2plus1D/spmecc.py b/results/2plus1D/spmecc.py deleted file mode 100644 index 0939667226..0000000000 --- a/results/2plus1D/spmecc.py +++ /dev/null @@ -1,59 +0,0 @@ -import pybamm -import numpy as np -import matplotlib.pyplot as plt - -# set logging level -pybamm.set_logging_level("INFO") - -# load current collector and SPMe models -cell_model = pybamm.lithium_ion.SPMe() -cc_model = pybamm.current_collector.EffectiveResistance2D() -models = [cell_model, cc_model] - -# set parameters based on the cell model -param = cell_model.default_parameter_values - -# make current collectors not so conductive, just for illustrative purposes -param.update( - { - "Negative current collector conductivity [S.m-1]": 5.96e6, - "Positive current collector conductivity [S.m-1]": 3.55e6, - } -) - -# process model and geometry, and discretise -for model in models: - param.process_model(model) - geometry = model.default_geometry - param.process_geometry(geometry) - mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) - disc = pybamm.Discretisation(mesh, model.default_spatial_methods) - disc.process_model(model) - - -# solve current collector model -cc_solution = cc_model.default_solver.solve(cc_model) - -# solve SPMe -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = cell_model.default_solver.solve(cell_model, t_eval) - -# plot terminal voltage -t, y = solution.t, solution.y -time = pybamm.ProcessedVariable(cell_model.variables["Time [h]"], t, y)(t) -voltage = pybamm.ProcessedVariable(cell_model.variables["Terminal voltage [V]"], t, y) -current = pybamm.ProcessedVariable(cell_model.variables["Current [A]"], t, y)(t) -delta = param.evaluate(pybamm.standard_parameters_lithium_ion.delta) -R_cc = param.process_symbol( - cc_model.variables["Effective current collector resistance [Ohm]"] -).evaluate(t=cc_solution.t, y=cc_solution.y)[0][0] -cc_ohmic_losses = -delta * current * R_cc - -plt.plot(time, voltage(t), label="SPMe") -plt.plot(time, voltage(t) + cc_ohmic_losses, label="SPMeCC") -plt.xlabel("Time [h]") -plt.ylabel("Terminal voltage [V]") -plt.legend() -plt.show() diff --git a/results/2plus1D/user_mesh_spm_1plus1D.py b/results/2plus1D/user_mesh_spm_1plus1D.py deleted file mode 100644 index 9d479d6a66..0000000000 --- a/results/2plus1D/user_mesh_spm_1plus1D.py +++ /dev/null @@ -1,73 +0,0 @@ -import pybamm -import numpy as np -import sys - -# set logging level -pybamm.set_logging_level("INFO") - -# load (1+1D) SPM model -options = { - "current collector": "potential pair", - "dimensionality": 1, - "thermal": "lumped", -} -model = pybamm.lithium_ion.SPM(options) - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -C_rate = 1 -current_1C = 24 * param.evaluate(pybamm.geometric_parameters.A_cc) -param.update( - { - "Typical current [A]": C_rate * current_1C, - "Initial temperature [K]": 298.15, - "Negative current collector conductivity [S.m-1]": 1e5, - "Positive current collector conductivity [S.m-1]": 1e5, - "Heat transfer coefficient [W.m-2.K-1]": 1, - "Negative tab centre z-coordinate [m]": 0, # negative tab at bottom - "Positive tab centre z-coordinate [m]": 0.137, # positive tab at top - } -) -param.process_model(model) -param.process_geometry(geometry) - -# set mesh using user-supplied edges in z -z_edges = np.array([0, 0.025, 0.05, 0.1, 0.3, 0.5, 0.7, 0.9, 0.95, 0.975, 1]) -submesh_types = model.default_submesh_types -submesh_types["current collector"] = pybamm.MeshGenerator( - pybamm.UserSupplied1DSubMesh, submesh_params={"edges": z_edges} -) -# Need to make sure var_pts for z is one less than number of edges (variables are -# evaluated at cell centres) -npts_z = len(z_edges) - 1 -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: npts_z} -# depending on number of points in y-z plane may need to increase recursion depth... -sys.setrecursionlimit(10000) -mesh = pybamm.Mesh(geometry, submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model -- simulate one hour discharge -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval = np.linspace(0, t_end, 120) -solution = model.default_solver.solve(model, t_eval) - -# plot -output_variables = [ - "X-averaged negative particle surface concentration [mol.m-3]", - "X-averaged positive particle surface concentration [mol.m-3]", - # "X-averaged cell temperature [K]", - "Local current collector potential difference [V]", - "Current collector current density [A.m-2]", - "Terminal voltage [V]", - "Volume-averaged cell temperature [K]", -] -plot = pybamm.QuickPlot(model, mesh, solution, output_variables) -plot.dynamic_plot() diff --git a/results/change_settings/change_solver_tolerances.py b/results/change_settings/change_solver_tolerances.py deleted file mode 100644 index 81261e796d..0000000000 --- a/results/change_settings/change_solver_tolerances.py +++ /dev/null @@ -1,52 +0,0 @@ -# -# Compare solution of li-ion battery models when changing solver tolerances -# -import numpy as np -import pybamm - -pybamm.set_logging_level("INFO") - -# load model -model = pybamm.lithium_ion.DFN() - - -# process and discretise -param = model.default_parameter_values -param.process_model(model) -geometry = model.default_geometry -param.process_geometry(geometry) -mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# tolerances (rtol, atol) -tols = [[1e-8, 1e-8], [1e-6, 1e-6], [1e-3, 1e-6], [1e-3, 1e-3]] - -# solve model -solutions = [None] * len(tols) -voltages = [None] * len(tols) -voltage_rmse = [None] * len(tols) -labels = [None] * len(tols) -t_eval = np.linspace(0, 0.17, 100) -for i, tol in enumerate(tols): - solver = pybamm.ScikitsDaeSolver(rtol=tol[0], atol=tol[1]) - solutions[i] = solver.solve(model, t_eval) - voltages[i] = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], - solutions[i].t, - solutions[i].y, - mesh=mesh, - )(solutions[i].t) - voltage_rmse[i] = pybamm.rmse(voltages[0], voltages[i]) - labels[i] = "rtol = {}, atol = {}".format(tol[0], tol[1]) - -# print RMSE voltage errors vs tighest tolerance -for i, tol in enumerate(tols): - print( - "rtol = {}, atol = {}, solve time = {} s, Voltage RMSE = {}".format( - tol[0], tol[1], solutions[i].solve_time, voltage_rmse[i] - ) - ) -# plot -plot = pybamm.QuickPlot([model] * len(solutions), mesh, solutions, labels=labels) -plot.dynamic_plot() diff --git a/results/change_settings/compare_var_pts.py b/results/change_settings/compare_var_pts.py deleted file mode 100644 index 2153a96f62..0000000000 --- a/results/change_settings/compare_var_pts.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# Compare solution of li-ion battery models when varying the number of grid points -# -import numpy as np -import pybamm -import matplotlib.pyplot as plt - -pybamm.set_logging_level("INFO") - -# choose number of points per domain (all domains will have same npts) -Npts = [30, 20, 10, 5] - -# create models -models = [None] * len(Npts) -for i, npts in enumerate(Npts): - models[i] = pybamm.lithium_ion.DFN(name="Npts = {}".format(npts)) - -# load parameter values and process models and geometry -param = models[0].default_parameter_values -for model in models: - param.process_model(model) - -# set mesh -meshes = [None] * len(models) - -# create geometry and discretise models -var = pybamm.standard_spatial_vars -for i, model in enumerate(models): - geometry = model.default_geometry - param.process_geometry(geometry) - var_pts = { - var.x_n: Npts[i], - var.x_s: Npts[i], - var.x_p: Npts[i], - var.r_n: Npts[i], - var.r_p: Npts[i], - } - meshes[i] = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts) - disc = pybamm.Discretisation(meshes[i], model.default_spatial_methods) - disc.process_model(model) - -# solve model and plot voltage -solutions = [None] * len(models) -voltages = [None] * len(models) -voltage_rmse = [None] * len(models) -t_eval = np.linspace(0, 0.17, 100) -for i, model in enumerate(models): - solutions[i] = model.default_solver.solve(model, t_eval) - voltages[i] = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], - solutions[i].t, - solutions[i].y, - mesh=meshes[i], - )(solutions[i].t) - voltage_rmse[i] = pybamm.rmse(voltages[0], voltages[i]) - plt.plot(solutions[i].t, voltages[i], label=model.name) - -for i, npts in enumerate(Npts): - print( - "npts = {}, solve time = {} s, Voltage RMSE = {}".format( - npts, solutions[i].solve_time, voltage_rmse[i] - ) - ) - -plt.xlabel(r"$t$") -plt.ylabel("Voltage [V]") -plt.legend() -plt.show() diff --git a/results/drive_cycles/US06_simulation.py b/results/drive_cycles/US06_simulation.py deleted file mode 100644 index 64f47fd5e5..0000000000 --- a/results/drive_cycles/US06_simulation.py +++ /dev/null @@ -1,42 +0,0 @@ -# -# Simulate drive cycle loaded from csv file -# -import pybamm -import numpy as np - -# load model -pybamm.set_logging_level("INFO") -model = pybamm.lithium_ion.SPMe() - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param["Current function"] = "[current data]US06" -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -var = pybamm.standard_spatial_vars -var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10, var.r_n: 5, var.r_p: 5} -mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# simulate US06 drive cycle -tau = param.evaluate(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_eval = np.linspace(0, 600 / tau, 600) - -# need to increase max solver steps if solving DAEs along with an erratic drive cycle -solver = pybamm.CasadiSolver() -if isinstance(solver, pybamm.DaeSolver): - solver.max_steps = 10000 - -solution = solver.solve(model, t_eval) - -# plot -plot = pybamm.QuickPlot(model, mesh, solution) -plot.dynamic_plot() diff --git a/results/drive_cycles/car_current_simulation.py b/results/drive_cycles/car_current_simulation.py deleted file mode 100644 index 84d37e4f17..0000000000 --- a/results/drive_cycles/car_current_simulation.py +++ /dev/null @@ -1,63 +0,0 @@ -# -# Simulate user-defined current profile -# -import pybamm -import numpy as np - - -def car_current(t): - """ - Piecewise constant current as a function of time in seconds. This is adapted - from the file getCarCurrent.m, which is part of the LIONSIMBA toolbox [1]_. - - References - ---------- - .. [1] M Torchio, L Magni, R Bushan Gopaluni, RD Braatz, and D. Raimondoa. - LIONSIMBA: A Matlab framework based on a finite volume model suitable - for Li-ion battery design, simulation, and control. Journal of The - Electrochemical Society, 163(7):1192-1205, 2016. - """ - - current = ( - 1 * (t >= 0) * (t <= 50) - - 0.5 * (t > 50) * (t <= 60) - + 0.5 * (t > 60) * (t <= 210) - + 1 * (t > 210) * (t <= 410) - + 2 * (t > 410) * (t <= 415) - + 1.25 * (t > 415) * (t <= 615) - - 0.5 * (t > 615) - ) - - return current - - -# load model -pybamm.set_logging_level("INFO") -model = pybamm.lithium_ion.SPMe() - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param["Current function"] = car_current -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# simulate car current for 30 minutes -tau = param.process_symbol( - pybamm.standard_parameters_lithium_ion.tau_discharge -).evaluate(0) -t_eval = np.linspace(0, 1800 / tau, 600) -solution = model.default_solver.solve(model, t_eval) - -# plot -plot = pybamm.QuickPlot(model, mesh, solution) -plot.dynamic_plot() diff --git a/results/drive_cycles/discharge_rest.py b/results/drive_cycles/discharge_rest.py deleted file mode 100644 index 9089648767..0000000000 --- a/results/drive_cycles/discharge_rest.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# Simulate discharge followed by rest -# -import pybamm -import numpy as np -import matplotlib.pyplot as plt - -# load model -pybamm.set_logging_level("INFO") -model = pybamm.lithium_ion.SPMe() - -# create geometry -geometry = model.default_geometry - -# load parameter values and process model and geometry -param = model.default_parameter_values -param.process_model(model) -param.process_geometry(geometry) - -# set mesh -mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) - -# discretise model -disc = pybamm.Discretisation(mesh, model.default_spatial_methods) -disc.process_model(model) - -# solve model during discharge stage (1 hour) -tau = param.process_symbol(pybamm.standard_parameters_lithium_ion.tau_discharge) -t_end = 3600 / tau.evaluate(0) -t_eval1 = np.linspace(0, t_end, 120) -solution1 = model.default_solver.solve(model, t_eval1) - -# process variables for later plotting -time1 = pybamm.ProcessedVariable(model.variables["Time [h]"], solution1.t, solution1.y) -voltage1 = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution1.t, solution1.y, mesh=mesh -) -current1 = pybamm.ProcessedVariable( - model.variables["Current [A]"], solution1.t, solution1.y, mesh=mesh -) - -# solve again with zero current, using last step of solution1 as initial conditions -# update the current to be zero -param["Current function"] = "[zero]" -param.update_model(model, disc) -# Note: need to update model.concatenated_initial_conditions *after* update_model, -# as update_model updates model.concatenated_initial_conditions, by concatenting -# the (unmodified) initial conditions for each variable -model.concatenated_initial_conditions = solution1.y[:, -1][:, np.newaxis] - -# simulate 1 hour of rest -t_start = solution1.t[-1] -t_end = t_start + 3600 / tau.evaluate(0) -t_eval2 = np.linspace(t_start, t_end, 120) -solution2 = model.default_solver.solve(model, t_eval2) - -# process variables for later plotting -time2 = pybamm.ProcessedVariable(model.variables["Time [h]"], solution2.t, solution2.y) -voltage2 = pybamm.ProcessedVariable( - model.variables["Terminal voltage [V]"], solution2.t, solution2.y, mesh=mesh -) -current2 = pybamm.ProcessedVariable( - model.variables["Current [A]"], solution2.t, solution2.y, mesh=mesh -) - -# plot -plt.subplot(121) -plt.plot(time1(t_eval1), voltage1(t_eval1), time2(t_eval2), voltage2(t_eval2)) -plt.xlabel("Time [h]") -plt.ylabel("Voltage [V]") -plt.subplot(122) -z = np.linspace(0, 1, 10) -plt.plot(time1(t_eval1), current1(t_eval1), time2(t_eval2), current2(t_eval2)) -plt.xlabel("Time [h]") -plt.ylabel("Current [A]") -plt.show() diff --git a/results/drive_cycles/user_sin_current_simulation.py b/results/drive_cycles/user_sin_current_simulation.py deleted file mode 100644 index c3f56d39b9..0000000000 --- a/results/drive_cycles/user_sin_current_simulation.py +++ /dev/null @@ -1,63 +0,0 @@ -# -# Simulate user-defined current profile which takes parameters -# -import pybamm -import numpy as np - - -# create user-defined function -def my_fun(t, A, omega): - return A * np.sin(2 * np.pi * omega * t) - - -# choose amplitude and frequencies -A = pybamm.electrical_parameters.I_typ -frequencies = [0.1, 1] - -# load models (need to create new instances of model, not copies) -pybamm.set_logging_level("INFO") -models = [None] * len(frequencies) -for i in range(len(frequencies)): - models[i] = pybamm.lithium_ion.SPM() - -# load parameter values and process models -param = models[0].default_parameter_values -for i, frequency in enumerate(frequencies): - - def current(t): - return my_fun(t, A, frequency) - - param.update({"Current function": current}) - param.process_model(models[i]) - -# discretise models -for model in models: - # create geometry - geometry = model.default_geometry - param.process_geometry(geometry) - mesh = pybamm.Mesh( - geometry, models[-1].default_submesh_types, model.default_var_pts - ) - disc = pybamm.Discretisation(mesh, model.default_spatial_methods) - disc.process_model(model) - -# Example: simulate for 30 seconds -simulation_time = 30 # end time in seconds -tau = param.process_symbol( - pybamm.standard_parameters_lithium_ion.tau_discharge -).evaluate(0) - -# loop over frequencies -solutions = [None] * len(frequencies) -labels = [None] * len(frequencies) -for i, frequency in enumerate(frequencies): - # need enough timesteps to resolve output - npts = 20 * simulation_time * frequency - t_eval = np.linspace(0, simulation_time / tau, npts) - solutions[i] = model.default_solver.solve(model, t_eval) - labels[i] = "Frequency: {} Hz".format(frequency) - -# plot -output_variables = ["Current [A]", "Terminal voltage [V]"] -plot = pybamm.QuickPlot(models, mesh, solutions, output_variables, labels) -plot.dynamic_plot() diff --git a/scripts/install_scikits_odes.sh b/scripts/install_scikits_odes.sh index f88135b413..997d67c4e1 100755 --- a/scripts/install_scikits_odes.sh +++ b/scripts/install_scikits_odes.sh @@ -1,7 +1,7 @@ #!/bin/bash -SUNDIALS_URL=https://github.com/LLNL/sundials/archive/v4.1.0.tar.gz -SUNDIALS_NAME=sundials-4.1.0.tar.gz +SUNDIALS_URL=https://github.com/LLNL/sundials/archive/v5.0.0.tar.gz +SUNDIALS_NAME=sundials-5.0.0.tar.gz CURRENT_DIR=`pwd` TMP_DIR=$CURRENT_DIR/tmp mkdir $TMP_DIR @@ -10,9 +10,9 @@ INSTALL_DIR=$CURRENT_DIR/sundials cd $TMP_DIR wget $SUNDIALS_URL -O $SUNDIALS_NAME tar -xvf $SUNDIALS_NAME -mkdir build-sundials-4.1.0 -cd build-sundials-4.1.0/ -cmake -DLAPACK_ENABLE=ON -DSUNDIALS_INDEX_TYPE=int32_t -DBUILD_ARKODE:BOOL=OFF -DEXAMPLES_ENABLE:BOOL=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR ../sundials-4.1.0/ +mkdir build-sundials-5.0.0 +cd build-sundials-5.0.0/ +cmake -DLAPACK_ENABLE=ON -DSUNDIALS_INDEX_TYPE=int32_t -DBUILD_ARKODE:BOOL=OFF -DEXAMPLES_ENABLE:BOOL=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR ../sundials-5.0.0/ make install cd $CURRENT_DIR rm -rf $TMP_DIR diff --git a/setup.py b/setup.py index fbaebb09cb..b7672fcacc 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ def load_version(): "scikit-fem>=0.2.0", "casadi>=3.5.0", "jupyter", # For example notebooks + "python-Levenshtein>=0.12.0", # Note: Matplotlib is loaded for debug plots, but to ensure pybamm runs # on systems without an attached display, it should never be imported # outside of plot() methods. diff --git a/tests/integration/test_models/standard_output_comparison.py b/tests/integration/test_models/standard_output_comparison.py index 7ed199d859..e4aa69e0f6 100644 --- a/tests/integration/test_models/standard_output_comparison.py +++ b/tests/integration/test_models/standard_output_comparison.py @@ -8,63 +8,39 @@ class StandardOutputComparison(object): "Calls all the tests comparing standard output variables." - def __init__(self, models, discs, solutions): - # Process variables - for model in models: - disc = discs[model] - solution = solutions[model] - model.variables = pybamm.post_process_variables( - model.variables, solution.t, solution.y, disc.mesh - ) - - self.models = models - self.discs = discs + def __init__(self, solutions): self.solutions = solutions - if isinstance(self.models[0], pybamm.lithium_ion.BaseModel): + if isinstance(solutions[0].model, pybamm.lithium_ion.BaseModel): self.chemistry = "Lithium-ion" - elif isinstance(self.models[0], pybamm.lead_acid.BaseModel): + elif isinstance(solutions[0].model, pybamm.lead_acid.BaseModel): self.chemistry = "Lead acid" self.t = self.get_output_times() - self.mesh = self.get_mesh() def get_output_times(self): # Get max time allowed from the simulation, i.e. the smallest end time common # to all the solutions - max_t = min([solution.t[-1] for solution in self.solutions.values()]) + max_t = min([solution.t[-1] for solution in self.solutions]) # Assign common time - solution0 = self.solutions[self.models[0]] + solution0 = self.solutions[0] max_index = np.where(solution0.t == max_t)[0][0] t_common = solution0.t[:max_index] # Check times - for model in self.models: - np.testing.assert_array_equal(t_common, self.solutions[model].t[:max_index]) + for solution in self.solutions: + np.testing.assert_array_equal(t_common, solution.t[:max_index]) return t_common - def get_mesh(self): - disc0 = self.discs[self.models[0]] - - # Check all nodes and edges are the same - for model in self.models: - disc = self.discs[model] - for domain in disc0.mesh: - submesh0 = disc0.mesh[domain] - submesh = disc.mesh[domain] - np.testing.assert_array_equal(submesh0[0].nodes, submesh[0].nodes) - np.testing.assert_array_equal(submesh0[0].edges, submesh[0].edges) - return disc0.mesh - def run_test_class(self, ClassName, skip_first_timestep=False): "Run all tests from a class 'ClassName'" if skip_first_timestep: t = self.t[1:] else: t = self.t - tests = ClassName(self.models, t, self.mesh, self.solutions) + tests = ClassName(t, self.solutions) tests.test_all() def test_averages(self, skip_first_timestep=False): @@ -81,22 +57,20 @@ def test_all(self, skip_first_timestep=False): class BaseOutputComparison(object): - def __init__(self, models, time, mesh, solutions): - self.models = models + def __init__(self, time, solutions): self.t = time - self.mesh = mesh self.solutions = solutions def compare(self, var, tol=1e-2): "Compare variables from different models" # Get variable for each model - model_variables = [model.variables[var] for model in self.models] + model_variables = [solution[var] for solution in self.solutions] var0 = model_variables[0] - if var0.domain == []: + if var0.mesh is None: x = None else: - x = self.mesh.combine_submeshes(*var0.domain)[0].nodes + x = var0.mesh[0].nodes # Calculate tolerance based on the value of var0 maxvar0 = np.max(abs(var0(self.t, x))) @@ -115,8 +89,8 @@ def compare(self, var, tol=1e-2): class AveragesComparison(BaseOutputComparison): "Compare variables whose average value should be the same across all models" - def __init__(self, models, time, mesh, solutions): - super().__init__(models, time, mesh, solutions) + def __init__(self, time, solutions): + super().__init__(time, solutions) def test_all(self): # Potentials @@ -135,8 +109,8 @@ def test_all(self): class VariablesComparison(BaseOutputComparison): "Compare variables across models" - def __init__(self, models, time, mesh, solutions): - super().__init__(models, time, mesh, solutions) + def __init__(self, time, solutions): + super().__init__(time, solutions) def test_all(self): # Concentrations @@ -163,8 +137,8 @@ def test_all(self): class ParticleConcentrationComparison(BaseOutputComparison): - def __init__(self, models, time, mesh, solutions): - super().__init__(models, time, mesh, solutions) + def __init__(self, time, solutions): + super().__init__(time, solutions) def test_all(self): self.compare("Negative particle concentration") @@ -174,8 +148,8 @@ def test_all(self): class PorosityComparison(BaseOutputComparison): - def __init__(self, models, time, mesh, solutions): - super().__init__(models, time, mesh, solutions) + def __init__(self, time, solutions): + super().__init__(time, solutions) def test_all(self): self.compare("Porosity") diff --git a/tests/integration/test_models/standard_output_tests.py b/tests/integration/test_models/standard_output_tests.py index fa525537d0..8fde9e6fc1 100644 --- a/tests/integration/test_models/standard_output_tests.py +++ b/tests/integration/test_models/standard_output_tests.py @@ -9,11 +9,6 @@ class StandardOutputTests(object): "Calls all the tests on the standard output variables." def __init__(self, model, parameter_values, disc, solution): - # Process variables - model.variables = pybamm.post_process_variables( - model.variables, solution.t, solution.y, disc.mesh - ) - # Assign attributes self.model = model self.parameter_values = parameter_values @@ -26,7 +21,7 @@ def __init__(self, model, parameter_values, disc, solution): self.chemistry = "Lead acid" # Only for constant current - current_sign = np.sign(parameter_values["Current function"]) + current_sign = np.sign(parameter_values["Current function [A]"]) if current_sign == 1: self.operating_condition = "discharge" @@ -104,29 +99,28 @@ def __init__(self, model, param, disc, solution, operating_condition): class VoltageTests(BaseOutputTest): def __init__(self, model, param, disc, solution, operating_condition): super().__init__(model, param, disc, solution, operating_condition) - variables = self.model.variables - self.eta_r_n = variables["Negative electrode reaction overpotential [V]"] - self.eta_r_p = variables["Positive electrode reaction overpotential [V]"] - self.eta_r_n_av = variables[ + self.eta_r_n = solution["Negative electrode reaction overpotential [V]"] + self.eta_r_p = solution["Positive electrode reaction overpotential [V]"] + self.eta_r_n_av = solution[ "X-averaged negative electrode reaction overpotential [V]" ] - self.eta_r_p_av = variables[ + self.eta_r_p_av = solution[ "X-averaged positive electrode reaction overpotential [V]" ] - self.eta_r_av = variables["X-averaged reaction overpotential [V]"] + self.eta_r_av = solution["X-averaged reaction overpotential [V]"] - self.eta_e_av = variables["X-averaged electrolyte overpotential [V]"] - self.delta_phi_s_av = variables["X-averaged solid phase ohmic losses [V]"] + self.eta_e_av = solution["X-averaged electrolyte overpotential [V]"] + self.delta_phi_s_av = solution["X-averaged solid phase ohmic losses [V]"] - self.ocp_n_av = variables[ + self.ocp_n_av = solution[ "X-averaged negative electrode open circuit potential [V]" ] - self.ocp_p_av = variables[ + self.ocp_p_av = solution[ "X-averaged positive electrode open circuit potential [V]" ] - self.ocv_av = variables["X-averaged open circuit voltage [V]"] - self.voltage = variables["Terminal voltage [V]"] + self.ocv_av = solution["X-averaged open circuit voltage [V]"] + self.voltage = solution["Terminal voltage [V]"] def test_each_reaction_overpotential(self): """Testing that: @@ -251,16 +245,15 @@ def test_all(self): class ParticleConcentrationTests(BaseOutputTest): def __init__(self, model, param, disc, solution, operating_condition): super().__init__(model, param, disc, solution, operating_condition) - variables = self.model.variables - self.c_s_n = variables["Negative particle concentration"] - self.c_s_p = variables["Positive particle concentration"] + self.c_s_n = solution["Negative particle concentration"] + self.c_s_p = solution["Positive particle concentration"] - self.c_s_n_surf = variables["Negative particle surface concentration"] - self.c_s_p_surf = variables["Positive particle surface concentration"] + self.c_s_n_surf = solution["Negative particle surface concentration"] + self.c_s_p_surf = solution["Positive particle surface concentration"] - self.N_s_n = variables["Negative particle flux"] - self.N_s_p = variables["Positive particle flux"] + self.N_s_n = solution["Negative particle flux"] + self.N_s_p = solution["Positive particle flux"] def test_concentration_increase_decrease(self): """Test all concentrations in negative particles decrease and all @@ -347,22 +340,21 @@ def test_all(self): class ElectrolyteConcentrationTests(BaseOutputTest): def __init__(self, model, param, disc, solution, operating_condition): super().__init__(model, param, disc, solution, operating_condition) - variables = self.model.variables - self.c_e = variables["Electrolyte concentration"] + self.c_e = solution["Electrolyte concentration"] - self.c_e_n = variables["Negative electrolyte concentration"] - self.c_e_s = variables["Separator electrolyte concentration"] - self.c_e_p = variables["Positive electrolyte concentration"] + self.c_e_n = solution["Negative electrolyte concentration"] + self.c_e_s = solution["Separator electrolyte concentration"] + self.c_e_p = solution["Positive electrolyte concentration"] # TODO: output average electrolyte concentration - # self.c_e_av = variables["X-averaged electrolyte concentration"] - # self.c_e_n_av = variables["X-averaged negative electrolyte concentration"] - # self.c_e_s_av = variables["X-averaged separator electrolyte concentration"] - # self.c_e_p_av = variables["X-averaged positive electrolyte concentration"] + # self.c_e_av = solution["X-averaged electrolyte concentration"] + # self.c_e_n_av = solution["X-averaged negative electrolyte concentration"] + # self.c_e_s_av = solution["X-averaged separator electrolyte concentration"] + # self.c_e_p_av = solution["X-averaged positive electrolyte concentration"] - self.N_e_hat = variables["Electrolyte flux"] - # self.N_e_hat = variables["Reduced cation flux"] + self.N_e_hat = solution["Electrolyte flux"] + # self.N_e_hat = solution["Reduced cation flux"] def test_concentration_limit(self): "Test that the electrolyte concentration is always greater than zero." @@ -433,37 +425,36 @@ def test_all(self): class PotentialTests(BaseOutputTest): def __init__(self, model, param, disc, solution, operating_condition): super().__init__(model, param, disc, solution, operating_condition) - variables = self.model.variables - self.phi_s_n = variables["Negative electrode potential [V]"] - self.phi_s_p = variables["Positive electrode potential [V]"] - self.phi_s_n_av = variables["X-averaged negative electrode potential [V]"] - self.phi_s_p_av = variables["X-averaged positive electrode potential [V]"] + self.phi_s_n = solution["Negative electrode potential [V]"] + self.phi_s_p = solution["Positive electrode potential [V]"] + self.phi_s_n_av = solution["X-averaged negative electrode potential [V]"] + self.phi_s_p_av = solution["X-averaged positive electrode potential [V]"] - self.phi_e = variables["Electrolyte potential [V]"] - self.phi_e_n = variables["Negative electrolyte potential [V]"] - self.phi_e_s = variables["Separator electrolyte potential [V]"] - self.phi_e_p = variables["Positive electrolyte potential [V]"] - self.phi_e_n_av = variables["X-averaged negative electrolyte potential [V]"] - self.phi_e_p_av = variables["X-averaged positive electrolyte potential [V]"] + self.phi_e = solution["Electrolyte potential [V]"] + self.phi_e_n = solution["Negative electrolyte potential [V]"] + self.phi_e_s = solution["Separator electrolyte potential [V]"] + self.phi_e_p = solution["Positive electrolyte potential [V]"] + self.phi_e_n_av = solution["X-averaged negative electrolyte potential [V]"] + self.phi_e_p_av = solution["X-averaged positive electrolyte potential [V]"] - self.delta_phi_n = variables[ + self.delta_phi_n = solution[ "Negative electrode surface potential difference [V]" ] - self.delta_phi_p = variables[ + self.delta_phi_p = solution[ "Positive electrode surface potential difference [V]" ] - self.delta_phi_n_av = variables[ + self.delta_phi_n_av = solution[ "X-averaged negative electrode surface potential difference [V]" ] - self.delta_phi_p_av = variables[ + self.delta_phi_p_av = solution[ "X-averaged positive electrode surface potential difference [V]" ] - self.grad_phi_e = variables["Gradient of electrolyte potential"] - self.grad_phi_e_n = variables["Gradient of negative electrolyte potential"] - self.grad_phi_e_s = variables["Gradient of separator electrolyte potential"] - self.grad_phi_e_p = variables["Gradient of positive electrolyte potential"] + self.grad_phi_e = solution["Gradient of electrolyte potential"] + self.grad_phi_e_n = solution["Gradient of negative electrolyte potential"] + self.grad_phi_e_s = solution["Gradient of separator electrolyte potential"] + self.grad_phi_e_p = solution["Gradient of positive electrolyte potential"] def test_negative_electrode_potential_profile(self): """Test that negative electrode potential is zero on left boundary. Test @@ -526,27 +517,26 @@ def test_all(self): class CurrentTests(BaseOutputTest): def __init__(self, model, param, disc, solution, operating_condition): super().__init__(model, param, disc, solution, operating_condition) - variables = self.model.variables - self.j = variables["Interfacial current density"] - self.j0 = variables["Exchange current density"] + self.j = solution["Interfacial current density"] + self.j0 = solution["Exchange current density"] - self.j_n = variables["Negative electrode interfacial current density"] - self.j_p = variables["Positive electrode interfacial current density"] - self.j_n_av = variables[ + self.j_n = solution["Negative electrode interfacial current density"] + self.j_p = solution["Positive electrode interfacial current density"] + self.j_n_av = solution[ "X-averaged negative electrode interfacial current density" ] - self.j_p_av = variables[ + self.j_p_av = solution[ "X-averaged positive electrode interfacial current density" ] - self.j0_n = variables["Negative electrode exchange current density"] - self.j0_p = variables["Positive electrode exchange current density"] + self.j0_n = solution["Negative electrode exchange current density"] + self.j0_p = solution["Positive electrode exchange current density"] - self.i_s_n = variables["Negative electrode current density"] - self.i_s_p = variables["Positive electrode current density"] - self.i_s = variables["Electrode current density"] - self.i_e = variables["Electrolyte current density"] + self.i_s_n = solution["Negative electrode current density"] + self.i_s_p = solution["Positive electrode current density"] + self.i_s = solution["Electrode current density"] + self.i_e = solution["Electrolyte current density"] def test_interfacial_current_average(self): """Test that average of the interfacial current density is equal to the true @@ -611,11 +601,10 @@ def test_all(self): class VelocityTests(BaseOutputTest): def __init__(self, model, param, disc, solution, operating_condition): super().__init__(model, param, disc, solution, operating_condition) - variables = self.model.variables - self.v_box = variables["Volume-averaged velocity"] - self.i_e = variables["Electrolyte current density"] - self.dVbox_dz = variables["Vertical volume-averaged acceleration"] + self.v_box = solution["Volume-averaged velocity"] + self.i_e = solution["Electrolyte current density"] + self.dVbox_dz = solution["Vertical volume-averaged acceleration"] def test_velocity_boundaries(self): """Test the boundary values of the current densities""" diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_asymptotics_convergence.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_asymptotics_convergence.py index 0934922960..8b4e16f037 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_asymptotics_convergence.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_asymptotics_convergence.py @@ -17,8 +17,13 @@ def test_leading_order_convergence(self): leading_order_model = pybamm.lead_acid.LOQS() composite_model = pybamm.lead_acid.Composite() full_model = pybamm.lead_acid.Full() + + def current_function(t): + return pybamm.InputParameter("Current") + # Same parameters, same geometry parameter_values = full_model.default_parameter_values + parameter_values["Current function [A]"] = current_function parameter_values.process_model(leading_order_model) parameter_values.process_model(composite_model) parameter_values.process_model(full_model) @@ -44,48 +49,36 @@ def test_leading_order_convergence(self): def get_max_error(current): pybamm.logger.info("current = {}".format(current)) - # Update current (and hence C_e) in the parameters - param = pybamm.ParameterValues(chemistry=pybamm.parameter_sets.Sulzer2019) - param.update({"Typical current [A]": current}) - param.update_model(leading_order_model, loqs_disc) - param.update_model(composite_model, comp_disc) - param.update_model(full_model, full_disc) # Solve, make sure times are the same and use tight tolerances t_eval = np.linspace(0, 0.6) solver_loqs = leading_order_model.default_solver solver_loqs.rtol = 1e-8 solver_loqs.atol = 1e-8 - solution_loqs = solver_loqs.solve(leading_order_model, t_eval) + solution_loqs = solver_loqs.solve( + leading_order_model, t_eval, inputs={"Current": current} + ) solver_comp = composite_model.default_solver solver_comp.rtol = 1e-8 solver_comp.atol = 1e-8 - solution_comp = solver_comp.solve(composite_model, t_eval) + solution_comp = solver_comp.solve( + composite_model, t_eval, inputs={"Current": current} + ) solver_full = full_model.default_solver solver_full.rtol = 1e-8 solver_full.atol = 1e-8 - solution_full = solver_full.solve(full_model, t_eval) + solution_full = solver_full.solve( + full_model, t_eval, inputs={"Current": current} + ) # Post-process variables - t_loqs, y_loqs = solution_loqs.t, solution_loqs.y - t_comp, y_comp = solution_comp.t, solution_comp.y - t_full, y_full = solution_full.t, solution_full.y - voltage_loqs = pybamm.ProcessedVariable( - leading_order_model.variables["Terminal voltage"], - t_loqs, - y_loqs, - loqs_disc.mesh, - ) - voltage_comp = pybamm.ProcessedVariable( - composite_model.variables["Terminal voltage"], - t_comp, - y_comp, - comp_disc.mesh, - ) - voltage_full = pybamm.ProcessedVariable( - full_model.variables["Terminal voltage"], t_full, y_full, full_disc.mesh - ) + voltage_loqs = solution_loqs["Terminal voltage"] + voltage_comp = solution_comp["Terminal voltage"] + voltage_full = solution_full["Terminal voltage"] # Compare + t_loqs = solution_loqs.t + t_comp = solution_comp.t + t_full = solution_full.t t = t_full[: np.min([len(t_loqs), len(t_comp), len(t_full)])] loqs_error = np.max(np.abs(voltage_loqs(t) - voltage_full(t))) comp_error = np.max(np.abs(voltage_comp(t) - voltage_full(t))) @@ -112,4 +105,5 @@ def get_max_error(current): if "-v" in sys.argv: debug = True + pybamm.set_logging_level("DEBUG") unittest.main() diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_compare_outputs.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_compare_outputs.py index fd71861069..50df5f43fe 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_compare_outputs.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_compare_outputs.py @@ -21,7 +21,7 @@ def test_compare_averages_asymptotics(self): # load parameter values (same for all models) param = models[0].default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) for model in models: param.process_model(model) @@ -30,24 +30,22 @@ def test_compare_averages_asymptotics(self): var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10} # discretise models - discs = {} for model in models: geometry = model.default_geometry param.process_geometry(geometry) mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) disc = pybamm.Discretisation(mesh, model.default_spatial_methods) disc.process_model(model) - discs[model] = disc # solve model - solutions = {} + solutions = [] t_eval = np.linspace(0, 1, 100) - for i, model in enumerate(models): + for model in models: solution = model.default_solver.solve(model, t_eval) - solutions[model] = solution + solutions.append(solution) # test averages - comparison = StandardOutputComparison(models, discs, solutions) + comparison = StandardOutputComparison(solutions) comparison.test_averages() def test_compare_outputs_surface_form(self): @@ -67,7 +65,7 @@ def test_compare_outputs_surface_form(self): for models in model_combos: # load parameter values (same for all models) param = models[0].default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) for model in models: param.process_model(model) @@ -86,14 +84,14 @@ def test_compare_outputs_surface_form(self): discs[model] = disc # solve model - solutions = {} + solutions = [] t_eval = np.linspace(0, 1, 100) - for i, model in enumerate(models): + for model in models: solution = model.default_solver.solve(model, t_eval) - solutions[model] = solution + solutions.append(solution) # compare outputs - comparison = StandardOutputComparison(models, discs, solutions) + comparison = StandardOutputComparison(solutions) comparison.test_all(skip_first_timestep=True) diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_composite.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_composite.py index 05c2d9bb16..b8f5f3fe3a 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_composite.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_composite.py @@ -12,14 +12,14 @@ class TestLeadAcidComposite(unittest.TestCase): def test_basic_processing(self): model = pybamm.lead_acid.Composite() param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() def test_basic_processing_with_convection(self): model = pybamm.lead_acid.Composite() param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() @@ -51,7 +51,7 @@ def test_basic_processing_differential(self): options = {"surface form": "differential"} model = pybamm.lead_acid.Composite(options) param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() @@ -59,7 +59,7 @@ def test_basic_processing_algebraic(self): options = {"surface form": "algebraic"} model = pybamm.lead_acid.Composite(options) param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() # solver=pybamm.CasadiSolver()) @@ -68,7 +68,7 @@ class TestLeadAcidCompositeExtended(unittest.TestCase): def test_basic_processing(self): model = pybamm.lead_acid.CompositeExtended() param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_foqs.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_foqs.py index be8c4cbca0..380d47dd12 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_foqs.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_foqs.py @@ -14,7 +14,7 @@ def test_basic_processing(self): options = {"thermal": "isothermal", "convection": False} model = pybamm.lead_acid.FOQS(options) param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() @@ -22,7 +22,7 @@ def test_basic_processing_with_convection(self): options = {"thermal": "isothermal", "convection": True} model = pybamm.lead_acid.FOQS(options) param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() @@ -60,7 +60,7 @@ def test_basic_processing_differential(self): } model = pybamm.lead_acid.FOQS(options) param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() @@ -72,7 +72,7 @@ def test_basic_processing_algebraic(self): } model = pybamm.lead_acid.FOQS(options) param = model.default_parameter_values - param.update({"Typical current [A]": 1}) + param.update({"Current function [A]": 1}) modeltest = tests.StandardModelTest(model, parameter_values=param) modeltest.test_all() diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_full.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_full.py index 0fe2b5f688..22103c86b6 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_full.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_full.py @@ -13,7 +13,7 @@ def test_basic_processing(self): options = {"thermal": "isothermal"} model = pybamm.lead_acid.Full(options) modeltest = tests.StandardModelTest(model) - modeltest.test_all(t_eval=np.linspace(0, 0.6)) + modeltest.test_all(t_eval=np.linspace(0, 0.6), solver=pybamm.CasadiSolver()) def test_basic_processing_with_convection(self): options = {"thermal": "isothermal", "convection": True} diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs.py index 20f5205ea1..368cf5e97c 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs.py @@ -39,16 +39,14 @@ def test_set_up(self): def test_charge(self): model = pybamm.lead_acid.LOQS() parameter_values = model.default_parameter_values - parameter_values.update({"Typical current [A]": -1}) + parameter_values.update({"Current function [A]": -1}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all() def test_zero_current(self): model = pybamm.lead_acid.LOQS() parameter_values = model.default_parameter_values - parameter_values.update( - {"Current function": "[zero]"} - ) + parameter_values.update({"Current function [A]": 0}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all() diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs_surface_form.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs_surface_form.py index 5f8775018b..1e37c35358 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs_surface_form.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_loqs_surface_form.py @@ -2,7 +2,6 @@ # Tests for the lead-acid LOQS model with capacitance # import pybamm -from pybamm.solvers.scikits_ode_solver import scikits_odes_spec import tests import unittest @@ -11,7 +10,6 @@ class TestLeadAcidLoqsSurfaceForm(unittest.TestCase): - @unittest.skipIf(scikits_odes_spec is None, "scikits.odes not installed") def test_basic_processing(self): options = {"surface form": "algebraic"} model = pybamm.lead_acid.LOQS(options) @@ -35,7 +33,6 @@ def test_basic_processing_1p1D_differential(self): modeltest = tests.StandardModelTest(model) modeltest.test_all(skip_output_tests=True) - @unittest.skipIf(scikits_odes_spec is None, "scikits.odes not installed") def test_basic_processing_1p1D_algebraic(self): options = { "surface form": "algebraic", diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_composite_side_reactions.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_composite_side_reactions.py index b713d2e6ce..e0528c4816 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_composite_side_reactions.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_composite_side_reactions.py @@ -26,7 +26,7 @@ def test_basic_processing_charge(self): model = pybamm.lead_acid.Composite(options) parameter_values = model.default_parameter_values parameter_values.update( - {"Typical current [A]": -1, "Initial State of Charge": 0.5} + {"Current function [A]": -1, "Initial State of Charge": 0.5} ) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all(skip_output_tests=True) @@ -35,7 +35,7 @@ def test_basic_processing_zero_current(self): options = {"side reactions": ["oxygen"], "surface form": "differential"} model = pybamm.lead_acid.Composite(options) parameter_values = model.default_parameter_values - parameter_values.update({"Current function": "[zero]"}) + parameter_values.update({"Current function [A]": 0}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all(skip_output_tests=True) diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_full_side_reactions.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_full_side_reactions.py index 46ec22f882..bb86413b5c 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_full_side_reactions.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_full_side_reactions.py @@ -32,7 +32,7 @@ def test_basic_processing_charge(self): model = pybamm.lead_acid.Full(options) parameter_values = model.default_parameter_values parameter_values.update( - {"Typical current [A]": -1, "Initial State of Charge": 0.5} + {"Current function [A]": -1, "Initial State of Charge": 0.5} ) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all(skip_output_tests=True) @@ -41,7 +41,7 @@ def test_basic_processing_zero_current(self): options = {"side reactions": ["oxygen"], "surface form": "differential"} model = pybamm.lead_acid.Full(options) parameter_values = model.default_parameter_values - parameter_values.update({"Current function": "[zero]"}) + parameter_values.update({"Current function [A]": 0}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all(skip_output_tests=True) diff --git a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_loqs_side_reactions.py b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_loqs_side_reactions.py index 852b884cea..4ef6aa0123 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_loqs_side_reactions.py +++ b/tests/integration/test_models/test_full_battery_models/test_lead_acid/test_side_reactions/test_loqs_side_reactions.py @@ -34,7 +34,7 @@ def test_charge(self): model = pybamm.lead_acid.LOQS(options) parameter_values = model.default_parameter_values parameter_values.update( - {"Typical current [A]": -1, "Initial State of Charge": 0.5} + {"Current function [A]": -1, "Initial State of Charge": 0.5} ) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all(skip_output_tests=True) @@ -43,7 +43,7 @@ def test_zero_current(self): options = {"surface form": "differential", "side reactions": ["oxygen"]} model = pybamm.lead_acid.LOQS(options) parameter_values = model.default_parameter_values - parameter_values.update({"Current function": "[zero]"}) + parameter_values.update({"Current function [A]": 0}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all(skip_output_tests=True) diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_compare_basic_models.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_compare_basic_models.py new file mode 100644 index 0000000000..dfb2f3c893 --- /dev/null +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_compare_basic_models.py @@ -0,0 +1,68 @@ +# +# Compare basic models with full models +# +import pybamm + +import numpy as np +import unittest + + +class TestCompareBasicModels(unittest.TestCase): + def test_compare_dfns(self): + basic_dfn = pybamm.lithium_ion.BasicDFN() + dfn = pybamm.lithium_ion.DFN() + + # Solve basic DFN + basic_sim = pybamm.Simulation(basic_dfn) + t_eval = np.linspace(0, 1) + basic_sim.solve(t_eval) + basic_sol = basic_sim.solution + + # Solve main DFN + sim = pybamm.Simulation(dfn) + t_eval = np.linspace(0, 1) + sim.solve(t_eval) + sol = sim.solution + + # Compare solution data + np.testing.assert_array_almost_equal(basic_sol.y, sol.y, decimal=4) + np.testing.assert_array_almost_equal(basic_sol.t, sol.t, decimal=4) + # Compare variables + for name in basic_dfn.variables: + np.testing.assert_array_almost_equal( + basic_sol[name].entries, sol[name].entries, decimal=4 + ) + + def test_compare_spms(self): + basic_spm = pybamm.lithium_ion.BasicSPM() + spm = pybamm.lithium_ion.SPM() + + # Solve basic SPM + basic_sim = pybamm.Simulation(basic_spm) + t_eval = np.linspace(0, 1) + basic_sim.solve(t_eval) + basic_sol = basic_sim.solution + + # Solve main SPM + sim = pybamm.Simulation(spm) + t_eval = np.linspace(0, 1) + sim.solve(t_eval) + sol = sim.solution + + # Compare solution data + np.testing.assert_array_almost_equal(basic_sol.y, sol.y) + np.testing.assert_array_almost_equal(basic_sol.t, sol.t) + # Compare variables + for name in basic_spm.variables: + np.testing.assert_array_almost_equal( + basic_sol[name].entries, sol[name].entries + ) + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + unittest.main() diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py index 030e9b406b..be6eb491f8 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py @@ -69,8 +69,8 @@ def test_set_up(self): model = pybamm.lithium_ion.DFN() optimtest = tests.OptimisationsTest(model) optimtest.set_up_model(simplify=False, to_python=True) - optimtest.set_up_model(simplify=True, to_python=True) optimtest.set_up_model(simplify=False, to_python=False) + optimtest.set_up_model(simplify=True, to_python=True) optimtest.set_up_model(simplify=True, to_python=False) def test_full_thermal(self): @@ -107,6 +107,21 @@ def test_surface_form_algebraic(self): modeltest = tests.StandardModelTest(model) modeltest.test_all() + def test_particle_distribution_in_x(self): + model = pybamm.lithium_ion.DFN() + param = model.default_parameter_values + + def negative_distribution(x): + return 1 + x + + def positive_distribution(x): + return 1 + (x - (1 - model.param.l_p)) + + param["Negative particle distribution in x"] = negative_distribution + param["Positive particle distribution in x"] = positive_distribution + modeltest = tests.StandardModelTest(model, parameter_values=param) + modeltest.test_all() + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_external/test_external_temperature.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_external/test_external_temperature.py index 26600fc463..fad8f88793 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_external/test_external_temperature.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_external/test_external_temperature.py @@ -25,7 +25,6 @@ def test_external_lumped_temperature(self): T_av += 1 sim.step(dt, external_variables=external_variables) - @unittest.skipIf(not pybamm.have_idaklu(), "idaklu solver is not installed") def test_external_temperature(self): model_options = { diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_spm.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_spm.py index 79fd752361..9a933dade6 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_spm.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_spm.py @@ -74,7 +74,7 @@ def test_charge(self): options = {"thermal": "isothermal"} model = pybamm.lithium_ion.SPM(options) parameter_values = model.default_parameter_values - parameter_values.update({"Typical current [A]": -1}) + parameter_values.update({"Current function [A]": -1}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all() @@ -82,7 +82,7 @@ def test_zero_current(self): options = {"thermal": "isothermal"} model = pybamm.lithium_ion.SPM(options) parameter_values = model.default_parameter_values - parameter_values.update({"Current function": "[zero]"}) + parameter_values.update({"Current function [A]": 0}) modeltest = tests.StandardModelTest(model, parameter_values=parameter_values) modeltest.test_all() diff --git a/tests/integration/test_models/test_submodels/test_external_circuit/__init__.py b/tests/integration/test_models/test_submodels/test_external_circuit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/test_models/test_submodels/test_external_circuit/test_function_control.py b/tests/integration/test_models/test_submodels/test_external_circuit/test_function_control.py new file mode 100644 index 0000000000..538846c377 --- /dev/null +++ b/tests/integration/test_models/test_submodels/test_external_circuit/test_function_control.py @@ -0,0 +1,159 @@ +# +# Test function control submodel +# +import numpy as np +import pybamm +import unittest + + +class TestFunctionControl(unittest.TestCase): + def test_constant_current(self): + class ConstantCurrent: + num_switches = 0 + + def __call__(self, variables): + I = variables["Current [A]"] + return I + 1 + + # load models + models = [ + pybamm.lithium_ion.SPM(), + pybamm.lithium_ion.SPM({"operating mode": ConstantCurrent()}), + ] + + # load parameter values and process models and geometry + params = [model.default_parameter_values for model in models] + + # First model: 1A charge + params[0]["Current function [A]"] = -1 + params[1]["Current function [A]"] = -1 + + # set parameters and discretise models + for i, model in enumerate(models): + # create geometry + geometry = model.default_geometry + params[i].process_model(model) + params[i].process_geometry(geometry) + mesh = pybamm.Mesh( + geometry, model.default_submesh_types, model.default_var_pts + ) + disc = pybamm.Discretisation(mesh, model.default_spatial_methods) + disc.process_model(model) + + # solve model + solutions = [None] * len(models) + t_eval = np.linspace(0, 1, 100) + for i, model in enumerate(models): + solutions[i] = model.default_solver.solve(model, t_eval) + + np.testing.assert_array_almost_equal( + solutions[0]["Discharge capacity [A.h]"].entries, + solutions[0]["Current [A]"].entries * solutions[0]["Time [h]"].entries, + ) + np.testing.assert_array_almost_equal( + solutions[0]["Terminal voltage [V]"](solutions[0].t), + solutions[1]["Terminal voltage [V]"](solutions[0].t), + ) + + def test_constant_voltage(self): + class ConstantVoltage: + num_switches = 0 + + def __call__(self, variables): + V = variables["Terminal voltage [V]"] + return V - 4.1 + + # load models + models = [ + pybamm.lithium_ion.SPM({"operating mode": "voltage"}), + pybamm.lithium_ion.SPM({"operating mode": ConstantVoltage()}), + ] + + # load parameter values and process models and geometry + params = [model.default_parameter_values for model in models] + + # First model: 4.1V charge + params[0]["Voltage function [V]"] = 4.1 + + # set parameters and discretise models + var = pybamm.standard_spatial_vars + var_pts = {var.x_n: 5, var.x_s: 5, var.x_p: 30, var.r_n: 10, var.r_p: 10} + for i, model in enumerate(models): + # create geometry + geometry = model.default_geometry + params[i].process_model(model) + params[i].process_geometry(geometry) + mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) + disc = pybamm.Discretisation(mesh, model.default_spatial_methods) + disc.process_model(model) + + # solve model + solutions = [None] * len(models) + t_eval = np.linspace(0, 1, 100) + for i, model in enumerate(models): + solutions[i] = model.default_solver.solve(model, t_eval) + + V0 = solutions[0]["Terminal voltage [V]"].entries + V1 = solutions[1]["Terminal voltage [V]"].entries + np.testing.assert_array_almost_equal(V0, V1) + + I0 = solutions[0]["Current [A]"].entries + I1 = solutions[1]["Current [A]"].entries + np.testing.assert_array_almost_equal(abs((I1 - I0) / I0), 0, decimal=1) + + def test_constant_power(self): + class ConstantPower: + num_switches = 0 + + def __call__(self, variables): + I = variables["Current [A]"] + V = variables["Terminal voltage [V]"] + return I * V - 4 + + # load models + models = [ + pybamm.lithium_ion.SPM({"operating mode": "power"}), + pybamm.lithium_ion.SPM({"operating mode": ConstantPower()}), + ] + + # load parameter values and process models and geometry + params = [model.default_parameter_values for model in models] + + # First model: 4W charge + params[0]["Power function [W]"] = 4 + + # set parameters and discretise models + for i, model in enumerate(models): + # create geometry + geometry = model.default_geometry + params[i].process_model(model) + params[i].process_geometry(geometry) + mesh = pybamm.Mesh( + geometry, model.default_submesh_types, model.default_var_pts + ) + disc = pybamm.Discretisation(mesh, model.default_spatial_methods) + disc.process_model(model) + + # solve model + solutions = [None] * len(models) + t_eval = np.linspace(0, 1, 100) + for i, model in enumerate(models): + solutions[i] = model.default_solver.solve(model, t_eval) + + V0 = solutions[0]["Terminal voltage [V]"].entries + V1 = solutions[1]["Terminal voltage [V]"].entries + np.testing.assert_array_equal(V0, V1) + + I0 = solutions[0]["Current [A]"].entries + I1 = solutions[1]["Current [A]"].entries + np.testing.assert_array_equal(I0, I1) + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/integration/test_models/test_submodels/test_interface/test_butler_volmer.py b/tests/integration/test_models/test_submodels/test_interface/test_butler_volmer.py index d4219771f8..dbb67410a9 100644 --- a/tests/integration/test_models/test_submodels/test_interface/test_butler_volmer.py +++ b/tests/integration/test_models/test_submodels/test_interface/test_butler_volmer.py @@ -94,7 +94,7 @@ def test_discretisation(self): j_p = model_p.get_coupled_variables(self.variables)[ "Positive electrode interfacial current density" ] - j = pybamm.Concatenation(j_n, pybamm.Broadcast(0, ["separator"]), j_p) + j = pybamm.Concatenation(j_n, pybamm.PrimaryBroadcast(0, ["separator"]), j_p) # Process parameters and discretise parameter_values = pybamm.lithium_ion.BaseModel().default_parameter_values diff --git a/tests/integration/test_quick_plot.py b/tests/integration/test_quick_plot.py index 8b66e3ec14..582503c334 100644 --- a/tests/integration/test_quick_plot.py +++ b/tests/integration/test_quick_plot.py @@ -24,7 +24,7 @@ def test_plot_lithium_ion(self): t_eval = np.linspace(0, 2, 100) solution_spm = spm.default_solver.solve(spm, t_eval) solution_spme = spme.default_solver.solve(spme, t_eval) - quick_plot = pybamm.QuickPlot([spm, spme], mesh, [solution_spm, solution_spme]) + quick_plot = pybamm.QuickPlot([solution_spm, solution_spme]) quick_plot.plot(0) # update the axis @@ -42,10 +42,10 @@ def test_plot_lithium_ion(self): quick_plot.update(0.01) # Update parameters, solve, plot again - param.update({"Current function": "[zero]"}) + param.update({"Current function [A]": 0}) param.update_model(spm, disc_spm) solution_spm = spm.default_solver.solve(spm, t_eval) - quick_plot = pybamm.QuickPlot(spm, mesh, solution_spm) + quick_plot = pybamm.QuickPlot(solution_spm) quick_plot.plot(0) # Test with different output variables @@ -54,7 +54,7 @@ def test_plot_lithium_ion(self): "Electrolyte concentration", "Positive particle surface concentration", ] - quick_plot = pybamm.QuickPlot(spm, mesh, solution_spm, output_vars) + quick_plot = pybamm.QuickPlot(solution_spm, output_vars) self.assertEqual(len(quick_plot.axis), 3) quick_plot.plot(0) @@ -84,7 +84,7 @@ def test_plot_lead_acid(self): t_eval = np.linspace(0, 1, 100) solution_loqs = loqs.default_solver.solve(loqs, t_eval) - pybamm.QuickPlot(loqs, mesh, solution_loqs) + pybamm.QuickPlot(solution_loqs) if __name__ == "__main__": diff --git a/tests/integration/test_solvers/__init__.py b/tests/integration/test_solvers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/test_solvers/test_idaklu.py b/tests/integration/test_solvers/test_idaklu.py index 260c242f7b..392854890f 100644 --- a/tests/integration/test_solvers/test_idaklu.py +++ b/tests/integration/test_solvers/test_idaklu.py @@ -50,7 +50,7 @@ def test_changing_grid(self): # Calculate time for each solver and each number of grid points var = pybamm.standard_spatial_vars - t_eval = np.linspace(0, 0.17, 100) + t_eval = np.linspace(0, 0.25, 100) for npts in [100, 200]: # discretise var_pts = { diff --git a/tests/integration/test_solvers/test_solution.py b/tests/integration/test_solvers/test_solution.py new file mode 100644 index 0000000000..23adfe0583 --- /dev/null +++ b/tests/integration/test_solvers/test_solution.py @@ -0,0 +1,68 @@ +# +# Tests for the Solution class +# +import pybamm +import unittest +import numpy as np + + +class TestSolution(unittest.TestCase): + def test_append(self): + model = pybamm.lithium_ion.SPMe() + # create geometry + geometry = model.default_geometry + + # load parameter values and process model and geometry + param = model.default_parameter_values + param.process_model(model) + param.process_geometry(geometry) + + # set mesh + mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts) + + # discretise model + disc = pybamm.Discretisation(mesh, model.default_spatial_methods) + disc.process_model(model) + + # solve model + t_eval = np.linspace(0, 0.2, 100) + solver = model.default_solver + solution = solver.solve(model, t_eval) + + # step model + old_t = 0 + step_solver = model.default_solver + step_solution = None + for t in solution.t[1:]: + dt = t - old_t + step_solution = step_solver.step(step_solution, model, dt=dt, npts=10) + if t == solution.t[1]: + # Create voltage variable + step_solution.update("Terminal voltage") + old_t = t + + # Step solution should have been updated as we go along so be quicker to + # calculate + timer = pybamm.Timer() + step_solution.update("Terminal voltage") + step_sol_time = timer.time() + timer.reset() + solution.update("Terminal voltage") + sol_time = timer.time() + self.assertLess(step_sol_time, sol_time) + # Check both give the same answer + np.testing.assert_array_almost_equal( + solution["Terminal voltage"](solution.t), + step_solution["Terminal voltage"](solution.t), + decimal=4, + ) + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_discretisations/test_discretisation.py b/tests/unit/test_discretisations/test_discretisation.py index e1f3fcab91..5f691b6e65 100644 --- a/tests/unit/test_discretisations/test_discretisation.py +++ b/tests/unit/test_discretisations/test_discretisation.py @@ -9,10 +9,12 @@ get_mesh_for_testing, get_discretisation_for_testing, get_1p1d_discretisation_for_testing, + get_2p1d_mesh_for_testing, ) from tests.shared import SpatialMethodForTesting -from scipy.sparse import block_diag +from scipy.sparse import block_diag, csc_matrix +from scipy.sparse.linalg import inv class TestDiscretise(unittest.TestCase): @@ -48,9 +50,9 @@ def test_no_mesh(self): def test_add_internal_boundary_conditions(self): model = pybamm.BaseModel() - c_e_n = pybamm.Broadcast(0, ["negative electrode"]) - c_e_s = pybamm.Broadcast(0, ["separator"]) - c_e_p = pybamm.Broadcast(0, ["positive electrode"]) + c_e_n = pybamm.PrimaryBroadcast(0, ["negative electrode"]) + c_e_s = pybamm.PrimaryBroadcast(0, ["separator"]) + c_e_p = pybamm.PrimaryBroadcast(0, ["positive electrode"]) c_e = pybamm.Concatenation(c_e_n, c_e_s, c_e_p) lbc = (pybamm.Scalar(0), "Neumann") rbc = (pybamm.Scalar(0), "Neumann") @@ -79,8 +81,21 @@ def test_adding_0D_external_variable(self): disc = pybamm.Discretisation() disc.process_model(model) - self.assertEqual(len(model.y_slices), 2) - self.assertEqual(model.y_slices[b.id][0], slice(1, 2, None)) + self.assertIsInstance(model.variables["b"], pybamm.ExternalVariable) + self.assertEqual(model.variables["b"].evaluate(u={"b": np.array([1])}), 1) + + def test_adding_0D_external_variable_fail(self): + model = pybamm.BaseModel() + a = pybamm.Variable("a") + b = pybamm.Variable("b") + + model.rhs = {a: a * b} + model.initial_conditions = {a: 0} + model.external_variables = [b] + + disc = pybamm.Discretisation() + with self.assertRaisesRegex(ValueError, "Variable b must be in the model"): + disc.process_model(model) def test_adding_1D_external_variable(self): model = pybamm.BaseModel() @@ -115,9 +130,12 @@ def test_adding_1D_external_variable(self): disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) - self.assertEqual(len(model.y_slices), 2) - self.assertEqual(model.y_slices[a.id][0], slice(0, 10, None)) - self.assertEqual(model.y_slices[b.id][0], slice(10, 20, None)) + self.assertEqual(disc.y_slices[a.id][0], slice(0, 10, None)) + + b_test = np.ones((10, 1)) + np.testing.assert_array_equal( + model.variables["b"].evaluate(u={"b": b_test}), b_test + ) # check that b is added to the boundary conditions model.bcs[b.id]["left"] @@ -128,6 +146,109 @@ def test_adding_1D_external_variable(self): self.assertEqual(model.variables["grad b"].shape_for_testing, (11, 1)) self.assertEqual(model.variables["div grad b"].shape_for_testing, (10, 1)) + def test_concatenation_external_variables(self): + model = pybamm.BaseModel() + + a = pybamm.Variable("a", domain=["test", "test1"]) + b1 = pybamm.Variable("b", domain=["test"]) + b2 = pybamm.Variable("c", domain=["test1"]) + b = pybamm.Concatenation(b1, b2) + + model.rhs = {a: a * b} + model.boundary_conditions = { + a: {"left": (0, "Dirichlet"), "right": (0, "Dirichlet")} + } + model.initial_conditions = {a: 0} + model.external_variables = [b] + model.variables = { + "a": a, + "b": b, + "b1": b1, + "b2": b2, + "c": a * b, + "grad b": pybamm.grad(b), + "div grad b": pybamm.div(pybamm.grad(b)), + } + + x = pybamm.SpatialVariable("x", domain="test", coord_sys="cartesian") + y = pybamm.SpatialVariable("y", domain="test1", coord_sys="cartesian") + geometry = { + "test": { + "primary": {x: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}} + }, + "test1": { + "primary": {y: {"min": pybamm.Scalar(1), "max": pybamm.Scalar(2)}} + }, + } + + submesh_types = { + "test": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), + "test1": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh), + } + var_pts = {x: 10, y: 5} + mesh = pybamm.Mesh(geometry, submesh_types, var_pts) + + spatial_methods = { + "test": pybamm.FiniteVolume(), + "test1": pybamm.FiniteVolume(), + } + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + + self.assertEqual(disc.y_slices[a.id][0], slice(0, 15, None)) + + b_test = np.linspace(0, 1, 15)[:, np.newaxis] + np.testing.assert_array_equal( + model.variables["b"].evaluate(u={"b": b_test}), b_test + ) + np.testing.assert_array_equal( + model.variables["b1"].evaluate(u={"b": b_test}), b_test[:10] + ) + np.testing.assert_array_equal( + model.variables["b2"].evaluate(u={"b": b_test}), b_test[10:] + ) + + # check that b is added to the boundary conditions + model.bcs[b.id]["left"] + model.bcs[b.id]["right"] + + # check that grad and div(grad ) produce the correct shapes + self.assertEqual(model.variables["b"].shape_for_testing, (15, 1)) + self.assertEqual(model.variables["grad b"].shape_for_testing, (16, 1)) + self.assertEqual(model.variables["div grad b"].shape_for_testing, (15, 1)) + self.assertEqual(model.variables["b1"].shape_for_testing, (10, 1)) + self.assertEqual(model.variables["b2"].shape_for_testing, (5, 1)) + + def test_adding_2D_external_variable_fail(self): + model = pybamm.BaseModel() + a = pybamm.Variable( + "a", + domain=["negative electrode", "separator"], + auxiliary_domains={"secondary": "current collector"}, + ) + b1 = pybamm.Variable( + "b", + domain="negative electrode", + auxiliary_domains={"secondary": "current collector"}, + ) + b2 = pybamm.Variable( + "b", + domain="separator", + auxiliary_domains={"secondary": "current collector"}, + ) + b = pybamm.Concatenation(b1, b2) + + model.rhs = {a: a * b} + model.initial_conditions = {a: 0} + model.external_variables = [b] + model.variables = {"b": b} + + disc = get_1p1d_discretisation_for_testing() + with self.assertRaisesRegex( + NotImplementedError, "Cannot create 2D external variable" + ): + disc.process_model(model) + def test_discretise_slicing(self): # create discretisation mesh = get_mesh_for_testing() @@ -751,9 +872,7 @@ def test_broadcast(self): combined_submesh = mesh.combine_submeshes(*whole_cell) # scalar - broad = disc._spatial_methods[whole_cell[0]].broadcast( - a, whole_cell, {}, broadcast_type="full" - ) + broad = disc.process_symbol(pybamm.FullBroadcast(a, whole_cell, {})) np.testing.assert_array_equal( broad.evaluate(), 7 * np.ones_like(combined_submesh[0].nodes[:, np.newaxis]) ) @@ -766,7 +885,7 @@ def test_broadcast(self): # process Broadcast variable disc.y_slices = {var.id: [slice(1)]} - broad1 = pybamm.Broadcast(var, ["negative electrode"]) + broad1 = pybamm.FullBroadcast(var, ["negative electrode"], None) broad1_disc = disc.process_symbol(broad1) self.assertIsInstance(broad1_disc, pybamm.Multiplication) self.assertIsInstance(broad1_disc.children[0], pybamm.StateVector) @@ -777,39 +896,40 @@ def test_broadcast_2D(self): var = pybamm.Variable("var", ["current collector"]) disc = get_1p1d_discretisation_for_testing() mesh = disc.mesh - broad = pybamm.Broadcast(var, "separator", broadcast_type="primary") + broad = pybamm.PrimaryBroadcast(var, "separator") disc.set_variable_slices([var]) broad_disc = disc.process_symbol(broad) - self.assertIsInstance(broad_disc, pybamm.Outer) - self.assertIsInstance(broad_disc.children[0], pybamm.StateVector) - self.assertIsInstance(broad_disc.children[1], pybamm.Vector) + self.assertIsInstance(broad_disc, pybamm.MatrixMultiplication) + self.assertIsInstance(broad_disc.children[0], pybamm.Matrix) + self.assertIsInstance(broad_disc.children[1], pybamm.StateVector) self.assertEqual( broad_disc.shape, (mesh["separator"][0].npts * mesh["current collector"][0].npts, 1), ) + y_test = np.linspace(0, 1, mesh["current collector"][0].npts) + np.testing.assert_array_equal( + broad_disc.evaluate(y=y_test), + np.outer(y_test, np.ones(mesh["separator"][0].npts)).reshape(-1, 1), + ) - def test_outer(self): - - # create discretisation - disc = get_1p1d_discretisation_for_testing() + def test_secondary_broadcast_2D(self): + # secondary broadcast in 2D --> Matrix multiplication + disc = get_discretisation_for_testing() mesh = disc.mesh - - var_z = pybamm.Variable("var_z", ["current collector"]) - var_x = pybamm.Vector( - np.linspace(0, 1, mesh["separator"][0].npts), domain="separator" + var = pybamm.Vector( + mesh["negative particle"][0].nodes, domain=["negative particle"] ) + broad = pybamm.SecondaryBroadcast(var, "negative electrode") - # process Outer variable - disc.set_variable_slices([var_z, var_x]) - outer = pybamm.outer(var_z, var_x) - outer_disc = disc.process_symbol(outer) - self.assertIsInstance(outer_disc, pybamm.Outer) - self.assertIsInstance(outer_disc.children[0], pybamm.StateVector) - self.assertIsInstance(outer_disc.children[1], pybamm.Vector) + disc.set_variable_slices([var]) + broad_disc = disc.process_symbol(broad) + self.assertIsInstance(broad_disc, pybamm.MatrixMultiplication) + self.assertIsInstance(broad_disc.children[0], pybamm.Matrix) + self.assertIsInstance(broad_disc.children[1], pybamm.Vector) self.assertEqual( - outer_disc.shape, - (mesh["separator"][0].npts * mesh["current collector"][0].npts, 1), + broad_disc.shape, + (mesh["negative particle"][0].npts * mesh["negative electrode"][0].npts, 1), ) def test_concatenation(self): @@ -825,8 +945,8 @@ def test_concatenation(self): def test_concatenation_of_scalars(self): whole_cell = ["negative electrode", "separator", "positive electrode"] - a = pybamm.Broadcast(5, ["negative electrode"]) - b = pybamm.Broadcast(4, ["positive electrode"]) + a = pybamm.PrimaryBroadcast(5, ["negative electrode"]) + b = pybamm.PrimaryBroadcast(4, ["separator"]) # create discretisation disc = get_discretisation_for_testing() @@ -840,7 +960,7 @@ def test_concatenation_of_scalars(self): expected_vector = np.concatenate( [ 5 * np.ones_like(mesh["negative electrode"][0].nodes), - 4 * np.ones_like(mesh["positive electrode"][0].nodes), + 4 * np.ones_like(mesh["separator"][0].nodes), ] )[:, np.newaxis] np.testing.assert_allclose(eqn_disc.evaluate(), expected_vector) @@ -898,7 +1018,7 @@ def test_exceptions(self): # check doesn't raise if broadcast model.variables = { - c_n.name: pybamm.Broadcast(pybamm.Scalar(2), ["negative electrode"]) + c_n.name: pybamm.PrimaryBroadcast(pybamm.Scalar(2), ["negative electrode"]) } disc.process_model(model) @@ -934,6 +1054,37 @@ def test_process_with_no_check(self): disc = get_discretisation_for_testing() disc.process_model(model, check_model=False) + def test_mass_matirx_inverse(self): + # get mesh + mesh = get_2p1d_mesh_for_testing(ypts=5, zpts=5) + spatial_methods = { + "macroscale": pybamm.FiniteVolume(), + "current collector": pybamm.ScikitFiniteElement(), + } + # create model + a = pybamm.Variable("a", domain="negative electrode") + b = pybamm.Variable("b", domain="current collector") + model = pybamm.BaseModel() + model.rhs = {a: pybamm.Laplacian(a), b: 4 * pybamm.Laplacian(b)} + model.initial_conditions = {a: pybamm.Scalar(3), b: pybamm.Scalar(10)} + model.boundary_conditions = { + a: {"left": (0, "Neumann"), "right": (0, "Neumann")}, + b: {"negative tab": (0, "Neumann"), "positive tab": (0, "Neumann")}, + } + model.variables = {"a": a, "b": b} + + # create discretisation + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + + # test that computing mass matrix block-by-block (as is done during + # discretisation) gives the correct result + # Note: inverse is more efficient in csc format + mass_inv = inv(csc_matrix(model.mass_matrix.entries)) + np.testing.assert_equal( + model.mass_matrix_inv.entries.toarray(), mass_inv.toarray() + ) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_expression_tree/test_binary_operators.py b/tests/unit/test_expression_tree/test_binary_operators.py index 2381bcab6f..8127b41b22 100644 --- a/tests/unit/test_expression_tree/test_binary_operators.py +++ b/tests/unit/test_expression_tree/test_binary_operators.py @@ -58,54 +58,6 @@ def test_power(self): pow2 = pybamm.Power(a, b) self.assertEqual(pow2.evaluate(), 16) - def test_outer(self): - # Outer class - v = pybamm.Vector(np.ones(5), domain="current collector") - w = pybamm.Vector(2 * np.ones(3), domain="test") - outer = pybamm.Outer(v, w) - np.testing.assert_array_equal(outer.evaluate(), 2 * np.ones((15, 1))) - self.assertEqual(outer.domain, w.domain) - self.assertEqual( - str(outer), "outer(Column vector of length 5, Column vector of length 3)" - ) - - # outer function - # if there is no domain clash, normal multiplication is retured - u = pybamm.Vector(np.linspace(0, 1, 5)) - outer = pybamm.outer(u, v) - self.assertIsInstance(outer, pybamm.Multiplication) - np.testing.assert_array_equal(outer.evaluate(), u.evaluate()) - # otherwise, Outer class is returned - outer_fun = pybamm.outer(v, w) - outer_class = pybamm.Outer(v, w) - self.assertEqual(outer_fun.id, outer_class.id) - - # failures - y = pybamm.StateVector(slice(10)) - with self.assertRaisesRegex( - TypeError, "right child must only contain Vectors and Scalars" - ): - pybamm.Outer(v, y) - with self.assertRaises(NotImplementedError): - outer_fun.diff(None) - - def test_kron(self): - # Kron class - A = pybamm.Matrix(np.eye(2)) - b = pybamm.Vector(np.array([[4], [5]])) - kron = pybamm.Kron(A, b) - np.testing.assert_array_equal( - kron.evaluate().toarray(), np.kron(A.entries, b.entries) - ) - - # failures - with self.assertRaises(NotImplementedError): - kron.diff(None) - - y = pybamm.StateVector(slice(0, 2)) - with self.assertRaises(NotImplementedError): - kron.jac(y) - def test_known_eval(self): # Scalars a = pybamm.Scalar(4) diff --git a/tests/unit/test_expression_tree/test_broadcasts.py b/tests/unit/test_expression_tree/test_broadcasts.py index a331652d9f..8febf50bc5 100644 --- a/tests/unit/test_expression_tree/test_broadcasts.py +++ b/tests/unit/test_expression_tree/test_broadcasts.py @@ -7,24 +7,110 @@ class TestBroadcasts(unittest.TestCase): - def test_broadcast(self): + def test_primary_broadcast(self): a = pybamm.Symbol("a") - broad_a = pybamm.Broadcast(a, ["negative electrode"]) + broad_a = pybamm.PrimaryBroadcast(a, ["negative electrode"]) self.assertEqual(broad_a.name, "broadcast") self.assertEqual(broad_a.children[0].name, a.name) self.assertEqual(broad_a.domain, ["negative electrode"]) - def test_broadcast_number(self): - broad_a = pybamm.Broadcast(1, ["negative electrode"]) + a = pybamm.Symbol( + "a", + domain="negative electrode", + auxiliary_domains={"secondary": "current collector"}, + ) + broad_a = pybamm.PrimaryBroadcast(a, ["negative particle"]) + self.assertEqual(broad_a.domain, ["negative particle"]) + self.assertEqual( + broad_a.auxiliary_domains, + {"secondary": ["negative electrode"], "tertiary": ["current collector"]}, + ) + + a = pybamm.Symbol("a", domain="current collector") + with self.assertRaisesRegex( + pybamm.DomainError, "Primary broadcast from current collector" + ): + pybamm.PrimaryBroadcast(a, "bad domain") + a = pybamm.Symbol("a", domain="negative electrode") + with self.assertRaisesRegex( + pybamm.DomainError, "Primary broadcast from electrode" + ): + pybamm.PrimaryBroadcast(a, "current collector") + a = pybamm.Symbol("a", domain="negative particle") + with self.assertRaisesRegex( + pybamm.DomainError, "Cannot do primary broadcast from particle domain" + ): + pybamm.PrimaryBroadcast(a, "current collector") + + def test_secondary_broadcast(self): + a = pybamm.Symbol( + "a", + domain=["negative particle"], + auxiliary_domains={"secondary": "current collector"}, + ) + broad_a = pybamm.SecondaryBroadcast(a, ["negative electrode"]) + self.assertEqual(broad_a.domain, ["negative particle"]) + self.assertEqual( + broad_a.auxiliary_domains, + {"secondary": ["negative electrode"], "tertiary": ["current collector"]}, + ) + + a = pybamm.Symbol("a", domain="negative particle") + with self.assertRaisesRegex( + pybamm.DomainError, "Secondary broadcast from particle" + ): + pybamm.SecondaryBroadcast(a, "current collector") + a = pybamm.Symbol("a", domain="negative electrode") + with self.assertRaisesRegex( + pybamm.DomainError, "Secondary broadcast from electrode" + ): + pybamm.SecondaryBroadcast(a, "negative particle") + + a = pybamm.Symbol("a", domain="current collector") + with self.assertRaisesRegex( + pybamm.DomainError, "Cannot do secondary broadcast" + ): + pybamm.SecondaryBroadcast(a, "electrode") + + def test_full_broadcast(self): + a = pybamm.Symbol("a") + broad_a = pybamm.FullBroadcast(a, ["negative electrode"], "current collector") + self.assertEqual(broad_a.domain, ["negative electrode"]) + self.assertEqual(broad_a.auxiliary_domains["secondary"], ["current collector"]) + + def test_full_broadcast_number(self): + broad_a = pybamm.FullBroadcast(1, ["negative electrode"], None) self.assertEqual(broad_a.name, "broadcast") self.assertIsInstance(broad_a.children[0], pybamm.Symbol) self.assertEqual(broad_a.children[0].evaluate(), np.array([1])) self.assertEqual(broad_a.domain, ["negative electrode"]) - def test_broadcast_type(self): a = pybamm.Symbol("a", domain="current collector") - with self.assertRaisesRegex(ValueError, "Variables on the current collector"): - pybamm.Broadcast(a, "electrode") + with self.assertRaisesRegex(pybamm.DomainError, "Cannot do full broadcast"): + pybamm.FullBroadcast(a, "electrode", None) + + def test_ones_like(self): + a = pybamm.Variable("a") + ones_like_a = pybamm.ones_like(a) + self.assertEqual(ones_like_a.id, pybamm.Scalar(1).id) + + a = pybamm.Variable( + "a", + domain="negative electrode", + auxiliary_domains={"secondary": "current collector"}, + ) + ones_like_a = pybamm.ones_like(a) + self.assertIsInstance(ones_like_a, pybamm.FullBroadcast) + self.assertEqual(ones_like_a.name, "broadcast") + self.assertEqual(ones_like_a.domain, a.domain) + self.assertEqual(ones_like_a.auxiliary_domains, a.auxiliary_domains) + + b = pybamm.Variable("b", domain="current collector") + ones_like_ab = pybamm.ones_like(b, a) + self.assertIsInstance(ones_like_ab, pybamm.FullBroadcast) + self.assertEqual(ones_like_ab.name, "broadcast") + self.assertEqual(ones_like_ab.domain, a.domain) + self.assertEqual(ones_like_ab.auxiliary_domains, a.auxiliary_domains) if __name__ == "__main__": diff --git a/tests/unit/test_expression_tree/test_concatenations.py b/tests/unit/test_expression_tree/test_concatenations.py index a010df4704..2a5a161148 100644 --- a/tests/unit/test_expression_tree/test_concatenations.py +++ b/tests/unit/test_expression_tree/test_concatenations.py @@ -205,9 +205,9 @@ def test_broadcast_and_concatenate(self): mesh = disc.mesh # Piecewise constant scalars - a = pybamm.Broadcast(1, ["negative electrode"]) - b = pybamm.Broadcast(2, ["separator"]) - c = pybamm.Broadcast(3, ["positive electrode"]) + a = pybamm.PrimaryBroadcast(1, ["negative electrode"]) + b = pybamm.PrimaryBroadcast(2, ["separator"]) + c = pybamm.PrimaryBroadcast(3, ["positive electrode"]) conc = pybamm.Concatenation(a, b, c) self.assertEqual( @@ -229,9 +229,9 @@ def test_broadcast_and_concatenate(self): ) # Piecewise constant functions of time - a_t = pybamm.Broadcast(pybamm.t, ["negative electrode"]) - b_t = pybamm.Broadcast(2 * pybamm.t, ["separator"]) - c_t = pybamm.Broadcast(3 * pybamm.t, ["positive electrode"]) + a_t = pybamm.PrimaryBroadcast(pybamm.t, ["negative electrode"]) + b_t = pybamm.PrimaryBroadcast(2 * pybamm.t, ["separator"]) + c_t = pybamm.PrimaryBroadcast(3 * pybamm.t, ["positive electrode"]) conc = pybamm.Concatenation(a_t, b_t, c_t) self.assertEqual( @@ -254,9 +254,13 @@ def test_broadcast_and_concatenate(self): ) # Piecewise constant state vectors - a_sv = pybamm.Broadcast(pybamm.StateVector(slice(0, 1)), ["negative electrode"]) - b_sv = pybamm.Broadcast(pybamm.StateVector(slice(1, 2)), ["separator"]) - c_sv = pybamm.Broadcast(pybamm.StateVector(slice(2, 3)), ["positive electrode"]) + a_sv = pybamm.PrimaryBroadcast( + pybamm.StateVector(slice(0, 1)), ["negative electrode"] + ) + b_sv = pybamm.PrimaryBroadcast(pybamm.StateVector(slice(1, 2)), ["separator"]) + c_sv = pybamm.PrimaryBroadcast( + pybamm.StateVector(slice(2, 3)), ["positive electrode"] + ) conc = pybamm.Concatenation(a_sv, b_sv, c_sv) self.assertEqual( diff --git a/tests/unit/test_expression_tree/test_input_parameter.py b/tests/unit/test_expression_tree/test_input_parameter.py new file mode 100644 index 0000000000..f5c24e3909 --- /dev/null +++ b/tests/unit/test_expression_tree/test_input_parameter.py @@ -0,0 +1,38 @@ +# +# Tests for the InputParameter class +# +import numbers +import pybamm +import unittest + + +class TestInputParameter(unittest.TestCase): + def test_input_parameter_init(self): + a = pybamm.InputParameter("a") + self.assertEqual(a.name, "a") + self.assertEqual(a.evaluate(u={"a": 1}), 1) + self.assertEqual(a.evaluate(u={"a": 5}), 5) + + def test_evaluate_for_shape(self): + a = pybamm.InputParameter("a") + self.assertIsInstance(a.evaluate_for_shape(), numbers.Number) + + def test_errors(self): + a = pybamm.InputParameter("a") + with self.assertRaises(TypeError): + a.evaluate(u="not a dictionary") + with self.assertRaises(KeyError): + a.evaluate(u={"bad param": 5}) + # if u is not provided it gets turned into a dictionary and then raises KeyError + with self.assertRaises(KeyError): + a.evaluate() + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_expression_tree/test_operations/test_convert_to_casadi.py b/tests/unit/test_expression_tree/test_operations/test_convert_to_casadi.py index ab7d50d9b9..017cae7f7a 100644 --- a/tests/unit/test_expression_tree/test_operations/test_convert_to_casadi.py +++ b/tests/unit/test_expression_tree/test_operations/test_convert_to_casadi.py @@ -71,12 +71,6 @@ def test_convert_array_symbols(self): # State Vector self.assert_casadi_equal(pybamm_y.to_casadi(casadi_t, casadi_y), casadi_y) - # outer product - outer = pybamm.Outer(pybamm_a, pybamm_a) - self.assert_casadi_equal( - outer.to_casadi(), casadi.MX(outer.evaluate()), evalf=True - ) - def test_special_functions(self): a = pybamm.Array(np.array([1, 2, 3, 4, 5])) self.assert_casadi_equal(pybamm.max(a).to_casadi(), casadi.MX(5), evalf=True) @@ -124,7 +118,7 @@ def test_concatenations(self): # Domain concatenation mesh = get_mesh_for_testing() a_dom = ["negative electrode"] - b_dom = ["positive electrode"] + b_dom = ["separator"] a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]][0].nodes), domain=a_dom) b = pybamm.Vector(np.ones_like(mesh[b_dom[0]][0].nodes), domain=b_dom) conc = pybamm.DomainConcatenation([b, a], mesh) @@ -157,6 +151,59 @@ def myfunction(x, y): f = pybamm.Function(myfunction, a, b).diff(b) self.assert_casadi_equal(f.to_casadi(), casadi.MX(3), evalf=True) + def test_convert_input_parameter(self): + casadi_t = casadi.MX.sym("t") + casadi_y = casadi.MX.sym("y", 10) + casadi_us = { + "Input 1": casadi.MX.sym("Input 1"), + "Input 2": casadi.MX.sym("Input 2"), + } + + pybamm_y = pybamm.StateVector(slice(0, 10)) + pybamm_u1 = pybamm.InputParameter("Input 1") + pybamm_u2 = pybamm.InputParameter("Input 2") + + # Input only + self.assert_casadi_equal( + pybamm_u1.to_casadi(casadi_t, casadi_y, casadi_us), casadi_us["Input 1"] + ) + + # More complex + expr = pybamm_u1 + pybamm_y + self.assert_casadi_equal( + expr.to_casadi(casadi_t, casadi_y, casadi_us), + casadi_us["Input 1"] + casadi_y, + ) + expr = pybamm_u2 * pybamm_y + self.assert_casadi_equal( + expr.to_casadi(casadi_t, casadi_y, casadi_us), + casadi_us["Input 2"] * casadi_y, + ) + + def test_convert_external_variable(self): + casadi_t = casadi.MX.sym("t") + casadi_y = casadi.MX.sym("y", 10) + casadi_us = { + "External 1": casadi.MX.sym("External 1", 3), + "External 2": casadi.MX.sym("External 2", 10), + } + + pybamm_y = pybamm.StateVector(slice(0, 10)) + pybamm_u1 = pybamm.ExternalVariable("External 1", 3) + pybamm_u2 = pybamm.ExternalVariable("External 2", 10) + + # External only + self.assert_casadi_equal( + pybamm_u1.to_casadi(casadi_t, casadi_y, casadi_us), casadi_us["External 1"] + ) + + # More complex + expr = pybamm_u2 + pybamm_y + self.assert_casadi_equal( + expr.to_casadi(casadi_t, casadi_y, casadi_us), + casadi_us["External 2"] + casadi_y, + ) + def test_errors(self): y = pybamm.StateVector(slice(0, 10)) with self.assertRaisesRegex( diff --git a/tests/unit/test_expression_tree/test_operations/test_copy.py b/tests/unit/test_expression_tree/test_operations/test_copy.py index 3161967d20..32afe6c273 100644 --- a/tests/unit/test_expression_tree/test_operations/test_copy.py +++ b/tests/unit/test_expression_tree/test_operations/test_copy.py @@ -32,11 +32,13 @@ def test_symbol_new_copy(self): pybamm.BoundaryValue(v_n, "right"), pybamm.BoundaryGradient(v_n, "right"), pybamm.PrimaryBroadcast(a, "domain"), + pybamm.SecondaryBroadcast(v_n, "current collector"), pybamm.FullBroadcast(a, "domain", {"secondary": "other domain"}), pybamm.Concatenation(v_n, v_s), pybamm.NumpyConcatenation(a, b, v_s), pybamm.DomainConcatenation([v_n, v_s], mesh), pybamm.Parameter("param"), + pybamm.InputParameter("param"), pybamm.StateVector(slice(0, 56)), pybamm.Matrix(np.ones((50, 40))), pybamm.SpatialVariable("x", ["negative electrode"]), diff --git a/tests/unit/test_expression_tree/test_operations/test_evaluate.py b/tests/unit/test_expression_tree/test_operations/test_evaluate.py index 52addcb634..88bc911e1f 100644 --- a/tests/unit/test_expression_tree/test_operations/test_evaluate.py +++ b/tests/unit/test_expression_tree/test_operations/test_evaluate.py @@ -244,7 +244,7 @@ def test_domain_concatenation_2D(self): disc = get_1p1d_discretisation_for_testing() a_dom = ["negative electrode"] - b_dom = ["positive electrode"] + b_dom = ["separator"] a = pybamm.Variable("a", domain=a_dom) b = pybamm.Variable("b", domain=b_dom) conc = pybamm.Concatenation(a, b) @@ -391,14 +391,6 @@ def test_evaluator_python(self): result = evaluator.evaluate(t=t, y=y).toarray() np.testing.assert_allclose(result, expr.evaluate(t=t, y=y).toarray()) - # test Outer - v = pybamm.Vector(np.ones(5), domain="current collector") - w = pybamm.Vector(2 * np.ones(3), domain="test") - expr = pybamm.Outer(v, w) - evaluator = pybamm.EvaluatorPython(expr) - result = evaluator.evaluate() - np.testing.assert_allclose(result, expr.evaluate()) - # test Inner v = pybamm.Vector(np.ones(5), domain="test") w = pybamm.Vector(2 * np.ones(5), domain="test") diff --git a/tests/unit/test_expression_tree/test_operations/test_jac.py b/tests/unit/test_expression_tree/test_operations/test_jac.py index 526f313903..270b030c44 100644 --- a/tests/unit/test_expression_tree/test_operations/test_jac.py +++ b/tests/unit/test_expression_tree/test_operations/test_jac.py @@ -56,32 +56,6 @@ def test_linear(self): du_dv = u.jac(v).evaluate().toarray() np.testing.assert_array_equal(du_dv, jacobian) - # test Jacobian of Outer (must set domain to be 'current collector') - u.domain = ["current collector"] - func = pybamm.Outer(u, pybamm.Scalar(4)) - jacobian = np.array([[4, 0, 0, 0], [0, 4, 0, 0]]) - dfunc_dy = func.jac(y).evaluate(y=y0) - np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) - - func = pybamm.Outer(u, pybamm.Vector(np.array([1, 2, 3]))) - jacobian = np.array( - [ - [1, 0, 0, 0], - [2, 0, 0, 0], - [3, 0, 0, 0], - [0, 1, 0, 0], - [0, 2, 0, 0], - [0, 3, 0, 0], - ] - ) - dfunc_dy = func.jac(y).evaluate(y=y0) - np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) - - # test jac of outer if left evaluates to number - func = pybamm.Outer(pybamm.Scalar(1), pybamm.Scalar(4)) - dfunc_dy = func.jac(y).evaluate(y=y0) - np.testing.assert_array_equal(0, dfunc_dy.toarray()) - def test_nonlinear(self): y = pybamm.StateVector(slice(0, 4)) u = pybamm.StateVector(slice(0, 2)) diff --git a/tests/unit/test_expression_tree/test_operations/test_jac_2D.py b/tests/unit/test_expression_tree/test_operations/test_jac_2D.py index 0d3679715e..d08e2b313a 100644 --- a/tests/unit/test_expression_tree/test_operations/test_jac_2D.py +++ b/tests/unit/test_expression_tree/test_operations/test_jac_2D.py @@ -86,40 +86,6 @@ def test_linear(self): with self.assertRaises(NotImplementedError): u.jac(v) - # test Jacobian of Outer (must set domain to be 'current collector') - u.domain = ["current collector"] - func = pybamm.Outer(u, pybamm.Scalar(4)) - jacobian = np.array( - [ - [4, 0, 0, 0, 0, 0, 0, 0], - [0, 4, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 4, 0, 0, 0], - [0, 0, 0, 0, 0, 4, 0, 0], - ] - ) - dfunc_dy = func.jac(y).evaluate(y=y0) - np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) - - func = pybamm.Outer(u, pybamm.Vector(np.array([1, 2, 3]))) - jacobian = np.array( - [ - [1, 0, 0, 0, 0, 0, 0, 0], - [2, 0, 0, 0, 0, 0, 0, 0], - [3, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 2, 0, 0, 0, 0, 0, 0], - [0, 3, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 2, 0, 0, 0], - [0, 0, 0, 0, 3, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 2, 0, 0], - [0, 0, 0, 0, 0, 3, 0, 0], - ] - ) - dfunc_dy = func.jac(y).evaluate(y=y0) - np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) - def test_nonlinear(self): y = pybamm.StateVector(slice(0, 8)) u = pybamm.StateVector(slice(0, 2), slice(4, 6)) @@ -251,21 +217,16 @@ def test_jac_of_domain_concatenation(self): a_dom = ["negative electrode"] b_dom = ["separator"] c_dom = ["positive electrode"] - a_npts = mesh[a_dom[0]][0].npts - b_npts = mesh[b_dom[0]][0].npts - c_npts = mesh[c_dom[0]][0].npts cc_npts = mesh["current collector"][0].npts curr_coll_vector = pybamm.Vector(np.ones(cc_npts), domain="current collector") - a = 2 * pybamm.Outer( - curr_coll_vector, pybamm.Vector(np.ones(a_npts), domain=a_dom) - ) - b = pybamm.Outer(curr_coll_vector, pybamm.Vector(np.ones(b_npts), domain=b_dom)) - c = 3 * pybamm.Outer( - curr_coll_vector, pybamm.Vector(np.ones(c_npts), domain=c_dom) - ) + a = 2 * pybamm.PrimaryBroadcast(curr_coll_vector, a_dom) + b = pybamm.PrimaryBroadcast(curr_coll_vector, b_dom) + c = 3 * pybamm.PrimaryBroadcast(curr_coll_vector, c_dom) - conc = pybamm.DomainConcatenation([a, b, c], mesh) - jac = conc.jac(y).evaluate().toarray() + conc = pybamm.Concatenation(a, b, c) + disc.set_variable_slices([conc]) + conc_disc = disc.process_symbol(conc) + jac = conc_disc.jac(y).evaluate().toarray() np.testing.assert_array_equal(jac, np.zeros((1500, 1500))) # Jacobian of a DomainConcatenation of StateVectors diff --git a/tests/unit/test_expression_tree/test_operations/test_simplify.py b/tests/unit/test_expression_tree/test_operations/test_simplify.py index 4b45a35ede..9c47da4401 100644 --- a/tests/unit/test_expression_tree/test_operations/test_simplify.py +++ b/tests/unit/test_expression_tree/test_operations/test_simplify.py @@ -78,7 +78,7 @@ def myfunction(x, y): # Delta function self.assertIsInstance( - (pybamm.DeltaFunction(v_neg, "right", None)).simplify(), + (pybamm.DeltaFunction(v_neg, "right", "domain")).simplify(), pybamm.DeltaFunction, ) @@ -564,39 +564,11 @@ def test_simplify_concatenation_state_vectors(self): self.assertEqual(conc_simp.y_slices[0].stop, len(y)) np.testing.assert_array_equal(conc_disc.evaluate(y=y), conc_simp.evaluate(y=y)) - def test_simplify_outer(self): - v = pybamm.Vector(np.ones(5), domain="current collector") - w = pybamm.Vector(2 * np.ones(3), domain="test") - outer_simp = pybamm.Outer(v, w).simplify() - self.assertIsInstance(outer_simp, pybamm.Vector) - np.testing.assert_array_equal(outer_simp.evaluate(), 2 * np.ones((15, 1))) - - def test_simplify_divide_outer(self): - u = pybamm.Scalar(1) - v = pybamm.StateVector(slice(0, 5), domain="current collector") - outer = pybamm.Outer(v, u) - - exp1 = pybamm.Division(pybamm.Division(outer, u), u) - self.assertIsInstance(exp1.simplify(), pybamm.Outer) - - exp2 = pybamm.Division(pybamm.Division(outer, 2 * u), u) - self.assertIsInstance(exp2.simplify(), pybamm.Multiplication) - - exp3 = pybamm.Division(pybamm.Division(outer, u), 2 * u) - self.assertIsInstance(exp3.simplify(), pybamm.Multiplication) - - exp4 = pybamm.Division(pybamm.Division(outer, 2 * u), 2 * u) - self.assertIsInstance(exp4.simplify(), pybamm.Multiplication) - - def test_simplify_kron(self): - A = pybamm.Matrix(np.eye(2)) - b = pybamm.Vector(np.array([[4], [5]])) - kron = pybamm.Kron(A, b) - kron_simp = kron.simplify() - self.assertIsInstance(kron_simp, pybamm.Matrix) - np.testing.assert_array_equal( - kron_simp.evaluate().toarray(), np.kron(A.entries, b.entries) - ) + def test_simplify_broadcast(self): + v = pybamm.StateVector(slice(0, 1)) + broad = pybamm.PrimaryBroadcast(v, "test") + broad_simp = broad.simplify() + self.assertEqual(broad_simp.id, broad.id) def test_simplify_heaviside(self): a = pybamm.Scalar(1) diff --git a/tests/unit/test_expression_tree/test_symbol.py b/tests/unit/test_expression_tree/test_symbol.py index 55956c5514..9782a57ab9 100644 --- a/tests/unit/test_expression_tree/test_symbol.py +++ b/tests/unit/test_expression_tree/test_symbol.py @@ -43,6 +43,16 @@ def test_symbol_domains(self): self.assertEqual(a.domain, ["t", "e", "s"]) with self.assertRaises(TypeError): a = pybamm.Symbol("a", domain=1) + with self.assertRaisesRegex( + pybamm.DomainError, + "Domain cannot be empty if auxiliary domains are not empty", + ): + b = pybamm.Symbol("b", auxiliary_domains={"sec": ["test sec"]}) + b = pybamm.Symbol("b", domain="test", auxiliary_domains={"sec": ["test sec"]}) + with self.assertRaisesRegex( + pybamm.DomainError, "Domain cannot be the same as an auxiliary domain" + ): + b.domain = "test sec" def test_symbol_auxiliary_domains(self): a = pybamm.Symbol( @@ -58,6 +68,19 @@ def test_symbol_auxiliary_domains(self): self.assertEqual(a.domain, ["t", "e", "s"]) with self.assertRaises(TypeError): a = pybamm.Symbol("a", domain=1) + b = pybamm.Symbol("b", domain="test sec") + with self.assertRaisesRegex( + pybamm.DomainError, "Domain cannot be the same as an auxiliary domain" + ): + b.auxiliary_domains = {"sec": "test sec"} + with self.assertRaisesRegex( + pybamm.DomainError, "All auxiliary domains must be different" + ): + b = pybamm.Symbol( + "b", + domain="test", + auxiliary_domains={"sec": ["test sec"], "tert": ["test sec"]}, + ) def test_symbol_methods(self): a = pybamm.Symbol("a") @@ -361,14 +384,14 @@ def test_shape_and_size_for_testing(self): self.assertEqual(concat.size_for_testing, 30) var = pybamm.Variable("var", domain="negative electrode") - broadcast = pybamm.Broadcast(0, "negative electrode") + broadcast = pybamm.PrimaryBroadcast(0, "negative electrode") self.assertEqual(var.shape_for_testing, broadcast.shape_for_testing) self.assertEqual( (var + broadcast).shape_for_testing, broadcast.shape_for_testing ) var = pybamm.Variable("var", domain=["random domain", "other domain"]) - broadcast = pybamm.Broadcast(0, ["random domain", "other domain"]) + broadcast = pybamm.PrimaryBroadcast(0, ["random domain", "other domain"]) self.assertEqual(var.shape_for_testing, broadcast.shape_for_testing) self.assertEqual( (var + broadcast).shape_for_testing, broadcast.shape_for_testing diff --git a/tests/unit/test_expression_tree/test_unary_operators.py b/tests/unit/test_expression_tree/test_unary_operators.py index 570411b1cd..16c6519638 100644 --- a/tests/unit/test_expression_tree/test_unary_operators.py +++ b/tests/unit/test_expression_tree/test_unary_operators.py @@ -195,6 +195,10 @@ def test_delta_function(self): self.assertEqual(delta_a.side, "right") self.assertEqual(delta_a.child.id, a.id) self.assertFalse(delta_a.evaluates_on_edges()) + with self.assertRaisesRegex( + pybamm.DomainError, "Delta function domain cannot be None" + ): + delta_a = pybamm.DeltaFunction(a, "right", None) def test_boundary_operators(self): a = pybamm.Symbol("a", domain="some domain") @@ -213,7 +217,7 @@ def test_boundary_value(self): self.assertEqual(boundary_a.id, a.id) boundary_broad_a = pybamm.boundary_value( - pybamm.Broadcast(a, ["negative electrode"]), "left" + pybamm.PrimaryBroadcast(a, ["negative electrode"]), "left" ) self.assertEqual(boundary_broad_a.evaluate(), np.array([1])) @@ -253,13 +257,15 @@ def test_average(self): average_a = pybamm.x_average(a) self.assertEqual(average_a.id, a.id) - average_broad_a = pybamm.x_average(pybamm.Broadcast(a, ["negative electrode"])) + average_broad_a = pybamm.x_average( + pybamm.PrimaryBroadcast(a, ["negative electrode"]) + ) self.assertEqual(average_broad_a.evaluate(), np.array([1])) conc_broad = pybamm.Concatenation( - pybamm.Broadcast(1, ["negative electrode"]), - pybamm.Broadcast(2, ["separator"]), - pybamm.Broadcast(3, ["positive electrode"]), + pybamm.PrimaryBroadcast(1, ["negative electrode"]), + pybamm.PrimaryBroadcast(2, ["separator"]), + pybamm.PrimaryBroadcast(3, ["positive electrode"]), ) average_conc_broad = pybamm.x_average(conc_broad) self.assertIsInstance(average_conc_broad, pybamm.Division) @@ -288,7 +294,9 @@ def test_r_average(self): average_a = pybamm.r_average(a) self.assertEqual(average_a.id, a.id) - average_broad_a = pybamm.r_average(pybamm.Broadcast(a, ["negative particle"])) + average_broad_a = pybamm.r_average( + pybamm.PrimaryBroadcast(a, ["negative particle"]) + ) self.assertEqual(average_broad_a.evaluate(), np.array([1])) for domain in [["negative particle"], ["positive particle"]]: @@ -312,9 +320,11 @@ def test_yz_average(self): self.assertEqual(z_average_a.id, a.id) self.assertEqual(yz_average_a.id, a.id) - z_average_broad_a = pybamm.z_average(pybamm.Broadcast(a, ["current collector"])) + z_average_broad_a = pybamm.z_average( + pybamm.PrimaryBroadcast(a, ["current collector"]) + ) yz_average_broad_a = pybamm.yz_average( - pybamm.Broadcast(a, ["current collector"]) + pybamm.PrimaryBroadcast(a, ["current collector"]) ) self.assertEqual(z_average_broad_a.evaluate(), np.array([1])) self.assertEqual(yz_average_broad_a.evaluate(), np.array([1])) diff --git a/tests/unit/test_expression_tree/test_variable.py b/tests/unit/test_expression_tree/test_variable.py index 749532d645..1e4e1b0bee 100644 --- a/tests/unit/test_expression_tree/test_variable.py +++ b/tests/unit/test_expression_tree/test_variable.py @@ -2,6 +2,7 @@ # Tests for the Variable class # import pybamm +import numpy as np import unittest @@ -25,6 +26,31 @@ def test_variable_id(self): self.assertNotEqual(a1.id, a4.id) +class TestExternalVariable(unittest.TestCase): + def test_external_variable_scalar(self): + a = pybamm.ExternalVariable("a", 1) + self.assertEqual(a.size, 1) + + self.assertEqual(a.evaluate(u={"a": 3}), 3) + + with self.assertRaisesRegex(KeyError, "External variable"): + a.evaluate() + with self.assertRaisesRegex(TypeError, "inputs u"): + a.evaluate(u="not a dictionary") + + def test_external_variable_vector(self): + a = pybamm.ExternalVariable("a", 10) + self.assertEqual(a.size, 10) + + a_test = 2 * np.ones((10, 1)) + np.testing.assert_array_equal(a.evaluate(u={"a": a_test}), a_test) + + np.testing.assert_array_equal(a.evaluate(u={"a": 2}), a_test) + + with self.assertRaisesRegex(ValueError, "External variable"): + a.evaluate(u={"a": np.ones((5, 1))}) + + if __name__ == "__main__": print("Add -v for more debug output") import sys diff --git a/tests/unit/test_models/test_base_model.py b/tests/unit/test_models/test_base_model.py index 6421e41bce..7feb273503 100644 --- a/tests/unit/test_models/test_base_model.py +++ b/tests/unit/test_models/test_base_model.py @@ -328,10 +328,8 @@ def test_check_well_posedness_output_variables(self): } # Check warning raised - # TODO: getting a strange bug here, related to CPython bug here: - # https://bugs.python.org/issue29620 - # with self.assertWarns(pybamm.ModelWarning): - model.check_well_posedness() + with self.assertWarns(pybamm.ModelWarning): + model.check_well_posedness() # Check None entries have been removed from the variables dictionary for key, item in model._variables.items(): diff --git a/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py b/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py index e1d3cfce91..aeda97fc0b 100644 --- a/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py +++ b/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py @@ -122,12 +122,10 @@ def test_bad_options(self): pybamm.BaseBatteryModel({"surface form": "bad surface form"}) with self.assertRaisesRegex(pybamm.OptionError, "particle model"): pybamm.BaseBatteryModel({"particle": "bad particle"}) - with self.assertRaisesRegex(pybamm.OptionError, "option single"): - pybamm.BaseBatteryModel( - {"current collector": "single particle potential pair"} - ) with self.assertRaisesRegex(pybamm.OptionError, "option set external"): pybamm.BaseBatteryModel({"current collector": "set external potential"}) + with self.assertRaisesRegex(pybamm.OptionError, "operating mode"): + pybamm.BaseBatteryModel({"operating mode": "bad operating mode"}) def test_build_twice(self): model = pybamm.lithium_ion.SPM() # need to pick a model to set vars and build diff --git a/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py b/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py index 83f3ef8571..59697e2b3a 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py +++ b/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py @@ -155,6 +155,31 @@ def test_default_geometry(self): self.assertIn("current collector", model.default_geometry) +class TestLeadAcidLOQSExternalCircuits(unittest.TestCase): + def test_well_posed_voltage(self): + options = {"operating mode": "voltage"} + model = pybamm.lead_acid.LOQS(options) + model.check_well_posedness() + + def test_well_posed_power(self): + options = {"operating mode": "power"} + model = pybamm.lead_acid.LOQS(options) + model.check_well_posedness() + + def test_well_posed_function(self): + class ExternalCircuitFunction: + num_switches = 0 + + def __call__(self, variables): + I = variables["Current [A]"] + V = variables["Terminal voltage [V]"] + return V + I - pybamm.FunctionParameter("Function", pybamm.t) + + options = {"operating mode": ExternalCircuitFunction()} + model = pybamm.lead_acid.LOQS(options) + model.check_well_posedness() + + if __name__ == "__main__": print("Add -v for more debug output") import sys diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py new file mode 100644 index 0000000000..2b56b0bb21 --- /dev/null +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py @@ -0,0 +1,35 @@ +# +# Tests for the basic lithium-ion models +# +import pybamm +import unittest + + +class TestBasicModels(unittest.TestCase): + def test_dfn_well_posed(self): + model = pybamm.lithium_ion.BasicDFN() + model.check_well_posedness() + + def test_dfn_default_geometry(self): + model = pybamm.lithium_ion.BasicDFN() + self.assertIsInstance(model.default_geometry, pybamm.Geometry) + self.assertTrue("secondary" in model.default_geometry["negative particle"]) + + def test_spm_well_posed(self): + model = pybamm.lithium_ion.BasicSPM() + model.check_well_posedness() + + def test_spm_default_geometry(self): + model = pybamm.lithium_ion.BasicSPM() + self.assertIsInstance(model.default_geometry, pybamm.Geometry) + self.assertTrue("secondary" not in model.default_geometry["negative particle"]) + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py index 3cd9c4065e..d8bc4fd2ea 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py @@ -39,20 +39,6 @@ def test_well_posed_2plus1D(self): model = pybamm.lithium_ion.SPM(options) model.check_well_posedness() - options = { - "current collector": "single particle potential pair", - "dimensionality": 1, - } - model = pybamm.lithium_ion.SPM(options) - model.check_well_posedness() - - options = { - "current collector": "single particle potential pair", - "dimensionality": 2, - } - model = pybamm.lithium_ion.SPM(options) - model.check_well_posedness() - options = {"current collector": "set external potential", "dimensionality": 0} with self.assertRaises(NotImplementedError): pybamm.lithium_ion.SPM(options) @@ -202,6 +188,31 @@ def test_surface_form_algebraic(self): model.check_well_posedness() +class TestSPMExternalCircuits(unittest.TestCase): + def test_well_posed_voltage(self): + options = {"operating mode": "voltage"} + model = pybamm.lithium_ion.SPM(options) + model.check_well_posedness() + + def test_well_posed_power(self): + options = {"operating mode": "power"} + model = pybamm.lithium_ion.SPM(options) + model.check_well_posedness() + + def test_well_posed_function(self): + class ExternalCircuitFunction: + num_switches = 0 + + def __call__(self, variables): + I = variables["Current [A]"] + V = variables["Terminal voltage [V]"] + return V + I - pybamm.FunctionParameter("Function", pybamm.t) + + options = {"operating mode": ExternalCircuitFunction()} + model = pybamm.lithium_ion.SPM(options) + model.check_well_posedness() + + if __name__ == "__main__": print("Add -v for more debug output") import sys diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme.py index 072c32d02e..9020d70b53 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme.py @@ -34,20 +34,6 @@ def test_well_posed_2plus1D(self): model = pybamm.lithium_ion.SPMe(options) model.check_well_posedness() - options = { - "current collector": "single particle potential pair", - "dimensionality": 1, - } - model = pybamm.lithium_ion.SPMe(options) - model.check_well_posedness() - - options = { - "current collector": "single particle potential pair", - "dimensionality": 2, - } - model = pybamm.lithium_ion.SPMe(options) - model.check_well_posedness() - options = {"bc_options": {"dimensionality": 5}} with self.assertRaises(pybamm.OptionError): model = pybamm.lithium_ion.SPMe(options) diff --git a/tests/unit/test_models/test_submodels/test_convection/test_composite_convection.py b/tests/unit/test_models/test_submodels/test_convection/test_composite_convection.py index b64f6b6171..126c815f3f 100644 --- a/tests/unit/test_models/test_submodels/test_convection/test_composite_convection.py +++ b/tests/unit/test_models/test_submodels/test_convection/test_composite_convection.py @@ -14,11 +14,11 @@ def test_public_functions(self): a = pybamm.Scalar(0) variables = { "Current collector current density": a, - "Negative electrode interfacial current density": pybamm.Broadcast( + "Negative electrode interfacial current density": pybamm.PrimaryBroadcast( a, ["negative electrode"] ), "X-averaged negative electrode interfacial current density": a, - "Positive electrode interfacial current density": pybamm.Broadcast( + "Positive electrode interfacial current density": pybamm.PrimaryBroadcast( a, ["positive electrode"] ), "X-averaged positive electrode interfacial current density": a, diff --git a/tests/unit/test_models/test_submodels/test_current_collector/test_composite_potential_pair.py b/tests/unit/test_models/test_submodels/test_current_collector/test_composite_potential_pair.py index a73bce5bb8..8648edf21c 100644 --- a/tests/unit/test_models/test_submodels/test_current_collector/test_composite_potential_pair.py +++ b/tests/unit/test_models/test_submodels/test_current_collector/test_composite_potential_pair.py @@ -14,7 +14,8 @@ def test_public_functions(self): variables = { "Positive current collector potential": pybamm.PrimaryBroadcast( 0, "current collector" - ) + ), + "Total current density": 0, } std_tests = tests.StandardSubModelTests(submodel, variables) diff --git a/tests/unit/test_models/test_submodels/test_current_collector/test_effective_current_collector.py b/tests/unit/test_models/test_submodels/test_current_collector/test_effective_current_collector.py index 3e3098dbf6..28ea508205 100644 --- a/tests/unit/test_models/test_submodels/test_current_collector/test_effective_current_collector.py +++ b/tests/unit/test_models/test_submodels/test_current_collector/test_effective_current_collector.py @@ -52,23 +52,11 @@ def test_get_processed_potentials(self): solutions[1] = models[1].default_solver.solve(models[1], t_eval) # Process SPM V and I - V = pybamm.ProcessedVariable( - models[1].variables["Terminal voltage"], - solutions[1].t, - solutions[1].y, - mesh=meshes[1], - ) - I = pybamm.ProcessedVariable( - models[1].variables["Total current density"], - solutions[1].t, - solutions[1].y, - mesh=meshes[1], - ) + V = solutions[1]["Terminal voltage"] + I = solutions[1]["Total current density"] # Test potential can be constructed and evaluated without raising error - potentials = models[0].get_processed_potentials( - solutions[0], meshes[0], param, V, I - ) + potentials = models[0].get_processed_potentials(solutions[0], param, V, I) for var, processed_var in potentials.items(): processed_var(0.05, 0.5, 0.5) diff --git a/tests/unit/test_models/test_submodels/test_current_collector/test_homogeneous_current_collector.py b/tests/unit/test_models/test_submodels/test_current_collector/test_homogeneous_current_collector.py index 06f13b8c06..7fa012b450 100644 --- a/tests/unit/test_models/test_submodels/test_current_collector/test_homogeneous_current_collector.py +++ b/tests/unit/test_models/test_submodels/test_current_collector/test_homogeneous_current_collector.py @@ -15,7 +15,8 @@ def test_public_functions(self): variables = { "Positive current collector potential": pybamm.PrimaryBroadcast( 0, "current collector" - ) + ), + "Total current density": 0, } std_tests = tests.StandardSubModelTests(submodel, variables) std_tests.test_all() diff --git a/tests/unit/test_models/test_submodels/test_current_collector/test_potential_pair.py b/tests/unit/test_models/test_submodels/test_current_collector/test_potential_pair.py index f1f8d0dffc..e28275ca08 100644 --- a/tests/unit/test_models/test_submodels/test_current_collector/test_potential_pair.py +++ b/tests/unit/test_models/test_submodels/test_current_collector/test_potential_pair.py @@ -13,7 +13,8 @@ def test_public_functions(self): variables = { "Positive current collector potential": pybamm.PrimaryBroadcast( 0, "current collector" - ) + ), + "Total current density": 0, } submodel = pybamm.current_collector.PotentialPair1plus1D(param) std_tests = tests.StandardSubModelTests(submodel, variables) diff --git a/tests/unit/test_models/test_submodels/test_current_collector/test_set_potential_spm_1plus1d.py b/tests/unit/test_models/test_submodels/test_current_collector/test_set_potential_spm_1plus1d.py index f58ed75f1b..5a29752e76 100644 --- a/tests/unit/test_models/test_submodels/test_current_collector/test_set_potential_spm_1plus1d.py +++ b/tests/unit/test_models/test_submodels/test_current_collector/test_set_potential_spm_1plus1d.py @@ -8,7 +8,7 @@ import pybamm.models.submodels.current_collector as cc -class TestSetPotetetialSPM1plus1DModel(unittest.TestCase): +class TestSetPotentialSPM1plus1DModel(unittest.TestCase): def test_public_functions(self): param = pybamm.standard_parameters_lithium_ion submodel = cc.SetPotentialSingleParticle1plus1D(param) @@ -20,7 +20,9 @@ def test_public_functions(self): "X-averaged negative electrode reaction overpotential": val, "X-averaged electrolyte overpotential": val, "X-averaged positive electrode ohmic losses": val, - "X-averaged negative electrode ohmic losses": val + "X-averaged negative electrode ohmic losses": val, + "Total current density": 0, + "Local voltage": val, } std_tests = tests.StandardSubModelTests(submodel, variables) @@ -39,7 +41,9 @@ def test_public_functions(self): "X-averaged negative electrode reaction overpotential": val, "X-averaged electrolyte overpotential": val, "X-averaged positive electrode ohmic losses": val, - "X-averaged negative electrode ohmic losses": val + "X-averaged negative electrode ohmic losses": val, + "Total current density": 0, + "Local voltage": val, } std_tests = tests.StandardSubModelTests(submodel, variables) diff --git a/tests/unit/test_models/test_submodels/test_electrode/test_ohm/test_surface_form_ohm.py b/tests/unit/test_models/test_submodels/test_electrode/test_ohm/test_surface_form_ohm.py index 4c82cd3acf..e899441e29 100644 --- a/tests/unit/test_models/test_submodels/test_electrode/test_ohm/test_surface_form_ohm.py +++ b/tests/unit/test_models/test_submodels/test_electrode/test_ohm/test_surface_form_ohm.py @@ -14,10 +14,10 @@ def test_public_functions(self): a = pybamm.Scalar(0) variables = { "Current collector current density": a, - "Negative electrolyte current density": pybamm.Broadcast( + "Negative electrolyte current density": pybamm.PrimaryBroadcast( a, ["negative electrode"] ), - "Positive electrolyte current density": pybamm.Broadcast( + "Positive electrolyte current density": pybamm.PrimaryBroadcast( a, ["positive electrode"] ), "Negative electrode porosity": a, diff --git a/tests/unit/test_models/test_submodels/test_electrolyte/test_stefan_maxwell/test_conductivity/test_surface_form/test_leading_surface_form_stefan_maxwell_conductivity.py b/tests/unit/test_models/test_submodels/test_electrolyte/test_stefan_maxwell/test_conductivity/test_surface_form/test_leading_surface_form_stefan_maxwell_conductivity.py index 86c2e5a33e..aaada829c8 100644 --- a/tests/unit/test_models/test_submodels/test_electrolyte/test_stefan_maxwell/test_conductivity/test_surface_form/test_leading_surface_form_stefan_maxwell_conductivity.py +++ b/tests/unit/test_models/test_submodels/test_electrolyte/test_stefan_maxwell/test_conductivity/test_surface_form/test_leading_surface_form_stefan_maxwell_conductivity.py @@ -11,9 +11,9 @@ class TestLeadingOrderModel(unittest.TestCase): def test_public_functions(self): param = pybamm.standard_parameters_lithium_ion a = pybamm.Scalar(0) - a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"]) - a_s = pybamm.Broadcast(pybamm.Scalar(0), ["separator"]) - a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"]) + a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) + a_s = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["separator"]) + a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) variables = { "Current collector current density": a, "Negative electrode porosity": a_n, diff --git a/tests/unit/test_models/test_submodels/test_external_circuit/__init__.py b/tests/unit/test_models/test_submodels/test_external_circuit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit/test_models/test_submodels/test_external_circuit/test_current_control.py b/tests/unit/test_models/test_submodels/test_external_circuit/test_current_control.py new file mode 100644 index 0000000000..c2dc1c19de --- /dev/null +++ b/tests/unit/test_models/test_submodels/test_external_circuit/test_current_control.py @@ -0,0 +1,25 @@ +# +# Test current control submodel +# + +import pybamm +import tests +import unittest + + +class TestCurrentControl(unittest.TestCase): + def test_public_functions(self): + param = pybamm.standard_parameters_lithium_ion + submodel = pybamm.external_circuit.CurrentControl(param) + std_tests = tests.StandardSubModelTests(submodel) + std_tests.test_all() + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py b/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py new file mode 100644 index 0000000000..ec961b4316 --- /dev/null +++ b/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py @@ -0,0 +1,38 @@ +# +# Test function control submodel +# +import pybamm +import tests +import unittest + + +class ExternalCircuitFunction: + num_switches = 0 + + def __call__(self, variables): + I = variables["Current [A]"] + V = variables["Terminal voltage [V]"] + return ( + V + I - pybamm.FunctionParameter("Current plus voltage function", pybamm.t) + ) + + +class TestFunctionControl(unittest.TestCase): + def test_public_functions(self): + param = pybamm.standard_parameters_lithium_ion + submodel = pybamm.external_circuit.FunctionControl( + param, ExternalCircuitFunction() + ) + variables = {"Terminal voltage [V]": pybamm.Scalar(0)} + std_tests = tests.StandardSubModelTests(submodel, variables) + std_tests.test_all() + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_models/test_submodels/test_external_circuit/test_power_control.py b/tests/unit/test_models/test_submodels/test_external_circuit/test_power_control.py new file mode 100644 index 0000000000..414970a815 --- /dev/null +++ b/tests/unit/test_models/test_submodels/test_external_circuit/test_power_control.py @@ -0,0 +1,26 @@ +# +# Test power control submodel +# + +import pybamm +import tests +import unittest + + +class TestPowerControl(unittest.TestCase): + def test_public_functions(self): + param = pybamm.standard_parameters_lithium_ion + submodel = pybamm.external_circuit.PowerFunctionControl(param) + variables = {"Terminal voltage [V]": pybamm.Scalar(0)} + std_tests = tests.StandardSubModelTests(submodel, variables) + std_tests.test_all() + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_models/test_submodels/test_interface/test_lead_acid.py b/tests/unit/test_models/test_submodels/test_interface/test_lead_acid.py index da98c47ac0..674e4729a6 100644 --- a/tests/unit/test_models/test_submodels/test_interface/test_lead_acid.py +++ b/tests/unit/test_models/test_submodels/test_interface/test_lead_acid.py @@ -12,8 +12,8 @@ def test_public_functions(self): param = pybamm.standard_parameters_lead_acid a = pybamm.Scalar(0) - a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"]) - a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"]) + a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) + a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) variables = { "Current collector current density": a, "Negative electrode potential": a_n, diff --git a/tests/unit/test_models/test_submodels/test_interface/test_lithium_ion.py b/tests/unit/test_models/test_submodels/test_interface/test_lithium_ion.py index 705a5e1c95..2f5352e2b6 100644 --- a/tests/unit/test_models/test_submodels/test_interface/test_lithium_ion.py +++ b/tests/unit/test_models/test_submodels/test_interface/test_lithium_ion.py @@ -11,8 +11,8 @@ class TestLithiumIon(unittest.TestCase): def test_public_functions(self): param = pybamm.standard_parameters_lithium_ion - a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"]) - a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"]) + a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) + a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) a = pybamm.Scalar(0) variables = { "Current collector current density": a, diff --git a/tests/unit/test_models/test_submodels/test_porosity/test_full_reaction_driven_porosity.py b/tests/unit/test_models/test_submodels/test_porosity/test_full_reaction_driven_porosity.py index 5c33a07196..bf19212d75 100644 --- a/tests/unit/test_models/test_submodels/test_porosity/test_full_reaction_driven_porosity.py +++ b/tests/unit/test_models/test_submodels/test_porosity/test_full_reaction_driven_porosity.py @@ -10,8 +10,8 @@ class TestFull(unittest.TestCase): def test_public_functions(self): param = pybamm.standard_parameters_lead_acid - a_n = pybamm.Broadcast(pybamm.Scalar(0), ["negative electrode"]) - a_p = pybamm.Broadcast(pybamm.Scalar(0), ["positive electrode"]) + a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"]) + a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"]) variables = { "Negative electrode interfacial current density": a_n, "Positive electrode interfacial current density": a_p, diff --git a/tests/unit/test_models/test_submodels/test_porosity/test_leading_reaction_driven_porosity.py b/tests/unit/test_models/test_submodels/test_porosity/test_leading_reaction_driven_porosity.py index ce90a031c6..3cf918091e 100644 --- a/tests/unit/test_models/test_submodels/test_porosity/test_leading_reaction_driven_porosity.py +++ b/tests/unit/test_models/test_submodels/test_porosity/test_leading_reaction_driven_porosity.py @@ -10,7 +10,7 @@ class TestLeadingOrder(unittest.TestCase): def test_public_functions(self): param = pybamm.standard_parameters_lead_acid - a = pybamm.Broadcast(pybamm.Scalar(0), "current collector") + a = pybamm.PrimaryBroadcast(pybamm.Scalar(0), "current collector") variables = { "X-averaged negative electrode interfacial current density": a, "X-averaged positive electrode interfacial current density": a, diff --git a/tests/unit/test_parameters/test_current_functions.py b/tests/unit/test_parameters/test_current_functions.py index a87871916b..f70459c5ca 100644 --- a/tests/unit/test_parameters/test_current_functions.py +++ b/tests/unit/test_parameters/test_current_functions.py @@ -15,7 +15,7 @@ def test_constant_current(self): { "Typical current [A]": 2, "Typical timescale [s]": 1, - "Current function": "[constant]", + "Current function [A]": 2, } ) processed_current = parameter_values.process_symbol(current) @@ -28,7 +28,7 @@ def test_get_current_data(self): { "Typical current [A]": 2, "Typical timescale [s]": 1, - "Current function": "[current data]car_current", + "Current function [A]": "[current data]car_current", } ) dimensional_current_eval = parameter_values.process_symbol(dimensional_current) @@ -57,7 +57,7 @@ def current(t): "Typical current [A]": 2, "Typical timescale [s]": 1, "omega": 3, - "Current function": current, + "Current function [A]": current, } ) dimensional_current = pybamm.electrical_parameters.dimensional_current_with_time diff --git a/tests/unit/test_parameters/test_electrical_parameters.py b/tests/unit/test_parameters/test_electrical_parameters.py index 894d08a431..c1aaf9259f 100644 --- a/tests/unit/test_parameters/test_electrical_parameters.py +++ b/tests/unit/test_parameters/test_electrical_parameters.py @@ -24,7 +24,7 @@ def test_current_functions(self): "Number of electrodes connected in parallel to make a cell": 8, "Typical current [A]": 2, "Typical timescale [s]": 60, - "Current function": "[constant]", + "Current function [A]": 2, } ) dimensional_current_eval = parameter_values.process_symbol(dimensional_current) diff --git a/tests/unit/test_parameters/test_parameter_values.py b/tests/unit/test_parameters/test_parameter_values.py index 05cd23b7da..893e5a6d90 100644 --- a/tests/unit/test_parameters/test_parameter_values.py +++ b/tests/unit/test_parameters/test_parameter_values.py @@ -20,6 +20,10 @@ def test_init(self): # from dict param = pybamm.ParameterValues({"a": 1}) self.assertEqual(param["a"], 1) + self.assertEqual(list(param.keys())[0], "a") + self.assertEqual(list(param.values())[0], 1) + self.assertEqual(list(param.items())[0], ("a", 1)) + # from file param = pybamm.ParameterValues( values="input/parameters/lithium-ion/cathodes/lico2_Marquis2019/" @@ -52,41 +56,64 @@ def test_update(self): self.assertEqual(param["a"], 2) # with conflict param.update({"a": 3}) - self.assertEqual(param["a"], 3) + # via __setitem__ + param["a"] = 2 + self.assertEqual(param["a"], 2) with self.assertRaisesRegex( - ValueError, "parameter 'a' already defined with value '3'" + ValueError, "parameter 'a' already defined with value '2'" ): param.update({"a": 4}, check_conflict=True) + # with parameter not existing yet + with self.assertRaisesRegex(KeyError, "Cannot update parameter"): + param.update({"b": 1}) def test_check_and_update_parameter_values(self): # Can't provide a current density of 0, as this will cause a ZeroDivision error bad_values = {"Typical current [A]": 0} with self.assertRaisesRegex(ValueError, "Typical current"): pybamm.ParameterValues(bad_values) - # same with C-rate - bad_values = {"C-rate": 0} - with self.assertRaisesRegex(ValueError, "C-rate"): - pybamm.ParameterValues(bad_values) - # if both C-rate and current are provided they must match with capacity - bad_values = {"C-rate": 1, "Typical current [A]": 5, "Cell capacity [A.h]": 10} - with self.assertRaisesRegex(ValueError, "do not match"): + # can't provide both C-rate and current function + bad_values = {"C-rate": 1, "Current function [A]": 5} + with self.assertRaisesRegex(ValueError, "Cannot provide both"): pybamm.ParameterValues(bad_values) # if only C-rate and capacity provided, update current values = {"C-rate": 1, "Cell capacity [A.h]": 10} param = pybamm.ParameterValues(values) - self.assertEqual(param["Typical current [A]"], 10) + self.assertEqual(param["Current function [A]"], 10) # if only current and capacity provided, update C-rate - values = {"Typical current [A]": 1, "Cell capacity [A.h]": 10} + values = {"Current function [A]": 1, "Cell capacity [A.h]": 10} param = pybamm.ParameterValues(values) self.assertEqual(param["C-rate"], 1 / 10) - # Test with current function - values = {"Typical current [A]": 1, "Current function": "[constant]"} + # With functions + # if only C-rate and capacity provided, update current + values = {"C-rate": pybamm.sin, "Cell capacity [A.h]": 10} + param = pybamm.ParameterValues(values) + self.assertEqual(param["Current function [A]"](2).evaluate(), 10 * np.sin(2)) + # if only current and capacity provided, update C-rate + values = {"Current function [A]": pybamm.exp, "Cell capacity [A.h]": 10} + param = pybamm.ParameterValues(values) + self.assertEqual(param["C-rate"](5).evaluate(), np.exp(5) / 10) + + # With data + # if only C-rate and capacity provided, update current + x = np.linspace(0, 10)[:, np.newaxis] + linear = np.hstack([x, 2 * x]) + values = {"C-rate": ("linear", linear), "Cell capacity [A.h]": 10} param = pybamm.ParameterValues(values) - self.assertEqual(param["Current function"], 1) - values = {"Typical current [A]": 1, "Current function": "[zero]"} + self.assertEqual(param["Current function [A]"][0], "linear_to_Crate") + np.testing.assert_array_equal( + param["Current function [A]"][1], np.hstack([x, 20 * x]) + ) + # if only current and capacity provided, update C-rate + x = np.linspace(0, 10)[:, np.newaxis] + linear = np.hstack([x, 2 * x]) + values = {"Current function [A]": ("linear", linear), "Cell capacity [A.h]": 10} param = pybamm.ParameterValues(values) - self.assertEqual(param["Current function"], 0) + self.assertEqual(param["C-rate"][0], "linear_to_current") + np.testing.assert_array_almost_equal( + param["C-rate"][1], np.hstack([x, 0.2 * x]) + ) def test_process_symbol(self): parameter_values = pybamm.ParameterValues({"a": 1, "b": 2, "c": 3}) @@ -156,7 +183,7 @@ def test_process_symbol(self): # process broadcast whole_cell = ["negative electrode", "separator", "positive electrode"] - broad = pybamm.Broadcast(a, whole_cell) + broad = pybamm.PrimaryBroadcast(a, whole_cell) processed_broad = parameter_values.process_symbol(broad) self.assertIsInstance(processed_broad, pybamm.Broadcast) self.assertEqual(processed_broad.domain, whole_cell) @@ -217,17 +244,33 @@ def test_process_symbol(self): processed_g.evaluate(y=np.ones(10)), np.ones((10, 1)) ) - # process outer - c = pybamm.Parameter("c", domain="current collector") - outer = pybamm.Outer(c, b) - processed_outer = parameter_values.process_symbol(outer) - self.assertIsInstance(processed_outer, pybamm.Outer) - # not implemented sym = pybamm.Symbol("sym") with self.assertRaises(NotImplementedError): parameter_values.process_symbol(sym) + # not found + with self.assertRaises(KeyError): + x = pybamm.Parameter("x") + parameter_values.process_symbol(x) + + def test_process_input_parameter(self): + parameter_values = pybamm.ParameterValues({"a": "[input]", "b": 3}) + # process input parameter + a = pybamm.Parameter("a") + processed_a = parameter_values.process_symbol(a) + self.assertIsInstance(processed_a, pybamm.InputParameter) + self.assertEqual(processed_a.evaluate(u={"a": 5}), 5) + + # process binary operation + b = pybamm.Parameter("b") + add = a + b + processed_add = parameter_values.process_symbol(add) + self.assertIsInstance(processed_add, pybamm.Addition) + self.assertIsInstance(processed_add.children[0], pybamm.InputParameter) + self.assertIsInstance(processed_add.children[1], pybamm.Scalar) + self.assertEqual(processed_add.evaluate(u={"a": 4}), 7) + def test_process_function_parameter(self): parameter_values = pybamm.ParameterValues( { @@ -246,7 +289,9 @@ def test_process_function_parameter(self): # process constant function const = pybamm.FunctionParameter("const", a) processed_const = parameter_values.process_symbol(const) - self.assertIsInstance(processed_const, pybamm.Scalar) + self.assertIsInstance(processed_const, pybamm.Multiplication) + self.assertIsInstance(processed_const.left, pybamm.Scalar) + self.assertIsInstance(processed_const.right, pybamm.Scalar) self.assertEqual(processed_const.evaluate(), 254) # process differentiated function parameter @@ -331,6 +376,7 @@ def test_interpolant_against_function(self): "cathodes", "lico2_Marquis2019", ), + check_already_exists=False, ) a = pybamm.Parameter("a") @@ -446,6 +492,14 @@ def test_process_model(self): isinstance(model.variables["d_var1"].children[1], pybamm.Variable) ) + # bad boundary conditions + model = pybamm.BaseModel() + model.algebraic = {var1: var1} + x = pybamm.Parameter("x") + model.boundary_conditions = {var1: {"left": (x, "Dirichlet")}} + with self.assertRaises(KeyError): + parameter_values.process_model(model) + def test_process_empty_model(self): model = pybamm.BaseModel() parameter_values = pybamm.ParameterValues({"a": 1, "b": 2, "c": 3, "d": 42}) diff --git a/tests/unit/test_parameters/test_standard_parameters_lead_acid.py b/tests/unit/test_parameters/test_standard_parameters_lead_acid.py index 0521776d59..3aa676eb04 100644 --- a/tests/unit/test_parameters/test_standard_parameters_lead_acid.py +++ b/tests/unit/test_parameters/test_standard_parameters_lead_acid.py @@ -17,7 +17,7 @@ def test_scipy_constants(self): def test_all_defined(self): parameters = pybamm.standard_parameters_lead_acid parameter_values = pybamm.lead_acid.BaseModel().default_parameter_values - output_file = "results/2019_08_sulzer_thesis/parameters.txt" + output_file = "lead_acid_parameters.txt" pybamm.print_parameters(parameters, parameter_values, output_file) # test print_parameters with dict and without C-rate del parameter_values["Cell capacity [A.h]"] @@ -89,7 +89,7 @@ def test_current_functions(self): "Typical electrolyte concentration [mol.m-3]": 1, "Number of electrodes connected in parallel to make a cell": 8, "Typical current [A]": 2, - "Current function": "[constant]", + "Current function [A]": 2, } ) dimensional_current_density_eval = parameter_values.process_symbol( diff --git a/tests/unit/test_parameters/test_update_parameters.py b/tests/unit/test_parameters/test_update_parameters.py index e26c153dad..5193e44306 100644 --- a/tests/unit/test_parameters/test_update_parameters.py +++ b/tests/unit/test_parameters/test_update_parameters.py @@ -52,9 +52,9 @@ def test_update_model(self): parameter_values_update = pybamm.ParameterValues( chemistry=pybamm.parameter_sets.Marquis2019 ) - parameter_values_update.update({"Typical current [A]": 2}) + parameter_values_update.update({"Current function [A]": 1}) modeltest2.test_update_parameters(parameter_values_update) - self.assertEqual(model2.variables["Current [A]"].evaluate(), 2) + self.assertEqual(model2.variables["Current [A]"].evaluate(), 1) modeltest2.test_solving(t_eval=t_eval) Y2 = modeltest2.solution.y @@ -68,12 +68,14 @@ def test_update_model(self): parameter_values_update = pybamm.ParameterValues( chemistry=pybamm.parameter_sets.Marquis2019 ) - parameter_values_update.update({"Current function": "[zero]"}) + parameter_values_update.update({"Current function [A]": 0}) modeltest3.test_update_parameters(parameter_values_update) modeltest3.test_solving(t_eval=t_eval) Y3 = modeltest3.solution.y - self.assertIsInstance(model3.variables["Current [A]"], pybamm.Scalar) + self.assertIsInstance(model3.variables["Current [A]"], pybamm.Multiplication) + self.assertIsInstance(model3.variables["Current [A]"].left, pybamm.Scalar) + self.assertIsInstance(model3.variables["Current [A]"].right, pybamm.Scalar) self.assertEqual(model3.variables["Current [A]"].evaluate(), 0.0) # results should be different @@ -94,10 +96,14 @@ def test_update_geometry(self): # test on simple lead-acid model model1 = pybamm.lead_acid.LOQS() modeltest1 = tests.StandardModelTest(model1) + parameter_values = pybamm.ParameterValues( + chemistry=pybamm.parameter_sets.Sulzer2019 + ) + parameter_values.update({"C-rate": 0.05}) t_eval = np.linspace(0, 0.5) - modeltest1.test_all(t_eval=t_eval, skip_output_tests=True) - - T1, Y1 = modeltest1.solution.t, modeltest1.solution.y + modeltest1.test_all( + param=parameter_values, t_eval=t_eval, skip_output_tests=True + ) # trying to update the geometry fails parameter_values_update = pybamm.ParameterValues( @@ -105,6 +111,7 @@ def test_update_geometry(self): ) parameter_values_update.update( { + "C-rate": 0.05, "Negative electrode thickness [m]": 0.0002, "Separator thickness [m]": 0.0003, "Positive electrode thickness [m]": 0.0004, @@ -120,22 +127,13 @@ def test_update_geometry(self): modeltest2.test_all( param=parameter_values_update, t_eval=t_eval, skip_output_tests=True ) - T2, Y2 = modeltest2.solution.t, modeltest2.solution.y # results should be different - c1 = pybamm.ProcessedVariable( - modeltest1.model.variables["Electrolyte concentration"], - T1, - Y1, - mesh=modeltest1.disc.mesh, - ).entries - c2 = pybamm.ProcessedVariable( - modeltest2.model.variables["Electrolyte concentration"], - T2, - Y2, - mesh=modeltest2.disc.mesh, - ).entries + c1 = modeltest1.solution["Electrolyte concentration"].entries + c2 = modeltest2.solution["Electrolyte concentration"].entries self.assertNotEqual(np.linalg.norm(c1 - c2), 0) - self.assertNotEqual(np.linalg.norm(Y1 - Y2), 0) + self.assertNotEqual( + np.linalg.norm(modeltest1.solution.y - modeltest2.solution.y), 0 + ) if __name__ == "__main__": diff --git a/tests/unit/test_processed_variable.py b/tests/unit/test_processed_variable.py index 3dac40405a..79a31c9c9f 100644 --- a/tests/unit/test_processed_variable.py +++ b/tests/unit/test_processed_variable.py @@ -14,9 +14,10 @@ def test_processed_variable_1D(self): t = pybamm.t y = pybamm.StateVector(slice(0, 1)) var = t * y + var.mesh = None t_sol = np.linspace(0, 1) y_sol = np.array([np.linspace(0, 5)]) - processed_var = pybamm.ProcessedVariable(var, t_sol, y_sol) + processed_var = pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal(processed_var.entries, t_sol * y_sol[0]) def test_processed_variable_2D(self): @@ -31,13 +32,14 @@ def test_processed_variable_2D(self): x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) eqn_sol = disc.process_symbol(eqn) + eqn_sol.mesh = disc.mesh.combine_submeshes(*eqn.domain) t_sol = np.linspace(0, 1) y_sol = np.ones_like(x_sol)[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) - np.testing.assert_array_equal(processed_var.entries[1:-1], y_sol) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) + np.testing.assert_array_equal(processed_var.entries, y_sol) np.testing.assert_array_equal(processed_var(t_sol, x_sol), y_sol) - processed_eqn = pybamm.ProcessedVariable(eqn_sol, t_sol, y_sol, mesh=disc.mesh) + processed_eqn = pybamm.ProcessedVariable(eqn_sol, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal( processed_eqn(t_sol, x_sol), t_sol * y_sol + x_sol[:, np.newaxis] ) @@ -50,9 +52,12 @@ def test_processed_variable_2D(self): # On edges x_s_edge = pybamm.Matrix(disc.mesh["separator"][0].edges, domain="separator") - processed_x_s_edge = pybamm.ProcessedVariable(x_s_edge, t_sol, y_sol, disc.mesh) + x_s_edge.mesh = disc.mesh["separator"] + processed_x_s_edge = pybamm.ProcessedVariable( + x_s_edge, pybamm.Solution(t_sol, y_sol) + ) np.testing.assert_array_equal( - x_s_edge.entries[:, 0], processed_x_s_edge.entries[1:-1, 0] + x_s_edge.entries[:, 0], processed_x_s_edge.entries[:, 0] ) def test_processed_variable_2D_unknown_domain(self): @@ -78,7 +83,8 @@ def test_processed_variable_2D_unknown_domain(self): ) c = pybamm.StateVector(slice(0, var_pts[x]), domain=["SEI layer"]) - pybamm.ProcessedVariable(c, solution.t, solution.y, mesh) + c.mesh = mesh["SEI layer"] + pybamm.ProcessedVariable(c, solution) def test_processed_variable_3D_x_r(self): var = pybamm.Variable( @@ -99,10 +105,10 @@ def test_processed_variable_3D_x_r(self): t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal( processed_var.entries, - np.reshape(y_sol, [len(x_sol), len(r_sol), len(t_sol)]), + np.reshape(y_sol, [len(r_sol), len(x_sol), len(t_sol)]), ) def test_processed_variable_3D_x_z(self): @@ -124,7 +130,7 @@ def test_processed_variable_3D_x_z(self): t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(z_sol))[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal( processed_var.entries, np.reshape(y_sol, [len(x_sol), len(z_sol), len(t_sol)]), @@ -132,14 +138,17 @@ def test_processed_variable_3D_x_z(self): # On edges x_s_edge = pybamm.Matrix( - np.repeat(disc.mesh["separator"][0].edges, len(z_sol)), + np.tile(disc.mesh["separator"][0].edges, len(z_sol)), domain="separator", auxiliary_domains={"secondary": "current collector"}, ) - processed_x_s_edge = pybamm.ProcessedVariable(x_s_edge, t_sol, y_sol, disc.mesh) + x_s_edge.mesh = disc.mesh["separator"] + x_s_edge.secondary_mesh = disc.mesh["current collector"] + processed_x_s_edge = pybamm.ProcessedVariable( + x_s_edge, pybamm.Solution(t_sol, y_sol) + ) np.testing.assert_array_equal( - x_s_edge.entries[:, 0], - processed_x_s_edge.entries[:, :, 0].reshape(-1, 1)[:, 0], + x_s_edge.entries.flatten(), processed_x_s_edge.entries[:, :, 0].T.flatten() ) def test_processed_variable_3D_scikit(self): @@ -150,10 +159,11 @@ def test_processed_variable_3D_scikit(self): y = disc.mesh["current collector"][0].edges["y"] z = disc.mesh["current collector"][0].edges["z"] var_sol = disc.process_symbol(var) + var_sol.mesh = disc.mesh["current collector"] t_sol = np.linspace(0, 1) u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, u_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, u_sol)) np.testing.assert_array_equal( processed_var.entries, np.reshape(u_sol, [len(y), len(z), len(t_sol)]) ) @@ -166,10 +176,11 @@ def test_processed_variable_2Dspace_scikit(self): y = disc.mesh["current collector"][0].edges["y"] z = disc.mesh["current collector"][0].edges["z"] var_sol = disc.process_symbol(var) + var_sol.mesh = disc.mesh["current collector"] t_sol = np.array([0]) u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, u_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, u_sol)) np.testing.assert_array_equal( processed_var.entries, np.reshape(u_sol, [len(y), len(z)]) ) @@ -180,17 +191,19 @@ def test_processed_var_1D_interpolation(self): y = pybamm.StateVector(slice(0, 1)) var = y eqn = t * y + var.mesh = None + eqn.mesh = None t_sol = np.linspace(0, 1, 1000) y_sol = np.array([np.linspace(0, 5, 1000)]) - processed_var = pybamm.ProcessedVariable(var, t_sol, y_sol) + processed_var = pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol)) # vector np.testing.assert_array_equal(processed_var(t_sol), y_sol[0]) # scalar np.testing.assert_array_equal(processed_var(0.5), 2.5) np.testing.assert_array_equal(processed_var(0.7), 3.5) - processed_eqn = pybamm.ProcessedVariable(eqn, t_sol, y_sol) + processed_eqn = pybamm.ProcessedVariable(eqn, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal(processed_eqn(t_sol), t_sol * y_sol[0]) np.testing.assert_array_almost_equal(processed_eqn(0.5), 0.5 * 2.5) @@ -210,10 +223,11 @@ def test_processed_var_2D_interpolation(self): x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) eqn_sol = disc.process_symbol(eqn) + eqn_sol.mesh = disc.mesh.combine_submeshes(*eqn.domain) t_sol = np.linspace(0, 1) y_sol = x_sol[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) # 2 vectors np.testing.assert_array_almost_equal(processed_var(t_sol, x_sol), y_sol) # 1 vector, 1 scalar @@ -227,7 +241,7 @@ def test_processed_var_2D_interpolation(self): np.testing.assert_array_almost_equal( processed_var(0.5, x_sol[-1]), 2.5 * x_sol[-1] ) - processed_eqn = pybamm.ProcessedVariable(eqn_sol, t_sol, y_sol, mesh=disc.mesh) + processed_eqn = pybamm.ProcessedVariable(eqn_sol, pybamm.Solution(t_sol, y_sol)) # 2 vectors np.testing.assert_array_almost_equal( processed_eqn(t_sol, x_sol), t_sol * y_sol + x_sol[:, np.newaxis] @@ -242,8 +256,9 @@ def test_processed_var_2D_interpolation(self): r_n = pybamm.Matrix( disc.mesh["negative particle"][0].nodes, domain="negative particle" ) - processed_r_n = pybamm.ProcessedVariable(r_n, t_sol, y_sol, disc.mesh) - np.testing.assert_array_equal(r_n.entries[:, 0], processed_r_n.entries[1:-1, 0]) + r_n.mesh = disc.mesh["negative particle"] + processed_r_n = pybamm.ProcessedVariable(r_n, pybamm.Solution(t_sol, y_sol)) + np.testing.assert_array_equal(r_n.entries[:, 0], processed_r_n.entries[:, 0]) np.testing.assert_array_almost_equal( processed_r_n(0, r=np.linspace(0, 1))[:, 0], np.linspace(0, 1) ) @@ -267,17 +282,17 @@ def test_processed_var_3D_interpolation(self): t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) # 3 vectors np.testing.assert_array_equal( - processed_var(t_sol, x_sol, r_sol).shape, (40, 10, 50) + processed_var(t_sol, x_sol, r_sol).shape, (10, 40, 50) ) np.testing.assert_array_equal( processed_var(t_sol, x_sol, r_sol), - np.reshape(y_sol, [len(x_sol), len(r_sol), len(t_sol)]), + np.reshape(y_sol, [len(r_sol), len(x_sol), len(t_sol)]), ) # 2 vectors, 1 scalar - np.testing.assert_array_equal(processed_var(0.5, x_sol, r_sol).shape, (40, 10)) + np.testing.assert_array_equal(processed_var(0.5, x_sol, r_sol).shape, (10, 40)) np.testing.assert_array_equal(processed_var(t_sol, 0.2, r_sol).shape, (10, 50)) np.testing.assert_array_equal(processed_var(t_sol, x_sol, 0.5).shape, (40, 50)) # 1 vectors, 2 scalar @@ -305,19 +320,15 @@ def test_processed_var_3D_interpolation(self): t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) # 3 vectors np.testing.assert_array_equal( - processed_var(t_sol, x_sol, r_sol).shape, (35, 10, 50) + processed_var(t_sol, x_sol, r_sol).shape, (10, 35, 50) ) - def test_processed_var_3D_r_first_dimension(self): - var = pybamm.Variable( - "var", - domain=["negative particle"], - auxiliary_domains={"secondary": ["negative electrode"]}, - ) - broad_var = pybamm.PrimaryBroadcast(var, "negative electrode") + def test_processed_var_3D_secondary_broadcast(self): + var = pybamm.Variable("var", domain=["negative particle"]) + broad_var = pybamm.SecondaryBroadcast(var, "negative electrode") x = pybamm.SpatialVariable("x", domain=["negative electrode"]) r = pybamm.SpatialVariable("r", domain=["negative particle"]) @@ -329,17 +340,17 @@ def test_processed_var_3D_r_first_dimension(self): t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) # 3 vectors np.testing.assert_array_equal( - processed_var(t_sol, x_sol, r_sol).shape, (40, 10, 50) + processed_var(t_sol, x_sol, r_sol).shape, (10, 40, 50) ) np.testing.assert_array_equal( processed_var(t_sol, x_sol, r_sol), - np.reshape(y_sol, [len(x_sol), len(r_sol), len(t_sol)]), + np.reshape(y_sol, [len(r_sol), len(x_sol), len(t_sol)]), ) # 2 vectors, 1 scalar - np.testing.assert_array_equal(processed_var(0.5, x_sol, r_sol).shape, (40, 10)) + np.testing.assert_array_equal(processed_var(0.5, x_sol, r_sol).shape, (10, 40)) np.testing.assert_array_equal(processed_var(t_sol, 0.2, r_sol).shape, (10, 50)) np.testing.assert_array_equal(processed_var(t_sol, x_sol, 0.5).shape, (40, 50)) # 1 vectors, 2 scalar @@ -351,7 +362,7 @@ def test_processed_var_3D_r_first_dimension(self): # positive particle var = pybamm.Variable("var", domain=["positive particle"]) - broad_var = pybamm.PrimaryBroadcast(var, "positive electrode") + broad_var = pybamm.SecondaryBroadcast(var, "positive electrode") x = pybamm.SpatialVariable("x", domain=["positive electrode"]) r = pybamm.SpatialVariable("r", domain=["positive particle"]) @@ -362,10 +373,10 @@ def test_processed_var_3D_r_first_dimension(self): t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) # 3 vectors np.testing.assert_array_equal( - processed_var(t_sol, x_sol, r_sol).shape, (35, 10, 50) + processed_var(t_sol, x_sol, r_sol).shape, (10, 35, 50) ) def test_processed_var_3D_scikit_interpolation(self): @@ -376,10 +387,11 @@ def test_processed_var_3D_scikit_interpolation(self): y_sol = disc.mesh["current collector"][0].edges["y"] z_sol = disc.mesh["current collector"][0].edges["z"] var_sol = disc.process_symbol(var) + var_sol.mesh = disc.mesh["current collector"] t_sol = np.linspace(0, 1) u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, u_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, u_sol)) # 3 vectors np.testing.assert_array_equal( processed_var(t_sol, y=y_sol, z=z_sol).shape, (15, 15, 50) @@ -413,10 +425,11 @@ def test_processed_var_2Dspace_scikit_interpolation(self): y_sol = disc.mesh["current collector"][0].edges["y"] z_sol = disc.mesh["current collector"][0].edges["z"] var_sol = disc.process_symbol(var) + var_sol.mesh = disc.mesh["current collector"] t_sol = np.array([0]) u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, u_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, u_sol)) # 2 vectors np.testing.assert_array_equal( processed_var(t=None, y=y_sol, z=z_sol).shape, (15, 15) @@ -440,9 +453,8 @@ def test_processed_variable_ode_pde_solution(self): model.variables = {"c": c} modeltest = tests.StandardModelTest(model) modeltest.test_all() - t_sol, y_sol = modeltest.solution.t, modeltest.solution.y - processed_vars = pybamm.post_process_variables(model.variables, t_sol, y_sol) - np.testing.assert_array_almost_equal(processed_vars["c"](t_sol), np.exp(-t_sol)) + sol = modeltest.solution + np.testing.assert_array_almost_equal(sol["c"](sol.t), np.exp(-sol.t)) # with space # set up and solve model @@ -469,17 +481,13 @@ def test_processed_variable_ode_pde_solution(self): modeltest = tests.StandardModelTest(model) modeltest.test_all() # set up testing - t_sol, y_sol = modeltest.solution.t, modeltest.solution.y + sol = modeltest.solution x = pybamm.SpatialVariable("x", domain=whole_cell) x_sol = modeltest.disc.process_symbol(x).entries[:, 0] - processed_vars = pybamm.post_process_variables( - model.variables, t_sol, y_sol, modeltest.disc.mesh - ) # test np.testing.assert_array_almost_equal( - processed_vars["c"](t_sol, x_sol), - np.ones_like(x_sol)[:, np.newaxis] * np.exp(-t_sol), + sol["c"](sol.t, x_sol), np.ones_like(x_sol)[:, np.newaxis] * np.exp(-sol.t) ) def test_call_failure(self): @@ -493,7 +501,7 @@ def test_call_failure(self): t_sol = np.linspace(0, 1) y_sol = x_sol[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) with self.assertRaisesRegex(ValueError, "x cannot be None"): processed_var(0) @@ -506,12 +514,24 @@ def test_call_failure(self): var_sol = disc.process_symbol(var) y_sol = r_sol[:, np.newaxis] * np.linspace(0, 5) - processed_var = pybamm.ProcessedVariable(var_sol, t_sol, y_sol, mesh=disc.mesh) + processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) with self.assertRaisesRegex(ValueError, "r cannot be None"): processed_var(0) with self.assertRaisesRegex(ValueError, "r cannot be None"): processed_var(0, 1) + def test_solution_too_short(self): + t = pybamm.t + y = pybamm.StateVector(slice(0, 1)) + var = t * y + var.mesh = None + t_sol = np.array([1]) + y_sol = np.array([np.linspace(0, 5)]) + with self.assertRaisesRegex( + pybamm.SolverError, "Solution time vector must have length > 1" + ): + pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol)) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_quick_plot.py b/tests/unit/test_quick_plot.py index 601f348927..df41b16a50 100644 --- a/tests/unit/test_quick_plot.py +++ b/tests/unit/test_quick_plot.py @@ -47,7 +47,7 @@ def test_simple_ode_model(self): solver = model.default_solver t_eval = np.linspace(0, 2, 100) solution = solver.solve(model, t_eval) - quick_plot = pybamm.QuickPlot(model, mesh, solution) + quick_plot = pybamm.QuickPlot(solution) quick_plot.plot(0) # update the axis @@ -65,15 +65,12 @@ def test_simple_ode_model(self): quick_plot.update(0.01) # Test with different output variables - quick_plot = pybamm.QuickPlot(model, mesh, solution, ["b broadcasted"]) + quick_plot = pybamm.QuickPlot(solution, ["b broadcasted"]) self.assertEqual(len(quick_plot.axis), 1) quick_plot.plot(0) quick_plot = pybamm.QuickPlot( - model, - mesh, - solution, - [["a", "a"], ["b broadcasted", "b broadcasted"], "c broadcasted"], + solution, [["a", "a"], ["b broadcasted", "b broadcasted"], "c broadcasted"] ) self.assertEqual(len(quick_plot.axis), 3) quick_plot.plot(0) @@ -95,19 +92,19 @@ def test_simple_ode_model(self): # Test longer name model.variables["Variable with a very long name"] = model.variables["a"] - quick_plot = pybamm.QuickPlot(model, mesh, solution) + quick_plot = pybamm.QuickPlot(solution) quick_plot.plot(0) # Test errors with self.assertRaisesRegex(ValueError, "mismatching variable domains"): - pybamm.QuickPlot(model, mesh, solution, [["a", "b broadcasted"]]) + pybamm.QuickPlot(solution, [["a", "b broadcasted"]]) model.variables["3D variable"] = disc.process_symbol( pybamm.FullBroadcast( 1, "negative particle", {"secondary": "negative electrode"} ) ) with self.assertRaisesRegex(NotImplementedError, "cannot plot 3D variables"): - pybamm.QuickPlot(model, mesh, solution, ["3D variable"]) + pybamm.QuickPlot(solution, ["3D variable"]) def test_loqs_spm_base(self): t_eval = np.linspace(0, 0.01, 2) @@ -125,7 +122,7 @@ def test_loqs_spm_base(self): disc.process_model(model) solver = model.default_solver solution = solver.solve(model, t_eval) - pybamm.QuickPlot(model, mesh, solution) + pybamm.QuickPlot(solution) # test quick plot of particle for spm if model.name == "Single Particle Model": @@ -133,29 +130,11 @@ def test_loqs_spm_base(self): "X-averaged negative particle concentration [mol.m-3]", "X-averaged positive particle concentration [mol.m-3]", ] - pybamm.QuickPlot(model, mesh, solution, output_variables) + pybamm.QuickPlot(solution, output_variables) def test_failure(self): - with self.assertRaisesRegex(TypeError, "'models' must be"): - pybamm.QuickPlot(1, None, None) - with self.assertRaisesRegex(TypeError, "'meshes' must be"): - model = pybamm.lithium_ion.SPM() - pybamm.QuickPlot(model, 1, None) with self.assertRaisesRegex(TypeError, "'solutions' must be"): - geometry = model.default_geometry - param = model.default_parameter_values - param.process_model(model) - param.process_geometry(geometry) - mesh = pybamm.Mesh( - geometry, model.default_submesh_types, model.default_var_pts - ) - pybamm.QuickPlot(model, mesh, 1) - with self.assertRaisesRegex(ValueError, "must provide the same"): - pybamm.QuickPlot( - model, - mesh, - [pybamm.Solution(0, 0, 0, 0, ""), pybamm.Solution(0, 0, 0, 0, "")], - ) + pybamm.QuickPlot(1) if __name__ == "__main__": diff --git a/tests/unit/test_simulation.py b/tests/unit/test_simulation.py index e245942ef3..fb0c7c141b 100644 --- a/tests/unit/test_simulation.py +++ b/tests/unit/test_simulation.py @@ -31,7 +31,9 @@ def test_basic_ops(self): self.assertFalse(sim._disc is None) for val in list(sim.built_model.rhs.values()): self.assertFalse(val.has_symbol_of_classes(pybamm.Parameter)) - self.assertTrue(val.has_symbol_of_classes(pybamm.Matrix)) + # skip test for scalar variables (e.g. discharge capacity) + if val.size > 1: + self.assertTrue(val.has_symbol_of_classes(pybamm.Matrix)) sim.reset() sim.set_parameters() @@ -60,7 +62,9 @@ def test_solve(self): self.assertFalse(sim._solution is None) for val in list(sim.built_model.rhs.values()): self.assertFalse(val.has_symbol_of_classes(pybamm.Parameter)) - self.assertTrue(val.has_symbol_of_classes(pybamm.Matrix)) + # skip test for scalar variables (e.g. discharge capacity) + if val.size > 1: + self.assertTrue(val.has_symbol_of_classes(pybamm.Matrix)) sim.reset() self.assertEqual(sim.model_with_set_params, None) @@ -76,7 +80,9 @@ def test_solve(self): sim.solve(check_model=False) for val in list(sim.built_model.rhs.values()): self.assertFalse(val.has_symbol_of_classes(pybamm.Parameter)) - self.assertTrue(val.has_symbol_of_classes(pybamm.Matrix)) + # skip test for scalar variables (e.g. discharge capacity) + if val.size > 1: + self.assertTrue(val.has_symbol_of_classes(pybamm.Matrix)) def test_reuse_commands(self): @@ -185,10 +191,7 @@ def test_get_variable_array(self): self.assertIsInstance(c_e, np.ndarray) def test_set_external_variable(self): - model_options = { - "thermal": "x-lumped", - "external submodels": ["thermal"], - } + model_options = {"thermal": "x-lumped", "external submodels": ["thermal"]} model = pybamm.lithium_ion.SPMe(model_options) sim = pybamm.Simulation(model) @@ -220,6 +223,31 @@ def test_step(self): self.assertEqual(sim.solution.t[0], 2 * dt) self.assertEqual(sim.solution.t[1], 3 * dt) + def test_step_with_inputs(self): + def current_function(t): + return pybamm.InputParameter("Current") + + dt = 0.001 + model = pybamm.lithium_ion.SPM() + param = model.default_parameter_values + param.update({"Current function [A]": current_function}) + sim = pybamm.Simulation(model, parameter_values=param) + sim.step(dt, inputs={"Current": 1}) # 1 step stores first two points + self.assertEqual(sim.solution.t.size, 2) + self.assertEqual(sim.solution.y[0, :].size, 2) + self.assertEqual(sim.solution.t[0], 0) + self.assertEqual(sim.solution.t[1], dt) + np.testing.assert_array_equal(sim.solution.inputs["Current"], 1) + sim.step(dt, inputs={"Current": 2}) # automatically append the next step + self.assertEqual(sim.solution.t.size, 3) + self.assertEqual(sim.solution.y[0, :].size, 3) + self.assertEqual(sim.solution.t[0], 0) + self.assertEqual(sim.solution.t[1], dt) + self.assertEqual(sim.solution.t[2], 2 * dt) + np.testing.assert_array_equal( + sim.solution.inputs["Current"], np.array([1, 1, 2]) + ) + def test_save_load(self): model = pybamm.lead_acid.LOQS() model.use_jacobian = True @@ -266,23 +294,13 @@ def test_save_load_dae(self): sim.save("test.pickle") # with Casadi solver + model.convert_to_format = "casadi" sim = pybamm.Simulation(model, solver=pybamm.CasadiSolver()) sim.solve() sim.save("test.pickle") sim_load = pybamm.load_sim("test.pickle") self.assertEqual(sim.model.name, sim_load.model.name) - @unittest.skipIf(not pybamm.have_idaklu(), "idaklu solver is not installed") - def test_save_load_klu(self): - model = pybamm.lead_acid.LOQS({"surface form": "algebraic"}) - model.use_jacobian = True - # with KLU solver - sim = pybamm.Simulation(model, solver=pybamm.IDAKLUSolver()) - sim.solve() - sim.save("test.pickle") - sim_load = pybamm.load_sim("test.pickle") - self.assertEqual(sim.model.name, sim_load.model.name) - def test_set_defaults2(self): model = pybamm.lithium_ion.SPM() @@ -302,7 +320,10 @@ def test_set_defaults2(self): sim.set_defaults() # Not sure of best way to test nested dicts? # self.geometry = model.default_geometry - self.assertEqual(sim._parameter_values, model.default_parameter_values) + self.assertEqual( + sim._parameter_values._dict_items, + model.default_parameter_values._dict_items, + ) for domain, submesh in model.default_submesh_types.items(): self.assertEqual( sim._submesh_types[domain].submesh_type, submesh.submesh_type diff --git a/tests/unit/test_solvers/test_base_solver.py b/tests/unit/test_solvers/test_base_solver.py index 01fb85bcc9..5883781346 100644 --- a/tests/unit/test_solvers/test_base_solver.py +++ b/tests/unit/test_solvers/test_base_solver.py @@ -3,6 +3,7 @@ # import pybamm import numpy as np +from scipy.sparse import csr_matrix import unittest @@ -18,29 +19,108 @@ def test_base_solver_init(self): solver.rtol = 1e-7 self.assertEqual(solver.rtol, 1e-7) - with self.assertRaises(NotImplementedError): - solver.compute_solution(None, None) - with self.assertRaises(NotImplementedError): - solver.set_up(None) - def test_step_or_solve_empty_model(self): model = pybamm.BaseModel() solver = pybamm.BaseSolver() with self.assertRaisesRegex(pybamm.ModelError, "Cannot step empty model"): - solver.step(model, None) + solver.step(None, model, None) with self.assertRaisesRegex(pybamm.ModelError, "Cannot solve empty model"): solver.solve(model, None) - def test_set_external_variables(self): - options = {"thermal": "x-full", "external submodels": ["thermal"]} - model = pybamm.lithium_ion.SPM(options) - sim = pybamm.Simulation(model) - sim.build() + def test_nonmonotonic_teval(self): + solver = pybamm.BaseSolver(rtol=1e-2, atol=1e-4) + model = pybamm.BaseModel() + a = pybamm.Scalar(0) + model.rhs = {a: a} + with self.assertRaisesRegex( + pybamm.SolverError, "t_eval must increase monotonically" + ): + solver.solve(model, np.array([1, 2, 3, 2])) + + def test_ode_solver_fail_with_dae(self): + model = pybamm.BaseModel() + a = pybamm.Scalar(1) + model.algebraic = {a: a} + solver = pybamm.ScipySolver() + with self.assertRaisesRegex(pybamm.SolverError, "Cannot use ODE solver"): + solver.set_up(model) + + def test_find_consistent_initial_conditions(self): + # Simple system: a single algebraic equation + class ScalarModel: + concatenated_initial_conditions = np.array([[2]]) + jac_algebraic_eval = None + + def rhs_eval(self, t, y): + return np.array([]) + + def algebraic_eval(self, t, y): + return y + 2 + solver = pybamm.BaseSolver() + init_cond = solver.calculate_consistent_initial_conditions(ScalarModel()) + np.testing.assert_array_equal(init_cond, -2) + + # More complicated system + vec = np.array([0.0, 1.0, 1.5, 2.0]) + + class VectorModel: + concatenated_initial_conditions = np.zeros_like(vec) + jac_algebraic_eval = None + + def rhs_eval(self, t, y): + return y[0:1] - T = np.ones((60, 1)) - external_variables = {"Cell temperature": T} - solver.set_external_variables(sim.built_model, external_variables) + def algebraic_eval(self, t, y): + return (y[1:] - vec[1:]) ** 2 + + model = VectorModel() + init_cond = solver.calculate_consistent_initial_conditions(model) + np.testing.assert_array_almost_equal(init_cond, vec) + + # With jacobian + def jac_dense(t, y): + return 2 * np.hstack([np.zeros((3, 1)), np.diag(y[1:] - vec[1:])]) + + model.jac_algebraic_eval = jac_dense + init_cond = solver.calculate_consistent_initial_conditions(model) + np.testing.assert_array_almost_equal(init_cond, vec) + + # With sparse jacobian + def jac_sparse(t, y): + return 2 * csr_matrix( + np.hstack([np.zeros((3, 1)), np.diag(y[1:] - vec[1:])]) + ) + + model.jac_algebraic_eval = jac_sparse + init_cond = solver.calculate_consistent_initial_conditions(model) + np.testing.assert_array_almost_equal(init_cond, vec) + + def test_fail_consistent_initial_conditions(self): + class Model: + concatenated_initial_conditions = np.array([2]) + jac_algebraic_eval = None + + def rhs_eval(self, t, y): + return np.array([]) + + def algebraic_eval(self, t, y): + # algebraic equation has no root + return y ** 2 + 1 + + solver = pybamm.BaseSolver(root_method="hybr") + + with self.assertRaisesRegex( + pybamm.SolverError, + "Could not find consistent initial conditions: The iteration is not making", + ): + solver.calculate_consistent_initial_conditions(Model()) + solver = pybamm.BaseSolver() + with self.assertRaisesRegex( + pybamm.SolverError, + "Could not find consistent initial conditions: solver terminated", + ): + solver.calculate_consistent_initial_conditions(Model()) if __name__ == "__main__": diff --git a/tests/unit/test_solvers/test_casadi_solver.py b/tests/unit/test_solvers/test_casadi_solver.py index 13d66ff0d2..ffb5d6aa1f 100644 --- a/tests/unit/test_solvers/test_casadi_solver.py +++ b/tests/unit/test_solvers/test_casadi_solver.py @@ -1,124 +1,99 @@ # # Tests for the Casadi Solver class # -import casadi import pybamm import unittest import numpy as np from tests import get_mesh_for_testing, get_discretisation_for_testing -import warnings +from scipy.sparse import eye class TestCasadiSolver(unittest.TestCase): - def test_integrate(self): - # Constant - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") + def test_bad_mode(self): + with self.assertRaisesRegex(ValueError, "invalid mode"): + pybamm.CasadiSolver(mode="bad mode") - t = casadi.MX.sym("t") - y = casadi.MX.sym("y") - constant_growth = casadi.MX(0.5) - rhs = casadi.Function("rhs", [t, y], [constant_growth]) + def test_model_solver(self): + # Create model + model = pybamm.BaseModel() + var = pybamm.Variable("var") + model.rhs = {var: 0.1 * var} + model.initial_conditions = {var: 1} + # No need to set parameters; can use base discretisation (no spatial operators) - y0 = np.array([0]) + # create discretisation + disc = pybamm.Discretisation() + disc.process_model(model) + # Solve + solver = pybamm.CasadiSolver(mode="fast", rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 1, 100) - solution = solver.integrate_casadi(rhs, None, y0, t_eval) + solution = solver.solve(model, t_eval) np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) + np.testing.assert_array_almost_equal( + solution.y[0], np.exp(0.1 * solution.t), decimal=5 + ) - # Exponential decay - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="cvodes") + # Safe mode (enforce events that won't be triggered) + model.events = {"an event": var + 1} + disc.process_model(model) + solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 1, 100) + solution = solver.solve(model, t_eval) + np.testing.assert_array_equal(solution.t, t_eval) + np.testing.assert_array_almost_equal( + solution.y[0], np.exp(0.1 * solution.t), decimal=5 + ) - exponential_decay = -0.1 * y - rhs = casadi.Function("rhs", [t, y], [exponential_decay]) + def test_model_solver_python(self): + # Create model + pybamm.set_logging_level("ERROR") + model = pybamm.BaseModel() + model.convert_to_format = "python" + var = pybamm.Variable("var") + model.rhs = {var: 0.1 * var} + model.initial_conditions = {var: 1} + # No need to set parameters; can use base discretisation (no spatial operators) - y0 = np.array([1]) + # create discretisation + disc = pybamm.Discretisation() + disc.process_model(model) + # Solve + solver = pybamm.CasadiSolver(mode="fast", rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 1, 100) - solution = solver.integrate_casadi(rhs, None, y0, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) - self.assertEqual(solution.termination, "final time") - - def test_integrate_failure(self): - # Turn off warnings to ignore sqrt error - warnings.simplefilter("ignore") - - t = casadi.MX.sym("t") - y = casadi.MX.sym("y") - sqrt_decay = -np.sqrt(y) - - y0 = np.array([1]) - t_eval = np.linspace(0, 3, 100) - solver = pybamm.CasadiSolver() - rhs = casadi.Function("rhs", [t, y], [sqrt_decay]) - # Expect solver to fail when y goes negative - with self.assertRaises(pybamm.SolverError): - solver.integrate_casadi(rhs, None, y0, t_eval) + solution = solver.solve(model, t_eval) + np.testing.assert_array_equal(solution.t, t_eval) + np.testing.assert_array_almost_equal( + solution.y[0], np.exp(0.1 * solution.t), decimal=5 + ) + pybamm.set_logging_level("WARNING") - # Set up as a model and solve + def test_model_solver_failure(self): # Create model model = pybamm.BaseModel() - domain = ["negative electrode", "separator", "positive electrode"] - var = pybamm.Variable("var", domain=domain) - model.rhs = {var: -pybamm.Function(np.sqrt, var)} + var = pybamm.Variable("var") + model.rhs = {var: -pybamm.sqrt(var)} model.initial_conditions = {var: 1} # add events so that safe mode is used (won't be triggered) model.events = {"10": var - 10} # No need to set parameters; can use base discretisation (no spatial operators) # create discretisation - mesh = get_mesh_for_testing() - spatial_methods = {"macroscale": pybamm.FiniteVolume()} - disc = pybamm.Discretisation(mesh, spatial_methods) + disc = pybamm.Discretisation() disc.process_model(model) + + solver = pybamm.CasadiSolver(regularity_check=False) + # Solve with failure at t=2 - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") t_eval = np.linspace(0, 20, 100) with self.assertRaises(pybamm.SolverError): solver.solve(model, t_eval) # Solve with failure at t=0 model.initial_conditions = {var: 0} disc.process_model(model) - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") t_eval = np.linspace(0, 20, 100) with self.assertRaises(pybamm.SolverError): solver.solve(model, t_eval) - # Turn warnings back on - warnings.simplefilter("default") - - def test_bad_mode(self): - with self.assertRaisesRegex(ValueError, "invalid mode"): - pybamm.CasadiSolver(mode="bad mode") - - def test_model_solver(self): - # Create model - model = pybamm.BaseModel() - domain = ["negative electrode", "separator", "positive electrode"] - var = pybamm.Variable("var", domain=domain) - model.rhs = {var: 0.1 * var} - model.initial_conditions = {var: 1} - # No need to set parameters; can use base discretisation (no spatial operators) - - # create discretisation - mesh = get_mesh_for_testing() - spatial_methods = {"macroscale": pybamm.FiniteVolume()} - disc = pybamm.Discretisation(mesh, spatial_methods) - disc.process_model(model) - # Solve - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") - t_eval = np.linspace(0, 1, 100) - solution = solver.solve(model, t_eval) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) - - # Safe mode (enforce events that won't be triggered) - model.events = {"an event": var + 1} - disc.process_model(model) - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") - t_eval = np.linspace(0, 1, 100) - solution = solver.solve(model, t_eval) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) - def test_model_solver_events(self): # Create model model = pybamm.BaseModel() @@ -163,27 +138,175 @@ def test_model_step(self): disc = pybamm.Discretisation(mesh, spatial_methods) disc.process_model(model) - solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8, method="idas") + solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) # Step once dt = 0.1 - step_sol = solver.step(model, dt) + step_sol = solver.step(None, model, dt) np.testing.assert_array_equal(step_sol.t, [0, dt]) np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) # Step again (return 5 points) - step_sol_2 = solver.step(model, dt, npts=5) - np.testing.assert_array_equal(step_sol_2.t, np.linspace(dt, 2 * dt, 5)) + step_sol_2 = solver.step(step_sol, model, dt, npts=5) + np.testing.assert_array_equal( + step_sol_2.t, np.concatenate([np.array([0]), np.linspace(dt, 2 * dt, 5)]) + ) np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t)) - # append solutions - step_sol.append(step_sol_2) - # Check steps give same solution as solve t_eval = step_sol.t solution = solver.solve(model, t_eval) np.testing.assert_allclose(solution.y[0], step_sol.y[0]) + def test_model_step_with_input(self): + # Create model + model = pybamm.BaseModel() + var = pybamm.Variable("var") + a = pybamm.InputParameter("a") + model.rhs = {var: a * var} + model.initial_conditions = {var: 1} + model.variables = {"a": a} + # No need to set parameters; can use base discretisation (no spatial operators) + + # create discretisation + disc = pybamm.Discretisation() + disc.process_model(model) + + solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) + + # Step with an input + dt = 0.1 + step_sol = solver.step(None, model, dt, npts=5, inputs={"a": 0.1}) + np.testing.assert_array_equal(step_sol.t, np.linspace(0, dt, 5)) + np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) + + # Step again with different inputs + step_sol_2 = solver.step(step_sol, model, dt, npts=5, inputs={"a": -1}) + np.testing.assert_array_equal(step_sol_2.t, np.linspace(0, 2 * dt, 9)) + np.testing.assert_array_equal( + step_sol_2["a"].entries, + np.array([0.1, 0.1, 0.1, 0.1, 0.1, -1, -1, -1, -1]), + ) + np.testing.assert_allclose( + step_sol_2.y[0], + np.concatenate( + [ + np.exp(0.1 * step_sol.t[:5]), + np.exp(0.1 * step_sol.t[4]) * np.exp(-(step_sol.t[5:] - dt)), + ] + ), + ) + + def test_model_step_events(self): + # Create model + model = pybamm.BaseModel() + var1 = pybamm.Variable("var1") + var2 = pybamm.Variable("var2") + model.rhs = {var1: 0.1 * var1} + model.algebraic = {var2: 2 * var1 - var2} + model.initial_conditions = {var1: 1, var2: 2} + model.events = { + "var1 = 1.5": pybamm.min(var1 - 1.5), + "var2 = 2.5": pybamm.min(var2 - 2.5), + } + disc = pybamm.Discretisation() + disc.process_model(model) + + # Solve + step_solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) + dt = 0.05 + time = 0 + end_time = 5 + step_solution = None + while time < end_time: + step_solution = step_solver.step(step_solution, model, dt=dt, npts=10) + time += dt + np.testing.assert_array_less(step_solution.y[0], 1.5) + np.testing.assert_array_less(step_solution.y[-1], 2.5001) + np.testing.assert_array_almost_equal( + step_solution.y[0], np.exp(0.1 * step_solution.t), decimal=5 + ) + np.testing.assert_array_almost_equal( + step_solution.y[-1], 2 * np.exp(0.1 * step_solution.t), decimal=4 + ) + + def test_model_solver_with_inputs(self): + # Create model + model = pybamm.BaseModel() + domain = ["negative electrode", "separator", "positive electrode"] + var = pybamm.Variable("var", domain=domain) + model.rhs = {var: -pybamm.InputParameter("rate") * var} + model.initial_conditions = {var: 1} + model.events = {"var=0.5": pybamm.min(var - 0.5)} + # No need to set parameters; can use base discretisation (no spatial + # operators) + + # create discretisation + mesh = get_mesh_for_testing() + spatial_methods = {"macroscale": pybamm.FiniteVolume()} + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + # Solve + solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 10, 100) + solution = solver.solve(model, t_eval, inputs={"rate": 0.1}) + self.assertLess(len(solution.t), len(t_eval)) + np.testing.assert_array_equal(solution.t, t_eval[: len(solution.t)]) + np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t), rtol=1e-06) + + def test_model_solver_with_external(self): + # Create model + model = pybamm.BaseModel() + domain = ["negative electrode", "separator", "positive electrode"] + var1 = pybamm.Variable("var1", domain=domain) + var2 = pybamm.Variable("var2", domain=domain) + model.rhs = {var1: -var2} + model.initial_conditions = {var1: 1} + model.external_variables = [var2] + model.variables = {"var1": var1, "var2": var2} + # No need to set parameters; can use base discretisation (no spatial + # operators) + + # create discretisation + mesh = get_mesh_for_testing() + spatial_methods = {"macroscale": pybamm.FiniteVolume()} + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + # Solve + solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 10, 100) + solution = solver.solve(model, t_eval, external_variables={"var2": 0.5}) + np.testing.assert_allclose(solution.y[0], 1 - 0.5 * solution.t, rtol=1e-06) + + def test_model_solver_with_non_identity_mass(self): + model = pybamm.BaseModel() + var1 = pybamm.Variable("var1", domain="negative electrode") + var2 = pybamm.Variable("var2", domain="negative electrode") + model.rhs = {var1: var1} + model.algebraic = {var2: 2 * var1 - var2} + model.initial_conditions = {var1: 1, var2: 2} + disc = get_discretisation_for_testing() + disc.process_model(model) + + # FV discretisation has identity mass. Manually set the mass matrix to + # be a diag of 10s here for testing. Note that the algebraic part is all + # zeros + mass_matrix = 10 * model.mass_matrix.entries + model.mass_matrix = pybamm.Matrix(mass_matrix) + + # Note that mass_matrix_inv is just the inverse of the ode block of the + # mass matrix + mass_matrix_inv = 0.1 * eye(int(mass_matrix.shape[0] / 2)) + model.mass_matrix_inv = pybamm.Matrix(mass_matrix_inv) + + # Solve + solver = pybamm.CasadiSolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 1, 100) + solution = solver.solve(model, t_eval) + np.testing.assert_array_equal(solution.t, t_eval) + np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) + np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t)) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_solvers/test_dae_solver.py b/tests/unit/test_solvers/test_dae_solver.py deleted file mode 100644 index 6e35964415..0000000000 --- a/tests/unit/test_solvers/test_dae_solver.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# Tests for the DAE Solver class -# -import pybamm -import unittest -import numpy as np -from scipy.sparse import csr_matrix - - -class TestDaeSolver(unittest.TestCase): - def test_find_consistent_initial_conditions(self): - # Simple system: a single algebraic equation - def rhs(t, y): - return np.array([]) - - def algebraic(t, y): - return y + 2 - - solver = pybamm.DaeSolver() - y0 = np.array([2]) - init_cond = solver.calculate_consistent_initial_conditions(rhs, algebraic, y0) - np.testing.assert_array_equal(init_cond, -2) - - # More complicated system - vec = np.array([0.0, 1.0, 1.5, 2.0]) - - def rhs(t, y): - return y[0:1] - - def algebraic(t, y): - return (y[1:] - vec[1:]) ** 2 - - y0 = np.zeros_like(vec) - init_cond = solver.calculate_consistent_initial_conditions(rhs, algebraic, y0) - np.testing.assert_array_almost_equal(init_cond, vec) - - # With jacobian - def jac_dense(t, y): - return 2 * np.hstack([np.zeros((3, 1)), np.diag(y[1:] - vec[1:])]) - - init_cond = solver.calculate_consistent_initial_conditions( - rhs, algebraic, y0, jac_dense - ) - np.testing.assert_array_almost_equal(init_cond, vec) - - # With sparse jacobian - def jac_sparse(t, y): - return 2 * csr_matrix( - np.hstack([np.zeros((3, 1)), np.diag(y[1:] - vec[1:])]) - ) - - init_cond = solver.calculate_consistent_initial_conditions( - rhs, algebraic, y0, jac_sparse - ) - np.testing.assert_array_almost_equal(init_cond, vec) - - def test_fail_consistent_initial_conditions(self): - def rhs(t, y): - return np.array([]) - - def algebraic(t, y): - # algebraic equation has no root - return y ** 2 + 1 - - solver = pybamm.DaeSolver(root_method="hybr") - y0 = np.array([2]) - - with self.assertRaisesRegex( - pybamm.SolverError, - "Could not find consistent initial conditions: The iteration is not making", - ): - solver.calculate_consistent_initial_conditions(rhs, algebraic, y0) - solver = pybamm.DaeSolver() - with self.assertRaisesRegex( - pybamm.SolverError, - "Could not find consistent initial conditions: solver terminated", - ): - solver.calculate_consistent_initial_conditions(rhs, algebraic, y0) - - -if __name__ == "__main__": - print("Add -v for more debug output") - import sys - - if "-v" in sys.argv: - debug = True - pybamm.settings.debug_mode = True - unittest.main() diff --git a/tests/unit/test_solvers/test_idaklu_solver.py b/tests/unit/test_solvers/test_idaklu_solver.py index a873e7480b..f6f25c0453 100644 --- a/tests/unit/test_solvers/test_idaklu_solver.py +++ b/tests/unit/test_solvers/test_idaklu_solver.py @@ -3,7 +3,6 @@ # import pybamm import numpy as np -import scipy.sparse as sparse import unittest @@ -13,70 +12,39 @@ def test_ida_roberts_klu(self): # this test implements a python version of the ida Roberts # example provided in sundials # see sundials ida examples pdf + for form in ["python", "casadi"]: + model = pybamm.BaseModel() + model.convert_to_format = form + u = pybamm.Variable("u") + v = pybamm.Variable("v") + model.rhs = {u: 0.1 * v} + model.algebraic = {v: 1 - v} + model.initial_conditions = {u: 0, v: 1} + model.events = {"1": u - 0.2, "2": v} - # times and initial conditions - t_eval = np.linspace(0, 3, 100) - y0 = np.array([0.0, 1.0]) - - # Standard pybamm functions - def jac(t, y): - J = np.zeros((2, 2)) - J[0][0] = 0.0 - J[0][1] = 1.0 - J[1][0] = 0.0 - J[1][1] = -1.0 - return sparse.csr_matrix(J) - - def event_1(t, y): - return y[0] - 0.2 - - def event_2(t, y): - return y[1] - 0.0 - - events = np.array([event_1, event_2]) + disc = pybamm.Discretisation() + disc.process_model(model) - def res(t, y, yp): - # must be of form r = f(t, y) - y' - r = np.zeros((y.size,)) - r[0] = 0.1 * y[1] - r[1] = 1 - y[1] - r[0] += -yp[0] - return r - - mass_matrix_dense = np.zeros((2, 2)) - mass_matrix_dense[0][0] = 1 - mass_matrix = sparse.csr_matrix(mass_matrix_dense) - - def rhs(t, y): - return np.array([0.1 * y[1]]) - - def alg(t, y): - return np.array([1 - y[1]]) - - solver = pybamm.IDAKLUSolver() - solver.residuals = res - solver.rhs = rhs - solver.algebraic = alg + solver = pybamm.IDAKLUSolver() - solution = solver.integrate( - res, y0, t_eval, events, mass_matrix, jac, model=None - ) + t_eval = np.linspace(0, 3, 100) + solution = solver.solve(model, t_eval) - # test that final time is time of event - # y = 0.1 t + y0 so y=0.2 when t=2 - np.testing.assert_array_almost_equal(solution.t[-1], 2.0) + # test that final time is time of event + # y = 0.1 t + y0 so y=0.2 when t=2 + np.testing.assert_array_almost_equal(solution.t[-1], 2.0) - # test that final value is the event value - np.testing.assert_array_almost_equal(solution.y[0, -1], 0.2) + # test that final value is the event value + np.testing.assert_array_almost_equal(solution.y[0, -1], 0.2) - # test that y[1] remains constant - np.testing.assert_array_almost_equal( - solution.y[1, :], np.ones(solution.t.shape) - ) + # test that y[1] remains constant + np.testing.assert_array_almost_equal( + solution.y[1, :], np.ones(solution.t.shape) + ) - # test that y[0] = to true solution - true_solution = 0.1 * solution.t - np.testing.assert_array_almost_equal(solution.y[0, :], true_solution) + # test that y[0] = to true solution + true_solution = 0.1 * solution.t + np.testing.assert_array_almost_equal(solution.y[0, :], true_solution) def test_set_atol(self): model = pybamm.lithium_ion.SPMe() @@ -92,6 +60,25 @@ def test_set_atol(self): variable_tols = {"Electrolyte concentration": 1e-3} solver.set_atol_by_variable(variable_tols, model) + def test_failures(self): + # this test implements a python version of the ida Roberts + # example provided in sundials + # see sundials ida examples pdf + model = pybamm.BaseModel() + model.use_jacobian = False + u = pybamm.Variable("u") + model.rhs = {u: -0.1 * u} + model.initial_conditions = {u: 1} + + disc = pybamm.Discretisation() + disc.process_model(model) + + solver = pybamm.IDAKLUSolver() + + t_eval = np.linspace(0, 3, 100) + with self.assertRaisesRegex(pybamm.SolverError, "KLU requires the Jacobian"): + solver.solve(model, t_eval) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_solvers/test_ode_solver.py b/tests/unit/test_solvers/test_ode_solver.py deleted file mode 100644 index 5469e4f726..0000000000 --- a/tests/unit/test_solvers/test_ode_solver.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# Tests for the ODE Solver class -# -import pybamm -import unittest - - -class TestOdeSolver(unittest.TestCase): - def test_exceptions(self): - solver = pybamm.OdeSolver() - with self.assertRaises(NotImplementedError): - solver.integrate(None, None, None) - - def test_wrong_solver(self): - # Create model - model = pybamm.BaseModel() - var = pybamm.Variable("var") - model.rhs = {var: var} - model.algebraic = {var: var - 1} - - # test errors - solver = pybamm.OdeSolver() - with self.assertRaisesRegex( - pybamm.SolverError, "Cannot use ODE solver to solve model with DAEs" - ): - solver.solve(model, None) - with self.assertRaisesRegex( - pybamm.SolverError, "Cannot use ODE solver to solve model with DAEs" - ): - solver.set_up_casadi(model) - - -if __name__ == "__main__": - print("Add -v for more debug output") - import sys - - if "-v" in sys.argv: - debug = True - pybamm.settings.debug_mode = True - unittest.main() diff --git a/tests/unit/test_solvers/test_scikits_solvers.py b/tests/unit/test_solvers/test_scikits_solvers.py index 7ac16b5af3..99e4053850 100644 --- a/tests/unit/test_solvers/test_scikits_solvers.py +++ b/tests/unit/test_solvers/test_scikits_solvers.py @@ -3,7 +3,6 @@ # import pybamm import numpy as np -import scipy.sparse as sparse import unittest import warnings from tests import get_mesh_for_testing, get_discretisation_for_testing @@ -11,385 +10,93 @@ @unittest.skipIf(not pybamm.have_scikits_odes(), "scikits.odes not installed") class TestScikitsSolvers(unittest.TestCase): - def test_ode_integrate(self): - # Constant - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) - - def constant_growth(t, y): - return 0.5 * np.ones_like(y) - - y0 = np.array([0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(constant_growth, y0, t_eval) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - - # Exponential decay - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) - - def exponential_decay(t, y): - return -0.1 * y - - y0 = np.array([1]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(exponential_decay, y0, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) - self.assertEqual(solution.termination, "final time") - - def test_ode_integrate_failure(self): + def test_model_ode_integrate_failure(self): # Turn off warnings to ignore sqrt error warnings.simplefilter("ignore") - def sqrt_decay(t, y): - return -np.sqrt(y) + model = pybamm.BaseModel() + var = pybamm.Variable("var") + model.rhs = {var: -pybamm.sqrt(var)} + model.initial_conditions = {var: 1} + disc = pybamm.Discretisation() + disc.process_model(model) - y0 = np.array([1]) t_eval = np.linspace(0, 3, 100) solver = pybamm.ScikitsOdeSolver() # Expect solver to fail when y goes negative with self.assertRaises(pybamm.SolverError): - solver.integrate(sqrt_decay, y0, t_eval) + solver.solve(model, t_eval) # Turn warnings back on warnings.simplefilter("default") - def test_ode_integrate_with_event(self): - # Constant - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) - - def constant_decay(t, y): - return -2 * np.ones_like(y) - - def y_equal_0(t, y): - return y[0] - - y0 = np.array([1]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(constant_decay, y0, t_eval, events=[y_equal_0]) - np.testing.assert_allclose(1 - 2 * solution.t, solution.y[0]) - self.assertLess(len(solution.t), len(t_eval)) - np.testing.assert_array_less(0, solution.y[0]) - np.testing.assert_array_less(solution.t, 0.5) - np.testing.assert_allclose(solution.t_event, 0.5) - np.testing.assert_allclose(solution.y_event, 0) - self.assertEqual(solution.termination, "event") - - # Expnonential growth - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) - - def exponential_growth(t, y): - return y - - def y_eq_9(t, y): - return y - 9 - - def ysq_eq_7(t, y): - return y ** 2 - 7 - - y0 = np.array([1]) - t_eval = np.linspace(0, 3, 100) - solution = solver.integrate( - exponential_growth, y0, t_eval, events=[ysq_eq_7, y_eq_9] - ) - self.assertLess(len(solution.t), len(t_eval)) - np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) - np.testing.assert_array_less(solution.y, 9) - np.testing.assert_array_less(solution.y ** 2, 7) - np.testing.assert_allclose(solution.t_event, np.log(7) / 2, rtol=1e-4) - np.testing.assert_allclose(solution.y_event ** 2, 7, rtol=1e-4) - self.assertEqual(solution.termination, "event") - - def test_ode_integrate_with_jacobian(self): - # Linear - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) - - def linear_ode(t, y): - return np.array([0.5, 2 - y[0]]) - - J = np.array([[0.0, 0.0], [-1.0, 0.0]]) - sJ = sparse.csr_matrix(J) - - def jacobian(t, y): - return J - - def sparse_jacobian(t, y): - return sJ - - y0 = np.array([0.0, 0.0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(linear_ode, y0, t_eval, jacobian=jacobian) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose( - 2.0 * solution.t - 0.25 * solution.t ** 2, solution.y[1], rtol=1e-4 - ) - - y0 = np.array([0.0, 0.0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(linear_ode, y0, t_eval, jacobian=sparse_jacobian) - - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose( - 2.0 * solution.t - 0.25 * solution.t ** 2, solution.y[1], rtol=1e-4 - ) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8, linsolver="spgmr") - - solution = solver.integrate(linear_ode, y0, t_eval, jacobian=jacobian) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose( - 2.0 * solution.t - 0.25 * solution.t ** 2, solution.y[1], rtol=1e-4 - ) - - solution = solver.integrate(linear_ode, y0, t_eval, jacobian=sparse_jacobian) - - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose( - 2.0 * solution.t - 0.25 * solution.t ** 2, solution.y[1], rtol=1e-4 - ) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - - # Nonlinear exponential grwoth - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8) - - def exponential_growth(t, y): - return np.array([y[0], (1.0 - y[0]) * y[1]]) - - def jacobian(t, y): - return np.array([[1.0, 0.0], [-y[1], 1 - y[0]]]) - - def sparse_jacobian(t, y): - return sparse.csr_matrix(jacobian(t, y)) - - y0 = np.array([1.0, 1.0]) - t_eval = np.linspace(0, 1, 100) - - solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=jacobian) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) - np.testing.assert_allclose( - np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4 - ) - - solution = solver.integrate( - exponential_growth, y0, t_eval, jacobian=sparse_jacobian - ) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) - np.testing.assert_allclose( - np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4 - ) - - solver = pybamm.ScikitsOdeSolver(rtol=1e-8, atol=1e-8, linsolver="spgmr") - - solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=jacobian) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) - np.testing.assert_allclose( - np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4 - ) - - solution = solver.integrate( - exponential_growth, y0, t_eval, jacobian=sparse_jacobian - ) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) - np.testing.assert_allclose( - np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4 - ) - - def test_dae_integrate(self): - # Constant + def test_model_dae_integrate_failure_bad_ics(self): + # Force model to fail by providing bad ics solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - def constant_growth_dae(t, y, ydot): - return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]] + # Create custom model so that custom ics + class Model: + mass_matrix = pybamm.Matrix(np.array([[1.0, 0.0], [0.0, 0.0]])) + y0 = np.array([0.0, 1.0]) + events_eval = [] - y0 = np.array([0, 0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(constant_growth_dae, y0, t_eval) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose(1.0 * solution.t, solution.y[1]) - - # Exponential decay - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) + def residuals_eval(self, t, y, ydot): + return np.array([0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]]) - def exponential_decay_dae(t, y, ydot): - return [-0.1 * y[0] - ydot[0], 2 * y[0] - y[1]] + def jacobian_eval(self, t, y): + return np.array([[0.0, 0.0], [2.0, -1.0]]) - y0 = np.array([1, 2]) + model = Model() t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(exponential_decay_dae, y0, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) - np.testing.assert_allclose(solution.y[1], 2 * np.exp(-0.1 * solution.t)) - self.assertEqual(solution.termination, "final time") - def test_dae_integrate_failure(self): - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - - def constant_growth_dae(t, y, ydot): - return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]] - - y0 = np.array([0, 1]) - t_eval = np.linspace(0, 1, 100) with self.assertRaises(pybamm.SolverError): - solver.integrate(constant_growth_dae, y0, t_eval) + solver._integrate(model, t_eval) def test_dae_integrate_bad_ics(self): + # Make sure that dae solver can fix bad ics automatically # Constant solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - def constant_growth_dae(t, y, ydot): - return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]] - - def constant_growth_dae_rhs(t, y): - return np.array([constant_growth_dae(t, y, [0])[0]]) - - def constant_growth_dae_algebraic(t, y): - return np.array([constant_growth_dae(t, y, [0])[1]]) + model = pybamm.BaseModel() + var = pybamm.Variable("var") + var2 = pybamm.Variable("var2") + model.rhs = {var: 0.5} + model.algebraic = {var2: 2 * var - var2} + model.initial_conditions = {var: 0, var2: 1} + disc = pybamm.Discretisation() + disc.process_model(model) - y0_guess = np.array([0, 1]) t_eval = np.linspace(0, 1, 100) - y0 = solver.calculate_consistent_initial_conditions( - constant_growth_dae_rhs, constant_growth_dae_algebraic, y0_guess - ) + solver.set_up(model) # check y0 - np.testing.assert_array_equal(y0, [0, 0]) + np.testing.assert_array_equal(model.y0, [0, 0]) # check dae solutions - solution = solver.integrate(constant_growth_dae, y0, t_eval) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose(1.0 * solution.t, solution.y[1]) - - # Exponential decay - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - - def exponential_decay_dae(t, y, ydot): - return [-0.1 * y[0] - ydot[0], 2 * y[0] - y[1]] - - y0 = np.array([1, 2]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(exponential_decay_dae, y0, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) - np.testing.assert_allclose(solution.y[1], 2 * np.exp(-0.1 * solution.t)) - - def test_dae_integrate_with_event(self): - # Constant - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - - def constant_growth_dae(t, y, ydot): - return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]] - - def y0_eq_2(t, y): - return y[0] - 2 - - def y1_eq_5(t, y): - return y[1] - 5 - - y0 = np.array([0, 0]) - t_eval = np.linspace(0, 7, 100) - solution = solver.integrate( - constant_growth_dae, y0, t_eval, events=[y0_eq_2, y1_eq_5] - ) - self.assertLess(len(solution.t), len(t_eval)) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose(1.0 * solution.t, solution.y[1]) - np.testing.assert_array_less(solution.y[0], 2) - np.testing.assert_array_less(solution.y[1], 5) - np.testing.assert_allclose(solution.t_event, 4) - np.testing.assert_allclose(solution.y_event[0], 2) - np.testing.assert_allclose(solution.y_event[1], 4) - self.assertEqual(solution.termination, "event") - - # Exponential decay - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - - def exponential_decay_dae(t, y, ydot): - return np.array([-0.1 * y[0] - ydot[0], 2 * y[0] - y[1]]) - - def y0_eq_0pt9(t, y): - return y[0] - 0.9 - - def t_eq_0pt5(t, y): - return t - 0.5 - - y0 = np.array([1, 2]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate( - exponential_decay_dae, y0, t_eval, events=[y0_eq_0pt9, t_eq_0pt5] - ) - - self.assertLess(len(solution.t), len(t_eval)) - np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) - np.testing.assert_allclose(solution.y[1], 2 * np.exp(-0.1 * solution.t)) - np.testing.assert_array_less(0.9, solution.y[0]) - np.testing.assert_array_less(solution.t, 0.5) - np.testing.assert_allclose(solution.t_event, 0.5) - np.testing.assert_allclose(solution.y_event[0], np.exp(-0.1 * 0.5)) - np.testing.assert_allclose(solution.y_event[1], 2 * np.exp(-0.1 * 0.5)) - self.assertEqual(solution.termination, "event") - - def test_dae_integrate_with_jacobian(self): - # Constant - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - - def constant_growth_dae(t, y, ydot): - return np.array([0.5 * np.ones_like(y[0]) - ydot[0], 2.0 * y[0] - y[1]]) - - mass_matrix = np.array([[1.0, 0.0], [0.0, 0.0]]) - - def jacobian(t, y): - return np.array([[0.0, 0.0], [2.0, -1.0]]) - - y0 = np.array([0.0, 0.0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate( - constant_growth_dae, y0, t_eval, mass_matrix=mass_matrix, jacobian=jacobian - ) + solution = solver.solve(model, t_eval) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) np.testing.assert_allclose(1.0 * solution.t, solution.y[1]) - # Nonlinear (tests when Jacobian a function of y) - solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - - def nonlinear_dae(t, y, ydot): - return np.array([0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] ** 2 - y[1]]) - - mass_matrix = np.array([[1.0, 0.0], [0.0, 0.0]]) - - def jacobian(t, y): - return np.array([[0.0, 0.0], [4.0 * y[0], -1.0]]) - - y0 = np.array([0.0, 0.0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate( - nonlinear_dae, y0, t_eval, mass_matrix=mass_matrix, jacobian=jacobian - ) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose(0.5 * solution.t ** 2, solution.y[1]) - def test_dae_integrate_with_non_unity_mass(self): # Constant solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) - def constant_growth_dae(t, y, ydot): - return np.array([0.5 * np.ones_like(y[0]) - 4 * ydot[0], 2.0 * y[0] - y[1]]) + # Create custom model so that custom mass matrix can be used + class Model: + mass_matrix = pybamm.Matrix(np.array([[4.0, 0.0], [0.0, 0.0]])) + y0 = np.array([0.0, 0.0]) + events_eval = [] - mass_matrix = np.array([[4.0, 0.0], [0.0, 0.0]]) + def residuals_eval(self, t, y, ydot): + return np.array( + [0.5 * np.ones_like(y[0]) - 4 * ydot[0], 2.0 * y[0] - y[1]] + ) - def jacobian(t, y): - return np.array([[0.0, 0.0], [2.0, -1.0]]) + def jacobian_eval(self, t, y): + return np.array([[0.0, 0.0], [2.0, -1.0]]) - y0 = np.array([0.0, 0.0]) + model = Model() t_eval = np.linspace(0, 1, 100) - solution = solver.integrate( - constant_growth_dae, y0, t_eval, mass_matrix=mass_matrix, jacobian=jacobian - ) + solution = solver._integrate(model, t_eval) np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(0.125 * solution.t, solution.y[0]) np.testing.assert_allclose(0.25 * solution.t, solution.y[1]) @@ -453,19 +160,6 @@ def test_model_solver_ode_jacobian_python(self): ) N = combined_submesh[0].npts - # construct jacobian in order of model.rhs - J = [] - for var in model.rhs.keys(): - if var.id == var1.id: - J.append([np.eye(N), np.zeros((N, N))]) - else: - J.append([-1.0 * np.eye(N), np.zeros((N, N))]) - - J = np.block(J) - - def jacobian(t, y): - return J - # Solve solver = pybamm.ScikitsOdeSolver(rtol=1e-9, atol=1e-9) t_eval = np.linspace(0, 1, 100) @@ -576,6 +270,7 @@ def jacobian(t, y): ] ) + model.jacobian = jacobian # Solve solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) t_eval = np.linspace(0, 1, 100) @@ -605,7 +300,7 @@ def test_model_step_ode_python(self): model.convert_to_format = "python" whole_cell = ["negative electrode", "separator", "positive electrode"] var = pybamm.Variable("var", domain=whole_cell) - model.rhs = {var: 0.1 * var} + model.rhs = {var: -0.1 * var} model.initial_conditions = {var: 1} disc = get_discretisation_for_testing() disc.process_model(model) @@ -614,20 +309,21 @@ def test_model_step_ode_python(self): # Step once dt = 0.1 - step_sol = solver.step(model, dt) + step_sol = solver.step(None, model, dt) np.testing.assert_array_equal(step_sol.t, [0, dt]) - np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) + np.testing.assert_allclose(step_sol.y[0], np.exp(-0.1 * step_sol.t)) # Step again (return 5 points) - step_sol_2 = solver.step(model, dt, npts=5) - np.testing.assert_array_equal(step_sol_2.t, np.linspace(dt, 2 * dt, 5)) - np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t)) + step_sol_2 = solver.step(step_sol, model, dt, npts=5) + np.testing.assert_array_equal( + step_sol_2.t, np.concatenate([np.array([0]), np.linspace(dt, 2 * dt, 5)]) + ) + np.testing.assert_allclose(step_sol_2.y[0], np.exp(-0.1 * step_sol_2.t)) # Check steps give same solution as solve - t_eval = np.concatenate((step_sol.t, step_sol_2.t[1:])) + t_eval = step_sol.t solution = solver.solve(model, t_eval) - concatenated_steps = np.concatenate((step_sol.y[0], step_sol_2.y[0, 1:])) - np.testing.assert_allclose(solution.y[0], concatenated_steps) + np.testing.assert_allclose(solution.y[0], step_sol.y[0]) def test_model_step_dae_python(self): model = pybamm.BaseModel() @@ -646,20 +342,19 @@ def test_model_step_dae_python(self): # Step once dt = 0.1 - step_sol = solver.step(model, dt) + step_sol = solver.step(None, model, dt) np.testing.assert_array_equal(step_sol.t, [0, dt]) np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) np.testing.assert_allclose(step_sol.y[-1], 2 * np.exp(0.1 * step_sol.t)) # Step again (return 5 points) - step_sol_2 = solver.step(model, dt, npts=5) - np.testing.assert_array_equal(step_sol_2.t, np.linspace(dt, 2 * dt, 5)) + step_sol_2 = solver.step(step_sol, model, dt, npts=5) + np.testing.assert_array_equal( + step_sol_2.t, np.concatenate([np.array([0]), np.linspace(dt, 2 * dt, 5)]) + ) np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t)) np.testing.assert_allclose(step_sol_2.y[-1], 2 * np.exp(0.1 * step_sol_2.t)) - # append solutions - step_sol.append(step_sol_2) - # Check steps give same solution as solve t_eval = step_sol.t solution = solver.solve(model, t_eval) @@ -717,6 +412,57 @@ def test_model_solver_dae_events_casadi(self): np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t)) + def test_model_solver_dae_inputs_events(self): + # Create model + for form in ["python", "casadi"]: + model = pybamm.BaseModel() + model.convert_to_format = form + whole_cell = ["negative electrode", "separator", "positive electrode"] + var1 = pybamm.Variable("var1", domain=whole_cell) + var2 = pybamm.Variable("var2", domain=whole_cell) + model.rhs = {var1: pybamm.InputParameter("rate 1") * var1} + model.algebraic = {var2: pybamm.InputParameter("rate 2") * var1 - var2} + model.initial_conditions = {var1: 1, var2: 2} + model.events = { + "var1 = 1.5": pybamm.min(var1 - 1.5), + "var2 = 2.5": pybamm.min(var2 - 2.5), + } + disc = get_discretisation_for_testing() + disc.process_model(model) + + # Solve + solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 5, 100) + solution = solver.solve(model, t_eval, inputs={"rate 1": 0.1, "rate 2": 2}) + np.testing.assert_array_less(solution.y[0], 1.5) + np.testing.assert_array_less(solution.y[-1], 2.5) + np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) + np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t)) + + def test_model_solver_dae_with_external(self): + # Create model + model = pybamm.BaseModel() + domain = ["negative electrode", "separator", "positive electrode"] + var1 = pybamm.Variable("var1", domain=domain) + var2 = pybamm.Variable("var2", domain=domain) + model.rhs = {var1: -var2} + model.initial_conditions = {var1: 1} + model.external_variables = [var2] + model.variables = {"var1": var1, "var2": var2} + # No need to set parameters; can use base discretisation (no spatial + # operators) + + # create discretisation + mesh = get_mesh_for_testing() + spatial_methods = {"macroscale": pybamm.FiniteVolume()} + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + # Solve + solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 10, 100) + solution = solver.solve(model, t_eval, external_variables={"var2": 0.5}) + np.testing.assert_allclose(solution.y[0], 1 - 0.5 * solution.t, rtol=1e-06) + def test_solve_ode_model_with_dae_solver_casadi(self): model = pybamm.BaseModel() model.convert_to_format = "casadi" @@ -733,6 +479,47 @@ def test_solve_ode_model_with_dae_solver_casadi(self): np.testing.assert_array_equal(solution.t, t_eval) np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t)) + def test_model_step_events(self): + # Create model + model = pybamm.BaseModel() + var1 = pybamm.Variable("var1") + var2 = pybamm.Variable("var2") + model.rhs = {var1: 0.1 * var1} + model.algebraic = {var2: 2 * var1 - var2} + model.initial_conditions = {var1: 1, var2: 2} + model.events = { + "var1 = 1.5": pybamm.min(var1 - 1.5), + "var2 = 2.5": pybamm.min(var2 - 2.5), + } + disc = pybamm.Discretisation() + disc.process_model(model) + + # Solve + step_solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8) + dt = 0.05 + time = 0 + end_time = 5 + step_solution = None + while time < end_time: + step_solution = step_solver.step(step_solution, model, dt=dt, npts=10) + time += dt + np.testing.assert_array_less(step_solution.y[0], 1.5) + np.testing.assert_array_less(step_solution.y[-1], 2.5001) + np.testing.assert_array_almost_equal( + step_solution.y[0], np.exp(0.1 * step_solution.t), decimal=5 + ) + np.testing.assert_array_almost_equal( + step_solution.y[-1], 2 * np.exp(0.1 * step_solution.t), decimal=5 + ) + + def test_ode_solver_fail_with_dae(self): + model = pybamm.BaseModel() + a = pybamm.Scalar(1) + model.algebraic = {a: a} + solver = pybamm.ScikitsOdeSolver() + with self.assertRaisesRegex(pybamm.SolverError, "Cannot use ODE solver"): + solver.set_up(model) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_solvers/test_scipy_solver.py b/tests/unit/test_solvers/test_scipy_solver.py index 68ecd1de05..31fb7489a9 100644 --- a/tests/unit/test_solvers/test_scipy_solver.py +++ b/tests/unit/test_solvers/test_scipy_solver.py @@ -9,129 +9,6 @@ class TestScipySolver(unittest.TestCase): - def test_integrate(self): - # Constant - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="RK45") - - def constant_growth(t, y): - return 0.5 * np.ones_like(y) - - y0 = np.array([0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(constant_growth, y0, t_eval) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - - # Exponential decay - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="BDF") - - def exponential_decay(t, y): - return -0.1 * y - - y0 = np.array([1]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(exponential_decay, y0, t_eval) - np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) - self.assertEqual(solution.termination, "final time") - - def test_integrate_failure(self): - # Turn off warnings to ignore sqrt error - warnings.simplefilter("ignore") - - def sqrt_decay(t, y): - return -np.sqrt(y) - - y0 = np.array([1]) - t_eval = np.linspace(0, 3, 100) - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="RK45") - # Expect solver to fail when y goes negative - with self.assertRaises(pybamm.SolverError): - solver.integrate(sqrt_decay, y0, t_eval) - - # Turn warnings back on - warnings.simplefilter("default") - - def test_integrate_with_event(self): - # Constant - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="RK45") - - def constant_growth(t, y): - return 0.5 * np.ones_like(y) - - def y_eq_2(t, y): - return y - 2 - - y0 = np.array([0]) - t_eval = np.linspace(0, 10, 100) - solution = solver.integrate(constant_growth, y0, t_eval, events=[y_eq_2]) - self.assertLess(len(solution.t), len(t_eval)) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose(solution.t_event, 4) - np.testing.assert_allclose(solution.y_event, 2) - self.assertEqual(solution.termination, "event") - - # Exponential decay - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="BDF") - - def exponential_growth(t, y): - return y - - def y_eq_5(t, y): - return np.max(y - 5) - - def t_eq_6(t, y): - return t - 6 - - y0 = np.array([1, 2]) - t_eval = np.linspace(0, 7, 100) - solution = solver.integrate( - exponential_growth, y0, t_eval, events=[y_eq_5, t_eq_6] - ) - np.testing.assert_allclose(solution.y[0], np.exp(solution.t), rtol=1e-6) - np.testing.assert_allclose(solution.y[1], 2 * np.exp(solution.t), rtol=1e-6) - np.testing.assert_array_less(solution.t, 6) - np.testing.assert_array_less(solution.y, 5) - np.testing.assert_allclose(solution.t_event, np.log(5 / 2), rtol=1e-6) - np.testing.assert_allclose(solution.y_event[0], 5 / 2, rtol=1e-6) - np.testing.assert_allclose(solution.y_event[1], 5, rtol=1e-6) - - def test_ode_integrate_with_jacobian(self): - # Linear - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="BDF") - - def linear_ode(t, y): - return np.array([0.5 * np.ones_like(y[0]), 2.0 - y[0]]) - - def jacobian(t, y): - return np.array([[0.0, 0.0], [-1.0, 0.0]]) - - y0 = np.array([0.0, 0.0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(linear_ode, y0, t_eval, jacobian=jacobian) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(0.5 * solution.t, solution.y[0]) - np.testing.assert_allclose( - 2.0 * solution.t - 0.25 * solution.t ** 2, solution.y[1], rtol=1e-4 - ) - - # Nonlinear exponential grwoth - solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="BDF") - - def exponential_growth(t, y): - return np.array([y[0], (1.0 - y[0]) * y[1]]) - - def jacobian(t, y): - return np.array([[1.0, 0.0], [-y[1], 1 - y[0]]]) - - y0 = np.array([1.0, 1.0]) - t_eval = np.linspace(0, 1, 100) - solution = solver.integrate(exponential_growth, y0, t_eval, jacobian=jacobian) - np.testing.assert_array_equal(solution.t, t_eval) - np.testing.assert_allclose(np.exp(solution.t), solution.y[0], rtol=1e-4) - np.testing.assert_allclose( - np.exp(1 + solution.t - np.exp(solution.t)), solution.y[1], rtol=1e-4 - ) - def test_model_solver_python(self): # Create model model = pybamm.BaseModel() @@ -158,6 +35,30 @@ def test_model_solver_python(self): self.assertEqual( solution.total_time, solution.solve_time + solution.set_up_time ) + self.assertEqual(solution.termination, "final time") + + def test_model_solver_failure(self): + # Turn off warnings to ignore sqrt error + warnings.simplefilter("ignore") + model = pybamm.BaseModel() + model.convert_to_format = "python" + var = pybamm.Variable("var") + model.rhs = {var: -pybamm.sqrt(var)} + model.initial_conditions = {var: 1} + # No need to set parameters; can use base discretisation (no spatial operators) + + # create discretisation + disc = pybamm.Discretisation() + disc.process_model(model) + + t_eval = np.linspace(0, 3, 100) + solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="RK45") + # Expect solver to fail when y goes negative + with self.assertRaises(pybamm.SolverError): + solver.solve(model, t_eval) + + # Turn warnings back on + warnings.simplefilter("default") def test_model_solver_with_event_python(self): # Create model @@ -255,23 +156,73 @@ def test_model_step_python(self): # Step once dt = 0.1 - step_sol = solver.step(model, dt) + step_sol = solver.step(None, model, dt) np.testing.assert_array_equal(step_sol.t, [0, dt]) np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t)) # Step again (return 5 points) - step_sol_2 = solver.step(model, dt, npts=5) - np.testing.assert_array_equal(step_sol_2.t, np.linspace(dt, 2 * dt, 5)) + step_sol_2 = solver.step(step_sol, model, dt, npts=5) + np.testing.assert_array_equal( + step_sol_2.t, np.concatenate([np.array([0]), np.linspace(dt, 2 * dt, 5)]) + ) np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t)) - # append solutions - step_sol.append(step_sol_2) - # Check steps give same solution as solve t_eval = step_sol.t solution = solver.solve(model, t_eval) np.testing.assert_allclose(solution.y[0], step_sol.y[0]) + def test_model_solver_with_inputs(self): + # Create model + model = pybamm.BaseModel() + model.convert_to_format = "python" + domain = ["negative electrode", "separator", "positive electrode"] + var = pybamm.Variable("var", domain=domain) + model.rhs = {var: -pybamm.InputParameter("rate") * var} + model.initial_conditions = {var: 1} + model.events = {"var=0.5": pybamm.min(var - 0.5)} + # No need to set parameters; can use base discretisation (no spatial + # operators) + + # create discretisation + mesh = get_mesh_for_testing() + spatial_methods = {"macroscale": pybamm.FiniteVolume()} + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + # Solve + solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="RK45") + t_eval = np.linspace(0, 10, 100) + solution = solver.solve(model, t_eval, inputs={"rate": 0.1}) + self.assertLess(len(solution.t), len(t_eval)) + np.testing.assert_array_equal(solution.t, t_eval[: len(solution.t)]) + np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) + + def test_model_solver_with_external(self): + # Create model + model = pybamm.BaseModel() + domain = ["negative electrode", "separator", "positive electrode"] + var1 = pybamm.Variable("var1", domain=domain) + var2 = pybamm.Variable("var2", domain=domain) + model.rhs = {var1: -var2} + model.initial_conditions = {var1: 1} + model.external_variables = [var2] + model.variables = {"var2": var2} + # No need to set parameters; can use base discretisation (no spatial + # operators) + + # create discretisation + mesh = get_mesh_for_testing() + spatial_methods = {"macroscale": pybamm.FiniteVolume()} + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + # Solve + solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8) + t_eval = np.linspace(0, 10, 100) + solution = solver.solve( + model, t_eval, external_variables={"var2": 0.5 * np.ones(100)} + ) + np.testing.assert_allclose(solution.y[0], 1 - 0.5 * solution.t, rtol=1e-06) + def test_model_solver_with_event_with_casadi(self): # Create model model = pybamm.BaseModel() @@ -299,6 +250,31 @@ def test_model_solver_with_event_with_casadi(self): np.testing.assert_array_equal(solution.t, t_eval[: len(solution.t)]) np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) + def test_model_solver_with_inputs_with_casadi(self): + # Create model + model = pybamm.BaseModel() + model.convert_to_format = "casadi" + domain = ["negative electrode", "separator", "positive electrode"] + var = pybamm.Variable("var", domain=domain) + model.rhs = {var: -pybamm.InputParameter("rate") * var} + model.initial_conditions = {var: 1} + model.events = {"var=0.5": pybamm.min(var - 0.5)} + # No need to set parameters; can use base discretisation (no spatial + # operators) + + # create discretisation + mesh = get_mesh_for_testing() + spatial_methods = {"macroscale": pybamm.FiniteVolume()} + disc = pybamm.Discretisation(mesh, spatial_methods) + disc.process_model(model) + # Solve + solver = pybamm.ScipySolver(rtol=1e-8, atol=1e-8, method="RK45") + t_eval = np.linspace(0, 10, 100) + solution = solver.solve(model, t_eval, inputs={"rate": 0.1}) + self.assertLess(len(solution.t), len(t_eval)) + np.testing.assert_array_equal(solution.t, t_eval[: len(solution.t)]) + np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t)) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_solvers/test_solution.py b/tests/unit/test_solvers/test_solution.py new file mode 100644 index 0000000000..7aa4db34fe --- /dev/null +++ b/tests/unit/test_solvers/test_solution.py @@ -0,0 +1,130 @@ +# +# Tests for the Solution class +# +import pybamm +import unittest +import numpy as np + + +class TestSolution(unittest.TestCase): + def test_init(self): + t = np.linspace(0, 1) + y = np.tile(t, (20, 1)) + sol = pybamm.Solution(t, y) + np.testing.assert_array_equal(sol.t, t) + np.testing.assert_array_equal(sol.y, y) + self.assertEqual(sol.t_event, None) + self.assertEqual(sol.y_event, None) + self.assertEqual(sol.termination, "final time") + self.assertEqual(sol.inputs, {}) + self.assertEqual(sol.model, None) + + def test_append(self): + # Set up first solution + t1 = np.linspace(0, 1) + y1 = np.tile(t1, (20, 1)) + sol1 = pybamm.Solution(t1, y1) + sol1.solve_time = 1.5 + sol1.inputs = {} + + # Set up second solution + t2 = np.linspace(1, 2) + y2 = np.tile(t2, (20, 1)) + sol2 = pybamm.Solution(t2, y2) + sol2.solve_time = 1 + sol1.append(sol2) + + # Test + self.assertEqual(sol1.solve_time, 2.5) + np.testing.assert_array_equal(sol1.t, np.concatenate([t1, t2[1:]])) + np.testing.assert_array_equal(sol1.y, np.concatenate([y1, y2[:, 1:]], axis=1)) + + def test_total_time(self): + sol = pybamm.Solution([], None) + sol.set_up_time = 0.5 + sol.solve_time = 1.2 + self.assertEqual(sol.total_time, 1.7) + + def test_getitem(self): + model = pybamm.BaseModel() + c = pybamm.Variable("c") + model.rhs = {c: -c} + model.initial_conditions = {c: 1} + model.variables["c"] = c + model.variables["2c"] = 2 * c + + disc = pybamm.Discretisation() + disc.process_model(model) + solution = pybamm.ScipySolver().solve(model, np.linspace(0, 1)) + + # test create a new processed variable + c_sol = solution["c"] + self.assertIsInstance(c_sol, pybamm.ProcessedVariable) + np.testing.assert_array_equal(c_sol.entries, c_sol(solution.t)) + + # test call an already created variable + solution.update("2c") + twoc_sol = solution["2c"] + self.assertIsInstance(twoc_sol, pybamm.ProcessedVariable) + np.testing.assert_array_equal(twoc_sol.entries, twoc_sol(solution.t)) + np.testing.assert_array_equal(twoc_sol.entries, 2 * c_sol.entries) + + def test_save(self): + model = pybamm.BaseModel() + c = pybamm.Variable("c") + model.rhs = {c: -c} + model.initial_conditions = {c: 1} + model.variables["c"] = c + + disc = pybamm.Discretisation() + disc.process_model(model) + solution = pybamm.ScipySolver().solve(model, np.linspace(0, 1)) + + # test save data + with self.assertRaises(ValueError): + solution.save_data("test.pickle") + # set variables first then save + solution.update(["c"]) + solution.save_data("test.pickle") + data_load = pybamm.load("test.pickle") + np.testing.assert_array_equal(solution.data["c"], data_load["c"]) + + # test save + solution.save("test.pickle") + solution_load = pybamm.load("test.pickle") + self.assertEqual(solution.model.name, solution_load.model.name) + np.testing.assert_array_equal(solution["c"].entries, solution_load["c"].entries) + + def test_solution_evals_with_inputs(self): + model = pybamm.lithium_ion.SPM() + geometry = model.default_geometry + param = model.default_parameter_values + param.update({"Electrode height [m]": "[input]"}) + param.process_model(model) + param.process_geometry(geometry) + 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} + spatial_methods = model.default_spatial_methods + solver = model.default_solver + sim = pybamm.Simulation( + model=model, + geometry=geometry, + parameter_values=param, + var_pts=var_pts, + spatial_methods=spatial_methods, + solver=solver, + ) + inputs = {"Electrode height [m]": 0.1} + sim.solve(t_eval=np.linspace(0, 0.01, 10), inputs=inputs) + time = sim.solution["Time [h]"](sim.solution.t) + self.assertEqual(len(time), 10) + + +if __name__ == "__main__": + print("Add -v for more debug output") + import sys + + if "-v" in sys.argv: + debug = True + pybamm.settings.debug_mode = True + unittest.main() diff --git a/tests/unit/test_spatial_methods/test_finite_volume/test_extrapolation.py b/tests/unit/test_spatial_methods/test_finite_volume/test_extrapolation.py index 781c5c083d..e9ec4d0f22 100644 --- a/tests/unit/test_spatial_methods/test_finite_volume/test_extrapolation.py +++ b/tests/unit/test_spatial_methods/test_finite_volume/test_extrapolation.py @@ -515,4 +515,3 @@ def test_extrapolate_2d_models(self): debug = True pybamm.settings.debug_mode = True unittest.main() - diff --git a/tests/unit/test_spatial_methods/test_finite_volume/test_finite_volume.py b/tests/unit/test_spatial_methods/test_finite_volume/test_finite_volume.py index 1864d7420a..96de8693ed 100644 --- a/tests/unit/test_spatial_methods/test_finite_volume/test_finite_volume.py +++ b/tests/unit/test_spatial_methods/test_finite_volume/test_finite_volume.py @@ -23,20 +23,33 @@ def test_node_to_edge_to_node(self): # node to edge c = pybamm.Vector(np.ones(n), domain=["negative electrode"]) - diffusivity_c = fin_vol.node_to_edge(c) - np.testing.assert_array_equal(diffusivity_c.evaluate(), np.ones((n + 1, 1))) + diffusivity_c_ari = fin_vol.node_to_edge(c, method="arithmetic") + np.testing.assert_array_equal(diffusivity_c_ari.evaluate(), np.ones((n + 1, 1))) + diffusivity_c_har = fin_vol.node_to_edge(c, method="harmonic") + np.testing.assert_array_equal(diffusivity_c_har.evaluate(), np.ones((n + 1, 1))) # edge to node d = pybamm.StateVector(slice(0, n + 1), domain=["negative electrode"]) y_test = np.ones(n + 1) - diffusivity_d = fin_vol.edge_to_node(d) + diffusivity_d_ari = fin_vol.edge_to_node(d, method="arithmetic") np.testing.assert_array_equal( - diffusivity_d.evaluate(None, y_test), np.ones((n, 1)) + diffusivity_d_ari.evaluate(None, y_test), np.ones((n, 1)) + ) + diffusivity_d_har = fin_vol.edge_to_node(d, method="harmonic") + np.testing.assert_array_equal( + diffusivity_d_har.evaluate(None, y_test), np.ones((n, 1)) ) # bad shift key with self.assertRaisesRegex(ValueError, "shift key"): - fin_vol.shift(c, "bad shift key") + fin_vol.shift(c, "bad shift key", "arithmetic") + + with self.assertRaisesRegex(ValueError, "shift key"): + fin_vol.shift(c, "bad shift key", "harmonic") + + # bad method + with self.assertRaisesRegex(ValueError, "method"): + fin_vol.shift(c, "shift key", "bad method") def test_concatenation(self): mesh = get_mesh_for_testing() @@ -89,6 +102,7 @@ def test_discretise_diffusivity_times_spatial_operator(self): pybamm.div(2 * pybamm.grad(var)), pybamm.div(2 * pybamm.grad(var)) + 3 * var, -2 * pybamm.div(var * pybamm.grad(var) + 2 * pybamm.grad(var)), + pybamm.laplacian(var), ]: # Check that the equation can be evaluated in each case # Dirichlet diff --git a/tests/unit/test_spatial_methods/test_finite_volume/test_ghost_nodes.py b/tests/unit/test_spatial_methods/test_finite_volume/test_ghost_nodes_and_neumann.py similarity index 90% rename from tests/unit/test_spatial_methods/test_finite_volume/test_ghost_nodes.py rename to tests/unit/test_spatial_methods/test_finite_volume/test_ghost_nodes_and_neumann.py index 4a00656d3b..e4c67b5a39 100644 --- a/tests/unit/test_spatial_methods/test_finite_volume/test_ghost_nodes.py +++ b/tests/unit/test_spatial_methods/test_finite_volume/test_ghost_nodes_and_neumann.py @@ -32,7 +32,7 @@ def test_add_ghost_nodes(self): # Test sp_meth = pybamm.FiniteVolume() sp_meth.build(mesh) - sym_ghost = sp_meth.add_ghost_nodes(var, discretised_symbol, bcs) + sym_ghost, _ = sp_meth.add_ghost_nodes(var, discretised_symbol, bcs) combined_submesh = mesh.combine_submeshes(*whole_cell) y_test = np.linspace(0, 1, combined_submesh[0].npts) np.testing.assert_array_equal( @@ -49,9 +49,13 @@ def test_add_ghost_nodes(self): bcs = {"left": (pybamm.Scalar(0), "x"), "right": (pybamm.Scalar(3), "Neumann")} with self.assertRaisesRegex(ValueError, "boundary condition must be"): sp_meth.add_ghost_nodes(var, discretised_symbol, bcs) + with self.assertRaisesRegex(ValueError, "boundary condition must be"): + sp_meth.add_neumann_values(var, discretised_symbol, bcs, var.domain) bcs = {"left": (pybamm.Scalar(0), "Neumann"), "right": (pybamm.Scalar(3), "x")} with self.assertRaisesRegex(ValueError, "boundary condition must be"): sp_meth.add_ghost_nodes(var, discretised_symbol, bcs) + with self.assertRaisesRegex(ValueError, "boundary condition must be"): + sp_meth.add_neumann_values(var, discretised_symbol, bcs, var.domain) def test_add_ghost_nodes_concatenation(self): # Set up @@ -81,7 +85,9 @@ def test_add_ghost_nodes_concatenation(self): # both sp_meth = pybamm.FiniteVolume() sp_meth.build(mesh) - symbol_plus_ghost_both = sp_meth.add_ghost_nodes(var, discretised_symbol, bcs) + symbol_plus_ghost_both, _ = sp_meth.add_ghost_nodes( + var, discretised_symbol, bcs + ) np.testing.assert_array_equal( symbol_plus_ghost_both.evaluate(None, y_test)[1:-1], discretised_symbol.evaluate(None, y_test), @@ -128,8 +134,8 @@ def test_p2d_add_ghost_nodes(self): } sp_meth = pybamm.FiniteVolume() sp_meth.build(mesh) - c_s_n_plus_ghost = sp_meth.add_ghost_nodes(c_s_n, disc_c_s_n, bcs) - c_s_p_plus_ghost = sp_meth.add_ghost_nodes(c_s_p, disc_c_s_p, bcs) + c_s_n_plus_ghost, _ = sp_meth.add_ghost_nodes(c_s_n, disc_c_s_n, bcs) + c_s_p_plus_ghost, _ = sp_meth.add_ghost_nodes(c_s_p, disc_c_s_p, bcs) mesh_s_n = mesh["negative particle"] mesh_s_p = mesh["positive particle"] diff --git a/tests/unit/test_spatial_methods/test_scikit_finite_element.py b/tests/unit/test_spatial_methods/test_scikit_finite_element.py index cf1a852387..d94bafddfe 100644 --- a/tests/unit/test_spatial_methods/test_scikit_finite_element.py +++ b/tests/unit/test_spatial_methods/test_scikit_finite_element.py @@ -13,8 +13,6 @@ def test_not_implemented(self): spatial_method = pybamm.ScikitFiniteElement() spatial_method.build(mesh) self.assertEqual(spatial_method.mesh, mesh) - with self.assertRaises(NotImplementedError): - spatial_method.gradient(None, None, None) with self.assertRaises(NotImplementedError): spatial_method.divergence(None, None, None) with self.assertRaises(NotImplementedError): @@ -34,7 +32,7 @@ def test_discretise_equations(self): z = pybamm.SpatialVariable("z", ["current collector"]) disc.set_variable_slices([var]) y_test = np.ones(mesh["current collector"][0].npts) - unit_source = pybamm.Broadcast(1, "current collector") + unit_source = pybamm.PrimaryBroadcast(1, "current collector") disc.bcs = { var.id: { "negative tab": (pybamm.Scalar(0), "Neumann"), @@ -54,7 +52,6 @@ def test_discretise_equations(self): pybamm.laplacian(var) - pybamm.source(unit_source, var, boundary=True), pybamm.laplacian(var) - pybamm.source(unit_source ** 2 + 1 / var, var, boundary=True), - pybamm.grad_squared(var), ]: # Check that equation can be evaluated in each case # Dirichlet @@ -125,6 +122,40 @@ def test_discretise_equations(self): with self.assertRaises(pybamm.GeometryError): disc.process_symbol(x) + def test_gradient(self): + mesh = get_unit_2p1D_mesh_for_testing(ypts=32, zpts=32) + spatial_methods = { + "macroscale": pybamm.FiniteVolume(), + "current collector": pybamm.ScikitFiniteElement(), + } + disc = pybamm.Discretisation(mesh, spatial_methods) + + # test gradient of 5*y + 6*z + var = pybamm.Variable("var", domain="current collector") + disc.set_variable_slices([var]) + + y = mesh["current collector"][0].coordinates[0, :] + z = mesh["current collector"][0].coordinates[1, :] + + gradient = pybamm.grad(var) + grad_disc = disc.process_symbol(gradient) + grad_disc_y, grad_disc_z = grad_disc.children + + np.testing.assert_array_almost_equal( + grad_disc_y.evaluate(None, 5 * y + 6 * z), + 5 * np.ones_like(y)[:, np.newaxis], + ) + np.testing.assert_array_almost_equal( + grad_disc_z.evaluate(None, 5 * y + 6 * z), + 6 * np.ones_like(z)[:, np.newaxis], + ) + + # check grad_squared positive + eqn = pybamm.grad_squared(var) + eqn_disc = disc.process_symbol(eqn) + ans = eqn_disc.evaluate(None, 3 * y ** 2) + np.testing.assert_array_less(0, ans) + def test_manufactured_solution(self): mesh = get_unit_2p1D_mesh_for_testing(ypts=32, zpts=32) spatial_methods = { diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py index 465bfcfd29..1d9eb66c2b 100644 --- a/tests/unit/test_util.py +++ b/tests/unit/test_util.py @@ -77,6 +77,12 @@ def test_infinite_nested_dict(self): d[4][5] = "y" self.assertEqual(d[4][5], "y") + def test_fuzzy_dict(self): + d = pybamm.FuzzyDict({"test": 1, "test2": 2}) + self.assertEqual(d["test"], 1) + with self.assertRaisesRegex(KeyError, "'test3' not found. Best matches are "): + d["test3"] + if __name__ == "__main__": print("Add -v for more debug output")