def test_identity_ops(self):
        test_mesh = np.array([1, 2, 3])
        spatial_method = pybamm.ZeroDimensionalSpatialMethod()
        spatial_method.build(test_mesh)
        np.testing.assert_array_equal(spatial_method._mesh, test_mesh)

        a = pybamm.Symbol("a")
        self.assertEqual(a, spatial_method.integral(None, a, "primary"))
        self.assertEqual(
            a, spatial_method.indefinite_integral(None, a, "forward"))
        self.assertEqual(a, spatial_method.boundary_value_or_flux(None, a))
        self.assertEqual(
            (-a).id,
            spatial_method.indefinite_integral(None, a, "backward").id)

        mass_matrix = spatial_method.mass_matrix(None, None)
        self.assertIsInstance(mass_matrix, pybamm.Matrix)
        self.assertEqual(mass_matrix.shape, (1, 1))
        np.testing.assert_array_equal(mass_matrix.entries, 1)
Пример #2
0
        var_pts,
    )
    for geometry in geometries
]

# discretise model
disc_fv = pybamm.Discretisation(meshes[0], models[0].default_spatial_methods)
disc_sv = pybamm.Discretisation(
    meshes[1],
    {
        "negative particle": pybamm.SpectralVolume(order=order),
        "positive particle": pybamm.SpectralVolume(order=order),
        "negative electrode": pybamm.SpectralVolume(order=order),
        "separator": pybamm.SpectralVolume(order=order),
        "positive electrode": pybamm.SpectralVolume(order=order),
        "current collector": pybamm.ZeroDimensionalSpatialMethod(),
    },
)

disc_fv.process_model(models[0])
disc_sv.process_model(models[1])

# solve model
t_eval = np.linspace(0, 3600, 100)

casadi_fv = pybamm.CasadiSolver(atol=1e-8, rtol=1e-8).solve(models[0], t_eval)
casadi_sv = pybamm.CasadiSolver(atol=1e-8, rtol=1e-8).solve(models[1], t_eval)
solutions = [casadi_fv, casadi_sv]

# plot
plot = pybamm.QuickPlot(solutions)
Пример #3
0
    def test_quadratic_extrapolate_left_right(self):
        # create discretisation
        mesh = get_mesh_for_testing()
        method_options = {"extrapolation": {"order": "quadratic", "use bcs": False}}
        spatial_methods = {
            "macroscale": pybamm.FiniteVolume(method_options),
            "negative particle": pybamm.FiniteVolume(method_options),
            "current collector": pybamm.ZeroDimensionalSpatialMethod(method_options),
        }
        disc = pybamm.Discretisation(mesh, spatial_methods)

        whole_cell = ["negative electrode", "separator", "positive electrode"]
        macro_submesh = mesh.combine_submeshes(*whole_cell)
        micro_submesh = mesh["negative particle"]

        # Macroscale
        # create variable
        var = pybamm.Variable("var", domain=whole_cell)
        # boundary value should work with something more complicated than a variable
        extrap_left = pybamm.BoundaryValue(2 * var, "left")
        extrap_right = pybamm.BoundaryValue(4 - var, "right")
        disc.set_variable_slices([var])
        extrap_left_disc = disc.process_symbol(extrap_left)
        extrap_right_disc = disc.process_symbol(extrap_right)

        # check constant extrapolates to constant
        constant_y = np.ones_like(macro_submesh[0].nodes[:, np.newaxis])
        np.testing.assert_array_almost_equal(
            extrap_left_disc.evaluate(None, constant_y), 2.0
        )
        np.testing.assert_array_almost_equal(
            extrap_right_disc.evaluate(None, constant_y), 3.0
        )

        # check linear variable extrapolates correctly
        linear_y = macro_submesh[0].nodes
        np.testing.assert_array_almost_equal(
            extrap_left_disc.evaluate(None, linear_y), 0
        )
        np.testing.assert_array_almost_equal(
            extrap_right_disc.evaluate(None, linear_y), 3
        )

        # Fluxes
        extrap_flux_left = pybamm.BoundaryGradient(2 * var, "left")
        extrap_flux_right = pybamm.BoundaryGradient(1 - var, "right")
        extrap_flux_left_disc = disc.process_symbol(extrap_flux_left)
        extrap_flux_right_disc = disc.process_symbol(extrap_flux_right)

        # check constant extrapolates to constant
        np.testing.assert_array_almost_equal(
            extrap_flux_left_disc.evaluate(None, constant_y), 0
        )
        self.assertEqual(extrap_flux_right_disc.evaluate(None, constant_y), 0)

        # check linear variable extrapolates correctly
        np.testing.assert_array_almost_equal(
            extrap_flux_left_disc.evaluate(None, linear_y), 2
        )
        np.testing.assert_array_almost_equal(
            extrap_flux_right_disc.evaluate(None, linear_y), -1
        )

        # Microscale
        # create variable
        var = pybamm.Variable("var", domain="negative particle")
        surf_eqn = pybamm.surf(var)
        disc.set_variable_slices([var])
        surf_eqn_disc = disc.process_symbol(surf_eqn)

        # check constant extrapolates to constant
        constant_y = np.ones_like(micro_submesh[0].nodes[:, np.newaxis])
        np.testing.assert_array_almost_equal(
            surf_eqn_disc.evaluate(None, constant_y), 1
        )

        # check linear variable extrapolates correctly
        linear_y = micro_submesh[0].nodes
        y_surf = micro_submesh[0].edges[-1]
        np.testing.assert_array_almost_equal(
            surf_eqn_disc.evaluate(None, linear_y), y_surf
        )
Пример #4
0
    def test_leading_order_convergence(self):
        """
        Check that the leading-order model solution converges linearly in C_e to the
        full model solution
        """
        # Create models
        leading_order_model = pybamm.lead_acid.LOQS()
        composite_model = pybamm.lead_acid.Composite()
        full_model = pybamm.lead_acid.Full()

        # Same parameters, same geometry
        parameter_values = full_model.default_parameter_values
        parameter_values["Current function [A]"] = "[input]"
        parameter_values.process_model(leading_order_model)
        parameter_values.process_model(composite_model)
        parameter_values.process_model(full_model)
        geometry = full_model.default_geometry
        parameter_values.process_geometry(geometry)

        # Discretise (same mesh, create different discretisations)
        var = pybamm.standard_spatial_vars
        var_pts = {var.x_n: 3, var.x_s: 3, var.x_p: 3}
        mesh = pybamm.Mesh(geometry, full_model.default_submesh_types, var_pts)

        method_options = {
            "extrapolation": {
                "order": "linear",
                "use bcs": False
            }
        }
        spatial_methods = {
            "macroscale":
            pybamm.FiniteVolume(method_options),
            "current collector":
            pybamm.ZeroDimensionalSpatialMethod(method_options),
        }
        loqs_disc = pybamm.Discretisation(mesh, spatial_methods)
        loqs_disc.process_model(leading_order_model)
        comp_disc = pybamm.Discretisation(mesh, spatial_methods)
        comp_disc.process_model(composite_model)
        full_disc = pybamm.Discretisation(mesh, spatial_methods)
        full_disc.process_model(full_model)

        def get_max_error(current):
            pybamm.logger.info("current = {}".format(current))
            # Solve, make sure times are the same and use tight tolerances
            t_eval = np.linspace(0, 3600 * 17 / current)
            solver = pybamm.CasadiSolver()
            solver.rtol = 1e-8
            solver.atol = 1e-8
            solution_loqs = solver.solve(
                leading_order_model,
                t_eval,
                inputs={"Current function [A]": current})
            solution_comp = solver.solve(
                composite_model,
                t_eval,
                inputs={"Current function [A]": current})
            solution_full = solver.solve(
                full_model, t_eval, inputs={"Current function [A]": current})

            # Post-process variables
            voltage_loqs = solution_loqs["Terminal voltage"]
            voltage_comp = solution_comp["Terminal voltage"]
            voltage_full = solution_full["Terminal voltage"]

            # Compare
            t_loqs = solution_loqs.t
            t_comp = solution_comp.t
            t_full = solution_full.t
            t = t_full[:np.min([len(t_loqs), len(t_comp), len(t_full)])]
            loqs_error = np.max(np.abs(voltage_loqs(t) - voltage_full(t)))
            comp_error = np.max(np.abs(voltage_comp(t) - voltage_full(t)))
            return (loqs_error, comp_error)

        # Get errors
        currents = 0.5 / (2**np.arange(3))
        errs = np.array([get_max_error(current) for current in currents])
        loqs_errs, comp_errs = [np.array(err) for err in zip(*errs)]
        # Get rates: expect linear convergence for loqs, quadratic for composite
        loqs_rates = np.log2(loqs_errs[:-1] / loqs_errs[1:])

        np.testing.assert_array_less(0.99 * np.ones_like(loqs_rates),
                                     loqs_rates)
        # Composite not converging as expected
        comp_rates = np.log2(comp_errs[:-1] / comp_errs[1:])
        np.testing.assert_array_less(0.99 * np.ones_like(comp_rates),
                                     comp_rates)
        # Check composite more accurate than loqs
        np.testing.assert_array_less(comp_errs, loqs_errs)