Path: blob/main/examples/structured_2d_dgsem/elixir_lbm_eulerpolytropic_coupled.jl
2055 views
using OrdinaryDiffEqSSPRK1using Trixi2using LinearAlgebra: norm34###############################################################################5# Semidiscretizations of the polytropic Euler equations and Lattice-Boltzmann method (LBM)6# coupled using converter functions across their respective domains to generate a periodic system.7#8# In this elixir, we have a rectangular domain that is divided into a left and right half.9# On each half of the domain, an independent SemidiscretizationHyperbolic is created for each set of equations.10# The two systems are coupled in the x-direction and are periodic in the y-direction.11# For a high-level overview, see also the figure below:12#13# (-2, 1) ( 2, 1)14# ┌────────────────────┬────────────────────┐15# │ ↑ periodic ↑ │ ↑ periodic ↑ │16# │ │ │17# │ ========= │ ========= │18# │ system #1 │ system #2 │19# | Euler | LBM |20# │ ========= │ ========= │21# │ │ │22# │<-- coupled │<-- coupled │23# │ coupled -->│ coupled -->│24# │ │ │25# │ ↓ periodic ↓ │ ↓ periodic ↓ │26# └────────────────────┴────────────────────┘27# (-2, -1) ( 2, -1)2829polydeg = 230cells_per_dim_per_section = (16, 8)3132###########33# system #134# Euler35###########3637### Setup taken from "elixir_eulerpolytropic_isothermal_wave.jl" ###3839gamma = 1.0 # Isothermal gas40kappa = 1.0 # Scaling factor for the pressure, must fit to LBM `c_s`41eqs_euler = PolytropicEulerEquations2D(gamma, kappa)4243volume_flux = flux_winters_etal44solver_euler = DGSEM(polydeg = polydeg, surface_flux = flux_hll,45volume_integral = VolumeIntegralFluxDifferencing(volume_flux))4647# Linear pressure wave/Gaussian bump moving in the positive x-direction.48function initial_condition_pressure_bump(x, t, equations::PolytropicEulerEquations2D)49rho = ((1.0 + 0.01 * exp(-(x[1] - 1)^2 / 0.1)) / equations.kappa)^(1 / equations.gamma)50v1 = ((0.01 * exp(-(x[1] - 1)^2 / 0.1)) / equations.kappa)51v2 = 0.05253return prim2cons(SVector(rho, v1, v2), equations)54end55initial_condition_euler = initial_condition_pressure_bump5657coords_min_euler = (-2.0, -1.0)58coords_max_euler = (0.0, 1.0)59mesh_euler = StructuredMesh(cells_per_dim_per_section,60coords_min_euler, coords_max_euler,61periodicity = (false, true))6263# Use macroscopic variables derived from LBM populations64# as boundary values for the Euler equations65function coupling_function_LBM2Euler(x, u, equations_other, equations_own)66rho, v1, v2, _ = cons2macroscopic(u, equations_other)67return prim2cons(SVector(rho, v1, v2), equations_own)68end6970boundary_conditions_euler = (x_neg = BoundaryConditionCoupled(2, (:end, :i_forward),71Float64,72coupling_function_LBM2Euler),73x_pos = BoundaryConditionCoupled(2, (:begin, :i_forward),74Float64,75coupling_function_LBM2Euler),76y_neg = boundary_condition_periodic,77y_pos = boundary_condition_periodic)7879semi_euler = SemidiscretizationHyperbolic(mesh_euler, eqs_euler, initial_condition_euler,80solver_euler;81boundary_conditions = boundary_conditions_euler)8283###########84# system #285# LBM86###########8788# Results in c_s = c/sqrt(3) = 1.89# This in turn implies that also in the LBM, p = c_s^2 * rho = 1 * rho = kappa * rho holds90# This is absolutely essential for the correct coupling between the two systems.91c = sqrt(3)9293# Reference values `rho0, u0` correspond to the initial condition of the Euler equations.94# The gas should be inviscid (Re = Inf) to be consistent with the inviscid Euler equations.95# The Mach number `Ma` is computed internally from the speed of sound `c_s = c / sqrt(3)` and `u0`.96eqs_lbm = LatticeBoltzmannEquations2D(c = c, Re = Inf, rho0 = 1.0, u0 = 0.0, Ma = nothing)9798# Quick & dirty implementation of the `flux_godunov` for Cartesian, yet structured meshes.99@inline function Trixi.flux_godunov(u_ll, u_rr, normal_direction::AbstractVector,100equations::LatticeBoltzmannEquations2D)101RealT = eltype(normal_direction)102if isapprox(normal_direction[2], zero(RealT), atol = 2 * eps(RealT))103v_alpha = equations.v_alpha1 * abs(normal_direction[1])104elseif isapprox(normal_direction[1], zero(RealT), atol = 2 * eps(RealT))105v_alpha = equations.v_alpha2 * abs(normal_direction[2])106else107error("Invalid normal direction for flux_godunov: $normal_direction")108end109return 0.5f0 * (v_alpha .* (u_ll + u_rr) - abs.(v_alpha) .* (u_rr - u_ll))110end111112solver_lbm = DGSEM(polydeg = 2, surface_flux = flux_godunov)113114function initial_condition_lbm(x, t, equations::LatticeBoltzmannEquations2D)115rho = (1.0 + 0.01 * exp(-(x[1] - 1)^2 / 0.1))116v1 = 0.01 * exp(-(x[1] - 1)^2 / 0.1)117118v2 = 0.0119120return equilibrium_distribution(rho, v1, v2, equations)121end122123coords_min_lbm = (0.0, -1.0)124coords_max_lbm = (2.0, 1.0)125mesh_lbm = StructuredMesh(cells_per_dim_per_section,126coords_min_lbm, coords_max_lbm,127periodicity = (false, true))128129# Supply equilibrium (Maxwellian) distribution function computed130# from the Euler-variables as boundary values for the LBM equations131function coupling_function_Euler2LBM(x, u, equations_other, equations_own)132u_prim_euler = cons2prim(u, equations_other)133rho = u_prim_euler[1]134v1 = u_prim_euler[2]135v2 = u_prim_euler[3]136137return equilibrium_distribution(rho, v1, v2, equations_own)138end139140boundary_conditions_lbm = (x_neg = BoundaryConditionCoupled(1, (:end, :i_forward),141Float64,142coupling_function_Euler2LBM),143x_pos = BoundaryConditionCoupled(1, (:begin, :i_forward),144Float64,145coupling_function_Euler2LBM),146y_neg = boundary_condition_periodic,147y_pos = boundary_condition_periodic)148149semi_lbm = SemidiscretizationHyperbolic(mesh_lbm, eqs_lbm, initial_condition_lbm,150solver_lbm;151boundary_conditions = boundary_conditions_lbm)152153# Create a semidiscretization that bundles the two semidiscretizations154semi = SemidiscretizationCoupled(semi_euler, semi_lbm)155156###############################################################################157# ODE solvers, callbacks etc.158159tspan = (0.0, 10.0)160ode = semidiscretize(semi, tspan)161162summary_callback = SummaryCallback()163164analysis_interval = 100165analysis_callback_euler = AnalysisCallback(semi_euler, interval = analysis_interval)166analysis_callback_lbm = AnalysisCallback(semi_lbm, interval = analysis_interval)167analysis_callback = AnalysisCallbackCoupled(semi,168analysis_callback_euler,169analysis_callback_lbm)170171alive_callback = AliveCallback(analysis_interval = analysis_interval)172173# Need to implement `cons2macroscopic` for `PolytropicEulerEquations2D`174# in order to be able to use this in the `SaveSolutionCallback` below175@inline function Trixi.cons2macroscopic(u, equations::PolytropicEulerEquations2D)176u_prim = cons2prim(u, equations)177p = pressure(u, equations)178return SVector(u_prim[1], u_prim[2], u_prim[3], p)179end180function Trixi.varnames(::typeof(cons2macroscopic), ::PolytropicEulerEquations2D)181("rho", "v1", "v2", "p")182end183184save_solution = SaveSolutionCallback(interval = 50,185save_initial_solution = true,186save_final_solution = true,187solution_variables = cons2macroscopic)188189cfl = 2.0190stepsize_callback = StepsizeCallback(cfl = cfl)191192# Need special version of the LBM collision callback for a `SemidiscretizationCoupled`193@inline function Trixi.lbm_collision_callback(integrator)194dt = get_proposed_dt(integrator)195semi_coupled = integrator.p # Here `p` is the `SemidiscretizationCoupled`196u_ode_full = integrator.u # ODE Vector for the entire coupled system197for (semi_index, semi_i) in enumerate(semi_coupled.semis)198mesh, equations, solver, cache = Trixi.mesh_equations_solver_cache(semi_i)199if equations isa LatticeBoltzmannEquations2D200@unpack collision_op = equations201202u_ode_i = Trixi.get_system_u_ode(u_ode_full, semi_index, semi_coupled)203u = Trixi.wrap_array(u_ode_i, mesh, equations, solver, cache)204205Trixi.@trixi_timeit Trixi.timer() "LBM collision" Trixi.apply_collision!(u, dt,206collision_op,207mesh,208equations,209solver,210cache)211end212end213214return nothing215end216217collision_callback = LBMCollisionCallback()218219callbacks = CallbackSet(summary_callback,220analysis_callback, alive_callback,221save_solution,222stepsize_callback,223collision_callback)224225###############################################################################226# run the simulation227228sol = solve(ode, SSPRK83();229dt = 0.01, # solve needs some value here but it will be overwritten by the stepsize_callback230ode_default_options()..., callback = callbacks);231232233