.. _CONTRIBUTING.md: https://github.com/pybamm-team/PyBaMM/blob/develop/CONTRIBUTING.md
Adding a Spatial Method
=======================
As with any contribution to PyBaMM, please follow the workflow in CONTRIBUTING.md_.
In particular, start by creating an issue to discuss what you want to do - this is a good way to avoid wasted coding hours!
The role of spatial methods
---------------------------
All models in PyBaMM are implemented as `expression trees `_.
After it has been created and parameters have been set, the model is passed to the :class:`pybamm.Discretisation` class,
which converts it into a linear algebra form.
For example, the object:
.. code-block:: python
grad(u)
might get converted to a Matrix-Vector multiplication:
.. code-block:: python
Matrix(100,100) @ y[0:100]
(in Python 3.5+, @ means matrix multiplication, while * is elementwise product).
The :class:`pybamm.Discretisation` class is a wrapper that iterates through the different parts of the model,
performing the trivial conversions (e.g. Addition --> Addition),
and calls upon spatial methods to perform the harder conversions (e.g. grad(u) --> Matrix * StateVector, SpatialVariable --> Vector, etc).
Hence SpatialMethod classes only need to worry about the specific conversions, and :class:`pybamm.Discretisation` deals with the rest.
Implementing a new spatial method
---------------------------------
To add a new spatial method (e.g. My Fast Method), first create a new file (``my_fast_method.py``) in ``pybamm/spatial_methods/``,
with a single class that inherits from :class:`pybamm.SpatialMethod`, such as:
.. code-block:: python
class MyFastMethod(pybamm.SpatialMethod):
and add the class to ``pybamm/__init__.py``:
.. code-block:: python
from .spatial_methods.my_fast_method import MyFastMethod
You can then start implementing the spatial method by adding functions to the class.
In particular, any spatial method *must* have the following functions (from the base class :class:`pybamm.SpatialMethod`):
- :meth:`pybamm.SpatialMethod.gradient`
- :meth:`pybamm.SpatialMethod.divergence`
- :meth:`pybamm.SpatialMethod.integral`
- :meth:`pybamm.SpatialMethod.indefinite integral`
- :meth:`pybamm.SpatialMethod.boundary_value_or_flux`
Optionally, a new spatial method can also overwrite the default behaviour for the following functions:
- :meth:`pybamm.SpatialMethod.spatial_variable`
- :meth:`pybamm.SpatialMethod.broadcast`
- :meth:`pybamm.SpatialMethod.mass_matrix`
- :meth:`pybamm.SpatialMethod.process_binary_operators`
- :meth:`pybamm.SpatialMethod.concatenation`
For an example of an existing spatial method implementation, see the Finite Volume
`API docs `_
and
`notebook `_.
Unit tests for the new class
----------------------------
For the new spatial method to be added to PyBaMM, you must add unit tests to demonstrate that it behaves as expected
(see, for example, the `Finite Volume unit tests `_).
The best way to get started would be to create a file ``test_my_fast_method.py`` in ``tests/unit/test_spatial_methods/`` that performs at least the
following checks:
- Operations return objects that have the expected shape
- Standard operations behave as expected, e.g. (in 1D) grad(x^2) = 2*x, integral(sin(x), 0, pi) = 2
- (more advanced) make sure that the operations converge at the correct rate to known analytical solutions as you decrease the grid size
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 `_):
.. code-block:: python
def test_my_fast_method(self):
model = pybamm.lithium_ion.DFN()
spatial_methods = {
"macroscale": pybamm.MyFastMethod,
"negative particle": pybamm.MyFastMethod,
"positive particle": pybamm.MyFastMethod,
}
modeltest = tests.StandardModelTest(model, spatial_methods=spatial_methods)
modeltest.test_all()
This will check that the model can run with the new spatial method (but not that it gives a sensible answer!).
Once you have performed the above checks, you are almost ready to merge your code into the core PyBaMM - see
`CONTRIBUTING.md workflow `_
for how to do this.