def main(equation: str = "cahn-hilliard",
         t_range: float = 100,
         size: int = 32):
    """main routine testing the performance

    Args:
        equation (str): Chooses the equation to consider
        t_range (float): Sets the total duration that should be solved for
        size (int): The number of grid points along each axis
    """
    print("Reports duration in seconds (smaller is better)\n")

    # determine grid and initial state
    grid = UnitGrid([size, size], periodic=False)
    field = ScalarField.random_uniform(grid)
    print(f"GRID: {grid}")

    # determine the equation to solve
    if equation == "diffusion":
        eq = DiffusionPDE()
    elif equation == "cahn-hilliard":
        eq = CahnHilliardPDE()
    else:
        raise ValueError(f"Undefined equation `{equation}`")
    print(f"EQUATION: ∂c/∂t = {eq.expression}")

    print("\nSOLVER PERFORMANCE:")

    expected = eq.solve(field, t_range=t_range, dt=1e-5, tracker=None)

    solvers = {
        "Euler, fixed":
        (1e-3, ExplicitSolver(eq, scheme="euler", adaptive=False)),
        "Euler, adaptive":
        (1e-3, ExplicitSolver(eq, scheme="euler", adaptive=True)),
        "Runge-Kutta, fixed":
        (1e-3, ExplicitSolver(eq, scheme="rk", adaptive=False)),
        "Runge-Kutta, adaptive": (1e-3,
                                  ExplicitSolver(eq,
                                                 scheme="rk",
                                                 adaptive=True)),
        "implicit": (1e-3, ImplicitSolver(eq)),
        "scipy": (None, ScipySolver(eq)),
    }

    for name, (dt, solver) in solvers.items():

        solver.backend = "numba"
        controller = Controller(solver, t_range=t_range, tracker=None)
        result = controller.run(field, dt=dt)

        # call once to pre-compile and test result
        if np.allclose(result.data, expected.data, atol=1e-2):
            # report the duration
            print(f"{name:>21s}: {controller.info['profiler']['solver']:.3g}")
        else:
            # report the mismatch
            print(f"{name:>21s}: MISMATCH")
def test_compare_explicit():
    """ test explicit solvers """
    grid = UnitGrid([16, 16])
    field = ScalarField.random_uniform(grid, -1, 1)
    eq = DiffusionPDE()

    c1 = Controller(ExplicitSolver(eq), t_range=0.1, tracker=None)
    s1 = c1.run(field, dt=2e-3)

    c2 = Controller(ExplicitSolver(eq, scheme="runge-kutta"), t_range=0.1, tracker=None)
    with np.errstate(under="ignore"):
        s2 = c2.run(field, dt=2e-3)

    np.testing.assert_allclose(s1.data, s2.data, rtol=1e-2, atol=1e-2)
def test_stochastic_solvers(backend):
    """ test simple version of the stochastic solver """
    field = ScalarField.random_uniform(UnitGrid([16]), -1, 1)
    eq = DiffusionPDE()
    seq = DiffusionPDE(noise=1e-6)

    solver1 = ExplicitSolver(eq, backend=backend)
    c1 = Controller(solver1, t_range=1, tracker=None)
    s1 = c1.run(field, dt=1e-3)

    solver2 = ExplicitSolver(seq, backend=backend)
    c2 = Controller(solver2, t_range=1, tracker=None)
    s2 = c2.run(field, dt=1e-3)

    np.testing.assert_allclose(s1.data, s2.data, rtol=1e-4, atol=1e-4)
    assert not solver1.info["stochastic"]
    assert solver2.info["stochastic"]
Beispiel #4
0
def test_stochastic_adaptive_solver(caplog):
    """test using an adaptive, stochastic solver"""
    field = ScalarField.random_uniform(UnitGrid([16]), -1, 1)
    eq = DiffusionPDE(noise=1e-6)

    with caplog.at_level(logging.WARNING):
        solver = ExplicitSolver(eq, backend="numpy", adaptive=True)

    c = Controller(solver, t_range=1, tracker=None)
    c.run(field, dt=1e-2)

    assert "fixed" in caplog.text
Beispiel #5
0
def test_compare_solvers(solver_class):
    """compare several solvers"""
    field = ScalarField.random_uniform(UnitGrid([8, 8]), -1, 1)
    eq = DiffusionPDE()

    # ground truth
    c1 = Controller(ExplicitSolver(eq, scheme="runge-kutta"), t_range=0.1, tracker=None)
    s1 = c1.run(field, dt=5e-3)

    c2 = Controller(solver_class(eq), t_range=0.1, tracker=None)
    with np.errstate(under="ignore"):
        s2 = c2.run(field, dt=5e-3)

    np.testing.assert_allclose(s1.data, s2.data, rtol=1e-2, atol=1e-2)
Beispiel #6
0
def test_solvers_simple_example(scheme, adaptive):
    """test explicit solvers"""
    grid = UnitGrid([4])
    field = ScalarField(grid, 1)
    eq = PDE({"c": "c"})

    dt = 1e-3 if scheme == "euler" else 1e-2

    solver = ExplicitSolver(eq, scheme=scheme, adaptive=adaptive)
    controller = Controller(solver, t_range=10.0, tracker=None)
    res = controller.run(field, dt=dt)
    np.testing.assert_allclose(res.data, np.exp(10), rtol=0.1)
    if adaptive:
        assert solver.info["steps"] != pytest.approx(10 / dt, abs=1)
    else:
        assert solver.info["steps"] == pytest.approx(10 / dt, abs=1)
Beispiel #7
0
def test_solvers_simple_ode(scheme, adaptive):
    """test explicit solvers with a simple ODE"""
    grid = UnitGrid([1])
    field = ScalarField(grid, 1)
    eq = PDE({"y": "2*sin(t) - y"})

    dt = 1e-3 if scheme == "euler" else 1e-2

    storage = MemoryStorage()
    solver = ExplicitSolver(eq, scheme=scheme, adaptive=adaptive)
    controller = Controller(solver, t_range=20.0, tracker=storage.tracker(1))
    controller.run(field, dt=dt)

    ts = np.ravel(storage.times)
    expect = 2 * np.exp(-ts) - np.cos(ts) + np.sin(ts)
    np.testing.assert_allclose(np.ravel(storage.data), expect, atol=0.05)

    if adaptive:
        assert solver.info["steps"] < 20 / dt
    else:
        assert solver.info["steps"] == pytest.approx(20 / dt, abs=1)