Repository: pybamm-team/PyBaMM

Issue:
Example notebook showing changing submodel
Add an example notebook showing how to load up a default model (e.g. SPM) and swap out a submodel, e.g. ODEs for particle concentration (fast diffusion) instead of Fickian. Also show how to build up your own model from submodels.

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index 59aaf8425..2c47eb938 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,7 +55,6 @@ pyproject.toml
 # virtual enviroment
 venv/
 venv3.5/
-PyBaMM-env/
 bin/
 etc/
 lib/
diff --git a/examples/notebooks/README.md b/examples/notebooks/README.md
index db8c02228..05bb55ca0 100644
--- a/examples/notebooks/README.md
+++ b/examples/notebooks/README.md
@@ -16,10 +16,14 @@ from your local PyBaMM repository, or used online through [Binder](https://mybin
 The easiest way to start with PyBaMM is by running and comparing some of the inbuilt models:
 - [Run the Single Particle Model (SPM)](./models/SPM.ipynb)
 - [Compare models](./models/lead-acid.ipynb)
+- [Comparison with COMSOL](./compare-comsol-discharge-curve.ipynb)
 
-It is also easy to new models or change the setting that are used:
-- [Add a model](https://pybamm.readthedocs.io/en/latest/tutorials/add-model.html)
+It is also easy to add new models or change the setting that are used:
+- [Add a model (documentation)](https://pybamm.readthedocs.io/en/latest/tutorials/add-model.html)
+- [Add a model (example)](./create-model.ipynb)
+- [Using submodels](./using-submodels.ipynb)
 - [Change the settings](./change-settings.ipynb) (parameters, spatial method or solver)
+- [Change the applied current](./change-input-current.ipynb)
 
 For more advanced usage, new sets of parameters, spatial methods and solvers can be added:
 - [Add parameters](https://pybamm.readthedocs.io/en/latest/tutorials/add-parameter-values.html)
@@ -50,8 +54,9 @@ The following models are implemented and can easily be used or [compared](./mode
 
 ### Spatial Methods
 
-The following spatial method is implemented
+The following spatial methods are implemented
 - [Finite Volumes](./spatial_methods/finite-volumes.ipynb)
+- Finite Elements (only for 2D current collector domains)
 
 See [here](https://pybamm.readthedocs.io/en/latest/tutorials/add-spatial-method.html) for instructions on adding new spatial methods.
 
diff --git a/examples/notebooks/models/DFN.ipynb b/examples/notebooks/models/DFN.ipynb
index 58c998029..eac6beef9 100644
--- a/examples/notebooks/models/DFN.ipynb
+++ b/examples/notebooks/models/DFN.ipynb
@@ -284,7 +284,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.7"
+   "version": "3.6.8"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/models/SPM.ipynb b/examples/notebooks/models/SPM.ipynb
index b02ca84a0..bc6b8cc03 100644
--- a/examples/notebooks/models/SPM.ipynb
+++ b/examples/notebooks/models/SPM.ipynb
@@ -829,7 +829,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.3"
+   "version": "3.6.8"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/models/SPMe.ipynb b/examples/notebooks/models/SPMe.ipynb
index 70ff6a007..09fefdc45 100644
--- a/examples/notebooks/models/SPMe.ipynb
+++ b/examples/notebooks/models/SPMe.ipynb
@@ -273,7 +273,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.6.7"
+   "version": "3.6.8"
   }
  },
  "nbformat": 4,
diff --git a/examples/notebooks/using-submodels.ipynb b/examples/notebooks/using-submodels.ipynb
new file mode 100644
index 000000000..dd53cb409
--- /dev/null
+++ b/examples/notebooks/using-submodels.ipynb
@@ -0,0 +1,525 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Using submodels in PyBaMM\n",
+    "In this notebook we show how to modify existing models by swapping out submodels, and how to build your own model from scratch using exisitng submodels. To see all of the models and submodels available in PyBaMM, please take a look at the documentation [here](https://pybamm.readthedocs.io/en/latest/source/models/index.html)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Changing a submodel in an exisiting battery model\n",
+    "PyBaMM is designed to be a flexible modelling package that allows users to easily compare different models and numerical techniques within a common framework. Battery models within PyBaMM are built up using a number of submodels that describe different physics included within the model, such as mass conservation in the electrolyte or charge conservation in the solid. For ease of use, a number of popular battery models are pre-configured in PyBaMM. As an example, we look at the Single Particle Model (for more information see [here](./models/SPM.ipynb)). First we import PyBaMM and any other packages we need"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import pybamm\n",
+    "import os\n",
+    "import numpy as np\n",
+    "import matplotlib.pyplot as plt\n",
+    "os.chdir(pybamm.__path__[0]+'/..')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Then we load the SPM"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = pybamm.lithium_ion.SPM()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can look at the submodels that make up the SPM by accessing `model.submodels`, which is a dictionary of the submodel's name (i.e. the physics it represents) and the submodel that is selected"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "porosity <pybamm.models.submodels.porosity.constant_porosity.Constant object at 0x7fa33e506358>\n",
+      "convection <pybamm.models.submodels.convection.no_convection.NoConvection object at 0x7fa33e506a90>\n",
+      "negative interface <pybamm.models.submodels.interface.lithium_ion.InverseButlerVolmer object at 0x7fa33e506ac8>\n",
+      "positive interface <pybamm.models.submodels.interface.lithium_ion.InverseButlerVolmer object at 0x7fa33e506b00>\n",
+      "negative particle <pybamm.models.submodels.particle.fickian.fickian_single_particle.SingleParticle object at 0x7fa33e506b38>\n",
+      "positive particle <pybamm.models.submodels.particle.fickian.fickian_single_particle.SingleParticle object at 0x7fa33e506b70>\n",
+      "negative electrode <pybamm.models.submodels.electrode.ohm.leading_ohm.LeadingOrder object at 0x7fa33e506ba8>\n",
+      "electrolyte conductivity <pybamm.models.submodels.electrolyte.stefan_maxwell.conductivity.leading_stefan_maxwell_conductivity.LeadingOrder object at 0x7fa33e506be0>\n",
+      "electrolyte diffusion <pybamm.models.submodels.electrolyte.stefan_maxwell.diffusion.constant_stefan_maxwell_diffusion.ConstantConcentration object at 0x7fa33e506c18>\n",
+      "positive electrode <pybamm.models.submodels.electrode.ohm.leading_ohm.LeadingOrder object at 0x7fa33e506c50>\n",
+      "thermal <pybamm.models.submodels.thermal.isothermal.isothermal.Isothermal object at 0x7fa33e506c88>\n",
+      "current collector <pybamm.models.submodels.current_collector.homogeneous_current_collector.Uniform object at 0x7fa33e506cc0>\n"
+     ]
+    }
+   ],
+   "source": [
+    "for name, submodel in model.submodels.items():\n",
+    "    print(name, submodel)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "When you load a model in PyBaMM it builds by default. Building the model sets all of the model variables and sets up any variables which are coupled between different submodels: this is the process which couples the submodels together and allows one submodel to access variables from another. If you would like to swap out a submodel in an exisitng battery model you need to load it without building it by passing the keyword `build=False`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = pybamm.lithium_ion.SPM(build=False)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This collects all of the submodels which make up the SPM, but doesn't build the model. Now you are free to swap out one submodel for another. For instance, you may want to assume that diffusion within the negative particles is infinitely fast, so that the PDE describing diffusion is replaced with an ODE for the uniform particle concentration. To change a submodel you simply update the dictionary entry"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.submodels[\"negative particle\"] = pybamm.particle.fast.SingleParticle(model.param, \"Negative\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now if we look at the submodels again we see that the model for the negative particle has been changed"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "porosity <pybamm.models.submodels.porosity.constant_porosity.Constant object at 0x7fa33e0681d0>\n",
+      "convection <pybamm.models.submodels.convection.no_convection.NoConvection object at 0x7fa33e068208>\n",
+      "negative interface <pybamm.models.submodels.interface.lithium_ion.InverseButlerVolmer object at 0x7fa33e068240>\n",
+      "positive interface <pybamm.models.submodels.interface.lithium_ion.InverseButlerVolmer object at 0x7fa33e068278>\n",
+      "negative particle <pybamm.models.submodels.particle.fast.fast_single_particle.SingleParticle object at 0x7fa33e110940>\n",
+      "positive particle <pybamm.models.submodels.particle.fickian.fickian_single_particle.SingleParticle object at 0x7fa33e0682e8>\n",
+      "negative electrode <pybamm.models.submodels.electrode.ohm.leading_ohm.LeadingOrder object at 0x7fa33e068320>\n",
+      "electrolyte conductivity <pybamm.models.submodels.electrolyte.stefan_maxwell.conductivity.leading_stefan_maxwell_conductivity.LeadingOrder object at 0x7fa33e068358>\n",
+      "electrolyte diffusion <pybamm.models.submodels.electrolyte.stefan_maxwell.diffusion.constant_stefan_maxwell_diffusion.ConstantConcentration object at 0x7fa33e068390>\n",
+      "positive electrode <pybamm.models.submodels.electrode.ohm.leading_ohm.LeadingOrder object at 0x7fa33e0683c8>\n",
+      "thermal <pybamm.models.submodels.thermal.isothermal.isothermal.Isothermal object at 0x7fa33e068400>\n",
+      "current collector <pybamm.models.submodels.current_collector.homogeneous_current_collector.Uniform object at 0x7fa33e068438>\n"
+     ]
+    }
+   ],
+   "source": [
+    "for name, submodel in model.submodels.items():\n",
+    "    print(name, submodel)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Building the model also sets up the equations, boundary and initial conditions for the model. For example, if we look at `model.rhs` before building we see that it is empty "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{}"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "model.rhs"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "If we try to use this empty model, PyBaMM will give an error. So, before proceeding we must build the model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.build_model()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now if we look at `model.rhs` we see that it contains an entry relating to the concentration in each particle, as expected for the SPM"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "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"
+    }
+   ],
+   "source": [
+    "model.rhs"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now the model can be processed and solved in the usual way, and we still have access to model defaults such as the default geometry and default spatial methods"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# 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",
+    "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",
+    "\n",
+    "# plot\n",
+    "plt.plot(solution.t, voltage(solution.t))\n",
+    "plt.xlabel(r'$t$')\n",
+    "plt.ylabel('Terminal voltage')\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Building a custom model from submodels\n",
+    "Instead of editing a pre-exisitng model, you may wish to build your own model from scratch by combining exisitng submodels of you choice. In this section, we build a Single Particle Model in which the diffusion is assumed infinitely fast in both particles. \n",
+    "\n",
+    "To begin, we load a base lithium-ion model. This sets up the basic model structure behind the scenes, and also sets the default paramaters to be those corresponding to a lithium-ion battery. Note that the base model does not select any default submodels, so there is no need to pass `build=False`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = pybamm.lithium_ion.BaseModel()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Submodels can be added to the `model.submodels` dictionary in the same way that we changed the submodels earlier. \n",
+    "\n",
+    "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,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.submodels[\"current collector\"] = pybamm.current_collector.Uniform(model.param)\n",
+    "model.submodels[\"thermal\"] = pybamm.thermal.isothermal.Isothermal(model.param)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We assume that the current density varies linearly in the electrodes. This corresponds the the leading-order terms in Ohm's law in the limit in which the SPM is derived in [1]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.submodels[\"negative electrode\"] = pybamm.electrode.ohm.LeadingOrder(\n",
+    "    model.param, \"Negative\"\n",
+    ")\n",
+    "model.submodels[\"positive electrode\"] = pybamm.electrode.ohm.LeadingOrder(\n",
+    "    model.param, \"Positive\"\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We assume that diffusion is infinitely fast in both the negative and positive particles "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.submodels[\"negative particle\"] = pybamm.particle.fast.SingleParticle(\n",
+    "    model.param, \"Negative\"\n",
+    ")\n",
+    "model.submodels[\"positive particle\"] = pybamm.particle.fast.SingleParticle(\n",
+    "    model.param, \"Positive\"\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "In the Single Particle Model, the overpotential can be obtianed by inverting the Butler-Volmer relation, so we choose the `InverseButlerVolmer` submodel for the interface"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.submodels[\n",
+    "    \"negative interface\"\n",
+    "] = pybamm.interface.lithium_ion.InverseButlerVolmer(model.param, \"Negative\")\n",
+    "model.submodels[\n",
+    "    \"positive interface\"\n",
+    "] = pybamm.interface.lithium_ion.InverseButlerVolmer(model.param, \"Positive\")\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Finally, for the electrolyte we assume that diffusion is infinitely fast so that the concentration is uniform, and also use the leading-order model for charge conservation, which leads to a linear variation in ionic current in the electrodes"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "electrolyte = pybamm.electrolyte.stefan_maxwell\n",
+    "model.submodels[\"electrolyte diffusion\"] = electrolyte.diffusion.ConstantConcentration(\n",
+    "    model.param\n",
+    ")\n",
+    "model.submodels[\"electrolyte conductivity\"] = electrolyte.conductivity.LeadingOrder(\n",
+    "    model.param\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now that we have set all of the submodels we can build the model"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model.build_model()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The base lithium-ion model doesn't come with a default geometry, so we choose a 1D macroscopic geometry for the electrodes and separator, and 1D microscopic geometry for the particles (i.e. the geometry of the standard Newman model [2])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "geometry = pybamm.Geometry(\"1D macro\", \"1D micro\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The base model does come with defaults for the parameters, spatial methods and submeshes, so we can now proceed to solve the model in the usual way "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEICAYAAABF82P+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nO3deXxU1f3/8dcnC0kIkIQkrIFAQAVk30VF1NZStWqrdW/BDbVqtXuttrW29tvaurXV1n39WVxarVWrooAIChh2QdYgOyQQAgRIIMnn98cMNqYhDJDJnSTv5+NxH5m5c+7MmyB+5t5z7jnm7oiIiNQUF3QAERGJTSoQIiJSKxUIERGplQqEiIjUSgVCRERqlRB0gPqSlZXl3bp1CzqGiEijMmfOnK3unl3ba02mQHTr1o38/PygY4iINCpmtuZgr+kSk4iI1EoFQkREaqUCISIitVKBEBGRWqlAiIhIrVQgRESkVioQIiJSq2ZfIPZXVvHbNz9lQ8neoKOIiMSUZl8gNmzfy99nr+WKJ2ezY+/+oOOIiMSMZl8gumWl8vDlQ1i9dTfXPTuHfRVVQUcSEYkJzb5AAIzqmcXdF/Tno4Jt/PQfC9EqeyIiTWgupqP19UE5bNi+lz++s5ycjBS+f8ZxQUcSEQmUCkQ1N5zak3XFe/nT5JV0aduSbw7tEnQkEZHAqEBUY2b85ut92VCyl1v/uYjO6SmM6pkVdCwRkUCoD6KGxPg4Hrp8MD2yW3Htc3NYsWVX0JFERAKhAlGLNsmJPHHFMJIT47niqY8p2lUedCQRkQanAnEQndNTeHzcULaWlnPNM/mU7a8MOpKISINSgahD/5x07r9oEAvWl/D9F+dTVaXhryLSfKhAHMLYvh249au9eHPRZv7wzrKg44iINJiojWIys2RgGpAU/pyX3f2XNdrcB5waftoSaOfu6eHXKoFF4dfWuvs50cp6KNecnMdn2/bw16mr6J6ZyoXDNPxVRJq+aA5zLQdOc/dSM0sEppvZf9x95oEG7v69A4/N7CZgULXj97r7wCjmi5iZ8atzjmdd8R5+9soicjI0/FVEmr6oXWLykNLw08TwVtdF/EuAv0crz9FKjI/jwcsGk5edynXPzWFlYemhDxIRacSi2gdhZvFmNh8oBCa5+6yDtMsFugOTq+1ONrN8M5tpZucd5LgJ4Tb5RUVF9Z6/pjbJiTw+bhgtEuK48qmP2Vaq4a8i0nRFtUC4e2X4MlEOMNzM+h6k6cWE+iiqjyXNdfehwKXA/WbWo5b3f8Tdh7r70Ozs7HrPX5subVvy6LeHsmVnGROenaPhryLSZDXIKCZ3LwGmAGMP0uRialxecvcN4Z8FwFS+2D8RqEFdM7jvooHMWbOdH7+s2V9FpGmKWoEws2wzOzAiKQX4MrC0lna9gAzgo2r7MswsKfw4CzgRWBKtrEfizH4d+fHY43htwUbunbQ86DgiIvUumqOYOgJPm1k8oUL0oru/bmZ3Avnu/lq43cXARP/i1/DewMNmVhU+9nfuHlMFAuD6U3qwZuse/jx5JbmZqVwwJCfoSCIi9caayuWRoUOHen5+foN/7v7KKsY/OZvZq4t5+srhjOqh4a8i0niY2Zxwf+//0J3URykxPo6HLhtCt8xUrnt2DisLNfuriDQNKhD1IC0lkSfGD6NFQjzjn9TsryLSNKhA1JMubVt+Pvvr1c/ks3efhr+KSOOmAlGPBnRJ508XD2Lh+hJueWEelZr9VUQaMRWIenbG8R34xdl9eHvxFu5649Og44iIHDGtSR0FV5zYnbXFe3hixmpyMlK48qTuQUcSETlsKhBRcvtZfdhUUsav31hCx7RkvtqvY9CRREQOiy4xRUl8nHH/xQMZ1CWdW16Yz5w1xUFHEhE5LCoQUZScGM9j44bRKT2Fq57OZ1WRpggXkcZDBSLK2qa24KkrhpEQZ4x7YjaFO8uCjiQiEhEViAaQm5nKE+OHUbx7H+Of/JhdZfuDjiQickgqEA2kf046D102mOVbdnH9c3PZV1EVdCQRkTqpQDSgMce143fn92f6yq384KUFVOlGOhGJYRrm2sAuGJLD1tJyfvefpWSmtuCXX+uDmQUdS0Tkf6hABODa0XkU7Srn8emryW6dxA2n9gw6kojI/1CBCICZcduZvSnevY8/vL2MjJYtuHRE16BjiYh8gQpEQOLijLsv6E/Jnn3c9uoi2qQkcHb/TkHHEhH5nDqpA3RgsaGhuRl874X5vL+8KOhIIiKfU4EIWEqL0N3WPdu15rpn52hKDhGJGSoQMSAtJZFnrhxOh7Rkxj/5MZ9s2BF0JBERFYhYkd06ieeuHkGb5ES+/cRsrW0tIoFTgYghndNTeO7qEcSZcdljs1izbXfQkUSkGVOBiDHds1L5f1ePoLyiiksfncX67XuCjiQizZQKRAw6rkNrnrtqBLvK9nPpo7PYtGNv0JFEpBlSgYhRfTun8cxVIyjevY/LHp2lacJFpMGpQMSwgV3SeeqKYWzeWcYlj86kcJeKhIg0HBWIGDe0W1ueHD+MTTvKuOQRFQkRaTgqEI3AiLxMFQkRaXAqEI1E9SJx8cMz2bxDRUJEoksFohEZkZfJ01cOp3BXORc98hEbSjS6SUSiRwWikRnWrS3PXjWc4t37uOjhj1hXrPskRCQ6VCAaoUFdM3j+6pGUllfwzb99xMrC0qAjiUgTpALRSPXLSWPihJFUVDkXPvyRJvgTkXqnAtGI9erQhpeuO4HkhDgueXSmpgoXkXqlAtHIdc9K5aXrR5HVKonLHpvFlGWFQUcSkSZCBaIJ6JyewkvXnUCP7FZc83Q+r87bEHQkEWkColYgzCzZzGab2QIzW2xmv6qlzX1mNj+8LTezkmqvjTOzFeFtXLRyNhVZrZKYOGEkQ3IzuOWF+TwxfXXQkUSkkUuI4nuXA6e5e6mZJQLTzew/7j7zQAN3/96Bx2Z2EzAo/Lgt8EtgKODAHDN7zd23RzFvo9c6OZGnrxzOzRPncefrS9iys4yfjO1FXJwFHU1EGqGonUF4yIHxl4nhzes45BLg7+HHXwEmuXtxuChMAsZGK2tTkpwYz0OXDeFbI3N5eFoB33txPvsqqoKOJSKN0CELhJn1NLO3zWxB+Hl/M7s1kjc3s3gzmw8UEvof/qyDtMsFugOTw7s6A+uqNVkf3lfzuAlmlm9m+UVFRZFEahbi44w7zz2eH33lOP41fyPjnpjNjr37g44lIo1MJGcQjwG/Ag58DV0EXB7Jm7t7pbsPBHKA4WbW9yBNLwZedvfKSN632vs/4u5D3X1odnb24Rza5JkZN5zak3svHED+mmLO/+uHuutaRA5LJAUi1d0/PPDE3R04rK+j7l4CTOHgl4ku5r+XlwA2AF2qPc8J75PD9I3BOTxz5QgKd5bx9YdmMG+tunFEJDKRFIhtZtadcP+BmZ0HbD7UQWaWbWbp4ccpwJeBpbW06wVkAB9V2/02cIaZZZhZBnBGeJ8cgRN6ZPLP75xIyxYJXPzITF5bsDHoSCLSCERSIG4EHgd6mdka4KfA9REc1xGYYmYLgY8J9UG8bmZ3mtk51dpdDEwMn5kA4O7FwK/Dx30M3BneJ0eoZ7tWvPKdUQzISee7f5/HPe8so6qqrjEDItLcWbX/L9fd0Cwt3L7kkI0DMHToUM/Pzw86RszbV1HF7a8u4sX89Yw9vgP3XDiA1KRojnYWkVhmZnPcfWhtrx3y/wxm9t0azwF2AHPc/ZN6SSgNpkVCHL8/vz/Htm/Nb9/8lG88tJtHvj2E3MzUoKOJSIyJ5BLTKOBmoEd4uwk4B3jGzH4QxWwSJWbG1Sfn8fSVw9m8s4xz/jKDacs1TFhEviiSAtERGOjuN7v7zcBgIAs4CbgqmuEkuk4+Jpt/33gSHdOSGf/kbB6cslL9EiLyuUgKRHug+tqW5UB7d98TfiyNWNfMlvzj+lGc3b8Tf3h7GROenaOb6kQEiKxAvAB8ZGa3mdltwAfAC2aWCiyLajppEKlJCTxw8UB++bU+TF1WyDl/ma4FiEQkslFMZjYSODH8dEb1CfdihUYx1Y/8z4q58fl5FO/ex8/P7s3lI3MPDEwQkSaorlFMEU3WFy4ITxK623mtmXWqx3wSQ4Z2a8ubN5/MqJ6Z/Pxfi7nx+XnsLNMlJ5HmKJLJ+s4ys+WEJsybSWgSvcl1HyWNWdvUFjwxbhg/GduLtxZv5swHPmDOGk3RIdLcRHIGcRehy0vL3L0rofmUPohqKglcXJxx/ZgevHTdCZjBhQ9/xJ/fW0GlRjmJNBuRFIgKdy8C4szM3H0SMDzKuSRGDO6awRvfPZmz+3fknknLuejhj1i7TbPCijQHkRSIHWbWCphO6Oa4e/jisFdp4tokJ3L/RQO5/6KBLNuyi68+MI0XP15HpNO0iEjjFEmBOI9QQbgFmEpo2u2zo5hJYpCZcd6gzrx1y2j65aTx438s5Jpn8incVRZ0NBGJkkgKxK3hhX/2u/vj7n4v8P1oB5PY1Dk9heevHsntZ/XmgxVbOeO+afxr/gadTYg0QZEUiNoW+TmrvoNI4xEXF5rL6c2bT6Z7Vio3T5zPhGfnsGWnziZEmpKDFggzu9bM5gHHmdncatsK4NOGiyixqkd2K16+bhQ/O7MX05YX8aV732fi7LU6mxBpIg56J3V4JbdM4P8ILRJ0wC53L2yAbIdFd1IH67Otu/npPxcys6CYkXltuevr/eiR3SroWCJyCHXdSV1XgWhT15u6+856yFZvVCCCV1XlvJi/jt+++Sll+6u4bkwPvjOmB8mJ8UFHE5GDONICsY7wOtRAzcl4PHzTXMxQgYgdRbvKueuNJbw6fyPdMltyxznHM+a4dkHHEpFaHFGBaGxUIGLP9BVb+cW/PqFg627GHt+B28/uTU5Gy6BjiUg1R10gzOxMYHT46VR3f6se89ULFYjYVF5RyWMfrObPk1fgDted0oPrTulBSgtddhKJBUc1m6uZ3QX8GCgIbz82s9/Ub0RpqpIS4rnh1J6894MxfKlPex54bwWn3zOV1xZs1GgnkRh3yDMIM1sIDHL3yvDzBGCuu/dvgHwR0xlE4zCrYBu/+vcSlmzayaCu6dx+Vh+G5GYEHUuk2Trq9SCA6iOaWh99JGmuRuRl8u+bTuLu8/uzYftezv/rh9zw/Fw+27o76GgiUkNCBG3uBuaa2XuERjONAX4ezVDStMXHGRcO68JZ/Tvy8LQCHp1WwNufbObSEV357unHkNUqKeiIIkLkndSdgRHhp7PcfUNUUx0BXWJqvAp3lfHAuyuY+PE6khLiuOqk7lwzOo82yYlBRxNp8o5qFJOZ/ZPQUqOvu3vMTvOtAtH4rSoq5d5Jy3lj4SbSUhK57pQejBuVS8sWkZzoisiRONo+iAeBLwPLzGyimZ1nZi3qNaEIobmdHrx0MK/fdBKDuqbz+7eWMvruKTz2QQFl+yuDjifS7ER8o1x49NKXgauAL7l7ejSDHS6dQTQ9c9YUc9+kFUxfuZXs1klcOzqPy0bk6h4KkXp01KOYzCwJOAcYDwwldMlJJKqG5LbluatH8MKEkRzTrhW/eeNTTr57Mn97fxWl5RVBxxNp8iLpg3geOBGYBLwATD5wT0Qs0RlE0/fxZ8X86b0VfLBiK2kpiVx5YnfGj+pGWkt1ZoscqaPtpD4LeMfd90cjXH1RgWg+5q8r4S+TV/Lup1tIbRHP5SNzueqk7rRrkxx0NJFGR5P1SZP06aadPDR1FW8s3EhCXBznD8lhwug8umelBh1NpNFQgZAmbc223TwyrYCX5qxnf2UVX+3bgWtH92BAl5gaRyESk1QgpFko3FXGUzM+49mZa9hVVsEJeZlce0oepxybjVnNJU1EBI58waA6J+Nz94X1kK3eqEDIAaXlFfx91loen76azTvL6NWhNdeeksfZ/TuRGB/p9GMizcORFogP6nhPd/fRdbyOmSUD04AkQnM+vezuv6yl3YXAHYRWr1vg7peG91cCi8LN1rr7OXV9ngqE1LSvoorXFmzk4fdXsaKwlM7pKVx1UncuHt5Fd2eLhAVyiclC5/Sp7l5qZonAdOBmd59Zrc0xwIvAae6+3czauXth+LVSd4941XsVCDmYqipnyrJCHn6/gNmfFZPeMpHxo7oxflQ30ltqUgBp3uoqEBF9jTKzXkAf4PNxhO7+fF3HeKjylIafJoa3mtXoGuBBd98ePqYwkjwihyMuzji9d3tO792eOWuK+evUVdz/7goemVbAZSO6cs3oPNq11hBZkZoiWVHuduAR4G/AV4H7gQsieXMzizez+UAhMMndZ9VocixwrJnNMLOZZja22mvJZpYf3n9eJJ8ncihDctvy2LhhvH3LaM7o057Hp6/m5N9P4Y7XFrN5R1nQ8URiSiQ3yi0CBhJaRW6AmXUEnnL3r0T8IWbpwCvATe7+SbX9rwP7gQuBHEJ9Fv3cvcTMOrv7BjPLAyYDp7v7qhrvOwGYANC1a9cha9asiTSSCACrt+7mr1NX8s+5G4iLM741Mpfrx/TQmhTSbBztXEx7w1NrVJhZa2AzkHs4Ady9BJgCjK3x0nrgNXff7+6rgeXAMeFjNoR/FgBTgUG1vO8j7j7U3YdmZ2cfTiQRALpnpXL3BQOY8sMxnDugE0/OCJ1R3PPOMs33JM1eJAViXvgM4AkgH5gd3upkZtnh4zCzFEIzwS6t0exVQivUYWZZhC45FZhZRniCwAP7TwSWRPIHEjkSXdq25A/fHMC73z+FL/Vpz58nr2TMH6bw3Mw1VFRWBR1PJBCHNYrJzHoCbdx9bgRt+wNPA/GECtGL7n6nmd0J5Lv7a+GRTvcQOrOoBO5y94lmNgp4GKgKH3u/uz9e1+dpFJPUpwXrSrjrzU+ZvbqYY9u34jfn9WN497ZBxxKpd0c9zNXMOgBdqTbqyd0/rLeE9UAFQuqbu/P24i38+vUlbCjZy/mDc7j1zF7qn5Am5aiGuZrZb4HLCV0eOjDNtwNn1ltCkRhkZozt24HRx2bxl8krefSDAiYv3cJvzuvHWf07Bh1PJOoiGcW0DBjg7jE9BlBnEBJtK7bs4gcvLWDh+h18bUAn7jzneDJSdaOdNG5HO4ppNaF+BJFm7Zj2rfnn9aP4wZeP5T+LNjH2gWnkf1YcdCyRqImkQOwC5prZg2Z274Et2sFEYlFCfBw3nX4Mr95wIsmJ8Vz0yEwenVZAU5kVWaS6SKbaeCu8iUhY385p/Pumk/jRSwu4681PmbNmO/deNECTAEqTovUgRI6Cu/PYB6v5v/98yvGd0nh83FAtfSqNyhH1QZjZ38M/55nZ3JpbtMKKNCZmxjWj83j020NZVVTKeQ/OYOnmnUHHEqkXda0HkePu682sR22v15wXKWg6g5CgfbJhB1c9/TF7yit58ophDO2mG+sk9h3RGYS7rw//XBUuBpuAomqbiFTTt3Mar3znRLJbJ/Gtx2czY+XWoCOJHJVIpvu+2sw2EZpI7xNgcfiniNTQKT2FF649ga5tW3LFUx/z3qdbgo4kcsQiGeb6E0I3yuW4e1d37+LuXaMdTKSxym6dxMQJI0NrYT87R0VCGq1ICkQBoF43kcOQkdqC564eQZ9Obbj+ubl8sEJXZaXxiaRA/BSYoRvlRA5Pm+REnrlyOHnZqVzzTD4zC7YFHUnksERSIP4GzADmE+p/OLCJyCGktwydSXROT+Gqpz5mwbqSoCOJRCySyfrmu/vABspzxDTMVWLZlp1lnP/XD9ldXsFL142iZ7tWQUcSAY5+sr43zOzK8ApxbQ5s9ZxRpElr3yaZ564aQXxcHN96fBYbSvYGHUnkkCIpEN8GfgXM5b+XlzTMVeQwdctK5Zkrh1NaXsG3Hp9F8e59QUcSqVOdBcLM4oBvhoe2Vt80zFXkCPTp1IbHxw1jw/a9XPHkbErLK4KOJHJQdRYId68i1EktIvVkePe2PHTZYD7ZuJNrn82nvKLy0AeJBCCSS0xTzOzcqCcRaUZO792eu8/vz4yV27hl4nwqq5rGrMrStERSIMYDr5jZXjMrNrPtZqZltESO0vlDcrj9rN7855PN/OQfC6lSkZAYE8nqJllRTyHSTF19ch67yip44L0VJCfG8etz+2JmQccSASIoEO5eaWYXA3nu/lszywHaA3Oink6kGbjlS8dQVlHJw+8XkJQQz+1n9VaRkJhwyAJhZn8BEoHRwG+BPYQ6rodFN5pI82Bm/HRsL8r3V/H49NUAKhISEyK5xDTK3Qeb2TwAdy82sxZRziXSrJgZv/xaHwAen76a3eUV3PX1fsTHqUhIcCIpEPvD90M4gJllAlVRTSXSDB0oEm2SE/jT5JXsKq/gvgsH0iIhkrEkIvXvoAXCzBLcvQJ4EPgHkG1mvwIuJHRntYjUMzPj+2ccR6vkBH775lK2lZbz0GVDaJuqk3ZpeHV9NZkN4O7PALcDfwS2E7qzemIDZBNptiaM7sF9Fw1g7toSzn1wOss27wo6kjRDdRWIzy9+uvtid3/A3e93d83DJNIAvj4ohxcmjKRsfxXfeGgGbyzcFHQkaWbq6oPINrPvH+xFd9eiQSJRNqhrBv++8SSufW4ONzw/l8lLc7jjnD60Tk4MOpo0A3WdQcQDrYDWB9lEpAF0SEvm5etO4Lun9eSVeev56gMf8OGqrUHHkmbgoAsGmdlcdx/cwHmOmBYMkuZgzppivvfCAtYW7+FrAzpx25m96ZCWHHQsacSOdMEgDcAWiTFDctvy9i2jufn0Y3h78WZOu2cqf35vhaYNl6io6wyirbs3mkn5dAYhzc3abXv4zRtLeGfJFtJbJjJhdB7jTuhGalIktzeJhNR1BnHINakbCxUIaa4WrCvh/neXM2VZEW2SE7hkRFfGndCNTukpQUeTRkAFQqQZmLd2O499sJr/fLIJM+Mrx7fn4mFdOalnFnGaskMOoq4CEbVzUTNLBqYBSeHPedndf1lLuwuBOwhN5bHA3S8N7x9H6AY9gN+4+9PRyirSFAzqmsGDl2WwfvsenvloDS/lr+PNRZvpnJ7C+UNy+PqgznTPSg06pjQiUTuDsNBUlKnuXmpmicB04GZ3n1mtzTHAi8Bp7r7dzNq5e6GZtQXygaGECsccYIi7bz/Y5+kMQuSLyisqmbRkCxNnr2PGqq24w4Au6Zw7oBNn9uuo0U8CBHQG4aHKUxp+mhjealaja4AHD/yP390Lw/u/Akw60EluZpOAscDfo5VXpKlJSojn7P6dOLt/Jzbt2Mu/F2zk1XkbufP1Jdz5+hKG5mZwZr+OnHF8e3IyWgYdV2JQVPsgzCye0Lf/noQKwU9qvP4qsBw4kdCNeXe4+1tm9kMg2d1/E273c2Cvu/+xxvETgAkAXbt2HbJmzZqo/VlEmopVRaW8uXATbyzaxNLwHE99O7fhjD4d+FLv9vTu2FprUTQjgXdSm1k68ApwU/W5nMzsdWA/oRlicwj1WfQDriaCAlGdLjGJHL7VW3fzzuLNvL14M3PXlgDQOT2F03u347Re7RiZl0lyYnzAKSWaArnEVJ27l5jZFEKXiapP9rcemOXu+4HVZrYcOAbYAIyp1i4HmNoQWUWak+5ZqVx7Sg+uPaUHhbvKmLK0kHc/LeTF/HU889EaUhLjObFnJqf2aseY49rRWUNnm5VodlJnA/vDxSEFeAf4vbu/Xq3NWOASdx9nZlnAPGAg/+2YPjDVx1xCndQHvXFPZxAi9adsfyUfFWxjytJCJi8tZP32vQAc064VY47LZsxx7RjaLYOkBJ1dNHaBXGIys/7A04T6FuKAF939TjO7E8h399fCI53uIXRmUQncdWCtCTO7EvhZ+O3ucvcn6/o8FQiR6HB3VhWVMnVZEVOXFTF7dTH7Kqto2SKekXmZjD4mi5OPzSYvK1V9F41Q4H0QDUEFQqRh7C6vYGbBNqYuK+KDFUV8tm0PEOq7GH1sFicfk82oHpmkt9QqeI2BCoSIRM3abXt4f0UR01cU8eHKbewqr8AM+nVO46SeWZzYM4shuRnq7I5RKhAi0iAqKqtYsL6E6Su2MX1lEfPWllBR5bRIiGNobgYn9szihB6Z9O+cRkJ8XZNJS0NRgRCRQJSWVzB79TZmrNzGjJVbP7/volVSAsO6ZXBCj0xG5mVyfKc04jVfVCACH+YqIs1Tq6QETuvVntN6tQdgW2k5MwuKmbFqKzMLtjFlWREArZMSGNItgxHdMxnePYN+ndNpkaAzjKCpQIhIg8lslcRZ/TtyVv+OABTuLOOjgm3MXl3MrNXFTF22FICkhDgGdklnePe2DMnNYFDXDNJStA53Q9MlJhGJGVtLy8n/rJjZq7fz8WfFLN64gyoHMzi2XWsG56YzqEsGg3PTyctqpWnM64H6IESkUdpdXsH8dSXMWbOd/DXbmb92OzvLQsurtk5OYEBOOgO7pDOgSzoDctJo10Yz1B4u9UGISKOUmpTAieGhsgBVVU7B1lLmrilh/voSFqwr4a/vr6KyKvRFt0ObZPrlpNG/cxp9c9Lo1zmNrFZJQf4RGjUVCBFpNOLijJ7tWtOzXWsuHNYFgL37KlmyaQcL1u1gwfoSFm3YwaQlWz4/pmNaMn07h4pF385t6NtJZxqRUoEQkUYtpUU8Q3LbMiS37ef7dpXtZ/HGnXyyYQeLwtu7n27hwBX19m2S6Nc5jX6d0+mfk0b/nDQydabxP1QgRKTJaZ2cyMi80D0WB5SWV7Bk404WbdjBJxt2sHB9Ce8tLfy8aORkpDCgSzqDu2YwuGs6x3dKa/ZDbVUgRKRZaJWUwPDubRne/b9nGqXlFZ8Xi/nrSpi3ZjtvLNwEQHJiHIO7ZjC8e1tG5mUyuGtGsysYGsUkIlLN5h1lzF0bGmY7e3UxSzbtxB1atohnRPe2nHJsNl/q03SWadUwVxGRI7Rj735mFWxj+sqtfLBiK6u37gagd8c2nNGnPecO7ERedquAUx45FQgRkXqyeutuJi3ZzKQlW8hfsx136J+TxnkDO3P+4BzSWjauO75VIEREomDzjjJeX7iRV+dv4JMNO0lKiONrAzrxrZG5DOiSHnS8iKhAiIhE2ZKNO3lu1hpenbeBPfsqOSEvkxtP68moHsloxyQAAAnASURBVJkxvdKeCoSISAPZVbafFz5exyPTCijcVc6grun8ZGyvLwy5jSUqECIiDaxsfyUvz1nPQ1NWsnFHGWf0ac+tZ/ame1Zq0NG+oK4C0bwG9YqINJDkxHguH5nL5B+O4UdfOY4ZK7fy5Xvf5953llFeURl0vIioQIiIRFFyYjw3nNqTqT86lXMGdOJPk1dy9p+mM2/t9qCjHZIKhIhIA8huncS9Fw3kyfHDKC2v4Py/fsj97y7/fCbaWKQCISLSgE7t1Y53vjea8wZ25v53VzD+ydlsKy0POlatVCBERBpY6+RE7rlwAP/3jX7MWl3MWX+azsL1JUHH+h8qECIiATAzLhnelX9eP4r4OOPiR2YyZVlh0LG+QAVCRCRAfTun8cp3RtEtM5Wrn87npfx1QUf6nAqEiEjA2rVJ5oVrR3JCXiY/enkhj09fHXQkQAVCRCQmtE5O5Inxwxh7fAd+/foSnp+1NuhIKhAiIrGiRUIcf7pkEGOOy+a2Vxfx6rwNgeZRgRARiSEtEuL42+VDGNG9LT94aQGTlmwJLIsKhIhIjElOjOexccPo26kNN0+cx7LNuwLJoQIhIhKDWiUl8PC3hpKalMCEZ/PZsWd/g2dQgRARiVEd0pL52+WD2Viyl5smzmvwaTlUIEREYtiQ3LbceW5fpi0v4r5Jyxv0s1UgRERi3CXDu/LNITk8NHUlcxtwFlgVCBGRRuAXX+tDx7QUfvjSAsr2N8x6ElErEGaWbGazzWyBmS02s1/V0ma8mRWZ2fzwdnW11yqr7X8tWjlFRBqD1smJ3H1BfwqKdvOHt5c1yGcmRPG9y4HT3L3UzBKB6Wb2H3efWaPdC+5+Yy3H73X3gVHMJyLSqJzYM4tvn5DLEzNWc0af9oyI8jrXUTuD8JDS8NPE8Ba7K2OIiDQCP/1qL7pktORnryxif2VVVD8rqn0QZhZvZvOBQmCSu8+qpdn5ZrbQzF42sy7V9iebWb6ZzTSz8w7y/hPCbfKLioqi8UcQEYkpLVsk8POz+7CqaDcTZ0d3vqaoFgh3rwxfJsoBhptZ3xpN/g10c/f+wCTg6Wqv5br7UOBS4H4z61HL+z/i7kPdfWh2dnaU/hQiIrHlS73bcUJeJve9u4Ide6N3A12DjGJy9xJgCjC2xv5t7n5grb3HgCHVXtsQ/lkATAUGNURWEZFYZ2bcdlZvtu/Zx0NTVkbtc6I5iinbzNLDj1OALwNLa7TpWO3pOcCn4f0ZZpYUfpwFnAgsiVZWEZHGpm/nNM4fnMOTMz5jXfGeqHxGNM8gOgJTzGwh8DGhPojXzexOMzsn3Oa74SGwC4DvAuPD+3sD+eH9U4DfubsKhIhINT884zji44zfvbX00I2PQNSGubr7Qmq5LOTuv6j2+Fbg1lrafAj0i1Y2EZGmoENaMjed3pO9+ypxd8ysXt8/mvdBiIhIlH1nTM+ovbem2hARkVqpQIiISK1UIEREpFYqECIiUisVCBERqZUKhIiI1EoFQkREaqUCISIitTL3prFEg5kVAWuO4i2ygK31FCcaYj0fxH7GWM8HylgfYj0fxFbGXHevdTrsJlMgjpaZ5YenF49JsZ4PYj9jrOcDZawPsZ4PGkdG0CUmERE5CBUIERGplQrEfz0SdIBDiPV8EPsZYz0fKGN9iPV80Dgyqg9CRERqpzMIERGplQqEiIjUqskXCDMba2bLzGylmf20lteTzOyF8OuzzKxbtdduDe9fZmZfibWMZvZlM5tjZovCP0+LpXzVXu9qZqVm9sNo5DvajGbW38w+Ci9/u8jMkmMpo5klmtnT4Wyfmtn/rMLYQPlGm9lcM6swswtqvDbOzFaEt3HRyHc0Gc1sYLW/44VmdlEs5av2ehszW29mf4lGvsPm7k12A+KBVUAe0AJYAPSp0eY7wN/Cjy8GXgg/7hNunwR0D79PfIxlHAR0Cj/uC2yIpXzVXn8ZeAn4YQz+PScAC4EB4eeZMfj3fCkwMfy4JfAZ0C2AfN2A/sAzwAXV9rcFCsI/M8KPMwL6HR4s47HAMeHHnYBNQHqs5Kv2+gPA88Bf6vv3dyRbUz+DGA6sdPcCd98HTATOrdHmXODp8OOXgdMttLDruYT+UZa7+2pgZfj9Yiaju89z943h/YuBFDNLipV8AGZ2HrA6nC9ajibjGcBCd18A4O7b3L0yxjI6kGpmCUAKsA/Y2dD53P0zD601X1Xj2K8Ak9y92N23A5OAsfWc76gyuvtyd18RfrwRKARqvXs4iHwAZjYEaA+8U8+5jlhTLxCdgXXVnq8P76u1jbtXADsIfYuM5NigM1Z3PjDX3ctjJZ+ZtQJ+AvyqnjPVW0ZC3yzdzN4On/r/OAYzvgzsJvStdy3wR3cvDiBfNI49HPXyOWY2nNA3/FX1lOuAI85nZnHAPUDULsMeiYSgA8jRM7Pjgd8T+jYcS+4A7nP30vAJRSxKAE4ChgF7gPfMbI67vxdsrC8YDlQSujSSAXxgZu+6e0GwsRofM+sIPAuMc/f/+RYfoO8Ab7r7+lj6t9LUzyA2AF2qPc8J76u1TfgUPg3YFuGxQWfEzHKAV4Bvu3t9fyM62nwjgLvN7DPgFuBnZnZjjGVcD0xz963uvgd4ExgcYxkvBd5y9/3uXgjMAOp7Hp+j+e89lv6tHJSZtQHeAG5z95n1nA2OLt8JwI3hfyt/BL5tZr+r33hHIOhOkGhuhL4dFhDqZD7QaXR8jTY38MWOwRfDj4/ni53UBUSn8/JoMqaH238jFn+HNdrcQfQ6qY/md5gBzCXU+ZsAvAucFWMZfwI8GX6cCiwB+jd0vmptn+J/O6lXh3+XGeHHbYP4HdaRsQXwHnBLNP4bPNp8NV4bT4x0UgceIOp/QDgTWE7oeuNt4X13AueEHycTGmGzEpgN5FU79rbwccuAr8ZaRuB2Qtem51fb2sVKvhrvcQdRKhD18Pd8OaFO9E+Au2MtI9AqvH8xoeLwo4DyDSN0xrWb0JnN4mrHXhnOvRK4IsDfYa0Zw3/H+2v8WxkYK/lqvMd4YqRAaKoNERGpVVPvgxARkSOkAiEiIrVSgRARkVqpQIiISK1UIEREpFYqECIiUisVCBERqZUKhEiUmVlOtNYfEIkmFQiR6Dud6MzvJBJVupNaJIrM7CTgX0AJsIvQvFmahVUaBRUIkSgzs7cIzUP1SdBZRA6HLjGJRN9xwNKgQ4gcLhUIkSgysyxgh4dWiBNpVFQgRKKrG7DxUI1EYpEKhEh0LQWyzOwTMxsVdBiRw6FOahERqZXOIEREpFYqECIiUisVCBERqZUKhIiI1EoFQkREaqUCISIitVKBEBGRWv1/yJzvhqFc2TgAAAAASUVORK5CYII=\n",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "# 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",
+    "t_eval = np.linspace(0, 0.15, 100)\n",
+    "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",
+    "\n",
+    "# plot\n",
+    "plt.plot(solution.t, voltage(solution.t))\n",
+    "plt.xlabel(r'$t$')\n",
+    "plt.ylabel('Terminal voltage')\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## References\n",
+    "[1] SG Marquis, V Sulzer, R Timms, CP Please and SJ Chapman. “An asymptotion derivation of a single particle model with electrolyte”. In: arXiv preprint arXiv:1905.12553 (2019).\n",
+    "\n",
+    "[2] M Doyle, TF Fuller and J Newman. \"Modeling of galvanostatic charge and discharge of the lithium/polymer/insertion cell.\" Journal of the Electrochemical Society 140.6 (1993): 1526-1533."
+   ]
+  },
+  {
+   "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.8"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/examples/scripts/DFN.py b/examples/scripts/DFN.py
index 7023544a7..8697cfc53 100644
--- a/examples/scripts/DFN.py
+++ b/examples/scripts/DFN.py
@@ -19,7 +19,9 @@ param.process_model(model)
 param.process_geometry(geometry)
 
 # set mesh
-mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)
+var = pybamm.standard_spatial_vars
+var_pts = {var.x_n: 30, var.x_s: 30, var.x_p: 30, var.r_n: 10, var.r_p: 10}
+mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts)
 
 # discretise model
 disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
@@ -27,7 +29,10 @@ disc.process_model(model)
 
 # solve model
 t_eval = np.linspace(0, 0.2, 100)
-solution = model.default_solver.solve(model, t_eval)
+solver = model.default_solver
+solver.rtol = 1e-3
+solver.atol = 1e-6
+solution = solver.solve(model, t_eval)
 
 # plot
 plot = pybamm.QuickPlot(model, mesh, solution)
diff --git a/examples/scripts/custom_model.py b/examples/scripts/custom_model.py
new file mode 100644
index 000000000..ac6c5343e
--- /dev/null
+++ b/examples/scripts/custom_model.py
@@ -0,0 +1,69 @@
+#
+# Example showing how to create a custom lithium-ion model from submodels
+#
+
+import pybamm
+import numpy as np
+
+pybamm.set_logging_level("INFO")
+
+# load lithium-ion base model
+model = pybamm.lithium_ion.BaseModel(name="my li-ion model")
+
+# set choice of submodels
+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(
+    model.param, "Negative"
+)
+model.submodels["positive electrode"] = pybamm.electrode.ohm.LeadingOrder(
+    model.param, "Positive"
+)
+model.submodels["negative particle"] = pybamm.particle.fast.SingleParticle(
+    model.param, "Negative"
+)
+model.submodels["positive particle"] = pybamm.particle.fast.SingleParticle(
+    model.param, "Positive"
+)
+model.submodels[
+    "negative interface"
+] = pybamm.interface.lithium_ion.InverseButlerVolmer(model.param, "Negative")
+model.submodels[
+    "positive interface"
+] = pybamm.interface.lithium_ion.InverseButlerVolmer(model.param, "Positive")
+electrolyte = pybamm.electrolyte.stefan_maxwell
+model.submodels["electrolyte diffusion"] = electrolyte.diffusion.ConstantConcentration(
+    model.param
+)
+model.submodels["electrolyte conductivity"] = electrolyte.conductivity.LeadingOrder(
+    model.param
+)
+
+# build model
+model.build_model()
+
+# create geometry
+geometry = pybamm.Geometry("1D macro", "1D micro")
+
+# process model and geometry
+param = model.default_parameter_values
+param.process_model(model)
+param.process_geometry(geometry)
+
+# set mesh
+# Note: li-ion base model has defaults for mesh and var_pts
+mesh = pybamm.Mesh(geometry, model.default_submesh_types, model.default_var_pts)
+
+# discretise model
+# Note: li-ion base model has default spatial methods
+disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
+disc.process_model(model)
+
+# solve model
+t_eval = np.linspace(0, 0.2, 100)
+solver = pybamm.ScipySolver()
+solution = solver.solve(model, t_eval)
+
+# plot
+plot = pybamm.QuickPlot(model, mesh, solution)
+plot.dynamic_plot()
diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py
index c301255a7..41e587006 100644
--- a/pybamm/models/full_battery_models/base_battery_model.py
+++ b/pybamm/models/full_battery_models/base_battery_model.py
@@ -412,6 +412,14 @@ class BaseBatteryModel(pybamm.BaseModel):
             )
 
     def build_model(self):
+
+        # Check if already built
+        if self._built:
+            raise pybamm.ModelError(
+                """Model already built. If you are adding a new submodel, try using
+                `model.update` instead."""
+            )
+
         pybamm.logger.info("Building {}".format(self.name))
 
         # Get the fundamental variables
@@ -424,13 +432,37 @@ class BaseBatteryModel(pybamm.BaseModel):
             self.variables.update(submodel.get_fundamental_variables())
 
         # Get coupled variables
-        for submodel_name, submodel in self.submodels.items():
-            pybamm.logger.debug(
-                "Getting coupled variables for {} submodel ({})".format(
-                    submodel_name, self.name
-                )
-            )
-            self.variables.update(submodel.get_coupled_variables(self.variables))
+        # Note: pybamm will try to get the coupled variables for the submodels in the
+        # order they are set by the user. If this fails for a particular submodel,
+        # return to it later and try again. If setting coupled variables fails and
+        # there are no more submodels to try, raise an error.
+        submodels = list(self.submodels.keys())
+        while len(submodels) > 0:
+            for submodel_name, submodel in self.submodels.items():
+                if submodel_name in submodels:
+                    pybamm.logger.debug(
+                        "Getting coupled variables for {} submodel ({})".format(
+                            submodel_name, self.name
+                        )
+                    )
+                    try:
+                        self.variables.update(
+                            submodel.get_coupled_variables(self.variables)
+                        )
+                        submodels.remove(submodel_name)
+                    except KeyError as key:
+                        if len(submodels) == 1:
+                            # no more submodels to try
+                            raise pybamm.ModelError(
+                                """Submodel "{}" requires the variable {}, but it cannot be found.
+                                Check the selected submodels provide all of the required
+                                variables.""".format(
+                                    submodel_name, key
+                                )
+                            )
+                        else:
+                            # try setting coupled variables on next loop through
+                            pass
 
         # Set model equations
         for submodel_name, submodel in self.submodels.items():
@@ -468,6 +500,17 @@ class BaseBatteryModel(pybamm.BaseModel):
         pybamm.logger.debug("Setting SoC variables")
         self.set_soc_variables()
 
+        # Massive hack for consistent delta_phi = phi_s - phi_e with SPMe
+        # This needs to be corrected
+        if isinstance(self, pybamm.lithium_ion.SPMe):
+            for domain in ["Negative", "Positive"]:
+                phi_s = self.variables[domain + " electrode potential"]
+                phi_e = self.variables[domain + " electrolyte potential"]
+                delta_phi = phi_s - phi_e
+                s = self.submodels[domain.lower() + " interface"]
+                var = s._get_standard_surface_potential_difference_variables(delta_phi)
+                self.variables.update(var)
+
         self._built = True
 
     def set_thermal_submodel(self):
diff --git a/pybamm/models/full_battery_models/lead_acid/full.py b/pybamm/models/full_battery_models/lead_acid/full.py
index 2c57ad388..5e13d911b 100644
--- a/pybamm/models/full_battery_models/lead_acid/full.py
+++ b/pybamm/models/full_battery_models/lead_acid/full.py
@@ -9,6 +9,18 @@ class Full(BaseModel):
     """Porous electrode model for lead-acid, from [1]_, based on the Full
     model.
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [1] V Sulzer, SJ Chapman, CP Please, DA Howey, and CW Monroe. Faster lead-acid
@@ -19,7 +31,7 @@ class Full(BaseModel):
     **Extends:** :class:`pybamm.lead_acid.BaseModel`
     """
 
-    def __init__(self, options=None, name="Full model"):
+    def __init__(self, options=None, name="Full model", build=True):
         super().__init__(options, name)
 
         self.set_reactions()
@@ -32,7 +44,8 @@ class Full(BaseModel):
         self.set_side_reaction_submodels()
         self.set_current_collector_submodel()
 
-        self.build_model()
+        if build:
+            self.build_model()
 
     def set_porosity_submodel(self):
         self.submodels["porosity"] = pybamm.porosity.Full(self.param)
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 e4832937b..4f3e424d9 100644
--- a/pybamm/models/full_battery_models/lead_acid/higher_order.py
+++ b/pybamm/models/full_battery_models/lead_acid/higher_order.py
@@ -9,6 +9,18 @@ class BaseHigherOrderModel(BaseModel):
     """Base model for higher-order models for lead-acid, from [1]_.
     Uses leading-order model from :class:`pybamm.lead_acid.LOQS`
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [1] V Sulzer, SJ Chapman, CP Please, DA Howey, and CW Monroe. Faster lead-acid
@@ -19,7 +31,7 @@ class BaseHigherOrderModel(BaseModel):
     **Extends:** :class:`pybamm.lead_acid.BaseModel`
     """
 
-    def __init__(self, options=None, name="Composite model"):
+    def __init__(self, options=None, name="Composite model", build=True):
         super().__init__(options, name)
 
         self.set_leading_order_model()
@@ -40,7 +52,8 @@ class BaseHigherOrderModel(BaseModel):
         self.set_thermal_submodel()
         self.set_current_collector_submodel()
 
-        self.build_model()
+        if build:
+            self.build_model()
 
     def set_current_collector_submodel(self):
         cc = pybamm.current_collector
@@ -172,11 +185,23 @@ class FOQS(BaseHigherOrderModel):
     """First-order quasi-static model for lead-acid, from [1]_.
     Uses leading-order model from :class:`pybamm.lead_acid.LOQS`
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     **Extends:** :class:`pybamm.lead_acid.BaseHigherOrderModel`
     """
 
-    def __init__(self, options=None, name="FOQS model"):
-        super().__init__(options, name)
+    def __init__(self, options=None, name="FOQS model", build=True):
+        super().__init__(options, name, build=build)
 
     def set_electrolyte_diffusion_submodel(self):
         self.submodels[
@@ -207,8 +232,8 @@ class Composite(BaseHigherOrderModel):
     **Extends:** :class:`pybamm.lead_acid.BaseHigherOrderModel`
     """
 
-    def __init__(self, options=None, name="Composite model"):
-        super().__init__(options, name)
+    def __init__(self, options=None, name="Composite model", build=True):
+        super().__init__(options, name, build=build)
 
     def set_electrolyte_diffusion_submodel(self):
         self.submodels[
@@ -235,6 +260,18 @@ class CompositeExtended(BaseHigherOrderModel):
     """Extended composite model for lead-acid, from [2]_.
     Uses leading-order model from :class:`pybamm.lead_acid.LOQS`
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [2] V Sulzer. Mathematical modelling of lead-acid batteries. PhD thesis,
@@ -244,8 +281,8 @@ class CompositeExtended(BaseHigherOrderModel):
     **Extends:** :class:`pybamm.lead_acid.BaseHigherOrderModel`
     """
 
-    def __init__(self, options=None, name="Extended composite model"):
-        super().__init__(options, name)
+    def __init__(self, options=None, name="Extended composite model", build=True):
+        super().__init__(options, name, build=build)
 
     def set_electrolyte_diffusion_submodel(self):
         self.submodels[
diff --git a/pybamm/models/full_battery_models/lead_acid/loqs.py b/pybamm/models/full_battery_models/lead_acid/loqs.py
index b9af2a904..8814d3268 100644
--- a/pybamm/models/full_battery_models/lead_acid/loqs.py
+++ b/pybamm/models/full_battery_models/lead_acid/loqs.py
@@ -8,6 +8,18 @@ from .base_lead_acid_model import BaseModel
 class LOQS(BaseModel):
     """Leading-Order Quasi-Static model for lead-acid, from [1]_.
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [1] V Sulzer, SJ Chapman, CP Please, DA Howey, and CW Monroe. Faster lead-acid
@@ -18,7 +30,7 @@ class LOQS(BaseModel):
     **Extends:** :class:`pybamm.lead_acid.BaseModel`
     """
 
-    def __init__(self, options=None, name="LOQS model"):
+    def __init__(self, options=None, name="LOQS model", build=True):
         super().__init__(options, name)
 
         self.set_reactions()
@@ -32,7 +44,8 @@ class LOQS(BaseModel):
         self.set_side_reaction_submodels()
         self.set_current_collector_submodel()
 
-        self.build_model()
+        if build:
+            self.build_model()
 
         if self.options["dimensionality"] == 0:
             self.use_jacobian = False
diff --git a/pybamm/models/full_battery_models/lithium_ion/dfn.py b/pybamm/models/full_battery_models/lithium_ion/dfn.py
index d5800361a..c448c048b 100644
--- a/pybamm/models/full_battery_models/lithium_ion/dfn.py
+++ b/pybamm/models/full_battery_models/lithium_ion/dfn.py
@@ -8,6 +8,18 @@ from .base_lithium_ion_model import BaseModel
 class DFN(BaseModel):
     """Doyle-Fuller-Newman (DFN) model of a lithium-ion battery, from [1]_.
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [1] SG Marquis, V Sulzer, R Timms, CP Please and SJ Chapman. “An asymptotic
@@ -18,7 +30,7 @@ class DFN(BaseModel):
     **Extends:** :class:`pybamm.lithium_ion.BaseModel`
     """
 
-    def __init__(self, options=None, name="Doyle-Fuller-Newman model"):
+    def __init__(self, options=None, name="Doyle-Fuller-Newman model", build=True):
         super().__init__(options, name)
 
         self.set_reactions()
@@ -31,7 +43,8 @@ class DFN(BaseModel):
         self.set_thermal_submodel()
         self.set_current_collector_submodel()
 
-        self.build_model()
+        if build:
+            self.build_model()
 
     def set_porosity_submodel(self):
 
diff --git a/pybamm/models/full_battery_models/lithium_ion/spm.py b/pybamm/models/full_battery_models/lithium_ion/spm.py
index a0ff131ed..c8a954912 100644
--- a/pybamm/models/full_battery_models/lithium_ion/spm.py
+++ b/pybamm/models/full_battery_models/lithium_ion/spm.py
@@ -8,6 +8,18 @@ from .base_lithium_ion_model import BaseModel
 class SPM(BaseModel):
     """Single Particle Model (SPM) of a lithium-ion battery, from [1]_.
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [1] SG Marquis, V Sulzer, R Timms, CP Please and SJ Chapman. “An asymptotic
@@ -17,7 +29,7 @@ class SPM(BaseModel):
     **Extends:** :class:`pybamm.lithium_ion.BaseModel`
     """
 
-    def __init__(self, options=None, name="Single Particle Model"):
+    def __init__(self, options=None, name="Single Particle Model", build=True):
         super().__init__(options, name)
 
         self.set_porosity_submodel()
@@ -30,7 +42,8 @@ class SPM(BaseModel):
         self.set_thermal_submodel()
         self.set_current_collector_submodel()
 
-        self.build_model()
+        if build:
+            self.build_model()
 
     def set_porosity_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 f3a753700..e5e616759 100644
--- a/pybamm/models/full_battery_models/lithium_ion/spme.py
+++ b/pybamm/models/full_battery_models/lithium_ion/spme.py
@@ -9,6 +9,18 @@ class SPMe(BaseModel):
     """Single Particle Model with Electrolyte (SPMe) of a lithium-ion battery, from
     [1]_.
 
+    Parameters
+    ----------
+    options : dict, optional
+        A dictionary of options to be passed to the model.
+    name : str, optional
+        The name of the model.
+    build :  bool, optional
+        Whether to build the model on instantiation. Default is True. Setting this
+        option to False allows users to change any number of the submodels before
+        building the complete model (submodels cannot be changed after the model is
+        built).
+
     References
     ----------
     .. [1] SG Marquis, V Sulzer, R Timms, CP Please and SJ Chapman. “An asymptotic
@@ -18,7 +30,9 @@ class SPMe(BaseModel):
     **Extends:** :class:`pybamm.lithium_ion.BaseModel`
     """
 
-    def __init__(self, options=None, name="Single Particle Model with electrolyte"):
+    def __init__(
+        self, options=None, name="Single Particle Model with electrolyte", build=True
+    ):
         super().__init__(options, name)
 
         self.set_reactions()
@@ -32,17 +46,8 @@ class SPMe(BaseModel):
         self.set_thermal_submodel()
         self.set_current_collector_submodel()
 
-        self.build_model()
-
-        # Massive hack for consistent delta_phi = phi_s - phi_e
-        # This needs to be corrected
-        for domain in ["Negative", "Positive"]:
-            phi_s = self.variables[domain + " electrode potential"]
-            phi_e = self.variables[domain + " electrolyte potential"]
-            delta_phi = phi_s - phi_e
-            s = self.submodels[domain.lower() + " interface"]
-            var = s._get_standard_surface_potential_difference_variables(delta_phi)
-            self.variables.update(var)
+        if build:
+            self.build_model()
 
     def set_porosity_submodel(self):
 


Test patch:
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 44ec418b9..848de0330 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
@@ -121,6 +121,19 @@ class TestBaseBatteryModel(unittest.TestCase):
         with self.assertRaisesRegex(pybamm.OptionError, "particle model"):
             pybamm.BaseBatteryModel({"particle": "bad particle"})
 
+    def test_build_twice(self):
+        model = pybamm.lithium_ion.SPM()  # need to pick a model to set vars and build
+        with self.assertRaisesRegex(pybamm.ModelError, "Model already built"):
+            model.build_model()
+
+    def test_get_coupled_variables(self):
+        model = pybamm.lithium_ion.BaseModel()
+        model.submodels["current collector"] = pybamm.current_collector.Uniform(
+            model.param
+        )
+        with self.assertRaisesRegex(pybamm.ModelError, "Submodel"):
+            model.build_model()
+
 
 if __name__ == "__main__":
     print("Add -v for more debug output")
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 c5c44508c..d6c490071 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
@@ -11,6 +11,11 @@ class TestLeadAcidLOQS(unittest.TestCase):
         model = pybamm.lead_acid.LOQS(options)
         model.check_well_posedness()
 
+        # Test build after init
+        model = pybamm.lead_acid.LOQS(build=False)
+        model.build_model()
+        model.check_well_posedness()
+
     def test_well_posed_with_convection(self):
         options = {"thermal": "isothermal", "convection": True}
         model = pybamm.lead_acid.LOQS(options)
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 84bdea31e..98723a572 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
@@ -11,6 +11,11 @@ class TestSPM(unittest.TestCase):
         model = pybamm.lithium_ion.SPM(options)
         model.check_well_posedness()
 
+        # Test build after init
+        model = pybamm.lithium_ion.SPM(build=False)
+        model.build_model()
+        model.check_well_posedness()
+
     def test_default_geometry(self):
         options = {"thermal": "isothermal"}
         model = pybamm.lithium_ion.SPM(options)