def test_processed_variable_1D(self): var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) eqn = var + x # On nodes disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] eqn_sol = disc.process_symbol(eqn) # With scalar t_sol t_sol = [0] y_sol = np.ones_like(x_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) processed_eqn = pybamm.ProcessedSymbolicVariable(eqn_sol, sol) np.testing.assert_array_equal( processed_eqn.value(), y_sol + x_sol[:, np.newaxis] ) # With vector t_sol t_sol = np.linspace(0, 1) y_sol = np.ones_like(x_sol)[:, np.newaxis] * np.linspace(0, 5) sol = pybamm.Solution(t_sol, y_sol) processed_eqn = pybamm.ProcessedSymbolicVariable(eqn_sol, sol) np.testing.assert_array_equal( processed_eqn.value(), (y_sol + x_sol[:, np.newaxis]).T.reshape(-1, 1) )
def test_processed_var_0D_interpolation(self): # without spatial dependence t = pybamm.t y = pybamm.StateVector(slice(0, 1)) var = y eqn = t * y var.mesh = None eqn.mesh = None t_sol = np.linspace(0, 1, 1000) y_sol = np.array([np.linspace(0, 5, 1000)]) processed_var = pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol)) # vector np.testing.assert_array_equal(processed_var(t_sol), y_sol[0]) # scalar np.testing.assert_array_equal(processed_var(0.5), 2.5) np.testing.assert_array_equal(processed_var(0.7), 3.5) processed_eqn = pybamm.ProcessedVariable(eqn, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal(processed_eqn(t_sol), t_sol * y_sol[0]) np.testing.assert_array_almost_equal(processed_eqn(0.5), 0.5 * 2.5) # Suppress warning for this test pybamm.set_logging_level("ERROR") np.testing.assert_array_equal(processed_eqn(2), np.nan) pybamm.set_logging_level("WARNING")
def test_processed_variable_0D(self): # without space t = pybamm.t y = pybamm.StateVector(slice(0, 1)) var = t * y var.mesh = None t_sol = np.linspace(0, 1) y_sol = np.array([np.linspace(0, 5)]) var_casadi = to_casadi(var, y_sol) processed_var = pybamm.ProcessedVariable( [var], [var_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) np.testing.assert_array_equal(processed_var.entries, t_sol * y_sol[0]) # scalar value var = y var.mesh = None t_sol = np.array([0]) y_sol = np.array([1])[:, np.newaxis] var_casadi = to_casadi(var, y_sol) processed_var = pybamm.ProcessedVariable( [var], [var_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) np.testing.assert_array_equal(processed_var.entries, y_sol[0])
def test_call_failure(self): # x domain var = pybamm.Variable("var x", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) t_sol = np.linspace(0, 1) y_sol = x_sol[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) with self.assertRaisesRegex(ValueError, "x cannot be None"): processed_var(0) # r domain var = pybamm.Variable("var r", domain=["negative particle"]) r = pybamm.SpatialVariable("r", domain=["negative particle"]) disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) r_sol = disc.process_symbol(r).entries[:, 0] var_sol = disc.process_symbol(var) y_sol = r_sol[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) with self.assertRaisesRegex(ValueError, "r cannot be None"): processed_var(0) with self.assertRaisesRegex(ValueError, "r cannot be None"): processed_var(0, 1)
def test_processed_var_1D_interpolation(self): t = pybamm.t var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) eqn = t * var + x disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) eqn_sol = disc.process_symbol(eqn) t_sol = np.linspace(0, 1) y_sol = x_sol[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable( var_sol, pybamm.Solution(t_sol, y_sol), warn=False ) # 2 vectors np.testing.assert_array_almost_equal(processed_var(t_sol, x_sol), y_sol) # 1 vector, 1 scalar np.testing.assert_array_almost_equal( processed_var(0.5, x_sol)[:, 0], 2.5 * x_sol ) np.testing.assert_array_equal( processed_var(t_sol, x_sol[-1]), x_sol[-1] * np.linspace(0, 5) ) # 2 scalars np.testing.assert_array_almost_equal( processed_var(0.5, x_sol[-1]), 2.5 * x_sol[-1] ) processed_eqn = pybamm.ProcessedVariable( eqn_sol, pybamm.Solution(t_sol, y_sol), warn=False ) # 2 vectors np.testing.assert_array_almost_equal( processed_eqn(t_sol, x_sol), t_sol * y_sol + x_sol[:, np.newaxis] ) # 1 vector, 1 scalar self.assertEqual(processed_eqn(0.5, x_sol[10:30]).shape, (20, 1)) self.assertEqual(processed_eqn(t_sol[4:9], x_sol[-1]).shape, (5,)) # 2 scalars self.assertEqual(processed_eqn(0.5, x_sol[-1]).shape, (1,)) # test x processed_x = pybamm.ProcessedVariable( disc.process_symbol(x), pybamm.Solution(t_sol, y_sol), warn=False ) np.testing.assert_array_almost_equal(processed_x(x=x_sol), x_sol[:, np.newaxis]) # On microscale r_n = pybamm.Matrix( disc.mesh["negative particle"].nodes, domain="negative particle" ) r_n.mesh = disc.mesh["negative particle"] processed_r_n = pybamm.ProcessedVariable( r_n, pybamm.Solution(t_sol, y_sol), warn=False ) np.testing.assert_array_equal(r_n.entries[:, 0], processed_r_n.entries[:, 0])
def test_add_solutions(self): # Set up first solution t1 = np.linspace(0, 1) y1 = np.tile(t1, (20, 1)) sol1 = pybamm.Solution(t1, y1, pybamm.BaseModel(), {"a": 1}) sol1.solve_time = 1.5 sol1.integration_time = 0.3 # Set up second solution t2 = np.linspace(1, 2) y2 = np.tile(t2, (20, 1)) sol2 = pybamm.Solution(t2, y2, pybamm.BaseModel(), {"a": 2}) sol2.solve_time = 1 sol2.integration_time = 0.5 sol_sum = sol1 + sol2 # Test self.assertEqual(sol_sum.solve_time, 2.5) self.assertEqual(sol_sum.integration_time, 0.8) np.testing.assert_array_equal(sol_sum.t, np.concatenate([t1, t2[1:]])) np.testing.assert_array_equal(sol_sum.y, np.concatenate([y1, y2[:, 1:]], axis=1)) np.testing.assert_array_equal(sol_sum.all_inputs, [{"a": 1}, {"a": 2}]) # Test sub-solutions self.assertEqual(len(sol_sum.sub_solutions), 2) np.testing.assert_array_equal(sol_sum.sub_solutions[0].t, t1) np.testing.assert_array_equal(sol_sum.sub_solutions[1].t, t2) self.assertEqual(sol_sum.sub_solutions[0].all_models[0], sol_sum.all_models[0]) np.testing.assert_array_equal( sol_sum.sub_solutions[0].all_inputs[0]["a"], 1) self.assertEqual(sol_sum.sub_solutions[1].all_models[0], sol2.all_models[0]) self.assertEqual(sol_sum.all_models[1], sol2.all_models[0]) np.testing.assert_array_equal( sol_sum.sub_solutions[1].all_inputs[0]["a"], 2) # Add solution already contained in existing solution t3 = np.array([2]) y3 = np.ones((20, 1)) sol3 = pybamm.Solution(t3, y3, pybamm.BaseModel(), {"a": 3}) self.assertEqual((sol_sum + sol3).all_ts, sol_sum.copy().all_ts) # radd sol4 = None + sol3 self.assertEqual(sol3.all_ys, sol4.all_ys) # radd failure with self.assertRaisesRegex( pybamm.SolverError, "Only a Solution or None can be added to a Solution"): sol3 + 2 with self.assertRaisesRegex( pybamm.SolverError, "Only a Solution or None can be added to a Solution"): 2 + sol3
def test_processed_var_2D_secondary_broadcast(self): var = pybamm.Variable("var", domain=["negative particle"]) broad_var = pybamm.SecondaryBroadcast(var, "negative electrode") x = pybamm.SpatialVariable("x", domain=["negative electrode"]) r = pybamm.SpatialVariable("r", domain=["negative particle"]) disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] r_sol = disc.process_symbol(r).entries[:, 0] var_sol = disc.process_symbol(broad_var) t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable( var_sol, pybamm.Solution(t_sol, y_sol), warn=False ) # 3 vectors np.testing.assert_array_equal( processed_var(t_sol, x_sol, r_sol).shape, (10, 40, 50) ) np.testing.assert_array_equal( processed_var(t_sol, x_sol, r_sol), np.reshape(y_sol, [len(r_sol), len(x_sol), len(t_sol)]), ) # 2 vectors, 1 scalar np.testing.assert_array_equal(processed_var(0.5, x_sol, r_sol).shape, (10, 40)) np.testing.assert_array_equal(processed_var(t_sol, 0.2, r_sol).shape, (10, 50)) np.testing.assert_array_equal(processed_var(t_sol, x_sol, 0.5).shape, (40, 50)) # 1 vectors, 2 scalar np.testing.assert_array_equal(processed_var(0.5, 0.2, r_sol).shape, (10,)) np.testing.assert_array_equal(processed_var(0.5, x_sol, 0.5).shape, (40,)) np.testing.assert_array_equal(processed_var(t_sol, 0.2, 0.5).shape, (50,)) # 3 scalars np.testing.assert_array_equal(processed_var(0.2, 0.2, 0.2).shape, ()) # positive particle var = pybamm.Variable("var", domain=["positive particle"]) broad_var = pybamm.SecondaryBroadcast(var, "positive electrode") x = pybamm.SpatialVariable("x", domain=["positive electrode"]) r = pybamm.SpatialVariable("r", domain=["positive particle"]) disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] r_sol = disc.process_symbol(r).entries[:, 0] var_sol = disc.process_symbol(broad_var) t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable( var_sol, pybamm.Solution(t_sol, y_sol), warn=False ) # 3 vectors np.testing.assert_array_equal( processed_var(t_sol, x_sol, r_sol).shape, (10, 35, 50) )
def test_processed_variable_2D_x_z(self): var = pybamm.Variable( "var", domain=["negative electrode", "separator"], auxiliary_domains={"secondary": "current collector"}, ) x = pybamm.SpatialVariable( "x", domain=["negative electrode", "separator"], auxiliary_domains={"secondary": "current collector"}, ) z = pybamm.SpatialVariable("z", domain=["current collector"]) disc = tests.get_1p1d_discretisation_for_testing() disc.set_variable_slices([var]) z_sol = disc.process_symbol(z).entries[:, 0] x_sol = disc.process_symbol(x).entries[:, 0] # Keep only the first iteration of entries x_sol = x_sol[:len(x_sol) // len(z_sol)] var_sol = disc.process_symbol(var) t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(z_sol))[:, np.newaxis] * np.linspace( 0, 5) var_casadi = to_casadi(var_sol, y_sol) processed_var = pybamm.ProcessedVariable( [var_sol], [var_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) np.testing.assert_array_equal( processed_var.entries, np.reshape( y_sol, [len(x_sol), len(z_sol), len(t_sol)]), ) # On edges x_s_edge = pybamm.Matrix( np.tile(disc.mesh["separator"].edges, len(z_sol)), domain="separator", auxiliary_domains={"secondary": "current collector"}, ) x_s_edge.mesh = disc.mesh["separator"] x_s_edge.secondary_mesh = disc.mesh["current collector"] x_s_casadi = to_casadi(x_s_edge, y_sol) processed_x_s_edge = pybamm.ProcessedVariable( [x_s_edge], [x_s_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) np.testing.assert_array_equal( x_s_edge.entries.flatten(), processed_x_s_edge.entries[:, :, 0].T.flatten())
def test_processed_variable_1D(self): t = pybamm.t var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) eqn = t * var + x # On nodes disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) eqn_sol = disc.process_symbol(eqn) t_sol = np.linspace(0, 1) y_sol = np.ones_like(x_sol)[:, np.newaxis] * np.linspace(0, 5) processed_var = pybamm.ProcessedVariable( var_sol, pybamm.Solution(t_sol, y_sol), warn=False ) np.testing.assert_array_equal(processed_var.entries, y_sol) np.testing.assert_array_equal(processed_var(t_sol, x_sol), y_sol) processed_eqn = pybamm.ProcessedVariable( eqn_sol, pybamm.Solution(t_sol, y_sol), warn=False ) np.testing.assert_array_equal( processed_eqn(t_sol, x_sol), t_sol * y_sol + x_sol[:, np.newaxis] ) # Test extrapolation np.testing.assert_array_equal(processed_var.entries[0], 2 * y_sol[0] - y_sol[1]) np.testing.assert_array_equal( processed_var.entries[1], 2 * y_sol[-1] - y_sol[-2] ) # On edges x_s_edge = pybamm.Matrix(disc.mesh["separator"].edges, domain="separator") x_s_edge.mesh = disc.mesh["separator"] processed_x_s_edge = pybamm.ProcessedVariable( x_s_edge, pybamm.Solution(t_sol, y_sol), warn=False ) np.testing.assert_array_equal( x_s_edge.entries[:, 0], processed_x_s_edge.entries[:, 0] ) # space only eqn = var + x eqn_sol = disc.process_symbol(eqn) t_sol = np.array([0]) y_sol = np.ones_like(x_sol)[:, np.newaxis] processed_eqn2 = pybamm.ProcessedVariable( eqn_sol, pybamm.Solution(t_sol, y_sol), warn=False ) np.testing.assert_array_equal( processed_eqn2.entries, y_sol + x_sol[:, np.newaxis] )
def test_processed_variable_1D_with_scalar_inputs(self): var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) p = pybamm.InputParameter("p") q = pybamm.InputParameter("q") eqn = var * p + 2 * q # On nodes disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] eqn_sol = disc.process_symbol(eqn) # Scalar t t_sol = [0] y_sol = np.ones_like(x_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) sol.inputs = {"p": casadi.MX.sym("p"), "q": casadi.MX.sym("q")} processed_eqn = pybamm.ProcessedSymbolicVariable(eqn_sol, sol) # Test values np.testing.assert_array_equal( processed_eqn.value({"p": 27, "q": -42}), 27 * y_sol - 84, ) # Test sensitivities np.testing.assert_array_equal( processed_eqn.sensitivity({"p": 27, "q": -84}), np.c_[y_sol, 2 * np.ones_like(y_sol)], ) ################################################################################ # Vector t t_sol = np.linspace(0, 1) y_sol = np.ones_like(x_sol)[:, np.newaxis] * np.linspace(0, 5) sol = pybamm.Solution(t_sol, y_sol) sol.inputs = {"p": casadi.MX.sym("p"), "q": casadi.MX.sym("q")} processed_eqn = pybamm.ProcessedSymbolicVariable(eqn_sol, sol) # Test values np.testing.assert_array_equal( processed_eqn.value({"p": 27, "q": -42}), (27 * y_sol - 84).T.reshape(-1, 1), ) # Test sensitivities np.testing.assert_array_equal( processed_eqn.sensitivity({"p": 27, "q": -42}), np.c_[y_sol.T.flatten(), 2 * np.ones_like(y_sol.T.flatten())], )
def test_call_failure(self): # x domain var = pybamm.Variable("var x", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) t_sol = np.linspace(0, 1) y_sol = x_sol[:, np.newaxis] * np.linspace(0, 5) var_casadi = to_casadi(var_sol, y_sol) processed_var = pybamm.ProcessedVariable( [var_sol], [var_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) with self.assertRaisesRegex(ValueError, "x cannot be None"): processed_var(0) # r domain var = pybamm.Variable("var r", domain=["negative particle"]) r = pybamm.SpatialVariable( "r", domain=["negative particle"], auxiliary_domains={"secondary": ["negative electrode"]}, ) disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) r_sol = disc.process_symbol(r).entries[:, 0] var_sol = disc.process_symbol(var) y_sol = r_sol[:, np.newaxis] * np.linspace(0, 5) var_casadi = to_casadi(var_sol, y_sol) processed_var = pybamm.ProcessedVariable( [var_sol], [var_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) with self.assertRaisesRegex(ValueError, "r cannot be None"): processed_var(0) with self.assertRaisesRegex(ValueError, "r cannot be None"): processed_var(0, 1) # t is None but len(solution.t) > 1 with self.assertRaisesRegex(ValueError, "t cannot be None"): processed_var()
def test_processed_variable_1D_unknown_domain(self): x = pybamm.SpatialVariable("x", domain="SEI layer", coord_sys="cartesian") geometry = pybamm.Geometry() geometry.add_domain( "SEI layer", { "primary": { x: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } }, ) submesh_types = {"SEI layer": pybamm.Uniform1DSubMesh} var_pts = {x: 100} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) nt = 100 solution = pybamm.Solution( np.linspace(0, 1, nt), np.zeros((var_pts[x], nt)), np.linspace(0, 1, 1), np.zeros((var_pts[x])), "test", ) c = pybamm.StateVector(slice(0, var_pts[x]), domain=["SEI layer"]) c.mesh = mesh["SEI layer"] pybamm.ProcessedVariable(c, solution)
def test_processed_variable_2D_x_r(self): var = pybamm.Variable( "var", domain=["negative particle"], auxiliary_domains={"secondary": ["negative electrode"]}, ) x = pybamm.SpatialVariable("x", domain=["negative electrode"]) r = pybamm.SpatialVariable("r", domain=["negative particle"]) disc = tests.get_p2d_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] r_sol = disc.process_symbol(r).entries[:, 0] # Keep only the first iteration of entries r_sol = r_sol[:len(r_sol) // len(x_sol)] var_sol = disc.process_symbol(var) t_sol = np.linspace(0, 1) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] * np.linspace( 0, 5) processed_var = pybamm.ProcessedVariable(var_sol, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal( processed_var.entries, np.reshape( y_sol, [len(r_sol), len(x_sol), len(t_sol)]), )
def _integrate(self, model, t_eval, inputs_dict=None): """ Solve an empty model. Parameters ---------- model : :class:`pybamm.BaseModel` The model whose solution to calculate. t_eval : :class:`numpy.array`, size (k,) The times at which to compute the solution inputs_dict : dict, optional Any input parameters to pass to the model when solving Returns ------- :class:`pybamm.Solution` A Solution object containing the times and values of the solution, as well as various diagnostic messages. """ y_sol = np.zeros((1, t_eval.size)) sol = pybamm.Solution(t_eval, y_sol, model, inputs_dict, termination="final time") sol.integration_time = 0 return sol
def test_processed_variable_0D_with_inputs(self): # with symbolic inputs y = pybamm.StateVector(slice(0, 1)) p = pybamm.InputParameter("p") q = pybamm.InputParameter("q") var = p * y + q var.mesh = None t_sol = np.linspace(0, 1) y_sol = np.array([np.linspace(0, 5)]) solution = pybamm.Solution(t_sol, y_sol) solution.inputs = {"p": casadi.MX.sym("p"), "q": casadi.MX.sym("q")} processed_var = pybamm.ProcessedSymbolicVariable(var, solution) np.testing.assert_array_equal( processed_var.value({"p": 3, "q": 4}).full(), 3 * y_sol + 4 ) np.testing.assert_array_equal( processed_var.sensitivity({"p": 3, "q": 4}).full(), np.c_[y_sol.T, np.ones_like(y_sol).T], ) # via value_and_sensitivity val, sens = processed_var.value_and_sensitivity({"p": 3, "q": 4}) np.testing.assert_array_equal(val.full(), 3 * y_sol + 4) np.testing.assert_array_equal( sens.full(), np.c_[y_sol.T, np.ones_like(y_sol).T] ) # Test bad inputs with self.assertRaisesRegex(TypeError, "inputs should be 'dict'"): processed_var.value(1) with self.assertRaisesRegex(KeyError, "Inconsistent input keys"): processed_var.value({"not p": 3})
def test_processed_var_2D_fixed_t_scikit_interpolation(self): var = pybamm.Variable("var", domain=["current collector"]) disc = tests.get_2p1d_discretisation_for_testing() disc.set_variable_slices([var]) y_sol = disc.mesh["current collector"].edges["y"] z_sol = disc.mesh["current collector"].edges["z"] var_sol = disc.process_symbol(var) var_sol.mesh = disc.mesh["current collector"] t_sol = np.array([0]) u_sol = np.ones(var_sol.shape[0])[:, np.newaxis] var_casadi = to_casadi(var_sol, u_sol) processed_var = pybamm.ProcessedVariable( [var_sol], [var_casadi], pybamm.Solution(t_sol, u_sol, pybamm.BaseModel(), {}), warn=False, ) # 2 vectors np.testing.assert_array_equal( processed_var(y=y_sol, z=z_sol).shape, (15, 15)) # 1 vector, 1 scalar np.testing.assert_array_equal( processed_var(y=0.2, z=z_sol).shape, (15, )) np.testing.assert_array_equal( processed_var(y=y_sol, z=0.5).shape, (15, )) # 2 scalars np.testing.assert_array_equal( processed_var(t=None, y=0.2, z=0.2).shape, ())
def test_processed_var_1D_fixed_t_interpolation(self): var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) eqn = var + x disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] eqn_sol = disc.process_symbol(eqn) t_sol = np.array([1]) y_sol = x_sol[:, np.newaxis] eqn_casadi = to_casadi(eqn_sol, y_sol) processed_var = pybamm.ProcessedVariable( [eqn_sol], [eqn_casadi], pybamm.Solution(t_sol, y_sol, pybamm.BaseModel(), {}), warn=False, ) # vector np.testing.assert_array_almost_equal(processed_var(x=x_sol), 2 * x_sol[:, np.newaxis]) # scalar np.testing.assert_array_almost_equal(processed_var(x=0.5), 1)
def _integrate(self, model, t_eval, inputs=None): """ Solve a model defined by dydt with initial conditions y0. Parameters ---------- model : :class:`pybamm.BaseModel` The model whose solution to calculate. t_eval : :class:`numpy.array`, size (k,) The times at which to compute the solution inputs : dict, optional Any input parameters to pass to the model when solving Returns ------- object An object containing the times and values of the solution, as well as various diagnostic messages. """ if model not in self._cached_solves: self._cached_solves[model] = self.create_solve(model, t_eval) y = self._cached_solves[model](inputs) # note - the actual solve is not done until this line! y = onp.array(y) termination = "final time" t_event = None y_event = onp.array(None) return pybamm.Solution(t_eval, y, t_event, y_event, termination)
def test_processed_variable_1D_unknown_domain(self): x = pybamm.SpatialVariable("x", domain="SEI layer", coord_sys="cartesian") geometry = pybamm.Geometry({ "SEI layer": { x: { "min": pybamm.Scalar(0), "max": pybamm.Scalar(1) } } }) submesh_types = {"SEI layer": pybamm.Uniform1DSubMesh} var_pts = {x: 100} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) nt = 100 y_sol = np.zeros((var_pts[x], nt)) solution = pybamm.Solution( np.linspace(0, 1, nt), y_sol, pybamm.BaseModel(), {}, np.linspace(0, 1, 1), np.zeros((var_pts[x])), "test", ) c = pybamm.StateVector(slice(0, var_pts[x]), domain=["SEI layer"]) c.mesh = mesh["SEI layer"] c_casadi = to_casadi(c, y_sol) pybamm.ProcessedVariable([c], [c_casadi], solution, warn=False)
def test_processed_var_2D_fixed_t_interpolation(self): var = pybamm.Variable( "var", domain=["negative particle"], auxiliary_domains={"secondary": ["negative electrode"]}, ) x = pybamm.SpatialVariable("x", domain=["negative electrode"]) r = pybamm.SpatialVariable( "r", domain=["negative particle"], auxiliary_domains={"secondary": ["negative electrode"]}, ) disc = tests.get_p2d_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] r_sol = disc.process_symbol(r).entries[:, 0] # Keep only the first iteration of entries r_sol = r_sol[: len(r_sol) // len(x_sol)] var_sol = disc.process_symbol(var) t_sol = np.array([0]) y_sol = np.ones(len(x_sol) * len(r_sol))[:, np.newaxis] processed_var = pybamm.ProcessedVariable( var_sol, pybamm.Solution(t_sol, y_sol), warn=False ) # 2 vectors np.testing.assert_array_equal(processed_var(x=x_sol, r=r_sol).shape, (10, 40)) # 1 vector, 1 scalar np.testing.assert_array_equal(processed_var(x=0.2, r=r_sol).shape, (10,)) np.testing.assert_array_equal(processed_var(x=x_sol, r=0.5).shape, (40,)) # 2 scalars np.testing.assert_array_equal(processed_var(x=0.2, r=0.2).shape, ())
def _integrate(self, model, t_eval, inputs=None): """ Solve a model defined by dydt with initial conditions y0. Parameters ---------- model : :class:`pybamm.BaseModel` The model whose solution to calculate. t_eval : :class:`numpy.array`, size (k,) The times at which to compute the solution inputs : dict, optional Any input parameters to pass to the model when solving Returns ------- object An object containing the times and values of the solution, as well as various diagnostic messages. """ extra_options = {"rtol": self.rtol, "atol": self.atol} # check for user-supplied Jacobian implicit_methods = ["Radau", "BDF", "LSODA"] if np.any([self.method in implicit_methods]): if model.jacobian_eval: extra_options.update({"jac": model.jacobian_eval}) # make events terminal so that the solver stops when they are reached if model.terminate_events_eval: for event in model.terminate_events_eval: event.terminal = True extra_options.update({"events": model.terminate_events_eval}) sol = it.solve_ivp(model.rhs_eval, (t_eval[0], t_eval[-1]), model.y0, t_eval=t_eval, method=self.method, dense_output=True, **extra_options) if sol.success: # Set the reason for termination if sol.message == "A termination event occurred.": termination = "event" t_event = [] for time in sol.t_events: if time.size > 0: t_event = np.append(t_event, np.max(time)) t_event = np.array([np.max(t_event)]) y_event = sol.sol(t_event) elif sol.message.startswith( "The solver successfully reached the end"): termination = "final time" t_event = None y_event = np.array(None) return pybamm.Solution(sol.t, sol.y, t_event, y_event, termination) else: raise pybamm.SolverError(sol.message)
def test_errors(self): bad_ts = [np.array([1, 2, 3]), np.array([3, 4, 5])] sol = pybamm.Solution(bad_ts, [np.ones( (1, 3)), np.ones((1, 3))], pybamm.BaseModel(), {}) with self.assertRaisesRegex( ValueError, "Solution time vector must be strictly increasing"): sol.set_t()
def test_append(self): # Set up first solution t1 = np.linspace(0, 1) y1 = np.tile(t1, (20, 1)) sol1 = pybamm.Solution(t1, y1) sol1.solve_time = 1.5 sol1.integration_time = 0.3 sol1.model = pybamm.BaseModel() sol1.inputs = {"a": 1} # Set up second solution t2 = np.linspace(1, 2) y2 = np.tile(t2, (20, 1)) sol2 = pybamm.Solution(t2, y2) sol2.solve_time = 1 sol2.integration_time = 0.5 sol2.inputs = {"a": 2} sol1.append(sol2, create_sub_solutions=True) # Test self.assertEqual(sol1.solve_time, 2.5) self.assertEqual(sol1.integration_time, 0.8) np.testing.assert_array_equal(sol1.t, np.concatenate([t1, t2[1:]])) np.testing.assert_array_equal(sol1.y, np.concatenate([y1, y2[:, 1:]], axis=1)) np.testing.assert_array_equal( sol1.inputs["a"], np.concatenate([1 * np.ones_like(t1), 2 * np.ones_like(t2[1:])])[ np.newaxis, : ], ) # Test sub-solutions self.assertEqual(len(sol1.sub_solutions), 2) np.testing.assert_array_equal(sol1.sub_solutions[0].t, t1) np.testing.assert_array_equal(sol1.sub_solutions[1].t, t2) self.assertEqual(sol1.sub_solutions[0].model, sol1.model) np.testing.assert_array_equal( sol1.sub_solutions[0].inputs["a"], 1 * np.ones_like(t1)[np.newaxis, :] ) self.assertEqual(sol1.sub_solutions[1].model, sol2.model) np.testing.assert_array_equal( sol1.sub_solutions[1].inputs["a"], 2 * np.ones_like(t2)[np.newaxis, :] )
def _run_integrator(self, model, y0, inputs, t_eval): integrator, use_grid = self.integrators[model] len_rhs = model.concatenated_rhs.size y0_diff = y0[:len_rhs] y0_alg = y0[len_rhs:] try: # Try solving if use_grid is True: # Call the integrator once, with the grid sol = integrator(x0=y0_diff, z0=y0_alg, p=inputs, **self.extra_options_call) y_sol = np.concatenate([sol["xf"].full(), sol["zf"].full()]) return pybamm.Solution(t_eval, y_sol) else: # Repeated calls to the integrator x = y0_diff z = y0_alg y_diff = x y_alg = z for i in range(len(t_eval) - 1): t_min = t_eval[i] t_max = t_eval[i + 1] inputs_with_tlims = casadi.vertcat(inputs, t_min, t_max) sol = integrator(x0=x, z0=z, p=inputs_with_tlims, **self.extra_options_call) x = sol["xf"] z = sol["zf"] y_diff = casadi.horzcat(y_diff, x) if not z.is_empty(): y_alg = casadi.horzcat(y_alg, z) if z.is_empty(): return pybamm.Solution(t_eval, y_diff) else: y_sol = casadi.vertcat(y_diff, y_alg) return pybamm.Solution(t_eval, y_sol) except RuntimeError as e: # If it doesn't work raise error raise pybamm.SolverError(e.args[0])
def test_init(self): t = np.linspace(0, 1) y = np.tile(t, (20, 1)) sol = pybamm.Solution(t, y, pybamm.BaseModel(), {}) np.testing.assert_array_equal(sol.t, t) np.testing.assert_array_equal(sol.y, y) self.assertEqual(sol.t_event, None) self.assertEqual(sol.y_event, None) self.assertEqual(sol.termination, "final time") self.assertEqual(sol.all_inputs, [{}]) self.assertIsInstance(sol.all_models[0], pybamm.BaseModel)
def test_processed_variable_0D(self): # without space t = pybamm.t y = pybamm.StateVector(slice(0, 1)) var = t * y var.mesh = None t_sol = np.linspace(0, 1) y_sol = np.array([np.linspace(0, 5)]) processed_var = pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol)) np.testing.assert_array_equal(processed_var.entries, t_sol * y_sol[0])
def test_solution_too_short(self): t = pybamm.t y = pybamm.StateVector(slice(0, 1)) var = t * y var.mesh = None t_sol = np.array([1]) y_sol = np.array([np.linspace(0, 5)]) with self.assertRaisesRegex( pybamm.SolverError, "Solution time vector must have length > 1"): pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol))
def test_solution_too_short(self): t = pybamm.t y = pybamm.StateVector(slice(0, 3)) var = t * y disc = tests.get_2p1d_discretisation_for_testing() var.mesh = disc.mesh["current collector"] t_sol = np.array([1]) y_sol = np.linspace(0, 5)[:, np.newaxis] with self.assertRaisesRegex( pybamm.SolverError, "Solution time vector must have length > 1"): pybamm.ProcessedVariable(var, pybamm.Solution(t_sol, y_sol))
def test_failure(self): with self.assertRaisesRegex(TypeError, "'models' must be"): pybamm.QuickPlot(1, None, None) with self.assertRaisesRegex(TypeError, "'meshes' must be"): model = pybamm.lithium_ion.SPM() pybamm.QuickPlot(model, 1, None) with self.assertRaisesRegex(TypeError, "'solutions' must be"): geometry = model.default_geometry param = model.default_parameter_values param.process_model(model) param.process_geometry(geometry) mesh = pybamm.Mesh( geometry, model.default_submesh_types, model.default_var_pts ) pybamm.QuickPlot(model, mesh, 1) with self.assertRaisesRegex(ValueError, "must provide the same"): pybamm.QuickPlot( model, mesh, [pybamm.Solution(0, 0, 0, 0, ""), pybamm.Solution(0, 0, 0, 0, "")], )
def test_processed_variable_1D_with_vector_inputs(self): var = pybamm.Variable("var", domain=["negative electrode", "separator"]) x = pybamm.SpatialVariable("x", domain=["negative electrode", "separator"]) p = pybamm.InputParameter("p", domain=["negative electrode", "separator"]) p.set_expected_size(65) q = pybamm.InputParameter("q") eqn = (var * p) ** 2 + 2 * q # On nodes disc = tests.get_discretisation_for_testing() disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] n = x_sol.size eqn_sol = disc.process_symbol(eqn) # Scalar t t_sol = [0] y_sol = np.ones_like(x_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) sol.inputs = {"p": casadi.MX.sym("p", n), "q": casadi.MX.sym("q")} processed_eqn = pybamm.ProcessedSymbolicVariable(eqn_sol, sol) # Test values - constant p np.testing.assert_array_equal( processed_eqn.value({"p": 27 * np.ones(n), "q": -42}), (27 * y_sol) ** 2 - 84, ) # Test values - varying p p = np.linspace(0, 1, n) np.testing.assert_array_equal( processed_eqn.value({"p": p, "q": 3}), (p[:, np.newaxis] * y_sol) ** 2 + 6 ) # Test sensitivities - constant p np.testing.assert_array_equal( processed_eqn.sensitivity({"p": 2 * np.ones(n), "q": -84}), np.c_[100 * np.eye(y_sol.size), 2 * np.ones(n)], ) # Test sensitivities - varying p # d/dy((py)**2) = (2*p*y) * y np.testing.assert_array_equal( processed_eqn.sensitivity({"p": p, "q": -84}), np.c_[ np.diag((2 * p[:, np.newaxis] * y_sol ** 2).flatten()), 2 * np.ones(n) ], ) # Bad shape with self.assertRaisesRegex( ValueError, "Wrong shape for input 'p': expected 65, actual 5" ): processed_eqn.value({"p": casadi.MX.sym("p", 5), "q": 1})