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)
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)))
def outer(left, right): """ Return outer product of two symbols. If the symbols have the same domain, the outer product is just a multiplication. If they have different domains, make a copy of the left child with same domain as right child, and then take outer product. """ try: return left * right except pybamm.DomainError: return pybamm.Outer(left, right)
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"] a_npts = mesh[a_dom[0]][0].npts b_npts = mesh[b_dom[0]][0].npts c_npts = mesh[c_dom[0]][0].npts cc_npts = mesh["current collector"][0].npts curr_coll_vector = pybamm.Vector(np.ones(cc_npts), domain="current collector") a = 2 * pybamm.Outer(curr_coll_vector, pybamm.Vector(np.ones(a_npts), domain=a_dom)) b = pybamm.Outer(curr_coll_vector, pybamm.Vector(np.ones(b_npts), domain=b_dom)) c = 3 * pybamm.Outer(curr_coll_vector, 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((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))
def test_simplify_divide_outer(self): u = pybamm.Scalar(1) v = pybamm.StateVector(slice(0, 5), domain="current collector") outer = pybamm.Outer(v, u) exp1 = pybamm.Division(pybamm.Division(outer, u), u) self.assertIsInstance(exp1.simplify(), pybamm.Outer) exp2 = pybamm.Division(pybamm.Division(outer, 2 * u), u) self.assertIsInstance(exp2.simplify(), pybamm.Multiplication) exp3 = pybamm.Division(pybamm.Division(outer, u), 2 * u) self.assertIsInstance(exp3.simplify(), pybamm.Multiplication) exp4 = pybamm.Division(pybamm.Division(outer, 2 * u), 2 * u) self.assertIsInstance(exp4.simplify(), pybamm.Multiplication)
def test_convert_array_symbols(self): # Arrays a = np.array([1, 2, 3, 4, 5]) pybamm_a = pybamm.Array(a) self.assertTrue(casadi.is_equal(pybamm_a.to_casadi(), casadi.SX(a))) casadi_t = casadi.SX.sym("t") casadi_y = casadi.SX.sym("y", 10) pybamm_t = pybamm.Time() pybamm_y = pybamm.StateVector(slice(0, 10)) # Time self.assertEqual(pybamm_t.to_casadi(casadi_t, casadi_y), casadi_t) # State Vector self.assertTrue( casadi.is_equal(pybamm_y.to_casadi(casadi_t, casadi_y), casadi_y)) # outer product outer = pybamm.Outer(pybamm_a, pybamm_a) self.assertTrue( casadi.is_equal(outer.to_casadi(), casadi.SX(outer.evaluate())))
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_pts_for_broadcast = sum( self.mesh[dom][0].npts_for_broadcast for dom in domain ) full_pts_for_broadcast = sum( subdom.npts_for_broadcast for dom in domain for subdom in self.mesh[dom] ) if broadcast_type == "primary": out = pybamm.Outer( symbol, pybamm.Vector(np.ones(primary_pts_for_broadcast), domain=domain) ) elif broadcast_type == "full": out = symbol * pybamm.Vector(np.ones(full_pts_for_broadcast), domain=domain) out.auxiliary_domains = auxiliary_domains return out
def test_process_symbol(self): parameter_values = pybamm.ParameterValues({"a": 1, "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, 1) # process binary operation b = pybamm.Parameter("b") add = a + b 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.Scalar) self.assertEqual(processed_add.children[0].value, 1) self.assertEqual(processed_add.children[1].value, 2) scal = pybamm.Scalar(34) mul = a * scal processed_mul = parameter_values.process_symbol(mul) self.assertIsInstance(processed_mul, pybamm.Multiplication) self.assertIsInstance(processed_mul.children[0], pybamm.Scalar) self.assertIsInstance(processed_mul.children[1], pybamm.Scalar) self.assertEqual(processed_mul.children[0].value, 1) self.assertEqual(processed_mul.children[1].value, 34) # 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, 1) self.assertEqual(processed_integ.integration_variable[0].id, x.id) # process unary operation grad = pybamm.Gradient(a) processed_grad = parameter_values.process_symbol(grad) self.assertIsInstance(processed_grad, pybamm.Gradient) self.assertIsInstance(processed_grad.children[0], pybamm.Scalar) self.assertEqual(processed_grad.children[0].value, 1) # 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, 1) # 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, 1) self.assertEqual(processed_x.id, x.id) # process broadcast whole_cell = ["negative electrode", "separator", "positive electrode"] broad = pybamm.Broadcast(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(), np.array([1])) # process concatenation conc = pybamm.Concatenation(pybamm.Vector(np.ones(10)), pybamm.Vector(2 * np.ones(15))) 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, 1) 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))) # process outer c = pybamm.Parameter("c", domain="current collector") outer = pybamm.Outer(c, b) processed_outer = parameter_values.process_symbol(outer) self.assertIsInstance(processed_outer, pybamm.Outer) # not implemented sym = pybamm.Symbol("sym") with self.assertRaises(NotImplementedError): parameter_values.process_symbol(sym)
def _binary_simplify(self, left, right): """ See :meth:`pybamm.BinaryOperator.simplify()`. """ # Make sure left child keeps same domain left.domain = self.left.domain return pybamm.Outer(left, right)
def test_linear(self): y = pybamm.StateVector(slice(0, 4)) u = pybamm.StateVector(slice(0, 2)) v = pybamm.StateVector(slice(2, 4)) y0 = np.ones(4) func = u jacobian = np.array([[1, 0, 0, 0], [0, 1, 0, 0]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = -v jacobian = np.array([[0, 0, -1, 0], [0, 0, 0, -1]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = 3 * u + 4 * v jacobian = np.array([[3, 0, 4, 0], [0, 3, 0, 4]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = 7 * u - v * 9 jacobian = np.array([[7, 0, -9, 0], [0, 7, 0, -9]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) A = pybamm.Matrix(2 * eye(2)) func = A @ u jacobian = np.array([[2, 0, 0, 0], [0, 2, 0, 0]]) dfunc_dy = func.jac(y).simplify().evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = u @ pybamm.StateVector(slice(0, 1)) with self.assertRaises(NotImplementedError): func.jac(y) # when differentiating by independent part of the state vector jacobian = np.array([[0, 0], [0, 0]]) du_dv = u.jac(v).evaluate().toarray() np.testing.assert_array_equal(du_dv, jacobian) # test Jacobian of Outer (must set domain to be 'current collector') u.domain = ["current collector"] func = pybamm.Outer(u, pybamm.Scalar(4)) jacobian = np.array([[4, 0, 0, 0], [0, 4, 0, 0]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = pybamm.Outer(u, pybamm.Vector(np.array([1, 2, 3]))) jacobian = np.array([ [1, 0, 0, 0], [2, 0, 0, 0], [3, 0, 0, 0], [0, 1, 0, 0], [0, 2, 0, 0], [0, 3, 0, 0], ]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) # test jac of outer if left evaluates to number func = pybamm.Outer(pybamm.Scalar(1), pybamm.Scalar(4)) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(0, dfunc_dy.toarray())
def _binary_simplify(self, left, right): """ See :meth:`pybamm.BinaryOperator._binary_simplify()`. """ return pybamm.Outer(left, right)
def test_evaluator_python(self): a = pybamm.StateVector(slice(0, 1)) b = pybamm.StateVector(slice(1, 2)) y_tests = [np.array([[2], [3]]), np.array([[1], [3]])] t_tests = [1, 2] # test a * b expr = a * b evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate(t=None, y=np.array([[2], [3]])) self.assertEqual(result, 6) result = evaluator.evaluate(t=None, y=np.array([[1], [3]])) self.assertEqual(result, 3) # test function(a*b) expr = pybamm.Function(test_function, a * b) evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate(t=None, y=np.array([[2], [3]])) self.assertEqual(result, 12) # test a constant expression expr = pybamm.Scalar(2) * pybamm.Scalar(3) evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate() self.assertEqual(result, 6) # test a larger expression expr = a * b + b + a**2 / b + 2 * a + b / 2 + 4 evaluator = pybamm.EvaluatorPython(expr) for y in y_tests: result = evaluator.evaluate(t=None, y=y) self.assertEqual(result, expr.evaluate(t=None, y=y)) # test something with time expr = a * pybamm.t evaluator = pybamm.EvaluatorPython(expr) for t, y in zip(t_tests, y_tests): result = evaluator.evaluate(t=t, y=y) self.assertEqual(result, expr.evaluate(t=t, y=y)) # test something with a matrix multiplication A = pybamm.Matrix(np.array([[1, 2], [3, 4]])) expr = A @ pybamm.StateVector(slice(0, 2)) evaluator = pybamm.EvaluatorPython(expr) for t, y in zip(t_tests, y_tests): result = evaluator.evaluate(t=t, y=y) np.testing.assert_allclose(result, expr.evaluate(t=t, y=y)) # test something with an index expr = pybamm.Index(A @ pybamm.StateVector(slice(0, 2)), 0) evaluator = pybamm.EvaluatorPython(expr) for t, y in zip(t_tests, y_tests): result = evaluator.evaluate(t=t, y=y) self.assertEqual(result, expr.evaluate(t=t, y=y)) # test something with a sparse matrix multiplication A = pybamm.Matrix(np.array([[1, 2], [3, 4]])) B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]]))) C = pybamm.Matrix(scipy.sparse.coo_matrix(np.array([[1, 0], [0, 4]]))) expr = A @ B @ C @ pybamm.StateVector(slice(0, 2)) evaluator = pybamm.EvaluatorPython(expr) for t, y in zip(t_tests, y_tests): result = evaluator.evaluate(t=t, y=y) np.testing.assert_allclose(result, expr.evaluate(t=t, y=y)) # test numpy concatenation a = pybamm.Vector(np.array([[1], [2]])) b = pybamm.Vector(np.array([[3]])) expr = pybamm.NumpyConcatenation(a, b) evaluator = pybamm.EvaluatorPython(expr) for t, y in zip(t_tests, y_tests): result = evaluator.evaluate(t=t, y=y) np.testing.assert_allclose(result, expr.evaluate(t=t, y=y)) # test sparse stack A = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[1, 0], [0, 4]]))) B = pybamm.Matrix(scipy.sparse.csr_matrix(np.array([[2, 0], [5, 0]]))) expr = pybamm.SparseStack(A, B) evaluator = pybamm.EvaluatorPython(expr) for t, y in zip(t_tests, y_tests): result = evaluator.evaluate(t=t, y=y).toarray() np.testing.assert_allclose(result, expr.evaluate(t=t, y=y).toarray()) # test Outer v = pybamm.Vector(np.ones(5), domain="current collector") w = pybamm.Vector(2 * np.ones(3), domain="test") expr = pybamm.Outer(v, w) evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate() np.testing.assert_allclose(result, expr.evaluate()) # test Inner v = pybamm.Vector(np.ones(5), domain="test") w = pybamm.Vector(2 * np.ones(5), domain="test") expr = pybamm.Inner(v, w) evaluator = pybamm.EvaluatorPython(expr) result = evaluator.evaluate() np.testing.assert_allclose(result, expr.evaluate())