def test_model_solver_dae_inputs_events(self):
        # Create model
        for form in ["python", "casadi"]:
            model = pybamm.BaseModel()
            model.convert_to_format = form
            whole_cell = ["negative electrode", "separator", "positive electrode"]
            var1 = pybamm.Variable("var1", domain=whole_cell)
            var2 = pybamm.Variable("var2", domain=whole_cell)
            model.rhs = {var1: pybamm.InputParameter("rate 1") * var1}
            model.algebraic = {var2: pybamm.InputParameter("rate 2") * var1 - var2}
            model.initial_conditions = {var1: 1, var2: 2}
            model.events = [
                pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
                pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)),
            ]
            disc = get_discretisation_for_testing()
            disc.process_model(model)

            # Solve
            if form == "python":
                solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm")
            else:
                solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
            t_eval = np.linspace(0, 5, 100)
            solution = solver.solve(model, t_eval, inputs={"rate 1": 0.1, "rate 2": 2})
            np.testing.assert_array_less(solution.y[0], 1.5)
            np.testing.assert_array_less(solution.y[-1], 2.5)
            np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
            np.testing.assert_allclose(solution.y[-1], 2 * np.exp(0.1 * solution.t))
Exemple #2
0
    def test_dae_integrate(self):
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def constant_growth_dae(t, y, ydot):
            return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]]

        y0 = np.array([0, 0])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(constant_growth_dae, y0, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.5 * solution.t, solution.y[0])
        np.testing.assert_allclose(1.0 * solution.t, solution.y[1])

        # Exponential decay
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def exponential_decay_dae(t, y, ydot):
            return [-0.1 * y[0] - ydot[0], 2 * y[0] - y[1]]

        y0 = np.array([1, 2])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(exponential_decay_dae, y0, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t))
        np.testing.assert_allclose(solution.y[1],
                                   2 * np.exp(-0.1 * solution.t))
        self.assertEqual(solution.termination, "final time")
Exemple #3
0
    def test_dae_integrate_with_event(self):
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def constant_growth_dae(t, y, ydot):
            return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]]

        def y0_eq_2(t, y):
            return y[0] - 2

        def y1_eq_5(t, y):
            return y[1] - 5

        y0 = np.array([0, 0])
        t_eval = np.linspace(0, 7, 100)
        solution = solver.integrate(constant_growth_dae,
                                    y0,
                                    t_eval,
                                    events=[y0_eq_2, y1_eq_5])
        self.assertLess(len(solution.t), len(t_eval))
        np.testing.assert_allclose(0.5 * solution.t, solution.y[0])
        np.testing.assert_allclose(1.0 * solution.t, solution.y[1])
        np.testing.assert_array_less(solution.y[0], 2)
        np.testing.assert_array_less(solution.y[1], 5)
        np.testing.assert_allclose(solution.t_event, 4)
        np.testing.assert_allclose(solution.y_event[0], 2)
        np.testing.assert_allclose(solution.y_event[1], 4)
        self.assertEqual(solution.termination, "event")

        # Exponential decay
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def exponential_decay_dae(t, y, ydot):
            return np.array([-0.1 * y[0] - ydot[0], 2 * y[0] - y[1]])

        def y0_eq_0pt9(t, y):
            return y[0] - 0.9

        def t_eq_0pt5(t, y):
            return t - 0.5

        y0 = np.array([1, 2])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(exponential_decay_dae,
                                    y0,
                                    t_eval,
                                    events=[y0_eq_0pt9, t_eq_0pt5])

        self.assertLess(len(solution.t), len(t_eval))
        np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t))
        np.testing.assert_allclose(solution.y[1],
                                   2 * np.exp(-0.1 * solution.t))
        np.testing.assert_array_less(0.9, solution.y[0])
        np.testing.assert_array_less(solution.t, 0.5)
        np.testing.assert_allclose(solution.t_event, 0.5)
        np.testing.assert_allclose(solution.y_event[0], np.exp(-0.1 * 0.5))
        np.testing.assert_allclose(solution.y_event[1], 2 * np.exp(-0.1 * 0.5))
        self.assertEqual(solution.termination, "event")
Exemple #4
0
    def test_model_solver_dae_with_jacobian(self):
        # Create simple test model
        model = pybamm.BaseModel()
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1.0, var2: 2.0}
        model.initial_conditions_ydot = {var1: 0.1, var2: 0.2}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Add user-supplied Jacobian to model
        mesh = get_mesh_for_testing()
        combined_submesh = mesh.combine_submeshes("negative electrode",
                                                  "separator",
                                                  "positive electrode")
        N = combined_submesh[0].npts

        def jacobian(t, y):
            return np.block([
                [0.1 * np.eye(N), np.zeros((N, N))],
                [2.0 * np.eye(N), -1.0 * np.eye(N)],
            ])

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1],
                                   2 * np.exp(0.1 * solution.t))
    def test_model_step_dae_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        model.use_jacobian = False
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8, root_method="lm")

        # Step once
        dt = 1
        step_sol = solver.step(None, model, dt)
        np.testing.assert_array_equal(step_sol.t, [0, dt])
        np.testing.assert_allclose(step_sol.y[0], np.exp(0.1 * step_sol.t))
        np.testing.assert_allclose(step_sol.y[-1], 2 * np.exp(0.1 * step_sol.t))

        # Step again (return 5 points)
        step_sol_2 = solver.step(step_sol, model, dt, npts=5)
        np.testing.assert_array_equal(
            step_sol_2.t, np.concatenate([np.array([0]), np.linspace(dt, 2 * dt, 5)])
        )
        np.testing.assert_allclose(step_sol_2.y[0], np.exp(0.1 * step_sol_2.t))
        np.testing.assert_allclose(step_sol_2.y[-1], 2 * np.exp(0.1 * step_sol_2.t))

        # Check steps give same solution as solve
        t_eval = step_sol.t
        solution = solver.solve(model, t_eval)
        np.testing.assert_allclose(solution.y[0], step_sol.y[0, :])
        np.testing.assert_allclose(solution.y[-1], step_sol.y[-1, :])
Exemple #6
0
    def test_solver_citations(self):
        # Test that solving each solver adds the right citations
        citations = pybamm.citations

        citations._reset()
        self.assertNotIn("virtanen2020scipy", citations._papers_to_cite)
        pybamm.ScipySolver()
        self.assertIn("virtanen2020scipy", citations._papers_to_cite)

        citations._reset()
        self.assertNotIn("virtanen2020scipy", citations._papers_to_cite)
        pybamm.AlgebraicSolver()
        self.assertIn("virtanen2020scipy", citations._papers_to_cite)

        if pybamm.have_scikits_odes():
            citations._reset()
            self.assertNotIn("scikits-odes", citations._papers_to_cite)
            pybamm.ScikitsOdeSolver()
            self.assertIn("scikits-odes", citations._papers_to_cite)

            citations._reset()
            self.assertNotIn("scikits-odes", citations._papers_to_cite)
            pybamm.ScikitsDaeSolver()
            self.assertIn("scikits-odes", citations._papers_to_cite)

        if pybamm.have_idaklu():
            citations._reset()
            self.assertNotIn("hindmarsh2005sundials",
                             citations._papers_to_cite)
            pybamm.IDAKLUSolver()
            self.assertIn("hindmarsh2005sundials", citations._papers_to_cite)
Exemple #7
0
    def default_solver(self):
        """
        Create and return the default solver for this model
        """

        # Default solver to DAE
        return pybamm.ScikitsDaeSolver()
    def test_model_solver_dae_events_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        model.events = [
            pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
            pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)),
        ]
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8,
                                         atol=1e-8,
                                         root_method="lm")
        t_eval = np.linspace(0, 5, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_less(solution.y[0], 1.5)
        np.testing.assert_array_less(solution.y[-1], 2.5)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1],
                                   2 * np.exp(0.1 * solution.t))
    def test_dae_integrate_bad_ics(self):
        # Make sure that dae solver can fix bad ics automatically
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        model = pybamm.BaseModel()
        model.timescale_eval = 1
        var = pybamm.Variable("var")
        var2 = pybamm.Variable("var2")
        model.rhs = {var: 0.5}
        model.algebraic = {var2: 2 * var - var2}
        model.initial_conditions = {var: 0, var2: 1}
        disc = pybamm.Discretisation()
        disc.process_model(model)

        t_eval = np.linspace(0, 1, 100)
        solver.set_up(model)
        solver._set_initial_conditions(model, {}, True)
        # check y0
        np.testing.assert_array_equal(model.y0, [0, 0])
        # check dae solutions
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.5 * solution.t, solution.y[0])
        np.testing.assert_allclose(1.0 * solution.t, solution.y[1])
Exemple #10
0
    def test_model_solver_dae_with_external(self):
        # Create model
        model = pybamm.BaseModel()
        domain = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=domain)
        var2 = pybamm.Variable("var2", domain=domain)
        model.rhs = {var1: -var2}
        model.initial_conditions = {var1: 1}
        model.external_variables = [var2]
        model.variables = {"var1": var1, "var2": var2}
        # No need to set parameters; can use base discretisation (no spatial
        # operators)

        # create discretisation
        mesh = get_mesh_for_testing()
        spatial_methods = {"macroscale": pybamm.FiniteVolume()}
        disc = pybamm.Discretisation(mesh, spatial_methods)
        disc.process_model(model)
        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 10, 100)
        solution = solver.solve(model,
                                t_eval,
                                external_variables={"var2": 0.5})
        np.testing.assert_allclose(solution.y[0],
                                   1 - 0.5 * solution.t,
                                   rtol=1e-06)
Exemple #11
0
    def test_model_solver_dae(self):
        # Create model
        model = pybamm.BaseModel()
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        model.use_jacobian = False
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1],
                                   2 * np.exp(0.1 * solution.t))

        # Test time
        self.assertGreater(solution.total_time,
                           solution.solve_time + solution.set_up_time)
    def test_model_dae_integrate_failure_bad_ics(self):
        # Force model to fail by providing bad ics
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        # Create custom model so that custom ics
        class Model:
            mass_matrix = pybamm.Matrix([[1.0, 0.0], [0.0, 0.0]])
            y0 = np.array([0.0, 1.0])
            terminate_events_eval = []
            timescale_eval = 1
            length_scales = {}
            convert_to_format = "python"

            def residuals_eval(self, t, y, ydot, inputs):
                return np.array(
                    [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]])

            def jacobian_eval(self, t, y, inputs):
                return np.array([[0.0, 0.0], [2.0, -1.0]])

        model = Model()
        t_eval = np.linspace(0, 1, 100)

        with self.assertRaises(pybamm.SolverError):
            solver._integrate(model, t_eval)
Exemple #13
0
    def test_model_step_events(self):
        # Create model
        model = pybamm.BaseModel()
        var1 = pybamm.Variable("var1")
        var2 = pybamm.Variable("var2")
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        model.events = [
            pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
            pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)),
        ]
        disc = pybamm.Discretisation()
        disc.process_model(model)

        # Solve
        step_solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        dt = 0.05
        time = 0
        end_time = 5
        step_solution = None
        while time < end_time:
            step_solution = step_solver.step(step_solution,
                                             model,
                                             dt=dt,
                                             npts=10)
            time += dt
        np.testing.assert_array_less(step_solution.y[0], 1.5)
        np.testing.assert_array_less(step_solution.y[-1], 2.5001)
        np.testing.assert_array_almost_equal(step_solution.y[0],
                                             np.exp(0.1 * step_solution.t),
                                             decimal=5)
        np.testing.assert_array_almost_equal(step_solution.y[-1],
                                             2 * np.exp(0.1 * step_solution.t),
                                             decimal=5)
Exemple #14
0
    def test_dae_integrate_with_non_unity_mass(self):
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        # Create custom model so that custom mass matrix can be used
        class Model:
            mass_matrix = pybamm.Matrix(np.array([[4.0, 0.0], [0.0, 0.0]]))
            y0 = np.array([0.0, 0.0])
            terminate_events_eval = []
            timescale_eval = 1
            convert_to_format = "python"

            def residuals_eval(self, t, y, ydot, inputs):
                return np.array([
                    0.5 * np.ones_like(y[0]) - 4 * ydot[0], 2.0 * y[0] - y[1]
                ])

            def jacobian_eval(self, t, y, inputs):
                return np.array([[0.0, 0.0], [2.0, -1.0]])

        model = Model()
        t_eval = np.linspace(0, 1, 100)
        solution = solver._integrate(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.125 * solution.t, solution.y[0])
        np.testing.assert_allclose(0.25 * solution.t, solution.y[1])
Exemple #15
0
    def test_model_solver_dae_events_casadi(self):
        # Create model
        model = pybamm.BaseModel()
        for use_jacobian in [True, False]:
            model.use_jacobian = use_jacobian
            model.convert_to_format = "casadi"
            whole_cell = [
                "negative electrode", "separator", "positive electrode"
            ]
            var1 = pybamm.Variable("var1", domain=whole_cell)
            var2 = pybamm.Variable("var2", domain=whole_cell)
            model.rhs = {var1: 0.1 * var1}
            model.algebraic = {var2: 2 * var1 - var2}
            model.initial_conditions = {var1: 1, var2: 2}
            model.events = [
                pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
                pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)),
            ]
            disc = get_discretisation_for_testing()
            model_disc = disc.process_model(model, inplace=False)

            # Solve
            solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
            t_eval = np.linspace(0, 5, 100)
            solution = solver.solve(model_disc, t_eval)
            np.testing.assert_array_less(solution.y[0], 1.5)
            np.testing.assert_array_less(solution.y[-1], 2.5)
            np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
            np.testing.assert_allclose(solution.y[-1],
                                       2 * np.exp(0.1 * solution.t))
Exemple #16
0
    def test_dae_integrate_with_jacobian(self):
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def constant_growth_dae(t, y, ydot):
            return np.array(
                [0.5 * np.ones_like(y[0]) - ydot[0], 2.0 * y[0] - y[1]])

        mass_matrix = np.array([[1.0, 0.0], [0.0, 0.0]])

        def jacobian(t, y):
            return np.array([[0.0, 0.0], [2.0, -1.0]])

        y0 = np.array([0.0, 0.0])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(constant_growth_dae,
                                    y0,
                                    t_eval,
                                    mass_matrix=mass_matrix,
                                    jacobian=jacobian)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.5 * solution.t, solution.y[0])
        np.testing.assert_allclose(1.0 * solution.t, solution.y[1])

        # Nonlinear (tests when Jacobian a function of y)
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def nonlinear_dae(t, y, ydot):
            return np.array(
                [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0]**2 - y[1]])

        mass_matrix = np.array([[1.0, 0.0], [0.0, 0.0]])

        def jacobian(t, y):
            return np.array([[0.0, 0.0], [4.0 * y[0], -1.0]])

        y0 = np.array([0.0, 0.0])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(nonlinear_dae,
                                    y0,
                                    t_eval,
                                    mass_matrix=mass_matrix,
                                    jacobian=jacobian)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.5 * solution.t, solution.y[0])
        np.testing.assert_allclose(0.5 * solution.t**2, solution.y[1])
Exemple #17
0
    def default_solver(self):
        """
        Create and return the default solver for this model
        """

        if (self.options["current collector"] != "uniform"
                or self.options["surface form"] == "algebraic"):
            return pybamm.ScikitsDaeSolver()
        else:
            return pybamm.ScipySolver()
Exemple #18
0
 def default_solver(self):
     """
     Create and return the default solver for this model
     """
     # Different solver depending on whether we solve ODEs or DAEs
     if (self.options["surface form"] == "differential"
             and self.options["current collector"] == "uniform"):
         return pybamm.ScipySolver()
     else:
         return pybamm.ScikitsDaeSolver()
Exemple #19
0
    def test_dae_integrate_failure(self):
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def constant_growth_dae(t, y, ydot):
            return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]]

        y0 = np.array([0, 1])
        t_eval = np.linspace(0, 1, 100)
        with self.assertRaises(pybamm.SolverError):
            solver.integrate(constant_growth_dae, y0, t_eval)
Exemple #20
0
 def default_solver(self):
     """
     Create and return the default solver for this model
     """
     # Different solver depending on whether we solve ODEs or DAEs
     dimensionality = self.options["dimensionality"]
     if dimensionality == 0:
         return pybamm.ScipySolver()
     else:
         return pybamm.ScikitsDaeSolver()
Exemple #21
0
 def default_solver(self):
     """
     Return default solver based on whether model is ODE model or DAE model.
     There are bugs with KLU on the lead-acid models.
     """
     if len(self.algebraic) == 0:
         return pybamm.ScipySolver()
     elif pybamm.have_scikits_odes():
         return pybamm.ScikitsDaeSolver()
     else:  # pragma: no cover
         return pybamm.CasadiSolver(mode="safe")
Exemple #22
0
    def test_dae_solver_algebraic_model(self):
        model = pybamm.BaseModel()
        var = pybamm.Variable("var")
        model.algebraic = {var: var + 1}
        model.initial_conditions = {var: 0}

        disc = pybamm.Discretisation()
        disc.process_model(model)

        solver = pybamm.ScikitsDaeSolver()
        t_eval = np.linspace(0, 1)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.y, -1)
Exemple #23
0
    def test_dae_integrate_bad_ics(self):
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def constant_growth_dae(t, y, ydot):
            return [0.5 * np.ones_like(y[0]) - ydot[0], 2 * y[0] - y[1]]

        def constant_growth_dae_rhs(t, y):
            return np.array([constant_growth_dae(t, y, [0])[0]])

        def constant_growth_dae_algebraic(t, y):
            return np.array([constant_growth_dae(t, y, [0])[1]])

        y0_guess = np.array([0, 1])
        t_eval = np.linspace(0, 1, 100)
        y0 = solver.calculate_consistent_initial_conditions(
            constant_growth_dae_rhs, constant_growth_dae_algebraic, y0_guess)
        # check y0
        np.testing.assert_array_equal(y0, [0, 0])
        # check dae solutions
        solution = solver.integrate(constant_growth_dae, y0, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.5 * solution.t, solution.y[0])
        np.testing.assert_allclose(1.0 * solution.t, solution.y[1])

        # Exponential decay
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def exponential_decay_dae(t, y, ydot):
            return [-0.1 * y[0] - ydot[0], 2 * y[0] - y[1]]

        y0 = np.array([1, 2])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(exponential_decay_dae, y0, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(-0.1 * solution.t))
        np.testing.assert_allclose(solution.y[1],
                                   2 * np.exp(-0.1 * solution.t))
Exemple #24
0
    def test_solve_ode_model_with_dae_solver_casadi(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "casadi"
        var = pybamm.Variable("var")
        model.rhs = {var: 0.1 * var}
        model.initial_conditions = {var: 1}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 1, 100)
        solution = solver.solve(model, t_eval)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
    def test_model_step_nonsmooth_events(self):
        # Create model
        model = pybamm.BaseModel()
        model.timescale = pybamm.Scalar(1)
        var1 = pybamm.Variable("var1")
        var2 = pybamm.Variable("var2")
        a = 0.6
        discontinuities = (np.arange(3) + 1) * a

        model.rhs = {var1: pybamm.Modulo(pybamm.t * model.timescale, a)}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 0, var2: 0}
        model.events = [
            pybamm.Event("var1 = 0.55", pybamm.min(var1 - 0.55)),
            pybamm.Event("var2 = 1.2", pybamm.min(var2 - 1.2)),
        ]
        for discontinuity in discontinuities:
            model.events.append(
                pybamm.Event("nonsmooth rate", pybamm.Scalar(discontinuity)))
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        step_solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        dt = 0.05
        time = 0
        end_time = 3
        step_solution = None
        while time < end_time:
            step_solution = step_solver.step(step_solution,
                                             model,
                                             dt=dt,
                                             npts=10)
            time += dt
        np.testing.assert_array_less(step_solution.y[0, :-1], 0.55)
        np.testing.assert_array_less(step_solution.y[-1, :-1], 1.2)
        np.testing.assert_equal(step_solution.t_event[0], step_solution.t[-1])
        np.testing.assert_array_equal(step_solution.y_event[:, 0],
                                      step_solution.y[:, -1])
        var1_soln = (step_solution.t %
                     a)**2 / 2 + a**2 / 2 * (step_solution.t // a)
        var2_soln = 2 * var1_soln
        np.testing.assert_array_almost_equal(step_solution.y[0],
                                             var1_soln,
                                             decimal=5)
        np.testing.assert_array_almost_equal(step_solution.y[-1],
                                             var2_soln,
                                             decimal=5)
Exemple #26
0
    def test_model_solver_dae_inputs_in_initial_conditions(self):
        # Create model
        model = pybamm.BaseModel()
        var1 = pybamm.Variable("var1")
        var2 = pybamm.Variable("var2")
        model.rhs = {var1: pybamm.InputParameter("rate") * var1}
        model.algebraic = {var2: var1 - var2}
        model.initial_conditions = {
            var1: pybamm.InputParameter("ic 1"),
            var2: pybamm.InputParameter("ic 2"),
        }

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)
        t_eval = np.linspace(0, 5, 100)
        solution = solver.solve(model,
                                t_eval,
                                inputs={
                                    "rate": -1,
                                    "ic 1": 0.1,
                                    "ic 2": 2
                                })
        np.testing.assert_array_almost_equal(solution.y[0],
                                             0.1 * np.exp(-solution.t),
                                             decimal=5)
        np.testing.assert_array_almost_equal(solution.y[-1],
                                             0.1 * np.exp(-solution.t),
                                             decimal=5)

        # Solve again with different initial conditions
        solution = solver.solve(model,
                                t_eval,
                                inputs={
                                    "rate": -0.1,
                                    "ic 1": 1,
                                    "ic 2": 3
                                })
        np.testing.assert_array_almost_equal(solution.y[0],
                                             1 * np.exp(-0.1 * solution.t),
                                             decimal=5)
        np.testing.assert_array_almost_equal(solution.y[-1],
                                             1 * np.exp(-0.1 * solution.t),
                                             decimal=5)
Exemple #27
0
    def test_dae_integrate_with_non_unity_mass(self):
        # Constant
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

        def constant_growth_dae(t, y, ydot):
            return np.array(
                [0.5 * np.ones_like(y[0]) - 4 * ydot[0], 2.0 * y[0] - y[1]])

        mass_matrix = np.array([[4.0, 0.0], [0.0, 0.0]])

        def jacobian(t, y):
            return np.array([[0.0, 0.0], [2.0, -1.0]])

        y0 = np.array([0.0, 0.0])
        t_eval = np.linspace(0, 1, 100)
        solution = solver.integrate(constant_growth_dae,
                                    y0,
                                    t_eval,
                                    mass_matrix=mass_matrix,
                                    jacobian=jacobian)
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(0.125 * solution.t, solution.y[0])
        np.testing.assert_allclose(0.25 * solution.t, solution.y[1])
Exemple #28
0
    def test_model_solver_dae_no_nonsmooth_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        discontinuity = 5.6

        def nonsmooth_rate(t):
            return 0.1 * int(t < discontinuity) + 0.1

        def nonsmooth_mult(t):
            return int(t < discontinuity) + 1.0

        # put in an extra heaviside with no time dependence, this should be ignored by
        # the solver i.e. no extra discontinuities added
        model.rhs = {var1: 0.1 * var1}
        model.algebraic = {var2: 2 * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-9,
                                         atol=1e-9,
                                         root_method="lm")

        # create two time series, one without a time point on the discontinuity,
        # and one with
        t_eval = np.linspace(0, 5, 10)
        solution = solver.solve(model, t_eval)

        # test solution, discontinuity should not be triggered
        np.testing.assert_array_equal(solution.t, t_eval)
        np.testing.assert_allclose(solution.y[0], np.exp(0.1 * solution.t))
        np.testing.assert_allclose(solution.y[-1],
                                   2 * np.exp(0.1 * solution.t))
Exemple #29
0
    def test_model_solver_dae_nonsmooth(self):
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2")
        discontinuity = 0.6

        # Create three different models with the same solution, each expressing the
        # discontinuity in a different way

        # first model explicitly adds a discontinuity event
        def nonsmooth_rate(t):
            return 0.1 * (t < discontinuity) + 0.1

        rate = pybamm.Function(nonsmooth_rate, pybamm.t)
        model1 = pybamm.BaseModel()
        model1.rhs = {var1: rate * var1}
        model1.algebraic = {var2: var2}
        model1.initial_conditions = {var1: 1, var2: 0}
        model1.events = [
            pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
            pybamm.Event(
                "nonsmooth rate",
                pybamm.Scalar(discontinuity),
                pybamm.EventType.DISCONTINUITY,
            ),
        ]

        # second model implicitly adds a discontinuity event via a heaviside function
        model2 = pybamm.BaseModel()
        model2.rhs = {var1: (0.1 * (pybamm.t < discontinuity) + 0.1) * var1}
        model2.algebraic = {var2: var2}
        model2.initial_conditions = {var1: 1, var2: 0}
        model2.events = [pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5))]

        # third model implicitly adds a discontinuity event via another heaviside
        # function
        model3 = pybamm.BaseModel()
        model3.rhs = {var1: (-0.1 * (discontinuity < pybamm.t) + 0.2) * var1}
        model3.algebraic = {var2: var2}
        model3.initial_conditions = {var1: 1, var2: 0}
        model3.events = [pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5))]

        for model in [model1, model2, model3]:

            disc = get_discretisation_for_testing()
            disc.process_model(model)

            # Solve
            solver = pybamm.ScikitsDaeSolver(rtol=1e-8, atol=1e-8)

            # create two time series, one without a time point on the discontinuity,
            # and one with
            t_eval1 = np.linspace(0, 5, 10)
            t_eval2 = np.insert(t_eval1,
                                np.searchsorted(t_eval1,
                                                discontinuity), discontinuity)
            solution1 = solver.solve(model, t_eval1)
            solution2 = solver.solve(model, t_eval2)

            # check time vectors
            for solution in [solution1, solution2]:
                # time vectors are ordered
                self.assertTrue(np.all(solution.t[:-1] <= solution.t[1:]))

                # time value before and after discontinuity is an epsilon away
                dindex = np.searchsorted(solution.t, discontinuity)
                value_before = solution.t[dindex - 1]
                value_after = solution.t[dindex]
                self.assertEqual(value_before + sys.float_info.epsilon,
                                 discontinuity)
                self.assertEqual(value_after - sys.float_info.epsilon,
                                 discontinuity)

            # both solution time vectors should have same number of points
            self.assertEqual(len(solution1.t), len(solution2.t))

            # check solution
            for solution in [solution1, solution2]:
                np.testing.assert_array_less(solution.y[0], 1.5)
                np.testing.assert_array_less(solution.y[-1], 2.5)
                var1_soln = np.exp(0.2 * solution.t)
                y0 = np.exp(0.2 * discontinuity)
                var1_soln[solution.t > discontinuity] = y0 * np.exp(
                    0.1 *
                    (solution.t[solution.t > discontinuity] - discontinuity))
                np.testing.assert_allclose(solution.y[0],
                                           var1_soln,
                                           rtol=1e-06)
Exemple #30
0
    def test_model_solver_dae_nonsmooth_python(self):
        model = pybamm.BaseModel()
        model.convert_to_format = "python"
        whole_cell = ["negative electrode", "separator", "positive electrode"]
        var1 = pybamm.Variable("var1", domain=whole_cell)
        var2 = pybamm.Variable("var2", domain=whole_cell)
        discontinuity = 0.6

        def nonsmooth_rate(t):
            return 0.1 * int(t < discontinuity) + 0.1

        def nonsmooth_mult(t):
            return int(t < discontinuity) + 1.0

        rate = pybamm.Function(nonsmooth_rate, pybamm.t)
        mult = pybamm.Function(nonsmooth_mult, pybamm.t)
        # put in an extra heaviside with no time dependence, this should be ignored by
        # the solver i.e. no extra discontinuities added
        model.rhs = {var1: rate * var1 + (var1 < 0)}
        model.algebraic = {var2: mult * var1 - var2}
        model.initial_conditions = {var1: 1, var2: 2}
        model.events = [
            pybamm.Event("var1 = 1.5", pybamm.min(var1 - 1.5)),
            pybamm.Event("var2 = 2.5", pybamm.min(var2 - 2.5)),
            pybamm.Event(
                "nonsmooth rate",
                pybamm.Scalar(discontinuity),
                pybamm.EventType.DISCONTINUITY,
            ),
            pybamm.Event(
                "nonsmooth mult",
                pybamm.Scalar(discontinuity),
                pybamm.EventType.DISCONTINUITY,
            ),
        ]
        disc = get_discretisation_for_testing()
        disc.process_model(model)

        # Solve
        solver = pybamm.ScikitsDaeSolver(rtol=1e-8,
                                         atol=1e-8,
                                         root_method="lm")

        # create two time series, one without a time point on the discontinuity,
        # and one with
        t_eval1 = np.linspace(0, 5, 10)
        t_eval2 = np.insert(t_eval1, np.searchsorted(t_eval1, discontinuity),
                            discontinuity)
        solution1 = solver.solve(model, t_eval1)
        solution2 = solver.solve(model, t_eval2)

        # check time vectors
        for solution in [solution1, solution2]:
            # time vectors are ordered
            self.assertTrue(np.all(solution.t[:-1] <= solution.t[1:]))

            # time value before and after discontinuity is an epsilon away
            dindex = np.searchsorted(solution.t, discontinuity)
            value_before = solution.t[dindex - 1]
            value_after = solution.t[dindex]
            self.assertEqual(value_before + sys.float_info.epsilon,
                             discontinuity)
            self.assertEqual(value_after - sys.float_info.epsilon,
                             discontinuity)

        # both solution time vectors should have same number of points
        self.assertEqual(len(solution1.t), len(solution2.t))

        # check solution
        for solution in [solution1, solution2]:
            np.testing.assert_array_less(solution.y[0], 1.5)
            np.testing.assert_array_less(solution.y[-1], 2.5)
            var1_soln = np.exp(0.2 * solution.t)
            y0 = np.exp(0.2 * discontinuity)
            var1_soln[solution.t > discontinuity] = y0 * np.exp(
                0.1 * (solution.t[solution.t > discontinuity] - discontinuity))
            var2_soln = 2 * var1_soln
            var2_soln[solution.t > discontinuity] = var1_soln[
                solution.t > discontinuity]
            np.testing.assert_allclose(solution.y[0], var1_soln, rtol=1e-06)
            np.testing.assert_allclose(solution.y[-1], var2_soln, rtol=1e-06)