Example #1
0
 def test_delta_function(self):
     a = pybamm.Symbol("a")
     delta_a = pybamm.DeltaFunction(a, "right", "some domain")
     self.assertEqual(delta_a.side, "right")
     self.assertEqual(delta_a.child.id, a.id)
     self.assertFalse(delta_a.evaluates_on_edges())
     with self.assertRaisesRegex(pybamm.DomainError,
                                 "Delta function domain cannot be None"):
         delta_a = pybamm.DeltaFunction(a, "right", None)
Example #2
0
    def test_delta_function(self):
        a = pybamm.Symbol("a")
        delta_a = pybamm.DeltaFunction(a, "right", "some domain")
        self.assertEqual(delta_a.side, "right")
        self.assertEqual(delta_a.child.id, a.id)
        self.assertEqual(delta_a.domain, ["some domain"])
        self.assertFalse(delta_a.evaluates_on_edges("primary"))

        a = pybamm.Symbol("a", domain="some domain")
        delta_a = pybamm.DeltaFunction(a, "left", "another domain")
        self.assertEqual(delta_a.side, "left")
        self.assertEqual(delta_a.domain, ["another domain"])
        self.assertEqual(delta_a.auxiliary_domains,
                         {"secondary": ["some domain"]})

        with self.assertRaisesRegex(pybamm.DomainError,
                                    "Delta function domain cannot be None"):
            delta_a = pybamm.DeltaFunction(a, "right", None)
Example #3
0
    def test_delta_function(self):
        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)

        var = pybamm.Variable("var")
        delta_fn_left = pybamm.DeltaFunction(var, "left", "negative electrode")
        delta_fn_right = pybamm.DeltaFunction(var, "right",
                                              "negative electrode")
        disc.set_variable_slices([var])
        delta_fn_left_disc = disc.process_symbol(delta_fn_left)
        delta_fn_right_disc = disc.process_symbol(delta_fn_right)

        # Basic shape and type tests
        y = np.ones_like(mesh["negative electrode"][0].nodes[:, np.newaxis])
        # Left
        self.assertEqual(delta_fn_left_disc.domain, delta_fn_left.domain)
        self.assertEqual(delta_fn_left_disc.auxiliary_domains,
                         delta_fn_left.auxiliary_domains)
        self.assertIsInstance(delta_fn_left_disc, pybamm.Multiplication)
        self.assertIsInstance(delta_fn_left_disc.left, pybamm.Matrix)
        np.testing.assert_array_equal(
            delta_fn_left_disc.left.evaluate()[:, 1:], 0)
        self.assertEqual(delta_fn_left_disc.shape, y.shape)
        # Right
        self.assertEqual(delta_fn_right_disc.domain, delta_fn_right.domain)
        self.assertEqual(delta_fn_right_disc.auxiliary_domains,
                         delta_fn_right.auxiliary_domains)
        self.assertIsInstance(delta_fn_right_disc, pybamm.Multiplication)
        self.assertIsInstance(delta_fn_right_disc.left, pybamm.Matrix)
        np.testing.assert_array_equal(
            delta_fn_right_disc.left.evaluate()[:, :-1], 0)
        self.assertEqual(delta_fn_right_disc.shape, y.shape)

        # Value tests
        # Delta function should integrate to the same thing as variable
        var_disc = disc.process_symbol(var)
        x = pybamm.standard_spatial_vars.x_n
        delta_fn_int_disc = disc.process_symbol(
            pybamm.Integral(delta_fn_left, x))
        np.testing.assert_array_equal(
            var_disc.evaluate(y=y) * mesh["negative electrode"][0].edges[-1],
            np.sum(delta_fn_int_disc.evaluate(y=y)),
        )
    def test_process_symbol(self):
        parameter_values = pybamm.ParameterValues({"a": 4, "b": 2, "c": 3})
        # process parameter
        a = pybamm.Parameter("a")
        processed_a = parameter_values.process_symbol(a)
        self.assertIsInstance(processed_a, pybamm.Scalar)
        self.assertEqual(processed_a.value, 4)

        # process binary operation
        var = pybamm.Variable("var")
        add = a + var
        processed_add = parameter_values.process_symbol(add)
        self.assertIsInstance(processed_add, pybamm.Addition)
        self.assertIsInstance(processed_add.children[0], pybamm.Scalar)
        self.assertIsInstance(processed_add.children[1], pybamm.Variable)
        self.assertEqual(processed_add.children[0].value, 4)

        b = pybamm.Parameter("b")
        add = a + b
        processed_add = parameter_values.process_symbol(add)
        self.assertIsInstance(processed_add, pybamm.Scalar)
        self.assertEqual(processed_add.value, 6)

        scal = pybamm.Scalar(34)
        mul = a * scal
        processed_mul = parameter_values.process_symbol(mul)
        self.assertIsInstance(processed_mul, pybamm.Scalar)
        self.assertEqual(processed_mul.value, 136)

        # process integral
        aa = pybamm.Parameter("a", domain=["negative electrode"])
        x = pybamm.SpatialVariable("x", domain=["negative electrode"])
        integ = pybamm.Integral(aa, x)
        processed_integ = parameter_values.process_symbol(integ)
        self.assertIsInstance(processed_integ, pybamm.Integral)
        self.assertIsInstance(processed_integ.children[0], pybamm.Scalar)
        self.assertEqual(processed_integ.children[0].value, 4)
        self.assertEqual(processed_integ.integration_variable[0].id, x.id)

        # process unary operation
        v = pybamm.Variable("v", domain="test")
        grad = pybamm.Gradient(v)
        processed_grad = parameter_values.process_symbol(grad)
        self.assertIsInstance(processed_grad, pybamm.Gradient)
        self.assertIsInstance(processed_grad.children[0], pybamm.Variable)

        # process delta function
        aa = pybamm.Parameter("a")
        delta_aa = pybamm.DeltaFunction(aa, "left", "some domain")
        processed_delta_aa = parameter_values.process_symbol(delta_aa)
        self.assertIsInstance(processed_delta_aa, pybamm.DeltaFunction)
        self.assertEqual(processed_delta_aa.side, "left")
        processed_a = processed_delta_aa.children[0]
        self.assertIsInstance(processed_a, pybamm.Scalar)
        self.assertEqual(processed_a.value, 4)

        # process boundary operator (test for BoundaryValue)
        aa = pybamm.Parameter("a", domain=["negative electrode"])
        x = pybamm.SpatialVariable("x", domain=["negative electrode"])
        boundary_op = pybamm.BoundaryValue(aa * x, "left")
        processed_boundary_op = parameter_values.process_symbol(boundary_op)
        self.assertIsInstance(processed_boundary_op, pybamm.BoundaryOperator)
        processed_a = processed_boundary_op.children[0].children[0]
        processed_x = processed_boundary_op.children[0].children[1]
        self.assertIsInstance(processed_a, pybamm.Scalar)
        self.assertEqual(processed_a.value, 4)
        self.assertEqual(processed_x.id, x.id)

        # process broadcast
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        broad = pybamm.PrimaryBroadcast(a, whole_cell)
        processed_broad = parameter_values.process_symbol(broad)
        self.assertIsInstance(processed_broad, pybamm.Broadcast)
        self.assertEqual(processed_broad.domain, whole_cell)
        self.assertIsInstance(processed_broad.children[0], pybamm.Scalar)
        self.assertEqual(processed_broad.children[0].evaluate(), 4)

        # process concatenation
        conc = pybamm.concatenation(
            pybamm.Vector(np.ones(10), domain="test"),
            pybamm.Vector(2 * np.ones(15), domain="test 2"),
        )
        processed_conc = parameter_values.process_symbol(conc)
        self.assertIsInstance(processed_conc.children[0], pybamm.Vector)
        self.assertIsInstance(processed_conc.children[1], pybamm.Vector)
        np.testing.assert_array_equal(processed_conc.children[0].entries, 1)
        np.testing.assert_array_equal(processed_conc.children[1].entries, 2)

        # process domain concatenation
        c_e_n = pybamm.Variable("c_e_n", ["negative electrode"])
        c_e_s = pybamm.Variable("c_e_p", ["separator"])
        test_mesh = shared.get_mesh_for_testing()
        dom_con = pybamm.DomainConcatenation([a * c_e_n, b * c_e_s], test_mesh)
        processed_dom_con = parameter_values.process_symbol(dom_con)
        a_proc = processed_dom_con.children[0].children[0]
        b_proc = processed_dom_con.children[1].children[0]
        self.assertIsInstance(a_proc, pybamm.Scalar)
        self.assertIsInstance(b_proc, pybamm.Scalar)
        self.assertEqual(a_proc.value, 4)
        self.assertEqual(b_proc.value, 2)

        # process variable
        c = pybamm.Variable("c")
        processed_c = parameter_values.process_symbol(c)
        self.assertIsInstance(processed_c, pybamm.Variable)
        self.assertEqual(processed_c.name, "c")

        # process scalar
        d = pybamm.Scalar(14)
        processed_d = parameter_values.process_symbol(d)
        self.assertIsInstance(processed_d, pybamm.Scalar)
        self.assertEqual(processed_d.value, 14)

        # process array types
        e = pybamm.Vector(np.ones(4))
        processed_e = parameter_values.process_symbol(e)
        self.assertIsInstance(processed_e, pybamm.Vector)
        np.testing.assert_array_equal(processed_e.evaluate(), np.ones((4, 1)))

        f = pybamm.Matrix(np.ones((5, 6)))
        processed_f = parameter_values.process_symbol(f)
        self.assertIsInstance(processed_f, pybamm.Matrix)
        np.testing.assert_array_equal(processed_f.evaluate(), np.ones((5, 6)))

        # process statevector
        g = pybamm.StateVector(slice(0, 10))
        processed_g = parameter_values.process_symbol(g)
        self.assertIsInstance(processed_g, pybamm.StateVector)
        np.testing.assert_array_equal(
            processed_g.evaluate(y=np.ones(10)), np.ones((10, 1))
        )

        # not implemented
        sym = pybamm.Symbol("sym")
        with self.assertRaises(NotImplementedError):
            parameter_values.process_symbol(sym)

        # not found
        with self.assertRaises(KeyError):
            x = pybamm.Parameter("x")
            parameter_values.process_symbol(x)
Example #5
0
 def test_delta_function(self):
     a = pybamm.Symbol("a")
     delta_a = pybamm.DeltaFunction(a, "right", "some domain")
     self.assertEqual(delta_a.side, "right")
     self.assertEqual(delta_a.child.id, a.id)
     self.assertFalse(delta_a.evaluates_on_edges())
Example #6
0
    def test_symbol_simplify(self):
        a = pybamm.Scalar(0)
        b = pybamm.Scalar(1)
        c = pybamm.Parameter("c")
        d = pybamm.Scalar(-1)
        e = pybamm.Scalar(2)
        g = pybamm.Variable("g")

        # negate
        self.assertIsInstance((-a).simplify(), pybamm.Scalar)
        self.assertEqual((-a).simplify().evaluate(), 0)
        self.assertIsInstance((-b).simplify(), pybamm.Scalar)
        self.assertEqual((-b).simplify().evaluate(), -1)

        # absolute value
        self.assertIsInstance((abs(a)).simplify(), pybamm.Scalar)
        self.assertEqual((abs(a)).simplify().evaluate(), 0)
        self.assertIsInstance((abs(d)).simplify(), pybamm.Scalar)
        self.assertEqual((abs(d)).simplify().evaluate(), 1)

        # function
        def sin(x):
            return math.sin(x)

        f = pybamm.Function(sin, b)
        self.assertIsInstance((f).simplify(), pybamm.Scalar)
        self.assertEqual((f).simplify().evaluate(), math.sin(1))

        def myfunction(x, y):
            return x * y

        f = pybamm.Function(myfunction, a, b)
        self.assertIsInstance((f).simplify(), pybamm.Scalar)
        self.assertEqual((f).simplify().evaluate(), 0)

        # FunctionParameter
        f = pybamm.FunctionParameter("function", b)
        self.assertIsInstance((f).simplify(), pybamm.FunctionParameter)
        self.assertEqual((f).simplify().children[0].id, b.id)

        f = pybamm.FunctionParameter("function", a, b)
        self.assertIsInstance((f).simplify(), pybamm.FunctionParameter)
        self.assertEqual((f).simplify().children[0].id, a.id)
        self.assertEqual((f).simplify().children[1].id, b.id)

        # Gradient
        self.assertIsInstance((pybamm.grad(a)).simplify(), pybamm.Scalar)
        self.assertEqual((pybamm.grad(a)).simplify().evaluate(), 0)
        v = pybamm.Variable("v")
        self.assertIsInstance((pybamm.grad(v)).simplify(), pybamm.Gradient)

        # Divergence
        self.assertIsInstance((pybamm.div(a)).simplify(), pybamm.Scalar)
        self.assertEqual((pybamm.div(a)).simplify().evaluate(), 0)
        self.assertIsInstance((pybamm.div(v)).simplify(), pybamm.Divergence)

        # Integral
        self.assertIsInstance(
            (pybamm.Integral(a, pybamm.t)).simplify(), pybamm.Integral
        )

        # BoundaryValue
        v_neg = pybamm.Variable("v", domain=["negative electrode"])
        self.assertIsInstance(
            (pybamm.boundary_value(v_neg, "right")).simplify(), pybamm.BoundaryValue
        )

        # Delta function
        self.assertIsInstance(
            (pybamm.DeltaFunction(v_neg, "right", "domain")).simplify(),
            pybamm.DeltaFunction,
        )

        # addition
        self.assertIsInstance((a + b).simplify(), pybamm.Scalar)
        self.assertEqual((a + b).simplify().evaluate(), 1)
        self.assertIsInstance((b + b).simplify(), pybamm.Scalar)
        self.assertEqual((b + b).simplify().evaluate(), 2)
        self.assertIsInstance((b + a).simplify(), pybamm.Scalar)
        self.assertEqual((b + a).simplify().evaluate(), 1)

        # subtraction
        self.assertIsInstance((a - b).simplify(), pybamm.Scalar)
        self.assertEqual((a - b).simplify().evaluate(), -1)
        self.assertIsInstance((b - b).simplify(), pybamm.Scalar)
        self.assertEqual((b - b).simplify().evaluate(), 0)
        self.assertIsInstance((b - a).simplify(), pybamm.Scalar)
        self.assertEqual((b - a).simplify().evaluate(), 1)

        # addition and subtraction with matrix zero
        v = pybamm.Vector(np.zeros((10, 1)))
        self.assertIsInstance((b + v).simplify(), pybamm.Array)
        np.testing.assert_array_equal((b + v).simplify().evaluate(), np.ones((10, 1)))
        self.assertIsInstance((v + b).simplify(), pybamm.Array)
        np.testing.assert_array_equal((v + b).simplify().evaluate(), np.ones((10, 1)))
        self.assertIsInstance((b - v).simplify(), pybamm.Array)
        np.testing.assert_array_equal((b - v).simplify().evaluate(), np.ones((10, 1)))
        self.assertIsInstance((v - b).simplify(), pybamm.Array)
        np.testing.assert_array_equal((v - b).simplify().evaluate(), -np.ones((10, 1)))

        # multiplication
        self.assertIsInstance((a * b).simplify(), pybamm.Scalar)
        self.assertEqual((a * b).simplify().evaluate(), 0)
        self.assertIsInstance((b * a).simplify(), pybamm.Scalar)
        self.assertEqual((b * a).simplify().evaluate(), 0)
        self.assertIsInstance((b * b).simplify(), pybamm.Scalar)
        self.assertEqual((b * b).simplify().evaluate(), 1)
        self.assertIsInstance((a * a).simplify(), pybamm.Scalar)
        self.assertEqual((a * a).simplify().evaluate(), 0)

        # test when other node is a parameter
        self.assertIsInstance((a + c).simplify(), pybamm.Parameter)
        self.assertIsInstance((c + a).simplify(), pybamm.Parameter)
        self.assertIsInstance((c + b).simplify(), pybamm.Addition)
        self.assertIsInstance((b + c).simplify(), pybamm.Addition)
        self.assertIsInstance((a * c).simplify(), pybamm.Scalar)
        self.assertEqual((a * c).simplify().evaluate(), 0)
        self.assertIsInstance((c * a).simplify(), pybamm.Scalar)
        self.assertEqual((c * a).simplify().evaluate(), 0)
        self.assertIsInstance((b * c).simplify(), pybamm.Parameter)
        self.assertIsInstance((e * c).simplify(), pybamm.Multiplication)

        expr = (e * (e * c)).simplify()
        self.assertIsInstance(expr, pybamm.Multiplication)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = (e / (e * c)).simplify()
        self.assertIsInstance(expr, pybamm.Division)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 1.0)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = (e * (e / c)).simplify()
        self.assertIsInstance(expr, pybamm.Division)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 4.0)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = (e * (c / e)).simplify()
        self.assertIsInstance(expr, pybamm.Multiplication)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 1.0)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = ((e * c) * (c / e)).simplify()
        self.assertIsInstance(expr, pybamm.Multiplication)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 1.0)
        self.assertIsInstance(expr.children[1], pybamm.Multiplication)
        self.assertIsInstance(expr.children[1].children[0], pybamm.Parameter)
        self.assertIsInstance(expr.children[1].children[1], pybamm.Parameter)

        expr = (e + (e + c)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 4.0)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = (e + (e - c)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 4.0)
        self.assertIsInstance(expr.children[1], pybamm.Negate)
        self.assertIsInstance(expr.children[1].children[0], pybamm.Parameter)

        expr = (e + (g - c)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 2.0)
        self.assertIsInstance(expr.children[1], pybamm.Subtraction)
        self.assertIsInstance(expr.children[1].children[0], pybamm.Variable)
        self.assertIsInstance(expr.children[1].children[1], pybamm.Parameter)

        expr = ((2 + c) + (c + 2)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 4.0)
        self.assertIsInstance(expr.children[1], pybamm.Multiplication)
        self.assertIsInstance(expr.children[1].children[0], pybamm.Scalar)
        self.assertEqual(expr.children[1].children[0].evaluate(), 2)
        self.assertIsInstance(expr.children[1].children[1], pybamm.Parameter)

        expr = ((-1 + c) - (c + 1) + (c - 1)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), -3.0)

        # check these don't simplify
        self.assertIsInstance((c * e).simplify(), pybamm.Multiplication)
        self.assertIsInstance((e / c).simplify(), pybamm.Division)
        self.assertIsInstance((c).simplify(), pybamm.Parameter)
        c1 = pybamm.Parameter("c1")
        self.assertIsInstance((c1 * c).simplify(), pybamm.Multiplication)

        # should simplify division to multiply
        self.assertIsInstance((c / e).simplify(), pybamm.Multiplication)

        self.assertIsInstance((c / b).simplify(), pybamm.Parameter)
        self.assertIsInstance((c * b).simplify(), pybamm.Parameter)

        # negation with parameter
        self.assertIsInstance((-c).simplify(), pybamm.Negate)

        self.assertIsInstance((a + b + a).simplify(), pybamm.Scalar)
        self.assertEqual((a + b + a).simplify().evaluate(), 1)
        self.assertIsInstance((b + a + a).simplify(), pybamm.Scalar)
        self.assertEqual((b + a + a).simplify().evaluate(), 1)
        self.assertIsInstance((a * b * b).simplify(), pybamm.Scalar)
        self.assertEqual((a * b * b).simplify().evaluate(), 0)
        self.assertIsInstance((b * a * b).simplify(), pybamm.Scalar)
        self.assertEqual((b * a * b).simplify().evaluate(), 0)

        # power simplification
        self.assertIsInstance((c ** a).simplify(), pybamm.Scalar)
        self.assertEqual((c ** a).simplify().evaluate(), 1)
        self.assertIsInstance((a ** c).simplify(), pybamm.Scalar)
        self.assertEqual((a ** c).simplify().evaluate(), 0)
        d = pybamm.Scalar(2)
        self.assertIsInstance((c ** d).simplify(), pybamm.Power)

        # division
        self.assertIsInstance((a / b).simplify(), pybamm.Scalar)
        self.assertEqual((a / b).simplify().evaluate(), 0)
        self.assertIsInstance((b / a).simplify(), pybamm.Scalar)
        self.assertEqual((b / a).simplify().evaluate(), np.inf)
        self.assertIsInstance((a / a).simplify(), pybamm.Scalar)
        self.assertTrue(np.isnan((a / a).simplify().evaluate()))
        self.assertIsInstance((b / b).simplify(), pybamm.Scalar)
        self.assertEqual((b / b).simplify().evaluate(), 1)

        # not implemented for Symbol
        sym = pybamm.Symbol("sym")
        with self.assertRaises(NotImplementedError):
            sym.simplify()

        # A + A = 2A (#323)
        a = pybamm.Parameter("A")
        expr = (a + a).simplify()
        self.assertIsInstance(expr, pybamm.Multiplication)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 2)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = (a + a + a + a).simplify()
        self.assertIsInstance(expr, pybamm.Multiplication)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 4)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        expr = (a - a + a - a + a + a).simplify()
        self.assertIsInstance(expr, pybamm.Multiplication)
        self.assertIsInstance(expr.children[0], pybamm.Scalar)
        self.assertEqual(expr.children[0].evaluate(), 2)
        self.assertIsInstance(expr.children[1], pybamm.Parameter)

        # A - A = 0 (#323)
        expr = (a - a).simplify()
        self.assertIsInstance(expr, pybamm.Scalar)
        self.assertEqual(expr.evaluate(), 0)

        # B - (A+A) = B - 2*A (#323)
        expr = (b - (a + a)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.right, pybamm.Negate)
        self.assertIsInstance(expr.right.child, pybamm.Multiplication)
        self.assertEqual(expr.right.child.left.id, pybamm.Scalar(2).id)
        self.assertEqual(expr.right.child.right.id, a.id)

        # B - (1*A + 2*A) = B - 3*A (#323)
        expr = (b - (1 * a + 2 * a)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.right, pybamm.Negate)
        self.assertIsInstance(expr.right.child, pybamm.Multiplication)
        self.assertEqual(expr.right.child.left.id, pybamm.Scalar(3).id)
        self.assertEqual(expr.right.child.right.id, a.id)

        # B - (A + C) = B - (A + C) (not B - (A - C))
        expr = (b - (a + c)).simplify()
        self.assertIsInstance(expr, pybamm.Addition)
        self.assertIsInstance(expr.right, pybamm.Subtraction)
        self.assertEqual(expr.right.left.id, (-a).id)
        self.assertEqual(expr.right.right.id, c.id)