def test_convert_scalar_symbols(self): a = pybamm.Scalar(0) b = pybamm.Scalar(1) c = pybamm.Scalar(-1) d = pybamm.Scalar(2) e = pybamm.Scalar(3) g = pybamm.Scalar(3.3) self.assertEqual(a.to_casadi(), casadi.MX(0)) self.assertEqual(d.to_casadi(), casadi.MX(2)) # negate self.assertEqual((-b).to_casadi(), casadi.MX(-1)) # absolute value self.assertEqual(abs(c).to_casadi(), casadi.MX(1)) # floor self.assertEqual(pybamm.Floor(g).to_casadi(), casadi.MX(3)) # ceiling self.assertEqual(pybamm.Ceiling(g).to_casadi(), casadi.MX(4)) # function def square_plus_one(x): return x**2 + 1 f = pybamm.Function(square_plus_one, b) self.assertEqual(f.to_casadi(), 2) def myfunction(x, y): return x + y f = pybamm.Function(myfunction, b, d) self.assertEqual(f.to_casadi(), casadi.MX(3)) # use classes to avoid simplification # addition self.assertEqual((pybamm.Addition(a, b)).to_casadi(), casadi.MX(1)) # subtraction self.assertEqual(pybamm.Subtraction(c, d).to_casadi(), casadi.MX(-3)) # multiplication self.assertEqual( pybamm.Multiplication(c, d).to_casadi(), casadi.MX(-2)) # power self.assertEqual(pybamm.Power(c, d).to_casadi(), casadi.MX(1)) # division self.assertEqual(pybamm.Division(b, d).to_casadi(), casadi.MX(1 / 2)) # modulo self.assertEqual(pybamm.Modulo(e, d).to_casadi(), casadi.MX(1)) # minimum and maximum self.assertEqual(pybamm.Minimum(a, b).to_casadi(), casadi.MX(0)) self.assertEqual(pybamm.Maximum(a, b).to_casadi(), casadi.MX(1))
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_scalar_symbols(self): a = pybamm.Scalar(0) b = pybamm.Scalar(1) c = pybamm.Scalar(-1) d = pybamm.Scalar(2) self.assertEqual(a.to_casadi(), casadi.MX(0)) self.assertEqual(d.to_casadi(), casadi.MX(2)) # negate self.assertEqual((-b).to_casadi(), casadi.MX(-1)) # absolute value self.assertEqual(abs(c).to_casadi(), casadi.MX(1)) # function def sin(x): return np.sin(x) f = pybamm.Function(sin, b) self.assertEqual(f.to_casadi(), casadi.MX(np.sin(1))) def myfunction(x, y): return x + y f = pybamm.Function(myfunction, b, d) self.assertEqual(f.to_casadi(), casadi.MX(3)) # use classes to avoid simplification # addition self.assertEqual((pybamm.Addition(a, b)).to_casadi(), casadi.MX(1)) # subtraction self.assertEqual(pybamm.Subtraction(c, d).to_casadi(), casadi.MX(-3)) # multiplication self.assertEqual( pybamm.Multiplication(c, d).to_casadi(), casadi.MX(-2)) # power self.assertEqual(pybamm.Power(c, d).to_casadi(), casadi.MX(1)) # division self.assertEqual(pybamm.Division(b, d).to_casadi(), casadi.MX(1 / 2)) # minimum and maximum self.assertEqual(pybamm.Minimum(a, b).to_casadi(), casadi.MX(0)) self.assertEqual(pybamm.Maximum(a, b).to_casadi(), casadi.MX(1))
def test_to_equation(self): # Test print_name pybamm.Addition.print_name = "test" self.assertEqual(pybamm.Addition(1, 2).to_equation(), sympy.symbols("test")) # Test Power self.assertEqual(pybamm.Power(7, 2).to_equation(), 49) # Test Division self.assertEqual(pybamm.Division(10, 2).to_equation(), 5) # Test Matrix Multiplication arr1 = pybamm.Array([[1, 0], [0, 1]]) arr2 = pybamm.Array([[4, 1], [2, 2]]) self.assertEqual( pybamm.MatrixMultiplication(arr1, arr2).to_equation(), sympy.Matrix([[4.0, 1.0], [2.0, 2.0]]), ) # Test EqualHeaviside self.assertEqual(pybamm.EqualHeaviside(1, 0).to_equation(), False) # Test NotEqualHeaviside self.assertEqual(pybamm.NotEqualHeaviside(2, 4).to_equation(), True)
def __rtruediv__(self, other): """return a :class:`Division` object""" return pybamm.simplify_if_constant(pybamm.Division(other, self), keep_domains=True)
def __rtruediv__(self, other): """return a :class:`Division` object""" if isinstance(other, (Symbol, numbers.Number)): return pybamm.Division(other, self) else: raise NotImplementedError
def simplified_division(left, right): left, right = simplify_elementwise_binary_broadcasts(left, right) # Check for Concatenations and Broadcasts out = simplified_binary_broadcast_concatenation(left, right, simplified_division) if out is not None: return out # zero divided by anything returns zero (being careful about shape) if pybamm.is_scalar_zero(left): return pybamm.zeros_like(right) # matrix zero divided by anything returns matrix zero (i.e. itself) if pybamm.is_matrix_zero(left): return pybamm.zeros_like(pybamm.Division(left, right)) # anything divided by zero raises error if pybamm.is_scalar_zero(right): raise ZeroDivisionError # anything divided by one is itself if pybamm.is_scalar_one(right): return left # a symbol divided by itself is 1s of the same shape if left.id == right.id: return pybamm.ones_like(left) # anything multiplied by a matrix one returns itself if # - the shapes are the same # - both left and right evaluate on edges, or both evaluate on nodes, in all # dimensions # (and possibly more generally, but not implemented here) try: if left.shape_for_testing == right.shape_for_testing and all( left.evaluates_on_edges(dim) == right.evaluates_on_edges(dim) for dim in ["primary", "secondary", "tertiary"]): if pybamm.is_matrix_one(right): return left # also check for negative one if pybamm.is_matrix_minus_one(right): return -left except NotImplementedError: pass # Return constant if both sides are constant if left.is_constant() and right.is_constant(): return pybamm.simplify_if_constant(pybamm.Division(left, right)) # Simplify (B @ c) / a to (B / a) @ c if (B / a) is constant # This is a common construction that appears from discretisation of averages elif isinstance(left, MatrixMultiplication) and right.is_constant(): l_left, l_right = left.orphans new_left = l_left / right if new_left.is_constant(): # be careful about domains to avoid weird errors new_left.clear_domains() new_division = new_left @ l_right # Keep the domain of the old left new_division.copy_domains(left) return new_division if isinstance(left, Multiplication): # Simplify (a * b) / c to (a / c) * b if (a / c) is constant if left.left.is_constant(): l_left, l_right = left.orphans new_left = l_left / right if new_left.is_constant(): return new_left * l_right # Simplify (a * b) / c to a * (b / c) if (b / c) is constant elif left.right.is_constant(): l_left, l_right = left.orphans new_right = l_right / right if new_right.is_constant(): return l_left * new_right # Negation simplifications elif isinstance(left, pybamm.Negate) and right.is_constant(): # Simplify (-a) / b to a / (-b) if (-b) is constant return left.orphans[0] / (-right) elif isinstance(right, pybamm.Negate) and left.is_constant(): # Simplify a / (-b) to (-a) / b if (-a) is constant return (-left) / right.orphans[0] return pybamm.simplify_if_constant(pybamm.Division(left, right))