diff --git a/.gitignore b/.gitignore index d59641db3c..444611541e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.png /local/ *.DS_Store +*.mat # don't ignore important .txt files !requirements* diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bcc47938b..3489391045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Features - Added `InputParameter` node for quickly changing parameter values ([#752](https://github.com/pybamm-team/PyBaMM/pull/752)) +- Added optional R(x) distribution in particle models ([#745](https://github.com/pybamm-team/PyBaMM/pull/745)) - Generalized importing of external variables ([#728](https://github.com/pybamm-team/PyBaMM/pull/728)) - Separated active and inactive material volume fractions ([#726](https://github.com/pybamm-team/PyBaMM/pull/726)) - Added submodels for tortuosity ([#726](https://github.com/pybamm-team/PyBaMM/pull/726)) diff --git a/examples/scripts/SPMe_SOC.py b/examples/scripts/SPMe_SOC.py index 411eb50f67..0ba2b5295e 100644 --- a/examples/scripts/SPMe_SOC.py +++ b/examples/scripts/SPMe_SOC.py @@ -27,14 +27,14 @@ w = 0.207 / factor A = h * w l_s = 2.5e-5 - l1d = (l_n + l_p + l_s) + l1d = l_n + l_p + l_s vol = h * w * l1d vol_cm3 = vol * 1e6 tot_cap = 0.0 tot_time = 0.0 fig, axes = plt.subplots(1, 2, sharey=True) I_mag = 1.01 / factor - print('*' * 30) + print("*" * 30) for enum, I_app in enumerate([-1.0, 1.0]): I_app *= I_mag # load model @@ -44,27 +44,33 @@ # load parameter values and process model and geometry param = model.default_parameter_values param.update( - {"Electrode height [m]": h, - "Electrode width [m]": w, - "Negative electrode thickness [m]": l_n, - "Positive electrode thickness [m]": l_p, - "Separator thickness [m]": l_s, - "Lower voltage cut-off [V]": 2.8, - "Upper voltage cut-off [V]": 4.7, - "Maximum concentration in negative electrode [mol.m-3]": 25000, - "Maximum concentration in positive electrode [mol.m-3]": 50000, - "Initial concentration in negative electrode [mol.m-3]": 12500, - "Initial concentration in positive electrode [mol.m-3]": 25000, - "Negative electrode surface area density [m-1]": 180000.0, - "Positive electrode surface area density [m-1]": 150000.0, - "Typical current [A]": I_app, - } + { + "Electrode height [m]": h, + "Electrode width [m]": w, + "Negative electrode thickness [m]": l_n, + "Positive electrode thickness [m]": l_p, + "Separator thickness [m]": l_s, + "Lower voltage cut-off [V]": 2.8, + "Upper voltage cut-off [V]": 4.7, + "Maximum concentration in negative electrode [mol.m-3]": 25000, + "Maximum concentration in positive electrode [mol.m-3]": 50000, + "Initial concentration in negative electrode [mol.m-3]": 12500, + "Initial concentration in positive electrode [mol.m-3]": 25000, + "Negative electrode surface area density [m-1]": 180000.0, + "Positive electrode surface area density [m-1]": 150000.0, + "Typical current [A]": I_app, + } ) param.process_model(model) param.process_geometry(geometry) s_var = pybamm.standard_spatial_vars - var_pts = {s_var.x_n: 5, s_var.x_s: 5, s_var.x_p: 5, - s_var.r_n: 5, s_var.r_p: 10} + var_pts = { + s_var.x_n: 5, + s_var.x_s: 5, + s_var.x_p: 5, + s_var.r_n: 5, + s_var.r_p: 10, + } # set mesh mesh = pybamm.Mesh(geometry, model.default_submesh_types, var_pts) # discretise model @@ -74,58 +80,61 @@ t_eval = np.linspace(0, 0.2, 100) sol = model.default_solver.solve(model, t_eval) var = "Positive electrode average extent of lithiation" - xpext = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) + xpext = pybamm.ProcessedVariable(model.variables[var], sol.t, sol.y, mesh=mesh) var = "Negative electrode average extent of lithiation" - xnext = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) + xnext = pybamm.ProcessedVariable(model.variables[var], sol.t, sol.y, mesh=mesh) var = "X-averaged positive particle surface concentration" - xpsurf = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) + xpsurf = pybamm.ProcessedVariable(model.variables[var], sol.t, sol.y, mesh=mesh) var = "X-averaged negative particle surface concentration" - xnsurf = pybamm.ProcessedVariable(model.variables[var], - sol.t, sol.y, mesh=mesh) - time = pybamm.ProcessedVariable(model.variables["Time [h]"], - sol.t, sol.y, mesh=mesh) + xnsurf = pybamm.ProcessedVariable(model.variables[var], sol.t, sol.y, mesh=mesh) + time = pybamm.ProcessedVariable( + model.variables["Time [h]"], sol.t, sol.y, mesh=mesh + ) # Coulomb counting time_hours = time(sol.t) dc_time = np.around(time_hours[-1], 3) # Capacity mAh cap = np.absolute(I_app * 1000 * dc_time) cap_time = np.absolute(I_app * 1000 * time_hours) - axes[enum].plot(cap_time, - xnext(sol.t), 'r-', label='Average Neg') - axes[enum].plot(cap_time, - xpext(sol.t), 'b-', label='Average Pos') - axes[enum].plot(cap_time, - xnsurf(sol.t), 'r--', label='Surface Neg') - axes[enum].plot(cap_time, - xpsurf(sol.t), 'b--', label='Surface Pos') - axes[enum].set_xlabel('Capacity [mAh]') + axes[enum].plot(cap_time, xnext(sol.t), "r-", label="Average Neg") + axes[enum].plot(cap_time, xpext(sol.t), "b-", label="Average Pos") + axes[enum].plot(cap_time, xnsurf(sol.t), "r--", label="Surface Neg") + axes[enum].plot(cap_time, xpsurf(sol.t), "b--", label="Surface Pos") + axes[enum].set_xlabel("Capacity [mAh]") handles, labels = axes[enum].get_legend_handles_labels() axes[enum].legend(handles, labels) if I_app < 0.0: - axes[enum].set_ylabel('Extent of Lithiation, Elecrode Ratio: ' - + str(e_ratio)) - axes[enum].title.set_text('Charge') + axes[enum].set_ylabel( + "Extent of Lithiation, Elecrode Ratio: " + str(e_ratio) + ) + axes[enum].title.set_text("Charge") else: - axes[enum].title.set_text('Discharge') - print('Applied Current', I_app, 'A', 'Time', - dc_time, 'hrs', 'Capacity', cap, 'mAh') + axes[enum].title.set_text("Discharge") + print( + "Applied Current", + I_app, + "A", + "Time", + dc_time, + "hrs", + "Capacity", + cap, + "mAh", + ) tot_cap += cap tot_time += dc_time - print('Anode : Cathode thickness', e_ratio) - print('Total Charge/Discharge Time', tot_time, 'hrs') - print('Total Capacity', np.around(tot_cap, 3), 'mAh') + print("Anode : Cathode thickness", e_ratio) + print("Total Charge/Discharge Time", tot_time, "hrs") + print("Total Capacity", np.around(tot_cap, 3), "mAh") specific_cap = np.around(tot_cap, 3) / vol_cm3 - print('Total Capacity', specific_cap, 'mAh.cm-3') + print("Total Capacity", specific_cap, "mAh.cm-3") capacities.append(tot_cap) specific_capacities.append(specific_cap) fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) ax1.plot(thicknesses / l_p, capacities) ax2.plot(thicknesses / l_p, specific_capacities) -ax1.set_ylabel('Capacity [mAh]') -ax2.set_ylabel('Specific Capacity [mAh.cm-3]') -ax2.set_xlabel('Anode : Cathode thickness') +ax1.set_ylabel("Capacity [mAh]") +ax2.set_ylabel("Specific Capacity [mAh.cm-3]") +ax2.set_xlabel("Anode : Cathode thickness") diff --git a/examples/scripts/SPMe_step.py b/examples/scripts/SPMe_step.py index 666976c61d..f669deefd6 100644 --- a/examples/scripts/SPMe_step.py +++ b/examples/scripts/SPMe_step.py @@ -49,7 +49,6 @@ # plot voltage = pybamm.ProcessedVariable( model.variables["Terminal voltage [V]"], solution.t, solution.y, mesh=mesh - ) step_voltage = pybamm.ProcessedVariable( model.variables["Terminal voltage [V]"], step_solution.t, step_solution.y, mesh=mesh diff --git a/examples/scripts/compare_extrapolations.py b/examples/scripts/compare_extrapolations.py index defcf557f0..5ef9d98834 100644 --- a/examples/scripts/compare_extrapolations.py +++ b/examples/scripts/compare_extrapolations.py @@ -29,4 +29,3 @@ solutions = [sim_lin.solution, sim_quad.solution] plot = pybamm.QuickPlot(models, sim_lin.mesh, solutions) plot.dynamic_plot() - diff --git a/examples/scripts/compare_lithium_ion_particle_distribution.py b/examples/scripts/compare_lithium_ion_particle_distribution.py new file mode 100644 index 0000000000..57d68d089f --- /dev/null +++ b/examples/scripts/compare_lithium_ion_particle_distribution.py @@ -0,0 +1,82 @@ +# +# Compare lithium-ion battery models +# +import argparse +import numpy as np +import pybamm + +parser = argparse.ArgumentParser() +parser.add_argument( + "--debug", action="store_true", help="Set logging level to 'DEBUG'." +) +args = parser.parse_args() +if args.debug: + pybamm.set_logging_level("DEBUG") +else: + pybamm.set_logging_level("INFO") + +# load models +options = {"thermal": "isothermal"} +models = [ + pybamm.lithium_ion.DFN(options, name="standard DFN"), + pybamm.lithium_ion.DFN(options, name="particle DFN"), +] + + +# load parameter values and process models and geometry +params = [models[0].default_parameter_values, models[1].default_parameter_values] +params[0]["Typical current [A]"] = 1.0 +params[0].process_model(models[0]) + + +params[1]["Typical current [A]"] = 1.0 + + +def negative_distribution(x): + return 1 + x + + +def positive_distribution(x): + return 1 + (x - (1 - models[1].param.l_p)) + + +params[1]["Negative particle distribution in x"] = negative_distribution +params[1]["Positive particle distribution in x"] = positive_distribution +params[1].process_model(models[1]) + +# set mesh +var = pybamm.standard_spatial_vars +var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10, var.r_n: 5, var.r_p: 5} + +# discretise models +for param, model in zip(params, models): + # create geometry + geometry = model.default_geometry + param.process_geometry(geometry) + mesh = pybamm.Mesh(geometry, models[-1].default_submesh_types, var_pts) + disc = pybamm.Discretisation(mesh, model.default_spatial_methods) + disc.process_model(model) + +# solve model +solutions = [None] * len(models) +t_eval = np.linspace(0, 0.3, 100) +for i, model in enumerate(models): + solutions[i] = model.default_solver.solve(model, t_eval) + + +output_variables = [ + "Negative particle surface concentration", + "Electrolyte concentration", + "Positive particle surface concentration", + "Current [A]", + "Negative electrode potential [V]", + "Electrolyte potential [V]", + "Positive electrode potential [V]", + "Terminal voltage [V]", + "Negative particle distribution in x", + "Positive particle distribution in x", +] + +# plot +plot = pybamm.QuickPlot(models, mesh, solutions, output_variables=output_variables) +plot.dynamic_plot() diff --git a/examples/scripts/heat_equation.py b/examples/scripts/heat_equation.py index 5bccd4fd9d..8ea4a2a0cc 100644 --- a/examples/scripts/heat_equation.py +++ b/examples/scripts/heat_equation.py @@ -122,11 +122,7 @@ def T_exact(x, t): label="Numerical" if i == 0 else "", ) plt.plot( - xx, - T_exact(xx, t), - "-", - color=color, - label="Exact (t={})".format(plot_times[i]), + xx, T_exact(xx, t), "-", color=color, label="Exact (t={})".format(plot_times[i]) ) plt.xlabel("x", fontsize=16) plt.ylabel("T", fontsize=16) diff --git a/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv b/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv index 3d1469d95c..9a5fcbcd21 100644 --- a/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv +++ b/input/parameters/lithium-ion/anodes/graphite_mcmb2528_Marquis2019/parameters.csv @@ -11,6 +11,7 @@ Negative electrode OCP [V],[function]graphite_mcmb2528_ocp_Dualfoil1998, Negative electrode porosity,0.3,Scott Moura FastDFN,electrolyte volume fraction Negative electrode active material volume fraction,0.7,,assuming zero binder volume fraction Negative particle radius [m],1E-05,Scott Moura FastDFN, +Negative particle distribution in x,1,, Negative electrode surface area density [m-1],180000,Scott Moura FastDFN, Negative electrode Bruggeman coefficient (electrolyte),1.5,Scott Moura FastDFN, Negative electrode Bruggeman coefficient (electrode),1.5,Scott Moura FastDFN, diff --git a/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv b/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv index d1fabddca7..1eafb9b658 100644 --- a/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv +++ b/input/parameters/lithium-ion/cathodes/lico2_Marquis2019/parameters.csv @@ -11,6 +11,7 @@ Positive electrode OCP [V],[function]lico2_ocp_Dualfoil1998, Positive electrode porosity,0.3,Scott Moura FastDFN,electrolyte volume fraction Positive electrode active material volume fraction,0.7,,assuming zero binder volume fraction Positive particle radius [m],1E-05,Scott Moura FastDFN, +Positive particle distribution in x,1,, Positive electrode surface area density [m-1],150000,Scott Moura FastDFN, Positive electrode Bruggeman coefficient (electrolyte),1.5,Scott Moura FastDFN, Positive electrode Bruggeman coefficient (electrode),1.5,Scott Moura FastDFN, diff --git a/pybamm/__init__.py b/pybamm/__init__.py index 5c5a18353b..a97a49f57d 100644 --- a/pybamm/__init__.py +++ b/pybamm/__init__.py @@ -100,7 +100,12 @@ def version(formatted=False): from .expression_tree.interpolant import Interpolant from .expression_tree.input_parameter import InputParameter from .expression_tree.parameter import Parameter, FunctionParameter -from .expression_tree.broadcasts import Broadcast, PrimaryBroadcast, FullBroadcast +from .expression_tree.broadcasts import ( + Broadcast, + PrimaryBroadcast, + FullBroadcast, + ones_like, +) from .expression_tree.scalar import Scalar from .expression_tree.variable import Variable from .expression_tree.independent_variable import ( diff --git a/pybamm/expression_tree/broadcasts.py b/pybamm/expression_tree/broadcasts.py index 5395b880c8..5e32b28098 100644 --- a/pybamm/expression_tree/broadcasts.py +++ b/pybamm/expression_tree/broadcasts.py @@ -184,3 +184,26 @@ def evaluate_for_shape(self): ) return child_eval * vec + + +def ones_like(*symbols): + """ + Create a symbol with the same shape as the input symbol and with constant value '1', + using `FullBroadcast`. + + Parameters + ---------- + symbols : :class:`Symbol` + Symbols whose shape to copy + """ + # Make a symbol that combines all the children, to get the right domain + # that takes all the child symbols into account + sum_symbol = symbols[0] + for sym in symbols: + sum_symbol += sym + + # Just return scalar 1 if symbol has no domain (no broadcasting necessary) + if sum_symbol.domain == []: + return pybamm.Scalar(1) + else: + return FullBroadcast(1, sum_symbol.domain, sum_symbol.auxiliary_domains) diff --git a/pybamm/models/submodels/particle/fickian/base_fickian_particle.py b/pybamm/models/submodels/particle/fickian/base_fickian_particle.py index 216164a633..368927c5c1 100644 --- a/pybamm/models/submodels/particle/fickian/base_fickian_particle.py +++ b/pybamm/models/submodels/particle/fickian/base_fickian_particle.py @@ -35,16 +35,6 @@ def _flux_law(self, c, T): def _unpack(self, variables): raise NotImplementedError - def set_rhs(self, variables): - - c, N, _ = self._unpack(variables) - - if self.domain == "Negative": - self.rhs = {c: -(1 / self.param.C_n) * pybamm.div(N)} - - elif self.domain == "Positive": - self.rhs = {c: -(1 / self.param.C_p) * pybamm.div(N)} - def set_boundary_conditions(self, variables): c, _, j = self._unpack(variables) diff --git a/pybamm/models/submodels/particle/fickian/fickian_many_particles.py b/pybamm/models/submodels/particle/fickian/fickian_many_particles.py index 159a0a710f..8b0d9d40cd 100644 --- a/pybamm/models/submodels/particle/fickian/fickian_many_particles.py +++ b/pybamm/models/submodels/particle/fickian/fickian_many_particles.py @@ -46,8 +46,30 @@ def get_coupled_variables(self, variables): N_s = self._flux_law(c_s, T_k) variables.update(self._get_standard_flux_variables(N_s, N_s)) + + if self.domain == "Negative": + x = pybamm.standard_spatial_vars.x_n + R = pybamm.FunctionParameter("Negative particle distribution in x", x) + variables.update({"Negative particle distribution in x": R}) + + elif self.domain == "Positive": + x = pybamm.standard_spatial_vars.x_p + R = pybamm.FunctionParameter("Positive particle distribution in x", x) + variables.update({"Positive particle distribution in x": R}) return variables + def set_rhs(self, variables): + + c, N, _ = self._unpack(variables) + + if self.domain == "Negative": + R = variables["Negative particle distribution in x"] + self.rhs = {c: -(1 / (R ** 2 * self.param.C_n)) * pybamm.div(N)} + + elif self.domain == "Positive": + R = variables["Positive particle distribution in x"] + self.rhs = {c: -(1 / (R ** 2 * self.param.C_p)) * pybamm.div(N)} + def _unpack(self, variables): c_s = variables[self.domain + " particle concentration"] N_s = variables[self.domain + " particle flux"] diff --git a/pybamm/models/submodels/particle/fickian/fickian_single_particle.py b/pybamm/models/submodels/particle/fickian/fickian_single_particle.py index db3486a9da..51bed8f20a 100644 --- a/pybamm/models/submodels/particle/fickian/fickian_single_particle.py +++ b/pybamm/models/submodels/particle/fickian/fickian_single_particle.py @@ -52,6 +52,16 @@ def get_coupled_variables(self, variables): return variables + def set_rhs(self, variables): + + c, N, _ = self._unpack(variables) + + if self.domain == "Negative": + self.rhs = {c: -(1 / self.param.C_n) * pybamm.div(N)} + + elif self.domain == "Positive": + self.rhs = {c: -(1 / self.param.C_p) * pybamm.div(N)} + def _unpack(self, variables): c_s_xav = variables[ "X-averaged " + self.domain.lower() + " particle concentration" diff --git a/pybamm/parameters/parameter_values.py b/pybamm/parameters/parameter_values.py index 2e98c72707..488b68f280 100644 --- a/pybamm/parameters/parameter_values.py +++ b/pybamm/parameters/parameter_values.py @@ -441,8 +441,11 @@ def _process_symbol(self, symbol): function = pybamm.Interpolant(data, *new_children, name=name) elif isinstance(function_name, numbers.Number): # If the "function" is provided is actually a scalar, return a Scalar - # object instead of throwing an error - function = pybamm.Scalar(function_name, name=symbol.name) + # object instead of throwing an error. + # Also use ones_like so that we get the right shapes + function = pybamm.Scalar( + function_name, name=symbol.name + ) * pybamm.ones_like(*new_children) else: # otherwise evaluate the function to create a new PyBaMM object function = function_name(*new_children) diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py index b8ac651251..938df0a3dc 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py @@ -95,6 +95,21 @@ def test_particle_fast_diffusion(self): modeltest = tests.StandardModelTest(model) modeltest.test_all() + def test_particle_distribution_in_x(self): + model = pybamm.lithium_ion.DFN() + param = model.default_parameter_values + + def negative_distribution(x): + return 1 + x + + def positive_distribution(x): + return 1 + (x - (1 - model.param.l_p)) + + param["Negative particle distribution in x"] = negative_distribution + param["Positive particle distribution in x"] = positive_distribution + modeltest = tests.StandardModelTest(model, parameter_values=param) + modeltest.test_all() + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_expression_tree/test_broadcasts.py b/tests/unit/test_expression_tree/test_broadcasts.py index a331652d9f..c91a45386e 100644 --- a/tests/unit/test_expression_tree/test_broadcasts.py +++ b/tests/unit/test_expression_tree/test_broadcasts.py @@ -26,6 +26,29 @@ def test_broadcast_type(self): with self.assertRaisesRegex(ValueError, "Variables on the current collector"): pybamm.Broadcast(a, "electrode") + def test_ones_like(self): + a = pybamm.Variable("a") + ones_like_a = pybamm.ones_like(a) + self.assertEqual(ones_like_a.id, pybamm.Scalar(1).id) + + a = pybamm.Variable( + "a", + domain="negative electrode", + auxiliary_domains={"secondary": "current collector"}, + ) + ones_like_a = pybamm.ones_like(a) + self.assertIsInstance(ones_like_a, pybamm.FullBroadcast) + self.assertEqual(ones_like_a.name, "broadcast") + self.assertEqual(ones_like_a.domain, a.domain) + self.assertEqual(ones_like_a.auxiliary_domains, a.auxiliary_domains) + + b = pybamm.Variable("b", domain="current collector") + ones_like_ab = pybamm.ones_like(b, a) + self.assertIsInstance(ones_like_ab, pybamm.FullBroadcast) + self.assertEqual(ones_like_ab.name, "broadcast") + self.assertEqual(ones_like_ab.domain, a.domain) + self.assertEqual(ones_like_ab.auxiliary_domains, a.auxiliary_domains) + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_parameters/test_parameter_values.py b/tests/unit/test_parameters/test_parameter_values.py index c5182c3c8c..225303211b 100644 --- a/tests/unit/test_parameters/test_parameter_values.py +++ b/tests/unit/test_parameters/test_parameter_values.py @@ -263,7 +263,9 @@ def test_process_function_parameter(self): # process constant function const = pybamm.FunctionParameter("const", a) processed_const = parameter_values.process_symbol(const) - self.assertIsInstance(processed_const, pybamm.Scalar) + self.assertIsInstance(processed_const, pybamm.Multiplication) + self.assertIsInstance(processed_const.left, pybamm.Scalar) + self.assertIsInstance(processed_const.right, pybamm.Scalar) self.assertEqual(processed_const.evaluate(), 254) # process differentiated function parameter diff --git a/tests/unit/test_parameters/test_update_parameters.py b/tests/unit/test_parameters/test_update_parameters.py index e26c153dad..b3e80b7904 100644 --- a/tests/unit/test_parameters/test_update_parameters.py +++ b/tests/unit/test_parameters/test_update_parameters.py @@ -73,7 +73,9 @@ def test_update_model(self): modeltest3.test_solving(t_eval=t_eval) Y3 = modeltest3.solution.y - self.assertIsInstance(model3.variables["Current [A]"], pybamm.Scalar) + self.assertIsInstance(model3.variables["Current [A]"], pybamm.Multiplication) + self.assertIsInstance(model3.variables["Current [A]"].left, pybamm.Scalar) + self.assertIsInstance(model3.variables["Current [A]"].right, pybamm.Scalar) self.assertEqual(model3.variables["Current [A]"].evaluate(), 0.0) # results should be different