From 2fb4e558962d9d0803225db532a8e8752098b468 Mon Sep 17 00:00:00 2001 From: John Brittain Date: Thu, 31 Aug 2023 13:05:50 +0100 Subject: [PATCH] Improve testing / code-coverage --- pybamm/solvers/processed_variable_var.py | 33 ++++++------- tests/unit/test_solvers/test_idaklu_solver.py | 16 +++++++ .../test_processed_variable_var.py | 48 ++++++++++++++++++- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/pybamm/solvers/processed_variable_var.py b/pybamm/solvers/processed_variable_var.py index e6cb1c5ea3..d318e88067 100644 --- a/pybamm/solvers/processed_variable_var.py +++ b/pybamm/solvers/processed_variable_var.py @@ -116,21 +116,22 @@ def _unroll_nnz(self, realdata=None): # unroll in nnz != numel, otherwise copy if realdata is None: realdata = self.base_variables_data - sp = self.base_variables_casadi[0](0, 0, 0).sparsity() - if sp.nnz() != sp.numel(): - data = [None] * len(realdata) - for datak in range(len(realdata)): - data[datak] = np.zeros(self.base_eval_shape[0] * len(self.t_pts)) - var_data = realdata[0].flatten() - k = 0 - for t_i in range(len(self.t_pts)): - base = t_i * sp.numel() - for r in sp.row(): - data[datak][base + r] = var_data[k] - k = k + 1 - else: - data = realdata - return data + # sp = self.base_variables_casadi[0](0, 0, 0).sparsity() + # if sp.nnz() != sp.numel(): + # data = [None] * len(realdata) + # for datak in range(len(realdata)): + # data[datak] = np.zeros(self.base_eval_shape[0] * len(self.t_pts)) + # var_data = realdata[0].flatten() + # k = 0 + # for t_i in range(len(self.t_pts)): + # base = t_i * sp.numel() + # for r in sp.row(): + # data[datak][base + r] = var_data[k] + # k = k + 1 + # else: + # data = realdata + # return data + return realdata def unroll_0D(self, realdata=None): if realdata is None: @@ -172,7 +173,7 @@ def unroll(self, realdata=None): return self.unroll_2D(realdata=realdata) else: # Raise error for 3D variable - raise NotImplementedError("Unsupported data dimension: {self.dimensions}") + raise NotImplementedError(f"Unsupported data dimension: {self.dimensions}") def initialise_0D(self): entries = self.unroll_0D() diff --git a/tests/unit/test_solvers/test_idaklu_solver.py b/tests/unit/test_solvers/test_idaklu_solver.py index 9c9be64984..1cde09c43e 100644 --- a/tests/unit/test_solvers/test_idaklu_solver.py +++ b/tests/unit/test_solvers/test_idaklu_solver.py @@ -76,6 +76,22 @@ def test_model_events(self): # create discretisation disc = pybamm.Discretisation() model_disc = disc.process_model(model, inplace=False) + # Invalid atol (dict) raises error, valid options are float or ndarray + self.assertRaises( + pybamm.SolverError, + pybamm.IDAKLUSolver( + rtol=1e-8, atol={'key': 'value'}, + root_method=root_method) + ) + # output_variables only valid with convert_to_format=='casadi' + if form == "python" or form == "jax": + self.assertRaises( + pybamm.SolverError, + pybamm.IDAKLUSolver( + rtol=1e-8, atol=1e-8, + output_variables=['var'], + root_method=root_method) + ) # Solve solver = pybamm.IDAKLUSolver(rtol=1e-8, atol=1e-8, root_method=root_method) t_eval = np.linspace(0, 1, 100) diff --git a/tests/unit/test_solvers/test_processed_variable_var.py b/tests/unit/test_solvers/test_processed_variable_var.py index 65c5b7ec0f..dd9b2f712b 100644 --- a/tests/unit/test_solvers/test_processed_variable_var.py +++ b/tests/unit/test_solvers/test_processed_variable_var.py @@ -82,10 +82,19 @@ def test_processed_variable_0D(self): pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) + # Assert that the processed variable is the same as the solution np.testing.assert_array_equal(processed_var.entries, y_sol[0]) + # Check that 'data' produces the same output as 'entries' + np.testing.assert_array_equal(processed_var.entries, processed_var.data) - # check empty sensitivity works + # Check unroll function + np.testing.assert_array_equal(processed_var.unroll(), y_sol[0]) + # Check cumtrapz workflow produces no errors + processed_var.cumtrapz_ic = 1 + processed_var.initialise_0D() + + # check empty sensitivity works def test_processed_variable_0D_no_sensitivity(self): # without space t = pybamm.t @@ -153,8 +162,33 @@ def test_processed_variable_1D(self): # the full solver y_sol = y_sol.reshape((y_sol.shape[1], y_sol.shape[0])).transpose() np.testing.assert_array_equal(processed_var.entries, y_sol) + np.testing.assert_array_equal(processed_var.entries, processed_var.data) np.testing.assert_array_almost_equal(processed_var(t_sol, x_sol), y_sol) + # Check unroll function + np.testing.assert_array_equal(processed_var.unroll(), y_sol) + + # Check no error when data dimension is transposed vs node/edge + processed_var.mesh.nodes, processed_var.mesh.edges = \ + processed_var.mesh.edges, processed_var.mesh.nodes + processed_var.initialise_1D() + processed_var.mesh.nodes, processed_var.mesh.edges = \ + processed_var.mesh.edges, processed_var.mesh.nodes + + # Check no errors with domain-specific attributes + # (see ProcessedVariableVar.initialise_2D() for details) + domain_list = [ + ["particle", "electrode"], + ["separator", "current collector"], + ["particle", "particle size"], + ["particle size", "electrode"], + ["particle size", "current collector"] + ] + for domain, secondary in domain_list: + processed_var.domain[0] = domain + processed_var.domains["secondary"] = [secondary] + processed_var.initialise_1D() + def test_processed_variable_1D_unknown_domain(self): x = pybamm.SpatialVariable("x", domain="SEI layer", coord_sys="cartesian") geometry = pybamm.Geometry( @@ -218,6 +252,18 @@ def test_processed_variable_2D_space_only(self): processed_var.entries, np.reshape(y_sol, [len(r_sol), len(x_sol), len(t_sol)]), ) + np.testing.assert_array_equal( + processed_var.entries, + processed_var.data, + ) + + # Check unroll function (2D) + np.testing.assert_array_equal(processed_var.unroll(), y_sol.reshape(10, 40, 1)) + + # Check unroll function (3D) + with self.assertRaises(NotImplementedError): + processed_var.dimensions = 3 + processed_var.unroll() def test_processed_variable_2D_fixed_t_scikit(self): var = pybamm.Variable("var", domain=["current collector"])