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_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_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 update(self, variables): """Add ProcessedVariables to the dictionary of variables in the solution""" # Convert single entry to list if isinstance(variables, str): variables = [variables] # Process for key in variables: pybamm.logger.debug("Post-processing {}".format(key)) # If there are symbolic inputs then we need to make a # ProcessedSymbolicVariable if self.has_symbolic_inputs is True: var = pybamm.ProcessedSymbolicVariable( self.model.variables[key], self) # Otherwise a standard ProcessedVariable is ok else: var = pybamm.ProcessedVariable(self.model.variables[key], self, self._known_evals) # Update known_evals in order to process any other variables faster for t in var.known_evals: self._known_evals[t].update(var.known_evals[t]) # Save variable and data self._variables[key] = var self.data[key] = var.data
def update(self, variables): """Add ProcessedVariables to the dictionary of variables in the solution""" # Convert single entry to list if isinstance(variables, str): variables = [variables] # Process for key in variables: pybamm.logger.debug("Post-processing {}".format(key)) # If there are symbolic inputs then we need to make a # ProcessedSymbolicVariable if self.has_symbolic_inputs is True: var = pybamm.ProcessedSymbolicVariable( self.all_models[0].variables[key], self ) # Otherwise a standard ProcessedVariable is ok else: vars_pybamm = [model.variables[key] for model in self.all_models] # Iterate through all models, some may be in the list several times and # therefore only get set up once vars_casadi = [] for model, ys, inputs, var_pybamm in zip( self.all_models, self.all_ys, self.all_inputs, vars_pybamm ): if key in model._variables_casadi: var_casadi = model._variables_casadi[key] else: t_MX = casadi.MX.sym("t") y_MX = casadi.MX.sym("y", ys.shape[0]) symbolic_inputs_dict = { key: casadi.MX.sym("input", value.shape[0]) for key, value in inputs.items() } symbolic_inputs = casadi.vertcat( *[p for p in symbolic_inputs_dict.values()] ) # Convert variable to casadi # Make all inputs symbolic first for converting to casadi var_sym = var_pybamm.to_casadi( t_MX, y_MX, inputs=symbolic_inputs_dict ) var_casadi = casadi.Function( "variable", [t_MX, y_MX, symbolic_inputs], [var_sym] ) model._variables_casadi[key] = var_casadi vars_casadi.append(var_casadi) var = pybamm.ProcessedVariable(vars_pybamm, vars_casadi, self) # Save variable and data self._variables[key] = var self.data[key] = var.data
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})
def test_processed_variable_0D(self): # without inputs y = pybamm.StateVector(slice(0, 1)) var = 2 * y var.mesh = None t_sol = np.linspace(0, 1) y_sol = np.array([np.linspace(0, 5)]) solution = pybamm.Solution(t_sol, y_sol) processed_var = pybamm.ProcessedSymbolicVariable(var, solution) np.testing.assert_array_equal(processed_var.value(), 2 * y_sol) # No sensitivity as variable is not symbolic with self.assertRaisesRegex(ValueError, "Variable is not symbolic"): processed_var.sensitivity()
def test_processed_variable_0D_some_inputs(self): # with some symbolic inputs and some non-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": 2} processed_var = pybamm.ProcessedSymbolicVariable(var, solution) np.testing.assert_array_equal( processed_var.value({"p": 3}).full(), 3 * y_sol - 2 ) np.testing.assert_array_equal( processed_var.sensitivity({"p": 3}).full(), y_sol.T )
def test_1D_different_domains(self): # Negative electrode domain var = pybamm.Variable("var", domain=["negative electrode"]) x = pybamm.SpatialVariable("x", domain=["negative electrode"]) 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 = [0] y_sol = np.ones_like(x_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) pybamm.ProcessedSymbolicVariable(var_sol, sol) # Particle domain var = pybamm.Variable("var", 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) t_sol = [0] y_sol = np.ones_like(r_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) pybamm.ProcessedSymbolicVariable(var_sol, sol) # Current collector domain var = pybamm.Variable("var", domain=["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] var_sol = disc.process_symbol(var) t_sol = [0] y_sol = np.ones_like(z_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) pybamm.ProcessedSymbolicVariable(var_sol, sol) # Other domain var = pybamm.Variable("var", domain=["line"]) x = pybamm.SpatialVariable("x", domain=["line"]) geometry = pybamm.Geometry( {"line": {x: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}}} ) submesh_types = {"line": pybamm.MeshGenerator(pybamm.Uniform1DSubMesh)} var_pts = {x: 10} mesh = pybamm.Mesh(geometry, submesh_types, var_pts) disc = pybamm.Discretisation(mesh, {"line": pybamm.FiniteVolume()}) disc.set_variable_slices([var]) x_sol = disc.process_symbol(x).entries[:, 0] var_sol = disc.process_symbol(var) t_sol = [0] y_sol = np.ones_like(x_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) pybamm.ProcessedSymbolicVariable(var_sol, sol) # 2D fails var = pybamm.Variable( "var", domain=["negative particle"], auxiliary_domains={"secondary": "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]) r_sol = disc.process_symbol(r).entries[:, 0] var_sol = disc.process_symbol(var) t_sol = [0] y_sol = np.ones_like(r_sol)[:, np.newaxis] * 5 sol = pybamm.Solution(t_sol, y_sol) with self.assertRaisesRegex(NotImplementedError, "Shape not recognized"): pybamm.ProcessedSymbolicVariable(var_sol, sol)