def test_absolute(self): a = pybamm.Symbol("a") absa = pybamm.AbsoluteValue(a) self.assertEqual(absa.name, "abs") self.assertEqual(absa.children[0].name, a.name) b = pybamm.Scalar(-4) absb = pybamm.AbsoluteValue(b) self.assertEqual(absb.evaluate(), 4) # Test broadcast gets switched broad_a = pybamm.PrimaryBroadcast(a, "test") abs_broad = abs(broad_a) self.assertEqual(abs_broad.id, pybamm.PrimaryBroadcast(absa, "test").id) broad_a = pybamm.FullBroadcast(a, "test", "test2") abs_broad = abs(broad_a) self.assertEqual(abs_broad.id, pybamm.FullBroadcast(absa, "test", "test2").id) # Test recursion broad_a = pybamm.PrimaryBroadcast(pybamm.PrimaryBroadcast(a, "test"), "test2") abs_broad = abs(broad_a) self.assertEqual( abs_broad.id, pybamm.PrimaryBroadcast(pybamm.PrimaryBroadcast(absa, "test"), "test2").id, )
def test_absolute(self): a = pybamm.Symbol("a") absa = pybamm.AbsoluteValue(a) self.assertEqual(absa.name, "abs") self.assertEqual(absa.children[0].name, a.name) b = pybamm.Scalar(-4) absb = pybamm.AbsoluteValue(b) self.assertEqual(absb.evaluate(), 4)
def _set_dimensional_parameters(self): "Defines the dimensional parameters" self.I_typ = pybamm.Parameter("Typical current [A]") self.Q = pybamm.Parameter("Cell capacity [A.h]") self.C_rate = pybamm.AbsoluteValue(self.I_typ / self.Q) self.n_electrodes_parallel = pybamm.Parameter( "Number of electrodes connected in parallel to make a cell") self.n_cells = pybamm.Parameter( "Number of cells connected in series to make a battery") self.i_typ = pybamm.Function( np.abs, self.I_typ / (self.n_electrodes_parallel * self.geo.A_cc)) self.voltage_low_cut_dimensional = pybamm.Parameter( "Lower voltage cut-off [V]") self.voltage_high_cut_dimensional = pybamm.Parameter( "Upper voltage cut-off [V]") # Current as a function of *dimensional* time. The below is overwritten in # lithium_ion_parameters.py and lead_acid_parameters.py to use the correct # timescale used for non-dimensionalisation. For a base model, the user may # provide the typical timescale as a parameter. self.timescale = pybamm.Parameter("Typical timescale [s]") self.dimensional_current_with_time = pybamm.FunctionParameter( "Current function [A]", {"Time[s]": pybamm.t * self.timescale}) self.dimensional_current_density_with_time = ( self.dimensional_current_with_time / (self.n_electrodes_parallel * self.geo.A_cc))
def test_to_equation(self): a = pybamm.Symbol("a", domain="negative particle") b = pybamm.Symbol("b", domain="current collector") c = pybamm.Symbol("c", domain="test") # Test print_name pybamm.Floor.print_name = "test" self.assertEqual(pybamm.Floor(-2.5).to_equation(), sympy.symbols("test")) # Test Negate self.assertEqual(pybamm.Negate(4).to_equation(), -4.0) # Test AbsoluteValue self.assertEqual(pybamm.AbsoluteValue(-4).to_equation(), 4.0) # Test Gradient self.assertEqual(pybamm.Gradient(a).to_equation(), sympy_Gradient("a")) # Test Divergence self.assertEqual( pybamm.Divergence(pybamm.Gradient(a)).to_equation(), sympy_Divergence(sympy_Gradient(a)), ) # Test BoundaryValue self.assertEqual( pybamm.BoundaryValue(a, "right").to_equation(), sympy.symbols("a^{surf}") ) self.assertEqual( pybamm.BoundaryValue(b, "positive tab").to_equation(), sympy.symbols(str(b)) ) self.assertEqual( pybamm.BoundaryValue(c, "left").to_equation(), sympy.symbols("c^{left}") )
def __abs__(self): """return an :class:`AbsoluteValue` object, or a smooth approximation""" k = pybamm.settings.abs_smoothing # Return exact approximation if that is the setting or the outcome is a constant # (i.e. no need for smoothing) if k == "exact" or is_constant(self): out = pybamm.AbsoluteValue(self) else: out = pybamm.smooth_absolute_value(self, k) return pybamm.simplify_if_constant(out, keep_domains=True)
def test_unary_operator(self): a = pybamm.Symbol("a", domain=["test"]) un = pybamm.UnaryOperator("unary test", a) self.assertEqual(un.children[0].name, a.name) self.assertEqual(un.domain, a.domain) # with number a = pybamm.InputParameter("a") absval = pybamm.AbsoluteValue(-a) self.assertEqual(absval.evaluate(inputs={"a": 10}), 10) self.assertEqual(absval.evaluate(inputs={"a": 10}, known_evals={})[0], 10)
def test_unary_operator(self): a = pybamm.Symbol("a", domain=["test"]) un = pybamm.UnaryOperator("unary test", a) self.assertEqual(un.children[0].name, a.name) self.assertEqual(un.domain, a.domain) # with number absval = pybamm.AbsoluteValue(-10) self.assertEqual(absval.evaluate(), 10) log = pybamm.log(10) self.assertEqual(log.evaluate(), np.log(10))
def test_nonlinear(self): y = pybamm.StateVector(slice(0, 4)) u = pybamm.StateVector(slice(0, 2)) v = pybamm.StateVector(slice(2, 4)) y0 = np.array([1, 2, 3, 4]) func = v**2 jacobian = np.array([[0, 0, 6, 0], [0, 0, 0, 8]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = 2**v jacobian = np.array([[0, 0, 2**3 * np.log(2), 0], [0, 0, 0, 2**4 * np.log(2)]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = v**v jacobian = [[0, 0, 27 * (1 + np.log(3)), 0], [0, 0, 0, 256 * (1 + np.log(4))]] dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_almost_equal(jacobian, dfunc_dy.toarray()) func = u * v jacobian = np.array([[3, 0, 1, 0], [0, 4, 0, 2]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = u * (u + v) jacobian = np.array([[5, 0, 1, 0], [0, 8, 0, 2]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = 1 / u + v / 3 jacobian = np.array([[-1, 0, 1 / 3, 0], [0, -1 / 4, 0, 1 / 3]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = u / v jacobian = np.array([[1 / 3, 0, -1 / 9, 0], [0, 1 / 4, 0, -1 / 8]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = v / (1 + v) jacobian = np.array([[0, 0, 1 / 16, 0], [0, 0, 0, 1 / 25]]) dfunc_dy = func.jac(y).evaluate(y=y0) np.testing.assert_array_equal(jacobian, dfunc_dy.toarray()) func = pybamm.AbsoluteValue(v) with self.assertRaises(pybamm.UndefinedOperationError): func.jac(y)
def test_smooth_absolute_value(self): # Test that smooth absolute value is used when the setting is changed a = pybamm.Symbol("a") pybamm.settings.abs_smoothing = 10 self.assertEqual(str(abs(a)), str(pybamm.smooth_absolute_value(a, 10))) # But exact absolute value should still be used for constants a = pybamm.Parameter("a") self.assertEqual(str(abs(a)), str(pybamm.AbsoluteValue(a))) a = -1 self.assertEqual(str(abs(a)), "1") # Change setting back for other tests pybamm.settings.abs_smoothing = "exact"
def get_fundamental_variables(self): param = self.param # Current is a variable i_cell = pybamm.Variable("Total current density") # Update derived variables I = i_cell * pybamm.AbsoluteValue(param.I_typ) i_cell_dim = I / (param.n_electrodes_parallel * param.A_cc) variables = { "Total current density": i_cell, "Total current density [A.m-2]": i_cell_dim, "Current [A]": I, "C-rate": I / param.Q, } # Add discharge capacity variable variables.update(super().get_fundamental_variables()) return variables
def __abs__(self): """return an :class:`AbsoluteValue` object, or a smooth approximation.""" if isinstance(self, pybamm.AbsoluteValue): # No need to apply abs a second time return self elif isinstance(self, pybamm.Broadcast): # Move absolute value inside the broadcast # Apply recursively abs_self_not_broad = pybamm.simplify_if_constant( abs(self.orphans[0])) return self._unary_new_copy(abs_self_not_broad) else: k = pybamm.settings.abs_smoothing # Return exact approximation if that is the setting or the outcome is a # constant (i.e. no need for smoothing) if k == "exact" or is_constant(self): out = pybamm.AbsoluteValue(self) else: out = pybamm.smooth_absolute_value(self, k) return pybamm.simplify_if_constant(out)
def __abs__(self): """return an :class:`AbsoluteValue` object""" return pybamm.simplify_if_constant(pybamm.AbsoluteValue(self), keep_domains=True)
def __abs__(self): """return an :class:`AbsoluteValue` object""" return pybamm.AbsoluteValue(self)