Skip to content

Commit

Permalink
Merge branch 'develop' into issue-569-create-model-notebooks
Browse files Browse the repository at this point in the history
  • Loading branch information
rtimms committed Mar 11, 2020
2 parents 4ffd43b + eae19f2 commit b8ebc9c
Show file tree
Hide file tree
Showing 29 changed files with 421 additions and 157 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
## Features

- Added additional notebooks showing how to create and compare models ([#877](https://github.com/pybamm-team/PyBaMM/pull/877))
- Added `Minimum`, `Maximum` and `Sign` operators ([#876](https://github.com/pybamm-team/PyBaMM/pull/876))
- Added a search feature to `FuzzyDict` ([#875](https://github.com/pybamm-team/PyBaMM/pull/875))
- Add ambient temperature as a function of time ([#872](https://github.com/pybamm-team/PyBaMM/pull/872))
- Added `CasadiAlgebraicSolver` for solving algebraic systems with casadi ([#868](https://github.com/pybamm-team/PyBaMM/pull/868))
- Added `CasadiAlgebraicSolver` for solving algebraic systems with CasADi ([#868](https://github.com/pybamm-team/PyBaMM/pull/868))

## Bug fixes

- Some bug fixes to generalize specifying models that aren't battery models, see [#846](https://github.com/pybamm-team/PyBaMM/issues/846)
- Reformatted interface submodels to be more readable ([#866](https://github.com/pybamm-team/PyBaMM/pull/866))
- Removed double-counted "number of electrodes connected in parallel" from simulation ([#864](https://github.com/pybamm-team/PyBaMM/pull/864))

Expand Down
16 changes: 16 additions & 0 deletions docs/source/expression_tree/binary_operator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,20 @@ Binary Operators
.. autoclass:: pybamm.Heaviside
:members:

.. autoclass:: pybamm.EqualHeaviside
:members:

.. autoclass:: pybamm.NotEqualHeaviside
:members:

.. autoclass:: pybamm.Minimum
:members:

.. autoclass:: pybamm.Maximum
:members:

.. autofunction:: pybamm.minimum

.. autofunction:: pybamm.maximum

.. autofunction:: pybamm.source
11 changes: 11 additions & 0 deletions docs/source/expression_tree/unary_operator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Unary Operators
.. autoclass:: pybamm.AbsoluteValue
:members:

.. autoclass:: pybamm.Sign
:members:

.. autoclass:: pybamm.Index
:members:

Expand Down Expand Up @@ -67,4 +70,12 @@ Unary Operators

.. autofunction:: pybamm.x_average

.. autofunction:: pybamm.r_average

.. autofunction:: pybamm.z_average

.. autofunction:: pybamm.yz_average

.. autofunction:: pybamm.boundary_value

.. autofunction:: pybamm.sign
59 changes: 7 additions & 52 deletions pybamm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,70 +65,25 @@ def version(formatted=False):
#
# Classes for the Expression Tree
#
from .expression_tree.symbol import (
Symbol,
domain_size,
create_object_of_size,
evaluate_for_shape_using_domain,
)
from .expression_tree.binary_operators import (
is_scalar_zero,
is_matrix_zero,
BinaryOperator,
Addition,
Power,
Subtraction,
Multiplication,
MatrixMultiplication,
Division,
Inner,
inner,
Heaviside,
source,
)
from .expression_tree.concatenations import (
Concatenation,
NumpyConcatenation,
DomainConcatenation,
SparseStack,
)
from .expression_tree.symbol import *
from .expression_tree.binary_operators import *
from .expression_tree.concatenations import *
from .expression_tree.array import Array
from .expression_tree.matrix import Matrix
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,
SecondaryBroadcast,
FullBroadcast,
ones_like,
)
from .expression_tree.broadcasts import *
from .expression_tree.scalar import Scalar
from .expression_tree.variable import Variable, ExternalVariable
from .expression_tree.independent_variable import (
IndependentVariable,
Time,
SpatialVariable,
)
from .expression_tree.independent_variable import *
from .expression_tree.independent_variable import t
from .expression_tree.vector import Vector
from .expression_tree.state_vector import StateVector

from .expression_tree.exceptions import (
DomainError,
OptionError,
ModelError,
SolverError,
SolverWarning,
ShapeError,
ModelWarning,
UndefinedOperationError,
GeometryError,
InputError,
)
from .expression_tree.exceptions import *

# Operations
from .expression_tree.operations.simplify import (
Expand Down Expand Up @@ -193,7 +148,7 @@ def version(formatted=False):
Geometry2DCurrentCollector,
)

from .expression_tree.independent_variable import KNOWN_SPATIAL_VARS, KNOWN_COORD_SYS
from .expression_tree.independent_variable import KNOWN_COORD_SYS
from .geometry import standard_spatial_vars

#
Expand Down
20 changes: 18 additions & 2 deletions pybamm/discretisations/discretisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ def set_variable_slices(self, variables):
y_slices[variable.id].append(slice(start, end))
start = end

self.y_slices = y_slices
# Convert y_slices back to normal dictionary
self.y_slices = dict(y_slices)

# reset discretised_symbols
self._discretised_symbols = {}
Expand Down Expand Up @@ -885,8 +886,23 @@ def _process_symbol(self, symbol):
return out

else:
# add a try except block for a more informative error if a variable
# can't be found. This should usually be caught earlier by
# model.check_well_posedness, but won't be if debug_mode is False
try:
y_slices = self.y_slices[symbol.id]
except KeyError:
raise pybamm.ModelError(
"""
No key set for variable '{}'. Make sure it is included in either
model.rhs, model.algebraic, or model.external_variables in an
unmodified form (e.g. not Broadcasted)
""".format(
symbol.name
)
)
return pybamm.StateVector(
*self.y_slices[symbol.id],
*y_slices,
domain=symbol.domain,
auxiliary_domains=symbol.auxiliary_domains
)
Expand Down
123 changes: 102 additions & 21 deletions pybamm/expression_tree/binary_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,23 +657,10 @@ class Heaviside(BinaryOperator):
**Extends:** :class:`BinaryOperator`
"""

def __init__(self, left, right, equal):
def __init__(self, name, left, right):
""" See :meth:`pybamm.BinaryOperator.__init__()`. """
# 'equal' determines whether to return 1 or 0 when left = right
self.equal = equal
if equal is True:
name = "<="
else:
name = "<"
super().__init__(name, left, right)

def __str__(self):
""" See :meth:`pybamm.Symbol.__str__()`. """
if self.equal is True:
return "{!s} <= {!s}".format(self.left, self.right)
else:
return "{!s} < {!s}".format(self.left, self.right)

def diff(self, variable):
""" See :meth:`pybamm.Symbol.diff()`. """
# Heaviside should always be multiplied by something else so hopefully don't
Expand All @@ -686,18 +673,112 @@ def _binary_jac(self, left_jac, right_jac):
# need to worry about shape
return pybamm.Scalar(0)


class EqualHeaviside(Heaviside):
"A heaviside function with equality (return 1 when left = right)"

def __init__(self, left, right):
""" See :meth:`pybamm.BinaryOperator.__init__()`. """
super().__init__("<=", left, right)

def __str__(self):
""" See :meth:`pybamm.Symbol.__str__()`. """
return "{!s} <= {!s}".format(self.left, self.right)

def _binary_evaluate(self, left, right):
""" See :meth:`pybamm.BinaryOperator._binary_evaluate()`. """
# don't raise RuntimeWarning for NaNs
with np.errstate(invalid="ignore"):
if self.equal is True:
return left <= right
else:
return left < right
return left <= right

def _binary_new_copy(self, left, right):
""" See :meth:`pybamm.BinaryOperator._binary_new_copy()`. """
return Heaviside(left, right, self.equal)

class NotEqualHeaviside(Heaviside):
"A heaviside function without equality (return 0 when left = right)"

def __init__(self, left, right):
super().__init__("<", left, right)

def __str__(self):
""" See :meth:`pybamm.Symbol.__str__()`. """
return "{!s} < {!s}".format(self.left, self.right)

def _binary_evaluate(self, left, right):
""" See :meth:`pybamm.BinaryOperator._binary_evaluate()`. """
# don't raise RuntimeWarning for NaNs
with np.errstate(invalid="ignore"):
return left < right


class Minimum(BinaryOperator):
" Returns the smaller of two objects "

def __init__(self, left, right):
super().__init__("minimum", left, right)

def __str__(self):
""" See :meth:`pybamm.Symbol.__str__()`. """
return "minimum({!s}, {!s})".format(self.left, self.right)

def _diff(self, variable):
""" See :meth:`pybamm.Symbol._diff()`. """
left, right = self.orphans
return (left <= right) * left.diff(variable) + (left > right) * right.diff(
variable
)

def _binary_jac(self, left_jac, right_jac):
""" See :meth:`pybamm.BinaryOperator._binary_jac()`. """
left, right = self.orphans
return (left <= right) * left_jac + (left > right) * right_jac

def _binary_evaluate(self, left, right):
""" See :meth:`pybamm.BinaryOperator._binary_evaluate()`. """
# don't raise RuntimeWarning for NaNs
return np.minimum(left, right)


class Maximum(BinaryOperator):
" Returns the smaller of two objects "

def __init__(self, left, right):
super().__init__("maximum", left, right)

def __str__(self):
""" See :meth:`pybamm.Symbol.__str__()`. """
return "maximum({!s}, {!s})".format(self.left, self.right)

def _diff(self, variable):
""" See :meth:`pybamm.Symbol._diff()`. """
left, right = self.orphans
return (left >= right) * left.diff(variable) + (left < right) * right.diff(
variable
)

def _binary_jac(self, left_jac, right_jac):
""" See :meth:`pybamm.BinaryOperator._binary_jac()`. """
left, right = self.orphans
return (left >= right) * left_jac + (left < right) * right_jac

def _binary_evaluate(self, left, right):
""" See :meth:`pybamm.BinaryOperator._binary_evaluate()`. """
# don't raise RuntimeWarning for NaNs
return np.maximum(left, right)


def minimum(left, right):
"""
Returns the smaller of two objects. Not to be confused with :meth:`pybamm.min`,
which returns min function of child.
"""
return pybamm.simplify_if_constant(Minimum(left, right), keep_domains=True)


def maximum(left, right):
"""
Returns the larger of two objects. Not to be confused with :meth:`pybamm.max`,
which returns max function of child.
"""
return pybamm.simplify_if_constant(Maximum(left, right), keep_domains=True)


def source(left, right, boundary=False):
Expand Down
8 changes: 0 additions & 8 deletions pybamm/expression_tree/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,6 @@ class ModelWarning(UserWarning):
pass


class UndefinedOperationError(Exception):
"""
Undefined operation: Raised when a mathematical operation is not well-defined
"""

pass


class InputError(Exception):
"""
An external variable has been input incorrectly into PyBaMM
Expand Down
10 changes: 8 additions & 2 deletions pybamm/expression_tree/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,18 @@ def log10(child):


def max(child):
" Returns max function of child. "
"""
Returns max function of child. Not to be confused with :meth:`pybamm.maximum`, which
returns the larger of two objects.
"""
return pybamm.simplify_if_constant(Function(np.max, child), keep_domains=True)


def min(child):
" Returns min function of child. "
"""
Returns min function of child. Not to be confused with :meth:`pybamm.minimum`, which
returns the smaller of two objects.
"""
return pybamm.simplify_if_constant(Function(np.min, child), keep_domains=True)


Expand Down
5 changes: 0 additions & 5 deletions pybamm/expression_tree/independent_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
import pybamm

KNOWN_COORD_SYS = ["cartesian", "spherical polar"]
KNOWN_SPATIAL_VARS = ["x", "y", "z", "r", "x_n", "x_s", "x_p", "r_n", "r_p"]
KNOWN_SPATIAL_VARS_EXTENDED = [v + "_edge" for v in KNOWN_SPATIAL_VARS]
KNOWN_SPATIAL_VARS.extend(KNOWN_SPATIAL_VARS_EXTENDED)


class IndependentVariable(pybamm.Symbol):
Expand Down Expand Up @@ -84,8 +81,6 @@ def __init__(self, name, domain=None, auxiliary_domains=None, coord_sys=None):
super().__init__(name, domain=domain, auxiliary_domains=auxiliary_domains)
domain = self.domain

if name not in KNOWN_SPATIAL_VARS:
raise ValueError(f"name must be in {KNOWN_SPATIAL_VARS} but is '{name}'")
if domain == []:
raise ValueError("domain must be provided")

Expand Down
4 changes: 4 additions & 0 deletions pybamm/expression_tree/operations/convert_to_casadi.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ def _convert(self, symbol, t=None, y=None, u=None):
# process children
converted_left = self.convert(left, t, y, u)
converted_right = self.convert(right, t, y, u)
if isinstance(symbol, pybamm.Minimum):
return casadi.fmin(converted_left, converted_right)
if isinstance(symbol, pybamm.Maximum):
return casadi.fmax(converted_left, converted_right)
# _binary_evaluate defined in derived classes for specific rules
return symbol._binary_evaluate(converted_left, converted_right)

Expand Down
Loading

0 comments on commit b8ebc9c

Please sign in to comment.