#
# Variable class
#
import numpy as np
import sympy
import pybamm
class VariableBase(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:`pybamm.StateVector` 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': ...,
'quaternary': ...}). 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'
domains : dict
A dictionary equivalent to {'primary': domain, auxiliary_domains}. Either
'domain' and 'auxiliary_domains', or just 'domains', should be provided
(not both). In future, the 'domain' and 'auxiliary_domains' arguments may be
deprecated.
bounds : tuple, optional
Physical bounds on the variable
print_name : str, optional
The name to use for printing. Default is None, in which case self.name is used.
scale : float or :class:`pybamm.Symbol`, optional
The scale of the variable, used for scaling the model when solving. The state
vector representing this variable will be multiplied by this scale.
Default is 1.
reference : float or :class:`pybamm.Symbol`, optional
The reference value of the variable, used for scaling the model when solving.
This value will be added to the state vector representing this variable.
Default is 0.
*Extends:* :class:`Symbol`
"""
def __init__(
self,
name,
domain=None,
auxiliary_domains=None,
domains=None,
bounds=None,
print_name=None,
scale=1,
reference=0,
):
self._scale = scale
self._reference = reference
super().__init__(
name,
domain=domain,
auxiliary_domains=auxiliary_domains,
domains=domains,
)
if bounds is None:
bounds = (-np.inf, np.inf)
else:
if bounds[0] >= bounds[1]:
raise ValueError(
"Invalid bounds {}. ".format(bounds)
+ "Lower bound should be strictly less than upper bound."
)
self.bounds = bounds
self.print_name = print_name
def set_id(self):
self._id = hash(
(self.__class__, self.name, self.scale, self.reference)
+ tuple([(k, tuple(v)) for k, v in self.domains.items() if v != []])
)
def create_copy(self):
"""See :meth:`pybamm.Symbol.new_copy()`."""
return self.__class__(
self.name,
domains=self.domains,
bounds=self.bounds,
print_name=self._raw_print_name,
scale=self.scale,
reference=self.reference,
)
def _evaluate_for_shape(self):
"""See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()`"""
return pybamm.evaluate_for_shape_using_domain(self.domains)
def to_equation(self):
"""Convert the node and its subtree into a SymPy equation."""
if self.print_name is not None:
return sympy.Symbol(self.print_name)
else:
return self.name
[docs]class Variable(VariableBase):
"""
A node in the expression tree represending a dependent variable.
This node will be discretised by :class:`.Discretisation` and converted
to a :class:`pybamm.StateVector` node.
Parameters
----------
name : str
name of the node
domain : iterable of str, optional
list of domains that this variable is valid over
auxiliary_domains : dict, optional
dictionary of auxiliary domains ({'secondary': ..., 'tertiary': ...,
'quaternary': ...}). 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'
domains : dict
A dictionary equivalent to {'primary': domain, auxiliary_domains}. Either
'domain' and 'auxiliary_domains', or just 'domains', should be provided
(not both). In future, the 'domain' and 'auxiliary_domains' arguments may be
deprecated.
bounds : tuple, optional
Physical bounds on the variable
print_name : str, optional
The name to use for printing. Default is None, in which case self.name is used.
scale : float or :class:`pybamm.Symbol`, optional
The scale of the variable, used for scaling the model when solving. The state
vector representing this variable will be multiplied by this scale.
Default is 1.
reference : float or :class:`pybamm.Symbol`, optional
The reference value of the variable, used for scaling the model when solving.
This value will be added to the state vector representing this variable.
Default is 0.
*Extends:* :class:`VariableBase`
"""
[docs] def diff(self, variable):
if variable == self:
return pybamm.Scalar(1)
elif variable == pybamm.t:
# reference gets differentiated out
return pybamm.VariableDot(
self.name + "'", domains=self.domains, scale=self.scale
)
else:
return pybamm.Scalar(0)
[docs]class VariableDot(VariableBase):
"""
A node in the expression tree represending the time derviative of a dependent
variable
This node will be discretised by :class:`.Discretisation` and converted
to a :class:`pybamm.StateVectorDot` 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': ...,
'quaternary': ...}). 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'
domains : dict
A dictionary equivalent to {'primary': domain, auxiliary_domains}. Either
'domain' and 'auxiliary_domains', or just 'domains', should be provided
(not both). In future, the 'domain' and 'auxiliary_domains' arguments may be
deprecated.
bounds : tuple, optional
Physical bounds on the variable. Included for compatibility with `VariableBase`,
but ignored.
print_name : str, optional
The name to use for printing. Default is None, in which case self.name is used.
scale : float or :class:`pybamm.Symbol`, optional
The scale of the variable, used for scaling the model when solving. The state
vector representing this variable will be multiplied by this scale.
Default is 1.
reference : float or :class:`pybamm.Symbol`, optional
The reference value of the variable, used for scaling the model when solving.
This value will be added to the state vector representing this variable.
Default is 0.
*Extends:* :class:`VariableBase`
"""
[docs] def get_variable(self):
"""
return a :class:`.Variable` corresponding to this VariableDot
Note: Variable._jac adds a dash to the name of the corresponding VariableDot, so
we remove this here
"""
return Variable(self.name[:-1], domains=self.domains, scale=self.scale)
[docs] def diff(self, variable):
if variable == self:
return pybamm.Scalar(1)
elif variable == pybamm.t:
raise pybamm.ModelError("cannot take second time derivative of a Variable")
else:
return pybamm.Scalar(0)