Exemple #1
0
    def spatial_variable(self, symbol):
        """
        Creates a discretised spatial variable compatible with
        the FiniteElement method.

        Parameters
        -----------
        symbol : :class:`pybamm.SpatialVariable`
            The spatial variable to be discretised.

        Returns
        -------
        :class:`pybamm.Vector`
            Contains the discretised spatial variable
        """
        symbol_mesh = self.mesh
        if symbol.name == "y":
            vector = pybamm.Vector(symbol_mesh["current collector"]
                                   [0].coordinates[0, :][:, np.newaxis])
        elif symbol.name == "z":
            vector = pybamm.Vector(symbol_mesh["current collector"]
                                   [0].coordinates[1, :][:, np.newaxis])
        else:
            raise pybamm.GeometryError(
                "Spatial variable must be 'y' or 'z' not {}".format(
                    symbol.name))
        return vector
    def test_concatenations(self):
        y = np.linspace(0, 1, 10)[:, np.newaxis]
        a = pybamm.Vector(y)
        b = pybamm.Scalar(16)
        c = pybamm.Scalar(3)
        conc = pybamm.NumpyConcatenation(a, b, c)
        self.assert_casadi_equal(conc.to_casadi(),
                                 casadi.MX(conc.evaluate()),
                                 evalf=True)

        # Domain concatenation
        mesh = get_mesh_for_testing()
        a_dom = ["negative electrode"]
        b_dom = ["separator"]
        a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom)
        b = pybamm.Vector(np.ones_like(mesh[b_dom[0]].nodes), domain=b_dom)
        conc = pybamm.DomainConcatenation([b, a], mesh)
        self.assert_casadi_equal(conc.to_casadi(),
                                 casadi.MX(conc.evaluate()),
                                 evalf=True)

        # 2d
        disc = get_1p1d_discretisation_for_testing()
        a = pybamm.Variable("a", domain=a_dom)
        b = pybamm.Variable("b", domain=b_dom)
        conc = pybamm.Concatenation(a, b)
        disc.set_variable_slices([conc])
        expr = disc.process_symbol(conc)
        y = casadi.SX.sym("y", expr.size)
        x = expr.to_casadi(None, y)
        f = casadi.Function("f", [x], [x])
        y_eval = np.linspace(0, 1, expr.size)
        self.assert_casadi_equal(f(y_eval), casadi.SX(expr.evaluate(y=y_eval)))
Exemple #3
0
    def divergence(self, symbol, discretised_symbol, boundary_conditions):
        """Matrix-vector multiplication to implement the divergence operator.
        See :meth:`pybamm.SpatialMethod.divergence`
        """
        domain = symbol.domain
        submesh_list = self.mesh.combine_submeshes(*domain)

        divergence_matrix = self.divergence_matrix(domain)

        # check for particle domain
        if submesh_list[0].coord_sys == "spherical polar":
            second_dim = len(submesh_list)
            edges = submesh_list[0].edges

            # create np.array of repeated submesh[0].nodes
            r_numpy = np.kron(np.ones(second_dim), submesh_list[0].nodes)
            r_edges_numpy = np.kron(np.ones(second_dim), edges)

            r = pybamm.Vector(r_numpy)
            r_edges = pybamm.Vector(r_edges_numpy)

            out = (1 / (r ** 2)) * (
                divergence_matrix @ ((r_edges ** 2) * discretised_symbol)
            )
        else:
            out = divergence_matrix @ discretised_symbol

        return out
    def test_base_concatenation(self):
        a = pybamm.Symbol("a", domain="test a")
        b = pybamm.Symbol("b", domain="test b")
        c = pybamm.Symbol("c", domain="test c")
        conc = pybamm.concatenation(a, b, c)
        self.assertEqual(conc.name, "concatenation")
        self.assertEqual(str(conc), "concatenation(a, b, c)")
        self.assertIsInstance(conc.children[0], pybamm.Symbol)
        self.assertEqual(conc.children[0].name, "a")
        self.assertEqual(conc.children[1].name, "b")
        self.assertEqual(conc.children[2].name, "c")
        d = pybamm.Vector([2], domain="test a")
        e = pybamm.Vector([1], domain="test b")
        f = pybamm.Vector([3], domain="test c")
        conc2 = pybamm.concatenation(d, e, f)
        with self.assertRaises(TypeError):
            conc2.evaluate()

        # trying to concatenate non-pybamm symbols
        with self.assertRaises(TypeError):
            pybamm.concatenation(1, 2)

        # concatenation of length 0
        with self.assertRaisesRegex(ValueError,
                                    "Cannot create empty concatenation"):
            pybamm.concatenation()

        # concatenation of lenght 1
        self.assertEqual(pybamm.concatenation(a), a)
Exemple #5
0
    def test_outer(self):
        # Outer class
        v = pybamm.Vector(np.ones(5), domain="current collector")
        w = pybamm.Vector(2 * np.ones(3), domain="test")
        outer = pybamm.Outer(v, w)
        np.testing.assert_array_equal(outer.evaluate(), 2 * np.ones((15, 1)))
        self.assertEqual(outer.domain, w.domain)
        self.assertEqual(
            str(outer),
            "outer(Column vector of length 5, Column vector of length 3)")

        # outer function
        # if there is no domain clash, normal multiplication is retured
        u = pybamm.Vector(np.linspace(0, 1, 5))
        outer = pybamm.outer(u, v)
        self.assertIsInstance(outer, pybamm.Multiplication)
        np.testing.assert_array_equal(outer.evaluate(), u.evaluate())
        # otherwise, Outer class is returned
        outer_fun = pybamm.outer(v, w)
        outer_class = pybamm.Outer(v, w)
        self.assertEqual(outer_fun.id, outer_class.id)

        # failures
        y = pybamm.StateVector(slice(10))
        with self.assertRaisesRegex(
                TypeError,
                "right child must only contain SpatialVariable and scalars"):
            pybamm.Outer(v, y)
        with self.assertRaises(NotImplementedError):
            outer_fun.diff(None)
Exemple #6
0
 def test_simplify_outer(self):
     v = pybamm.Vector(np.ones(5), domain="current collector")
     w = pybamm.Vector(2 * np.ones(3), domain="test")
     outer_simp = pybamm.Outer(v, w).simplify()
     self.assertIsInstance(outer_simp, pybamm.Vector)
     np.testing.assert_array_equal(outer_simp.evaluate(), 2 * np.ones(
         (15, 1)))
Exemple #7
0
    def test_grad_1plus1d(self):
        mesh = get_1p1d_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        a = pybamm.Variable("a", domain=["negative electrode"])
        b = pybamm.Variable("b", domain=["separator"])
        c = pybamm.Variable("c", domain=["positive electrode"])
        var = pybamm.Concatenation(a, b, c)
        boundary_conditions = {
            var.id: {
                "left": (pybamm.Vector(np.linspace(0, 1, 15)), "Neumann"),
                "right": (pybamm.Vector(np.linspace(0, 1, 15)), "Neumann"),
            }
        }

        disc.bcs = boundary_conditions
        disc.set_variable_slices([var])
        grad_eqn_disc = disc.process_symbol(pybamm.grad(var))

        # Evaulate
        combined_submesh = mesh.combine_submeshes(*var.domain)
        linear_y = np.outer(np.linspace(0, 1, 15),
                            combined_submesh[0].nodes).reshape(-1, 1)

        expected = np.outer(np.linspace(0, 1, 15),
                            np.ones_like(combined_submesh[0].edges)).reshape(
                                -1, 1)
        np.testing.assert_array_almost_equal(
            grad_eqn_disc.evaluate(None, linear_y), expected)
Exemple #8
0
    def broadcast(self, symbol, domain, auxiliary_domains, broadcast_type):
        """
        Broadcast symbol to a specified domain.

        Parameters
        ----------
        symbol : :class:`pybamm.Symbol`
            The symbol to be broadcasted
        domain : iterable of strings
            The domain to broadcast to
        auxiliary_domains : dict of strings
            The auxiliary domains for broadcasting
        broadcast_type : str
            The type of broadcast: 'primary to node', 'primary to edges', 'secondary to
            nodes', 'secondary to edges', 'full to nodes' or 'full to edges'

        Returns
        -------
        broadcasted_symbol: class: `pybamm.Symbol`
            The discretised symbol of the correct size for the spatial method
        """

        primary_domain_size = sum(self.mesh[dom].npts_for_broadcast_to_nodes
                                  for dom in domain)
        secondary_domain_size = self._get_auxiliary_domain_repeats(
            {k: v
             for k, v in auxiliary_domains.items() if k == "secondary"})
        auxiliary_domains_size = self._get_auxiliary_domain_repeats(
            auxiliary_domains)
        full_domain_size = primary_domain_size * auxiliary_domains_size
        if broadcast_type.endswith("to edges"):
            # add one point to each domain for broadcasting to edges
            primary_domain_size += 1
            full_domain_size = primary_domain_size * auxiliary_domains_size
            secondary_domain_size += 1

        if broadcast_type.startswith("primary"):
            # Make copies of the child stacked on top of each other
            sub_vector = np.ones((primary_domain_size, 1))
            if symbol.shape_for_testing == ():
                out = symbol * pybamm.Vector(sub_vector)
            else:
                # Repeat for secondary points
                matrix = csr_matrix(
                    kron(eye(symbol.shape_for_testing[0]), sub_vector))
                out = pybamm.Matrix(matrix) @ symbol
            out.domain = domain
        elif broadcast_type.startswith("secondary"):
            # Make copies of the child stacked on top of each other
            identity = eye(symbol.shape[0])
            matrix = vstack([identity for _ in range(secondary_domain_size)])
            out = pybamm.Matrix(matrix) @ symbol
        elif broadcast_type.startswith("full"):
            out = symbol * pybamm.Vector(np.ones(full_domain_size),
                                         domain=domain)

        out.auxiliary_domains = auxiliary_domains.copy()
        return out
Exemple #9
0
    def test_vector_zero_simplify(self):
        a1 = pybamm.Scalar(0)
        v1 = pybamm.Vector(np.zeros(10))
        a2 = pybamm.Scalar(1)
        v2 = pybamm.Vector(np.ones(10))

        for expr in [a1 * v1, v1 * a1, a2 * v1, v1 * a2, a1 * v2, v2 * a1, v1 * v2]:
            self.assertIsInstance(expr.simplify(), pybamm.Vector)
            np.testing.assert_array_equal(expr.simplify().entries, np.zeros((10, 1)))
    def test_numpy_domain_concatenation(self):
        # create mesh
        mesh = get_mesh_for_testing()

        a_dom = ["negative electrode"]
        b_dom = ["positive electrode"]
        a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom)
        b = pybamm.Vector(np.ones_like(mesh[b_dom[0]].nodes), domain=b_dom)

        # concatenate them the "wrong" way round to check they get reordered correctly
        conc = pybamm.DomainConcatenation([b, a], mesh)
        np.testing.assert_array_equal(
            conc.evaluate(),
            np.concatenate([
                np.full(mesh[a_dom[0]].npts, 2),
                np.full(mesh[b_dom[0]].npts, 1)
            ])[:, np.newaxis],
        )
        # test size and shape
        self.assertEqual(conc.size, mesh[a_dom[0]].npts + mesh[b_dom[0]].npts)
        self.assertEqual(conc.shape,
                         (mesh[a_dom[0]].npts + mesh[b_dom[0]].npts, 1))

        # check the reordering in case a child vector has to be split up
        a_dom = ["separator"]
        b_dom = ["negative electrode", "positive electrode"]
        a = 2 * pybamm.Vector(np.ones_like(mesh[a_dom[0]].nodes), domain=a_dom)
        b = pybamm.Vector(
            np.concatenate([
                np.full(mesh[b_dom[0]].npts, 1),
                np.full(mesh[b_dom[1]].npts, 3)
            ])[:, np.newaxis],
            domain=b_dom,
        )

        conc = pybamm.DomainConcatenation([a, b], mesh)
        np.testing.assert_array_equal(
            conc.evaluate(),
            np.concatenate([
                np.full(mesh[b_dom[0]].npts, 1),
                np.full(mesh[a_dom[0]].npts, 2),
                np.full(mesh[b_dom[1]].npts, 3),
            ])[:, np.newaxis],
        )
        # test size and shape
        self.assertEqual(
            conc.size,
            mesh[b_dom[0]].npts + mesh[a_dom[0]].npts + mesh[b_dom[1]].npts,
        )
        self.assertEqual(
            conc.shape,
            (
                mesh[b_dom[0]].npts + mesh[a_dom[0]].npts +
                mesh[b_dom[1]].npts,
                1,
            ),
        )
Exemple #11
0
    def broadcast(self, symbol, domain, auxiliary_domains, broadcast_type):
        """
        Broadcast symbol to a specified domain.

        Parameters
        ----------
        symbol : :class:`pybamm.Symbol`
            The symbol to be broadcasted
        domain : iterable of strings
            The domain to broadcast to
        broadcast_type : str
            The type of broadcast, either: 'primary' or 'full'

        Returns
        -------
        broadcasted_symbol: class: `pybamm.Symbol`
            The discretised symbol of the correct size for the spatial method
        """

        primary_domain_size = sum(self.mesh[dom][0].npts_for_broadcast
                                  for dom in domain)

        full_domain_size = sum(subdom.npts_for_broadcast for dom in domain
                               for subdom in self.mesh[dom])

        if broadcast_type == "primary":
            # Make copies of the child stacked on top of each other
            sub_vector = np.ones((primary_domain_size, 1))
            if symbol.shape_for_testing == ():
                out = symbol * pybamm.Vector(sub_vector)
            else:
                # Repeat for secondary points
                matrix = csr_matrix(
                    kron(eye(symbol.shape_for_testing[0]), sub_vector))
                out = pybamm.Matrix(matrix) @ symbol
            out.domain = domain
        elif broadcast_type == "secondary":
            secondary_domain_size = sum(
                self.mesh[dom][0].npts_for_broadcast
                for dom in auxiliary_domains["secondary"])
            kron_size = full_domain_size // primary_domain_size
            # Symbol may be on edges so need to calculate size carefully
            symbol_primary_size = symbol.shape[0] // kron_size
            # Make copies of the child stacked on top of each other
            identity = eye(symbol_primary_size)
            sub_matrix = vstack(
                [identity for _ in range(secondary_domain_size)])
            # Repeat for secondary points
            matrix = csr_matrix(kron(eye(kron_size), sub_matrix))
            out = pybamm.Matrix(matrix) @ symbol
        elif broadcast_type == "full":
            out = symbol * pybamm.Vector(np.ones(full_domain_size),
                                         domain=domain)

        out.auxiliary_domains = auxiliary_domains
        return out
Exemple #12
0
    def test_jac_of_domain_concatenation(self):
        # create mesh
        mesh = get_mesh_for_testing()
        y = pybamm.StateVector(slice(0, 100))

        # Jacobian of a DomainConcatenation of constants is a zero matrix of the
        # appropriate size
        a_dom = ["negative electrode"]
        b_dom = ["separator"]
        c_dom = ["positive electrode"]
        a_npts = mesh[a_dom[0]].npts
        b_npts = mesh[b_dom[0]].npts
        c_npts = mesh[c_dom[0]].npts
        a = 2 * pybamm.Vector(np.ones(a_npts), domain=a_dom)
        b = pybamm.Vector(np.ones(b_npts), domain=b_dom)
        c = 3 * pybamm.Vector(np.ones(c_npts), domain=c_dom)

        conc = pybamm.DomainConcatenation([a, b, c], mesh)
        jac = conc.jac(y).evaluate().toarray()
        np.testing.assert_array_equal(jac, np.zeros((100, 100)))

        # Jacobian of a DomainConcatenation of StateVectors
        a = 2 * pybamm.StateVector(slice(0, a_npts), domain=a_dom)
        b = pybamm.StateVector(slice(a_npts, a_npts + b_npts), domain=b_dom)
        c = 3 * pybamm.StateVector(
            slice(a_npts + b_npts, a_npts + b_npts + c_npts), domain=c_dom
        )
        conc = pybamm.DomainConcatenation([a, b, c], mesh)

        y0 = np.ones(100)
        jac = conc.jac(y).evaluate(y=y0).toarray()
        np.testing.assert_array_equal(
            jac,
            np.diag(
                np.concatenate(
                    [2 * np.ones(a_npts), np.ones(b_npts), 3 * np.ones(c_npts)]
                )
            ),
        )

        # multi=domain case not implemented
        a = 2 * pybamm.StateVector(slice(0, a_npts), domain=a_dom)
        b = pybamm.StateVector(
            slice(a_npts, a_npts + b_npts + c_npts), domain=b_dom + c_dom
        )
        conc = pybamm.DomainConcatenation([a, b], mesh)
        with self.assertRaisesRegex(
            NotImplementedError, "jacobian only implemented for when each child has"
        ):
            conc.jac(y)
 def test_numpy_concatenation_vectors(self):
     # with entries
     y = np.linspace(0, 1, 15)[:, np.newaxis]
     a = pybamm.Vector(y[:5])
     b = pybamm.Vector(y[5:9])
     c = pybamm.Vector(y[9:])
     conc = pybamm.NumpyConcatenation(a, b, c)
     np.testing.assert_array_equal(conc.evaluate(None, y), y)
     # with y_slice
     a = pybamm.StateVector(slice(0, 10))
     b = pybamm.StateVector(slice(10, 15))
     c = pybamm.StateVector(slice(15, 23))
     conc = pybamm.NumpyConcatenation(a, b, c)
     y = np.linspace(0, 1, 23)[:, np.newaxis]
     np.testing.assert_array_equal(conc.evaluate(None, y), y)
    def test_numpy_concatenation_vector_scalar(self):
        # with entries
        y = np.linspace(0, 1, 10)[:, np.newaxis]
        a = pybamm.Vector(y)
        b = pybamm.Scalar(16)
        c = pybamm.Scalar(3)
        conc = pybamm.NumpyConcatenation(a, b, c)
        np.testing.assert_array_equal(
            conc.evaluate(y=y),
            np.concatenate([y, np.array([[16]]),
                            np.array([[3]])]))

        # with y_slice
        a = pybamm.StateVector(slice(0, 10))
        conc = pybamm.NumpyConcatenation(a, b, c)
        np.testing.assert_array_equal(
            conc.evaluate(y=y),
            np.concatenate([y, np.array([[16]]),
                            np.array([[3]])]))

        # with time
        b = pybamm.t
        conc = pybamm.NumpyConcatenation(a, b, c)
        np.testing.assert_array_equal(
            conc.evaluate(16, y),
            np.concatenate([y, np.array([[16]]),
                            np.array([[3]])]))
Exemple #15
0
    def test_jac_of_domain_concatenation(self):
        # create mesh
        disc = get_1p1d_discretisation_for_testing()
        mesh = disc.mesh
        y = pybamm.StateVector(slice(0, 1500))

        # Jacobian of a DomainConcatenation of constants is a zero matrix of the
        # appropriate size
        a_dom = ["negative electrode"]
        b_dom = ["separator"]
        c_dom = ["positive electrode"]
        cc_npts = mesh["current collector"][0].npts
        curr_coll_vector = pybamm.Vector(np.ones(cc_npts), domain="current collector")
        a = 2 * pybamm.PrimaryBroadcast(curr_coll_vector, a_dom)
        b = pybamm.PrimaryBroadcast(curr_coll_vector, b_dom)
        c = 3 * pybamm.PrimaryBroadcast(curr_coll_vector, c_dom)

        conc = pybamm.Concatenation(a, b, c)
        disc.set_variable_slices([conc])
        conc_disc = disc.process_symbol(conc)
        jac = conc_disc.jac(y).evaluate().toarray()
        np.testing.assert_array_equal(jac, np.zeros((1500, 1500)))

        # Jacobian of a DomainConcatenation of StateVectors
        a = pybamm.Variable("a", domain=a_dom)
        b = pybamm.Variable("b", domain=b_dom)
        c = pybamm.Variable("c", domain=c_dom)
        conc = pybamm.Concatenation(a, b, c)
        disc.set_variable_slices([conc])
        conc_disc = disc.process_symbol(conc)
        y0 = np.ones(1500)
        jac = conc_disc.jac(y).evaluate(y=y0).toarray()
        np.testing.assert_array_equal(jac, np.eye(1500))
Exemple #16
0
    def spatial_variable(self, symbol):
        """
        Convert a :class:`pybamm.SpatialVariable` node to a linear algebra object that
        can be evaluated (here, a :class:`pybamm.Vector` on either the nodes or the
        edges).

        Parameters
        -----------
        symbol : :class:`pybamm.SpatialVariable`
            The spatial variable to be discretised.

        Returns
        -------
        :class:`pybamm.Vector`
            Contains the discretised spatial variable
        """
        symbol_mesh = self.mesh.combine_submeshes(*symbol.domain)
        repeats = self._get_auxiliary_domain_repeats(symbol.auxiliary_domains)
        if symbol.evaluates_on_edges("primary"):
            entries = np.tile(symbol_mesh.edges, repeats)
        else:
            entries = np.tile(symbol_mesh.nodes, repeats)
        return pybamm.Vector(entries,
                             domain=symbol.domain,
                             auxiliary_domains=symbol.auxiliary_domains)
Exemple #17
0
    def test_symbol_evaluates_to_constant_number(self):
        a = pybamm.Scalar(3)
        self.assertTrue(a.evaluates_to_constant_number())

        a = pybamm.Parameter("a")
        self.assertFalse(a.evaluates_to_constant_number())

        a = pybamm.Variable("a")
        self.assertFalse(a.evaluates_to_constant_number())

        a = pybamm.Scalar(3) - 2
        self.assertTrue(a.evaluates_to_constant_number())

        a = pybamm.Vector(np.ones(5))
        self.assertFalse(a.evaluates_to_constant_number())

        a = pybamm.Matrix(np.ones((4, 6)))
        self.assertFalse(a.evaluates_to_constant_number())

        a = pybamm.StateVector(slice(0, 10))
        self.assertFalse(a.evaluates_to_constant_number())

        # Time variable returns true
        a = 3 * pybamm.t + 2
        self.assertFalse(a.evaluates_to_constant_number())
Exemple #18
0
def simplify_if_constant(symbol, keep_domains=False):
    """
    Utility function to simplify an expression tree if it evalutes to a constant
    scalar, vector or matrix
    """
    if keep_domains is True:
        domain = symbol.domain
        auxiliary_domains = symbol.auxiliary_domains
    else:
        domain = None
        auxiliary_domains = None
    if symbol.is_constant():
        result = symbol.evaluate_ignoring_errors()
        if result is not None:
            if (isinstance(result, numbers.Number)
                    or (isinstance(result, np.ndarray) and result.ndim == 0)
                    or isinstance(result, np.bool_)):
                return pybamm.Scalar(result)
            elif isinstance(result, np.ndarray) or issparse(result):
                if result.ndim == 1 or result.shape[1] == 1:
                    return pybamm.Vector(result,
                                         domain=domain,
                                         auxiliary_domains=auxiliary_domains)
                else:
                    # Turn matrix of zeros into sparse matrix
                    if isinstance(result, np.ndarray) and np.all(result == 0):
                        result = csr_matrix(result)
                    return pybamm.Matrix(result,
                                         domain=domain,
                                         auxiliary_domains=auxiliary_domains)

    return symbol
Exemple #19
0
    def _binary_simplify(self, left, right):
        """ See :meth:`pybamm.BinaryOperator.simplify()`. """

        # anything multiplied by a scalar zero returns a scalar zero
        if is_scalar_zero(left):
            if isinstance(right, pybamm.Array):
                return pybamm.Array(np.zeros(right.shape))
            else:
                return pybamm.Scalar(0)
        if is_scalar_zero(right):
            if isinstance(left, pybamm.Array):
                return pybamm.Array(np.zeros(left.shape))
            else:
                return pybamm.Scalar(0)

        # if one of the children is a zero matrix, we have to be careful about shapes
        if is_matrix_zero(left) or is_matrix_zero(right):
            shape = (left * right).shape
            if len(shape) == 1 or shape[1] == 1:
                return pybamm.Vector(np.zeros(shape))
            else:
                return pybamm.Matrix(csr_matrix(shape))

        # anything multiplied by a scalar one returns itself
        if is_one(left):
            return right
        if is_one(right):
            return left

        return pybamm.simplify_multiplication_division(self.__class__, left,
                                                       right)
Exemple #20
0
    def test_function_of_one_variable(self):
        a = pybamm.Symbol("a")
        funca = pybamm.Function(test_function, a)
        self.assertEqual(funca.name, "function (test_function)")
        self.assertEqual(str(funca), "test_function(a)")
        self.assertEqual(funca.children[0].name, a.name)

        b = pybamm.Scalar(1)
        sina = pybamm.Function(np.sin, b)
        self.assertEqual(sina.evaluate(), np.sin(1))
        self.assertEqual(sina.name, "function ({})".format(np.sin.__name__))

        c = pybamm.Vector(np.linspace(0, 1))
        cosb = pybamm.Function(np.cos, c)
        np.testing.assert_array_equal(cosb.evaluate(), np.cos(c.evaluate()))

        var = pybamm.StateVector(slice(0, 100))
        y = np.linspace(0, 1, 100)[:, np.newaxis]
        logvar = pybamm.Function(np.log1p, var)
        np.testing.assert_array_equal(logvar.evaluate(y=y), np.log1p(y))

        # use known_evals
        np.testing.assert_array_equal(
            logvar.evaluate(y=y, known_evals={})[0], np.log1p(y)
        )
Exemple #21
0
 def test_base_concatenation(self):
     a = pybamm.Symbol("a")
     b = pybamm.Symbol("b")
     c = pybamm.Symbol("c")
     conc = pybamm.Concatenation(a, b, c)
     self.assertEqual(conc.name, "concatenation")
     self.assertIsInstance(conc.children[0], pybamm.Symbol)
     self.assertEqual(conc.children[0].name, "a")
     self.assertEqual(conc.children[1].name, "b")
     self.assertEqual(conc.children[2].name, "c")
     d = pybamm.Vector(np.array([2]))
     e = pybamm.Vector(np.array([1]))
     f = pybamm.Vector(np.array([3]))
     conc2 = pybamm.Concatenation(d, e, f)
     with self.assertRaises(TypeError):
         conc2.evaluate()
Exemple #22
0
    def test_index(self):
        vec = pybamm.Vector(np.array([1, 2, 3, 4, 5]))
        # with integer
        ind = vec[3]
        self.assertIsInstance(ind, pybamm.Index)
        self.assertEqual(ind.slice, slice(3, 4))
        self.assertEqual(ind.evaluate(), 4)
        # with slice
        ind = vec[1:3]
        self.assertIsInstance(ind, pybamm.Index)
        self.assertEqual(ind.slice, slice(1, 3))
        np.testing.assert_array_equal(ind.evaluate(), np.array([[2], [3]]))
        # with only stop slice
        ind = vec[:3]
        self.assertIsInstance(ind, pybamm.Index)
        self.assertEqual(ind.slice, slice(3))
        np.testing.assert_array_equal(ind.evaluate(), np.array([[1], [2],
                                                                [3]]))

        # errors
        with self.assertRaisesRegex(TypeError,
                                    "index must be integer or slice"):
            pybamm.Index(vec, 0.0)
        with self.assertRaisesRegex(ValueError,
                                    "slice size exceeds child size"):
            pybamm.Index(vec, 5)
Exemple #23
0
    def boundary_value_or_flux(self, symbol, discretised_child, bcs=None):
        """
        Returns the average value of the symbol over the negative tab ("negative tab")
        or the positive tab ("positive tab") in the Finite Element Method.

        Overwrites the default :meth:`pybamm.SpatialMethod.boundary_value`
        """

        # Return average value on the negative tab for "negative tab" and positive tab
        # for "positive tab"
        if isinstance(symbol, pybamm.BoundaryValue):
            # get integration_vector
            if symbol.side == "negative tab":
                region = "negative tab"
            elif symbol.side == "positive tab":
                region = "positive tab"
            domain = symbol.children[0].domain[0]
            integration_vector = self.boundary_integral_vector(domain,
                                                               region=region)

            # divide integration weights by (numerical) tab width to give average value
            boundary_val_vector = integration_vector / (
                integration_vector @ pybamm.Vector(
                    np.ones(integration_vector.shape[1])))

        elif isinstance(symbol, pybamm.BoundaryGradient):
            raise NotImplementedError

        # Return boundary value with domain given by symbol
        boundary_value = boundary_val_vector @ discretised_child

        boundary_value.domain = symbol.domain

        return boundary_value
Exemple #24
0
    def test_symbol_evaluates_to_number(self):
        a = pybamm.Scalar(3)
        self.assertTrue(a.evaluates_to_number())

        a = pybamm.Parameter("a")
        self.assertFalse(a.evaluates_to_number())

        a = pybamm.Scalar(3) * pybamm.Time()
        self.assertTrue(a.evaluates_to_number())
        # highlight difference between this function and isinstance(a, Scalar)
        self.assertNotIsInstance(a, pybamm.Scalar)

        a = pybamm.Variable("a")
        self.assertFalse(a.evaluates_to_number())

        a = pybamm.Scalar(3) - 2
        self.assertTrue(a.evaluates_to_number())

        a = pybamm.Vector(np.ones(5))
        self.assertFalse(a.evaluates_to_number())

        a = pybamm.Matrix(np.ones((4, 6)))
        self.assertFalse(a.evaluates_to_number())

        a = pybamm.StateVector(slice(0, 10))
        self.assertFalse(a.evaluates_to_number())

        # Time variable returns true
        a = 3 * pybamm.t + 2
        self.assertTrue(a.evaluates_to_number())
Exemple #25
0
    def test_simplify_inner(self):
        a1 = pybamm.Scalar(0)
        M1 = pybamm.Matrix(np.zeros((10, 10)))
        v1 = pybamm.Vector(np.ones(10))
        a2 = pybamm.Scalar(1)
        M2 = pybamm.Matrix(np.ones((10, 10)))
        a3 = pybamm.Scalar(3)

        np.testing.assert_array_equal(
            pybamm.inner(a1, M2).simplify().evaluate().toarray(), M1.entries
        )
        self.assertEqual(pybamm.inner(a1, a2).simplify().evaluate(), 0)
        np.testing.assert_array_equal(
            pybamm.inner(M2, a1).simplify().evaluate().toarray(), M1.entries
        )
        self.assertEqual(pybamm.inner(a2, a1).simplify().evaluate(), 0)
        np.testing.assert_array_equal(
            pybamm.inner(M1, a3).simplify().evaluate().toarray(), M1.entries
        )
        np.testing.assert_array_equal(
            pybamm.inner(v1, a3).simplify().evaluate(), 3 * v1.entries
        )
        self.assertEqual(pybamm.inner(a2, a3).simplify().evaluate(), 3)
        self.assertEqual(pybamm.inner(a3, a2).simplify().evaluate(), 3)
        self.assertEqual(pybamm.inner(a3, a3).simplify().evaluate(), 9)
Exemple #26
0
 def test_simplify_kron(self):
     A = pybamm.Matrix(np.eye(2))
     b = pybamm.Vector(np.array([[4], [5]]))
     kron = pybamm.Kron(A, b)
     kron_simp = kron.simplify()
     self.assertIsInstance(kron_simp, pybamm.Matrix)
     np.testing.assert_array_equal(kron_simp.evaluate().toarray(),
                                   np.kron(A.entries, b.entries))
 def test_failure(self):
     t = np.ones(25)
     y = np.ones((120, 25))
     mat = pybamm.Vector(np.ones(120), domain=["negative particle"])
     disc = tests.get_p2d_discretisation_for_testing()
     with self.assertRaisesRegex(
             ValueError, "3D variable shape does not match domain shape"):
         pybamm.ProcessedVariable(mat, t, y, disc.mesh)
Exemple #28
0
    def test_index(self):
        vec = pybamm.StateVector(slice(0, 5))
        ind = pybamm.Index(vec, 3)
        jac = ind.jac(vec).evaluate(y=np.linspace(0, 2, 5)).toarray()
        np.testing.assert_array_equal(jac, np.array([[0, 0, 0, 1, 0]]))

        # jac of ind of something that isn't a StateVector should return zeros
        const_vec = pybamm.Vector(np.ones(3))
        ind = pybamm.Index(const_vec, 2)
        jac = ind.jac(vec).evaluate(y=np.linspace(0, 2, 5)).toarray()
        np.testing.assert_array_equal(jac, np.array([[0, 0, 0, 0, 0]]))
Exemple #29
0
 def __init__(self, *children):
     children = list(children)
     # Turn objects that evaluate to scalars to objects that evaluate to vectors,
     # so that we can concatenate them
     for i, child in enumerate(children):
         if child.evaluates_to_number():
             children[i] = child * pybamm.Vector(np.array([1]))
     super().__init__(*children,
                      name="numpy concatenation",
                      check_domain=False,
                      concat_fun=np.concatenate)
    def test_sparse_divide(self):
        row = np.array([0, 3, 1, 0])
        col = np.array([0, 3, 1, 2])
        data = np.array([4, 5, 7, 9])
        S1 = coo_matrix((data, (row, col)), shape=(4, 5))
        pybammS1 = pybamm.Matrix(S1)
        v1 = np.ones((4, 1))
        pybammv1 = pybamm.Vector(v1)

        np.testing.assert_array_equal(
            (pybammS1 / pybammv1).evaluate().toarray(), S1.toarray() / v1
        )