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"]
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
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)
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)
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)