Exemplo n.º 1
0
    def test_basic_symbols(self):
        a = pybamm.Scalar(1)
        unpacker = pybamm.SymbolUnpacker(pybamm.Scalar)

        unpacked = unpacker.unpack_symbol(a)
        self.assertEqual(unpacked, {a.id: a})

        b = pybamm.Parameter("b")
        unpacker_param = pybamm.SymbolUnpacker(pybamm.Parameter)

        unpacked = unpacker_param.unpack_symbol(a)
        self.assertEqual(unpacked, {})

        unpacked = unpacker_param.unpack_symbol(b)
        self.assertEqual(unpacked, {b.id: b})
Exemplo n.º 2
0
    def test_binary(self):
        a = pybamm.Scalar(1)
        b = pybamm.Parameter("b")

        unpacker = pybamm.SymbolUnpacker(pybamm.Scalar)
        unpacked = unpacker.unpack_symbol(a + b)
        # Can't check dictionary directly so check ids
        self.assertEqual(unpacked.keys(), {a.id: a}.keys())
        self.assertEqual(unpacked[a.id].id, a.id)

        unpacker_param = pybamm.SymbolUnpacker(pybamm.Parameter)
        unpacked = unpacker_param.unpack_symbol(a + b)
        # Can't check dictionary directly so check ids
        self.assertEqual(unpacked.keys(), {b.id: b}.keys())
        self.assertEqual(unpacked[b.id].id, b.id)
Exemplo n.º 3
0
    def check_and_convert_equations(self, equations):
        """
        Convert any scalar equations in dict to 'pybamm.Scalar'
        and check that domains are consistent
        """
        # Convert any numbers to a pybamm.Scalar
        for var, eqn in equations.items():
            if isinstance(eqn, numbers.Number):
                equations[var] = pybamm.Scalar(eqn)

        if not all([
                variable.domain == equation.domain or variable.domain == []
                or equation.domain == []
                for variable, equation in equations.items()
        ]):
            raise pybamm.DomainError(
                "variable and equation in '{}' must have the same domain".
                format(self.name))

        # For initial conditions, check that the equation doesn't contain any
        # Variable objects
        # skip this if the dictionary has no "name" attribute (which will be the case
        # after pickling)
        if hasattr(self, "name") and self.name == "initial_conditions":
            for var, eqn in equations.items():
                if eqn.has_symbol_of_classes(pybamm.Variable):
                    unpacker = pybamm.SymbolUnpacker(pybamm.Variable)
                    variable_in_equation = list(
                        unpacker.unpack_symbol(eqn).values())[0]
                    raise TypeError(
                        "Initial conditions cannot contain 'Variable' objects, "
                        "but '{!r}' found in initial conditions for '{}'".
                        format(variable_in_equation, var))

        return equations
Exemplo n.º 4
0
    def check_variables(self):
        # Create list of all Variable nodes that appear in the model's list of variables
        unpacker = pybamm.SymbolUnpacker(pybamm.Variable)
        all_vars = unpacker.unpack_list_of_symbols(self.variables.values())

        var_ids_in_keys = set()

        model_and_external_variables = (list(self.rhs.keys()) +
                                        list(self.algebraic.keys()) +
                                        self.external_variables)

        for var in model_and_external_variables:
            if isinstance(var, pybamm.Variable):
                var_ids_in_keys.add(var.id)
            # Key can be a concatenation
            elif isinstance(var, pybamm.Concatenation):
                var_ids_in_keys.update([child.id for child in var.children])

        for var_id, var in all_vars.items():
            if var_id not in var_ids_in_keys:
                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(var))
Exemplo n.º 5
0
 def check_algebraic_equations(self, post_discretisation):
     """
     Check that the algebraic equations are well-posed.
     Before discretisation, each algebraic equation key must appear in the equation
     After discretisation, there must be at least one StateVector in each algebraic
     equation
     """
     vars_in_bcs = set()
     unpacker = pybamm.SymbolUnpacker(pybamm.Variable)
     for side_eqn in self.boundary_conditions.values():
         all_vars = unpacker.unpack_list_of_symbols(
             [eqn for eqn, _ in side_eqn.values()])
         vars_in_bcs.update(all_vars.keys())
     if not post_discretisation:
         # After the model has been defined, each algebraic equation key should
         # 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()) 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"
                 )
     else:
         # variables in keys don't get discretised so they will no longer match
         # with the state vectors in the algebraic equations. Instead, we check
         # that each algebraic equation contains some StateVector
         for eqn in self.algebraic.values():
             if not eqn.has_symbol_of_classes(pybamm.StateVector):
                 raise pybamm.ModelError(
                     "each algebraic equation must contain at least one StateVector"
                 )
Exemplo n.º 6
0
    def shape(self):
        """
        Shape of an object, found by evaluating it with appropriate t and y.
        """
        try:
            return self._saved_shape
        except AttributeError:
            # Default behaviour is to try to evaluate the object directly
            # Try with some large y, to avoid having to unpack (slow)
            try:
                y = np.nan * np.ones((1000, 1))
                evaluated_self = self.evaluate(0, y, y, inputs="shape test")
            # If that fails, fall back to calculating how big y should really be
            except ValueError:
                unpacker = pybamm.SymbolUnpacker(pybamm.StateVector)
                state_vectors_in_node = unpacker.unpack_symbol(self).values()
                min_y_size = max(
                    max(
                        len(x._evaluation_array)
                        for x in state_vectors_in_node), 1)
                # Pick a y that won't cause RuntimeWarnings
                y = np.nan * np.ones((min_y_size, 1))
                evaluated_self = self.evaluate(0, y, y, inputs="shape test")

            # Return shape of evaluated object
            if isinstance(evaluated_self, numbers.Number):
                self._saved_shape = ()
            else:
                self._saved_shape = evaluated_self.shape

        return self._saved_shape
Exemplo n.º 7
0
    def shape(self):
        """
        Shape of an object, found by evaluating it with appropriate t and y.
        """
        # Default behaviour is to try to evaluate the object directly
        # Try with some large y, to avoid having to unpack (slow)
        try:
            y = np.linspace(0.1, 0.9, int(1e4))
            evaluated_self = self.evaluate(0, y, y, inputs="shape test")
        # If that fails, fall back to calculating how big y should really be
        except ValueError:
            unpacker = pybamm.SymbolUnpacker(pybamm.StateVector)
            state_vectors_in_node = unpacker.unpack_symbol(self).values()
            if state_vectors_in_node == []:
                y = None
            else:
                min_y_size = max(
                    len(x._evaluation_array) for x in state_vectors_in_node)
                # Pick a y that won't cause RuntimeWarnings
                y = np.linspace(0.1, 0.9, min_y_size)
            evaluated_self = self.evaluate(0, y, y, inputs="shape test")

        # Return shape of evaluated object
        if isinstance(evaluated_self, numbers.Number):
            return ()
        else:
            return evaluated_self.shape
Exemplo n.º 8
0
 def _find_input_parameters(self):
     "Find all the input parameters in the model"
     unpacker = pybamm.SymbolUnpacker(pybamm.InputParameter)
     all_input_parameters = unpacker.unpack_list_of_symbols(
         list(self.rhs.values()) + list(self.algebraic.values()) +
         list(self.initial_conditions.values()) +
         list(self.variables.values()) +
         [event.expression for event in self.events])
     return list(all_input_parameters.values())
Exemplo n.º 9
0
    def test_unpack_list_of_symbols(self):
        a = pybamm.Scalar(1)
        b = pybamm.Parameter("b")
        c = pybamm.Parameter("c")

        unpacker = pybamm.SymbolUnpacker(pybamm.Parameter)
        unpacked = unpacker.unpack_list_of_symbols([a + b, a - c, b + c])
        # Can't check dictionary directly so check ids
        self.assertEqual(unpacked.keys(), {b.id: b, c.id: c}.keys())
        self.assertEqual(unpacked[b.id].id, b.id)
        self.assertEqual(unpacked[c.id].id, c.id)
Exemplo n.º 10
0
 def _find_symbols(self, typ):
     """Find all the instances of `typ` in the model"""
     unpacker = pybamm.SymbolUnpacker(typ)
     all_input_parameters = unpacker.unpack_list_of_symbols(
         list(self.rhs.values()) + list(self.algebraic.values()) +
         list(self.initial_conditions.values()) + [
             x[side][0] for x in self.boundary_conditions.values()
             for side in x.keys()
         ] + list(self.variables.values()) +
         [event.expression for event in self.events] + [self.timescale] +
         list(self.length_scales.values()))
     return list(all_input_parameters.values())
Exemplo n.º 11
0
    def _find_parameters(self):
        "Find all the parameters in the model"
        unpacker = pybamm.SymbolUnpacker(
            (pybamm.Parameter, pybamm.InputParameter))

        def NestedDictValues(d):
            "Get all the values from a nested dict"
            for v in d.values():
                if isinstance(v, dict):
                    yield from NestedDictValues(v)
                else:
                    yield v

        all_parameters = unpacker.unpack_list_of_symbols(
            list(NestedDictValues(self)))
        return list(all_parameters.values())
Exemplo n.º 12
0
    def check_well_determined(self, post_discretisation):
        """ Check that the model is not under- or over-determined. """
        # Equations (differential and algebraic)
        # Get all the variables from differential and algebraic equations
        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, 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
        unpacker = pybamm.SymbolUnpacker((pybamm.Variable, pybamm.VariableDot))

        for var, eqn in self.rhs.items():
            # Find all variables and variabledot objects
            vars_in_rhs_keys_dict = unpacker.unpack_symbol(var)
            vars_in_eqns_dict = unpacker.unpack_symbol(eqn)

            # Store ids only
            # Look only for Variable (not VariableDot) in rhs keys
            vars_in_rhs_keys.update([
                var_id for var_id, var in vars_in_rhs_keys_dict.items()
                if isinstance(var, pybamm.Variable)
            ])
            vars_in_eqns.update(vars_in_eqns_dict.keys())
        for var, eqn in self.algebraic.items():
            # Find all variables and variabledot objects
            vars_in_algebraic_keys_dict = unpacker.unpack_symbol(var)
            vars_in_eqns_dict = unpacker.unpack_symbol(eqn)

            # Store ids only
            # Look only for Variable (not VariableDot) in algebraic keys
            vars_in_algebraic_keys.update([
                var_id for var_id, var in vars_in_algebraic_keys_dict.items()
                if isinstance(var, pybamm.Variable)
            ])
            vars_in_eqns.update(vars_in_eqns_dict.keys())
        for var, side_eqn in self.boundary_conditions.items():
            for side, (eqn, typ) in side_eqn.items():
                vars_in_eqns_dict = unpacker.unpack_symbol(eqn)
                vars_in_eqns.update(vars_in_eqns_dict.keys())

        # 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 (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
        # have been discretised to slices but keys will still be variables
        extra_algebraic_keys = vars_in_algebraic_keys.difference(vars_in_eqns)
        if extra_algebraic_keys and not post_discretisation:
            raise pybamm.ModelError(
                "model is overdetermined (extra algebraic keys)")
        # If any variables in the equations don't appear in the keys then the model is
        # underdetermined
        vars_in_keys = vars_in_rhs_keys.union(vars_in_algebraic_keys)
        extra_variables_in_equations = vars_in_eqns.difference(vars_in_keys)

        # get ids of external variables
        external_ids = {var.id for var in self.external_variables}
        for var in self.external_variables:
            if isinstance(var, pybamm.Concatenation):
                child_ids = {child.id for child in var.children}
                external_ids = external_ids.union(child_ids)

        extra_variables = extra_variables_in_equations.difference(external_ids)

        if extra_variables:
            raise pybamm.ModelError(
                "model is underdetermined (too many variables)")