diff --git a/demos/time_dependent_rayleigh_benard/timedep-rayleigh-benard.py.rst b/demos/time_dependent_rayleigh_benard/timedep-rayleigh-benard.py.rst new file mode 100644 index 0000000000..f5dd9b2a41 --- /dev/null +++ b/demos/time_dependent_rayleigh_benard/timedep-rayleigh-benard.py.rst @@ -0,0 +1,207 @@ +Time-dependent Rayleigh-Benard Convection using Irksome +======================================================= + +Contributed by `Robert Kirby `_ +and `Pablo Brubeck `_. + +This problem involves a variable-temperature incompressible fluid. +Variations in the fluid temperature are assumed to affect the momentum +balance through a buoyant term (the Boussinesq approximation), leading +to a Navier-Stokes equation with a nonlinear coupling to a +convection-diffusion equation for temperature. + +We will set up the problem using Taylor-Hood elements for +the Navier-Stokes part, and piecewise linear elements for the +temperature. The system will be integrated forward in time with a multi-stage +fully implicit Runge--Kutta method in `Irksome `_.:: + + try: + from irksome import Dt, MeshConstant, RadauIIA, TimeStepper + except ImportError: + warning("Unable to import irksome. See https://www.firedrakeproject.org/Irksome/ for installation instructions") + quit() + + + from firedrake import * + from firedrake.pyplot import FunctionPlotter, tripcolor + import matplotlib.pyplot as plt + from matplotlib.animation import FuncAnimation + +We solve the system with a multigrid method, so we need to set up a mesh hiearchy:: + + Nbase = 8 + ref_levels = 2 + N = Nbase * 2**ref_levels + + base_msh = UnitSquareMesh(Nbase, Nbase) + mh = MeshHierarchy(base_msh, ref_levels) + msh = mh[-1] + + MC = MeshConstant(msh) + + V = VectorFunctionSpace(msh, "CG", 2) + W = FunctionSpace(msh, "CG", 1) + Q = FunctionSpace(msh, "CG", 1) + Z = V * W * Q + + upT = Function(Z) + u, p, T = split(upT) + v, q, S = TestFunctions(Z) + +Two key physical parameters are the Rayleigh number (Ra), which +measures the ratio of energy from buoyant forces to viscous +dissipation and heat conduction and the +Prandtl number (Pr), which measures the ratio of viscosity to heat +conduction. :: + + Ra = MC.Constant(2000.0) + Pr = MC.Constant(6.8) + +Along with gravity, which points down. :: + + g = Constant((0, -1)) + +Set up variables for time and time-step size. :: + + t = MC.Constant(0.0) + dt = MC.Constant(1.0 / N) + +The PDE system is given by + +.. math:: + \begin{aligned} + \frac{\partial \mathbf{u}}{\partial t} - \Delta \mathbf{u} + \mathbf{u} \cdot \nabla \mathbf{u} + + \nabla & = \frac{Ra}{Pr} T \mathbf{g} \\ + \nabla \cdot \mathbf{u} & = 0 \\ + \frac{\partial T}{\partial t} + \mathbf{u} \cdot \nabla T + - \frac{1}{Pr} \Delta T & = 0 + \end{aligned} + +and we can write a Galerkin variational form in the usual way, leading to +the UFL representation:: + + F = ( + inner(Dt(u), v)*dx + + inner(grad(u), grad(v))*dx + + inner(dot(grad(u), u), v)*dx + - inner(p, div(v))*dx + - (Ra/Pr)*inner(T*g, v)*dx + + inner(div(u), q)*dx + + inner(Dt(T), S)*dx + + inner(dot(grad(T), u), S)*dx + + 1/Pr * inner(grad(T), grad(S))*dx + ) + +There are two common versions of this problem. In one case, heat is +applied from bottom to top so that the temperature gradient is +enforced parallel to the gravitation. In this case, the temperature +difference is applied horizontally, perpendicular to gravity. It +tends to make prettier pictures for low Rayleigh numbers, but also +tends to take more Newton iterations since the coupling terms in the +Jacobian are a bit stronger. Switching to the first case would be a +simple change of the boundary subdomains associated with the second and +third boundary conditions below:: + + bcs = [ + DirichletBC(Z.sub(0), Constant((0, 0)), (1, 2, 3, 4)), + DirichletBC(Z.sub(2), Constant(1.0), (3,)), + DirichletBC(Z.sub(2), Constant(0.0), (4,)) + ] + +Like Navier-Stokes, the pressure is only defined up to a constant. +Irksome takes list of tuples indicating which field index (piece of +`Z`) and the null space basis to impose on it, and then manipulates this +into a suitable null space to attach to the internal variational solver:: + + nullspace = [(1, VectorSpaceBasis(constant=True))] + +Set up the Butcher tableau to use for time-stepping:: + + num_stages = 2 + butcher_tableau = RadauIIA(num_stages) + +We are going to carry out time stepping via Irksome, but we need +to say how to solve the rather interesting stage-coupled system. +We will use an outer Newton method with linesearch. +The linear solver will be flexible GMRES. We adapt the the tolerance of +the inner solver via the Eisenstant-Walker trick using ``snes_ksp_ew``. +See the `PETSc docs `_ for further information. + +The linear solver will be preconditioned with a multigrid method. +As a relaxation scheme, we apply several iterations (accelerated via GMRES) +of a Vanka-type patch smoother via :class:`~.ASMVankaPC`. This smoother sets up a sequence of local problems involving all degrees of freedom for each field for each +Runge--Kutta stage on the cells containing a vertex in the mesh. +We use `exclude_inds` to indicate that we use velocity degrees of freedom on +the patch boundary but exclude the pressure and temperature degrees of freedom. +:: + + exclude_inds = ",".join([str(3*i+j) for i in range(num_stages) for j in (1, 2)]) + + params = { + "mat_type": "aij", + "snes_type": "newtonls", + "snes_converged_reason": None, + "snes_linesearch_type": "l2", + "snes_monitor": None, + "ksp_type": "fgmres", + "ksp_converged_reason": None, + "ksp_max_it": 200, + "ksp_atol": 1.e-12, + "snes_rtol": 1.e-10, + "snes_atol": 1.e-10, + "snes_ksp_ew": None, + "pc_type": "mg", + "mg_levels": { + "ksp_type": "gmres", + "ksp_max_it": 3, + "ksp_convergence_test": "skip", + "pc_type": "python", + "pc_python_type": "firedrake.ASMVankaPC", + "pc_vanka_construct_dim": 0, + "pc_vanka_backend_type": "tinyasm", + "pc_vanka_exclude_subspaces": exclude_inds}, + "mg_coarse": { + "ksp_type": "preonly", + "pc_type": "lu", + "pc_factor_mat_solver_type": "umfpack"} + } + + stepper = TimeStepper(F, butcher_tableau, t, dt, upT, bcs=bcs, + nullspace=nullspace, solver_parameters=params) + +Now that the stepper is set up, let's run over many time steps:: + + + plot_freq = 10 + Ts = [] + + for cur_step in ProgressBar("Integrating Rayleigh-Benard").iter(range(N)): + stepper.advance() + + t.assign(float(t) + float(dt)) + + if cur_step % plot_freq == 0: + Ts.append(upT.subfunctions[2].copy(deepcopy=True)) + + + nsp = 16 + fn_plotter = FunctionPlotter(msh, num_sample_points=nsp) + fig, axes = plt.subplots() + axes.set_aspect('equal') + Tzero = Function(Q) + colors = tripcolor(Tzero, num_sample_points=nsp, vmin=0, vmax=1, axes=axes) + fig.colorbar(colors) + + + def animate(q): + colors.set_array(fn_plotter(q)) + + + interval = 1e3 * plot_freq * float(dt) + animation = FuncAnimation(fig, animate, frames=Ts, interval=interval) + try: + animation.save("benard_temp.mp4", writer="ffmpeg") + except: + print("Failed to write movie! Try installing `ffmpeg`.") + +A python script version of this demo can be found :demo:`here `. diff --git a/docs/source/advanced_tut.rst b/docs/source/advanced_tut.rst index 9b83643c5f..89365f75f8 100644 --- a/docs/source/advanced_tut.rst +++ b/docs/source/advanced_tut.rst @@ -30,3 +30,6 @@ element systems. Vertex/edge star multigrid relaxation for H(div). Auxiliary space patch relaxation multigrid for H(curl). Steady Boussinesq problem with integral constraints. + Time-dependent Rayleigh-Benard convection using irksome. + + diff --git a/pyproject.toml b/pyproject.toml index 55dbcd6427..d39b0bc978 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,7 @@ vtk = [ # Dependencies needed to run the full test suite ci = [ "ipympl", # needed for notebook testing + "irksome @ git+https://github.com/firedrakeproject/Irksome.git", "jax", "matplotlib", "mpi-pytest", diff --git a/tests/firedrake/demos/test_demos_run.py b/tests/firedrake/demos/test_demos_run.py index 4d8eb623d8..9af1219a23 100644 --- a/tests/firedrake/demos/test_demos_run.py +++ b/tests/firedrake/demos/test_demos_run.py @@ -46,6 +46,7 @@ Demo(("patch", "hdiv_riesz_star"), []), Demo(("quasigeostrophy_1layer", "qg_1layer_wave"), ["hypre", "vtk"]), Demo(("saddle_point_pc", "saddle_point_systems"), ["hypre", "mumps"]), + Demo(("time_dependent_rayleigh_benard", "timedep-rayleigh-benard"), ["irksome", "mumps"]), Demo(('vlasov_poisson_1d', 'vp1d'), []), ] PARALLEL_DEMOS = [