From 0bbe913647b05d6928e54335ca35392a079d292c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 17 Jul 2023 13:27:19 +0200 Subject: [PATCH 01/74] First try mortars for parabolic terms --- .../elixir_navierstokes_convergence.jl | 17 +- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 389 +++++++++++++++++- 2 files changed, 401 insertions(+), 5 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 36a9f52e39d..107faf78591 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -1,4 +1,4 @@ -using OrdinaryDiffEq +using OrdinaryDiffEq, Plots using Trixi ############################################################################### @@ -24,6 +24,14 @@ mesh = TreeMesh(coordinates_min, coordinates_max, periodicity=(true, false), n_cells_max=30_000) # set maximum capacity of tree data structure +# Refine mesh manually +LLID = Trixi.local_leaf_cells(mesh.tree) +num_leafs = length(LLID) + +# Refine right 3 quarters of mesh +@assert num_leafs % 4 == 0 +Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/4)]) + # Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D` # since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`) # and by the initial condition (which passes in `CompressibleEulerEquations2D`). @@ -194,7 +202,7 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` -tspan = (0.0, 0.5) +tspan = (0.0, 0.01) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() @@ -207,7 +215,10 @@ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) # run the simulation time_int_tol = 1e-8 -sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, +sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5/4, ode_default_options()..., callback=callbacks) summary_callback() # print the timer summary +plot(sol) +pd = PlotData2D(sol) +plot!(getmesh(pd)) \ No newline at end of file diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index c5862579992..1e30bc88ca5 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -85,8 +85,18 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, P4estMesh{2}}, dg.surface_integral, dg) end - # TODO: parabolic; extend to mortars - @assert nmortars(dg, cache) == 0 + # Prolong solution to mortars + @trixi_timeit timer() "prolong2mortars" begin + prolong2mortars!(cache, flux_viscous, mesh, equations_parabolic, + dg.mortar, dg.surface_integral, dg) + end + + # Calculate mortar fluxes + @trixi_timeit timer() "mortar flux" begin + calc_mortar_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, + dg.mortar, dg.surface_integral, dg, cache, cache_parabolic) + end # Calculate surface integrals @trixi_timeit timer() "surface integral" begin @@ -500,6 +510,367 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end +function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray}, + mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, surface_integral, + dg::DGSEM) + flux_viscous_x, flux_viscous_y = flux_viscous + @threaded for mortar in eachmortar(dg, cache) + large_element = cache.mortars.neighbor_ids[3, mortar] + upper_element = cache.mortars.neighbor_ids[2, mortar] + lower_element = cache.mortars.neighbor_ids[1, mortar] + + # Copy solution small to small + if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_x[v, 1, l, + upper_element] + cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_x[v, 1, l, + lower_element] + end + end + else + # L2 mortars in y-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_y[v, l, 1, + upper_element] + cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_y[v, l, 1, + lower_element] + end + end + end + else # large_sides[mortar] == 2 -> small elements on left side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_x[v, nnodes(dg), l, + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_x[v, nnodes(dg), l, + lower_element] + end + end + else + # L2 mortars in y-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_y[v, l, nnodes(dg), + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_y[v, l, nnodes(dg), + lower_element] + end + end + end + end + + # Interpolate large element face data to small interface locations + if cache.mortars.large_sides[mortar] == 1 # -> large element on left side + leftright = 1 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(flux_viscous_x, :, nnodes(dg), :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + else + # L2 mortars in y-direction + u_large = view(flux_viscous_y, :, :, nnodes(dg), large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + end + else # large_sides[mortar] == 2 -> large element on right side + leftright = 2 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(flux_viscous_x, :, 1, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + else + # L2 mortars in y-direction + u_large = view(flux_viscous_y, :, :, 1, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + end + end + end + + return nothing +end + +function prolong2mortars!(cache, u_transformed::AbstractArray, + mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, surface_integral, + dg::DGSEM) + + @threaded for mortar in eachmortar(dg, cache) + large_element = cache.mortars.neighbor_ids[3, mortar] + upper_element = cache.mortars.neighbor_ids[2, mortar] + lower_element = cache.mortars.neighbor_ids[1, mortar] + + # Copy solution small to small + if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[2, v, l, mortar] = u_transformed[v, 1, l, + upper_element] + cache.mortars.u_lower[2, v, l, mortar] = u_transformed[v, 1, l, + lower_element] + end + end + else + # L2 mortars in y-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[2, v, l, mortar] = u_transformed[v, l, 1, + upper_element] + cache.mortars.u_lower[2, v, l, mortar] = u_transformed[v, l, 1, + lower_element] + end + end + end + else # large_sides[mortar] == 2 -> small elements on left side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, nnodes(dg), l, + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, nnodes(dg), l, + lower_element] + end + end + else + # L2 mortars in y-direction + for l in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, l, nnodes(dg), + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, l, nnodes(dg), + lower_element] + end + end + end + end + + # Interpolate large element face data to small interface locations + if cache.mortars.large_sides[mortar] == 1 # -> large element on left side + leftright = 1 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(u_transformed, :, nnodes(dg), :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + else + # L2 mortars in y-direction + u_large = view(u_transformed, :, :, nnodes(dg), large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + end + else # large_sides[mortar] == 2 -> large element on right side + leftright = 2 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(u_transformed, :, 1, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + else + # L2 mortars in y-direction + u_large = view(u_transformed, :, :, 1, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large) + end + end + end + + return nothing +end + +# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs no nonconservative terms. +# Reason: "calc_interface_flux!" for parabolic part is implemented as the version for +# hyperbolic terms with conserved terms only, i.e., no nonconservative terms. +function calc_mortar_flux!(surface_flux_values, + mesh::TreeMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, + surface_integral, dg::DG, cache, cache_parabolic) + @unpack surface_flux = surface_integral + @unpack u_lower, u_upper, orientations = cache.mortars + @unpack fstar_upper_threaded, fstar_lower_threaded = cache + + @threaded for mortar in eachmortar(dg, cache) + # Choose thread-specific pre-allocated container + fstar_upper = fstar_upper_threaded[Threads.threadid()] + fstar_lower = fstar_lower_threaded[Threads.threadid()] + + # Calculate fluxes + orientation = orientations[mortar] + calc_fstar!(fstar_upper, equations_parabolic, surface_flux, dg, u_upper, mortar, + orientation, cache_parabolic) + calc_fstar!(fstar_lower, equations_parabolic, surface_flux, dg, u_lower, mortar, + orientation, cache_parabolic) + + mortar_fluxes_to_elements!(surface_flux_values, + mesh, equations_parabolic, mortar_l2, dg, cache, + mortar, fstar_upper, fstar_lower) + end + + return nothing +end + +function calc_mortar_flux!(surface_flux_values, + u_transformed::AbstractArray, + mesh::TreeMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, + surface_integral, dg::DG, cache, cache_parabolic) + @unpack surface_flux = surface_integral + @unpack u_lower, u_upper, orientations = cache.mortars + @unpack fstar_upper_threaded, fstar_lower_threaded = cache + + @threaded for mortar in eachmortar(dg, cache) + # Choose thread-specific pre-allocated container + fstar_upper = fstar_upper_threaded[Threads.threadid()] + fstar_lower = fstar_lower_threaded[Threads.threadid()] + + # Calculate fluxes + orientation = orientations[mortar] + calc_fstar!(fstar_upper, equations_parabolic, surface_flux, u_transformed, dg, u_upper, mortar, + orientation, cache_parabolic) + calc_fstar!(fstar_lower, equations_parabolic, surface_flux, u_transformed, dg, u_lower, mortar, + orientation, cache_parabolic) + + mortar_fluxes_to_elements!(surface_flux_values, + mesh, equations_parabolic, mortar_l2, dg, cache, + mortar, fstar_upper, fstar_lower) + end + + return nothing +end + +@inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, + equations_parabolic::AbstractEquationsParabolic, + surface_flux, dg::DGSEM, + u_interfaces, interface, orientation, + cache_parabolic) + for i in eachnode(dg) + # Call pointwise two-point numerical flux function + u_ll, u_rr = get_surface_node_vars(cache_parabolic.interfaces.u, equations_parabolic, dg, i, interface) + #flux = surface_flux(u_ll, u_rr, orientation, equations) + # TODO: parabolic; only BR1 at the moment + flux = 0.5 * (u_ll + u_rr) # CARE: Not sure if correct! + + # Copy flux to left and right element storage + set_node_vars!(destination, flux, equations_parabolic, dg, i) + end + + return nothing +end + +@inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, + equations_parabolic::AbstractEquationsParabolic, + surface_flux, + u_transformed::AbstractArray, + dg::DGSEM, + u_interfaces, interface, orientation, + cache_parabolic) + for i in eachnode(dg) + # Call pointwise two-point numerical flux function + + # TODO: Transformed u or regular u? Regular u is used in "calc_gradient!" -> "interface flux" + #u_ll, u_rr = get_surface_node_vars(u_transformed, equations_parabolic, dg, i, interface) + u_ll, u_rr = get_surface_node_vars(cache_parabolic.interfaces.u, equations_parabolic, dg, i, interface) + + # CARE: Not sure if correct! + #flux = surface_flux(u_ll, u_rr, orientation, equations_parabolic) + # TODO: parabolic; only BR1 at the moment + flux = 0.5 * (u_ll + u_rr) + + # Copy flux to left and right element storage + set_node_vars!(destination, flux, equations_parabolic, dg, i) + end + + return nothing +end + +@inline function mortar_fluxes_to_elements!(surface_flux_values, + mesh::TreeMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, + dg::DGSEM, cache, + mortar, fstar_upper, fstar_lower) + large_element = cache.mortars.neighbor_ids[3, mortar] + upper_element = cache.mortars.neighbor_ids[2, mortar] + lower_element = cache.mortars.neighbor_ids[1, mortar] + + # Copy flux small to small + if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + direction = 1 + else + # L2 mortars in y-direction + direction = 3 + end + else # large_sides[mortar] == 2 -> small elements on left side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + direction = 2 + else + # L2 mortars in y-direction + direction = 4 + end + end + surface_flux_values[:, :, direction, upper_element] .= fstar_upper + surface_flux_values[:, :, direction, lower_element] .= fstar_lower + + # Project small fluxes to large element + if cache.mortars.large_sides[mortar] == 1 # -> large element on left side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + direction = 2 + else + # L2 mortars in y-direction + direction = 4 + end + else # large_sides[mortar] == 2 -> large element on right side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + direction = 1 + else + # L2 mortars in y-direction + direction = 3 + end + end + + # TODO: Taal performance + # for v in eachvariable(equations) + # # The code below is semantically equivalent to + # # surface_flux_values[v, :, direction, large_element] .= + # # (mortar_l2.reverse_upper * fstar_upper[v, :] + mortar_l2.reverse_lower * fstar_lower[v, :]) + # # but faster and does not allocate. + # # Note that `true * some_float == some_float` in Julia, i.e. `true` acts as + # # a universal `one`. Hence, the second `mul!` means "add the matrix-vector + # # product to the current value of the destination". + # @views mul!(surface_flux_values[v, :, direction, large_element], + # mortar_l2.reverse_upper, fstar_upper[v, :]) + # @views mul!(surface_flux_values[v, :, direction, large_element], + # mortar_l2.reverse_lower, fstar_lower[v, :], true, true) + # end + # The code above could be replaced by the following code. However, the relative efficiency + # depends on the types of fstar_upper/fstar_lower and dg.l2mortar_reverse_upper. + # Using StaticArrays for both makes the code above faster for common test cases. + multiply_dimensionwise!(view(surface_flux_values, :, :, direction, large_element), + mortar_l2.reverse_upper, fstar_upper, + mortar_l2.reverse_lower, fstar_lower) + + return nothing +end + # Calculate the gradient of the transformed variables function calc_gradient!(gradients, u_transformed, t, mesh::TreeMesh{2}, equations_parabolic, @@ -590,6 +961,20 @@ function calc_gradient!(gradients, u_transformed, t, end # TODO: parabolic; mortars + # Prolong solution to mortars + @trixi_timeit timer() "prolong2mortars" begin + prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, + dg.mortar, dg.surface_integral, dg) + end + + # Calculate mortar fluxes + @trixi_timeit timer() "mortar flux" begin + calc_mortar_flux!(surface_flux_values, u_transformed, + mesh, + equations_parabolic, + dg.mortar, dg.surface_integral, dg, cache, cache_parabolic) + end + # Calculate surface integrals @trixi_timeit timer() "surface integral" begin From fb4efa9ff44767e9698dd690c579a70e9e65064c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 17 Jul 2023 19:45:06 +0200 Subject: [PATCH 02/74] Use correct interface values in calc_fstar! --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 62 ++--------------------- 1 file changed, 3 insertions(+), 59 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 1e30bc88ca5..115e49df6ff 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -722,36 +722,6 @@ function calc_mortar_flux!(surface_flux_values, return nothing end -function calc_mortar_flux!(surface_flux_values, - u_transformed::AbstractArray, - mesh::TreeMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - mortar_l2::LobattoLegendreMortarL2, - surface_integral, dg::DG, cache, cache_parabolic) - @unpack surface_flux = surface_integral - @unpack u_lower, u_upper, orientations = cache.mortars - @unpack fstar_upper_threaded, fstar_lower_threaded = cache - - @threaded for mortar in eachmortar(dg, cache) - # Choose thread-specific pre-allocated container - fstar_upper = fstar_upper_threaded[Threads.threadid()] - fstar_lower = fstar_lower_threaded[Threads.threadid()] - - # Calculate fluxes - orientation = orientations[mortar] - calc_fstar!(fstar_upper, equations_parabolic, surface_flux, u_transformed, dg, u_upper, mortar, - orientation, cache_parabolic) - calc_fstar!(fstar_lower, equations_parabolic, surface_flux, u_transformed, dg, u_lower, mortar, - orientation, cache_parabolic) - - mortar_fluxes_to_elements!(surface_flux_values, - mesh, equations_parabolic, mortar_l2, dg, cache, - mortar, fstar_upper, fstar_lower) - end - - return nothing -end - @inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, equations_parabolic::AbstractEquationsParabolic, surface_flux, dg::DGSEM, @@ -759,36 +729,10 @@ end cache_parabolic) for i in eachnode(dg) # Call pointwise two-point numerical flux function - u_ll, u_rr = get_surface_node_vars(cache_parabolic.interfaces.u, equations_parabolic, dg, i, interface) - #flux = surface_flux(u_ll, u_rr, orientation, equations) - # TODO: parabolic; only BR1 at the moment - flux = 0.5 * (u_ll + u_rr) # CARE: Not sure if correct! - - # Copy flux to left and right element storage - set_node_vars!(destination, flux, equations_parabolic, dg, i) - end + u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, interface) - return nothing -end - -@inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, - equations_parabolic::AbstractEquationsParabolic, - surface_flux, - u_transformed::AbstractArray, - dg::DGSEM, - u_interfaces, interface, orientation, - cache_parabolic) - for i in eachnode(dg) - # Call pointwise two-point numerical flux function - - # TODO: Transformed u or regular u? Regular u is used in "calc_gradient!" -> "interface flux" - #u_ll, u_rr = get_surface_node_vars(u_transformed, equations_parabolic, dg, i, interface) - u_ll, u_rr = get_surface_node_vars(cache_parabolic.interfaces.u, equations_parabolic, dg, i, interface) - - # CARE: Not sure if correct! - #flux = surface_flux(u_ll, u_rr, orientation, equations_parabolic) # TODO: parabolic; only BR1 at the moment - flux = 0.5 * (u_ll + u_rr) + flux = 0.5 * (u_ll + u_rr) # CARE: Not sure if correct! # Copy flux to left and right element storage set_node_vars!(destination, flux, equations_parabolic, dg, i) @@ -969,7 +913,7 @@ function calc_gradient!(gradients, u_transformed, t, # Calculate mortar fluxes @trixi_timeit timer() "mortar flux" begin - calc_mortar_flux!(surface_flux_values, u_transformed, + calc_mortar_flux!(surface_flux_values, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg, cache, cache_parabolic) From db048cbb3f186c4160a833f7849779e5055fae77 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 17 Jul 2023 20:10:41 +0200 Subject: [PATCH 03/74] Format parabolic 2d dgsem --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 78 +++++++++++++---------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 115e49df6ff..d95443f79a5 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -511,10 +511,11 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra end function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray}, - mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, + mesh::TreeMesh{2}, + equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) - flux_viscous_x, flux_viscous_y = flux_viscous + flux_viscous_x, flux_viscous_y = flux_viscous @threaded for mortar in eachmortar(dg, cache) large_element = cache.mortars.neighbor_ids[3, mortar] upper_element = cache.mortars.neighbor_ids[2, mortar] @@ -527,9 +528,9 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra for l in eachnode(dg) for v in eachvariable(equations_parabolic) cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_x[v, 1, l, - upper_element] + upper_element] cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_x[v, 1, l, - lower_element] + lower_element] end end else @@ -537,9 +538,9 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra for l in eachnode(dg) for v in eachvariable(equations_parabolic) cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_y[v, l, 1, - upper_element] + upper_element] cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_y[v, l, 1, - lower_element] + lower_element] end end end @@ -548,20 +549,26 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra # L2 mortars in x-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_x[v, nnodes(dg), l, - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_x[v, nnodes(dg), l, - lower_element] + cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_x[v, + nnodes(dg), + l, + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_x[v, + nnodes(dg), + l, + lower_element] end end else # L2 mortars in y-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_y[v, l, nnodes(dg), - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_y[v, l, nnodes(dg), - lower_element] + cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_y[v, l, + nnodes(dg), + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_y[v, l, + nnodes(dg), + lower_element] end end end @@ -601,10 +608,10 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra end function prolong2mortars!(cache, u_transformed::AbstractArray, - mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, + mesh::TreeMesh{2}, + equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) - @threaded for mortar in eachmortar(dg, cache) large_element = cache.mortars.neighbor_ids[3, mortar] upper_element = cache.mortars.neighbor_ids[2, mortar] @@ -617,9 +624,9 @@ function prolong2mortars!(cache, u_transformed::AbstractArray, for l in eachnode(dg) for v in eachvariable(equations_parabolic) cache.mortars.u_upper[2, v, l, mortar] = u_transformed[v, 1, l, - upper_element] + upper_element] cache.mortars.u_lower[2, v, l, mortar] = u_transformed[v, 1, l, - lower_element] + lower_element] end end else @@ -627,9 +634,9 @@ function prolong2mortars!(cache, u_transformed::AbstractArray, for l in eachnode(dg) for v in eachvariable(equations_parabolic) cache.mortars.u_upper[2, v, l, mortar] = u_transformed[v, l, 1, - upper_element] + upper_element] cache.mortars.u_lower[2, v, l, mortar] = u_transformed[v, l, 1, - lower_element] + lower_element] end end end @@ -638,20 +645,26 @@ function prolong2mortars!(cache, u_transformed::AbstractArray, # L2 mortars in x-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, nnodes(dg), l, - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, nnodes(dg), l, - lower_element] + cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, + nnodes(dg), + l, + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, + nnodes(dg), + l, + lower_element] end end else # L2 mortars in y-direction for l in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, l, nnodes(dg), - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, l, nnodes(dg), - lower_element] + cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, l, + nnodes(dg), + upper_element] + cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, l, + nnodes(dg), + lower_element] end end end @@ -722,17 +735,18 @@ function calc_mortar_flux!(surface_flux_values, return nothing end -@inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, +@inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, equations_parabolic::AbstractEquationsParabolic, surface_flux, dg::DGSEM, u_interfaces, interface, orientation, cache_parabolic) for i in eachnode(dg) # Call pointwise two-point numerical flux function - u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, interface) + u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, + interface) # TODO: parabolic; only BR1 at the moment - flux = 0.5 * (u_ll + u_rr) # CARE: Not sure if correct! + flux = 0.5 * (u_ll + u_rr) # Copy flux to left and right element storage set_node_vars!(destination, flux, equations_parabolic, dg, i) @@ -904,7 +918,6 @@ function calc_gradient!(gradients, u_transformed, t, dg.surface_integral, dg) end - # TODO: parabolic; mortars # Prolong solution to mortars @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, @@ -918,7 +931,6 @@ function calc_gradient!(gradients, u_transformed, t, equations_parabolic, dg.mortar, dg.surface_integral, dg, cache, cache_parabolic) end - # Calculate surface integrals @trixi_timeit timer() "surface integral" begin From f8c340357d4523e53398d839c79ebbacb74a0509 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 18 Jul 2023 09:10:04 +0200 Subject: [PATCH 04/74] Remove unused function parameters --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index d95443f79a5..500cff3a7c4 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -95,7 +95,7 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, P4estMesh{2}}, @trixi_timeit timer() "mortar flux" begin calc_mortar_flux!(cache_parabolic.elements.surface_flux_values, mesh, equations_parabolic, - dg.mortar, dg.surface_integral, dg, cache, cache_parabolic) + dg.mortar, dg.surface_integral, dg, cache) end # Calculate surface integrals @@ -607,6 +607,8 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra return nothing end +# TODO: This is essentially not different from the implementation in standard 2D. +# Only difference: defined `u_transformed` (equivalent of `u`) as AbstractArray. function prolong2mortars!(cache, u_transformed::AbstractArray, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, @@ -703,14 +705,14 @@ function prolong2mortars!(cache, u_transformed::AbstractArray, return nothing end -# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs no nonconservative terms. -# Reason: "calc_interface_flux!" for parabolic part is implemented as the version for +# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms. +# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for # hyperbolic terms with conserved terms only, i.e., no nonconservative terms. function calc_mortar_flux!(surface_flux_values, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, - surface_integral, dg::DG, cache, cache_parabolic) + surface_integral, dg::DG, cache) @unpack surface_flux = surface_integral @unpack u_lower, u_upper, orientations = cache.mortars @unpack fstar_upper_threaded, fstar_lower_threaded = cache @@ -723,9 +725,9 @@ function calc_mortar_flux!(surface_flux_values, # Calculate fluxes orientation = orientations[mortar] calc_fstar!(fstar_upper, equations_parabolic, surface_flux, dg, u_upper, mortar, - orientation, cache_parabolic) + orientation) calc_fstar!(fstar_lower, equations_parabolic, surface_flux, dg, u_lower, mortar, - orientation, cache_parabolic) + orientation) mortar_fluxes_to_elements!(surface_flux_values, mesh, equations_parabolic, mortar_l2, dg, cache, @@ -738,13 +740,11 @@ end @inline function calc_fstar!(destination::AbstractArray{<:Any, 2}, equations_parabolic::AbstractEquationsParabolic, surface_flux, dg::DGSEM, - u_interfaces, interface, orientation, - cache_parabolic) + u_interfaces, interface, orientation) for i in eachnode(dg) # Call pointwise two-point numerical flux function u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, interface) - # TODO: parabolic; only BR1 at the moment flux = 0.5 * (u_ll + u_rr) @@ -929,7 +929,7 @@ function calc_gradient!(gradients, u_transformed, t, calc_mortar_flux!(surface_flux_values, mesh, equations_parabolic, - dg.mortar, dg.surface_integral, dg, cache, cache_parabolic) + dg.mortar, dg.surface_integral, dg, cache) end # Calculate surface integrals From e9d16c75b1c9c0e6eae9b047552b327dc9ee1737 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 18 Jul 2023 09:21:01 +0200 Subject: [PATCH 05/74] L2 Mortars for 3D DGSEM TreeMesh --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 433 +++++++++++++++++++++- 1 file changed, 430 insertions(+), 3 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index d6d74637021..177f256269e 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -85,8 +85,18 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{3}, dg.surface_integral, dg) end - # TODO: parabolic; extend to mortars - @assert nmortars(dg, cache) == 0 + # Prolong solution to mortars + @trixi_timeit timer() "prolong2mortars" begin + prolong2mortars!(cache, flux_viscous, mesh, equations_parabolic, + dg.mortar, dg.surface_integral, dg) + end + + # Calculate mortar fluxes + @trixi_timeit timer() "mortar flux" begin + calc_mortar_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, + dg.mortar, dg.surface_integral, dg, cache) + end # Calculate surface integrals @trixi_timeit timer() "surface integral" begin @@ -582,6 +592,411 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end +function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray, AbstractArray}, + mesh::TreeMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, + surface_integral, dg::DGSEM) + # temporary buffer for projections + @unpack fstar_tmp1_threaded = cache # CARE: Not sure if I have to do something with this (not present in 2D) + + flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous + @threaded for mortar in eachmortar(dg, cache) + fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()] + + lower_left_element = cache.mortars.neighbor_ids[1, mortar] + lower_right_element = cache.mortars.neighbor_ids[2, mortar] + upper_left_element = cache.mortars.neighbor_ids[3, mortar] + upper_right_element = cache.mortars.neighbor_ids[4, mortar] + large_element = cache.mortars.neighbor_ids[5, mortar] + + # Copy solution small to small + if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for k in eachnode(dg), j in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, + upper_left_element] + cache.mortars.u_upper_right[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, + upper_right_element] + cache.mortars.u_lower_left[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, + lower_left_element] + cache.mortars.u_lower_right[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, + lower_right_element] + end + end + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + for k in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, + lower_right_element] + end + end + else # orientations[mortar] == 3 + # L2 mortars in z-direction + for j in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, + lower_right_element] + end + end + end + else # large_sides[mortar] == 2 -> small elements on left side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for k in eachnode(dg), j in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[1, v, j, k, mortar] = flux_viscous_x[v, + nnodes(dg), + j, k, + upper_left_element] + cache.mortars.u_upper_right[1, v, j, k, mortar] = flux_viscous_x[v, + nnodes(dg), + j, k, + upper_right_element] + cache.mortars.u_lower_left[1, v, j, k, mortar] = flux_viscous_x[v, + nnodes(dg), + j, k, + lower_left_element] + cache.mortars.u_lower_right[1, v, j, k, mortar] = flux_viscous_x[v, + nnodes(dg), + j, k, + lower_right_element] + end + end + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + for k in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[1, v, i, k, mortar] = flux_viscous_y[v, i, + nnodes(dg), + k, + upper_left_element] + cache.mortars.u_upper_right[1, v, i, k, mortar] = flux_viscous_y[v, i, + nnodes(dg), + k, + upper_right_element] + cache.mortars.u_lower_left[1, v, i, k, mortar] = flux_viscous_y[v, i, + nnodes(dg), + k, + lower_left_element] + cache.mortars.u_lower_right[1, v, i, k, mortar] = flux_viscous_y[v, i, + nnodes(dg), + k, + lower_right_element] + end + end + else # if cache.mortars.orientations[mortar] == 3 + # L2 mortars in z-direction + for j in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[1, v, i, j, mortar] = flux_viscous_z[v, i, j, + nnodes(dg), + upper_left_element] + cache.mortars.u_upper_right[1, v, i, j, mortar] = flux_viscous_z[v, i, j, + nnodes(dg), + upper_right_element] + cache.mortars.u_lower_left[1, v, i, j, mortar] = flux_viscous_z[v, i, j, + nnodes(dg), + lower_left_element] + cache.mortars.u_lower_right[1, v, i, j, mortar] = flux_viscous_z[v, i, j, + nnodes(dg), + lower_right_element] + end + end + end + end + + # Interpolate large element face data to small interface locations + if cache.mortars.large_sides[mortar] == 1 # -> large element on left side + leftright = 1 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(flux_viscous_x, :, nnodes(dg), :, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + u_large = view(flux_viscous_y, :, :, nnodes(dg), :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + else # cache.mortars.orientations[mortar] == 3 + # L2 mortars in z-direction + u_large = view(flux_viscous_z, :, :, :, nnodes(dg), large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + end + else # large_sides[mortar] == 2 -> large element on right side + leftright = 2 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(flux_viscous_x, :, 1, :, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + u_large = view(flux_viscous_y, :, :, 1, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + else # cache.mortars.orientations[mortar] == 3 + # L2 mortars in z-direction + u_large = view(flux_viscous_z, :, :, :, 1, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + end + end + end + + return nothing +end + +# TODO: This is essentially not different from the implementation in standard 3D. +# Only difference: defined `u_transformed` (equivalent of `u`) as AbstractArray. +function prolong2mortars!(cache, u_transformed::AbstractArray, + mesh::TreeMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, + surface_integral, dg::DGSEM) + # temporary buffer for projections + @unpack fstar_tmp1_threaded = cache # CARE: Not sure if I have to do something with this (not present in 2D) + + @threaded for mortar in eachmortar(dg, cache) + fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()] + + lower_left_element = cache.mortars.neighbor_ids[1, mortar] + lower_right_element = cache.mortars.neighbor_ids[2, mortar] + upper_left_element = cache.mortars.neighbor_ids[3, mortar] + upper_right_element = cache.mortars.neighbor_ids[4, mortar] + large_element = cache.mortars.neighbor_ids[5, mortar] + + # Copy solution small to small + if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for k in eachnode(dg), j in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[2, v, j, k, mortar] = u_transformed[v, 1, j, k, + upper_left_element] + cache.mortars.u_upper_right[2, v, j, k, mortar] = u_transformed[v, 1, j, k, + upper_right_element] + cache.mortars.u_lower_left[2, v, j, k, mortar] = u_transformed[v, 1, j, k, + lower_left_element] + cache.mortars.u_lower_right[2, v, j, k, mortar] = u_transformed[v, 1, j, k, + lower_right_element] + end + end + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + for k in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[2, v, i, k, mortar] = u_transformed[v, i, 1, k, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, k, mortar] = u_transformed[v, i, 1, k, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, k, mortar] = u_transformed[v, i, 1, k, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, k, mortar] = u_transformed[v, i, 1, k, + lower_right_element] + end + end + else # orientations[mortar] == 3 + # L2 mortars in z-direction + for j in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[2, v, i, j, mortar] = u_transformed[v, i, j, 1, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, j, mortar] = u_transformed[v, i, j, 1, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, j, mortar] = u_transformed[v, i, j, 1, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, j, mortar] = u_transformed[v, i, j, 1, + lower_right_element] + end + end + end + else # large_sides[mortar] == 2 -> small elements on left side + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + for k in eachnode(dg), j in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[1, v, j, k, mortar] = u_transformed[v, + nnodes(dg), + j, k, + upper_left_element] + cache.mortars.u_upper_right[1, v, j, k, mortar] = u_transformed[v, + nnodes(dg), + j, k, + upper_right_element] + cache.mortars.u_lower_left[1, v, j, k, mortar] = u_transformed[v, + nnodes(dg), + j, k, + lower_left_element] + cache.mortars.u_lower_right[1, v, j, k, mortar] = u_transformed[v, + nnodes(dg), + j, k, + lower_right_element] + end + end + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + for k in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[1, v, i, k, mortar] = u_transformed[v, i, + nnodes(dg), + k, + upper_left_element] + cache.mortars.u_upper_right[1, v, i, k, mortar] = u_transformed[v, i, + nnodes(dg), + k, + upper_right_element] + cache.mortars.u_lower_left[1, v, i, k, mortar] = u_transformed[v, i, + nnodes(dg), + k, + lower_left_element] + cache.mortars.u_lower_right[1, v, i, k, mortar] = u_transformed[v, i, + nnodes(dg), + k, + lower_right_element] + end + end + else # if cache.mortars.orientations[mortar] == 3 + # L2 mortars in z-direction + for j in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations_parabolic) + cache.mortars.u_upper_left[1, v, i, j, mortar] = u_transformed[v, i, j, + nnodes(dg), + upper_left_element] + cache.mortars.u_upper_right[1, v, i, j, mortar] = u_transformed[v, i, j, + nnodes(dg), + upper_right_element] + cache.mortars.u_lower_left[1, v, i, j, mortar] = u_transformed[v, i, j, + nnodes(dg), + lower_left_element] + cache.mortars.u_lower_right[1, v, i, j, mortar] = u_transformed[v, i, j, + nnodes(dg), + lower_right_element] + end + end + end + end + + # Interpolate large element face data to small interface locations + if cache.mortars.large_sides[mortar] == 1 # -> large element on left side + leftright = 1 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(u_transformed, :, nnodes(dg), :, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + u_large = view(u_transformed, :, :, nnodes(dg), :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + else # cache.mortars.orientations[mortar] == 3 + # L2 mortars in z-direction + u_large = view(u_transformed, :, :, :, nnodes(dg), large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + end + else # large_sides[mortar] == 2 -> large element on right side + leftright = 2 + if cache.mortars.orientations[mortar] == 1 + # L2 mortars in x-direction + u_large = view(u_transformed, :, 1, :, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + elseif cache.mortars.orientations[mortar] == 2 + # L2 mortars in y-direction + u_large = view(u_transformed, :, :, 1, :, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + else # cache.mortars.orientations[mortar] == 3 + # L2 mortars in z-direction + u_large = view(u_transformed, :, :, :, 1, large_element) + element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, + mortar, u_large, fstar_tmp1) + end + end + end + + return nothing +end + +# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms. +# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for +# hyperbolic terms with conserved terms only, i.e., no nonconservative terms. +function calc_mortar_flux!(surface_flux_values, + mesh::TreeMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + mortar_l2::LobattoLegendreMortarL2, + surface_integral, dg::DG, cache) + @unpack surface_flux = surface_integral + @unpack u_lower_left, u_lower_right, u_upper_left, u_upper_right, orientations = cache.mortars + @unpack (fstar_upper_left_threaded, fstar_upper_right_threaded, + fstar_lower_left_threaded, fstar_lower_right_threaded, + fstar_tmp1_threaded) = cache + + @threaded for mortar in eachmortar(dg, cache) + # Choose thread-specific pre-allocated container + fstar_upper_left = fstar_upper_left_threaded[Threads.threadid()] + fstar_upper_right = fstar_upper_right_threaded[Threads.threadid()] + fstar_lower_left = fstar_lower_left_threaded[Threads.threadid()] + fstar_lower_right = fstar_lower_right_threaded[Threads.threadid()] + fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()] + + # Calculate fluxes + orientation = orientations[mortar] + calc_fstar!(fstar_upper_left, equations_parabolic, surface_flux, dg, u_upper_left, mortar, + orientation) + calc_fstar!(fstar_upper_right, equations_parabolic, surface_flux, dg, u_upper_right, + mortar, orientation) + calc_fstar!(fstar_lower_left, equations_parabolic, surface_flux, dg, u_lower_left, mortar, + orientation) + calc_fstar!(fstar_lower_right, equations_parabolic, surface_flux, dg, u_lower_right, + mortar, orientation) + + mortar_fluxes_to_elements!(surface_flux_values, + mesh, equations_parabolic, mortar_l2, dg, cache, mortar, + fstar_upper_left, fstar_upper_right, + fstar_lower_left, fstar_lower_right, + fstar_tmp1) + end + + return nothing +end + +@inline function calc_fstar!(destination::AbstractArray{<:Any, 3}, + equations_parabolic::AbstractEquationsParabolic, + surface_flux, dg::DGSEM, + u_interfaces, interface, orientation) + for j in eachnode(dg), i in eachnode(dg) + # Call pointwise two-point numerical flux function + u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, j, interface) + # TODO: parabolic; only BR1 at the moment + flux = 0.5 * (u_ll + u_rr) + + # Copy flux to left and right element storage + set_node_vars!(destination, flux, equations_parabolic, dg, i, j) + end + + return nothing +end + # Calculate the gradient of the transformed variables function calc_gradient!(gradients, u_transformed, t, mesh::TreeMesh{3}, equations_parabolic, @@ -678,7 +1093,19 @@ function calc_gradient!(gradients, u_transformed, t, dg.surface_integral, dg) end - # TODO: parabolic; mortars + # Prolong solution to mortars + @trixi_timeit timer() "prolong2mortars" begin + prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, + dg.mortar, dg.surface_integral, dg) + end + + # Calculate mortar fluxes + @trixi_timeit timer() "mortar flux" begin + calc_mortar_flux!(surface_flux_values, + mesh, + equations_parabolic, + dg.mortar, dg.surface_integral, dg, cache) + end # Calculate surface integrals @trixi_timeit timer() "surface integral" begin From a9185a5e49f27ac0f558ffefec7872d340123983 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 18 Jul 2023 09:22:12 +0200 Subject: [PATCH 06/74] Format --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 386 ++++++++++++++-------- 1 file changed, 249 insertions(+), 137 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 177f256269e..e5fc2a4d1f5 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -592,8 +592,10 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end -function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray, AbstractArray}, - mesh::TreeMesh{3}, +function prolong2mortars!(cache, + flux_viscous::Tuple{AbstractArray, AbstractArray, + AbstractArray}, + mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) @@ -616,42 +618,78 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra # L2 mortars in x-direction for k in eachnode(dg), j in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, - upper_left_element] - cache.mortars.u_upper_right[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, - upper_right_element] - cache.mortars.u_lower_left[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, - lower_left_element] - cache.mortars.u_lower_right[2, v, j, k, mortar] = flux_viscous_x[v, 1, j, k, - lower_right_element] + cache.mortars.u_upper_left[2, v, j, k, mortar] = flux_viscous_x[v, + 1, + j, + k, + upper_left_element] + cache.mortars.u_upper_right[2, v, j, k, mortar] = flux_viscous_x[v, + 1, + j, + k, + upper_right_element] + cache.mortars.u_lower_left[2, v, j, k, mortar] = flux_viscous_x[v, + 1, + j, + k, + lower_left_element] + cache.mortars.u_lower_right[2, v, j, k, mortar] = flux_viscous_x[v, + 1, + j, + k, + lower_right_element] end end elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction for k in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, k, mortar] = flux_viscous_y[v, i, 1, k, - lower_right_element] + cache.mortars.u_upper_left[2, v, i, k, mortar] = flux_viscous_y[v, + i, + 1, + k, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, k, mortar] = flux_viscous_y[v, + i, + 1, + k, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, k, mortar] = flux_viscous_y[v, + i, + 1, + k, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, k, mortar] = flux_viscous_y[v, + i, + 1, + k, + lower_right_element] end end else # orientations[mortar] == 3 # L2 mortars in z-direction for j in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, j, mortar] = flux_viscous_z[v, i, j, 1, - lower_right_element] + cache.mortars.u_upper_left[2, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + 1, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + 1, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + 1, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + 1, + lower_right_element] end end end @@ -661,61 +699,77 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra for k in eachnode(dg), j in eachnode(dg) for v in eachvariable(equations_parabolic) cache.mortars.u_upper_left[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, k, - upper_left_element] + nnodes(dg), + j, + k, + upper_left_element] cache.mortars.u_upper_right[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, k, - upper_right_element] + nnodes(dg), + j, + k, + upper_right_element] cache.mortars.u_lower_left[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, k, - lower_left_element] + nnodes(dg), + j, + k, + lower_left_element] cache.mortars.u_lower_right[1, v, j, k, mortar] = flux_viscous_x[v, - nnodes(dg), - j, k, - lower_right_element] + nnodes(dg), + j, + k, + lower_right_element] end end elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction for k in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, k, mortar] = flux_viscous_y[v, i, - nnodes(dg), - k, - upper_left_element] - cache.mortars.u_upper_right[1, v, i, k, mortar] = flux_viscous_y[v, i, - nnodes(dg), - k, - upper_right_element] - cache.mortars.u_lower_left[1, v, i, k, mortar] = flux_viscous_y[v, i, - nnodes(dg), - k, - lower_left_element] - cache.mortars.u_lower_right[1, v, i, k, mortar] = flux_viscous_y[v, i, - nnodes(dg), - k, - lower_right_element] + cache.mortars.u_upper_left[1, v, i, k, mortar] = flux_viscous_y[v, + i, + nnodes(dg), + k, + upper_left_element] + cache.mortars.u_upper_right[1, v, i, k, mortar] = flux_viscous_y[v, + i, + nnodes(dg), + k, + upper_right_element] + cache.mortars.u_lower_left[1, v, i, k, mortar] = flux_viscous_y[v, + i, + nnodes(dg), + k, + lower_left_element] + cache.mortars.u_lower_right[1, v, i, k, mortar] = flux_viscous_y[v, + i, + nnodes(dg), + k, + lower_right_element] end end else # if cache.mortars.orientations[mortar] == 3 # L2 mortars in z-direction for j in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, j, mortar] = flux_viscous_z[v, i, j, - nnodes(dg), - upper_left_element] - cache.mortars.u_upper_right[1, v, i, j, mortar] = flux_viscous_z[v, i, j, - nnodes(dg), - upper_right_element] - cache.mortars.u_lower_left[1, v, i, j, mortar] = flux_viscous_z[v, i, j, - nnodes(dg), - lower_left_element] - cache.mortars.u_lower_right[1, v, i, j, mortar] = flux_viscous_z[v, i, j, - nnodes(dg), - lower_right_element] + cache.mortars.u_upper_left[1, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + nnodes(dg), + upper_left_element] + cache.mortars.u_upper_right[1, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + nnodes(dg), + upper_right_element] + cache.mortars.u_lower_left[1, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + nnodes(dg), + lower_left_element] + cache.mortars.u_lower_right[1, v, i, j, mortar] = flux_viscous_z[v, + i, + j, + nnodes(dg), + lower_right_element] end end end @@ -789,42 +843,78 @@ function prolong2mortars!(cache, u_transformed::AbstractArray, # L2 mortars in x-direction for k in eachnode(dg), j in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, j, k, mortar] = u_transformed[v, 1, j, k, - upper_left_element] - cache.mortars.u_upper_right[2, v, j, k, mortar] = u_transformed[v, 1, j, k, - upper_right_element] - cache.mortars.u_lower_left[2, v, j, k, mortar] = u_transformed[v, 1, j, k, - lower_left_element] - cache.mortars.u_lower_right[2, v, j, k, mortar] = u_transformed[v, 1, j, k, - lower_right_element] + cache.mortars.u_upper_left[2, v, j, k, mortar] = u_transformed[v, + 1, + j, + k, + upper_left_element] + cache.mortars.u_upper_right[2, v, j, k, mortar] = u_transformed[v, + 1, + j, + k, + upper_right_element] + cache.mortars.u_lower_left[2, v, j, k, mortar] = u_transformed[v, + 1, + j, + k, + lower_left_element] + cache.mortars.u_lower_right[2, v, j, k, mortar] = u_transformed[v, + 1, + j, + k, + lower_right_element] end end elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction for k in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, k, mortar] = u_transformed[v, i, 1, k, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, k, mortar] = u_transformed[v, i, 1, k, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, k, mortar] = u_transformed[v, i, 1, k, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, k, mortar] = u_transformed[v, i, 1, k, - lower_right_element] + cache.mortars.u_upper_left[2, v, i, k, mortar] = u_transformed[v, + i, + 1, + k, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, k, mortar] = u_transformed[v, + i, + 1, + k, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, k, mortar] = u_transformed[v, + i, + 1, + k, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, k, mortar] = u_transformed[v, + i, + 1, + k, + lower_right_element] end end else # orientations[mortar] == 3 # L2 mortars in z-direction for j in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, j, mortar] = u_transformed[v, i, j, 1, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, j, mortar] = u_transformed[v, i, j, 1, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, j, mortar] = u_transformed[v, i, j, 1, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, j, mortar] = u_transformed[v, i, j, 1, - lower_right_element] + cache.mortars.u_upper_left[2, v, i, j, mortar] = u_transformed[v, + i, + j, + 1, + upper_left_element] + cache.mortars.u_upper_right[2, v, i, j, mortar] = u_transformed[v, + i, + j, + 1, + upper_right_element] + cache.mortars.u_lower_left[2, v, i, j, mortar] = u_transformed[v, + i, + j, + 1, + lower_left_element] + cache.mortars.u_lower_right[2, v, i, j, mortar] = u_transformed[v, + i, + j, + 1, + lower_right_element] end end end @@ -834,61 +924,77 @@ function prolong2mortars!(cache, u_transformed::AbstractArray, for k in eachnode(dg), j in eachnode(dg) for v in eachvariable(equations_parabolic) cache.mortars.u_upper_left[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, k, - upper_left_element] + nnodes(dg), + j, + k, + upper_left_element] cache.mortars.u_upper_right[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, k, - upper_right_element] + nnodes(dg), + j, + k, + upper_right_element] cache.mortars.u_lower_left[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, k, - lower_left_element] + nnodes(dg), + j, + k, + lower_left_element] cache.mortars.u_lower_right[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, k, - lower_right_element] + nnodes(dg), + j, + k, + lower_right_element] end end elseif cache.mortars.orientations[mortar] == 2 # L2 mortars in y-direction for k in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, k, mortar] = u_transformed[v, i, - nnodes(dg), - k, - upper_left_element] - cache.mortars.u_upper_right[1, v, i, k, mortar] = u_transformed[v, i, - nnodes(dg), - k, - upper_right_element] - cache.mortars.u_lower_left[1, v, i, k, mortar] = u_transformed[v, i, - nnodes(dg), - k, - lower_left_element] - cache.mortars.u_lower_right[1, v, i, k, mortar] = u_transformed[v, i, - nnodes(dg), - k, - lower_right_element] + cache.mortars.u_upper_left[1, v, i, k, mortar] = u_transformed[v, + i, + nnodes(dg), + k, + upper_left_element] + cache.mortars.u_upper_right[1, v, i, k, mortar] = u_transformed[v, + i, + nnodes(dg), + k, + upper_right_element] + cache.mortars.u_lower_left[1, v, i, k, mortar] = u_transformed[v, + i, + nnodes(dg), + k, + lower_left_element] + cache.mortars.u_lower_right[1, v, i, k, mortar] = u_transformed[v, + i, + nnodes(dg), + k, + lower_right_element] end end else # if cache.mortars.orientations[mortar] == 3 # L2 mortars in z-direction for j in eachnode(dg), i in eachnode(dg) for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, j, mortar] = u_transformed[v, i, j, - nnodes(dg), - upper_left_element] - cache.mortars.u_upper_right[1, v, i, j, mortar] = u_transformed[v, i, j, - nnodes(dg), - upper_right_element] - cache.mortars.u_lower_left[1, v, i, j, mortar] = u_transformed[v, i, j, - nnodes(dg), - lower_left_element] - cache.mortars.u_lower_right[1, v, i, j, mortar] = u_transformed[v, i, j, - nnodes(dg), - lower_right_element] + cache.mortars.u_upper_left[1, v, i, j, mortar] = u_transformed[v, + i, + j, + nnodes(dg), + upper_left_element] + cache.mortars.u_upper_right[1, v, i, j, mortar] = u_transformed[v, + i, + j, + nnodes(dg), + upper_right_element] + cache.mortars.u_lower_left[1, v, i, j, mortar] = u_transformed[v, + i, + j, + nnodes(dg), + lower_left_element] + cache.mortars.u_lower_right[1, v, i, j, mortar] = u_transformed[v, + i, + j, + nnodes(dg), + lower_right_element] end end end @@ -961,17 +1067,22 @@ function calc_mortar_flux!(surface_flux_values, # Calculate fluxes orientation = orientations[mortar] - calc_fstar!(fstar_upper_left, equations_parabolic, surface_flux, dg, u_upper_left, mortar, + calc_fstar!(fstar_upper_left, equations_parabolic, surface_flux, dg, + u_upper_left, mortar, orientation) - calc_fstar!(fstar_upper_right, equations_parabolic, surface_flux, dg, u_upper_right, + calc_fstar!(fstar_upper_right, equations_parabolic, surface_flux, dg, + u_upper_right, mortar, orientation) - calc_fstar!(fstar_lower_left, equations_parabolic, surface_flux, dg, u_lower_left, mortar, + calc_fstar!(fstar_lower_left, equations_parabolic, surface_flux, dg, + u_lower_left, mortar, orientation) - calc_fstar!(fstar_lower_right, equations_parabolic, surface_flux, dg, u_lower_right, + calc_fstar!(fstar_lower_right, equations_parabolic, surface_flux, dg, + u_lower_right, mortar, orientation) mortar_fluxes_to_elements!(surface_flux_values, - mesh, equations_parabolic, mortar_l2, dg, cache, mortar, + mesh, equations_parabolic, mortar_l2, dg, cache, + mortar, fstar_upper_left, fstar_upper_right, fstar_lower_left, fstar_lower_right, fstar_tmp1) @@ -980,13 +1091,14 @@ function calc_mortar_flux!(surface_flux_values, return nothing end -@inline function calc_fstar!(destination::AbstractArray{<:Any, 3}, +@inline function calc_fstar!(destination::AbstractArray{<:Any, 3}, equations_parabolic::AbstractEquationsParabolic, surface_flux, dg::DGSEM, u_interfaces, interface, orientation) for j in eachnode(dg), i in eachnode(dg) # Call pointwise two-point numerical flux function - u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, j, interface) + u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, j, + interface) # TODO: parabolic; only BR1 at the moment flux = 0.5 * (u_ll + u_rr) From 90ce643930fe2103c1a70e0b8fdf8297d1dfbcd4 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 18 Jul 2023 09:24:31 +0200 Subject: [PATCH 07/74] Back to original example --- .../elixir_navierstokes_convergence.jl | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 107faf78591..36a9f52e39d 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -1,4 +1,4 @@ -using OrdinaryDiffEq, Plots +using OrdinaryDiffEq using Trixi ############################################################################### @@ -24,14 +24,6 @@ mesh = TreeMesh(coordinates_min, coordinates_max, periodicity=(true, false), n_cells_max=30_000) # set maximum capacity of tree data structure -# Refine mesh manually -LLID = Trixi.local_leaf_cells(mesh.tree) -num_leafs = length(LLID) - -# Refine right 3 quarters of mesh -@assert num_leafs % 4 == 0 -Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/4)]) - # Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D` # since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`) # and by the initial condition (which passes in `CompressibleEulerEquations2D`). @@ -202,7 +194,7 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` -tspan = (0.0, 0.01) +tspan = (0.0, 0.5) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() @@ -215,10 +207,7 @@ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) # run the simulation time_int_tol = 1e-8 -sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5/4, +sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, ode_default_options()..., callback=callbacks) summary_callback() # print the timer summary -plot(sol) -pd = PlotData2D(sol) -plot!(getmesh(pd)) \ No newline at end of file From b37774ef2c433edc74cb014e23ae5bab0681622c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 18 Jul 2023 10:15:24 +0200 Subject: [PATCH 08/74] Dispatch 2D DGSEm rhs_parabolic for p4est and classic tree --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 91 ++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 500cff3a7c4..c0086df7603 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -13,7 +13,7 @@ # 2. compute f(u, grad(u)) # 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call) # boundary conditions will be applied to both grad(u) and div(f(u, grad(u))). -function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, P4estMesh{2}}, +function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) @@ -112,6 +112,95 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, P4estMesh{2}}, return nothing end +function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + initial_condition, boundary_conditions_parabolic, source_terms, + dg::DG, parabolic_scheme, cache, cache_parabolic) + (; u_transformed, gradients, flux_viscous) = cache_parabolic + + # Convert conservative variables to a form more suitable for viscous flux calculations + @trixi_timeit timer() "transform variables" begin + transform_variables!(u_transformed, u, mesh, equations_parabolic, + dg, parabolic_scheme, cache, cache_parabolic) + end + + # Compute the gradients of the transformed variables + @trixi_timeit timer() "calculate gradient" begin + calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, + boundary_conditions_parabolic, dg, cache, cache_parabolic) + end + + # Compute and store the viscous fluxes + @trixi_timeit timer() "calculate viscous fluxes" begin + calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, + equations_parabolic, dg, cache, cache_parabolic) + end + + # The remainder of this function is essentially a regular rhs! for parabolic + # equations (i.e., it computes the divergence of the viscous fluxes) + # + # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have + # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the + # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we + # do not need to recreate the existing data structure only with a different name, and c) we do not + # need to interpolate solutions *and* gradients to the surfaces. + + # TODO: parabolic; reconsider current data structure reuse strategy + + # Reset du + @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) + + # Calculate volume integral + @trixi_timeit timer() "volume integral" begin + calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + end + + # Prolong solution to interfaces + @trixi_timeit timer() "prolong2interfaces" begin + prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate interface fluxes + @trixi_timeit timer() "interface flux" begin + calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, dg, cache_parabolic) + end + + # Prolong solution to boundaries + @trixi_timeit timer() "prolong2boundaries" begin + prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate boundary fluxes + @trixi_timeit timer() "boundary flux" begin + calc_boundary_flux_divergence!(cache_parabolic, t, + boundary_conditions_parabolic, mesh, + equations_parabolic, + dg.surface_integral, dg) + end + + # TODO: parabolic; extend to mortars + @assert nmortars(dg, cache) == 0 + + # Calculate surface integrals + @trixi_timeit timer() "surface integral" begin + calc_surface_integral!(du, u, mesh, equations_parabolic, + dg.surface_integral, dg, cache_parabolic) + end + + # Apply Jacobian from mapping to reference element + @trixi_timeit timer() "Jacobian" begin + apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) + end + + return nothing +end + # Transform solution variables prior to taking the gradient # (e.g., conservative to primitive variables). Defaults to doing nothing. # TODO: can we avoid copying data? From 2149cfd9efd82ea086f2e4d6d138c4c683ad1c7a Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 18 Jul 2023 10:53:50 +0200 Subject: [PATCH 09/74] Re-use standard prolong2mortars in gradient comp --- src/solvers/dgsem_tree/dg_2d.jl | 2 +- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 98 ---------- src/solvers/dgsem_tree/dg_3d.jl | 2 +- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 225 ---------------------- 4 files changed, 2 insertions(+), 325 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl index d3227710686..aeb18da2066 100644 --- a/src/solvers/dgsem_tree/dg_2d.jl +++ b/src/solvers/dgsem_tree/dg_2d.jl @@ -782,7 +782,7 @@ function calc_boundary_flux_by_direction!(surface_flux_values::AbstractArray{<:A return nothing end -function prolong2mortars!(cache, u, +function prolong2mortars!(cache, u::AbstractArray, mesh::TreeMesh{2}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index c0086df7603..6cc229ff803 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -696,104 +696,6 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra return nothing end -# TODO: This is essentially not different from the implementation in standard 2D. -# Only difference: defined `u_transformed` (equivalent of `u`) as AbstractArray. -function prolong2mortars!(cache, u_transformed::AbstractArray, - mesh::TreeMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - mortar_l2::LobattoLegendreMortarL2, surface_integral, - dg::DGSEM) - @threaded for mortar in eachmortar(dg, cache) - large_element = cache.mortars.neighbor_ids[3, mortar] - upper_element = cache.mortars.neighbor_ids[2, mortar] - lower_element = cache.mortars.neighbor_ids[1, mortar] - - # Copy solution small to small - if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - for l in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[2, v, l, mortar] = u_transformed[v, 1, l, - upper_element] - cache.mortars.u_lower[2, v, l, mortar] = u_transformed[v, 1, l, - lower_element] - end - end - else - # L2 mortars in y-direction - for l in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[2, v, l, mortar] = u_transformed[v, l, 1, - upper_element] - cache.mortars.u_lower[2, v, l, mortar] = u_transformed[v, l, 1, - lower_element] - end - end - end - else # large_sides[mortar] == 2 -> small elements on left side - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - for l in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, - nnodes(dg), - l, - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, - nnodes(dg), - l, - lower_element] - end - end - else - # L2 mortars in y-direction - for l in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper[1, v, l, mortar] = u_transformed[v, l, - nnodes(dg), - upper_element] - cache.mortars.u_lower[1, v, l, mortar] = u_transformed[v, l, - nnodes(dg), - lower_element] - end - end - end - end - - # Interpolate large element face data to small interface locations - if cache.mortars.large_sides[mortar] == 1 # -> large element on left side - leftright = 1 - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - u_large = view(u_transformed, :, nnodes(dg), :, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large) - else - # L2 mortars in y-direction - u_large = view(u_transformed, :, :, nnodes(dg), large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large) - end - else # large_sides[mortar] == 2 -> large element on right side - leftright = 2 - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - u_large = view(u_transformed, :, 1, :, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large) - else - # L2 mortars in y-direction - u_large = view(u_transformed, :, :, 1, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large) - end - end - end - - return nothing -end - # NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms. # Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for # hyperbolic terms with conserved terms only, i.e., no nonconservative terms. diff --git a/src/solvers/dgsem_tree/dg_3d.jl b/src/solvers/dgsem_tree/dg_3d.jl index 95abb2595e5..c1df64b80c6 100644 --- a/src/solvers/dgsem_tree/dg_3d.jl +++ b/src/solvers/dgsem_tree/dg_3d.jl @@ -834,7 +834,7 @@ function calc_boundary_flux_by_direction!(surface_flux_values::AbstractArray{<:A return nothing end -function prolong2mortars!(cache, u, +function prolong2mortars!(cache, u::AbstractArray, mesh::TreeMesh{3}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index e5fc2a4d1f5..df1084a5b73 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -818,231 +818,6 @@ function prolong2mortars!(cache, return nothing end -# TODO: This is essentially not different from the implementation in standard 3D. -# Only difference: defined `u_transformed` (equivalent of `u`) as AbstractArray. -function prolong2mortars!(cache, u_transformed::AbstractArray, - mesh::TreeMesh{3}, - equations_parabolic::AbstractEquationsParabolic, - mortar_l2::LobattoLegendreMortarL2, - surface_integral, dg::DGSEM) - # temporary buffer for projections - @unpack fstar_tmp1_threaded = cache # CARE: Not sure if I have to do something with this (not present in 2D) - - @threaded for mortar in eachmortar(dg, cache) - fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()] - - lower_left_element = cache.mortars.neighbor_ids[1, mortar] - lower_right_element = cache.mortars.neighbor_ids[2, mortar] - upper_left_element = cache.mortars.neighbor_ids[3, mortar] - upper_right_element = cache.mortars.neighbor_ids[4, mortar] - large_element = cache.mortars.neighbor_ids[5, mortar] - - # Copy solution small to small - if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - for k in eachnode(dg), j in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, j, k, mortar] = u_transformed[v, - 1, - j, - k, - upper_left_element] - cache.mortars.u_upper_right[2, v, j, k, mortar] = u_transformed[v, - 1, - j, - k, - upper_right_element] - cache.mortars.u_lower_left[2, v, j, k, mortar] = u_transformed[v, - 1, - j, - k, - lower_left_element] - cache.mortars.u_lower_right[2, v, j, k, mortar] = u_transformed[v, - 1, - j, - k, - lower_right_element] - end - end - elseif cache.mortars.orientations[mortar] == 2 - # L2 mortars in y-direction - for k in eachnode(dg), i in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, k, mortar] = u_transformed[v, - i, - 1, - k, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, k, mortar] = u_transformed[v, - i, - 1, - k, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, k, mortar] = u_transformed[v, - i, - 1, - k, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, k, mortar] = u_transformed[v, - i, - 1, - k, - lower_right_element] - end - end - else # orientations[mortar] == 3 - # L2 mortars in z-direction - for j in eachnode(dg), i in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[2, v, i, j, mortar] = u_transformed[v, - i, - j, - 1, - upper_left_element] - cache.mortars.u_upper_right[2, v, i, j, mortar] = u_transformed[v, - i, - j, - 1, - upper_right_element] - cache.mortars.u_lower_left[2, v, i, j, mortar] = u_transformed[v, - i, - j, - 1, - lower_left_element] - cache.mortars.u_lower_right[2, v, i, j, mortar] = u_transformed[v, - i, - j, - 1, - lower_right_element] - end - end - end - else # large_sides[mortar] == 2 -> small elements on left side - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - for k in eachnode(dg), j in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, - k, - upper_left_element] - cache.mortars.u_upper_right[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, - k, - upper_right_element] - cache.mortars.u_lower_left[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, - k, - lower_left_element] - cache.mortars.u_lower_right[1, v, j, k, mortar] = u_transformed[v, - nnodes(dg), - j, - k, - lower_right_element] - end - end - elseif cache.mortars.orientations[mortar] == 2 - # L2 mortars in y-direction - for k in eachnode(dg), i in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, k, mortar] = u_transformed[v, - i, - nnodes(dg), - k, - upper_left_element] - cache.mortars.u_upper_right[1, v, i, k, mortar] = u_transformed[v, - i, - nnodes(dg), - k, - upper_right_element] - cache.mortars.u_lower_left[1, v, i, k, mortar] = u_transformed[v, - i, - nnodes(dg), - k, - lower_left_element] - cache.mortars.u_lower_right[1, v, i, k, mortar] = u_transformed[v, - i, - nnodes(dg), - k, - lower_right_element] - end - end - else # if cache.mortars.orientations[mortar] == 3 - # L2 mortars in z-direction - for j in eachnode(dg), i in eachnode(dg) - for v in eachvariable(equations_parabolic) - cache.mortars.u_upper_left[1, v, i, j, mortar] = u_transformed[v, - i, - j, - nnodes(dg), - upper_left_element] - cache.mortars.u_upper_right[1, v, i, j, mortar] = u_transformed[v, - i, - j, - nnodes(dg), - upper_right_element] - cache.mortars.u_lower_left[1, v, i, j, mortar] = u_transformed[v, - i, - j, - nnodes(dg), - lower_left_element] - cache.mortars.u_lower_right[1, v, i, j, mortar] = u_transformed[v, - i, - j, - nnodes(dg), - lower_right_element] - end - end - end - end - - # Interpolate large element face data to small interface locations - if cache.mortars.large_sides[mortar] == 1 # -> large element on left side - leftright = 1 - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - u_large = view(u_transformed, :, nnodes(dg), :, :, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large, fstar_tmp1) - elseif cache.mortars.orientations[mortar] == 2 - # L2 mortars in y-direction - u_large = view(u_transformed, :, :, nnodes(dg), :, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large, fstar_tmp1) - else # cache.mortars.orientations[mortar] == 3 - # L2 mortars in z-direction - u_large = view(u_transformed, :, :, :, nnodes(dg), large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large, fstar_tmp1) - end - else # large_sides[mortar] == 2 -> large element on right side - leftright = 2 - if cache.mortars.orientations[mortar] == 1 - # L2 mortars in x-direction - u_large = view(u_transformed, :, 1, :, :, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large, fstar_tmp1) - elseif cache.mortars.orientations[mortar] == 2 - # L2 mortars in y-direction - u_large = view(u_transformed, :, :, 1, :, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large, fstar_tmp1) - else # cache.mortars.orientations[mortar] == 3 - # L2 mortars in z-direction - u_large = view(u_transformed, :, :, :, 1, large_element) - element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright, - mortar, u_large, fstar_tmp1) - end - end - end - - return nothing -end - # NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms. # Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for # hyperbolic terms with conserved terms only, i.e., no nonconservative terms. From e3ad723dc9743e6434e2b82aec463e9fae25d9dc Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Wed, 19 Jul 2023 15:13:24 +0200 Subject: [PATCH 10/74] Back to original version --- src/solvers/dgsem_tree/dg_2d.jl | 2 +- src/solvers/dgsem_tree/dg_3d.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl index aeb18da2066..d3227710686 100644 --- a/src/solvers/dgsem_tree/dg_2d.jl +++ b/src/solvers/dgsem_tree/dg_2d.jl @@ -782,7 +782,7 @@ function calc_boundary_flux_by_direction!(surface_flux_values::AbstractArray{<:A return nothing end -function prolong2mortars!(cache, u::AbstractArray, +function prolong2mortars!(cache, u, mesh::TreeMesh{2}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) diff --git a/src/solvers/dgsem_tree/dg_3d.jl b/src/solvers/dgsem_tree/dg_3d.jl index c1df64b80c6..95abb2595e5 100644 --- a/src/solvers/dgsem_tree/dg_3d.jl +++ b/src/solvers/dgsem_tree/dg_3d.jl @@ -834,7 +834,7 @@ function calc_boundary_flux_by_direction!(surface_flux_values::AbstractArray{<:A return nothing end -function prolong2mortars!(cache, u::AbstractArray, +function prolong2mortars!(cache, u, mesh::TreeMesh{3}, equations, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) From 7e732c795c2610e64a57b14ccf4d6e06f1cca508 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 11:15:20 +0200 Subject: [PATCH 11/74] Add tests for L2 mortars for hyp-para --- test/test_parabolic_2d.jl | 44 +++++++++++++++++++++++++++++++++++++++ test/test_parabolic_3d.jl | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 471b976e990..49540dbeea2 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -125,6 +125,29 @@ isdir(outdir) && rm(outdir, recursive=true) ) end + @trixi_testset "TreeMesh2D: elixir_advection_diffusion.jl (Refined mesh)" begin + @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_diffusion.jl"), + tspan=(0.0, 0.0)) + LLID = Trixi.local_leaf_cells(mesh.tree) + num_leafs = length(LLID) + @assert num_leafs % 8 == 0 + Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/8)]) + tspan=(0.0, 1.5) + semi = SemidiscretizationHyperbolicParabolic(mesh, + (equations, equations_parabolic), + initial_condition, solver; + boundary_conditions=(boundary_conditions, + boundary_conditions_parabolic)) + ode = semidiscretize(semi, tspan) + analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, + ode_default_options()..., callback=callbacks) + ac_sol = analysis_callback(sol) + @test ac_sol.l2[1] ≈ 1.67452550744728e-6 + @test ac_sol.linf[1] ≈ 7.905059166368744e-6 + end + @trixi_testset "TreeMesh2D: elixir_advection_diffusion_nonperiodic.jl" begin @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_diffusion_nonperiodic.jl"), initial_refinement_level = 2, tspan=(0.0, 0.1), @@ -176,6 +199,27 @@ isdir(outdir) && rm(outdir, recursive=true) ) end + @trixi_testset "TreeMesh2D: elixir_navierstokes_convergence.jl (Refined mesh)" begin + @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_navierstokes_convergence.jl"), + tspan=(0.0, 0.0), initial_refinement_level=3) + LLID = Trixi.local_leaf_cells(mesh.tree) + num_leafs = length(LLID) + @assert num_leafs % 4 == 0 + Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/4)]) + tspan=(0.0, 0.5) + semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; + boundary_conditions=(boundary_conditions, boundary_conditions_parabolic), + source_terms=source_terms_navier_stokes_convergence_test) + ode = semidiscretize(semi, tspan) + analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, + ode_default_options()..., callback=callbacks) + ac_sol = analysis_callback(sol) + @test ac_sol.l2 ≈ [ 0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537] + @test ac_sol.linf ≈ [ 0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271] + end + @trixi_testset "TreeMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_navierstokes_lid_driven_cavity.jl"), initial_refinement_level = 2, tspan=(0.0, 0.5), diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 1ae5eed44ae..22bec203de4 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -78,6 +78,27 @@ isdir(outdir) && rm(outdir, recursive=true) ) end + @trixi_testset "TreeMesh3D: elixir_navierstokes_convergence.jl (Refined mesh)" begin + @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_convergence.jl"), + tspan=(0.0, 0.0)) + LLID = Trixi.local_leaf_cells(mesh.tree) + num_leafs = length(LLID) + @assert num_leafs % 16 == 0 + Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/16)]) + tspan=(0.0, 1.0) + semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; + boundary_conditions=(boundary_conditions, boundary_conditions_parabolic), + source_terms=source_terms_navier_stokes_convergence_test) + ode = semidiscretize(semi, tspan) + analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, + ode_default_options()..., callback=callbacks) + ac_sol = analysis_callback(sol) + @test ac_sol.l2 ≈ [0.0003991794175622818; 0.0008853745163670504; 0.0010658655552066817; 0.0008785559918324284; 0.001403163458422815] + @test ac_sol.linf ≈ [0.0035306410538458177; 0.01505692306169911; 0.008862444161110705; 0.015065647972869856; 0.030402714743065218] + end + @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl" begin @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"), initial_refinement_level = 2, tspan=(0.0, 0.25), @@ -86,6 +107,26 @@ isdir(outdir) && rm(outdir, recursive=true) ) end + @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Refined mesh)" begin + @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"), + tspan=(0.0, 0.0)) + LLID = Trixi.local_leaf_cells(mesh.tree) + num_leafs = length(LLID) + @assert num_leafs % 32 == 0 + Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/32)]) + tspan=(0.0, 10.0) + semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), + initial_condition, solver) + ode = semidiscretize(semi, tspan) + analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, + ode_default_options()..., callback=callbacks) + ac_sol = analysis_callback(sol) + @test ac_sol.l2 ≈ [0.001366611840906741; 0.23135816439703072; 0.23081642735389143; 0.17460247710200574; 0.2812199821469314] + @test ac_sol.linf ≈ [ 0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] + end + end # Clean up afterwards: delete Trixi.jl output directory From 1deb7d78af763f7309b59e4fee34bd79445ad70a Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 11:16:55 +0200 Subject: [PATCH 12/74] Working branch for amr for parabolic --- .../elixir_navierstokes_convergence.jl | 42 ++++- src/callbacks_step/amr.jl | 173 ++++++++++++++++++ src/callbacks_step/amr_dg2d.jl | 134 ++++++++++++++ ...semidiscretization_hyperbolic_parabolic.jl | 19 +- src/solvers/dg.jl | 2 + src/solvers/dgsem_tree/containers.jl | 8 +- src/time_integration/methods_2N.jl | 1 + 7 files changed, 370 insertions(+), 9 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 36a9f52e39d..7f99ccbabc3 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -1,4 +1,4 @@ -using OrdinaryDiffEq +using OrdinaryDiffEq, Plots using Trixi ############################################################################### @@ -23,7 +23,12 @@ mesh = TreeMesh(coordinates_min, coordinates_max, initial_refinement_level=4, periodicity=(true, false), n_cells_max=30_000) # set maximum capacity of tree data structure - +#= +LLID = Trixi.local_leaf_cells(mesh.tree) +num_leafs = length(LLID) +@assert num_leafs % 4 == 0 +Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/4)]) +=# # Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D` # since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`) # and by the initial condition (which passes in `CompressibleEulerEquations2D`). @@ -190,24 +195,53 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol boundary_conditions=(boundary_conditions, boundary_conditions_parabolic), source_terms=source_terms_navier_stokes_convergence_test) + +amr_indicator = IndicatorLöhner(semi, variable=Trixi.density) + +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level=0, + med_level=3, med_threshold=0.05, + max_level=5, max_threshold=0.1) + +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=false) + + ############################################################################### # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` tspan = (0.0, 0.5) -ode = semidiscretize(semi, tspan) +split_form = true +ode = semidiscretize(semi, tspan; split_form=split_form) summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=10) analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) ############################################################################### # run the simulation time_int_tol = 1e-8 + + sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, ode_default_options()..., callback=callbacks) -summary_callback() # print the timer summary +#= +sol = solve(ode, RDPK3SpFSAL49(); adaptive=false, dt = 5e-4, + ode_default_options()..., callback=callbacks) +=# +#= +ode_algorithm = Trixi.CarpenterKennedy2N54() +sol = Trixi.solve(ode, ode_algorithm, + dt=1e-5, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); += # +summary_callback() # print the timer summary +plot(sol) +=# \ No newline at end of file diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index bef49b4c482..dd134238bfb 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -175,7 +175,11 @@ function (amr_callback::AMRCallback)(integrator; kwargs...) has_changed = amr_callback(u_ode, semi, integrator.t, integrator.iter; kwargs...) if has_changed + println("Target length u_ode: ", length(u_ode)) + println("f.cache pre-change: ", length(integrator.f.cache)) resize!(integrator, length(u_ode)) + println("f.cache post-change: ", length(integrator.f.cache)) + u_modified!(integrator, true) end end @@ -192,6 +196,15 @@ end amr_callback(u_ode, mesh_equations_solver_cache(semi)..., semi, t, iter; kwargs...) end +@inline function (amr_callback::AMRCallback)(u_ode::AbstractVector, + semi::SemidiscretizationHyperbolicParabolic, + t, iter; + kwargs...) + # Note that we don't `wrap_array` the vector `u_ode` to be able to `resize!` + # it when doing AMR while still dispatching on the `mesh` etc. + amr_callback(u_ode, mesh_equations_solver_cache(semi)..., semi.cache_parabolic, semi, t, iter; kwargs...) +end + # `passive_args` is currently used for Euler with self-gravity to adapt the gravity solver # passively without querying its indicator, based on the assumption that both solvers use # the same mesh. That's a hack and should be improved in the future once we have more examples @@ -346,6 +359,166 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, return has_changed end +# `passive_args` is currently used for Euler with self-gravity to adapt the gravity solver +# passively without querying its indicator, based on the assumption that both solvers use +# the same mesh. That's a hack and should be improved in the future once we have more examples +# and a better understanding of such a coupling. +# `passive_args` is expected to be an iterable of `Tuple`s of the form +# `(p_u_ode, p_mesh, p_equations, p_dg, p_cache)`. +function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, + equations, dg::DG, + cache, cache_parabolic, + semi::SemidiscretizationHyperbolicParabolic, + t, iter; + only_refine = false, only_coarsen = false, + passive_args = ()) + @unpack controller, adaptor = amr_callback + + u = wrap_array(u_ode, mesh, equations, dg, cache) + # TODO: Keep indicator based on hyperbolic variables? + lambda = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache, + t = t, iter = iter) + + if mpi_isparallel() + # Collect lambda for all elements + lambda_global = Vector{eltype(lambda)}(undef, nelementsglobal(dg, cache)) + # Use parent because n_elements_by_rank is an OffsetArray + recvbuf = MPI.VBuffer(lambda_global, parent(cache.mpi_cache.n_elements_by_rank)) + MPI.Allgatherv!(lambda, recvbuf, mpi_comm()) + lambda = lambda_global + end + + leaf_cell_ids = leaf_cells(mesh.tree) + @boundscheck begin + @assert axes(lambda)==axes(leaf_cell_ids) ("Indicator (axes = $(axes(lambda))) and leaf cell (axes = $(axes(leaf_cell_ids))) arrays have different axes") + end + + @unpack to_refine, to_coarsen = amr_callback.amr_cache + empty!(to_refine) + empty!(to_coarsen) + for element in 1:length(lambda) + controller_value = lambda[element] + if controller_value > 0 + push!(to_refine, leaf_cell_ids[element]) + elseif controller_value < 0 + push!(to_coarsen, leaf_cell_ids[element]) + end + end + + @trixi_timeit timer() "refine" if !only_coarsen && !isempty(to_refine) + # refine mesh + refined_original_cells = @trixi_timeit timer() "mesh" refine!(mesh.tree, + to_refine) + + # Find all indices of elements whose cell ids are in refined_original_cells + # NOTE: This assumes same indices for hyperbolic and parabolic part! + elements_to_refine = findall(in(refined_original_cells), + cache.elements.cell_ids) + + # refine solver + @trixi_timeit timer() "solver" refine!(u_ode, adaptor, mesh, equations, dg, + cache, cache_parabolic, elements_to_refine) + for (p_u_ode, p_mesh, p_equations, p_dg, p_cache) in passive_args + @trixi_timeit timer() "passive solver" refine!(p_u_ode, adaptor, p_mesh, + p_equations, p_dg, p_cache, + elements_to_refine) + end + else + # If there is nothing to refine, create empty array for later use + refined_original_cells = Int[] + end + + @trixi_timeit timer() "coarsen" if !only_refine && !isempty(to_coarsen) + # Since the cells may have been shifted due to refinement, first we need to + # translate the old cell ids to the new cell ids + if !isempty(to_coarsen) + to_coarsen = original2refined(to_coarsen, refined_original_cells, mesh) + end + + # Next, determine the parent cells from which the fine cells are to be + # removed, since these are needed for the coarsen! function. However, since + # we only want to coarsen if *all* child cells are marked for coarsening, + # we count the coarsening indicators for each parent cell and only coarsen + # if all children are marked as such (i.e., where the count is 2^ndims). At + # the same time, check if a cell is marked for coarsening even though it is + # *not* a leaf cell -> this can only happen if it was refined due to 2:1 + # smoothing during the preceding refinement operation. + parents_to_coarsen = zeros(Int, length(mesh.tree)) + for cell_id in to_coarsen + # If cell has no parent, it cannot be coarsened + if !has_parent(mesh.tree, cell_id) + continue + end + + # If cell is not leaf (anymore), it cannot be coarsened + if !is_leaf(mesh.tree, cell_id) + continue + end + + # Increase count for parent cell + parent_id = mesh.tree.parent_ids[cell_id] + parents_to_coarsen[parent_id] += 1 + end + + # Extract only those parent cells for which all children should be coarsened + to_coarsen = collect(1:length(parents_to_coarsen))[parents_to_coarsen .== 2^ndims(mesh)] + + # Finally, coarsen mesh + coarsened_original_cells = @trixi_timeit timer() "mesh" coarsen!(mesh.tree, + to_coarsen) + + # Convert coarsened parent cell ids to the list of child cell ids that have + # been removed, since this is the information that is expected by the solver + removed_child_cells = zeros(Int, + n_children_per_cell(mesh.tree) * + length(coarsened_original_cells)) + for (index, coarse_cell_id) in enumerate(coarsened_original_cells) + for child in 1:n_children_per_cell(mesh.tree) + removed_child_cells[n_children_per_cell(mesh.tree) * (index - 1) + child] = coarse_cell_id + + child + end + end + + # Find all indices of elements whose cell ids are in removed_child_cells + # NOTE: This assumes same indices for hyperbolic and parabolic part! + elements_to_remove = findall(in(removed_child_cells), cache.elements.cell_ids) + + # coarsen solver + @trixi_timeit timer() "solver" coarsen!(u_ode, adaptor, mesh, equations, dg, + cache, cache_parabolic, elements_to_remove) + + for (p_u_ode, p_mesh, p_equations, p_dg, p_cache) in passive_args + @trixi_timeit timer() "passive solver" coarsen!(p_u_ode, adaptor, p_mesh, + p_equations, p_dg, p_cache, + elements_to_remove) + end + else + # If there is nothing to coarsen, create empty array for later use + coarsened_original_cells = Int[] + end + + # Store whether there were any cells coarsened or refined + has_changed = !isempty(refined_original_cells) || !isempty(coarsened_original_cells) + if has_changed # TODO: Taal decide, where shall we set this? + # don't set it to has_changed since there can be changes from earlier calls + mesh.unsaved_changes = true + end + + # Dynamically balance computational load by first repartitioning the mesh and then redistributing the cells/elements + if has_changed && mpi_isparallel() && amr_callback.dynamic_load_balancing + @trixi_timeit timer() "dynamic load balancing" begin + old_mpi_ranks_per_cell = copy(mesh.tree.mpi_ranks) + + partition!(mesh) + + rebalance_solver!(u_ode, mesh, equations, dg, cache, old_mpi_ranks_per_cell) + end + end + + # Return true if there were any cells coarsened or refined, otherwise false + return has_changed +end + # Copy controller values to quad user data storage, will be called below function copy_to_quad_iter_volume(info, user_data) info_pw = PointerWrapper(info) diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 400d16347d5..72eb06d8108 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -136,6 +136,67 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estM return nothing end +# Refine elements in the DG solver based on a list of cell_ids that should be refined +function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estMesh{2}}, + equations, dg::DGSEM, cache, cache_parabolic, elements_to_refine) + # Return early if there is nothing to do + if isempty(elements_to_refine) + if mpi_isparallel() + # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do + # locally (there still might be other MPI ranks that have refined elements) + reinitialize_containers!(mesh, equations, dg, cache) + end + return + end + + # Determine for each existing element whether it needs to be refined + needs_refinement = falses(nelements(dg, cache)) + needs_refinement[elements_to_refine] .= true + + # Retain current solution data + old_n_elements = nelements(dg, cache) + old_u_ode = copy(u_ode) + GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + reinitialize_containers!(mesh, equations, dg, cache) + reinitialize_containers!(mesh, equations, dg, cache_parabolic) + + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + # Loop over all elements in old container and either copy them or refine them + element_id = 1 + for old_element_id in 1:old_n_elements + if needs_refinement[old_element_id] + # Refine element and store solution directly in new data structure + refine_element!(u, element_id, old_u, old_element_id, + adaptor, equations, dg) + element_id += 2^ndims(mesh) + else + # Copy old element data to new element container + @views u[:, .., element_id] .= old_u[:, .., old_element_id] + element_id += 1 + end + end + # If everything is correct, we should have processed all elements. + # Depending on whether the last element processed above had to be refined or not, + # the counter `element_id` can have two different values at the end. + @assert element_id == + nelements(dg, cache) + + 1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + end # GC.@preserve old_u_ode + + # Sanity check + if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 && + !mpi_isparallel() + @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + end + + return nothing +end + # TODO: Taal compare performance of different implementations # Refine solution data u for an element, using L2 projection (interpolation) function refine_element!(u::AbstractArray{<:Any, 4}, element_id, @@ -275,6 +336,79 @@ function coarsen!(u_ode::AbstractVector, adaptor, return nothing end +# Coarsen elements in the DG solver based on a list of cell_ids that should be removed +function coarsen!(u_ode::AbstractVector, adaptor, + mesh::Union{TreeMesh{2}, P4estMesh{2}}, + equations, dg::DGSEM, cache, cache_parabolic, + elements_to_remove) + # Return early if there is nothing to do + if isempty(elements_to_remove) + if mpi_isparallel() + # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do + # locally (there still might be other MPI ranks that have coarsened elements) + reinitialize_containers!(mesh, equations, dg, cache) + end + return + end + + # Determine for each old element whether it needs to be removed + to_be_removed = falses(nelements(dg, cache)) + to_be_removed[elements_to_remove] .= true + + # Retain current solution data + old_n_elements = nelements(dg, cache) + old_u_ode = copy(u_ode) + GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + reinitialize_containers!(mesh, equations, dg, cache) + reinitialize_containers!(mesh, equations, dg, cache_parabolic) + + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + # Loop over all elements in old container and either copy them or coarsen them + skip = 0 + element_id = 1 + for old_element_id in 1:old_n_elements + # If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements + if skip > 0 + skip -= 1 + continue + end + + if to_be_removed[old_element_id] + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted + @assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order" + + # Coarsen elements and store solution directly in new data structure + coarsen_elements!(u, element_id, old_u, old_element_id, + adaptor, equations, dg) + element_id += 1 + skip = 2^ndims(mesh) - 1 + else + # Copy old element data to new element container + @views u[:, .., element_id] .= old_u[:, .., old_element_id] + element_id += 1 + end + end + # If everything is correct, we should have processed all elements. + @assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + @assert element_id==nelements(dg, cache_parabolic) + 1 "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" + end # GC.@preserve old_u_ode + + # Sanity check + if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 && + !mpi_isparallel() + @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + end + + return nothing +end + # TODO: Taal compare performance of different implementations # Coarsen solution data u for four elements, using L2 projection function coarsen_elements!(u::AbstractArray{<:Any, 4}, element_id, diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index f54bc744164..204cbe1df74 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -274,7 +274,7 @@ The parabolic right-hand side is the first function of the split ODE problem and will be used by default by the implicit part of IMEX methods from the SciML ecosystem. """ -function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan) +function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan; split_form::Bool=true) u0_ode = compute_coefficients(first(tspan), semi) # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using # mpi_isparallel() && MPI.Barrier(mpi_comm()) @@ -283,7 +283,12 @@ function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan) # Note that the IMEX time integration methods of OrdinaryDiffEq.jl treat the # first function implicitly and the second one explicitly. Thus, we pass the # stiffer parabolic function first. - return SplitODEProblem{iip}(rhs_parabolic!, rhs!, u0_ode, tspan, semi) + if split_form + return SplitODEProblem{iip}(rhs_parabolic!, rhs!, u0_ode, tspan, semi) + else + specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + return ODEProblem{iip, specialize}(rhs_hyperbolic_parabolic!, u0_ode, tspan, semi) + end end function rhs!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) @@ -305,6 +310,7 @@ end function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) @unpack mesh, equations_parabolic, initial_condition, boundary_conditions_parabolic, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi + #println("RHS para ODE solver: ", length(du_ode), " ", length(u_ode)) u = wrap_array(u_ode, mesh, equations_parabolic, solver, cache_parabolic) du = wrap_array(du_ode, mesh, equations_parabolic, solver, cache_parabolic) @@ -322,4 +328,13 @@ function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabol return nothing end + +function rhs_hyperbolic_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) + @trixi_timeit timer() "hyperbolic-parabolic rhs!" begin + du_ode_hyp = similar(du_ode) # TODO: Avoid allocations, make member variable of something? + rhs!(du_ode_hyp, u_ode, semi, t) + rhs_parabolic!(du_ode, u_ode, semi, t) + du_ode .+= du_ode_hyp + end +end end # @muladd diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 2536cfe0bf2..6215f30debd 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -535,6 +535,8 @@ end @inline function wrap_array(u_ode::AbstractVector, mesh::AbstractMesh, equations, dg::DGSEM, cache) + + #println(length(u_ode), " ", nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache), "\n") @boundscheck begin @assert length(u_ode) == nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache) diff --git a/src/solvers/dgsem_tree/containers.jl b/src/solvers/dgsem_tree/containers.jl index bba8b83b23a..3f05daf81d8 100644 --- a/src/solvers/dgsem_tree/containers.jl +++ b/src/solvers/dgsem_tree/containers.jl @@ -28,9 +28,11 @@ function reinitialize_containers!(mesh::TreeMesh, equations, dg::DGSEM, cache) init_boundaries!(boundaries, elements, mesh) # re-initialize mortars container - @unpack mortars = cache - resize!(mortars, count_required_mortars(mesh, leaf_cell_ids)) - init_mortars!(mortars, elements, mesh) + if hasproperty(cache, :mortars) # cache_parabolic does not carry mortars + @unpack mortars = cache + resize!(mortars, count_required_mortars(mesh, leaf_cell_ids)) + init_mortars!(mortars, elements, mesh) + end if mpi_isparallel() # re-initialize mpi_interfaces container diff --git a/src/time_integration/methods_2N.jl b/src/time_integration/methods_2N.jl index 557e8272128..6de507455fa 100644 --- a/src/time_integration/methods_2N.jl +++ b/src/time_integration/methods_2N.jl @@ -155,6 +155,7 @@ function solve!(integrator::SimpleIntegrator2N) integrator.u_tmp .= 0 for stage in eachindex(alg.c) t_stage = integrator.t + integrator.dt * alg.c[stage] + println("Lengths ODE solver: ", length(integrator.du), " ", length(integrator.u)) integrator.f(integrator.du, integrator.u, prob.p, t_stage) a_stage = alg.a[stage] From f5b1e62e1de8d0da6d737f49524440c64fd0d0c6 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 11:26:12 +0200 Subject: [PATCH 13/74] remove whitespaces --- test/test_parabolic_2d.jl | 4 ++-- test/test_parabolic_3d.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 49540dbeea2..ea0bb3bf404 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -216,8 +216,8 @@ isdir(outdir) && rm(outdir, recursive=true) sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, ode_default_options()..., callback=callbacks) ac_sol = analysis_callback(sol) - @test ac_sol.l2 ≈ [ 0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537] - @test ac_sol.linf ≈ [ 0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271] + @test ac_sol.l2 ≈ [0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537] + @test ac_sol.linf ≈ [0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271] end @trixi_testset "TreeMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 22bec203de4..8177afa6fbd 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -124,7 +124,7 @@ isdir(outdir) && rm(outdir, recursive=true) ode_default_options()..., callback=callbacks) ac_sol = analysis_callback(sol) @test ac_sol.l2 ≈ [0.001366611840906741; 0.23135816439703072; 0.23081642735389143; 0.17460247710200574; 0.2812199821469314] - @test ac_sol.linf ≈ [ 0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] + @test ac_sol.linf ≈ [0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] end end From 2111a9cabe37cdd45a0908e66826e4764436db5d Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 11:27:12 +0200 Subject: [PATCH 14/74] remove whitespace --- test/test_parabolic_2d.jl | 4 ++-- test/test_parabolic_3d.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl index 49540dbeea2..ea0bb3bf404 100644 --- a/test/test_parabolic_2d.jl +++ b/test/test_parabolic_2d.jl @@ -216,8 +216,8 @@ isdir(outdir) && rm(outdir, recursive=true) sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, ode_default_options()..., callback=callbacks) ac_sol = analysis_callback(sol) - @test ac_sol.l2 ≈ [ 0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537] - @test ac_sol.linf ≈ [ 0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271] + @test ac_sol.l2 ≈ [0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537] + @test ac_sol.linf ≈ [0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271] end @trixi_testset "TreeMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 22bec203de4..8177afa6fbd 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -124,7 +124,7 @@ isdir(outdir) && rm(outdir, recursive=true) ode_default_options()..., callback=callbacks) ac_sol = analysis_callback(sol) @test ac_sol.l2 ≈ [0.001366611840906741; 0.23135816439703072; 0.23081642735389143; 0.17460247710200574; 0.2812199821469314] - @test ac_sol.linf ≈ [ 0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] + @test ac_sol.linf ≈ [0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] end end From caf95295f7bbbf0988c1fff16e81c842a1c85678 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 11:47:12 +0200 Subject: [PATCH 15/74] Remove some println()s --- src/callbacks_step/amr.jl | 4 ---- .../semidiscretization_hyperbolic_parabolic.jl | 5 +++-- src/solvers/dg.jl | 3 +-- src/time_integration/methods_2N.jl | 1 - 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index dd134238bfb..78be24e62a5 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -175,11 +175,7 @@ function (amr_callback::AMRCallback)(integrator; kwargs...) has_changed = amr_callback(u_ode, semi, integrator.t, integrator.iter; kwargs...) if has_changed - println("Target length u_ode: ", length(u_ode)) - println("f.cache pre-change: ", length(integrator.f.cache)) resize!(integrator, length(u_ode)) - println("f.cache post-change: ", length(integrator.f.cache)) - u_modified!(integrator, true) end end diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index 204cbe1df74..8acebf42040 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -310,7 +310,6 @@ end function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) @unpack mesh, equations_parabolic, initial_condition, boundary_conditions_parabolic, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi - #println("RHS para ODE solver: ", length(du_ode), " ", length(u_ode)) u = wrap_array(u_ode, mesh, equations_parabolic, solver, cache_parabolic) du = wrap_array(du_ode, mesh, equations_parabolic, solver, cache_parabolic) @@ -331,7 +330,9 @@ end function rhs_hyperbolic_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) @trixi_timeit timer() "hyperbolic-parabolic rhs!" begin - du_ode_hyp = similar(du_ode) # TODO: Avoid allocations, make member variable of something? + # TODO: Avoid allocations, make member variable of something? + # -> Could reside in integrator, then pass in similar to indices of PERK + du_ode_hyp = similar(du_ode) rhs!(du_ode_hyp, u_ode, semi, t) rhs_parabolic!(du_ode, u_ode, semi, t) du_ode .+= du_ode_hyp diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 6215f30debd..6eca6cacfe8 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -535,8 +535,7 @@ end @inline function wrap_array(u_ode::AbstractVector, mesh::AbstractMesh, equations, dg::DGSEM, cache) - - #println(length(u_ode), " ", nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache), "\n") + @boundscheck begin @assert length(u_ode) == nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache) diff --git a/src/time_integration/methods_2N.jl b/src/time_integration/methods_2N.jl index 6de507455fa..557e8272128 100644 --- a/src/time_integration/methods_2N.jl +++ b/src/time_integration/methods_2N.jl @@ -155,7 +155,6 @@ function solve!(integrator::SimpleIntegrator2N) integrator.u_tmp .= 0 for stage in eachindex(alg.c) t_stage = integrator.t + integrator.dt * alg.c[stage] - println("Lengths ODE solver: ", length(integrator.du), " ", length(integrator.u)) integrator.f(integrator.du, integrator.u, prob.p, t_stage) a_stage = alg.a[stage] From eaf083887677723630a7af11414cacd9be8c4475 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 13:55:51 +0200 Subject: [PATCH 16/74] add to project --- Project.toml | 4 +++- examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 4c187ed38ff..ba8ba49328b 100644 --- a/Project.toml +++ b/Project.toml @@ -19,7 +19,9 @@ MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" Octavian = "6fd5a793-0b7e-452c-907f-f8bfe9c57db4" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" P4est = "7d669430-f675-4ae7-b43e-fab78ec5a902" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -59,8 +61,8 @@ HDF5 = "0.14, 0.15, 0.16" IfElse = "0.1" LinearMaps = "2.7, 3.0" LoopVectorization = "0.12.118" -Makie = "0.19" MPI = "0.20" +Makie = "0.19" MuladdMacro = "0.2.2" Octavian = "0.3.5" OffsetArrays = "1.3" diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 7f99ccbabc3..b8820871bf4 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -213,15 +213,15 @@ amr_callback = AMRCallback(semi, amr_controller, # Create ODE problem with time span `tspan` tspan = (0.0, 0.5) -split_form = true +split_form = false ode = semidiscretize(semi, tspan; split_form=split_form) summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=10) analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) -#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) -callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) +callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) +#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) ############################################################################### # run the simulation From e6969cf16f319926517436fb320ace1358a58a71 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 14:08:15 +0200 Subject: [PATCH 17/74] Use original analysis callback --- test/test_parabolic_3d.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 8177afa6fbd..520e4c1aa32 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -118,7 +118,10 @@ isdir(outdir) && rm(outdir, recursive=true) semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver) ode = semidiscretize(semi, tspan) - analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true, + extra_analysis_integrals=(energy_kinetic, + energy_internal, + enstrophy)) callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, ode_default_options()..., callback=callbacks) From 46ec996a0975d24789d7effe9071e2c532c81fe4 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 20 Jul 2023 16:42:28 +0200 Subject: [PATCH 18/74] Test Taylor-Green with different integrator --- test/test_parabolic_3d.jl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 520e4c1aa32..f4555c00bcc 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -107,6 +107,30 @@ isdir(outdir) && rm(outdir, recursive=true) ) end + @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Refined mesh)" begin + @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"), + tspan=(0.0, 0.0)) + LLID = Trixi.local_leaf_cells(mesh.tree) + num_leafs = length(LLID) + @assert num_leafs % 32 == 0 + Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/32)]) + tspan=(0.0, 10.0) + semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), + initial_condition, solver) + ode = semidiscretize(semi, tspan) + analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true, + extra_analysis_integrals=(energy_kinetic, + energy_internal, + enstrophy)) + callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=5e-3, + save_everystep=false, callback=callbacks); + ac_sol = analysis_callback(sol) + @test ac_sol.l2 ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005] + @test ac_sol.linf ≈ [ 0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645] + end + @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Refined mesh)" begin @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"), tspan=(0.0, 0.0)) From 051b2bfb67cd88fa4dffcb37f99f5ae01f848c1f Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 21 Jul 2023 12:00:28 +0200 Subject: [PATCH 19/74] Remove whitespace --- test/test_parabolic_3d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index f4555c00bcc..c2754afdf02 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -128,7 +128,7 @@ isdir(outdir) && rm(outdir, recursive=true) save_everystep=false, callback=callbacks); ac_sol = analysis_callback(sol) @test ac_sol.l2 ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005] - @test ac_sol.linf ≈ [ 0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645] + @test ac_sol.linf ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645] end @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Refined mesh)" begin From 44918bf2b6026c9b0769f10e5c6e849032bbeb94 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 21 Jul 2023 12:03:18 +0200 Subject: [PATCH 20/74] remove blank line --- src/solvers/dg.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 6eca6cacfe8..85136989bf5 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -534,8 +534,7 @@ function allocate_coefficients(mesh::AbstractMesh, equations, dg::DG, cache) end @inline function wrap_array(u_ode::AbstractVector, mesh::AbstractMesh, equations, - dg::DGSEM, cache) - + dg::DGSEM, cache) @boundscheck begin @assert length(u_ode) == nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache) From 332fb85b3924796e716b2e69169b50689c87ba18 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Sun, 23 Jul 2023 14:05:27 +0200 Subject: [PATCH 21/74] Work in progress --- .../elixir_advection_diffusion_amr.jl | 99 +++++++++ .../elixir_navierstokes_convergence.jl | 25 +-- .../elixir_navierstokes_lid_driven_cavity.jl | 26 ++- src/callbacks_step/amr_dg1d.jl | 190 ++++++++++++++++++ src/callbacks_step/amr_dg2d.jl | 7 + src/callbacks_step/amr_dg3d.jl | 145 +++++++++++++ src/equations/compressible_euler_2d.jl | 6 + src/solvers/dgsem_tree/dg_1d_parabolic.jl | 1 + 8 files changed, 477 insertions(+), 22 deletions(-) create mode 100644 examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl new file mode 100644 index 00000000000..6bdfd6907ec --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl @@ -0,0 +1,99 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the linear advection diffusion equation + +advection_velocity = 0.1 +equations = LinearScalarAdvectionEquation1D(advection_velocity) +diffusivity() = 0.1 +equations_parabolic = LaplaceDiffusion1D(diffusivity(), equations) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = -pi # minimum coordinate +coordinates_max = pi # maximum coordinate + +# Create a uniformly refined mesh with periodic boundaries +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level=4, + n_cells_max=30_000, # set maximum capacity of tree data structure + periodicity=true) + +function x_trans_periodic(x, domain_length = SVector(2*pi), center = SVector(0.0)) + x_normalized = x .- center + x_shifted = x_normalized .% domain_length + x_offset = ((x_shifted .< -0.5*domain_length) - (x_shifted .> 0.5*domain_length)) .* domain_length + return center + x_shifted + x_offset +end + +# Define initial condition +function initial_condition_diffusive_convergence_test(x, t, equation::LinearScalarAdvectionEquation1D) + # Store translated coordinate for easy use of exact solution + x_trans = x_trans_periodic(x - equation.advection_velocity * t) + + nu = diffusivity() + c = 0.0 + A = 1.0 + L = 2 + f = 1/L + omega = 1.0 + scalar = c + A * sin(omega * sum(x_trans)) * exp(-nu * omega^2 * t) + return SVector(scalar) +end +initial_condition = initial_condition_diffusive_convergence_test + +# define periodic boundary conditions everywhere +boundary_conditions = boundary_condition_periodic +boundary_conditions_parabolic = boundary_condition_periodic + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), + initial_condition_diffusive_convergence_test, solver; + boundary_conditions=(boundary_conditions, boundary_conditions_parabolic)) + + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan; split_form = false); + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# The AliveCallback prints short status information in regular intervals +alive_callback = AliveCallback(analysis_interval=100) + +amr_controller = ControllerThreeLevel(semi, + IndicatorMax(semi, variable=first), + base_level=4, + med_level=5, med_threshold=0.1, + max_level=6, max_threshold=0.6) + +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback) +#callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +time_int_tol = 1.0e-10 +time_abs_tol = 1.0e-10 +sol = solve(ode, KenCarp4(autodiff=false), abstol=time_abs_tol, reltol=time_int_tol, + save_everystep=false, callback=callbacks) + +# Print the timer summary +summary_callback() \ No newline at end of file diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index b8820871bf4..3a8a810d0d1 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -199,12 +199,12 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol amr_indicator = IndicatorLöhner(semi, variable=Trixi.density) amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level=0, - med_level=3, med_threshold=0.05, - max_level=5, max_threshold=0.1) + base_level=5, + med_level=6, med_threshold=0.05, + max_level=6, max_threshold=0.1) amr_callback = AMRCallback(semi, amr_controller, - interval=5, + interval=1, adapt_initial_condition=false) @@ -212,13 +212,13 @@ amr_callback = AMRCallback(semi, amr_controller, # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` -tspan = (0.0, 0.5) +tspan = (0.0, 0.1) split_form = false ode = semidiscretize(semi, tspan; split_form=split_form) summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=10) -analysis_interval = 100 +analysis_interval = 5 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) #callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) @@ -232,16 +232,5 @@ time_int_tol = 1e-8 sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, ode_default_options()..., callback=callbacks) -#= -sol = solve(ode, RDPK3SpFSAL49(); adaptive=false, dt = 5e-4, - ode_default_options()..., callback=callbacks) -=# -#= -ode_algorithm = Trixi.CarpenterKennedy2N54() -sol = Trixi.solve(ode, ode_algorithm, - dt=1e-5, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); -= # summary_callback() # print the timer summary -plot(sol) -=# \ No newline at end of file +plot(sol) \ No newline at end of file diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl b/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl index 81e48737e79..ffcb8f453e6 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl @@ -60,13 +60,30 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # Create ODE problem with time span `tspan` tspan = (0.0, 25.0) -ode = semidiscretize(semi, tspan); +ode = semidiscretize(semi, tspan; split_form = false); summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=100) -analysis_interval = 100 +analysis_interval = 10 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) -callbacks = CallbackSet(summary_callback, alive_callback) + +amr_indicator = IndicatorHennemannGassner(semi, + alpha_max=1.0, + alpha_min=0.0001, + alpha_smooth=false, + variable=Trixi.v1) + +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level=4, + med_level =5, med_threshold=0.25, + max_level =6, max_threshold=0.75) + +amr_callback = AMRCallback(semi, amr_controller, + interval=1, + adapt_initial_condition=false) + +callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) +#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) ############################################################################### # run the simulation @@ -75,5 +92,6 @@ time_int_tol = 1e-8 sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, ode_default_options()..., callback=callbacks) summary_callback() # print the timer summary +using Plots - +plot(sol) \ No newline at end of file diff --git a/src/callbacks_step/amr_dg1d.jl b/src/callbacks_step/amr_dg1d.jl index e31a74730ea..98ac2546dc0 100644 --- a/src/callbacks_step/amr_dg1d.jl +++ b/src/callbacks_step/amr_dg1d.jl @@ -76,6 +76,101 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, return nothing end +function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, + equations, dg::DGSEM, cache, cache_parabolic, + elements_to_refine) + # Return early if there is nothing to do + if isempty(elements_to_refine) + return + end + + # Determine for each existing element whether it needs to be refined + needs_refinement = falses(nelements(dg, cache)) + needs_refinement[elements_to_refine] .= true + + # Retain current solution data + old_n_elements = nelements(dg, cache) + old_u_ode = copy(u_ode) + GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + # Get new list of leaf cells + leaf_cell_ids = local_leaf_cells(mesh.tree) + + # re-initialize elements container + @unpack elements = cache + resize!(elements, length(leaf_cell_ids)) + init_elements!(elements, leaf_cell_ids, mesh, dg.basis) + @assert nelements(dg, cache) > old_n_elements + + @unpack elements = cache_parabolic + resize!(elements, length(leaf_cell_ids)) + init_elements!(elements, leaf_cell_ids, mesh, dg.basis) + @assert nelements(dg, cache_parabolic) > old_n_elements + + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + # Need to resize u_transformed, gradients, flux_viscous somehow + @unpack u_transformed = cache_parabolic + u_transformed = wrap_array(u_ode, mesh, equations, dg, cache) + println("amr: ", size(u_transformed)) + + # Loop over all elements in old container and either copy them or refine them + element_id = 1 + for old_element_id in 1:old_n_elements + if needs_refinement[old_element_id] + # Refine element and store solution directly in new data structure + refine_element!(u, element_id, old_u, old_element_id, + adaptor, equations, dg) + element_id += 2^ndims(mesh) + else + # Copy old element data to new element container + @views u[:, .., element_id] .= old_u[:, .., old_element_id] + element_id += 1 + end + end + # If everything is correct, we should have processed all elements. + # Depending on whether the last element processed above had to be refined or not, + # the counter `element_id` can have two different values at the end. + @assert element_id == + nelements(dg, cache) + + 1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + #= + @assert element_id == + nelements(dg, cache_parabolic) + + 1||element_id == nelements(dg, cache_parabolic) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" + =# + end # GC.@preserve old_u_ode + + # re-initialize interfaces container + @unpack interfaces = cache + resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids)) + init_interfaces!(interfaces, elements, mesh) + + @unpack interfaces = cache_parabolic + resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids)) + init_interfaces!(interfaces, elements, mesh) + + # re-initialize boundaries container + @unpack boundaries = cache + resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids)) + init_boundaries!(boundaries, elements, mesh) + + @unpack boundaries = cache_parabolic + resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids)) + init_boundaries!(boundaries, elements, mesh) + + # Sanity check + if isperiodic(mesh.tree) + @assert ninterfaces(interfaces)==1 * nelements(dg, cache) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements") + #@assert ninterfaces(interfaces)==1 * nelements(dg, cache_parabolic) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements") + end + + return nothing +end + # TODO: Taal compare performance of different implementations # Refine solution data u for an element, using L2 projection (interpolation) function refine_element!(u::AbstractArray{<:Any, 3}, element_id, @@ -201,6 +296,101 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, return nothing end +function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, + equations, dg::DGSEM, cache, cache_parabolic, + elements_to_remove) + # Return early if there is nothing to do + if isempty(elements_to_remove) + return + end + + # Determine for each old element whether it needs to be removed + to_be_removed = falses(nelements(dg, cache)) + to_be_removed[elements_to_remove] .= true + + # Retain current solution data + old_n_elements = nelements(dg, cache) + old_u_ode = copy(u_ode) + GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + # Get new list of leaf cells + leaf_cell_ids = local_leaf_cells(mesh.tree) + + # re-initialize elements container + @unpack elements = cache + resize!(elements, length(leaf_cell_ids)) + init_elements!(elements, leaf_cell_ids, mesh, dg.basis) + @assert nelements(dg, cache) < old_n_elements + + @unpack elements = cache_parabolic + resize!(elements, length(leaf_cell_ids)) + init_elements!(elements, leaf_cell_ids, mesh, dg.basis) + @assert nelements(dg, cache_parabolic) < old_n_elements + + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + # Loop over all elements in old container and either copy them or coarsen them + skip = 0 + element_id = 1 + for old_element_id in 1:old_n_elements + # If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements + if skip > 0 + skip -= 1 + continue + end + + if to_be_removed[old_element_id] + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted + @assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order" + + # Coarsen elements and store solution directly in new data structure + coarsen_elements!(u, element_id, old_u, old_element_id, + adaptor, equations, dg) + element_id += 1 + skip = 2^ndims(mesh) - 1 + else + # Copy old element data to new element container + @views u[:, .., element_id] .= old_u[:, .., old_element_id] + element_id += 1 + end + end + # If everything is correct, we should have processed all elements. + @assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + @assert element_id==nelements(dg, cache_parabolic) + 1 "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" + end # GC.@preserve old_u_ode + + # re-initialize interfaces container + @unpack interfaces = cache + resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids)) + init_interfaces!(interfaces, elements, mesh) + + @unpack interfaces = cache_parabolic + resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids)) + init_interfaces!(interfaces, elements, mesh) + + # re-initialize boundaries container + @unpack boundaries = cache + resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids)) + init_boundaries!(boundaries, elements, mesh) + + @unpack boundaries = cache_parabolic + resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids)) + init_boundaries!(boundaries, elements, mesh) + + # Sanity check + if isperiodic(mesh.tree) + @assert ninterfaces(interfaces)==1 * nelements(dg, cache) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements") + @assert ninterfaces(interfaces)==1 * nelements(dg, cache_parabolic) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements") + end + + return nothing +end + # TODO: Taal compare performance of different implementations # Coarsen solution data u for two elements, using L2 projection function coarsen_elements!(u::AbstractArray{<:Any, 3}, element_id, diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 72eb06d8108..c253c8eb2d9 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -186,12 +186,17 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estM @assert element_id == nelements(dg, cache) + 1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + @assert element_id == + nelements(dg, cache_parabolic) + + 1||element_id == nelements(dg, cache_parabolic) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" end # GC.@preserve old_u_ode # Sanity check if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 && !mpi_isparallel() @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + !mpi_isparallel() + @assert ninterfaces(cache_parabolic.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") end return nothing @@ -404,6 +409,8 @@ function coarsen!(u_ode::AbstractVector, adaptor, if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 && !mpi_isparallel() @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + !mpi_isparallel() + @assert ninterfaces(cache_parabolic.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") end return nothing diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index c8abe6fdb05..cfe33ea3bfb 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -69,6 +69,75 @@ function refine!(u_ode::AbstractVector, adaptor, return nothing end +function refine!(u_ode::AbstractVector, adaptor, + mesh::Union{TreeMesh{3}, P4estMesh{3}}, + equations, dg::DGSEM, cache, cache_parabolic, + elements_to_refine) + # Return early if there is nothing to do + if isempty(elements_to_refine) + if mpi_isparallel() + # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do + # locally (there still might be other MPI ranks that have refined elements) + reinitialize_containers!(mesh, equations, dg, cache) + end + return + end + + # Determine for each existing element whether it needs to be refined + needs_refinement = falses(nelements(dg, cache)) + needs_refinement[elements_to_refine] .= true + + # Retain current solution data + old_n_elements = nelements(dg, cache) + old_u_ode = copy(u_ode) + GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + reinitialize_containers!(mesh, equations, dg, cache) + reinitialize_containers!(mesh, equations, dg, cache_parabolic) + + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + # Loop over all elements in old container and either copy them or refine them + u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), + nnodes(dg), nnodes(dg)) + u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), + nnodes(dg), nnodes(dg)) + element_id = 1 + for old_element_id in 1:old_n_elements + if needs_refinement[old_element_id] + # Refine element and store solution directly in new data structure + refine_element!(u, element_id, old_u, old_element_id, + adaptor, equations, dg, u_tmp1, u_tmp2) + element_id += 2^ndims(mesh) + else + # Copy old element data to new element container + @views u[:, .., element_id] .= old_u[:, .., old_element_id] + element_id += 1 + end + end + # If everything is correct, we should have processed all elements. + # Depending on whether the last element processed above had to be refined or not, + # the counter `element_id` can have two different values at the end. + @assert element_id == + nelements(dg, cache) + + 1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + @assert element_id == + nelements(dg, cache_parabolic) + + 1||element_id == nelements(dg, cache_parabolic) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" + end # GC.@preserve old_u_ode + + # Sanity check + if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 + @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + end + + return nothing +end + # TODO: Taal compare performance of different implementations # Refine solution data u for an element, using L2 projection (interpolation) function refine_element!(u::AbstractArray{<:Any, 5}, element_id, @@ -218,6 +287,82 @@ function coarsen!(u_ode::AbstractVector, adaptor, return nothing end +function coarsen!(u_ode::AbstractVector, adaptor, + mesh::Union{TreeMesh{3}, P4estMesh{3}}, + equations, dg::DGSEM, cache, cache_parabolic, + elements_to_remove) + # Return early if there is nothing to do + if isempty(elements_to_remove) + if mpi_isparallel() + # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do + # locally (there still might be other MPI ranks that have coarsened elements) + reinitialize_containers!(mesh, equations, dg, cache) + end + return + end + + # Determine for each old element whether it needs to be removed + to_be_removed = falses(nelements(dg, cache)) + to_be_removed[elements_to_remove] .= true + + # Retain current solution data + old_n_elements = nelements(dg, cache) + old_u_ode = copy(u_ode) + GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed + old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) + + reinitialize_containers!(mesh, equations, dg, cache) + reinitialize_containers!(mesh, equations, dg, cache_parabolic) + + resize!(u_ode, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + u = wrap_array(u_ode, mesh, equations, dg, cache) + + # Loop over all elements in old container and either copy them or coarsen them + u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), + nnodes(dg), nnodes(dg)) + u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), + nnodes(dg), nnodes(dg)) + skip = 0 + element_id = 1 + for old_element_id in 1:old_n_elements + # If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements + if skip > 0 + skip -= 1 + continue + end + + if to_be_removed[old_element_id] + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted + @assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order" + + # Coarsen elements and store solution directly in new data structure + coarsen_elements!(u, element_id, old_u, old_element_id, + adaptor, equations, dg, u_tmp1, u_tmp2) + element_id += 1 + skip = 2^ndims(mesh) - 1 + else + # Copy old element data to new element container + @views u[:, .., element_id] .= old_u[:, .., old_element_id] + element_id += 1 + end + end + # If everything is correct, we should have processed all elements. + @assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" + @assert element_id==nelements(dg, cache_parabolic) + 1 "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" + end # GC.@preserve old_u_ode + + # Sanity check + if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 + @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") + end + + return nothing +end + # TODO: Taal compare performance of different implementations # Coarsen solution data u for four elements, using L2 projection function coarsen_elements!(u::AbstractArray{<:Any, 5}, element_id, diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl index 27b92f41953..b05318c24f7 100644 --- a/src/equations/compressible_euler_2d.jl +++ b/src/equations/compressible_euler_2d.jl @@ -1416,6 +1416,12 @@ end return rho end +@inline function v1(u, equations::CompressibleEulerEquations2D) + rho = u[1] + rho_v1 = u[2] + return rho_v1/rho +end + @inline function pressure(u, equations::CompressibleEulerEquations2D) rho, rho_v1, rho_v2, rho_e = u p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl index c2aa75388c8..7eb6c716001 100644 --- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl @@ -111,6 +111,7 @@ function transform_variables!(u_transformed, u, mesh::TreeMesh{1}, u_node = get_node_vars(u, equations_parabolic, dg, i, element) u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node, equations_parabolic) + println("dg:", size(u_transformed)) set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg, i, element) end From 04199a42fc6bc2105c84da32046fc50039750099 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Sun, 23 Jul 2023 14:06:28 +0200 Subject: [PATCH 22/74] check coverage status --- test/test_parabolic_3d.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index c2754afdf02..87d3f9a1f26 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -151,9 +151,8 @@ isdir(outdir) && rm(outdir, recursive=true) ode_default_options()..., callback=callbacks) ac_sol = analysis_callback(sol) @test ac_sol.l2 ≈ [0.001366611840906741; 0.23135816439703072; 0.23081642735389143; 0.17460247710200574; 0.2812199821469314] - @test ac_sol.linf ≈ [0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] + #@test ac_sol.linf ≈ [0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] end - end # Clean up afterwards: delete Trixi.jl output directory From 290a41fde09c6d5c7afd405b47b6e5b2a1104407 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 09:30:53 +0200 Subject: [PATCH 23/74] Stick to CK2N54 for 3D test --- test/test_parabolic_3d.jl | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index 87d3f9a1f26..93f3c5f5c2b 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -123,6 +123,7 @@ isdir(outdir) && rm(outdir, recursive=true) energy_internal, enstrophy)) callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + # Use CarpenterKennedy2N54 since `RDPK3SpFSAL49` gives slightly different results on different machines sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), dt=5e-3, save_everystep=false, callback=callbacks); @@ -130,29 +131,6 @@ isdir(outdir) && rm(outdir, recursive=true) @test ac_sol.l2 ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005] @test ac_sol.linf ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645] end - - @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Refined mesh)" begin - @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"), - tspan=(0.0, 0.0)) - LLID = Trixi.local_leaf_cells(mesh.tree) - num_leafs = length(LLID) - @assert num_leafs % 32 == 0 - Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/32)]) - tspan=(0.0, 10.0) - semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), - initial_condition, solver) - ode = semidiscretize(semi, tspan) - analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true, - extra_analysis_integrals=(energy_kinetic, - energy_internal, - enstrophy)) - callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) - sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, - ode_default_options()..., callback=callbacks) - ac_sol = analysis_callback(sol) - @test ac_sol.l2 ≈ [0.001366611840906741; 0.23135816439703072; 0.23081642735389143; 0.17460247710200574; 0.2812199821469314] - #@test ac_sol.linf ≈ [0.00693819371819604; 1.0282359522598283; 1.034545315852348; 1.0821049374639153; 1.2669864039948209] - end end # Clean up afterwards: delete Trixi.jl output directory From cea2100d0c5703e10cc61cabb45c2d0d0951741c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 14:44:57 +0200 Subject: [PATCH 24/74] Working version 1D parabolic AMR --- src/callbacks_step/amr_dg1d.jl | 32 ++++++++++++---- src/solvers/dgsem_tree/dg_1d_parabolic.jl | 45 +++++++++++++++++++++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/callbacks_step/amr_dg1d.jl b/src/callbacks_step/amr_dg1d.jl index 98ac2546dc0..3f73e45a4ef 100644 --- a/src/callbacks_step/amr_dg1d.jl +++ b/src/callbacks_step/amr_dg1d.jl @@ -103,7 +103,7 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, init_elements!(elements, leaf_cell_ids, mesh, dg.basis) @assert nelements(dg, cache) > old_n_elements - @unpack elements = cache_parabolic + @unpack elements, cache_viscous = cache_parabolic resize!(elements, length(leaf_cell_ids)) init_elements!(elements, leaf_cell_ids, mesh, dg.basis) @assert nelements(dg, cache_parabolic) > old_n_elements @@ -112,11 +112,17 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) - # Need to resize u_transformed, gradients, flux_viscous somehow - @unpack u_transformed = cache_parabolic - u_transformed = wrap_array(u_ode, mesh, equations, dg, cache) - println("amr: ", size(u_transformed)) - + # Resize viscous helpers + resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._u_transformed), + (nvariables(equations), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.gradients = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._gradients), + (nvariables(equations), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._flux_viscous), + (nvariables(equations), nnodes(dg), nelements(dg, cache))) # Loop over all elements in old container and either copy them or refine them element_id = 1 for old_element_id in 1:old_n_elements @@ -323,7 +329,7 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, init_elements!(elements, leaf_cell_ids, mesh, dg.basis) @assert nelements(dg, cache) < old_n_elements - @unpack elements = cache_parabolic + @unpack elements, cache_viscous = cache_parabolic resize!(elements, length(leaf_cell_ids)) init_elements!(elements, leaf_cell_ids, mesh, dg.basis) @assert nelements(dg, cache_parabolic) < old_n_elements @@ -332,6 +338,18 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + # Resize viscous helpers + resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._u_transformed), + (nvariables(equations), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.gradients = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._gradients), + (nvariables(equations), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._flux_viscous), + (nvariables(equations), nnodes(dg), nelements(dg, cache))) + # Loop over all elements in old container and either copy them or coarsen them skip = 0 element_id = 1 diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl index 7eb6c716001..374c2491e4d 100644 --- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl @@ -17,7 +17,9 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack u_transformed, gradients, flux_viscous = cache_parabolic + #@unpack u_transformed, gradients, flux_viscous = cache_parabolic + @unpack cache_viscous = cache_parabolic + @unpack u_transformed, gradients, flux_viscous = cache_viscous # Convert conservative variables to a form more suitable for viscous flux calculations @trixi_timeit timer() "transform variables" begin @@ -111,7 +113,6 @@ function transform_variables!(u_transformed, u, mesh::TreeMesh{1}, u_node = get_node_vars(u, equations_parabolic, dg, i, element) u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node, equations_parabolic) - println("dg:", size(u_transformed)) set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg, i, element) end @@ -531,6 +532,7 @@ function create_cache_parabolic(mesh::TreeMesh{1}, elements = init_elements(leaf_cell_ids, mesh, equations_hyperbolic, dg.basis, RealT, uEltype) + # Additions for parabolic n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) @@ -538,11 +540,13 @@ function create_cache_parabolic(mesh::TreeMesh{1}, gradients = similar(u_transformed) flux_viscous = similar(u_transformed) + cache_viscous = CacheViscous{uEltype}(n_vars, n_nodes, n_elements) + interfaces = init_interfaces(leaf_cell_ids, mesh, elements) boundaries = init_boundaries(leaf_cell_ids, mesh, elements) - cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) + cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed, cache_viscous) return cache end @@ -565,4 +569,39 @@ function apply_jacobian_parabolic!(du, mesh::TreeMesh{1}, return nothing end + +mutable struct CacheViscous{uEltype <: Real} + u_transformed::Array{uEltype} + gradients::Array{uEltype} + flux_viscous::Array{uEltype} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + _gradients::Vector{uEltype} + _flux_viscous::Vector{uEltype} + + function CacheViscous{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} + new(Array{uEltype}(undef, n_vars, n_nodes, n_elements), + Array{uEltype}(undef, n_vars, n_nodes, n_elements), + Array{uEltype}(undef, n_vars, n_nodes, n_elements), + Vector{uEltype}(undef, n_vars*n_nodes*n_elements), + Vector{uEltype}(undef, n_vars*n_nodes*n_elements), + Vector{uEltype}(undef, n_vars*n_nodes*n_elements)) + end +end + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(cache_viscous::CacheViscous, capacity) + resize!(cache_viscous._u_transformed, capacity) + resize!(cache_viscous._gradients, capacity) + resize!(cache_viscous._flux_viscous, capacity) + + return nothing +end + + end # @muladd From f35a39849921ceb620342e057d5a82a7a4f19f23 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 14:46:44 +0200 Subject: [PATCH 25/74] revert elixirs --- .../elixir_navierstokes_convergence.jl | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 3a8a810d0d1..36a9f52e39d 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -1,4 +1,4 @@ -using OrdinaryDiffEq, Plots +using OrdinaryDiffEq using Trixi ############################################################################### @@ -23,12 +23,7 @@ mesh = TreeMesh(coordinates_min, coordinates_max, initial_refinement_level=4, periodicity=(true, false), n_cells_max=30_000) # set maximum capacity of tree data structure -#= -LLID = Trixi.local_leaf_cells(mesh.tree) -num_leafs = length(LLID) -@assert num_leafs % 4 == 0 -Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/4)]) -=# + # Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D` # since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`) # and by the initial condition (which passes in `CompressibleEulerEquations2D`). @@ -195,42 +190,24 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol boundary_conditions=(boundary_conditions, boundary_conditions_parabolic), source_terms=source_terms_navier_stokes_convergence_test) - -amr_indicator = IndicatorLöhner(semi, variable=Trixi.density) - -amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level=5, - med_level=6, med_threshold=0.05, - max_level=6, max_threshold=0.1) - -amr_callback = AMRCallback(semi, amr_controller, - interval=1, - adapt_initial_condition=false) - - ############################################################################### # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` -tspan = (0.0, 0.1) -split_form = false -ode = semidiscretize(semi, tspan; split_form=split_form) +tspan = (0.0, 0.5) +ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=10) -analysis_interval = 5 +analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) -callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) -#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) +callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) ############################################################################### # run the simulation time_int_tol = 1e-8 - - sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, ode_default_options()..., callback=callbacks) - summary_callback() # print the timer summary -plot(sol) \ No newline at end of file + From c47894213d68ac5b4f70637fea393ad27e37422c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 14:47:28 +0200 Subject: [PATCH 26/74] revert elixirs --- .../elixir_navierstokes_lid_driven_cavity.jl | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl b/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl index ffcb8f453e6..81e48737e79 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl @@ -60,30 +60,13 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # Create ODE problem with time span `tspan` tspan = (0.0, 25.0) -ode = semidiscretize(semi, tspan; split_form = false); +ode = semidiscretize(semi, tspan); summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=100) -analysis_interval = 10 +analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -amr_indicator = IndicatorHennemannGassner(semi, - alpha_max=1.0, - alpha_min=0.0001, - alpha_smooth=false, - variable=Trixi.v1) - -amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level=4, - med_level =5, med_threshold=0.25, - max_level =6, max_threshold=0.75) - -amr_callback = AMRCallback(semi, amr_controller, - interval=1, - adapt_initial_condition=false) - -callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) -#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) +callbacks = CallbackSet(summary_callback, alive_callback) ############################################################################### # run the simulation @@ -92,6 +75,5 @@ time_int_tol = 1e-8 sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, ode_default_options()..., callback=callbacks) summary_callback() # print the timer summary -using Plots -plot(sol) \ No newline at end of file + From 6afed8f04812c2ec86f9c6f6d9c6753b4bd3fb5a Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 14:48:04 +0200 Subject: [PATCH 27/74] revert euler --- src/equations/compressible_euler_2d.jl | 2919 ++++++++++++------------ 1 file changed, 1457 insertions(+), 1462 deletions(-) diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl index b05318c24f7..0cafe304bbf 100644 --- a/src/equations/compressible_euler_2d.jl +++ b/src/equations/compressible_euler_2d.jl @@ -3,1496 +3,1491 @@ # we need to opt-in explicitly. # See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin -#! format: noindent - -@doc raw""" - CompressibleEulerEquations2D(gamma) - -The compressible Euler equations -```math -\frac{\partial}{\partial t} -\begin{pmatrix} -\rho \\ \rho v_1 \\ \rho v_2 \\ \rho e -\end{pmatrix} -+ -\frac{\partial}{\partial x} -\begin{pmatrix} - \rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ (\rho e +p) v_1 -\end{pmatrix} -+ -\frac{\partial}{\partial y} -\begin{pmatrix} -\rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + p \\ (\rho e +p) v_2 -\end{pmatrix} -= -\begin{pmatrix} -0 \\ 0 \\ 0 \\ 0 -\end{pmatrix} -``` -for an ideal gas with ratio of specific heats `gamma` -in two space dimensions. -Here, ``\rho`` is the density, ``v_1``, ``v_2`` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and -```math -p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2) \right) -``` -the pressure. -""" -struct CompressibleEulerEquations2D{RealT <: Real} <: - AbstractCompressibleEulerEquations{2, 4} - gamma::RealT # ratio of specific heats - inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications - - function CompressibleEulerEquations2D(gamma) - γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1)) - new{typeof(γ)}(γ, inv_gamma_minus_one) + #! format: noindent + + @doc raw""" + CompressibleEulerEquations2D(gamma) + + The compressible Euler equations + ```math + \frac{\partial}{\partial t} + \begin{pmatrix} + \rho \\ \rho v_1 \\ \rho v_2 \\ \rho e + \end{pmatrix} + + + \frac{\partial}{\partial x} + \begin{pmatrix} + \rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ (\rho e +p) v_1 + \end{pmatrix} + + + \frac{\partial}{\partial y} + \begin{pmatrix} + \rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + p \\ (\rho e +p) v_2 + \end{pmatrix} + = + \begin{pmatrix} + 0 \\ 0 \\ 0 \\ 0 + \end{pmatrix} + ``` + for an ideal gas with ratio of specific heats `gamma` + in two space dimensions. + Here, ``\rho`` is the density, ``v_1``, ``v_2`` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and + ```math + p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2) \right) + ``` + the pressure. + """ + struct CompressibleEulerEquations2D{RealT <: Real} <: + AbstractCompressibleEulerEquations{2, 4} + gamma::RealT # ratio of specific heats + inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications + + function CompressibleEulerEquations2D(gamma) + γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1)) + new{typeof(γ)}(γ, inv_gamma_minus_one) + end + end + + function varnames(::typeof(cons2cons), ::CompressibleEulerEquations2D) + ("rho", "rho_v1", "rho_v2", "rho_e") + end + varnames(::typeof(cons2prim), ::CompressibleEulerEquations2D) = ("rho", "v1", "v2", "p") + + # Set initial conditions at physical location `x` for time `t` + """ + initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) + + A constant initial condition to test free-stream preservation. + """ + function initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) + rho = 1.0 + rho_v1 = 0.1 + rho_v2 = -0.2 + rho_e = 10.0 + return SVector(rho, rho_v1, rho_v2, rho_e) + end + + """ + initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations2D) + + A smooth initial condition used for convergence tests in combination with + [`source_terms_convergence_test`](@ref) + (and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). + """ + function initial_condition_convergence_test(x, t, + equations::CompressibleEulerEquations2D) + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = ini^2 + + return SVector(rho, rho_v1, rho_v2, rho_e) + end + + """ + source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations2D) + + Source terms used for convergence tests in combination with + [`initial_condition_convergence_test`](@ref) + (and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). + """ + @inline function source_terms_convergence_test(u, x, t, + equations::CompressibleEulerEquations2D) + # Same settings as in `initial_condition` + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + γ = equations.gamma + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + tmp = (2 * rho - 1) * (γ - 1) + + du1 = rho_x + du2 = rho_x * (1 + tmp) + du3 = du2 + du4 = 2 * rho_x * (rho + tmp) + + return SVector(du1, du2, du3, du4) + end + + """ + initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) + + A sine wave in the density with constant velocity and pressure; reduces the + compressible Euler equations to the linear advection equations. + This setup is the test case for stability of EC fluxes from paper + - Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) + Stability issues of entropy-stable and/or split-form high-order schemes + [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) + with the following parameters + - domain [-1, 1] + - mesh = 4x4 + - polydeg = 5 + """ + function initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) + v1 = 0.1 + v2 = 0.2 + rho = 1 + 0.98 * sinpi(2 * (x[1] + x[2] - t * (v1 + v2))) + rho_v1 = rho * v1 + rho_v2 = rho * v2 + p = 20 + rho_e = p / (equations.gamma - 1) + 1 / 2 * rho * (v1^2 + v2^2) + return SVector(rho, rho_v1, rho_v2, rho_e) end -end - -function varnames(::typeof(cons2cons), ::CompressibleEulerEquations2D) - ("rho", "rho_v1", "rho_v2", "rho_e") -end -varnames(::typeof(cons2prim), ::CompressibleEulerEquations2D) = ("rho", "v1", "v2", "p") - -# Set initial conditions at physical location `x` for time `t` -""" - initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) - -A constant initial condition to test free-stream preservation. -""" -function initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) - rho = 1.0 - rho_v1 = 0.1 - rho_v2 = -0.2 - rho_e = 10.0 - return SVector(rho, rho_v1, rho_v2, rho_e) -end - -""" - initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations2D) - -A smooth initial condition used for convergence tests in combination with -[`source_terms_convergence_test`](@ref) -(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). -""" -function initial_condition_convergence_test(x, t, - equations::CompressibleEulerEquations2D) - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = ini^2 - - return SVector(rho, rho_v1, rho_v2, rho_e) -end - -""" - source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations2D) - -Source terms used for convergence tests in combination with -[`initial_condition_convergence_test`](@ref) -(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). -""" -@inline function source_terms_convergence_test(u, x, t, + + """ + initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations2D) + + A weak blast wave taken from + - Sebastian Hennemann, Gregor J. Gassner (2020) + A provably entropy stable subcell shock capturing approach for high order split form DG + [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044) + """ + function initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations2D) - # Same settings as in `initial_condition` - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - γ = equations.gamma - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - tmp = (2 * rho - 1) * (γ - 1) - - du1 = rho_x - du2 = rho_x * (1 + tmp) - du3 = du2 - du4 = 2 * rho_x * (rho + tmp) - - return SVector(du1, du2, du3, du4) -end - -""" - initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) - -A sine wave in the density with constant velocity and pressure; reduces the -compressible Euler equations to the linear advection equations. -This setup is the test case for stability of EC fluxes from paper -- Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) - Stability issues of entropy-stable and/or split-form high-order schemes - [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) -with the following parameters -- domain [-1, 1] -- mesh = 4x4 -- polydeg = 5 -""" -function initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) - v1 = 0.1 - v2 = 0.2 - rho = 1 + 0.98 * sinpi(2 * (x[1] + x[2] - t * (v1 + v2))) - rho_v1 = rho * v1 - rho_v2 = rho * v2 - p = 20 - rho_e = p / (equations.gamma - 1) + 1 / 2 * rho * (v1^2 + v2^2) - return SVector(rho, rho_v1, rho_v2, rho_e) -end - -""" - initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations2D) - -A weak blast wave taken from -- Sebastian Hennemann, Gregor J. Gassner (2020) - A provably entropy stable subcell shock capturing approach for high order split form DG - [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044) -""" -function initial_condition_weak_blast_wave(x, t, - equations::CompressibleEulerEquations2D) - # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) - # Set up polar coordinates - inicenter = SVector(0.0, 0.0) - x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) - phi = atan(y_norm, x_norm) - sin_phi, cos_phi = sincos(phi) - - # Calculate primitive variables - rho = r > 0.5 ? 1.0 : 1.1691 - v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi - v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi - p = r > 0.5 ? 1.0 : 1.245 - - return prim2cons(SVector(rho, v1, v2, p), equations) -end - -""" - initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations2D) - -Setup used for convergence tests of the Euler equations with self-gravity used in -- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) -in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref) -or [`source_terms_eoc_test_euler`](@ref). -""" -function initial_condition_eoc_test_coupled_euler_gravity(x, t, - equations::CompressibleEulerEquations2D) - # OBS! this assumes that γ = 2 other manufactured source terms are incorrect - if equations.gamma != 2.0 - error("adiabatic constant must be 2 for the coupling convergence test") + # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + phi = atan(y_norm, x_norm) + sin_phi, cos_phi = sincos(phi) + + # Calculate primitive variables + rho = r > 0.5 ? 1.0 : 1.1691 + v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi + v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi + p = r > 0.5 ? 1.0 : 1.245 + + return prim2cons(SVector(rho, v1, v2, p), equations) + end + + """ + initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations2D) + + Setup used for convergence tests of the Euler equations with self-gravity used in + - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) + in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref) + or [`source_terms_eoc_test_euler`](@ref). + """ + function initial_condition_eoc_test_coupled_euler_gravity(x, t, + equations::CompressibleEulerEquations2D) + # OBS! this assumes that γ = 2 other manufactured source terms are incorrect + if equations.gamma != 2.0 + error("adiabatic constant must be 2 for the coupling convergence test") + end + c = 2.0 + A = 0.1 + ini = c + A * sin(pi * (x[1] + x[2] - t)) + G = 1.0 # gravitational constant + + rho = ini + v1 = 1.0 + v2 = 1.0 + p = ini^2 * G / pi # * 2 / ndims, but ndims==2 here + + return prim2cons(SVector(rho, v1, v2, p), equations) + end + + """ + source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations2D) + + Setup used for convergence tests of the Euler equations with self-gravity used in + - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) + in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). + """ + @inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t, + equations::CompressibleEulerEquations2D) + # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` + c = 2.0 + A = 0.1 + G = 1.0 # gravitational constant, must match coupling solver + C_grav = -2 * G / pi # 2 == 4 / ndims + + x1, x2 = x + si, co = sincos(pi * (x1 + x2 - t)) + rhox = A * pi * co + rho = c + A * si + + du1 = rhox + du2 = rhox + du3 = rhox + du4 = (1.0 - C_grav * rho) * rhox + + return SVector(du1, du2, du3, du4) + end + + """ + source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations2D) + + Setup used for convergence tests of the Euler equations with self-gravity used in + - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) + in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). + """ + @inline function source_terms_eoc_test_euler(u, x, t, + equations::CompressibleEulerEquations2D) + # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` + c = 2.0 + A = 0.1 + G = 1.0 + C_grav = -2 * G / pi # 2 == 4 / ndims + + x1, x2 = x + si, co = sincos(pi * (x1 + x2 - t)) + rhox = A * pi * co + rho = c + A * si + + du1 = rhox + du2 = rhox * (1 - C_grav * rho) + du3 = rhox * (1 - C_grav * rho) + du4 = rhox * (1 - 3 * C_grav * rho) + + return SVector(du1, du2, du3, du4) + end + + """ + boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, + equations::CompressibleEulerEquations2D) + + Determine the boundary numerical surface flux for a slip wall condition. + Imposes a zero normal velocity at the wall. + Density is taken from the internal solution state and pressure is computed as an + exact solution of a 1D Riemann problem. Further details about this boundary state + are available in the paper: + - J. J. W. van der Vegt and H. van der Ven (2002) + Slip flow boundary conditions in discontinuous Galerkin discretizations of + the Euler equations of gas dynamics + [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) + + Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book + - Eleuterio F. Toro (2009) + Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + 3rd edition + [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + + Should be used together with [`UnstructuredMesh2D`](@ref). + """ + @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + x, t, + surface_flux_function, + equations::CompressibleEulerEquations2D) + norm_ = norm(normal_direction) + # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later + normal = normal_direction / norm_ + + # rotate the internal solution state + u_local = rotate_to_x(u_inner, normal, equations) + + # compute the primitive variables + rho_local, v_normal, v_tangent, p_local = cons2prim(u_local, equations) + + # Get the solution of the pressure Riemann problem + # See Section 6.3.3 of + # Eleuterio F. Toro (2009) + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + if v_normal <= 0.0 + sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed + p_star = p_local * + (1 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2 * + equations.gamma * + equations.inv_gamma_minus_one) + else # v_normal > 0.0 + A = 2 / ((equations.gamma + 1) * rho_local) + B = p_local * (equations.gamma - 1) / (equations.gamma + 1) + p_star = p_local + + 0.5 * v_normal / A * + (v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B))) + end + + # For the slip wall we directly set the flux as the normal velocity is zero + return SVector(zero(eltype(u_inner)), + p_star * normal[1], + p_star * normal[2], + zero(eltype(u_inner))) * norm_ + end + + """ + boundary_condition_slip_wall(u_inner, orientation, direction, x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) + + Should be used together with [`TreeMesh`](@ref). + """ + @inline function boundary_condition_slip_wall(u_inner, orientation, + direction, x, t, + surface_flux_function, + equations::CompressibleEulerEquations2D) + # get the appropriate normal vector from the orientation + if orientation == 1 + normal_direction = SVector(1, 0) + else # orientation == 2 + normal_direction = SVector(0, 1) + end + + # compute and return the flux using `boundary_condition_slip_wall` routine above + return boundary_condition_slip_wall(u_inner, normal_direction, direction, + x, t, surface_flux_function, equations) + end + + """ + boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) + + Should be used together with [`StructuredMesh`](@ref). + """ + @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + direction, x, t, + surface_flux_function, + equations::CompressibleEulerEquations2D) + # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back + # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh + if isodd(direction) + boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, + x, t, surface_flux_function, + equations) + else + boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, + x, t, surface_flux_function, + equations) + end + + return boundary_flux end - c = 2.0 - A = 0.1 - ini = c + A * sin(pi * (x[1] + x[2] - t)) - G = 1.0 # gravitational constant - - rho = ini - v1 = 1.0 - v2 = 1.0 - p = ini^2 * G / pi # * 2 / ndims, but ndims==2 here - - return prim2cons(SVector(rho, v1, v2, p), equations) -end - -""" - source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations2D) - -Setup used for convergence tests of the Euler equations with self-gravity used in -- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) -in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). -""" -@inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t, - equations::CompressibleEulerEquations2D) - # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` - c = 2.0 - A = 0.1 - G = 1.0 # gravitational constant, must match coupling solver - C_grav = -2 * G / pi # 2 == 4 / ndims - - x1, x2 = x - si, co = sincos(pi * (x1 + x2 - t)) - rhox = A * pi * co - rho = c + A * si - - du1 = rhox - du2 = rhox - du3 = rhox - du4 = (1.0 - C_grav * rho) * rhox - - return SVector(du1, du2, du3, du4) -end - -""" - source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations2D) - -Setup used for convergence tests of the Euler equations with self-gravity used in -- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) -in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). -""" -@inline function source_terms_eoc_test_euler(u, x, t, - equations::CompressibleEulerEquations2D) - # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` - c = 2.0 - A = 0.1 - G = 1.0 - C_grav = -2 * G / pi # 2 == 4 / ndims - - x1, x2 = x - si, co = sincos(pi * (x1 + x2 - t)) - rhox = A * pi * co - rho = c + A * si - - du1 = rhox - du2 = rhox * (1 - C_grav * rho) - du3 = rhox * (1 - C_grav * rho) - du4 = rhox * (1 - 3 * C_grav * rho) - - return SVector(du1, du2, du3, du4) -end - -""" - boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, + + # Calculate 2D flux for a single point + @inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = rho_v1 * v2 + f4 = (rho_e + p) * v1 + else + f1 = rho_v2 + f2 = rho_v2 * v1 + f3 = rho_v2 * v2 + p + f4 = (rho_e + p) * v2 + end + return SVector(f1, f2, f3, f4) + end + + # Calculate 2D flux for a single point in the normal direction + # Note, this directional vector is not normalized + @inline function flux(u, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_e = last(u) + rho, v1, v2, p = cons2prim(u, equations) + + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = rho_v_normal + f2 = rho_v_normal * v1 + p * normal_direction[1] + f3 = rho_v_normal * v2 + p * normal_direction[2] + f4 = (rho_e + p) * v_normal + return SVector(f1, f2, f3, f4) + end + + """ + flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction, + equations::CompressibleEulerEquations2D) + + This flux is is a modification of the original kinetic energy preserving two-point flux by + - Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018) + Kinetic energy and entropy preserving schemes for compressible flows + by split convective forms + [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) + + The modification is in the energy flux to guarantee pressure equilibrium and was developed by + - Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) + Preventing spurious pressure oscillations in split convective form discretizations for + compressible flows + [DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060) + """ + @inline function flux_shima_etal(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Average each factor of products in flux + rho_avg = 1 / 2 * (rho_ll + rho_rr) + v1_avg = 1 / 2 * (v1_ll + v1_rr) + v2_avg = 1 / 2 * (v2_ll + v2_rr) + p_avg = 1 / 2 * (p_ll + p_rr) + kin_avg = 1 / 2 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + pv1_avg = 1 / 2 * (p_ll * v1_rr + p_rr * v1_ll) + f1 = rho_avg * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * v2_avg + f4 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg + else + pv2_avg = 1 / 2 * (p_ll * v2_rr + p_rr * v2_ll) + f1 = rho_avg * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_avg + f4 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg + end + + return SVector(f1, f2, f3, f4) + end + + @inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # Average each factor of products in flux + rho_avg = 1 / 2 * (rho_ll + rho_rr) + v1_avg = 1 / 2 * (v1_ll + v1_rr) + v2_avg = 1 / 2 * (v2_ll + v2_rr) + v_dot_n_avg = 1 / 2 * (v_dot_n_ll + v_dot_n_rr) + p_avg = 1 / 2 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_avg * v_dot_n_avg + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + f4 = (f1 * velocity_square_avg + + p_avg * v_dot_n_avg * equations.inv_gamma_minus_one + + 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) + + return SVector(f1, f2, f3, f4) + end + + """ + flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction, + equations::CompressibleEulerEquations2D) + + Kinetic energy preserving two-point flux by + - Kennedy and Gruber (2008) + Reduced aliasing formulations of the convective terms within the + Navier-Stokes equations for a compressible fluid + [DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) + """ + @inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_e_ll = last(u_ll) + rho_e_rr = last(u_rr) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Average each factor of products in flux + rho_avg = 1 / 2 * (rho_ll + rho_rr) + v1_avg = 1 / 2 * (v1_ll + v1_rr) + v2_avg = 1 / 2 * (v2_ll + v2_rr) + p_avg = 1 / 2 * (p_ll + p_rr) + e_avg = 1 / 2 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_avg * v1_avg + f2 = rho_avg * v1_avg * v1_avg + p_avg + f3 = rho_avg * v1_avg * v2_avg + f4 = (rho_avg * e_avg + p_avg) * v1_avg + else + f1 = rho_avg * v2_avg + f2 = rho_avg * v2_avg * v1_avg + f3 = rho_avg * v2_avg * v2_avg + p_avg + f4 = (rho_avg * e_avg + p_avg) * v2_avg + end + + return SVector(f1, f2, f3, f4) + end + + @inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_e_ll = last(u_ll) + rho_e_rr = last(u_rr) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Average each factor of products in flux + rho_avg = 0.5 * (rho_ll + rho_rr) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] + p_avg = 0.5 * (p_ll + p_rr) + e_avg = 0.5 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_avg * v_dot_n_avg + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + f4 = f1 * e_avg + p_avg * v_dot_n_avg + + return SVector(f1, f2, f3, f4) + end + + """ + flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) + + Entropy conserving two-point flux by + - Chandrashekar (2013) + Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes + for Compressible Euler and Navier-Stokes Equations + [DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) + """ + @inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + beta_ll = 0.5 * rho_ll / p_ll + beta_rr = 0.5 * rho_rr / p_rr + specific_kin_ll = 0.5 * (v1_ll^2 + v2_ll^2) + specific_kin_rr = 0.5 * (v1_rr^2 + v2_rr^2) + + # Compute the necessary mean values + rho_avg = 0.5 * (rho_ll + rho_rr) + rho_mean = ln_mean(rho_ll, rho_rr) + beta_mean = ln_mean(beta_ll, beta_rr) + beta_avg = 0.5 * (beta_ll + beta_rr) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_mean = 0.5 * rho_avg / beta_avg + velocity_square_avg = specific_kin_ll + specific_kin_rr + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_mean + f3 = f1 * v2_avg + f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + + f2 * v1_avg + f3 * v2_avg + else + f1 = rho_mean * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_mean + f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + + f2 * v1_avg + f3 * v2_avg + end + + return SVector(f1, f2, f3, f4) + end + + """ + flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, + equations::CompressibleEulerEquations2D) + + Entropy conserving and kinetic energy preserving two-point flux by + - Hendrik Ranocha (2018) + Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods + for Hyperbolic Balance Laws + [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) + See also + - Hendrik Ranocha (2020) + Entropy Conserving and Kinetic Energy Preserving Numerical Methods for + the Euler Equations Using Summation-by-Parts Operators + [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) + """ + @inline function flux_ranocha(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` + # in exact arithmetic since + # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) + # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) + inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_avg = 0.5 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * v2_avg + f4 = f1 * + (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + + 0.5 * (p_ll * v1_rr + p_rr * v1_ll) + else + f1 = rho_mean * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_avg + f4 = f1 * + (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + + 0.5 * (p_ll * v2_rr + p_rr * v2_ll) + end + + return SVector(f1, f2, f3, f4) + end + + @inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` + # in exact arithmetic since + # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) + # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) + inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_avg = 0.5 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_mean * 0.5 * (v_dot_n_ll + v_dot_n_rr) + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + f4 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + + + 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) + + return SVector(f1, f2, f3, f4) + end + + """ + splitting_steger_warming(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}} + orientation::Integer, equations::CompressibleEulerEquations2D) - -Determine the boundary numerical surface flux for a slip wall condition. -Imposes a zero normal velocity at the wall. -Density is taken from the internal solution state and pressure is computed as an -exact solution of a 1D Riemann problem. Further details about this boundary state -are available in the paper: -- J. J. W. van der Vegt and H. van der Ven (2002) - Slip flow boundary conditions in discontinuous Galerkin discretizations of - the Euler equations of gas dynamics - [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) - -Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book -- Eleuterio F. Toro (2009) - Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - 3rd edition - [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - -Should be used together with [`UnstructuredMesh2D`](@ref). -""" -@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - x, t, - surface_flux_function, + + Splitting of the compressible Euler flux of Steger and Warming. + + Returns a tuple of the fluxes "minus" (associated with waves going into the + negative axis direction) and "plus" (associated with waves going into the + positive axis direction). If only one of the fluxes is required, use the + function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. + + !!! warning "Experimental implementation (upwind SBP)" + This is an experimental feature and may change in future releases. + + ## References + + - Joseph L. Steger and R. F. Warming (1979) + Flux Vector Splitting of the Inviscid Gasdynamic Equations + With Application to Finite Difference Methods + [NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf) + """ + @inline function splitting_steger_warming(u, orientation::Integer, equations::CompressibleEulerEquations2D) - norm_ = norm(normal_direction) - # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later - normal = normal_direction / norm_ - - # rotate the internal solution state - u_local = rotate_to_x(u_inner, normal, equations) - - # compute the primitive variables - rho_local, v_normal, v_tangent, p_local = cons2prim(u_local, equations) - - # Get the solution of the pressure Riemann problem - # See Section 6.3.3 of - # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - if v_normal <= 0.0 - sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed - p_star = p_local * - (1 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2 * - equations.gamma * - equations.inv_gamma_minus_one) - else # v_normal > 0.0 - A = 2 / ((equations.gamma + 1) * rho_local) - B = p_local * (equations.gamma - 1) / (equations.gamma + 1) - p_star = p_local + - 0.5 * v_normal / A * - (v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B))) + fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations) + fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations) + return fm, fp end - - # For the slip wall we directly set the flux as the normal velocity is zero - return SVector(zero(eltype(u_inner)), - p_star * normal[1], - p_star * normal[2], - zero(eltype(u_inner))) * norm_ -end - -""" - boundary_condition_slip_wall(u_inner, orientation, direction, x, t, - surface_flux_function, equations::CompressibleEulerEquations2D) - -Should be used together with [`TreeMesh`](@ref). -""" -@inline function boundary_condition_slip_wall(u_inner, orientation, - direction, x, t, - surface_flux_function, + + @inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer, equations::CompressibleEulerEquations2D) - # get the appropriate normal vector from the orientation - if orientation == 1 - normal_direction = SVector(1, 0) - else # orientation == 2 - normal_direction = SVector(0, 1) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + a = sqrt(equations.gamma * p / rho) + + if orientation == 1 + lambda1 = v1 + lambda2 = v1 + a + lambda3 = v1 - a + + lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) + lambda2_p = positive_part(lambda2) + lambda3_p = positive_part(lambda3) + + alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p + + rho_2gamma = 0.5 * rho / equations.gamma + f1p = rho_2gamma * alpha_p + f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p)) + f3p = rho_2gamma * alpha_p * v2 + f4p = rho_2gamma * + (alpha_p * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_p - lambda3_p) + + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) + else # orientation == 2 + lambda1 = v2 + lambda2 = v2 + a + lambda3 = v2 - a + + lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) + lambda2_p = positive_part(lambda2) + lambda3_p = positive_part(lambda3) + + alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p + + rho_2gamma = 0.5 * rho / equations.gamma + f1p = rho_2gamma * alpha_p + f2p = rho_2gamma * alpha_p * v1 + f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p)) + f4p = rho_2gamma * + (alpha_p * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_p - lambda3_p) + + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) + end + return SVector(f1p, f2p, f3p, f4p) end - - # compute and return the flux using `boundary_condition_slip_wall` routine above - return boundary_condition_slip_wall(u_inner, normal_direction, direction, - x, t, surface_flux_function, equations) -end - -""" - boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, - surface_flux_function, equations::CompressibleEulerEquations2D) - -Should be used together with [`StructuredMesh`](@ref). -""" -@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - direction, x, t, - surface_flux_function, + + @inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer, equations::CompressibleEulerEquations2D) - # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back - # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh - if isodd(direction) - boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, - x, t, surface_flux_function, - equations) - else - boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, - x, t, surface_flux_function, - equations) - end - - return boundary_flux -end - -# Calculate 2D flux for a single point -@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = rho_v1 * v2 - f4 = (rho_e + p) * v1 - else - f1 = rho_v2 - f2 = rho_v2 * v1 - f3 = rho_v2 * v2 + p - f4 = (rho_e + p) * v2 + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + a = sqrt(equations.gamma * p / rho) + + if orientation == 1 + lambda1 = v1 + lambda2 = v1 + a + lambda3 = v1 - a + + lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) + lambda2_m = negative_part(lambda2) + lambda3_m = negative_part(lambda3) + + alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m + + rho_2gamma = 0.5 * rho / equations.gamma + f1m = rho_2gamma * alpha_m + f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m)) + f3m = rho_2gamma * alpha_m * v2 + f4m = rho_2gamma * + (alpha_m * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_m - lambda3_m) + + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) + else # orientation == 2 + lambda1 = v2 + lambda2 = v2 + a + lambda3 = v2 - a + + lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) + lambda2_m = negative_part(lambda2) + lambda3_m = negative_part(lambda3) + + alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m + + rho_2gamma = 0.5 * rho / equations.gamma + f1m = rho_2gamma * alpha_m + f2m = rho_2gamma * alpha_m * v1 + f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m)) + f4m = rho_2gamma * + (alpha_m * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_m - lambda3_m) + + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) + end + return SVector(f1m, f2m, f3m, f4m) end - return SVector(f1, f2, f3, f4) -end - -# Calculate 2D flux for a single point in the normal direction -# Note, this directional vector is not normalized -@inline function flux(u, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_e = last(u) - rho, v1, v2, p = cons2prim(u, equations) - - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = rho_v_normal - f2 = rho_v_normal * v1 + p * normal_direction[1] - f3 = rho_v_normal * v2 + p * normal_direction[2] - f4 = (rho_e + p) * v_normal - return SVector(f1, f2, f3, f4) -end - -""" - flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) - -This flux is is a modification of the original kinetic energy preserving two-point flux by -- Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018) - Kinetic energy and entropy preserving schemes for compressible flows - by split convective forms - [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) - -The modification is in the energy flux to guarantee pressure equilibrium and was developed by -- Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) - Preventing spurious pressure oscillations in split convective form discretizations for - compressible flows - [DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060) -""" -@inline function flux_shima_etal(u_ll, u_rr, orientation::Integer, + + """ + splitting_vanleer_haenel(u, orientation::Integer, equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Average each factor of products in flux - rho_avg = 1 / 2 * (rho_ll + rho_rr) - v1_avg = 1 / 2 * (v1_ll + v1_rr) - v2_avg = 1 / 2 * (v2_ll + v2_rr) - p_avg = 1 / 2 * (p_ll + p_rr) - kin_avg = 1 / 2 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - pv1_avg = 1 / 2 * (p_ll * v1_rr + p_rr * v1_ll) - f1 = rho_avg * v1_avg - f2 = f1 * v1_avg + p_avg - f3 = f1 * v2_avg - f4 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg - else - pv2_avg = 1 / 2 * (p_ll * v2_rr + p_rr * v2_ll) - f1 = rho_avg * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_avg - f4 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg - end - - return SVector(f1, f2, f3, f4) -end - -@inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector, + splitting_vanleer_haenel(u, which::Union{Val{:minus}, Val{:plus}} + orientation::Integer, equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # Average each factor of products in flux - rho_avg = 1 / 2 * (rho_ll + rho_rr) - v1_avg = 1 / 2 * (v1_ll + v1_rr) - v2_avg = 1 / 2 * (v2_ll + v2_rr) - v_dot_n_avg = 1 / 2 * (v_dot_n_ll + v_dot_n_rr) - p_avg = 1 / 2 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_avg * v_dot_n_avg - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - f4 = (f1 * velocity_square_avg + - p_avg * v_dot_n_avg * equations.inv_gamma_minus_one - + 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) - - return SVector(f1, f2, f3, f4) -end - -""" - flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) - -Kinetic energy preserving two-point flux by -- Kennedy and Gruber (2008) - Reduced aliasing formulations of the convective terms within the - Navier-Stokes equations for a compressible fluid - [DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) -""" -@inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_e_ll = last(u_ll) - rho_e_rr = last(u_rr) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Average each factor of products in flux - rho_avg = 1 / 2 * (rho_ll + rho_rr) - v1_avg = 1 / 2 * (v1_ll + v1_rr) - v2_avg = 1 / 2 * (v2_ll + v2_rr) - p_avg = 1 / 2 * (p_ll + p_rr) - e_avg = 1 / 2 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_avg * v1_avg - f2 = rho_avg * v1_avg * v1_avg + p_avg - f3 = rho_avg * v1_avg * v2_avg - f4 = (rho_avg * e_avg + p_avg) * v1_avg - else - f1 = rho_avg * v2_avg - f2 = rho_avg * v2_avg * v1_avg - f3 = rho_avg * v2_avg * v2_avg + p_avg - f4 = (rho_avg * e_avg + p_avg) * v2_avg + + Splitting of the compressible Euler flux from van Leer. This splitting further + contains a reformulation due to Hänel et al. where the energy flux uses the + enthalpy. The pressure splitting is independent from the splitting of the + convective terms. As such there are many pressure splittings suggested across + the literature. We implement the 'p4' variant suggested by Liou and Steffen as + it proved the most robust in practice. + + Returns a tuple of the fluxes "minus" (associated with waves going into the + negative axis direction) and "plus" (associated with waves going into the + positive axis direction). If only one of the fluxes is required, use the + function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. + + !!! warning "Experimental implementation (upwind SBP)" + This is an experimental feature and may change in future releases. + + ## References + + - Bram van Leer (1982) + Flux-Vector Splitting for the Euler Equation + [DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5) + - D. Hänel, R. Schwane and G. Seider (1987) + On the accuracy of upwind schemes for the solution of the Navier-Stokes equations + [DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105) + - Meng-Sing Liou and Chris J. Steffen, Jr. (1991) + High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting + [NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425) + """ + @inline function splitting_vanleer_haenel(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + fm = splitting_vanleer_haenel(u, Val{:minus}(), orientation, equations) + fp = splitting_vanleer_haenel(u, Val{:plus}(), orientation, equations) + return fm, fp end - - return SVector(f1, f2, f3, f4) -end - -@inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_e_ll = last(u_ll) - rho_e_rr = last(u_rr) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Average each factor of products in flux - rho_avg = 0.5 * (rho_ll + rho_rr) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] - p_avg = 0.5 * (p_ll + p_rr) - e_avg = 0.5 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_avg * v_dot_n_avg - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - f4 = f1 * e_avg + p_avg * v_dot_n_avg - - return SVector(f1, f2, f3, f4) -end - -""" - flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) - -Entropy conserving two-point flux by -- Chandrashekar (2013) - Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes - for Compressible Euler and Navier-Stokes Equations - [DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) -""" -@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - beta_ll = 0.5 * rho_ll / p_ll - beta_rr = 0.5 * rho_rr / p_rr - specific_kin_ll = 0.5 * (v1_ll^2 + v2_ll^2) - specific_kin_rr = 0.5 * (v1_rr^2 + v2_rr^2) - - # Compute the necessary mean values - rho_avg = 0.5 * (rho_ll + rho_rr) - rho_mean = ln_mean(rho_ll, rho_rr) - beta_mean = ln_mean(beta_ll, beta_rr) - beta_avg = 0.5 * (beta_ll + beta_rr) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_mean = 0.5 * rho_avg / beta_avg - velocity_square_avg = specific_kin_ll + specific_kin_rr - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_mean - f3 = f1 * v2_avg - f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + - f2 * v1_avg + f3 * v2_avg - else - f1 = rho_mean * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_mean - f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + - f2 * v1_avg + f3 * v2_avg + + @inline function splitting_vanleer_haenel(u, ::Val{:plus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + + if orientation == 1 + M = v1 / a + p_plus = 0.5 * (1 + equations.gamma * M) * p + + f1p = 0.25 * rho * a * (M + 1)^2 + f2p = f1p * v1 + p_plus + f3p = f1p * v2 + f4p = f1p * H + else # orientation == 2 + M = v2 / a + p_plus = 0.5 * (1 + equations.gamma * M) * p + + f1p = 0.25 * rho * a * (M + 1)^2 + f2p = f1p * v1 + f3p = f1p * v2 + p_plus + f4p = f1p * H + end + return SVector(f1p, f2p, f3p, f4p) end - - return SVector(f1, f2, f3, f4) -end - -""" - flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) - -Entropy conserving and kinetic energy preserving two-point flux by -- Hendrik Ranocha (2018) - Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods - for Hyperbolic Balance Laws - [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) -See also -- Hendrik Ranocha (2020) - Entropy Conserving and Kinetic Energy Preserving Numerical Methods for - the Euler Equations Using Summation-by-Parts Operators - [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) -""" -@inline function flux_ranocha(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Compute the necessary mean values - rho_mean = ln_mean(rho_ll, rho_rr) - # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` - # in exact arithmetic since - # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) - # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) - inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_avg = 0.5 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_avg - f3 = f1 * v2_avg - f4 = f1 * - (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + - 0.5 * (p_ll * v1_rr + p_rr * v1_ll) - else - f1 = rho_mean * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_avg - f4 = f1 * - (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + - 0.5 * (p_ll * v2_rr + p_rr * v2_ll) + + @inline function splitting_vanleer_haenel(u, ::Val{:minus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + + if orientation == 1 + M = v1 / a + p_minus = 0.5 * (1 - equations.gamma * M) * p + + f1m = -0.25 * rho * a * (M - 1)^2 + f2m = f1m * v1 + p_minus + f3m = f1m * v2 + f4m = f1m * H + else # orientation == 2 + M = v2 / a + p_minus = 0.5 * (1 - equations.gamma * M) * p + + f1m = -0.25 * rho * a * (M - 1)^2 + f2m = f1m * v1 + f3m = f1m * v2 + p_minus + f4m = f1m * H + end + return SVector(f1m, f2m, f3m, f4m) end - - return SVector(f1, f2, f3, f4) -end - -@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # Compute the necessary mean values - rho_mean = ln_mean(rho_ll, rho_rr) - # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` - # in exact arithmetic since - # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) - # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) - inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_avg = 0.5 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_mean * 0.5 * (v_dot_n_ll + v_dot_n_rr) - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - f4 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) - + - 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) - - return SVector(f1, f2, f3, f4) -end - -""" - splitting_steger_warming(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) - -Splitting of the compressible Euler flux of Steger and Warming. - -Returns a tuple of the fluxes "minus" (associated with waves going into the -negative axis direction) and "plus" (associated with waves going into the -positive axis direction). If only one of the fluxes is required, use the -function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. - -!!! warning "Experimental implementation (upwind SBP)" - This is an experimental feature and may change in future releases. - -## References - -- Joseph L. Steger and R. F. Warming (1979) - Flux Vector Splitting of the Inviscid Gasdynamic Equations - With Application to Finite Difference Methods - [NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf) -""" -@inline function splitting_steger_warming(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations) - fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations) - return fm, fp -end - -@inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - a = sqrt(equations.gamma * p / rho) - - if orientation == 1 - lambda1 = v1 - lambda2 = v1 + a - lambda3 = v1 - a - - lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) - lambda2_p = positive_part(lambda2) - lambda3_p = positive_part(lambda3) - - alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p - - rho_2gamma = 0.5 * rho / equations.gamma - f1p = rho_2gamma * alpha_p - f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p)) - f3p = rho_2gamma * alpha_p * v2 - f4p = rho_2gamma * - (alpha_p * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_p - lambda3_p) - + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) - else # orientation == 2 - lambda1 = v2 - lambda2 = v2 + a - lambda3 = v2 - a - - lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) - lambda2_p = positive_part(lambda2) - lambda3_p = positive_part(lambda3) - - alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p - - rho_2gamma = 0.5 * rho / equations.gamma - f1p = rho_2gamma * alpha_p - f2p = rho_2gamma * alpha_p * v1 - f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p)) - f4p = rho_2gamma * - (alpha_p * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_p - lambda3_p) - + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) + + """ + splitting_lax_friedrichs(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}} + orientation::Integer, + equations::CompressibleEulerEquations2D) + + Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)` + and `f⁻ = 0.5 (f - λ u)` similar to a flux splitting one would apply, e.g., + to Burgers' equation. + + Returns a tuple of the fluxes "minus" (associated with waves going into the + negative axis direction) and "plus" (associated with waves going into the + positive axis direction). If only one of the fluxes is required, use the + function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. + + !!! warning "Experimental implementation (upwind SBP)" + This is an experimental feature and may change in future releases. + """ + @inline function splitting_lax_friedrichs(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + fm = splitting_lax_friedrichs(u, Val{:minus}(), orientation, equations) + fp = splitting_lax_friedrichs(u, Val{:plus}(), orientation, equations) + return fm, fp end - return SVector(f1p, f2p, f3p, f4p) -end - -@inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - a = sqrt(equations.gamma * p / rho) - - if orientation == 1 - lambda1 = v1 - lambda2 = v1 + a - lambda3 = v1 - a - - lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) - lambda2_m = negative_part(lambda2) - lambda3_m = negative_part(lambda3) - - alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m - - rho_2gamma = 0.5 * rho / equations.gamma - f1m = rho_2gamma * alpha_m - f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m)) - f3m = rho_2gamma * alpha_m * v2 - f4m = rho_2gamma * - (alpha_m * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_m - lambda3_m) - + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) - else # orientation == 2 - lambda1 = v2 - lambda2 = v2 + a - lambda3 = v2 - a - - lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) - lambda2_m = negative_part(lambda2) - lambda3_m = negative_part(lambda3) - - alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m - - rho_2gamma = 0.5 * rho / equations.gamma - f1m = rho_2gamma * alpha_m - f2m = rho_2gamma * alpha_m * v1 - f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m)) - f4m = rho_2gamma * - (alpha_m * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_m - lambda3_m) - + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) + + @inline function splitting_lax_friedrichs(u, ::Val{:plus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) + + if orientation == 1 + #lambda = 0.5 * (abs(v1) + a) + f1p = 0.5 * rho * v1 + lambda * u[1] + f2p = 0.5 * rho * v1 * v1 + 0.5 * p + lambda * u[2] + f3p = 0.5 * rho * v1 * v2 + lambda * u[3] + f4p = 0.5 * rho * v1 * H + lambda * u[4] + else # orientation == 2 + #lambda = 0.5 * (abs(v2) + a) + f1p = 0.5 * rho * v2 + lambda * u[1] + f2p = 0.5 * rho * v2 * v1 + lambda * u[2] + f3p = 0.5 * rho * v2 * v2 + 0.5 * p + lambda * u[3] + f4p = 0.5 * rho * v2 * H + lambda * u[4] + end + return SVector(f1p, f2p, f3p, f4p) end - return SVector(f1m, f2m, f3m, f4m) -end - -""" - splitting_vanleer_haenel(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - splitting_vanleer_haenel(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) - -Splitting of the compressible Euler flux from van Leer. This splitting further -contains a reformulation due to Hänel et al. where the energy flux uses the -enthalpy. The pressure splitting is independent from the splitting of the -convective terms. As such there are many pressure splittings suggested across -the literature. We implement the 'p4' variant suggested by Liou and Steffen as -it proved the most robust in practice. - -Returns a tuple of the fluxes "minus" (associated with waves going into the -negative axis direction) and "plus" (associated with waves going into the -positive axis direction). If only one of the fluxes is required, use the -function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. - -!!! warning "Experimental implementation (upwind SBP)" - This is an experimental feature and may change in future releases. - -## References - -- Bram van Leer (1982) - Flux-Vector Splitting for the Euler Equation - [DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5) -- D. Hänel, R. Schwane and G. Seider (1987) - On the accuracy of upwind schemes for the solution of the Navier-Stokes equations - [DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105) -- Meng-Sing Liou and Chris J. Steffen, Jr. (1991) - High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting - [NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425) -""" -@inline function splitting_vanleer_haenel(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - fm = splitting_vanleer_haenel(u, Val{:minus}(), orientation, equations) - fp = splitting_vanleer_haenel(u, Val{:plus}(), orientation, equations) - return fm, fp -end - -@inline function splitting_vanleer_haenel(u, ::Val{:plus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - - if orientation == 1 - M = v1 / a - p_plus = 0.5 * (1 + equations.gamma * M) * p - - f1p = 0.25 * rho * a * (M + 1)^2 - f2p = f1p * v1 + p_plus - f3p = f1p * v2 - f4p = f1p * H - else # orientation == 2 - M = v2 / a - p_plus = 0.5 * (1 + equations.gamma * M) * p - - f1p = 0.25 * rho * a * (M + 1)^2 - f2p = f1p * v1 - f3p = f1p * v2 + p_plus - f4p = f1p * H + + @inline function splitting_lax_friedrichs(u, ::Val{:minus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) + + if orientation == 1 + #lambda = 0.5 * (abs(v1) + a) + f1m = 0.5 * rho * v1 - lambda * u[1] + f2m = 0.5 * rho * v1 * v1 + 0.5 * p - lambda * u[2] + f3m = 0.5 * rho * v1 * v2 - lambda * u[3] + f4m = 0.5 * rho * v1 * H - lambda * u[4] + else # orientation == 2 + #lambda = 0.5 * (abs(v2) + a) + f1m = 0.5 * rho * v2 - lambda * u[1] + f2m = 0.5 * rho * v2 * v1 - lambda * u[2] + f3m = 0.5 * rho * v2 * v2 + 0.5 * p - lambda * u[3] + f4m = 0.5 * rho * v2 * H - lambda * u[4] + end + return SVector(f1m, f2m, f3m, f4m) end - return SVector(f1p, f2p, f3p, f4p) -end - -@inline function splitting_vanleer_haenel(u, ::Val{:minus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - - if orientation == 1 - M = v1 / a - p_minus = 0.5 * (1 - equations.gamma * M) * p - - f1m = -0.25 * rho * a * (M - 1)^2 - f2m = f1m * v1 + p_minus - f3m = f1m * v2 - f4m = f1m * H - else # orientation == 2 - M = v2 / a - p_minus = 0.5 * (1 - equations.gamma * M) * p - - f1m = -0.25 * rho * a * (M - 1)^2 - f2m = f1m * v1 - f3m = f1m * v2 + p_minus - f4m = f1m * H + + # Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the + # maximum velocity magnitude plus the maximum speed of sound + @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Get the velocity value in the appropriate direction + if orientation == 1 + v_ll = v1_ll + v_rr = v1_rr + else # orientation == 2 + v_ll = v2_ll + v_rr = v2_rr + end + # Calculate sound speeds + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + λ_max = max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) end - return SVector(f1m, f2m, f3m, f4m) -end - -""" - splitting_lax_friedrichs(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) - -Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)` -and `f⁻ = 0.5 (f - λ u)` similar to a flux splitting one would apply, e.g., -to Burgers' equation. - -Returns a tuple of the fluxes "minus" (associated with waves going into the -negative axis direction) and "plus" (associated with waves going into the -positive axis direction). If only one of the fluxes is required, use the -function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. - -!!! warning "Experimental implementation (upwind SBP)" - This is an experimental feature and may change in future releases. -""" -@inline function splitting_lax_friedrichs(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - fm = splitting_lax_friedrichs(u, Val{:minus}(), orientation, equations) - fp = splitting_lax_friedrichs(u, Val{:plus}(), orientation, equations) - return fm, fp -end - -@inline function splitting_lax_friedrichs(u, ::Val{:plus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) - - if orientation == 1 - #lambda = 0.5 * (abs(v1) + a) - f1p = 0.5 * rho * v1 + lambda * u[1] - f2p = 0.5 * rho * v1 * v1 + 0.5 * p + lambda * u[2] - f3p = 0.5 * rho * v1 * v2 + lambda * u[3] - f4p = 0.5 * rho * v1 * H + lambda * u[4] - else # orientation == 2 - #lambda = 0.5 * (abs(v2) + a) - f1p = 0.5 * rho * v2 + lambda * u[1] - f2p = 0.5 * rho * v2 * v1 + lambda * u[2] - f3p = 0.5 * rho * v2 * v2 + 0.5 * p + lambda * u[3] - f4p = 0.5 * rho * v2 * H + lambda * u[4] + + @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Calculate normal velocities and sound speed + # left + v_ll = (v1_ll * normal_direction[1] + + + v2_ll * normal_direction[2]) + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + # right + v_rr = (v1_rr * normal_direction[1] + + + v2_rr * normal_direction[2]) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) end - return SVector(f1p, f2p, f3p, f4p) -end - -@inline function splitting_lax_friedrichs(u, ::Val{:minus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) - - if orientation == 1 - #lambda = 0.5 * (abs(v1) + a) - f1m = 0.5 * rho * v1 - lambda * u[1] - f2m = 0.5 * rho * v1 * v1 + 0.5 * p - lambda * u[2] - f3m = 0.5 * rho * v1 * v2 - lambda * u[3] - f4m = 0.5 * rho * v1 * H - lambda * u[4] - else # orientation == 2 - #lambda = 0.5 * (abs(v2) + a) - f1m = 0.5 * rho * v2 - lambda * u[1] - f2m = 0.5 * rho * v2 * v1 - lambda * u[2] - f3m = 0.5 * rho * v2 * v2 + 0.5 * p - lambda * u[3] - f4m = 0.5 * rho * v2 * H - lambda * u[4] + + # Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes + @inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + if orientation == 1 # x-direction + λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll) + λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr) + else # y-direction + λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll) + λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr) + end + + return λ_min, λ_max end - return SVector(f1m, f2m, f3m, f4m) -end - -# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the -# maximum velocity magnitude plus the maximum speed of sound -@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Get the velocity value in the appropriate direction - if orientation == 1 - v_ll = v1_ll - v_rr = v1_rr - else # orientation == 2 - v_ll = v2_ll - v_rr = v2_rr + + @inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + norm_ = norm(normal_direction) + # The v_normals are already scaled by the norm + λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_ + λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_ + + return λ_min, λ_max end - # Calculate sound speeds - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - λ_max = max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) -end - -@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Calculate normal velocities and sound speed - # left - v_ll = (v1_ll * normal_direction[1] - + - v2_ll * normal_direction[2]) - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - # right - v_rr = (v1_rr * normal_direction[1] - + - v2_rr * normal_direction[2]) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) -end - -# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes -@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - if orientation == 1 # x-direction - λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll) - λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr) - else # y-direction - λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll) - λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr) + + # More refined estimates for minimum and maximum wave speeds for HLL-type fluxes + @inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + if orientation == 1 # x-direction + λ_min = min(v1_ll - c_ll, v1_rr - c_rr) + λ_max = max(v1_ll + c_ll, v1_rr + c_rr) + else # y-direction + λ_min = min(v2_ll - c_ll, v2_rr - c_rr) + λ_max = max(v2_ll + c_ll, v2_rr + c_rr) + end + + return λ_min, λ_max end - - return λ_min, λ_max -end - -@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - norm_ = norm(normal_direction) - # The v_normals are already scaled by the norm - λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_ - λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_ - - return λ_min, λ_max -end - -# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes -@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - if orientation == 1 # x-direction - λ_min = min(v1_ll - c_ll, v1_rr - c_rr) - λ_max = max(v1_ll + c_ll, v1_rr + c_rr) - else # y-direction - λ_min = min(v2_ll - c_ll, v2_rr - c_rr) - λ_max = max(v2_ll + c_ll, v2_rr + c_rr) + + # More refined estimates for minimum and maximum wave speeds for HLL-type fluxes + @inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + norm_ = norm(normal_direction) + + c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_ + c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_ + + v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # The v_normals are already scaled by the norm + λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr) + λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr) + + return λ_min, λ_max end - - return λ_min, λ_max -end - -# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes -@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - norm_ = norm(normal_direction) - - c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_ - c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_ - - v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # The v_normals are already scaled by the norm - λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr) - λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr) - - return λ_min, λ_max -end - -# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction -# has been normalized prior to this rotation of the state vector -@inline function rotate_to_x(u, normal_vector, equations::CompressibleEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 n_2 0; - # 0 t_1 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] + s * u[3], - -s * u[2] + c * u[3], - u[4]) -end - -# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction -# has been normalized prior to this back-rotation of the state vector -@inline function rotate_from_x(u, normal_vector, - equations::CompressibleEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D back-rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 t_1 0; - # 0 n_2 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] - s * u[3], - s * u[2] + c * u[3], - u[4]) -end - -""" - flux_hllc(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) - -Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro -[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf) -Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140) -""" -function flux_hllc(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Calculate primitive variables and speed of sound - rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr = u_rr - - v1_ll = rho_v1_ll / rho_ll - v2_ll = rho_v2_ll / rho_ll - e_ll = rho_e_ll / rho_ll - p_ll = (equations.gamma - 1) * (rho_e_ll - 1 / 2 * rho_ll * (v1_ll^2 + v2_ll^2)) - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - - v1_rr = rho_v1_rr / rho_rr - v2_rr = rho_v2_rr / rho_rr - e_rr = rho_e_rr / rho_rr - p_rr = (equations.gamma - 1) * (rho_e_rr - 1 / 2 * rho_rr * (v1_rr^2 + v2_rr^2)) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - # Obtain left and right fluxes - f_ll = flux(u_ll, orientation, equations) - f_rr = flux(u_rr, orientation, equations) - - # Compute Roe averages - sqrt_rho_ll = sqrt(rho_ll) - sqrt_rho_rr = sqrt(rho_rr) - sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr - if orientation == 1 # x-direction - vel_L = v1_ll - vel_R = v1_rr - ekin_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr)^2 - elseif orientation == 2 # y-direction - vel_L = v2_ll - vel_R = v2_rr - ekin_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr)^2 + + # Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction + # has been normalized prior to this rotation of the state vector + @inline function rotate_to_x(u, normal_vector, equations::CompressibleEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 n_2 0; + # 0 t_1 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] + s * u[3], + -s * u[2] + c * u[3], + u[4]) end - vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho - ekin_roe = 0.5 * (vel_roe^2 + ekin_roe / sum_sqrt_rho^2) - H_ll = (rho_e_ll + p_ll) / rho_ll - H_rr = (rho_e_rr + p_rr) / rho_rr - H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho - c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe)) - Ssl = min(vel_L - c_ll, vel_roe - c_roe) - Ssr = max(vel_R + c_rr, vel_roe + c_roe) - sMu_L = Ssl - vel_L - sMu_R = Ssr - vel_R - - if Ssl >= 0.0 - f1 = f_ll[1] - f2 = f_ll[2] - f3 = f_ll[3] - f4 = f_ll[4] - elseif Ssr <= 0.0 - f1 = f_rr[1] - f2 = f_rr[2] - f3 = f_rr[3] - f4 = f_rr[4] - else - SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) / - (rho_ll * sMu_L - rho_rr * sMu_R) - if Ssl <= 0.0 <= SStar - densStar = rho_ll * sMu_L / (Ssl - SStar) - enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L)) - UStar1 = densStar - UStar4 = densStar * enerStar - if orientation == 1 # x-direction - UStar2 = densStar * SStar - UStar3 = densStar * v2_ll - elseif orientation == 2 # y-direction - UStar2 = densStar * v1_ll - UStar3 = densStar * SStar - end - f1 = f_ll[1] + Ssl * (UStar1 - rho_ll) - f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll) - f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll) - f4 = f_ll[4] + Ssl * (UStar4 - rho_e_ll) + + # Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction + # has been normalized prior to this back-rotation of the state vector + @inline function rotate_from_x(u, normal_vector, + equations::CompressibleEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D back-rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 t_1 0; + # 0 n_2 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] - s * u[3], + s * u[2] + c * u[3], + u[4]) + end + + """ + flux_hllc(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) + + Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro + [Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf) + Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140) + """ + function flux_hllc(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Calculate primitive variables and speed of sound + rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + v2_ll = rho_v2_ll / rho_ll + e_ll = rho_e_ll / rho_ll + p_ll = (equations.gamma - 1) * (rho_e_ll - 1 / 2 * rho_ll * (v1_ll^2 + v2_ll^2)) + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + + v1_rr = rho_v1_rr / rho_rr + v2_rr = rho_v2_rr / rho_rr + e_rr = rho_e_rr / rho_rr + p_rr = (equations.gamma - 1) * (rho_e_rr - 1 / 2 * rho_rr * (v1_rr^2 + v2_rr^2)) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + # Obtain left and right fluxes + f_ll = flux(u_ll, orientation, equations) + f_rr = flux(u_rr, orientation, equations) + + # Compute Roe averages + sqrt_rho_ll = sqrt(rho_ll) + sqrt_rho_rr = sqrt(rho_rr) + sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr + if orientation == 1 # x-direction + vel_L = v1_ll + vel_R = v1_rr + ekin_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr)^2 + elseif orientation == 2 # y-direction + vel_L = v2_ll + vel_R = v2_rr + ekin_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr)^2 + end + vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho + ekin_roe = 0.5 * (vel_roe^2 + ekin_roe / sum_sqrt_rho^2) + H_ll = (rho_e_ll + p_ll) / rho_ll + H_rr = (rho_e_rr + p_rr) / rho_rr + H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho + c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe)) + Ssl = min(vel_L - c_ll, vel_roe - c_roe) + Ssr = max(vel_R + c_rr, vel_roe + c_roe) + sMu_L = Ssl - vel_L + sMu_R = Ssr - vel_R + + if Ssl >= 0.0 + f1 = f_ll[1] + f2 = f_ll[2] + f3 = f_ll[3] + f4 = f_ll[4] + elseif Ssr <= 0.0 + f1 = f_rr[1] + f2 = f_rr[2] + f3 = f_rr[3] + f4 = f_rr[4] else - densStar = rho_rr * sMu_R / (Ssr - SStar) - enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R)) - UStar1 = densStar - UStar4 = densStar * enerStar - if orientation == 1 # x-direction - UStar2 = densStar * SStar - UStar3 = densStar * v2_rr - elseif orientation == 2 # y-direction - UStar2 = densStar * v1_rr - UStar3 = densStar * SStar + SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) / + (rho_ll * sMu_L - rho_rr * sMu_R) + if Ssl <= 0.0 <= SStar + densStar = rho_ll * sMu_L / (Ssl - SStar) + enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L)) + UStar1 = densStar + UStar4 = densStar * enerStar + if orientation == 1 # x-direction + UStar2 = densStar * SStar + UStar3 = densStar * v2_ll + elseif orientation == 2 # y-direction + UStar2 = densStar * v1_ll + UStar3 = densStar * SStar + end + f1 = f_ll[1] + Ssl * (UStar1 - rho_ll) + f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll) + f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll) + f4 = f_ll[4] + Ssl * (UStar4 - rho_e_ll) + else + densStar = rho_rr * sMu_R / (Ssr - SStar) + enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R)) + UStar1 = densStar + UStar4 = densStar * enerStar + if orientation == 1 # x-direction + UStar2 = densStar * SStar + UStar3 = densStar * v2_rr + elseif orientation == 2 # y-direction + UStar2 = densStar * v1_rr + UStar3 = densStar * SStar + end + f1 = f_rr[1] + Ssr * (UStar1 - rho_rr) + f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr) + f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr) + f4 = f_rr[4] + Ssr * (UStar4 - rho_e_rr) end - f1 = f_rr[1] + Ssr * (UStar1 - rho_rr) - f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr) - f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr) - f4 = f_rr[4] + Ssr * (UStar4 - rho_e_rr) end + return SVector(f1, f2, f3, f4) + end + + """ + flux_hlle(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) + + Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations. + Special estimates of the signal velocites and linearization of the Riemann problem developed + by Einfeldt to ensure that the internal energy and density remain positive during the computation + of the numerical flux. + + - Bernd Einfeldt (1988) + On Godunov-type methods for gas dynamics. + [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021) + - Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991) + On Godunov-type methods near low densities. + [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3) + """ + function flux_hlle(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Calculate primitive variables, enthalpy and speed of sound + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # `u_ll[4]` is total energy `rho_e_ll` on the left + H_ll = (u_ll[4] + p_ll) / rho_ll + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + + # `u_rr[4]` is total energy `rho_e_rr` on the right + H_rr = (u_rr[4] + p_rr) / rho_rr + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + # Compute Roe averages + sqrt_rho_ll = sqrt(rho_ll) + sqrt_rho_rr = sqrt(rho_rr) + inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr) + + v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho + v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho + v_roe_mag = v1_roe^2 + v2_roe^2 + + H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho + c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5 * v_roe_mag)) + + # Compute convenience constant for positivity preservation, see + # https://doi.org/10.1016/0021-9991(91)90211-3 + beta = sqrt(0.5 * (equations.gamma - 1) / equations.gamma) + + # Estimate the edges of the Riemann fan (with positivity conservation) + if orientation == 1 # x-direction + SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, zero(v1_roe)) + SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, zero(v1_roe)) + elseif orientation == 2 # y-direction + SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, zero(v2_roe)) + SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, zero(v2_roe)) + end + + if SsL >= 0.0 && SsR > 0.0 + # Positive supersonic speed + f_ll = flux(u_ll, orientation, equations) + + f1 = f_ll[1] + f2 = f_ll[2] + f3 = f_ll[3] + f4 = f_ll[4] + elseif SsR <= 0.0 && SsL < 0.0 + # Negative supersonic speed + f_rr = flux(u_rr, orientation, equations) + + f1 = f_rr[1] + f2 = f_rr[2] + f3 = f_rr[3] + f4 = f_rr[4] + else + # Subsonic case + # Compute left and right fluxes + f_ll = flux(u_ll, orientation, equations) + f_rr = flux(u_rr, orientation, equations) + + f1 = (SsR * f_ll[1] - SsL * f_rr[1] + SsL * SsR * (u_rr[1] - u_ll[1])) / + (SsR - SsL) + f2 = (SsR * f_ll[2] - SsL * f_rr[2] + SsL * SsR * (u_rr[2] - u_ll[2])) / + (SsR - SsL) + f3 = (SsR * f_ll[3] - SsL * f_rr[3] + SsL * SsR * (u_rr[3] - u_ll[3])) / + (SsR - SsL) + f4 = (SsR * f_ll[4] - SsL * f_rr[4] + SsL * SsR * (u_rr[4] - u_ll[4])) / + (SsR - SsL) + end + + return SVector(f1, f2, f3, f4) end - return SVector(f1, f2, f3, f4) -end - -""" - flux_hlle(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) - -Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations. -Special estimates of the signal velocites and linearization of the Riemann problem developed -by Einfeldt to ensure that the internal energy and density remain positive during the computation -of the numerical flux. - -- Bernd Einfeldt (1988) - On Godunov-type methods for gas dynamics. - [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021) -- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991) - On Godunov-type methods near low densities. - [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3) -""" -function flux_hlle(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Calculate primitive variables, enthalpy and speed of sound - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # `u_ll[4]` is total energy `rho_e_ll` on the left - H_ll = (u_ll[4] + p_ll) / rho_ll - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - - # `u_rr[4]` is total energy `rho_e_rr` on the right - H_rr = (u_rr[4] + p_rr) / rho_rr - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - # Compute Roe averages - sqrt_rho_ll = sqrt(rho_ll) - sqrt_rho_rr = sqrt(rho_rr) - inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr) - - v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho - v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho - v_roe_mag = v1_roe^2 + v2_roe^2 - - H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho - c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5 * v_roe_mag)) - - # Compute convenience constant for positivity preservation, see - # https://doi.org/10.1016/0021-9991(91)90211-3 - beta = sqrt(0.5 * (equations.gamma - 1) / equations.gamma) - - # Estimate the edges of the Riemann fan (with positivity conservation) - if orientation == 1 # x-direction - SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, zero(v1_roe)) - SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, zero(v1_roe)) - elseif orientation == 2 # y-direction - SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, zero(v2_roe)) - SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, zero(v2_roe)) + + @inline function max_abs_speeds(u, equations::CompressibleEulerEquations2D) + rho, v1, v2, p = cons2prim(u, equations) + c = sqrt(equations.gamma * p / rho) + + return abs(v1) + c, abs(v2) + c end - - if SsL >= 0.0 && SsR > 0.0 - # Positive supersonic speed - f_ll = flux(u_ll, orientation, equations) - - f1 = f_ll[1] - f2 = f_ll[2] - f3 = f_ll[3] - f4 = f_ll[4] - elseif SsR <= 0.0 && SsL < 0.0 - # Negative supersonic speed - f_rr = flux(u_rr, orientation, equations) - - f1 = f_rr[1] - f2 = f_rr[2] - f3 = f_rr[3] - f4 = f_rr[4] - else - # Subsonic case - # Compute left and right fluxes - f_ll = flux(u_ll, orientation, equations) - f_rr = flux(u_rr, orientation, equations) - - f1 = (SsR * f_ll[1] - SsL * f_rr[1] + SsL * SsR * (u_rr[1] - u_ll[1])) / - (SsR - SsL) - f2 = (SsR * f_ll[2] - SsL * f_rr[2] + SsL * SsR * (u_rr[2] - u_ll[2])) / - (SsR - SsL) - f3 = (SsR * f_ll[3] - SsL * f_rr[3] + SsL * SsR * (u_rr[3] - u_ll[3])) / - (SsR - SsL) - f4 = (SsR * f_ll[4] - SsL * f_rr[4] + SsL * SsR * (u_rr[4] - u_ll[4])) / - (SsR - SsL) + + # Convert conservative variables to primitive + @inline function cons2prim(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + return SVector(rho, v1, v2, p) + end + + # Convert conservative variables to entropy + @inline function cons2entropy(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + v_square = v1^2 + v2^2 + p = (equations.gamma - 1) * (rho_e - 0.5 * rho * v_square) + s = log(p) - equations.gamma * log(rho) + rho_p = rho / p + + w1 = (equations.gamma - s) * equations.inv_gamma_minus_one - 0.5 * rho_p * v_square + w2 = rho_p * v1 + w3 = rho_p * v2 + w4 = -rho_p + + return SVector(w1, w2, w3, w4) + end + + @inline function entropy2cons(w, equations::CompressibleEulerEquations2D) + # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD + # [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1) + @unpack gamma = equations + + # convert to entropy `-rho * s` used by Hughes, France, Mallet (1986) + # instead of `-rho * s / (gamma - 1)` + V1, V2, V3, V5 = w .* (gamma - 1) + + # s = specific entropy, eq. (53) + s = gamma - V1 + (V2^2 + V3^2) / (2 * V5) + + # eq. (52) + rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) * + exp(-s * equations.inv_gamma_minus_one) + + # eq. (51) + rho = -rho_iota * V5 + rho_v1 = rho_iota * V2 + rho_v2 = rho_iota * V3 + rho_e = rho_iota * (1 - (V2^2 + V3^2) / (2 * V5)) + return SVector(rho, rho_v1, rho_v2, rho_e) + end + + # Convert primitive to conservative variables + @inline function prim2cons(prim, equations::CompressibleEulerEquations2D) + rho, v1, v2, p = prim + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_e = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) + return SVector(rho, rho_v1, rho_v2, rho_e) + end + + @inline function density(u, equations::CompressibleEulerEquations2D) + rho = u[1] + return rho + end + + @inline function pressure(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) + return p + end + + @inline function density_pressure(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + rho_times_p = (equations.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2 + rho_v2^2)) + return rho_times_p + end + + # Calculates the entropy flux in direction "orientation" and the entropy variables for a state cons + # NOTE: This method seems to work currently (b82534e) but is never used anywhere. Thus it is + # commented here until someone uses it or writes a test for it. + # @inline function cons2entropyvars_and_flux(gamma::Float64, cons, orientation::Int) + # entropy = MVector{4, Float64}(undef) + # v = (cons[2] / cons[1] , cons[3] / cons[1]) + # v_square= v[1]*v[1]+v[2]*v[2] + # p = (gamma - 1) * (cons[4] - 1/2 * (cons[2] * v[1] + cons[3] * v[2])) + # rho_p = cons[1] / p + # # thermodynamic entropy + # s = log(p) - gamma*log(cons[1]) + # # mathematical entropy + # S = - s*cons[1]/(gamma-1) + # # entropy variables + # entropy[1] = (gamma - s)/(gamma-1) - 0.5*rho_p*v_square + # entropy[2] = rho_p*v[1] + # entropy[3] = rho_p*v[2] + # entropy[4] = -rho_p + # # entropy flux + # entropy_flux = S*v[orientation] + # return entropy, entropy_flux + # end + + # Calculate thermodynamic entropy for a conservative state `cons` + @inline function entropy_thermodynamic(cons, equations::CompressibleEulerEquations2D) + # Pressure + p = (equations.gamma - 1) * (cons[4] - 1 / 2 * (cons[2]^2 + cons[3]^2) / cons[1]) + + # Thermodynamic entropy + s = log(p) - equations.gamma * log(cons[1]) + + return s + end + + # Calculate mathematical entropy for a conservative state `cons` + @inline function entropy_math(cons, equations::CompressibleEulerEquations2D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equations) * cons[1] * + equations.inv_gamma_minus_one + + return S + end + + # Default entropy is the mathematical entropy + @inline function entropy(cons, equations::CompressibleEulerEquations2D) + entropy_math(cons, equations) + end + + # Calculate total energy for a conservative state `cons` + @inline energy_total(cons, ::CompressibleEulerEquations2D) = cons[4] + + # Calculate kinetic energy for a conservative state `cons` + @inline function energy_kinetic(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + return (rho_v1^2 + rho_v2^2) / (2 * rho) + end + + # Calculate internal energy for a conservative state `cons` + @inline function energy_internal(cons, equations::CompressibleEulerEquations2D) + return energy_total(cons, equations) - energy_kinetic(cons, equations) end - - return SVector(f1, f2, f3, f4) -end - -@inline function max_abs_speeds(u, equations::CompressibleEulerEquations2D) - rho, v1, v2, p = cons2prim(u, equations) - c = sqrt(equations.gamma * p / rho) - - return abs(v1) + c, abs(v2) + c -end - -# Convert conservative variables to primitive -@inline function cons2prim(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - return SVector(rho, v1, v2, p) -end - -# Convert conservative variables to entropy -@inline function cons2entropy(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - v_square = v1^2 + v2^2 - p = (equations.gamma - 1) * (rho_e - 0.5 * rho * v_square) - s = log(p) - equations.gamma * log(rho) - rho_p = rho / p - - w1 = (equations.gamma - s) * equations.inv_gamma_minus_one - 0.5 * rho_p * v_square - w2 = rho_p * v1 - w3 = rho_p * v2 - w4 = -rho_p - - return SVector(w1, w2, w3, w4) -end - -@inline function entropy2cons(w, equations::CompressibleEulerEquations2D) - # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD - # [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1) - @unpack gamma = equations - - # convert to entropy `-rho * s` used by Hughes, France, Mallet (1986) - # instead of `-rho * s / (gamma - 1)` - V1, V2, V3, V5 = w .* (gamma - 1) - - # s = specific entropy, eq. (53) - s = gamma - V1 + (V2^2 + V3^2) / (2 * V5) - - # eq. (52) - rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) * - exp(-s * equations.inv_gamma_minus_one) - - # eq. (51) - rho = -rho_iota * V5 - rho_v1 = rho_iota * V2 - rho_v2 = rho_iota * V3 - rho_e = rho_iota * (1 - (V2^2 + V3^2) / (2 * V5)) - return SVector(rho, rho_v1, rho_v2, rho_e) -end - -# Convert primitive to conservative variables -@inline function prim2cons(prim, equations::CompressibleEulerEquations2D) - rho, v1, v2, p = prim - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_e = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) - return SVector(rho, rho_v1, rho_v2, rho_e) -end - -@inline function density(u, equations::CompressibleEulerEquations2D) - rho = u[1] - return rho -end - -@inline function v1(u, equations::CompressibleEulerEquations2D) - rho = u[1] - rho_v1 = u[2] - return rho_v1/rho -end - -@inline function pressure(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) - return p -end - -@inline function density_pressure(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - rho_times_p = (equations.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2 + rho_v2^2)) - return rho_times_p -end - -# Calculates the entropy flux in direction "orientation" and the entropy variables for a state cons -# NOTE: This method seems to work currently (b82534e) but is never used anywhere. Thus it is -# commented here until someone uses it or writes a test for it. -# @inline function cons2entropyvars_and_flux(gamma::Float64, cons, orientation::Int) -# entropy = MVector{4, Float64}(undef) -# v = (cons[2] / cons[1] , cons[3] / cons[1]) -# v_square= v[1]*v[1]+v[2]*v[2] -# p = (gamma - 1) * (cons[4] - 1/2 * (cons[2] * v[1] + cons[3] * v[2])) -# rho_p = cons[1] / p -# # thermodynamic entropy -# s = log(p) - gamma*log(cons[1]) -# # mathematical entropy -# S = - s*cons[1]/(gamma-1) -# # entropy variables -# entropy[1] = (gamma - s)/(gamma-1) - 0.5*rho_p*v_square -# entropy[2] = rho_p*v[1] -# entropy[3] = rho_p*v[2] -# entropy[4] = -rho_p -# # entropy flux -# entropy_flux = S*v[orientation] -# return entropy, entropy_flux -# end - -# Calculate thermodynamic entropy for a conservative state `cons` -@inline function entropy_thermodynamic(cons, equations::CompressibleEulerEquations2D) - # Pressure - p = (equations.gamma - 1) * (cons[4] - 1 / 2 * (cons[2]^2 + cons[3]^2) / cons[1]) - - # Thermodynamic entropy - s = log(p) - equations.gamma * log(cons[1]) - - return s -end - -# Calculate mathematical entropy for a conservative state `cons` -@inline function entropy_math(cons, equations::CompressibleEulerEquations2D) - # Mathematical entropy - S = -entropy_thermodynamic(cons, equations) * cons[1] * - equations.inv_gamma_minus_one - - return S -end - -# Default entropy is the mathematical entropy -@inline function entropy(cons, equations::CompressibleEulerEquations2D) - entropy_math(cons, equations) -end - -# Calculate total energy for a conservative state `cons` -@inline energy_total(cons, ::CompressibleEulerEquations2D) = cons[4] - -# Calculate kinetic energy for a conservative state `cons` -@inline function energy_kinetic(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - return (rho_v1^2 + rho_v2^2) / (2 * rho) -end - -# Calculate internal energy for a conservative state `cons` -@inline function energy_internal(cons, equations::CompressibleEulerEquations2D) - return energy_total(cons, equations) - energy_kinetic(cons, equations) -end -end # @muladd + end # @muladd + \ No newline at end of file From 95cd8358e5d54a33c76e9a4ab24e2425b35d838a Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 14:48:54 +0200 Subject: [PATCH 28/74] whitespace --- src/solvers/dg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 85136989bf5..66608a5fce5 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -534,7 +534,7 @@ function allocate_coefficients(mesh::AbstractMesh, equations, dg::DG, cache) end @inline function wrap_array(u_ode::AbstractVector, mesh::AbstractMesh, equations, - dg::DGSEM, cache) + dg::DGSEM, cache) @boundscheck begin @assert length(u_ode) == nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache) From 69a0eaae49bfeb71780cf4fa6870bd451d057142 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 14:53:57 +0200 Subject: [PATCH 29/74] Add to authors --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index abaa3e7e037..74bfaa9c852 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -28,6 +28,7 @@ are listed in alphabetical order: * Jesse Chan * Lars Christmann * Christof Czernik +* Daniel Doehring * Patrick Ersing * Erik Faulhaber * Gregor Gassner From d7a07b4e75eb40a545676bdcd8587712c6a30f92 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 24 Jul 2023 17:14:04 +0200 Subject: [PATCH 30/74] Parabolic AMR in 2D --- .../elixir_advection_diffusion_amr.jl | 9 +- .../elixir_advection_diffusion_amr.jl | 97 +++++++++++++++++++ src/callbacks_step/amr_dg2d.jl | 30 ++++++ src/solvers/dgsem_tree/cache_viscous.jl | 67 +++++++++++++ src/solvers/dgsem_tree/dg.jl | 3 + src/solvers/dgsem_tree/dg_1d_parabolic.jl | 41 +------- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 13 ++- 7 files changed, 215 insertions(+), 45 deletions(-) create mode 100644 examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl create mode 100644 src/solvers/dgsem_tree/cache_viscous.jl diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl index 6bdfd6907ec..2ac3fd88fbf 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl @@ -1,5 +1,5 @@ -using OrdinaryDiffEq +using OrdinaryDiffEq, Plots using Trixi ############################################################################### @@ -80,7 +80,7 @@ amr_controller = ControllerThreeLevel(semi, amr_callback = AMRCallback(semi, amr_controller, interval=5, - adapt_initial_condition=true) + adapt_initial_condition=true) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback) @@ -96,4 +96,7 @@ sol = solve(ode, KenCarp4(autodiff=false), abstol=time_abs_tol, reltol=time_int_ save_everystep=false, callback=callbacks) # Print the timer summary -summary_callback() \ No newline at end of file +summary_callback() +plot(sol) +pd = PlotData1D(sol) +plot!(getmesh(pd)) \ No newline at end of file diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl new file mode 100644 index 00000000000..8f5ba476788 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl @@ -0,0 +1,97 @@ +using OrdinaryDiffEq, Plots +using Trixi + +############################################################################### +# semidiscretization of the linear advection-diffusion equation + +advection_velocity = (1.5, 1.0) +equations = LinearScalarAdvectionEquation2D(advection_velocity) +diffusivity() = 5.0e-2 +equations_parabolic = LaplaceDiffusion2D(diffusivity(), equations) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) +coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y)) + +# Create a uniformly refined mesh with periodic boundaries +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level=4, + periodicity=true, + n_cells_max=30_000) # set maximum capacity of tree data structure + +# Define initial condition +function initial_condition_diffusive_convergence_test(x, t, equation::LinearScalarAdvectionEquation2D) + # Store translated coordinate for easy use of exact solution + x_trans = x - equation.advection_velocity * t + + nu = diffusivity() + c = 1.0 + A = 0.5 + L = 2 + f = 1/L + omega = 2 * pi * f + scalar = c + A * sin(omega * sum(x_trans)) * exp(-2 * nu * omega^2 * t) + return SVector(scalar) +end +initial_condition = initial_condition_diffusive_convergence_test + +# define periodic boundary conditions everywhere +boundary_conditions = boundary_condition_periodic +boundary_conditions_parabolic = boundary_condition_periodic + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolicParabolic(mesh, + (equations, equations_parabolic), + initial_condition, solver; + boundary_conditions=(boundary_conditions, + boundary_conditions_parabolic)) + + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.5 +tspan = (0.0, 0.4) +ode = semidiscretize(semi, tspan; split_form = false) + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +# The AliveCallback prints short status information in regular intervals +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +amr_controller = ControllerThreeLevel(semi, + IndicatorMax(semi, variable=first), + base_level=3, + med_level=4, med_threshold=0.8, + max_level=5, max_threshold=1.2) + +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback) + + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +alg = RDPK3SpFSAL49() +time_int_tol = 1.0e-11 +sol = solve(ode, alg; abstol=time_int_tol, reltol=time_int_tol, + ode_default_options()..., callback=callbacks) + +# Print the timer summary +summary_callback() +plot(sol) +pd = PlotData2D(sol) +plot!(getmesh(pd)) \ No newline at end of file diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index c253c8eb2d9..097e76d761c 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -166,6 +166,21 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estM nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + @unpack cache_viscous = cache_parabolic + resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._u_transformed), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + for dim in 1:2 + cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._gradients[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + end + # Loop over all elements in old container and either copy them or refine them element_id = 1 for old_element_id in 1:old_n_elements @@ -373,6 +388,21 @@ function coarsen!(u_ode::AbstractVector, adaptor, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + @unpack cache_viscous = cache_parabolic + resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._u_transformed), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + for dim in 1:2 + cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._gradients[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + end + # Loop over all elements in old container and either copy them or coarsen them skip = 0 element_id = 1 diff --git a/src/solvers/dgsem_tree/cache_viscous.jl b/src/solvers/dgsem_tree/cache_viscous.jl new file mode 100644 index 00000000000..a142e632bf6 --- /dev/null +++ b/src/solvers/dgsem_tree/cache_viscous.jl @@ -0,0 +1,67 @@ +mutable struct CacheViscous1D{uEltype <: Real} + u_transformed::Array{uEltype} + gradients::Array{uEltype} + flux_viscous::Array{uEltype} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + _gradients::Vector{uEltype} + _flux_viscous::Vector{uEltype} + + function CacheViscous1D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} + new(Array{uEltype}(undef, n_vars, n_nodes, n_elements), + Array{uEltype}(undef, n_vars, n_nodes, n_elements), + Array{uEltype}(undef, n_vars, n_nodes, n_elements), + Vector{uEltype}(undef, n_vars*n_nodes*n_elements), + Vector{uEltype}(undef, n_vars*n_nodes*n_elements), + Vector{uEltype}(undef, n_vars*n_nodes*n_elements)) + end +end + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(cache_viscous::CacheViscous1D, capacity) + resize!(cache_viscous._u_transformed, capacity) + resize!(cache_viscous._gradients, capacity) + resize!(cache_viscous._flux_viscous, capacity) + + return nothing +end + +mutable struct CacheViscous2D{uEltype <: Real} + u_transformed::Array{uEltype} + gradients::Vector{Array{uEltype}} + flux_viscous::Vector{Array{uEltype}} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + _gradients::Vector{Vector{uEltype}} + _flux_viscous::Vector{Vector{uEltype}} + + function CacheViscous2D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} + new(Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements), + [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], + [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], + Vector{uEltype}(undef, n_vars * n_nodes * n_nodes * n_elements), + [Vector{uEltype}(undef, n_vars * n_nodes * n_nodes * n_elements) for _ in 1:2], + [Vector{uEltype}(undef, n_vars * n_nodes * n_nodes * n_elements) for _ in 1:2]) + end +end + +# Only one-dimensional `Array`s are `resize!`able in Julia. +# Hence, we use `Vector`s as internal storage and `resize!` +# them whenever needed. Then, we reuse the same memory by +# `unsafe_wrap`ping multi-dimensional `Array`s around the +# internal storage. +function Base.resize!(cache_viscous::CacheViscous2D, capacity) + resize!(cache_viscous._u_transformed, capacity) + for dim in 1:2 + resize!(cache_viscous._gradients[dim], capacity) + resize!(cache_viscous._flux_viscous[dim], capacity) + end + + return nothing +end \ No newline at end of file diff --git a/src/solvers/dgsem_tree/dg.jl b/src/solvers/dgsem_tree/dg.jl index cb28dad968c..fa2e866b709 100644 --- a/src/solvers/dgsem_tree/dg.jl +++ b/src/solvers/dgsem_tree/dg.jl @@ -54,6 +54,9 @@ include("containers.jl") # Dimension-agnostic parallel setup include("dg_parallel.jl") +# Helper struct for parabolic dg +include("cache_viscous.jl") + # 1D DG implementation include("dg_1d.jl") include("dg_1d_parabolic.jl") diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl index 374c2491e4d..3d15b595bfa 100644 --- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl @@ -536,17 +536,14 @@ function create_cache_parabolic(mesh::TreeMesh{1}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) - u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_elements) - gradients = similar(u_transformed) - flux_viscous = similar(u_transformed) - cache_viscous = CacheViscous{uEltype}(n_vars, n_nodes, n_elements) + cache_viscous = CacheViscous1D{uEltype}(n_vars, n_nodes, n_elements) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) boundaries = init_boundaries(leaf_cell_ids, mesh, elements) - cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed, cache_viscous) + cache = (; elements, interfaces, boundaries, cache_viscous) return cache end @@ -570,38 +567,4 @@ function apply_jacobian_parabolic!(du, mesh::TreeMesh{1}, return nothing end -mutable struct CacheViscous{uEltype <: Real} - u_transformed::Array{uEltype} - gradients::Array{uEltype} - flux_viscous::Array{uEltype} - - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - _gradients::Vector{uEltype} - _flux_viscous::Vector{uEltype} - - function CacheViscous{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype}(undef, n_vars, n_nodes, n_elements), - Array{uEltype}(undef, n_vars, n_nodes, n_elements), - Array{uEltype}(undef, n_vars, n_nodes, n_elements), - Vector{uEltype}(undef, n_vars*n_nodes*n_elements), - Vector{uEltype}(undef, n_vars*n_nodes*n_elements), - Vector{uEltype}(undef, n_vars*n_nodes*n_elements)) - end -end - -# Only one-dimensional `Array`s are `resize!`able in Julia. -# Hence, we use `Vector`s as internal storage and `resize!` -# them whenever needed. Then, we reuse the same memory by -# `unsafe_wrap`ping multi-dimensional `Array`s around the -# internal storage. -function Base.resize!(cache_viscous::CacheViscous, capacity) - resize!(cache_viscous._u_transformed, capacity) - resize!(cache_viscous._gradients, capacity) - resize!(cache_viscous._flux_viscous, capacity) - - return nothing -end - - end # @muladd diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 6cc229ff803..71eb9e6002c 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -17,7 +17,9 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - (; u_transformed, gradients, flux_viscous) = cache_parabolic + #(; u_transformed, gradients, flux_viscous) = cache_parabolic + @unpack cache_viscous = cache_parabolic + @unpack u_transformed, gradients, flux_viscous = cache_viscous # Convert conservative variables to a form more suitable for viscous flux calculations @trixi_timeit timer() "transform variables" begin @@ -599,7 +601,8 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end -function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray}, +function prolong2mortars!(cache, #flux_viscous::Tuple{AbstractArray, AbstractArray}, + flux_viscous::Vector{Array{Float64}}, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, surface_integral, @@ -997,9 +1000,12 @@ function create_cache_parabolic(mesh::TreeMesh{2}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) + #= u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) gradients = ntuple(_ -> similar(u_transformed), ndims(mesh)) flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh)) + =# + cache_viscous = CacheViscous2D{uEltype}(n_vars, n_nodes, n_elements) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) @@ -1008,7 +1014,8 @@ function create_cache_parabolic(mesh::TreeMesh{2}, # mortars = init_mortars(leaf_cell_ids, mesh, elements, dg.mortar) # cache = (; elements, interfaces, boundaries, mortars) - cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) + #cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) + cache = (; elements, interfaces, boundaries, cache_viscous) # Add specialized parts of the cache required to compute the mortars etc. # cache = (;cache..., create_cache(mesh, equations_parabolic, dg.mortar, uEltype)...) From af9decc63b0e9239ec36dd2924779dd4cfb1631c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 09:40:32 +0200 Subject: [PATCH 31/74] 3D parabolic AMR --- src/callbacks_step/amr_dg1d.jl | 4 +-- src/callbacks_step/amr_dg2d.jl | 6 ++-- src/callbacks_step/amr_dg3d.jl | 32 ++++++++++++++++++++ src/solvers/dgsem_tree/cache_viscous.jl | 36 ++++++++++++++++++----- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 13 ++++++-- 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/callbacks_step/amr_dg1d.jl b/src/callbacks_step/amr_dg1d.jl index 3f73e45a4ef..2521a10ed05 100644 --- a/src/callbacks_step/amr_dg1d.jl +++ b/src/callbacks_step/amr_dg1d.jl @@ -112,7 +112,7 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) - # Resize viscous helpers + # Resize parabolic helper variables resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._u_transformed), @@ -338,7 +338,7 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) - # Resize viscous helpers + # Resize parabolic helper variables resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._u_transformed), diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 097e76d761c..33aa4a9ad4a 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -166,6 +166,7 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estM nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + # Resize parabolic helper variables @unpack cache_viscous = cache_parabolic resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) @@ -388,6 +389,7 @@ function coarsen!(u_ode::AbstractVector, adaptor, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + # Resize parabolic helper variables @unpack cache_viscous = cache_parabolic resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) @@ -396,8 +398,8 @@ function coarsen!(u_ode::AbstractVector, adaptor, (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) for dim in 1:2 cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._gradients[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) + pointer(cache_parabolic.cache_viscous._gradients[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index cfe33ea3bfb..df7ecad57b0 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -100,6 +100,22 @@ function refine!(u_ode::AbstractVector, adaptor, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + # Resize parabolic helper variables + @unpack cache_viscous = cache_parabolic + resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._u_transformed), + (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) + for dim in 1:3 + cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._gradients[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) + end + # Loop over all elements in old container and either copy them or refine them u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) @@ -244,6 +260,22 @@ function coarsen!(u_ode::AbstractVector, adaptor, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) + # Resize parabolic helper variables + @unpack cache_viscous = cache_parabolic + resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._u_transformed), + (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) + for dim in 1:3 + cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._gradients[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, + pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), + (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) + end + # Loop over all elements in old container and either copy them or coarsen them u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) diff --git a/src/solvers/dgsem_tree/cache_viscous.jl b/src/solvers/dgsem_tree/cache_viscous.jl index a142e632bf6..263282ccea0 100644 --- a/src/solvers/dgsem_tree/cache_viscous.jl +++ b/src/solvers/dgsem_tree/cache_viscous.jl @@ -12,9 +12,9 @@ mutable struct CacheViscous1D{uEltype <: Real} new(Array{uEltype}(undef, n_vars, n_nodes, n_elements), Array{uEltype}(undef, n_vars, n_nodes, n_elements), Array{uEltype}(undef, n_vars, n_nodes, n_elements), - Vector{uEltype}(undef, n_vars*n_nodes*n_elements), - Vector{uEltype}(undef, n_vars*n_nodes*n_elements), - Vector{uEltype}(undef, n_vars*n_nodes*n_elements)) + Vector{uEltype}(undef, n_vars * n_nodes * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements)) end end @@ -45,20 +45,40 @@ mutable struct CacheViscous2D{uEltype <: Real} new(Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements), [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], - Vector{uEltype}(undef, n_vars * n_nodes * n_nodes * n_elements), - [Vector{uEltype}(undef, n_vars * n_nodes * n_nodes * n_elements) for _ in 1:2], - [Vector{uEltype}(undef, n_vars * n_nodes * n_nodes * n_elements) for _ in 1:2]) + Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), + [Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements) for _ in 1:2], + [Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements) for _ in 1:2]) end end +mutable struct CacheViscous3D{uEltype <: Real} + u_transformed::Array{uEltype} + gradients::Vector{Array{uEltype}} + flux_viscous::Vector{Array{uEltype}} + + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + _gradients::Vector{Vector{uEltype}} + _flux_viscous::Vector{Vector{uEltype}} + + function CacheViscous3D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} + new(Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), + [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], + [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], + Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), + [Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements) for _ in 1:3], + [Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements) for _ in 1:3]) + end +end + # Only one-dimensional `Array`s are `resize!`able in Julia. # Hence, we use `Vector`s as internal storage and `resize!` # them whenever needed. Then, we reuse the same memory by # `unsafe_wrap`ping multi-dimensional `Array`s around the # internal storage. -function Base.resize!(cache_viscous::CacheViscous2D, capacity) +function Base.resize!(cache_viscous::Union{CacheViscous2D, CacheViscous3D}, capacity) resize!(cache_viscous._u_transformed, capacity) - for dim in 1:2 + for dim in 1:length(cache_viscous._gradients) resize!(cache_viscous._gradients[dim], capacity) resize!(cache_viscous._flux_viscous[dim], capacity) end diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index df1084a5b73..b27d4914295 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -17,7 +17,9 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack u_transformed, gradients, flux_viscous = cache_parabolic + #@unpack u_transformed, gradients, flux_viscous = cache_parabolic + @unpack cache_viscous = cache_parabolic + @unpack u_transformed, gradients, flux_viscous = cache_viscous # Convert conservative variables to a form more suitable for viscous flux calculations @trixi_timeit timer() "transform variables" begin @@ -593,8 +595,11 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra end function prolong2mortars!(cache, + #= flux_viscous::Tuple{AbstractArray, AbstractArray, AbstractArray}, + =# + flux_viscous::Vector{Array{Float64}}, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, @@ -1097,9 +1102,12 @@ function create_cache_parabolic(mesh::TreeMesh{3}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) + #= u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) gradients = ntuple(_ -> similar(u_transformed), ndims(mesh)) flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh)) + =# + cache_viscous = CacheViscous3D{uEltype}(n_vars, n_nodes, n_elements) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) @@ -1108,7 +1116,8 @@ function create_cache_parabolic(mesh::TreeMesh{3}, # mortars = init_mortars(leaf_cell_ids, mesh, elements, dg.mortar) # cache = (; elements, interfaces, boundaries, mortars) - cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) + #cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) + cache = (; elements, interfaces, boundaries, cache_viscous) # Add specialized parts of the cache required to compute the mortars etc. # cache = (;cache..., create_cache(mesh, equations_parabolic, dg.mortar, uEltype)...) From 724fecb45026d766573950da82033b2607d65c8e Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 13:39:05 +0200 Subject: [PATCH 32/74] try 3d amr --- .../elixir_navierstokes_convergence.jl | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl index b32355c48df..6055fac364c 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl @@ -249,13 +249,36 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # Create ODE problem with time span `tspan` tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan) +ode = semidiscretize(semi, tspan; split_form = false) summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=10) analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) -callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) + +#= +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), + base_level=2, + med_level=3, med_threshold=0.1, + max_level=4, max_threshold=0.6) +=# +amr_indicator = IndicatorHennemannGassner(semi, + alpha_max=1.0, + alpha_min=0.0001, + alpha_smooth=false, + variable=Trixi.density) + +amr_controller = ControllerThreeLevel(semi, amr_indicator, + base_level=2, + med_level =3, med_threshold=0.0003, # med_level = current level + max_level =4, max_threshold=0.003) + +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true) + +callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) +#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) ############################################################################### # run the simulation From 491c923d09ba335c03524894d9f59acf1d6ee699 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 15:19:48 +0200 Subject: [PATCH 33/74] Add more explicit dispatch --- src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 125 +++++++++++++++++++-- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 117 ++++++++++++++++++- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 93 +-------------- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 6 +- 4 files changed, 235 insertions(+), 106 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl index 7e90a83a9ca..1c4187c9db5 100644 --- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl @@ -22,6 +22,95 @@ function create_cache_parabolic(mesh::P4estMesh{2}, equations_hyperbolic::Abstra return cache end +function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + initial_condition, boundary_conditions_parabolic, source_terms, + dg::DG, parabolic_scheme, cache, cache_parabolic) + (; u_transformed, gradients, flux_viscous) = cache_parabolic + + # Convert conservative variables to a form more suitable for viscous flux calculations + @trixi_timeit timer() "transform variables" begin + transform_variables!(u_transformed, u, mesh, equations_parabolic, + dg, parabolic_scheme, cache, cache_parabolic) + end + + # Compute the gradients of the transformed variables + @trixi_timeit timer() "calculate gradient" begin + calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, + boundary_conditions_parabolic, dg, cache, cache_parabolic) + end + + # Compute and store the viscous fluxes + @trixi_timeit timer() "calculate viscous fluxes" begin + calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, + equations_parabolic, dg, cache, cache_parabolic) + end + + # The remainder of this function is essentially a regular rhs! for parabolic + # equations (i.e., it computes the divergence of the viscous fluxes) + # + # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have + # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the + # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we + # do not need to recreate the existing data structure only with a different name, and c) we do not + # need to interpolate solutions *and* gradients to the surfaces. + + # TODO: parabolic; reconsider current data structure reuse strategy + + # Reset du + @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) + + # Calculate volume integral + @trixi_timeit timer() "volume integral" begin + calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + end + + # Prolong solution to interfaces + @trixi_timeit timer() "prolong2interfaces" begin + prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate interface fluxes + @trixi_timeit timer() "interface flux" begin + calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, dg, cache_parabolic) + end + + # Prolong solution to boundaries + @trixi_timeit timer() "prolong2boundaries" begin + prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate boundary fluxes + @trixi_timeit timer() "boundary flux" begin + calc_boundary_flux_divergence!(cache_parabolic, t, + boundary_conditions_parabolic, mesh, + equations_parabolic, + dg.surface_integral, dg) + end + + # TODO: parabolic; extend to mortars + @assert nmortars(dg, cache) == 0 + + # Calculate surface integrals + @trixi_timeit timer() "surface integral" begin + calc_surface_integral!(du, u, mesh, equations_parabolic, + dg.surface_integral, dg, cache_parabolic) + end + + # Apply Jacobian from mapping to reference element + @trixi_timeit timer() "Jacobian" begin + apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) + end + + return nothing +end + function calc_gradient!(gradients, u_transformed, t, mesh::P4estMesh{2}, equations_parabolic, boundary_conditions_parabolic, dg::DG, @@ -473,15 +562,21 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, end function calc_boundary_flux_gradients!(cache, t, - boundary_condition::Union{BoundaryConditionPeriodic, - BoundaryConditionDoNothing - }, - mesh::P4estMesh, equations, surface_integral, dg::DG) + boundary_conditions_parabolic::BoundaryConditionPeriodic, + mesh::P4estMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + surface_integral, dg::DG) + return nothing +end + +function calc_boundary_flux_gradients!(cache, t, + boundary_condition::BoundaryConditionDoNothing, + mesh::P4estMesh{2}, equations, surface_integral, dg::DG) @assert isempty(eachboundary(dg, cache)) end # Function barrier for type stability -function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh, +function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh{2}, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -490,7 +585,21 @@ function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4es return nothing end -function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh, +function calc_boundary_flux_divergence!(cache, t, + boundary_conditions_parabolic::BoundaryConditionPeriodic, + mesh::P4estMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + surface_integral, dg::DG) + return nothing +end + +function calc_boundary_flux_divergence!(cache, t, + boundary_condition::BoundaryConditionDoNothing, + mesh::P4estMesh{2}, equations, surface_integral, dg::DG) + @assert isempty(eachboundary(dg, cache)) +end + +function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh{2}, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -504,7 +613,7 @@ end function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, BC_indices::NTuple{N, Vector{Int}}, operator_type, - mesh::P4estMesh, + mesh::P4estMesh{2}, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -528,7 +637,7 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - operator_type, mesh::P4estMesh, equations, + operator_type, mesh::P4estMesh{2}, equations, surface_integral, dg::DG) nothing end diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 5370c927e05..98398e4856a 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -22,6 +22,87 @@ function create_cache_parabolic(mesh::P4estMesh{3}, equations_hyperbolic::Abstra return cache end +function rhs_parabolic!(du, u, t, mesh::P4estMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + initial_condition, boundary_conditions_parabolic, source_terms, + dg::DG, parabolic_scheme, cache, cache_parabolic) + @unpack u_transformed, gradients, flux_viscous = cache_parabolic + + # Convert conservative variables to a form more suitable for viscous flux calculations + @trixi_timeit timer() "transform variables" begin + transform_variables!(u_transformed, u, mesh, equations_parabolic, + dg, parabolic_scheme, cache, cache_parabolic) + end + + # Compute the gradients of the transformed variables + @trixi_timeit timer() "calculate gradient" begin + calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, + boundary_conditions_parabolic, dg, cache, cache_parabolic) + end + + # Compute and store the viscous fluxes + @trixi_timeit timer() "calculate viscous fluxes" begin + calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, + equations_parabolic, dg, cache, cache_parabolic) + end + + # The remainder of this function is essentially a regular rhs! for parabolic + # equations (i.e., it computes the divergence of the viscous fluxes) + # + # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have + # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the + # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we + # do not need to recreate the existing data structure only with a different name, and c) we do not + # need to interpolate solutions *and* gradients to the surfaces. + + # TODO: parabolic; reconsider current data structure reuse strategy + + # Reset du + @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) + + # Calculate volume integral + @trixi_timeit timer() "volume integral" begin + calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + end + + # Prolong solution to interfaces + @trixi_timeit timer() "prolong2interfaces" begin + prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate interface fluxes + @trixi_timeit timer() "interface flux" begin + calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, dg, cache_parabolic) + end + + # Prolong solution to boundaries + @trixi_timeit timer() "prolong2boundaries" begin + prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # TODO: parabolic; extend to mortars + @assert nmortars(dg, cache) == 0 + + # Calculate surface integrals + @trixi_timeit timer() "surface integral" begin + calc_surface_integral!(du, u, mesh, equations_parabolic, + dg.surface_integral, dg, cache_parabolic) + end + + # Apply Jacobian from mapping to reference element + @trixi_timeit timer() "Jacobian" begin + apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) + end + + return nothing +end + function calc_gradient!(gradients, u_transformed, t, mesh::P4estMesh{3}, equations_parabolic, boundary_conditions_parabolic, dg::DG, @@ -563,9 +644,23 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, return nothing end +function calc_boundary_flux_gradients!(cache, t, + boundary_conditions_parabolic::BoundaryConditionPeriodic, + mesh::P4estMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + surface_integral, dg::DG) + return nothing +end + +function calc_boundary_flux_gradients!(cache, t, + boundary_condition::BoundaryConditionDoNothing, + mesh::P4estMesh{3}, equations, surface_integral, dg::DG) + @assert isempty(eachboundary(dg, cache)) +end + # # Function barrier for type stability # !!! TODO: Figure out why this cannot removed eventhough it exists in the dg_2d_parabolic.jl file -function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh, +function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh{3}, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -574,7 +669,21 @@ function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4es return nothing end -function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh, +function calc_boundary_flux_divergence!(cache, t, + boundary_conditions_parabolic::BoundaryConditionPeriodic, + mesh::P4estMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + surface_integral, dg::DG) + return nothing +end + +function calc_boundary_flux_divergence!(cache, t, + boundary_condition::BoundaryConditionDoNothing, + mesh::P4estMesh{3}, equations, surface_integral, dg::DG) + @assert isempty(eachboundary(dg, cache)) +end + +function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh{3}, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -588,7 +697,7 @@ end function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, BC_indices::NTuple{N, Vector{Int}}, operator_type, - mesh::P4estMesh, + mesh::P4estMesh{3}, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -612,7 +721,7 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - operator_type, mesh::P4estMesh, equations, + operator_type, mesh::P4estMesh{3}, equations, surface_integral, dg::DG) nothing end diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 6cc229ff803..b1dba93d81c 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -112,95 +112,6 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, return nothing end -function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - initial_condition, boundary_conditions_parabolic, source_terms, - dg::DG, parabolic_scheme, cache, cache_parabolic) - (; u_transformed, gradients, flux_viscous) = cache_parabolic - - # Convert conservative variables to a form more suitable for viscous flux calculations - @trixi_timeit timer() "transform variables" begin - transform_variables!(u_transformed, u, mesh, equations_parabolic, - dg, parabolic_scheme, cache, cache_parabolic) - end - - # Compute the gradients of the transformed variables - @trixi_timeit timer() "calculate gradient" begin - calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, - boundary_conditions_parabolic, dg, cache, cache_parabolic) - end - - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache, cache_parabolic) - end - - # The remainder of this function is essentially a regular rhs! for parabolic - # equations (i.e., it computes the divergence of the viscous fluxes) - # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the - # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the - # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* - # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we - # do not need to recreate the existing data structure only with a different name, and c) we do not - # need to interpolate solutions *and* gradients to the surfaces. - - # TODO: parabolic; reconsider current data structure reuse strategy - - # Reset du - @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) - - # Calculate volume integral - @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) - end - - # Prolong solution to interfaces - @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate interface fluxes - @trixi_timeit timer() "interface flux" begin - calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, - equations_parabolic, dg, cache_parabolic) - end - - # Prolong solution to boundaries - @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate boundary fluxes - @trixi_timeit timer() "boundary flux" begin - calc_boundary_flux_divergence!(cache_parabolic, t, - boundary_conditions_parabolic, mesh, - equations_parabolic, - dg.surface_integral, dg) - end - - # TODO: parabolic; extend to mortars - @assert nmortars(dg, cache) == 0 - - # Calculate surface integrals - @trixi_timeit timer() "surface integral" begin - calc_surface_integral!(du, u, mesh, equations_parabolic, - dg.surface_integral, dg, cache_parabolic) - end - - # Apply Jacobian from mapping to reference element - @trixi_timeit timer() "Jacobian" begin - apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) - end - - return nothing -end - # Transform solution variables prior to taking the gradient # (e.g., conservative to primitive variables). Defaults to doing nothing. # TODO: can we avoid copying data? @@ -420,7 +331,7 @@ end function calc_boundary_flux_gradients!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::Union{TreeMesh{2}, P4estMesh{2}}, + mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing @@ -428,7 +339,7 @@ end function calc_boundary_flux_divergence!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::Union{TreeMesh{2}, P4estMesh{2}}, + mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 2c0e62716d5..4294c364c23 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -13,7 +13,7 @@ # 2. compute f(u, grad(u)) # 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call) # boundary conditions will be applied to both grad(u) and div(f(u, grad(u))). -function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{3}, P4estMesh{3}}, +function rhs_parabolic!(du, u, t, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) @@ -390,7 +390,7 @@ end function calc_boundary_flux_gradients!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, + mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing @@ -398,7 +398,7 @@ end function calc_boundary_flux_divergence!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, + mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing From 5b5d433448746db29d9aab707ca4753a5075df86 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 16:04:11 +0200 Subject: [PATCH 34/74] Less invasive treatment for mortars and p4est --- src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 89 ++++++++++++++++++++ src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 97 ++++++++++++++++++++++ src/solvers/dgsem_tree/dg_2d_parabolic.jl | 89 -------------------- 3 files changed, 186 insertions(+), 89 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl index 7e90a83a9ca..51d3653ec22 100644 --- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl @@ -22,6 +22,95 @@ function create_cache_parabolic(mesh::P4estMesh{2}, equations_hyperbolic::Abstra return cache end +function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + initial_condition, boundary_conditions_parabolic, source_terms, + dg::DG, parabolic_scheme, cache, cache_parabolic) + (; u_transformed, gradients, flux_viscous) = cache_parabolic + + # Convert conservative variables to a form more suitable for viscous flux calculations + @trixi_timeit timer() "transform variables" begin + transform_variables!(u_transformed, u, mesh, equations_parabolic, + dg, parabolic_scheme, cache, cache_parabolic) + end + + # Compute the gradients of the transformed variables + @trixi_timeit timer() "calculate gradient" begin + calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, + boundary_conditions_parabolic, dg, cache, cache_parabolic) + end + + # Compute and store the viscous fluxes + @trixi_timeit timer() "calculate viscous fluxes" begin + calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, + equations_parabolic, dg, cache, cache_parabolic) + end + + # The remainder of this function is essentially a regular rhs! for parabolic + # equations (i.e., it computes the divergence of the viscous fluxes) + # + # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have + # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the + # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we + # do not need to recreate the existing data structure only with a different name, and c) we do not + # need to interpolate solutions *and* gradients to the surfaces. + + # TODO: parabolic; reconsider current data structure reuse strategy + + # Reset du + @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) + + # Calculate volume integral + @trixi_timeit timer() "volume integral" begin + calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + end + + # Prolong solution to interfaces + @trixi_timeit timer() "prolong2interfaces" begin + prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate interface fluxes + @trixi_timeit timer() "interface flux" begin + calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, dg, cache_parabolic) + end + + # Prolong solution to boundaries + @trixi_timeit timer() "prolong2boundaries" begin + prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate boundary fluxes + @trixi_timeit timer() "boundary flux" begin + calc_boundary_flux_divergence!(cache_parabolic, t, + boundary_conditions_parabolic, mesh, + equations_parabolic, + dg.surface_integral, dg) + end + + # TODO: parabolic; extend to mortars + @assert nmortars(dg, cache) == 0 + + # Calculate surface integrals + @trixi_timeit timer() "surface integral" begin + calc_surface_integral!(du, u, mesh, equations_parabolic, + dg.surface_integral, dg, cache_parabolic) + end + + # Apply Jacobian from mapping to reference element + @trixi_timeit timer() "Jacobian" begin + apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) + end + + return nothing +end + function calc_gradient!(gradients, u_transformed, t, mesh::P4estMesh{2}, equations_parabolic, boundary_conditions_parabolic, dg::DG, diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 5370c927e05..45ef2bca679 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -22,6 +22,103 @@ function create_cache_parabolic(mesh::P4estMesh{3}, equations_hyperbolic::Abstra return cache end +# This file collects all methods that have been updated to work with parabolic systems of equations +# +# assumptions: parabolic terms are of the form div(f(u, grad(u))) and +# will be discretized first order form as follows: +# 1. compute grad(u) +# 2. compute f(u, grad(u)) +# 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call) +# boundary conditions will be applied to both grad(u) and div(f(u, grad(u))). +function rhs_parabolic!(du, u, t, mesh::P4estMesh{3}, + equations_parabolic::AbstractEquationsParabolic, + initial_condition, boundary_conditions_parabolic, source_terms, + dg::DG, parabolic_scheme, cache, cache_parabolic) + @unpack u_transformed, gradients, flux_viscous = cache_parabolic + + # Convert conservative variables to a form more suitable for viscous flux calculations + @trixi_timeit timer() "transform variables" begin + transform_variables!(u_transformed, u, mesh, equations_parabolic, + dg, parabolic_scheme, cache, cache_parabolic) + end + + # Compute the gradients of the transformed variables + @trixi_timeit timer() "calculate gradient" begin + calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, + boundary_conditions_parabolic, dg, cache, cache_parabolic) + end + + # Compute and store the viscous fluxes + @trixi_timeit timer() "calculate viscous fluxes" begin + calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, + equations_parabolic, dg, cache, cache_parabolic) + end + + # The remainder of this function is essentially a regular rhs! for parabolic + # equations (i.e., it computes the divergence of the viscous fluxes) + # + # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have + # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the + # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we + # do not need to recreate the existing data structure only with a different name, and c) we do not + # need to interpolate solutions *and* gradients to the surfaces. + + # TODO: parabolic; reconsider current data structure reuse strategy + + # Reset du + @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) + + # Calculate volume integral + @trixi_timeit timer() "volume integral" begin + calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + end + + # Prolong solution to interfaces + @trixi_timeit timer() "prolong2interfaces" begin + prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate interface fluxes + @trixi_timeit timer() "interface flux" begin + calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, dg, cache_parabolic) + end + + # Prolong solution to boundaries + @trixi_timeit timer() "prolong2boundaries" begin + prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate boundary fluxes + @trixi_timeit timer() "boundary flux" begin + calc_boundary_flux_divergence!(cache_parabolic, t, + boundary_conditions_parabolic, + mesh, equations_parabolic, + dg.surface_integral, dg) + end + + # TODO: parabolic; extend to mortars + @assert nmortars(dg, cache) == 0 + + # Calculate surface integrals + @trixi_timeit timer() "surface integral" begin + calc_surface_integral!(du, u, mesh, equations_parabolic, + dg.surface_integral, dg, cache_parabolic) + end + + # Apply Jacobian from mapping to reference element + @trixi_timeit timer() "Jacobian" begin + apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) + end + + return nothing +end + function calc_gradient!(gradients, u_transformed, t, mesh::P4estMesh{3}, equations_parabolic, boundary_conditions_parabolic, dg::DG, diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 6cc229ff803..97af6c6acd2 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -112,95 +112,6 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, return nothing end -function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - initial_condition, boundary_conditions_parabolic, source_terms, - dg::DG, parabolic_scheme, cache, cache_parabolic) - (; u_transformed, gradients, flux_viscous) = cache_parabolic - - # Convert conservative variables to a form more suitable for viscous flux calculations - @trixi_timeit timer() "transform variables" begin - transform_variables!(u_transformed, u, mesh, equations_parabolic, - dg, parabolic_scheme, cache, cache_parabolic) - end - - # Compute the gradients of the transformed variables - @trixi_timeit timer() "calculate gradient" begin - calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, - boundary_conditions_parabolic, dg, cache, cache_parabolic) - end - - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache, cache_parabolic) - end - - # The remainder of this function is essentially a regular rhs! for parabolic - # equations (i.e., it computes the divergence of the viscous fluxes) - # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the - # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the - # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* - # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we - # do not need to recreate the existing data structure only with a different name, and c) we do not - # need to interpolate solutions *and* gradients to the surfaces. - - # TODO: parabolic; reconsider current data structure reuse strategy - - # Reset du - @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) - - # Calculate volume integral - @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) - end - - # Prolong solution to interfaces - @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate interface fluxes - @trixi_timeit timer() "interface flux" begin - calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, - equations_parabolic, dg, cache_parabolic) - end - - # Prolong solution to boundaries - @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate boundary fluxes - @trixi_timeit timer() "boundary flux" begin - calc_boundary_flux_divergence!(cache_parabolic, t, - boundary_conditions_parabolic, mesh, - equations_parabolic, - dg.surface_integral, dg) - end - - # TODO: parabolic; extend to mortars - @assert nmortars(dg, cache) == 0 - - # Calculate surface integrals - @trixi_timeit timer() "surface integral" begin - calc_surface_integral!(du, u, mesh, equations_parabolic, - dg.surface_integral, dg, cache_parabolic) - end - - # Apply Jacobian from mapping to reference element - @trixi_timeit timer() "Jacobian" begin - apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) - end - - return nothing -end - # Transform solution variables prior to taking the gradient # (e.g., conservative to primitive variables). Defaults to doing nothing. # TODO: can we avoid copying data? From bb68ee301464ed3e88424fbc93b42a26b07f3aee Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 16:34:25 +0200 Subject: [PATCH 35/74] Revert "Add more explicit dispatch" This reverts commit 491c923d09ba335c03524894d9f59acf1d6ee699. --- src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 125 ++------------------- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 117 +------------------ src/solvers/dgsem_tree/dg_2d_parabolic.jl | 93 ++++++++++++++- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 6 +- 4 files changed, 106 insertions(+), 235 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl index 1c4187c9db5..7e90a83a9ca 100644 --- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl @@ -22,95 +22,6 @@ function create_cache_parabolic(mesh::P4estMesh{2}, equations_hyperbolic::Abstra return cache end -function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - initial_condition, boundary_conditions_parabolic, source_terms, - dg::DG, parabolic_scheme, cache, cache_parabolic) - (; u_transformed, gradients, flux_viscous) = cache_parabolic - - # Convert conservative variables to a form more suitable for viscous flux calculations - @trixi_timeit timer() "transform variables" begin - transform_variables!(u_transformed, u, mesh, equations_parabolic, - dg, parabolic_scheme, cache, cache_parabolic) - end - - # Compute the gradients of the transformed variables - @trixi_timeit timer() "calculate gradient" begin - calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, - boundary_conditions_parabolic, dg, cache, cache_parabolic) - end - - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache, cache_parabolic) - end - - # The remainder of this function is essentially a regular rhs! for parabolic - # equations (i.e., it computes the divergence of the viscous fluxes) - # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the - # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the - # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* - # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we - # do not need to recreate the existing data structure only with a different name, and c) we do not - # need to interpolate solutions *and* gradients to the surfaces. - - # TODO: parabolic; reconsider current data structure reuse strategy - - # Reset du - @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) - - # Calculate volume integral - @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) - end - - # Prolong solution to interfaces - @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate interface fluxes - @trixi_timeit timer() "interface flux" begin - calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, - equations_parabolic, dg, cache_parabolic) - end - - # Prolong solution to boundaries - @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate boundary fluxes - @trixi_timeit timer() "boundary flux" begin - calc_boundary_flux_divergence!(cache_parabolic, t, - boundary_conditions_parabolic, mesh, - equations_parabolic, - dg.surface_integral, dg) - end - - # TODO: parabolic; extend to mortars - @assert nmortars(dg, cache) == 0 - - # Calculate surface integrals - @trixi_timeit timer() "surface integral" begin - calc_surface_integral!(du, u, mesh, equations_parabolic, - dg.surface_integral, dg, cache_parabolic) - end - - # Apply Jacobian from mapping to reference element - @trixi_timeit timer() "Jacobian" begin - apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) - end - - return nothing -end - function calc_gradient!(gradients, u_transformed, t, mesh::P4estMesh{2}, equations_parabolic, boundary_conditions_parabolic, dg::DG, @@ -562,21 +473,15 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, end function calc_boundary_flux_gradients!(cache, t, - boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::P4estMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - surface_integral, dg::DG) - return nothing -end - -function calc_boundary_flux_gradients!(cache, t, - boundary_condition::BoundaryConditionDoNothing, - mesh::P4estMesh{2}, equations, surface_integral, dg::DG) + boundary_condition::Union{BoundaryConditionPeriodic, + BoundaryConditionDoNothing + }, + mesh::P4estMesh, equations, surface_integral, dg::DG) @assert isempty(eachboundary(dg, cache)) end # Function barrier for type stability -function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh{2}, +function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -585,21 +490,7 @@ function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4es return nothing end -function calc_boundary_flux_divergence!(cache, t, - boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::P4estMesh{2}, - equations_parabolic::AbstractEquationsParabolic, - surface_integral, dg::DG) - return nothing -end - -function calc_boundary_flux_divergence!(cache, t, - boundary_condition::BoundaryConditionDoNothing, - mesh::P4estMesh{2}, equations, surface_integral, dg::DG) - @assert isempty(eachboundary(dg, cache)) -end - -function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh{2}, +function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -613,7 +504,7 @@ end function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, BC_indices::NTuple{N, Vector{Int}}, operator_type, - mesh::P4estMesh{2}, + mesh::P4estMesh, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -637,7 +528,7 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - operator_type, mesh::P4estMesh{2}, equations, + operator_type, mesh::P4estMesh, equations, surface_integral, dg::DG) nothing end diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 98398e4856a..5370c927e05 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -22,87 +22,6 @@ function create_cache_parabolic(mesh::P4estMesh{3}, equations_hyperbolic::Abstra return cache end -function rhs_parabolic!(du, u, t, mesh::P4estMesh{3}, - equations_parabolic::AbstractEquationsParabolic, - initial_condition, boundary_conditions_parabolic, source_terms, - dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack u_transformed, gradients, flux_viscous = cache_parabolic - - # Convert conservative variables to a form more suitable for viscous flux calculations - @trixi_timeit timer() "transform variables" begin - transform_variables!(u_transformed, u, mesh, equations_parabolic, - dg, parabolic_scheme, cache, cache_parabolic) - end - - # Compute the gradients of the transformed variables - @trixi_timeit timer() "calculate gradient" begin - calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, - boundary_conditions_parabolic, dg, cache, cache_parabolic) - end - - # Compute and store the viscous fluxes - @trixi_timeit timer() "calculate viscous fluxes" begin - calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, - equations_parabolic, dg, cache, cache_parabolic) - end - - # The remainder of this function is essentially a regular rhs! for parabolic - # equations (i.e., it computes the divergence of the viscous fluxes) - # - # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have - # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the - # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the - # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it - # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* - # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we - # do not need to recreate the existing data structure only with a different name, and c) we do not - # need to interpolate solutions *and* gradients to the surfaces. - - # TODO: parabolic; reconsider current data structure reuse strategy - - # Reset du - @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) - - # Calculate volume integral - @trixi_timeit timer() "volume integral" begin - calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) - end - - # Prolong solution to interfaces - @trixi_timeit timer() "prolong2interfaces" begin - prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # Calculate interface fluxes - @trixi_timeit timer() "interface flux" begin - calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, - equations_parabolic, dg, cache_parabolic) - end - - # Prolong solution to boundaries - @trixi_timeit timer() "prolong2boundaries" begin - prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, - dg.surface_integral, dg, cache) - end - - # TODO: parabolic; extend to mortars - @assert nmortars(dg, cache) == 0 - - # Calculate surface integrals - @trixi_timeit timer() "surface integral" begin - calc_surface_integral!(du, u, mesh, equations_parabolic, - dg.surface_integral, dg, cache_parabolic) - end - - # Apply Jacobian from mapping to reference element - @trixi_timeit timer() "Jacobian" begin - apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) - end - - return nothing -end - function calc_gradient!(gradients, u_transformed, t, mesh::P4estMesh{3}, equations_parabolic, boundary_conditions_parabolic, dg::DG, @@ -644,23 +563,9 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, return nothing end -function calc_boundary_flux_gradients!(cache, t, - boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::P4estMesh{3}, - equations_parabolic::AbstractEquationsParabolic, - surface_integral, dg::DG) - return nothing -end - -function calc_boundary_flux_gradients!(cache, t, - boundary_condition::BoundaryConditionDoNothing, - mesh::P4estMesh{3}, equations, surface_integral, dg::DG) - @assert isempty(eachboundary(dg, cache)) -end - # # Function barrier for type stability # !!! TODO: Figure out why this cannot removed eventhough it exists in the dg_2d_parabolic.jl file -function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh{3}, +function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -669,21 +574,7 @@ function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4es return nothing end -function calc_boundary_flux_divergence!(cache, t, - boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::P4estMesh{3}, - equations_parabolic::AbstractEquationsParabolic, - surface_integral, dg::DG) - return nothing -end - -function calc_boundary_flux_divergence!(cache, t, - boundary_condition::BoundaryConditionDoNothing, - mesh::P4estMesh{3}, equations, surface_integral, dg::DG) - @assert isempty(eachboundary(dg, cache)) -end - -function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh{3}, +function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh, equations, surface_integral, dg::DG) (; boundary_condition_types, boundary_indices) = boundary_conditions @@ -697,7 +588,7 @@ end function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, BC_indices::NTuple{N, Vector{Int}}, operator_type, - mesh::P4estMesh{3}, + mesh::P4estMesh, equations, surface_integral, dg::DG) where {N} # Extract the boundary condition type and index vector boundary_condition = first(BCs) @@ -721,7 +612,7 @@ end # terminate the type-stable iteration over tuples function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - operator_type, mesh::P4estMesh{3}, equations, + operator_type, mesh::P4estMesh, equations, surface_integral, dg::DG) nothing end diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index b1dba93d81c..6cc229ff803 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -112,6 +112,95 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, return nothing end +function rhs_parabolic!(du, u, t, mesh::P4estMesh{2}, + equations_parabolic::AbstractEquationsParabolic, + initial_condition, boundary_conditions_parabolic, source_terms, + dg::DG, parabolic_scheme, cache, cache_parabolic) + (; u_transformed, gradients, flux_viscous) = cache_parabolic + + # Convert conservative variables to a form more suitable for viscous flux calculations + @trixi_timeit timer() "transform variables" begin + transform_variables!(u_transformed, u, mesh, equations_parabolic, + dg, parabolic_scheme, cache, cache_parabolic) + end + + # Compute the gradients of the transformed variables + @trixi_timeit timer() "calculate gradient" begin + calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic, + boundary_conditions_parabolic, dg, cache, cache_parabolic) + end + + # Compute and store the viscous fluxes + @trixi_timeit timer() "calculate viscous fluxes" begin + calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh, + equations_parabolic, dg, cache, cache_parabolic) + end + + # The remainder of this function is essentially a regular rhs! for parabolic + # equations (i.e., it computes the divergence of the viscous fluxes) + # + # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have + # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the + # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the + # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it + # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values* + # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we + # do not need to recreate the existing data structure only with a different name, and c) we do not + # need to interpolate solutions *and* gradients to the surfaces. + + # TODO: parabolic; reconsider current data structure reuse strategy + + # Reset du + @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache) + + # Calculate volume integral + @trixi_timeit timer() "volume integral" begin + calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache) + end + + # Prolong solution to interfaces + @trixi_timeit timer() "prolong2interfaces" begin + prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate interface fluxes + @trixi_timeit timer() "interface flux" begin + calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh, + equations_parabolic, dg, cache_parabolic) + end + + # Prolong solution to boundaries + @trixi_timeit timer() "prolong2boundaries" begin + prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic, + dg.surface_integral, dg, cache) + end + + # Calculate boundary fluxes + @trixi_timeit timer() "boundary flux" begin + calc_boundary_flux_divergence!(cache_parabolic, t, + boundary_conditions_parabolic, mesh, + equations_parabolic, + dg.surface_integral, dg) + end + + # TODO: parabolic; extend to mortars + @assert nmortars(dg, cache) == 0 + + # Calculate surface integrals + @trixi_timeit timer() "surface integral" begin + calc_surface_integral!(du, u, mesh, equations_parabolic, + dg.surface_integral, dg, cache_parabolic) + end + + # Apply Jacobian from mapping to reference element + @trixi_timeit timer() "Jacobian" begin + apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic) + end + + return nothing +end + # Transform solution variables prior to taking the gradient # (e.g., conservative to primitive variables). Defaults to doing nothing. # TODO: can we avoid copying data? @@ -331,7 +420,7 @@ end function calc_boundary_flux_gradients!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::TreeMesh{2}, + mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing @@ -339,7 +428,7 @@ end function calc_boundary_flux_divergence!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::TreeMesh{2}, + mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 4294c364c23..2c0e62716d5 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -13,7 +13,7 @@ # 2. compute f(u, grad(u)) # 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call) # boundary conditions will be applied to both grad(u) and div(f(u, grad(u))). -function rhs_parabolic!(du, u, t, mesh::TreeMesh{3}, +function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{3}, P4estMesh{3}}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) @@ -390,7 +390,7 @@ end function calc_boundary_flux_gradients!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::TreeMesh{3}, + mesh::Union{TreeMesh{3}, P4estMesh{3}}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing @@ -398,7 +398,7 @@ end function calc_boundary_flux_divergence!(cache, t, boundary_conditions_parabolic::BoundaryConditionPeriodic, - mesh::TreeMesh{3}, + mesh::Union{TreeMesh{3}, P4estMesh{3}}, equations_parabolic::AbstractEquationsParabolic, surface_integral, dg::DG) return nothing From 7f816e2670b60de3574df93c76b6042053895479 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 16:37:26 +0200 Subject: [PATCH 36/74] More explicit dispatch --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 2c0e62716d5..48178c764a0 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -13,7 +13,7 @@ # 2. compute f(u, grad(u)) # 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call) # boundary conditions will be applied to both grad(u) and div(f(u, grad(u))). -function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{3}, P4estMesh{3}}, +function rhs_parabolic!(du, u, t, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) From e05d112cfbffa2aa7a5773efdb556d28be63020d Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 25 Jul 2023 19:25:02 +0200 Subject: [PATCH 37/74] Remove additional end --- test/test_parabolic_3d.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl index ce9b9defb3d..4629a1fb2b3 100644 --- a/test/test_parabolic_3d.jl +++ b/test/test_parabolic_3d.jl @@ -131,7 +131,6 @@ isdir(outdir) && rm(outdir, recursive=true) @test ac_sol.l2 ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005] @test ac_sol.linf ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645] end -end @trixi_testset "P4estMesh3D: elixir_navierstokes_convergence.jl" begin @test_trixi_include(joinpath(examples_dir(), "p4est_3d_dgsem", "elixir_navierstokes_convergence.jl"), @@ -148,8 +147,8 @@ end linf = [0.0006696415247340326, 0.03442565722527785, 0.03442565722577423, 0.06295407168705314, 0.032857472756916195] ) end - end + # Clean up afterwards: delete Trixi.jl output directory @test_nowarn isdir(outdir) && rm(outdir, recursive=true) From d60c9f52fd4b1d8bb5859f2b7551a05a0fe27302 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 27 Jul 2023 12:08:23 +0200 Subject: [PATCH 38/74] comment --- .../semidiscretization_hyperbolic_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index 8acebf42040..0db4e4b6170 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -331,7 +331,7 @@ end function rhs_hyperbolic_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) @trixi_timeit timer() "hyperbolic-parabolic rhs!" begin # TODO: Avoid allocations, make member variable of something? - # -> Could reside in integrator, then pass in similar to indices of PERK + # -> Could reside in (PERK) integrator, then pass in similar to indices of PERK du_ode_hyp = similar(du_ode) rhs!(du_ode_hyp, u_ode, semi, t) rhs_parabolic!(du_ode, u_ode, semi, t) From 14025d228a07563855aa3d37fae5bc27b5ec3895 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 27 Jul 2023 13:47:47 +0200 Subject: [PATCH 39/74] remove tabs --- src/equations/compressible_euler_2d.jl | 2915 ++++++++++++------------ 1 file changed, 1457 insertions(+), 1458 deletions(-) diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl index 0cafe304bbf..393f738509c 100644 --- a/src/equations/compressible_euler_2d.jl +++ b/src/equations/compressible_euler_2d.jl @@ -3,1491 +3,1490 @@ # we need to opt-in explicitly. # See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin - #! format: noindent - - @doc raw""" - CompressibleEulerEquations2D(gamma) - - The compressible Euler equations - ```math - \frac{\partial}{\partial t} - \begin{pmatrix} - \rho \\ \rho v_1 \\ \rho v_2 \\ \rho e - \end{pmatrix} - + - \frac{\partial}{\partial x} - \begin{pmatrix} - \rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ (\rho e +p) v_1 - \end{pmatrix} - + - \frac{\partial}{\partial y} - \begin{pmatrix} - \rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + p \\ (\rho e +p) v_2 - \end{pmatrix} - = - \begin{pmatrix} - 0 \\ 0 \\ 0 \\ 0 - \end{pmatrix} - ``` - for an ideal gas with ratio of specific heats `gamma` - in two space dimensions. - Here, ``\rho`` is the density, ``v_1``, ``v_2`` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and - ```math - p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2) \right) - ``` - the pressure. - """ - struct CompressibleEulerEquations2D{RealT <: Real} <: - AbstractCompressibleEulerEquations{2, 4} - gamma::RealT # ratio of specific heats - inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications - - function CompressibleEulerEquations2D(gamma) - γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1)) - new{typeof(γ)}(γ, inv_gamma_minus_one) - end - end - - function varnames(::typeof(cons2cons), ::CompressibleEulerEquations2D) - ("rho", "rho_v1", "rho_v2", "rho_e") - end - varnames(::typeof(cons2prim), ::CompressibleEulerEquations2D) = ("rho", "v1", "v2", "p") - - # Set initial conditions at physical location `x` for time `t` - """ - initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) - - A constant initial condition to test free-stream preservation. - """ - function initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) - rho = 1.0 - rho_v1 = 0.1 - rho_v2 = -0.2 - rho_e = 10.0 - return SVector(rho, rho_v1, rho_v2, rho_e) - end - - """ - initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations2D) - - A smooth initial condition used for convergence tests in combination with - [`source_terms_convergence_test`](@ref) - (and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). - """ - function initial_condition_convergence_test(x, t, - equations::CompressibleEulerEquations2D) - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = ini^2 - - return SVector(rho, rho_v1, rho_v2, rho_e) - end - - """ - source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations2D) - - Source terms used for convergence tests in combination with - [`initial_condition_convergence_test`](@ref) - (and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). - """ - @inline function source_terms_convergence_test(u, x, t, - equations::CompressibleEulerEquations2D) - # Same settings as in `initial_condition` - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - γ = equations.gamma - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - tmp = (2 * rho - 1) * (γ - 1) - - du1 = rho_x - du2 = rho_x * (1 + tmp) - du3 = du2 - du4 = 2 * rho_x * (rho + tmp) - - return SVector(du1, du2, du3, du4) - end - - """ - initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) - - A sine wave in the density with constant velocity and pressure; reduces the - compressible Euler equations to the linear advection equations. - This setup is the test case for stability of EC fluxes from paper - - Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) - Stability issues of entropy-stable and/or split-form high-order schemes - [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) - with the following parameters - - domain [-1, 1] - - mesh = 4x4 - - polydeg = 5 - """ - function initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) - v1 = 0.1 - v2 = 0.2 - rho = 1 + 0.98 * sinpi(2 * (x[1] + x[2] - t * (v1 + v2))) - rho_v1 = rho * v1 - rho_v2 = rho * v2 - p = 20 - rho_e = p / (equations.gamma - 1) + 1 / 2 * rho * (v1^2 + v2^2) - return SVector(rho, rho_v1, rho_v2, rho_e) +#! format: noindent + +@doc raw""" + CompressibleEulerEquations2D(gamma) + +The compressible Euler equations +```math +\frac{\partial}{\partial t} +\begin{pmatrix} +\rho \\ \rho v_1 \\ \rho v_2 \\ \rho e +\end{pmatrix} ++ +\frac{\partial}{\partial x} +\begin{pmatrix} + \rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ (\rho e +p) v_1 +\end{pmatrix} ++ +\frac{\partial}{\partial y} +\begin{pmatrix} +\rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + p \\ (\rho e +p) v_2 +\end{pmatrix} += +\begin{pmatrix} +0 \\ 0 \\ 0 \\ 0 +\end{pmatrix} +``` +for an ideal gas with ratio of specific heats `gamma` +in two space dimensions. +Here, ``\rho`` is the density, ``v_1``, ``v_2`` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and +```math +p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2) \right) +``` +the pressure. +""" +struct CompressibleEulerEquations2D{RealT <: Real} <: + AbstractCompressibleEulerEquations{2, 4} + gamma::RealT # ratio of specific heats + inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications + + function CompressibleEulerEquations2D(gamma) + γ, inv_gamma_minus_one = promote(gamma, inv(gamma - 1)) + new{typeof(γ)}(γ, inv_gamma_minus_one) end - - """ - initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations2D) - - A weak blast wave taken from - - Sebastian Hennemann, Gregor J. Gassner (2020) - A provably entropy stable subcell shock capturing approach for high order split form DG - [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044) - """ - function initial_condition_weak_blast_wave(x, t, +end + +function varnames(::typeof(cons2cons), ::CompressibleEulerEquations2D) + ("rho", "rho_v1", "rho_v2", "rho_e") +end +varnames(::typeof(cons2prim), ::CompressibleEulerEquations2D) = ("rho", "v1", "v2", "p") + +# Set initial conditions at physical location `x` for time `t` +""" + initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) + +A constant initial condition to test free-stream preservation. +""" +function initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) + rho = 1.0 + rho_v1 = 0.1 + rho_v2 = -0.2 + rho_e = 10.0 + return SVector(rho, rho_v1, rho_v2, rho_e) +end + +""" + initial_condition_convergence_test(x, t, equations::CompressibleEulerEquations2D) + +A smooth initial condition used for convergence tests in combination with +[`source_terms_convergence_test`](@ref) +(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). +""" +function initial_condition_convergence_test(x, t, + equations::CompressibleEulerEquations2D) + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = ini^2 + + return SVector(rho, rho_v1, rho_v2, rho_e) +end + +""" + source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations2D) + +Source terms used for convergence tests in combination with +[`initial_condition_convergence_test`](@ref) +(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). +""" +@inline function source_terms_convergence_test(u, x, t, equations::CompressibleEulerEquations2D) - # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) - # Set up polar coordinates - inicenter = SVector(0.0, 0.0) - x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) - phi = atan(y_norm, x_norm) - sin_phi, cos_phi = sincos(phi) - - # Calculate primitive variables - rho = r > 0.5 ? 1.0 : 1.1691 - v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi - v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi - p = r > 0.5 ? 1.0 : 1.245 - - return prim2cons(SVector(rho, v1, v2, p), equations) - end - - """ - initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations2D) - - Setup used for convergence tests of the Euler equations with self-gravity used in - - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) - in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref) - or [`source_terms_eoc_test_euler`](@ref). - """ - function initial_condition_eoc_test_coupled_euler_gravity(x, t, - equations::CompressibleEulerEquations2D) - # OBS! this assumes that γ = 2 other manufactured source terms are incorrect - if equations.gamma != 2.0 - error("adiabatic constant must be 2 for the coupling convergence test") - end - c = 2.0 - A = 0.1 - ini = c + A * sin(pi * (x[1] + x[2] - t)) - G = 1.0 # gravitational constant - - rho = ini - v1 = 1.0 - v2 = 1.0 - p = ini^2 * G / pi # * 2 / ndims, but ndims==2 here - - return prim2cons(SVector(rho, v1, v2, p), equations) - end - - """ - source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations2D) - - Setup used for convergence tests of the Euler equations with self-gravity used in - - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) - in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). - """ - @inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t, - equations::CompressibleEulerEquations2D) - # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` - c = 2.0 - A = 0.1 - G = 1.0 # gravitational constant, must match coupling solver - C_grav = -2 * G / pi # 2 == 4 / ndims - - x1, x2 = x - si, co = sincos(pi * (x1 + x2 - t)) - rhox = A * pi * co - rho = c + A * si - - du1 = rhox - du2 = rhox - du3 = rhox - du4 = (1.0 - C_grav * rho) * rhox - - return SVector(du1, du2, du3, du4) - end - - """ - source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations2D) - - Setup used for convergence tests of the Euler equations with self-gravity used in - - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) - in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). - """ - @inline function source_terms_eoc_test_euler(u, x, t, - equations::CompressibleEulerEquations2D) - # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` - c = 2.0 - A = 0.1 - G = 1.0 - C_grav = -2 * G / pi # 2 == 4 / ndims - - x1, x2 = x - si, co = sincos(pi * (x1 + x2 - t)) - rhox = A * pi * co - rho = c + A * si - - du1 = rhox - du2 = rhox * (1 - C_grav * rho) - du3 = rhox * (1 - C_grav * rho) - du4 = rhox * (1 - 3 * C_grav * rho) - - return SVector(du1, du2, du3, du4) - end - - """ - boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, - equations::CompressibleEulerEquations2D) - - Determine the boundary numerical surface flux for a slip wall condition. - Imposes a zero normal velocity at the wall. - Density is taken from the internal solution state and pressure is computed as an - exact solution of a 1D Riemann problem. Further details about this boundary state - are available in the paper: - - J. J. W. van der Vegt and H. van der Ven (2002) - Slip flow boundary conditions in discontinuous Galerkin discretizations of - the Euler equations of gas dynamics - [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) - - Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book - - Eleuterio F. Toro (2009) - Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - 3rd edition - [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - - Should be used together with [`UnstructuredMesh2D`](@ref). - """ - @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - x, t, - surface_flux_function, - equations::CompressibleEulerEquations2D) - norm_ = norm(normal_direction) - # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later - normal = normal_direction / norm_ - - # rotate the internal solution state - u_local = rotate_to_x(u_inner, normal, equations) - - # compute the primitive variables - rho_local, v_normal, v_tangent, p_local = cons2prim(u_local, equations) - - # Get the solution of the pressure Riemann problem - # See Section 6.3.3 of - # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - if v_normal <= 0.0 - sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed - p_star = p_local * - (1 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2 * - equations.gamma * - equations.inv_gamma_minus_one) - else # v_normal > 0.0 - A = 2 / ((equations.gamma + 1) * rho_local) - B = p_local * (equations.gamma - 1) / (equations.gamma + 1) - p_star = p_local + - 0.5 * v_normal / A * - (v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B))) - end - - # For the slip wall we directly set the flux as the normal velocity is zero - return SVector(zero(eltype(u_inner)), - p_star * normal[1], - p_star * normal[2], - zero(eltype(u_inner))) * norm_ - end - - """ - boundary_condition_slip_wall(u_inner, orientation, direction, x, t, - surface_flux_function, equations::CompressibleEulerEquations2D) - - Should be used together with [`TreeMesh`](@ref). - """ - @inline function boundary_condition_slip_wall(u_inner, orientation, - direction, x, t, - surface_flux_function, - equations::CompressibleEulerEquations2D) - # get the appropriate normal vector from the orientation - if orientation == 1 - normal_direction = SVector(1, 0) - else # orientation == 2 - normal_direction = SVector(0, 1) - end - - # compute and return the flux using `boundary_condition_slip_wall` routine above - return boundary_condition_slip_wall(u_inner, normal_direction, direction, - x, t, surface_flux_function, equations) - end - - """ - boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, - surface_flux_function, equations::CompressibleEulerEquations2D) - - Should be used together with [`StructuredMesh`](@ref). - """ - @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - direction, x, t, - surface_flux_function, - equations::CompressibleEulerEquations2D) - # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back - # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh - if isodd(direction) - boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, - x, t, surface_flux_function, - equations) - else - boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, - x, t, surface_flux_function, - equations) - end - - return boundary_flux + # Same settings as in `initial_condition` + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + γ = equations.gamma + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + tmp = (2 * rho - 1) * (γ - 1) + + du1 = rho_x + du2 = rho_x * (1 + tmp) + du3 = du2 + du4 = 2 * rho_x * (rho + tmp) + + return SVector(du1, du2, du3, du4) +end + +""" + initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) + +A sine wave in the density with constant velocity and pressure; reduces the +compressible Euler equations to the linear advection equations. +This setup is the test case for stability of EC fluxes from paper +- Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) + Stability issues of entropy-stable and/or split-form high-order schemes + [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) +with the following parameters +- domain [-1, 1] +- mesh = 4x4 +- polydeg = 5 +""" +function initial_condition_density_wave(x, t, equations::CompressibleEulerEquations2D) + v1 = 0.1 + v2 = 0.2 + rho = 1 + 0.98 * sinpi(2 * (x[1] + x[2] - t * (v1 + v2))) + rho_v1 = rho * v1 + rho_v2 = rho * v2 + p = 20 + rho_e = p / (equations.gamma - 1) + 1 / 2 * rho * (v1^2 + v2^2) + return SVector(rho, rho_v1, rho_v2, rho_e) +end + +""" + initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations2D) + +A weak blast wave taken from +- Sebastian Hennemann, Gregor J. Gassner (2020) + A provably entropy stable subcell shock capturing approach for high order split form DG + [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044) +""" +function initial_condition_weak_blast_wave(x, t, + equations::CompressibleEulerEquations2D) + # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + phi = atan(y_norm, x_norm) + sin_phi, cos_phi = sincos(phi) + + # Calculate primitive variables + rho = r > 0.5 ? 1.0 : 1.1691 + v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi + v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi + p = r > 0.5 ? 1.0 : 1.245 + + return prim2cons(SVector(rho, v1, v2, p), equations) +end + +""" + initial_condition_eoc_test_coupled_euler_gravity(x, t, equations::CompressibleEulerEquations2D) + +Setup used for convergence tests of the Euler equations with self-gravity used in +- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) +in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref) +or [`source_terms_eoc_test_euler`](@ref). +""" +function initial_condition_eoc_test_coupled_euler_gravity(x, t, + equations::CompressibleEulerEquations2D) + # OBS! this assumes that γ = 2 other manufactured source terms are incorrect + if equations.gamma != 2.0 + error("adiabatic constant must be 2 for the coupling convergence test") end - - # Calculate 2D flux for a single point - @inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = rho_v1 * v2 - f4 = (rho_e + p) * v1 - else - f1 = rho_v2 - f2 = rho_v2 * v1 - f3 = rho_v2 * v2 + p - f4 = (rho_e + p) * v2 - end - return SVector(f1, f2, f3, f4) - end - - # Calculate 2D flux for a single point in the normal direction - # Note, this directional vector is not normalized - @inline function flux(u, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_e = last(u) - rho, v1, v2, p = cons2prim(u, equations) - - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = rho_v_normal - f2 = rho_v_normal * v1 + p * normal_direction[1] - f3 = rho_v_normal * v2 + p * normal_direction[2] - f4 = (rho_e + p) * v_normal - return SVector(f1, f2, f3, f4) - end - - """ - flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) - - This flux is is a modification of the original kinetic energy preserving two-point flux by - - Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018) - Kinetic energy and entropy preserving schemes for compressible flows - by split convective forms - [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) - - The modification is in the energy flux to guarantee pressure equilibrium and was developed by - - Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) - Preventing spurious pressure oscillations in split convective form discretizations for - compressible flows - [DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060) - """ - @inline function flux_shima_etal(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Average each factor of products in flux - rho_avg = 1 / 2 * (rho_ll + rho_rr) - v1_avg = 1 / 2 * (v1_ll + v1_rr) - v2_avg = 1 / 2 * (v2_ll + v2_rr) - p_avg = 1 / 2 * (p_ll + p_rr) - kin_avg = 1 / 2 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - pv1_avg = 1 / 2 * (p_ll * v1_rr + p_rr * v1_ll) - f1 = rho_avg * v1_avg - f2 = f1 * v1_avg + p_avg - f3 = f1 * v2_avg - f4 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg - else - pv2_avg = 1 / 2 * (p_ll * v2_rr + p_rr * v2_ll) - f1 = rho_avg * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_avg - f4 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg - end - - return SVector(f1, f2, f3, f4) - end - - @inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # Average each factor of products in flux - rho_avg = 1 / 2 * (rho_ll + rho_rr) - v1_avg = 1 / 2 * (v1_ll + v1_rr) - v2_avg = 1 / 2 * (v2_ll + v2_rr) - v_dot_n_avg = 1 / 2 * (v_dot_n_ll + v_dot_n_rr) - p_avg = 1 / 2 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_avg * v_dot_n_avg - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - f4 = (f1 * velocity_square_avg + - p_avg * v_dot_n_avg * equations.inv_gamma_minus_one - + 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) - - return SVector(f1, f2, f3, f4) - end - - """ - flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) - - Kinetic energy preserving two-point flux by - - Kennedy and Gruber (2008) - Reduced aliasing formulations of the convective terms within the - Navier-Stokes equations for a compressible fluid - [DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) - """ - @inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_e_ll = last(u_ll) - rho_e_rr = last(u_rr) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Average each factor of products in flux - rho_avg = 1 / 2 * (rho_ll + rho_rr) - v1_avg = 1 / 2 * (v1_ll + v1_rr) - v2_avg = 1 / 2 * (v2_ll + v2_rr) - p_avg = 1 / 2 * (p_ll + p_rr) - e_avg = 1 / 2 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_avg * v1_avg - f2 = rho_avg * v1_avg * v1_avg + p_avg - f3 = rho_avg * v1_avg * v2_avg - f4 = (rho_avg * e_avg + p_avg) * v1_avg - else - f1 = rho_avg * v2_avg - f2 = rho_avg * v2_avg * v1_avg - f3 = rho_avg * v2_avg * v2_avg + p_avg - f4 = (rho_avg * e_avg + p_avg) * v2_avg - end - - return SVector(f1, f2, f3, f4) - end - - @inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_e_ll = last(u_ll) - rho_e_rr = last(u_rr) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Average each factor of products in flux - rho_avg = 0.5 * (rho_ll + rho_rr) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] - p_avg = 0.5 * (p_ll + p_rr) - e_avg = 0.5 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_avg * v_dot_n_avg - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - f4 = f1 * e_avg + p_avg * v_dot_n_avg - - return SVector(f1, f2, f3, f4) - end - - """ - flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) - - Entropy conserving two-point flux by - - Chandrashekar (2013) - Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes - for Compressible Euler and Navier-Stokes Equations - [DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) - """ - @inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - beta_ll = 0.5 * rho_ll / p_ll - beta_rr = 0.5 * rho_rr / p_rr - specific_kin_ll = 0.5 * (v1_ll^2 + v2_ll^2) - specific_kin_rr = 0.5 * (v1_rr^2 + v2_rr^2) - - # Compute the necessary mean values - rho_avg = 0.5 * (rho_ll + rho_rr) - rho_mean = ln_mean(rho_ll, rho_rr) - beta_mean = ln_mean(beta_ll, beta_rr) - beta_avg = 0.5 * (beta_ll + beta_rr) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_mean = 0.5 * rho_avg / beta_avg - velocity_square_avg = specific_kin_ll + specific_kin_rr - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_mean - f3 = f1 * v2_avg - f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + - f2 * v1_avg + f3 * v2_avg - else - f1 = rho_mean * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_mean - f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + - f2 * v1_avg + f3 * v2_avg - end - - return SVector(f1, f2, f3, f4) - end - - """ - flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) - - Entropy conserving and kinetic energy preserving two-point flux by - - Hendrik Ranocha (2018) - Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods - for Hyperbolic Balance Laws - [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) - See also - - Hendrik Ranocha (2020) - Entropy Conserving and Kinetic Energy Preserving Numerical Methods for - the Euler Equations Using Summation-by-Parts Operators - [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) - """ - @inline function flux_ranocha(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Compute the necessary mean values - rho_mean = ln_mean(rho_ll, rho_rr) - # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` - # in exact arithmetic since - # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) - # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) - inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_avg = 0.5 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_avg - f3 = f1 * v2_avg - f4 = f1 * - (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + - 0.5 * (p_ll * v1_rr + p_rr * v1_ll) - else - f1 = rho_mean * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_avg - f4 = f1 * - (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + - 0.5 * (p_ll * v2_rr + p_rr * v2_ll) - end - - return SVector(f1, f2, f3, f4) - end - - @inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # Compute the necessary mean values - rho_mean = ln_mean(rho_ll, rho_rr) - # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` - # in exact arithmetic since - # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) - # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) - inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_avg = 0.5 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_mean * 0.5 * (v_dot_n_ll + v_dot_n_rr) - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - f4 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) - + - 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) - - return SVector(f1, f2, f3, f4) - end - - """ - splitting_steger_warming(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) - - Splitting of the compressible Euler flux of Steger and Warming. - - Returns a tuple of the fluxes "minus" (associated with waves going into the - negative axis direction) and "plus" (associated with waves going into the - positive axis direction). If only one of the fluxes is required, use the - function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. - - !!! warning "Experimental implementation (upwind SBP)" - This is an experimental feature and may change in future releases. - - ## References - - - Joseph L. Steger and R. F. Warming (1979) - Flux Vector Splitting of the Inviscid Gasdynamic Equations - With Application to Finite Difference Methods - [NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf) - """ - @inline function splitting_steger_warming(u, orientation::Integer, + c = 2.0 + A = 0.1 + ini = c + A * sin(pi * (x[1] + x[2] - t)) + G = 1.0 # gravitational constant + + rho = ini + v1 = 1.0 + v2 = 1.0 + p = ini^2 * G / pi # * 2 / ndims, but ndims==2 here + + return prim2cons(SVector(rho, v1, v2, p), equations) +end + +""" + source_terms_eoc_test_coupled_euler_gravity(u, x, t, equations::CompressibleEulerEquations2D) + +Setup used for convergence tests of the Euler equations with self-gravity used in +- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) +in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). +""" +@inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t, + equations::CompressibleEulerEquations2D) + # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` + c = 2.0 + A = 0.1 + G = 1.0 # gravitational constant, must match coupling solver + C_grav = -2 * G / pi # 2 == 4 / ndims + + x1, x2 = x + si, co = sincos(pi * (x1 + x2 - t)) + rhox = A * pi * co + rho = c + A * si + + du1 = rhox + du2 = rhox + du3 = rhox + du4 = (1.0 - C_grav * rho) * rhox + + return SVector(du1, du2, du3, du4) +end + +""" + source_terms_eoc_test_euler(u, x, t, equations::CompressibleEulerEquations2D) + +Setup used for convergence tests of the Euler equations with self-gravity used in +- Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) +in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). +""" +@inline function source_terms_eoc_test_euler(u, x, t, + equations::CompressibleEulerEquations2D) + # Same settings as in `initial_condition_eoc_test_coupled_euler_gravity` + c = 2.0 + A = 0.1 + G = 1.0 + C_grav = -2 * G / pi # 2 == 4 / ndims + + x1, x2 = x + si, co = sincos(pi * (x1 + x2 - t)) + rhox = A * pi * co + rho = c + A * si + + du1 = rhox + du2 = rhox * (1 - C_grav * rho) + du3 = rhox * (1 - C_grav * rho) + du4 = rhox * (1 - 3 * C_grav * rho) + + return SVector(du1, du2, du3, du4) +end + +""" + boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, + equations::CompressibleEulerEquations2D) + +Determine the boundary numerical surface flux for a slip wall condition. +Imposes a zero normal velocity at the wall. +Density is taken from the internal solution state and pressure is computed as an +exact solution of a 1D Riemann problem. Further details about this boundary state +are available in the paper: +- J. J. W. van der Vegt and H. van der Ven (2002) + Slip flow boundary conditions in discontinuous Galerkin discretizations of + the Euler equations of gas dynamics + [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) + +Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book +- Eleuterio F. Toro (2009) + Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + 3rd edition + [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + +Should be used together with [`UnstructuredMesh2D`](@ref). +""" +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) - fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations) - fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations) - return fm, fp + norm_ = norm(normal_direction) + # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later + normal = normal_direction / norm_ + + # rotate the internal solution state + u_local = rotate_to_x(u_inner, normal, equations) + + # compute the primitive variables + rho_local, v_normal, v_tangent, p_local = cons2prim(u_local, equations) + + # Get the solution of the pressure Riemann problem + # See Section 6.3.3 of + # Eleuterio F. Toro (2009) + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + if v_normal <= 0.0 + sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed + p_star = p_local * + (1 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2 * + equations.gamma * + equations.inv_gamma_minus_one) + else # v_normal > 0.0 + A = 2 / ((equations.gamma + 1) * rho_local) + B = p_local * (equations.gamma - 1) / (equations.gamma + 1) + p_star = p_local + + 0.5 * v_normal / A * + (v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B))) end - - @inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer, + + # For the slip wall we directly set the flux as the normal velocity is zero + return SVector(zero(eltype(u_inner)), + p_star * normal[1], + p_star * normal[2], + zero(eltype(u_inner))) * norm_ +end + +""" + boundary_condition_slip_wall(u_inner, orientation, direction, x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) + +Should be used together with [`TreeMesh`](@ref). +""" +@inline function boundary_condition_slip_wall(u_inner, orientation, + direction, x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - a = sqrt(equations.gamma * p / rho) - - if orientation == 1 - lambda1 = v1 - lambda2 = v1 + a - lambda3 = v1 - a - - lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) - lambda2_p = positive_part(lambda2) - lambda3_p = positive_part(lambda3) - - alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p - - rho_2gamma = 0.5 * rho / equations.gamma - f1p = rho_2gamma * alpha_p - f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p)) - f3p = rho_2gamma * alpha_p * v2 - f4p = rho_2gamma * - (alpha_p * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_p - lambda3_p) - + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) - else # orientation == 2 - lambda1 = v2 - lambda2 = v2 + a - lambda3 = v2 - a - - lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) - lambda2_p = positive_part(lambda2) - lambda3_p = positive_part(lambda3) - - alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p - - rho_2gamma = 0.5 * rho / equations.gamma - f1p = rho_2gamma * alpha_p - f2p = rho_2gamma * alpha_p * v1 - f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p)) - f4p = rho_2gamma * - (alpha_p * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_p - lambda3_p) - + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) - end - return SVector(f1p, f2p, f3p, f4p) + # get the appropriate normal vector from the orientation + if orientation == 1 + normal_direction = SVector(1, 0) + else # orientation == 2 + normal_direction = SVector(0, 1) end - - @inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer, + + # compute and return the flux using `boundary_condition_slip_wall` routine above + return boundary_condition_slip_wall(u_inner, normal_direction, direction, + x, t, surface_flux_function, equations) +end + +""" + boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) + +Should be used together with [`StructuredMesh`](@ref). +""" +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + direction, x, t, + surface_flux_function, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - a = sqrt(equations.gamma * p / rho) - - if orientation == 1 - lambda1 = v1 - lambda2 = v1 + a - lambda3 = v1 - a - - lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) - lambda2_m = negative_part(lambda2) - lambda3_m = negative_part(lambda3) - - alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m - - rho_2gamma = 0.5 * rho / equations.gamma - f1m = rho_2gamma * alpha_m - f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m)) - f3m = rho_2gamma * alpha_m * v2 - f4m = rho_2gamma * - (alpha_m * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_m - lambda3_m) - + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) - else # orientation == 2 - lambda1 = v2 - lambda2 = v2 + a - lambda3 = v2 - a - - lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) - lambda2_m = negative_part(lambda2) - lambda3_m = negative_part(lambda3) - - alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m - - rho_2gamma = 0.5 * rho / equations.gamma - f1m = rho_2gamma * alpha_m - f2m = rho_2gamma * alpha_m * v1 - f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m)) - f4m = rho_2gamma * - (alpha_m * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_m - lambda3_m) - + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) - end - return SVector(f1m, f2m, f3m, f4m) + # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back + # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh + if isodd(direction) + boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, + x, t, surface_flux_function, + equations) + else + boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, + x, t, surface_flux_function, + equations) end - - """ - splitting_vanleer_haenel(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - splitting_vanleer_haenel(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, + + return boundary_flux +end + +# Calculate 2D flux for a single point +@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = rho_v1 * v2 + f4 = (rho_e + p) * v1 + else + f1 = rho_v2 + f2 = rho_v2 * v1 + f3 = rho_v2 * v2 + p + f4 = (rho_e + p) * v2 + end + return SVector(f1, f2, f3, f4) +end + +# Calculate 2D flux for a single point in the normal direction +# Note, this directional vector is not normalized +@inline function flux(u, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_e = last(u) + rho, v1, v2, p = cons2prim(u, equations) + + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = rho_v_normal + f2 = rho_v_normal * v1 + p * normal_direction[1] + f3 = rho_v_normal * v2 + p * normal_direction[2] + f4 = (rho_e + p) * v_normal + return SVector(f1, f2, f3, f4) +end + +""" + flux_shima_etal(u_ll, u_rr, orientation_or_normal_direction, + equations::CompressibleEulerEquations2D) + +This flux is is a modification of the original kinetic energy preserving two-point flux by +- Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018) + Kinetic energy and entropy preserving schemes for compressible flows + by split convective forms + [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) + +The modification is in the energy flux to guarantee pressure equilibrium and was developed by +- Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) + Preventing spurious pressure oscillations in split convective form discretizations for + compressible flows + [DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060) +""" +@inline function flux_shima_etal(u_ll, u_rr, orientation::Integer, equations::CompressibleEulerEquations2D) - - Splitting of the compressible Euler flux from van Leer. This splitting further - contains a reformulation due to Hänel et al. where the energy flux uses the - enthalpy. The pressure splitting is independent from the splitting of the - convective terms. As such there are many pressure splittings suggested across - the literature. We implement the 'p4' variant suggested by Liou and Steffen as - it proved the most robust in practice. - - Returns a tuple of the fluxes "minus" (associated with waves going into the - negative axis direction) and "plus" (associated with waves going into the - positive axis direction). If only one of the fluxes is required, use the - function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. - - !!! warning "Experimental implementation (upwind SBP)" - This is an experimental feature and may change in future releases. - - ## References - - - Bram van Leer (1982) - Flux-Vector Splitting for the Euler Equation - [DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5) - - D. Hänel, R. Schwane and G. Seider (1987) - On the accuracy of upwind schemes for the solution of the Navier-Stokes equations - [DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105) - - Meng-Sing Liou and Chris J. Steffen, Jr. (1991) - High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting - [NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425) - """ - @inline function splitting_vanleer_haenel(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - fm = splitting_vanleer_haenel(u, Val{:minus}(), orientation, equations) - fp = splitting_vanleer_haenel(u, Val{:plus}(), orientation, equations) - return fm, fp + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Average each factor of products in flux + rho_avg = 1 / 2 * (rho_ll + rho_rr) + v1_avg = 1 / 2 * (v1_ll + v1_rr) + v2_avg = 1 / 2 * (v2_ll + v2_rr) + p_avg = 1 / 2 * (p_ll + p_rr) + kin_avg = 1 / 2 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + pv1_avg = 1 / 2 * (p_ll * v1_rr + p_rr * v1_ll) + f1 = rho_avg * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * v2_avg + f4 = p_avg * v1_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv1_avg + else + pv2_avg = 1 / 2 * (p_ll * v2_rr + p_rr * v2_ll) + f1 = rho_avg * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_avg + f4 = p_avg * v2_avg * equations.inv_gamma_minus_one + f1 * kin_avg + pv2_avg end - - @inline function splitting_vanleer_haenel(u, ::Val{:plus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - - if orientation == 1 - M = v1 / a - p_plus = 0.5 * (1 + equations.gamma * M) * p - - f1p = 0.25 * rho * a * (M + 1)^2 - f2p = f1p * v1 + p_plus - f3p = f1p * v2 - f4p = f1p * H - else # orientation == 2 - M = v2 / a - p_plus = 0.5 * (1 + equations.gamma * M) * p - - f1p = 0.25 * rho * a * (M + 1)^2 - f2p = f1p * v1 - f3p = f1p * v2 + p_plus - f4p = f1p * H - end - return SVector(f1p, f2p, f3p, f4p) + + return SVector(f1, f2, f3, f4) +end + +@inline function flux_shima_etal(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # Average each factor of products in flux + rho_avg = 1 / 2 * (rho_ll + rho_rr) + v1_avg = 1 / 2 * (v1_ll + v1_rr) + v2_avg = 1 / 2 * (v2_ll + v2_rr) + v_dot_n_avg = 1 / 2 * (v_dot_n_ll + v_dot_n_rr) + p_avg = 1 / 2 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_avg * v_dot_n_avg + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + f4 = (f1 * velocity_square_avg + + p_avg * v_dot_n_avg * equations.inv_gamma_minus_one + + 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) + + return SVector(f1, f2, f3, f4) +end + +""" + flux_kennedy_gruber(u_ll, u_rr, orientation_or_normal_direction, + equations::CompressibleEulerEquations2D) + +Kinetic energy preserving two-point flux by +- Kennedy and Gruber (2008) + Reduced aliasing formulations of the convective terms within the + Navier-Stokes equations for a compressible fluid + [DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) +""" +@inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_e_ll = last(u_ll) + rho_e_rr = last(u_rr) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Average each factor of products in flux + rho_avg = 1 / 2 * (rho_ll + rho_rr) + v1_avg = 1 / 2 * (v1_ll + v1_rr) + v2_avg = 1 / 2 * (v2_ll + v2_rr) + p_avg = 1 / 2 * (p_ll + p_rr) + e_avg = 1 / 2 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_avg * v1_avg + f2 = rho_avg * v1_avg * v1_avg + p_avg + f3 = rho_avg * v1_avg * v2_avg + f4 = (rho_avg * e_avg + p_avg) * v1_avg + else + f1 = rho_avg * v2_avg + f2 = rho_avg * v2_avg * v1_avg + f3 = rho_avg * v2_avg * v2_avg + p_avg + f4 = (rho_avg * e_avg + p_avg) * v2_avg end - - @inline function splitting_vanleer_haenel(u, ::Val{:minus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - - if orientation == 1 - M = v1 / a - p_minus = 0.5 * (1 - equations.gamma * M) * p - - f1m = -0.25 * rho * a * (M - 1)^2 - f2m = f1m * v1 + p_minus - f3m = f1m * v2 - f4m = f1m * H - else # orientation == 2 - M = v2 / a - p_minus = 0.5 * (1 - equations.gamma * M) * p - - f1m = -0.25 * rho * a * (M - 1)^2 - f2m = f1m * v1 - f3m = f1m * v2 + p_minus - f4m = f1m * H - end - return SVector(f1m, f2m, f3m, f4m) + + return SVector(f1, f2, f3, f4) +end + +@inline function flux_kennedy_gruber(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_e_ll = last(u_ll) + rho_e_rr = last(u_rr) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Average each factor of products in flux + rho_avg = 0.5 * (rho_ll + rho_rr) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + v_dot_n_avg = v1_avg * normal_direction[1] + v2_avg * normal_direction[2] + p_avg = 0.5 * (p_ll + p_rr) + e_avg = 0.5 * (rho_e_ll / rho_ll + rho_e_rr / rho_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_avg * v_dot_n_avg + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + f4 = f1 * e_avg + p_avg * v_dot_n_avg + + return SVector(f1, f2, f3, f4) +end + +""" + flux_chandrashekar(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) + +Entropy conserving two-point flux by +- Chandrashekar (2013) + Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes + for Compressible Euler and Navier-Stokes Equations + [DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) +""" +@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + beta_ll = 0.5 * rho_ll / p_ll + beta_rr = 0.5 * rho_rr / p_rr + specific_kin_ll = 0.5 * (v1_ll^2 + v2_ll^2) + specific_kin_rr = 0.5 * (v1_rr^2 + v2_rr^2) + + # Compute the necessary mean values + rho_avg = 0.5 * (rho_ll + rho_rr) + rho_mean = ln_mean(rho_ll, rho_rr) + beta_mean = ln_mean(beta_ll, beta_rr) + beta_avg = 0.5 * (beta_ll + beta_rr) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_mean = 0.5 * rho_avg / beta_avg + velocity_square_avg = specific_kin_ll + specific_kin_rr + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_mean + f3 = f1 * v2_avg + f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + + f2 * v1_avg + f3 * v2_avg + else + f1 = rho_mean * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_mean + f4 = f1 * 0.5 * (1 / (equations.gamma - 1) / beta_mean - velocity_square_avg) + + f2 * v1_avg + f3 * v2_avg end - - """ - splitting_lax_friedrichs(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) - - Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)` - and `f⁻ = 0.5 (f - λ u)` similar to a flux splitting one would apply, e.g., - to Burgers' equation. - - Returns a tuple of the fluxes "minus" (associated with waves going into the - negative axis direction) and "plus" (associated with waves going into the - positive axis direction). If only one of the fluxes is required, use the - function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. - - !!! warning "Experimental implementation (upwind SBP)" - This is an experimental feature and may change in future releases. - """ - @inline function splitting_lax_friedrichs(u, orientation::Integer, - equations::CompressibleEulerEquations2D) - fm = splitting_lax_friedrichs(u, Val{:minus}(), orientation, equations) - fp = splitting_lax_friedrichs(u, Val{:plus}(), orientation, equations) - return fm, fp + + return SVector(f1, f2, f3, f4) +end + +""" + flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, + equations::CompressibleEulerEquations2D) + +Entropy conserving and kinetic energy preserving two-point flux by +- Hendrik Ranocha (2018) + Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods + for Hyperbolic Balance Laws + [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) +See also +- Hendrik Ranocha (2020) + Entropy Conserving and Kinetic Energy Preserving Numerical Methods for + the Euler Equations Using Summation-by-Parts Operators + [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) +""" +@inline function flux_ranocha(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` + # in exact arithmetic since + # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) + # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) + inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_avg = 0.5 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * v2_avg + f4 = f1 * + (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + + 0.5 * (p_ll * v1_rr + p_rr * v1_ll) + else + f1 = rho_mean * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_avg + f4 = f1 * + (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + + 0.5 * (p_ll * v2_rr + p_rr * v2_ll) end - - @inline function splitting_lax_friedrichs(u, ::Val{:plus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) - - if orientation == 1 - #lambda = 0.5 * (abs(v1) + a) - f1p = 0.5 * rho * v1 + lambda * u[1] - f2p = 0.5 * rho * v1 * v1 + 0.5 * p + lambda * u[2] - f3p = 0.5 * rho * v1 * v2 + lambda * u[3] - f4p = 0.5 * rho * v1 * H + lambda * u[4] - else # orientation == 2 - #lambda = 0.5 * (abs(v2) + a) - f1p = 0.5 * rho * v2 + lambda * u[1] - f2p = 0.5 * rho * v2 * v1 + lambda * u[2] - f3p = 0.5 * rho * v2 * v2 + 0.5 * p + lambda * u[3] - f4p = 0.5 * rho * v2 * H + lambda * u[4] - end - return SVector(f1p, f2p, f3p, f4p) + + return SVector(f1, f2, f3, f4) +end + +@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` + # in exact arithmetic since + # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) + # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) + inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_avg = 0.5 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll * v1_rr + v2_ll * v2_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_mean * 0.5 * (v_dot_n_ll + v_dot_n_rr) + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + f4 = (f1 * (velocity_square_avg + inv_rho_p_mean * equations.inv_gamma_minus_one) + + + 0.5 * (p_ll * v_dot_n_rr + p_rr * v_dot_n_ll)) + + return SVector(f1, f2, f3, f4) +end + +""" + splitting_steger_warming(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}} + orientation::Integer, + equations::CompressibleEulerEquations2D) + +Splitting of the compressible Euler flux of Steger and Warming. + +Returns a tuple of the fluxes "minus" (associated with waves going into the +negative axis direction) and "plus" (associated with waves going into the +positive axis direction). If only one of the fluxes is required, use the +function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. + +!!! warning "Experimental implementation (upwind SBP)" + This is an experimental feature and may change in future releases. + +## References + +- Joseph L. Steger and R. F. Warming (1979) + Flux Vector Splitting of the Inviscid Gasdynamic Equations + With Application to Finite Difference Methods + [NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf) +""" +@inline function splitting_steger_warming(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + fm = splitting_steger_warming(u, Val{:minus}(), orientation, equations) + fp = splitting_steger_warming(u, Val{:plus}(), orientation, equations) + return fm, fp +end + +@inline function splitting_steger_warming(u, ::Val{:plus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + a = sqrt(equations.gamma * p / rho) + + if orientation == 1 + lambda1 = v1 + lambda2 = v1 + a + lambda3 = v1 - a + + lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) + lambda2_p = positive_part(lambda2) + lambda3_p = positive_part(lambda3) + + alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p + + rho_2gamma = 0.5 * rho / equations.gamma + f1p = rho_2gamma * alpha_p + f2p = rho_2gamma * (alpha_p * v1 + a * (lambda2_p - lambda3_p)) + f3p = rho_2gamma * alpha_p * v2 + f4p = rho_2gamma * + (alpha_p * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_p - lambda3_p) + + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) + else # orientation == 2 + lambda1 = v2 + lambda2 = v2 + a + lambda3 = v2 - a + + lambda1_p = positive_part(lambda1) # Same as (lambda_i + abs(lambda_i)) / 2, but faster :) + lambda2_p = positive_part(lambda2) + lambda3_p = positive_part(lambda3) + + alpha_p = 2 * (equations.gamma - 1) * lambda1_p + lambda2_p + lambda3_p + + rho_2gamma = 0.5 * rho / equations.gamma + f1p = rho_2gamma * alpha_p + f2p = rho_2gamma * alpha_p * v1 + f3p = rho_2gamma * (alpha_p * v2 + a * (lambda2_p - lambda3_p)) + f4p = rho_2gamma * + (alpha_p * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_p - lambda3_p) + + a^2 * (lambda2_p + lambda3_p) * equations.inv_gamma_minus_one) end - - @inline function splitting_lax_friedrichs(u, ::Val{:minus}, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - a = sqrt(equations.gamma * p / rho) - H = (rho_e + p) / rho - lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) - - if orientation == 1 - #lambda = 0.5 * (abs(v1) + a) - f1m = 0.5 * rho * v1 - lambda * u[1] - f2m = 0.5 * rho * v1 * v1 + 0.5 * p - lambda * u[2] - f3m = 0.5 * rho * v1 * v2 - lambda * u[3] - f4m = 0.5 * rho * v1 * H - lambda * u[4] - else # orientation == 2 - #lambda = 0.5 * (abs(v2) + a) - f1m = 0.5 * rho * v2 - lambda * u[1] - f2m = 0.5 * rho * v2 * v1 - lambda * u[2] - f3m = 0.5 * rho * v2 * v2 + 0.5 * p - lambda * u[3] - f4m = 0.5 * rho * v2 * H - lambda * u[4] - end - return SVector(f1m, f2m, f3m, f4m) + return SVector(f1p, f2p, f3p, f4p) +end + +@inline function splitting_steger_warming(u, ::Val{:minus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + a = sqrt(equations.gamma * p / rho) + + if orientation == 1 + lambda1 = v1 + lambda2 = v1 + a + lambda3 = v1 - a + + lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) + lambda2_m = negative_part(lambda2) + lambda3_m = negative_part(lambda3) + + alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m + + rho_2gamma = 0.5 * rho / equations.gamma + f1m = rho_2gamma * alpha_m + f2m = rho_2gamma * (alpha_m * v1 + a * (lambda2_m - lambda3_m)) + f3m = rho_2gamma * alpha_m * v2 + f4m = rho_2gamma * + (alpha_m * 0.5 * (v1^2 + v2^2) + a * v1 * (lambda2_m - lambda3_m) + + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) + else # orientation == 2 + lambda1 = v2 + lambda2 = v2 + a + lambda3 = v2 - a + + lambda1_m = negative_part(lambda1) # Same as (lambda_i - abs(lambda_i)) / 2, but faster :) + lambda2_m = negative_part(lambda2) + lambda3_m = negative_part(lambda3) + + alpha_m = 2 * (equations.gamma - 1) * lambda1_m + lambda2_m + lambda3_m + + rho_2gamma = 0.5 * rho / equations.gamma + f1m = rho_2gamma * alpha_m + f2m = rho_2gamma * alpha_m * v1 + f3m = rho_2gamma * (alpha_m * v2 + a * (lambda2_m - lambda3_m)) + f4m = rho_2gamma * + (alpha_m * 0.5 * (v1^2 + v2^2) + a * v2 * (lambda2_m - lambda3_m) + + a^2 * (lambda2_m + lambda3_m) * equations.inv_gamma_minus_one) end - - # Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the - # maximum velocity magnitude plus the maximum speed of sound - @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Get the velocity value in the appropriate direction - if orientation == 1 - v_ll = v1_ll - v_rr = v1_rr - else # orientation == 2 - v_ll = v2_ll - v_rr = v2_rr - end - # Calculate sound speeds - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - λ_max = max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) + return SVector(f1m, f2m, f3m, f4m) +end + +""" + splitting_vanleer_haenel(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + splitting_vanleer_haenel(u, which::Union{Val{:minus}, Val{:plus}} + orientation::Integer, + equations::CompressibleEulerEquations2D) + +Splitting of the compressible Euler flux from van Leer. This splitting further +contains a reformulation due to Hänel et al. where the energy flux uses the +enthalpy. The pressure splitting is independent from the splitting of the +convective terms. As such there are many pressure splittings suggested across +the literature. We implement the 'p4' variant suggested by Liou and Steffen as +it proved the most robust in practice. + +Returns a tuple of the fluxes "minus" (associated with waves going into the +negative axis direction) and "plus" (associated with waves going into the +positive axis direction). If only one of the fluxes is required, use the +function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. + +!!! warning "Experimental implementation (upwind SBP)" + This is an experimental feature and may change in future releases. + +## References + +- Bram van Leer (1982) + Flux-Vector Splitting for the Euler Equation + [DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5) +- D. Hänel, R. Schwane and G. Seider (1987) + On the accuracy of upwind schemes for the solution of the Navier-Stokes equations + [DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105) +- Meng-Sing Liou and Chris J. Steffen, Jr. (1991) + High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting + [NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425) +""" +@inline function splitting_vanleer_haenel(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + fm = splitting_vanleer_haenel(u, Val{:minus}(), orientation, equations) + fp = splitting_vanleer_haenel(u, Val{:plus}(), orientation, equations) + return fm, fp +end + +@inline function splitting_vanleer_haenel(u, ::Val{:plus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + + if orientation == 1 + M = v1 / a + p_plus = 0.5 * (1 + equations.gamma * M) * p + + f1p = 0.25 * rho * a * (M + 1)^2 + f2p = f1p * v1 + p_plus + f3p = f1p * v2 + f4p = f1p * H + else # orientation == 2 + M = v2 / a + p_plus = 0.5 * (1 + equations.gamma * M) * p + + f1p = 0.25 * rho * a * (M + 1)^2 + f2p = f1p * v1 + f3p = f1p * v2 + p_plus + f4p = f1p * H end - - @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # Calculate normal velocities and sound speed - # left - v_ll = (v1_ll * normal_direction[1] - + - v2_ll * normal_direction[2]) - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - # right - v_rr = (v1_rr * normal_direction[1] - + - v2_rr * normal_direction[2]) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) + return SVector(f1p, f2p, f3p, f4p) +end + +@inline function splitting_vanleer_haenel(u, ::Val{:minus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + + if orientation == 1 + M = v1 / a + p_minus = 0.5 * (1 - equations.gamma * M) * p + + f1m = -0.25 * rho * a * (M - 1)^2 + f2m = f1m * v1 + p_minus + f3m = f1m * v2 + f4m = f1m * H + else # orientation == 2 + M = v2 / a + p_minus = 0.5 * (1 - equations.gamma * M) * p + + f1m = -0.25 * rho * a * (M - 1)^2 + f2m = f1m * v1 + f3m = f1m * v2 + p_minus + f4m = f1m * H end - - # Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes - @inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - if orientation == 1 # x-direction - λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll) - λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr) - else # y-direction - λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll) - λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr) - end - - return λ_min, λ_max + return SVector(f1m, f2m, f3m, f4m) +end + +""" + splitting_lax_friedrichs(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}} + orientation::Integer, + equations::CompressibleEulerEquations2D) + +Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)` +and `f⁻ = 0.5 (f - λ u)` similar to a flux splitting one would apply, e.g., +to Burgers' equation. + +Returns a tuple of the fluxes "minus" (associated with waves going into the +negative axis direction) and "plus" (associated with waves going into the +positive axis direction). If only one of the fluxes is required, use the +function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`. + +!!! warning "Experimental implementation (upwind SBP)" + This is an experimental feature and may change in future releases. +""" +@inline function splitting_lax_friedrichs(u, orientation::Integer, + equations::CompressibleEulerEquations2D) + fm = splitting_lax_friedrichs(u, Val{:minus}(), orientation, equations) + fp = splitting_lax_friedrichs(u, Val{:plus}(), orientation, equations) + return fm, fp +end + +@inline function splitting_lax_friedrichs(u, ::Val{:plus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) + + if orientation == 1 + #lambda = 0.5 * (abs(v1) + a) + f1p = 0.5 * rho * v1 + lambda * u[1] + f2p = 0.5 * rho * v1 * v1 + 0.5 * p + lambda * u[2] + f3p = 0.5 * rho * v1 * v2 + lambda * u[3] + f4p = 0.5 * rho * v1 * H + lambda * u[4] + else # orientation == 2 + #lambda = 0.5 * (abs(v2) + a) + f1p = 0.5 * rho * v2 + lambda * u[1] + f2p = 0.5 * rho * v2 * v1 + lambda * u[2] + f3p = 0.5 * rho * v2 * v2 + 0.5 * p + lambda * u[3] + f4p = 0.5 * rho * v2 * H + lambda * u[4] end - - @inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - norm_ = norm(normal_direction) - # The v_normals are already scaled by the norm - λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_ - λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_ - - return λ_min, λ_max + return SVector(f1p, f2p, f3p, f4p) +end + +@inline function splitting_lax_friedrichs(u, ::Val{:minus}, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + a = sqrt(equations.gamma * p / rho) + H = (rho_e + p) / rho + lambda = 0.5 * (sqrt(v1^2 + v2^2) + a) + + if orientation == 1 + #lambda = 0.5 * (abs(v1) + a) + f1m = 0.5 * rho * v1 - lambda * u[1] + f2m = 0.5 * rho * v1 * v1 + 0.5 * p - lambda * u[2] + f3m = 0.5 * rho * v1 * v2 - lambda * u[3] + f4m = 0.5 * rho * v1 * H - lambda * u[4] + else # orientation == 2 + #lambda = 0.5 * (abs(v2) + a) + f1m = 0.5 * rho * v2 - lambda * u[1] + f2m = 0.5 * rho * v2 * v1 - lambda * u[2] + f3m = 0.5 * rho * v2 * v2 + 0.5 * p - lambda * u[3] + f4m = 0.5 * rho * v2 * H - lambda * u[4] end - - # More refined estimates for minimum and maximum wave speeds for HLL-type fluxes - @inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - if orientation == 1 # x-direction - λ_min = min(v1_ll - c_ll, v1_rr - c_rr) - λ_max = max(v1_ll + c_ll, v1_rr + c_rr) - else # y-direction - λ_min = min(v2_ll - c_ll, v2_rr - c_rr) - λ_max = max(v2_ll + c_ll, v2_rr + c_rr) - end - - return λ_min, λ_max + return SVector(f1m, f2m, f3m, f4m) +end + +# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the +# maximum velocity magnitude plus the maximum speed of sound +@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Get the velocity value in the appropriate direction + if orientation == 1 + v_ll = v1_ll + v_rr = v1_rr + else # orientation == 2 + v_ll = v2_ll + v_rr = v2_rr end - - # More refined estimates for minimum and maximum wave speeds for HLL-type fluxes - @inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleEulerEquations2D) - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - norm_ = norm(normal_direction) - - c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_ - c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_ - - v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # The v_normals are already scaled by the norm - λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr) - λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr) - - return λ_min, λ_max + # Calculate sound speeds + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + λ_max = max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) +end + +@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # Calculate normal velocities and sound speed + # left + v_ll = (v1_ll * normal_direction[1] + + + v2_ll * normal_direction[2]) + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + # right + v_rr = (v1_rr * normal_direction[1] + + + v2_rr * normal_direction[2]) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) +end + +# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes +@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + if orientation == 1 # x-direction + λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll) + λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr) + else # y-direction + λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll) + λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr) end - - # Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction - # has been normalized prior to this rotation of the state vector - @inline function rotate_to_x(u, normal_vector, equations::CompressibleEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 n_2 0; - # 0 t_1 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] + s * u[3], - -s * u[2] + c * u[3], - u[4]) + + return λ_min, λ_max +end + +@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + norm_ = norm(normal_direction) + # The v_normals are already scaled by the norm + λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_ + λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_ + + return λ_min, λ_max +end + +# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes +@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + if orientation == 1 # x-direction + λ_min = min(v1_ll - c_ll, v1_rr - c_rr) + λ_max = max(v1_ll + c_ll, v1_rr + c_rr) + else # y-direction + λ_min = min(v2_ll - c_ll, v2_rr - c_rr) + λ_max = max(v2_ll + c_ll, v2_rr + c_rr) end - - # Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction - # has been normalized prior to this back-rotation of the state vector - @inline function rotate_from_x(u, normal_vector, - equations::CompressibleEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D back-rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 t_1 0; - # 0 n_2 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] - s * u[3], - s * u[2] + c * u[3], - u[4]) + + return λ_min, λ_max +end + +# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes +@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleEulerEquations2D) + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + norm_ = norm(normal_direction) + + c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_ + c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_ + + v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # The v_normals are already scaled by the norm + λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr) + λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr) + + return λ_min, λ_max +end + +# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction +# has been normalized prior to this rotation of the state vector +@inline function rotate_to_x(u, normal_vector, equations::CompressibleEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 n_2 0; + # 0 t_1 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] + s * u[3], + -s * u[2] + c * u[3], + u[4]) +end + +# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction +# has been normalized prior to this back-rotation of the state vector +@inline function rotate_from_x(u, normal_vector, + equations::CompressibleEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D back-rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 t_1 0; + # 0 n_2 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] - s * u[3], + s * u[2] + c * u[3], + u[4]) +end + +""" + flux_hllc(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) + +Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro +[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf) +Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140) +""" +function flux_hllc(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Calculate primitive variables and speed of sound + rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + v2_ll = rho_v2_ll / rho_ll + e_ll = rho_e_ll / rho_ll + p_ll = (equations.gamma - 1) * (rho_e_ll - 1 / 2 * rho_ll * (v1_ll^2 + v2_ll^2)) + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + + v1_rr = rho_v1_rr / rho_rr + v2_rr = rho_v2_rr / rho_rr + e_rr = rho_e_rr / rho_rr + p_rr = (equations.gamma - 1) * (rho_e_rr - 1 / 2 * rho_rr * (v1_rr^2 + v2_rr^2)) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + # Obtain left and right fluxes + f_ll = flux(u_ll, orientation, equations) + f_rr = flux(u_rr, orientation, equations) + + # Compute Roe averages + sqrt_rho_ll = sqrt(rho_ll) + sqrt_rho_rr = sqrt(rho_rr) + sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr + if orientation == 1 # x-direction + vel_L = v1_ll + vel_R = v1_rr + ekin_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr)^2 + elseif orientation == 2 # y-direction + vel_L = v2_ll + vel_R = v2_rr + ekin_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr)^2 end - - """ - flux_hllc(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) - - Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro - [Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf) - Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140) - """ - function flux_hllc(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Calculate primitive variables and speed of sound - rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr = u_rr - - v1_ll = rho_v1_ll / rho_ll - v2_ll = rho_v2_ll / rho_ll - e_ll = rho_e_ll / rho_ll - p_ll = (equations.gamma - 1) * (rho_e_ll - 1 / 2 * rho_ll * (v1_ll^2 + v2_ll^2)) - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - - v1_rr = rho_v1_rr / rho_rr - v2_rr = rho_v2_rr / rho_rr - e_rr = rho_e_rr / rho_rr - p_rr = (equations.gamma - 1) * (rho_e_rr - 1 / 2 * rho_rr * (v1_rr^2 + v2_rr^2)) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - # Obtain left and right fluxes - f_ll = flux(u_ll, orientation, equations) - f_rr = flux(u_rr, orientation, equations) - - # Compute Roe averages - sqrt_rho_ll = sqrt(rho_ll) - sqrt_rho_rr = sqrt(rho_rr) - sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr - if orientation == 1 # x-direction - vel_L = v1_ll - vel_R = v1_rr - ekin_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr)^2 - elseif orientation == 2 # y-direction - vel_L = v2_ll - vel_R = v2_rr - ekin_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr)^2 - end - vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho - ekin_roe = 0.5 * (vel_roe^2 + ekin_roe / sum_sqrt_rho^2) - H_ll = (rho_e_ll + p_ll) / rho_ll - H_rr = (rho_e_rr + p_rr) / rho_rr - H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho - c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe)) - Ssl = min(vel_L - c_ll, vel_roe - c_roe) - Ssr = max(vel_R + c_rr, vel_roe + c_roe) - sMu_L = Ssl - vel_L - sMu_R = Ssr - vel_R - - if Ssl >= 0.0 - f1 = f_ll[1] - f2 = f_ll[2] - f3 = f_ll[3] - f4 = f_ll[4] - elseif Ssr <= 0.0 - f1 = f_rr[1] - f2 = f_rr[2] - f3 = f_rr[3] - f4 = f_rr[4] - else - SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) / - (rho_ll * sMu_L - rho_rr * sMu_R) - if Ssl <= 0.0 <= SStar - densStar = rho_ll * sMu_L / (Ssl - SStar) - enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L)) - UStar1 = densStar - UStar4 = densStar * enerStar - if orientation == 1 # x-direction - UStar2 = densStar * SStar - UStar3 = densStar * v2_ll - elseif orientation == 2 # y-direction - UStar2 = densStar * v1_ll - UStar3 = densStar * SStar - end - f1 = f_ll[1] + Ssl * (UStar1 - rho_ll) - f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll) - f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll) - f4 = f_ll[4] + Ssl * (UStar4 - rho_e_ll) - else - densStar = rho_rr * sMu_R / (Ssr - SStar) - enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R)) - UStar1 = densStar - UStar4 = densStar * enerStar - if orientation == 1 # x-direction - UStar2 = densStar * SStar - UStar3 = densStar * v2_rr - elseif orientation == 2 # y-direction - UStar2 = densStar * v1_rr - UStar3 = densStar * SStar - end - f1 = f_rr[1] + Ssr * (UStar1 - rho_rr) - f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr) - f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr) - f4 = f_rr[4] + Ssr * (UStar4 - rho_e_rr) + vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho + ekin_roe = 0.5 * (vel_roe^2 + ekin_roe / sum_sqrt_rho^2) + H_ll = (rho_e_ll + p_ll) / rho_ll + H_rr = (rho_e_rr + p_rr) / rho_rr + H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho + c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe)) + Ssl = min(vel_L - c_ll, vel_roe - c_roe) + Ssr = max(vel_R + c_rr, vel_roe + c_roe) + sMu_L = Ssl - vel_L + sMu_R = Ssr - vel_R + + if Ssl >= 0.0 + f1 = f_ll[1] + f2 = f_ll[2] + f3 = f_ll[3] + f4 = f_ll[4] + elseif Ssr <= 0.0 + f1 = f_rr[1] + f2 = f_rr[2] + f3 = f_rr[3] + f4 = f_rr[4] + else + SStar = (p_rr - p_ll + rho_ll * vel_L * sMu_L - rho_rr * vel_R * sMu_R) / + (rho_ll * sMu_L - rho_rr * sMu_R) + if Ssl <= 0.0 <= SStar + densStar = rho_ll * sMu_L / (Ssl - SStar) + enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L)) + UStar1 = densStar + UStar4 = densStar * enerStar + if orientation == 1 # x-direction + UStar2 = densStar * SStar + UStar3 = densStar * v2_ll + elseif orientation == 2 # y-direction + UStar2 = densStar * v1_ll + UStar3 = densStar * SStar end - end - return SVector(f1, f2, f3, f4) - end - - """ - flux_hlle(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) - - Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations. - Special estimates of the signal velocites and linearization of the Riemann problem developed - by Einfeldt to ensure that the internal energy and density remain positive during the computation - of the numerical flux. - - - Bernd Einfeldt (1988) - On Godunov-type methods for gas dynamics. - [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021) - - Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991) - On Godunov-type methods near low densities. - [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3) - """ - function flux_hlle(u_ll, u_rr, orientation::Integer, - equations::CompressibleEulerEquations2D) - # Calculate primitive variables, enthalpy and speed of sound - rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) - - # `u_ll[4]` is total energy `rho_e_ll` on the left - H_ll = (u_ll[4] + p_ll) / rho_ll - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - - # `u_rr[4]` is total energy `rho_e_rr` on the right - H_rr = (u_rr[4] + p_rr) / rho_rr - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - # Compute Roe averages - sqrt_rho_ll = sqrt(rho_ll) - sqrt_rho_rr = sqrt(rho_rr) - inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr) - - v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho - v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho - v_roe_mag = v1_roe^2 + v2_roe^2 - - H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho - c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5 * v_roe_mag)) - - # Compute convenience constant for positivity preservation, see - # https://doi.org/10.1016/0021-9991(91)90211-3 - beta = sqrt(0.5 * (equations.gamma - 1) / equations.gamma) - - # Estimate the edges of the Riemann fan (with positivity conservation) - if orientation == 1 # x-direction - SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, zero(v1_roe)) - SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, zero(v1_roe)) - elseif orientation == 2 # y-direction - SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, zero(v2_roe)) - SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, zero(v2_roe)) - end - - if SsL >= 0.0 && SsR > 0.0 - # Positive supersonic speed - f_ll = flux(u_ll, orientation, equations) - - f1 = f_ll[1] - f2 = f_ll[2] - f3 = f_ll[3] - f4 = f_ll[4] - elseif SsR <= 0.0 && SsL < 0.0 - # Negative supersonic speed - f_rr = flux(u_rr, orientation, equations) - - f1 = f_rr[1] - f2 = f_rr[2] - f3 = f_rr[3] - f4 = f_rr[4] + f1 = f_ll[1] + Ssl * (UStar1 - rho_ll) + f2 = f_ll[2] + Ssl * (UStar2 - rho_v1_ll) + f3 = f_ll[3] + Ssl * (UStar3 - rho_v2_ll) + f4 = f_ll[4] + Ssl * (UStar4 - rho_e_ll) else - # Subsonic case - # Compute left and right fluxes - f_ll = flux(u_ll, orientation, equations) - f_rr = flux(u_rr, orientation, equations) - - f1 = (SsR * f_ll[1] - SsL * f_rr[1] + SsL * SsR * (u_rr[1] - u_ll[1])) / - (SsR - SsL) - f2 = (SsR * f_ll[2] - SsL * f_rr[2] + SsL * SsR * (u_rr[2] - u_ll[2])) / - (SsR - SsL) - f3 = (SsR * f_ll[3] - SsL * f_rr[3] + SsL * SsR * (u_rr[3] - u_ll[3])) / - (SsR - SsL) - f4 = (SsR * f_ll[4] - SsL * f_rr[4] + SsL * SsR * (u_rr[4] - u_ll[4])) / - (SsR - SsL) + densStar = rho_rr * sMu_R / (Ssr - SStar) + enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R)) + UStar1 = densStar + UStar4 = densStar * enerStar + if orientation == 1 # x-direction + UStar2 = densStar * SStar + UStar3 = densStar * v2_rr + elseif orientation == 2 # y-direction + UStar2 = densStar * v1_rr + UStar3 = densStar * SStar + end + f1 = f_rr[1] + Ssr * (UStar1 - rho_rr) + f2 = f_rr[2] + Ssr * (UStar2 - rho_v1_rr) + f3 = f_rr[3] + Ssr * (UStar3 - rho_v2_rr) + f4 = f_rr[4] + Ssr * (UStar4 - rho_e_rr) end - - return SVector(f1, f2, f3, f4) - end - - @inline function max_abs_speeds(u, equations::CompressibleEulerEquations2D) - rho, v1, v2, p = cons2prim(u, equations) - c = sqrt(equations.gamma * p / rho) - - return abs(v1) + c, abs(v2) + c - end - - # Convert conservative variables to primitive - @inline function cons2prim(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - return SVector(rho, v1, v2, p) - end - - # Convert conservative variables to entropy - @inline function cons2entropy(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - v_square = v1^2 + v2^2 - p = (equations.gamma - 1) * (rho_e - 0.5 * rho * v_square) - s = log(p) - equations.gamma * log(rho) - rho_p = rho / p - - w1 = (equations.gamma - s) * equations.inv_gamma_minus_one - 0.5 * rho_p * v_square - w2 = rho_p * v1 - w3 = rho_p * v2 - w4 = -rho_p - - return SVector(w1, w2, w3, w4) - end - - @inline function entropy2cons(w, equations::CompressibleEulerEquations2D) - # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD - # [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1) - @unpack gamma = equations - - # convert to entropy `-rho * s` used by Hughes, France, Mallet (1986) - # instead of `-rho * s / (gamma - 1)` - V1, V2, V3, V5 = w .* (gamma - 1) - - # s = specific entropy, eq. (53) - s = gamma - V1 + (V2^2 + V3^2) / (2 * V5) - - # eq. (52) - rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) * - exp(-s * equations.inv_gamma_minus_one) - - # eq. (51) - rho = -rho_iota * V5 - rho_v1 = rho_iota * V2 - rho_v2 = rho_iota * V3 - rho_e = rho_iota * (1 - (V2^2 + V3^2) / (2 * V5)) - return SVector(rho, rho_v1, rho_v2, rho_e) end - - # Convert primitive to conservative variables - @inline function prim2cons(prim, equations::CompressibleEulerEquations2D) - rho, v1, v2, p = prim - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_e = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) - return SVector(rho, rho_v1, rho_v2, rho_e) + return SVector(f1, f2, f3, f4) +end + +""" + flux_hlle(u_ll, u_rr, orientation, equations::CompressibleEulerEquations2D) + +Computes the HLLE (Harten-Lax-van Leer-Einfeldt) flux for the compressible Euler equations. +Special estimates of the signal velocites and linearization of the Riemann problem developed +by Einfeldt to ensure that the internal energy and density remain positive during the computation +of the numerical flux. + +- Bernd Einfeldt (1988) + On Godunov-type methods for gas dynamics. + [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021) +- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991) + On Godunov-type methods near low densities. + [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3) +""" +function flux_hlle(u_ll, u_rr, orientation::Integer, + equations::CompressibleEulerEquations2D) + # Calculate primitive variables, enthalpy and speed of sound + rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations) + + # `u_ll[4]` is total energy `rho_e_ll` on the left + H_ll = (u_ll[4] + p_ll) / rho_ll + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + + # `u_rr[4]` is total energy `rho_e_rr` on the right + H_rr = (u_rr[4] + p_rr) / rho_rr + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + # Compute Roe averages + sqrt_rho_ll = sqrt(rho_ll) + sqrt_rho_rr = sqrt(rho_rr) + inv_sum_sqrt_rho = inv(sqrt_rho_ll + sqrt_rho_rr) + + v1_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr) * inv_sum_sqrt_rho + v2_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr) * inv_sum_sqrt_rho + v_roe_mag = v1_roe^2 + v2_roe^2 + + H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) * inv_sum_sqrt_rho + c_roe = sqrt((equations.gamma - 1) * (H_roe - 0.5 * v_roe_mag)) + + # Compute convenience constant for positivity preservation, see + # https://doi.org/10.1016/0021-9991(91)90211-3 + beta = sqrt(0.5 * (equations.gamma - 1) / equations.gamma) + + # Estimate the edges of the Riemann fan (with positivity conservation) + if orientation == 1 # x-direction + SsL = min(v1_roe - c_roe, v1_ll - beta * c_ll, zero(v1_roe)) + SsR = max(v1_roe + c_roe, v1_rr + beta * c_rr, zero(v1_roe)) + elseif orientation == 2 # y-direction + SsL = min(v2_roe - c_roe, v2_ll - beta * c_ll, zero(v2_roe)) + SsR = max(v2_roe + c_roe, v2_rr + beta * c_rr, zero(v2_roe)) end - - @inline function density(u, equations::CompressibleEulerEquations2D) - rho = u[1] - return rho - end - - @inline function pressure(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) - return p - end - - @inline function density_pressure(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - rho_times_p = (equations.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2 + rho_v2^2)) - return rho_times_p - end - - # Calculates the entropy flux in direction "orientation" and the entropy variables for a state cons - # NOTE: This method seems to work currently (b82534e) but is never used anywhere. Thus it is - # commented here until someone uses it or writes a test for it. - # @inline function cons2entropyvars_and_flux(gamma::Float64, cons, orientation::Int) - # entropy = MVector{4, Float64}(undef) - # v = (cons[2] / cons[1] , cons[3] / cons[1]) - # v_square= v[1]*v[1]+v[2]*v[2] - # p = (gamma - 1) * (cons[4] - 1/2 * (cons[2] * v[1] + cons[3] * v[2])) - # rho_p = cons[1] / p - # # thermodynamic entropy - # s = log(p) - gamma*log(cons[1]) - # # mathematical entropy - # S = - s*cons[1]/(gamma-1) - # # entropy variables - # entropy[1] = (gamma - s)/(gamma-1) - 0.5*rho_p*v_square - # entropy[2] = rho_p*v[1] - # entropy[3] = rho_p*v[2] - # entropy[4] = -rho_p - # # entropy flux - # entropy_flux = S*v[orientation] - # return entropy, entropy_flux - # end - - # Calculate thermodynamic entropy for a conservative state `cons` - @inline function entropy_thermodynamic(cons, equations::CompressibleEulerEquations2D) - # Pressure - p = (equations.gamma - 1) * (cons[4] - 1 / 2 * (cons[2]^2 + cons[3]^2) / cons[1]) - - # Thermodynamic entropy - s = log(p) - equations.gamma * log(cons[1]) - - return s - end - - # Calculate mathematical entropy for a conservative state `cons` - @inline function entropy_math(cons, equations::CompressibleEulerEquations2D) - # Mathematical entropy - S = -entropy_thermodynamic(cons, equations) * cons[1] * - equations.inv_gamma_minus_one - - return S - end - - # Default entropy is the mathematical entropy - @inline function entropy(cons, equations::CompressibleEulerEquations2D) - entropy_math(cons, equations) - end - - # Calculate total energy for a conservative state `cons` - @inline energy_total(cons, ::CompressibleEulerEquations2D) = cons[4] - - # Calculate kinetic energy for a conservative state `cons` - @inline function energy_kinetic(u, equations::CompressibleEulerEquations2D) - rho, rho_v1, rho_v2, rho_e = u - return (rho_v1^2 + rho_v2^2) / (2 * rho) - end - - # Calculate internal energy for a conservative state `cons` - @inline function energy_internal(cons, equations::CompressibleEulerEquations2D) - return energy_total(cons, equations) - energy_kinetic(cons, equations) + + if SsL >= 0.0 && SsR > 0.0 + # Positive supersonic speed + f_ll = flux(u_ll, orientation, equations) + + f1 = f_ll[1] + f2 = f_ll[2] + f3 = f_ll[3] + f4 = f_ll[4] + elseif SsR <= 0.0 && SsL < 0.0 + # Negative supersonic speed + f_rr = flux(u_rr, orientation, equations) + + f1 = f_rr[1] + f2 = f_rr[2] + f3 = f_rr[3] + f4 = f_rr[4] + else + # Subsonic case + # Compute left and right fluxes + f_ll = flux(u_ll, orientation, equations) + f_rr = flux(u_rr, orientation, equations) + + f1 = (SsR * f_ll[1] - SsL * f_rr[1] + SsL * SsR * (u_rr[1] - u_ll[1])) / + (SsR - SsL) + f2 = (SsR * f_ll[2] - SsL * f_rr[2] + SsL * SsR * (u_rr[2] - u_ll[2])) / + (SsR - SsL) + f3 = (SsR * f_ll[3] - SsL * f_rr[3] + SsL * SsR * (u_rr[3] - u_ll[3])) / + (SsR - SsL) + f4 = (SsR * f_ll[4] - SsL * f_rr[4] + SsL * SsR * (u_rr[4] - u_ll[4])) / + (SsR - SsL) end - end # @muladd - \ No newline at end of file + + return SVector(f1, f2, f3, f4) +end + +@inline function max_abs_speeds(u, equations::CompressibleEulerEquations2D) + rho, v1, v2, p = cons2prim(u, equations) + c = sqrt(equations.gamma * p / rho) + + return abs(v1) + c, abs(v2) + c +end + +# Convert conservative variables to primitive +@inline function cons2prim(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + return SVector(rho, v1, v2, p) +end + +# Convert conservative variables to entropy +@inline function cons2entropy(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + v_square = v1^2 + v2^2 + p = (equations.gamma - 1) * (rho_e - 0.5 * rho * v_square) + s = log(p) - equations.gamma * log(rho) + rho_p = rho / p + + w1 = (equations.gamma - s) * equations.inv_gamma_minus_one - 0.5 * rho_p * v_square + w2 = rho_p * v1 + w3 = rho_p * v2 + w4 = -rho_p + + return SVector(w1, w2, w3, w4) +end + +@inline function entropy2cons(w, equations::CompressibleEulerEquations2D) + # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD + # [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1) + @unpack gamma = equations + + # convert to entropy `-rho * s` used by Hughes, France, Mallet (1986) + # instead of `-rho * s / (gamma - 1)` + V1, V2, V3, V5 = w .* (gamma - 1) + + # s = specific entropy, eq. (53) + s = gamma - V1 + (V2^2 + V3^2) / (2 * V5) + + # eq. (52) + rho_iota = ((gamma - 1) / (-V5)^gamma)^(equations.inv_gamma_minus_one) * + exp(-s * equations.inv_gamma_minus_one) + + # eq. (51) + rho = -rho_iota * V5 + rho_v1 = rho_iota * V2 + rho_v2 = rho_iota * V3 + rho_e = rho_iota * (1 - (V2^2 + V3^2) / (2 * V5)) + return SVector(rho, rho_v1, rho_v2, rho_e) +end + +# Convert primitive to conservative variables +@inline function prim2cons(prim, equations::CompressibleEulerEquations2D) + rho, v1, v2, p = prim + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_e = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) + return SVector(rho, rho_v1, rho_v2, rho_e) +end + +@inline function density(u, equations::CompressibleEulerEquations2D) + rho = u[1] + return rho +end + +@inline function pressure(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) + return p +end + +@inline function density_pressure(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + rho_times_p = (equations.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2 + rho_v2^2)) + return rho_times_p +end + +# Calculates the entropy flux in direction "orientation" and the entropy variables for a state cons +# NOTE: This method seems to work currently (b82534e) but is never used anywhere. Thus it is +# commented here until someone uses it or writes a test for it. +# @inline function cons2entropyvars_and_flux(gamma::Float64, cons, orientation::Int) +# entropy = MVector{4, Float64}(undef) +# v = (cons[2] / cons[1] , cons[3] / cons[1]) +# v_square= v[1]*v[1]+v[2]*v[2] +# p = (gamma - 1) * (cons[4] - 1/2 * (cons[2] * v[1] + cons[3] * v[2])) +# rho_p = cons[1] / p +# # thermodynamic entropy +# s = log(p) - gamma*log(cons[1]) +# # mathematical entropy +# S = - s*cons[1]/(gamma-1) +# # entropy variables +# entropy[1] = (gamma - s)/(gamma-1) - 0.5*rho_p*v_square +# entropy[2] = rho_p*v[1] +# entropy[3] = rho_p*v[2] +# entropy[4] = -rho_p +# # entropy flux +# entropy_flux = S*v[orientation] +# return entropy, entropy_flux +# end + +# Calculate thermodynamic entropy for a conservative state `cons` +@inline function entropy_thermodynamic(cons, equations::CompressibleEulerEquations2D) + # Pressure + p = (equations.gamma - 1) * (cons[4] - 1 / 2 * (cons[2]^2 + cons[3]^2) / cons[1]) + + # Thermodynamic entropy + s = log(p) - equations.gamma * log(cons[1]) + + return s +end + +# Calculate mathematical entropy for a conservative state `cons` +@inline function entropy_math(cons, equations::CompressibleEulerEquations2D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equations) * cons[1] * + equations.inv_gamma_minus_one + + return S +end + +# Default entropy is the mathematical entropy +@inline function entropy(cons, equations::CompressibleEulerEquations2D) + entropy_math(cons, equations) +end + +# Calculate total energy for a conservative state `cons` +@inline energy_total(cons, ::CompressibleEulerEquations2D) = cons[4] + +# Calculate kinetic energy for a conservative state `cons` +@inline function energy_kinetic(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + return (rho_v1^2 + rho_v2^2) / (2 * rho) +end + +# Calculate internal energy for a conservative state `cons` +@inline function energy_internal(cons, equations::CompressibleEulerEquations2D) + return energy_total(cons, equations) - energy_kinetic(cons, equations) +end +end # @muladd From 6e9737beabf87ca5364b523e81bf392f699dba67 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 27 Jul 2023 13:50:46 +0200 Subject: [PATCH 40/74] undo whitespaces --- src/equations/compressible_euler_2d.jl | 122 ++++++++++++------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl index 393f738509c..27b92f41953 100644 --- a/src/equations/compressible_euler_2d.jl +++ b/src/equations/compressible_euler_2d.jl @@ -17,7 +17,7 @@ The compressible Euler equations + \frac{\partial}{\partial x} \begin{pmatrix} - \rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ (\rho e +p) v_1 + \rho v_1 \\ \rho v_1^2 + p \\ \rho v_1 v_2 \\ (\rho e +p) v_1 \end{pmatrix} + \frac{\partial}{\partial y} @@ -131,8 +131,8 @@ A sine wave in the density with constant velocity and pressure; reduces the compressible Euler equations to the linear advection equations. This setup is the test case for stability of EC fluxes from paper - Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) - Stability issues of entropy-stable and/or split-form high-order schemes - [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) + Stability issues of entropy-stable and/or split-form high-order schemes + [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) with the following parameters - domain [-1, 1] - mesh = 4x4 @@ -154,8 +154,8 @@ end A weak blast wave taken from - Sebastian Hennemann, Gregor J. Gassner (2020) - A provably entropy stable subcell shock capturing approach for high order split form DG - [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044) + A provably entropy stable subcell shock capturing approach for high order split form DG + [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044) """ function initial_condition_weak_blast_wave(x, t, equations::CompressibleEulerEquations2D) @@ -182,8 +182,8 @@ end Setup used for convergence tests of the Euler equations with self-gravity used in - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) in combination with [`source_terms_eoc_test_coupled_euler_gravity`](@ref) or [`source_terms_eoc_test_euler`](@ref). """ @@ -211,8 +211,8 @@ end Setup used for convergence tests of the Euler equations with self-gravity used in - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). """ @inline function source_terms_eoc_test_coupled_euler_gravity(u, x, t, @@ -241,8 +241,8 @@ end Setup used for convergence tests of the Euler equations with self-gravity used in - Michael Schlottke-Lakemper, Andrew R. Winters, Hendrik Ranocha, Gregor J. Gassner (2020) - A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics - [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) + A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics + [arXiv: 2008.10593](https://arxiv.org/abs/2008.10593) in combination with [`initial_condition_eoc_test_coupled_euler_gravity`](@ref). """ @inline function source_terms_eoc_test_euler(u, x, t, @@ -268,7 +268,7 @@ end """ boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, - equations::CompressibleEulerEquations2D) + equations::CompressibleEulerEquations2D) Determine the boundary numerical surface flux for a slip wall condition. Imposes a zero normal velocity at the wall. @@ -276,15 +276,15 @@ Density is taken from the internal solution state and pressure is computed as an exact solution of a 1D Riemann problem. Further details about this boundary state are available in the paper: - J. J. W. van der Vegt and H. van der Ven (2002) - Slip flow boundary conditions in discontinuous Galerkin discretizations of - the Euler equations of gas dynamics - [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) + Slip flow boundary conditions in discontinuous Galerkin discretizations of + the Euler equations of gas dynamics + [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book - Eleuterio F. Toro (2009) - Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - 3rd edition - [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + 3rd edition + [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) Should be used together with [`UnstructuredMesh2D`](@ref). """ @@ -330,7 +330,7 @@ end """ boundary_condition_slip_wall(u_inner, orientation, direction, x, t, - surface_flux_function, equations::CompressibleEulerEquations2D) + surface_flux_function, equations::CompressibleEulerEquations2D) Should be used together with [`TreeMesh`](@ref). """ @@ -352,7 +352,7 @@ end """ boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, - surface_flux_function, equations::CompressibleEulerEquations2D) + surface_flux_function, equations::CompressibleEulerEquations2D) Should be used together with [`StructuredMesh`](@ref). """ @@ -417,15 +417,15 @@ end This flux is is a modification of the original kinetic energy preserving two-point flux by - Yuichi Kuya, Kosuke Totani and Soshi Kawai (2018) - Kinetic energy and entropy preserving schemes for compressible flows - by split convective forms - [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) + Kinetic energy and entropy preserving schemes for compressible flows + by split convective forms + [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) The modification is in the energy flux to guarantee pressure equilibrium and was developed by - Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) - Preventing spurious pressure oscillations in split convective form discretizations for - compressible flows - [DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060) + Preventing spurious pressure oscillations in split convective form discretizations for + compressible flows + [DOI: 10.1016/j.jcp.2020.110060](https://doi.org/10.1016/j.jcp.2020.110060) """ @inline function flux_shima_etal(u_ll, u_rr, orientation::Integer, equations::CompressibleEulerEquations2D) @@ -491,9 +491,9 @@ end Kinetic energy preserving two-point flux by - Kennedy and Gruber (2008) - Reduced aliasing formulations of the convective terms within the - Navier-Stokes equations for a compressible fluid - [DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) + Reduced aliasing formulations of the convective terms within the + Navier-Stokes equations for a compressible fluid + [DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) """ @inline function flux_kennedy_gruber(u_ll, u_rr, orientation::Integer, equations::CompressibleEulerEquations2D) @@ -556,9 +556,9 @@ end Entropy conserving two-point flux by - Chandrashekar (2013) - Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes - for Compressible Euler and Navier-Stokes Equations - [DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) + Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes + for Compressible Euler and Navier-Stokes Equations + [DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) """ @inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer, equations::CompressibleEulerEquations2D) @@ -600,18 +600,18 @@ end """ flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, - equations::CompressibleEulerEquations2D) + equations::CompressibleEulerEquations2D) Entropy conserving and kinetic energy preserving two-point flux by - Hendrik Ranocha (2018) - Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods - for Hyperbolic Balance Laws - [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) + Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods + for Hyperbolic Balance Laws + [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) See also - Hendrik Ranocha (2020) - Entropy Conserving and Kinetic Energy Preserving Numerical Methods for - the Euler Equations Using Summation-by-Parts Operators - [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) + Entropy Conserving and Kinetic Energy Preserving Numerical Methods for + the Euler Equations Using Summation-by-Parts Operators + [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) """ @inline function flux_ranocha(u_ll, u_rr, orientation::Integer, equations::CompressibleEulerEquations2D) @@ -684,10 +684,10 @@ end """ splitting_steger_warming(u, orientation::Integer, - equations::CompressibleEulerEquations2D) + equations::CompressibleEulerEquations2D) splitting_steger_warming(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) + orientation::Integer, + equations::CompressibleEulerEquations2D) Splitting of the compressible Euler flux of Steger and Warming. @@ -702,9 +702,9 @@ function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}() ## References - Joseph L. Steger and R. F. Warming (1979) - Flux Vector Splitting of the Inviscid Gasdynamic Equations - With Application to Finite Difference Methods - [NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf) + Flux Vector Splitting of the Inviscid Gasdynamic Equations + With Application to Finite Difference Methods + [NASA Technical Memorandum](https://ntrs.nasa.gov/api/citations/19790020779/downloads/19790020779.pdf) """ @inline function splitting_steger_warming(u, orientation::Integer, equations::CompressibleEulerEquations2D) @@ -811,10 +811,10 @@ end """ splitting_vanleer_haenel(u, orientation::Integer, - equations::CompressibleEulerEquations2D) + equations::CompressibleEulerEquations2D) splitting_vanleer_haenel(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) + orientation::Integer, + equations::CompressibleEulerEquations2D) Splitting of the compressible Euler flux from van Leer. This splitting further contains a reformulation due to Hänel et al. where the energy flux uses the @@ -834,14 +834,14 @@ function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}() ## References - Bram van Leer (1982) - Flux-Vector Splitting for the Euler Equation - [DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5) + Flux-Vector Splitting for the Euler Equation + [DOI: 10.1007/978-3-642-60543-7_5](https://doi.org/10.1007/978-3-642-60543-7_5) - D. Hänel, R. Schwane and G. Seider (1987) - On the accuracy of upwind schemes for the solution of the Navier-Stokes equations - [DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105) + On the accuracy of upwind schemes for the solution of the Navier-Stokes equations + [DOI: 10.2514/6.1987-1105](https://doi.org/10.2514/6.1987-1105) - Meng-Sing Liou and Chris J. Steffen, Jr. (1991) - High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting - [NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425) + High-Order Polynomial Expansions (HOPE) for Flux-Vector Splitting + [NASA Technical Memorandum](https://ntrs.nasa.gov/citations/19910016425) """ @inline function splitting_vanleer_haenel(u, orientation::Integer, equations::CompressibleEulerEquations2D) @@ -912,10 +912,10 @@ end """ splitting_lax_friedrichs(u, orientation::Integer, - equations::CompressibleEulerEquations2D) + equations::CompressibleEulerEquations2D) splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}} - orientation::Integer, - equations::CompressibleEulerEquations2D) + orientation::Integer, + equations::CompressibleEulerEquations2D) Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)` and `f⁻ = 0.5 (f - λ u)` similar to a flux splitting one would apply, e.g., @@ -1261,11 +1261,11 @@ by Einfeldt to ensure that the internal energy and density remain positive durin of the numerical flux. - Bernd Einfeldt (1988) - On Godunov-type methods for gas dynamics. - [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021) + On Godunov-type methods for gas dynamics. + [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021) - Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991) - On Godunov-type methods near low densities. - [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3) + On Godunov-type methods near low densities. + [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3) """ function flux_hlle(u_ll, u_rr, orientation::Integer, equations::CompressibleEulerEquations2D) From ebdbbc63cc661cfa728f2e07ef7d5b46c014dbe5 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 28 Jul 2023 11:36:11 +0200 Subject: [PATCH 41/74] Remove old stuff --- examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl | 2 +- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 8 +------- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 6 ------ 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl index 8f5ba476788..cea81b6af71 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl @@ -71,7 +71,7 @@ amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), base_level=3, med_level=4, med_threshold=0.8, - max_level=5, max_threshold=1.2) + max_level=5, max_threshold=1.3) amr_callback = AMRCallback(semi, amr_controller, interval=5, diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 1d8cd9f2ea0..7f010dba1ec 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -512,7 +512,7 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end -function prolong2mortars!(cache, #flux_viscous::Tuple{AbstractArray, AbstractArray}, +function prolong2mortars!(cache, flux_viscous::Vector{Array{Float64}}, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, @@ -911,11 +911,6 @@ function create_cache_parabolic(mesh::TreeMesh{2}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) - #= - u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) - gradients = ntuple(_ -> similar(u_transformed), ndims(mesh)) - flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh)) - =# cache_viscous = CacheViscous2D{uEltype}(n_vars, n_nodes, n_elements) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) @@ -925,7 +920,6 @@ function create_cache_parabolic(mesh::TreeMesh{2}, # mortars = init_mortars(leaf_cell_ids, mesh, elements, dg.mortar) # cache = (; elements, interfaces, boundaries, mortars) - #cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) cache = (; elements, interfaces, boundaries, cache_viscous) # Add specialized parts of the cache required to compute the mortars etc. diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 090d6f1f0c4..028097e5f4a 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -1103,11 +1103,6 @@ function create_cache_parabolic(mesh::TreeMesh{3}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) - #= - u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) - gradients = ntuple(_ -> similar(u_transformed), ndims(mesh)) - flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh)) - =# cache_viscous = CacheViscous3D{uEltype}(n_vars, n_nodes, n_elements) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) @@ -1117,7 +1112,6 @@ function create_cache_parabolic(mesh::TreeMesh{3}, # mortars = init_mortars(leaf_cell_ids, mesh, elements, dg.mortar) # cache = (; elements, interfaces, boundaries, mortars) - #cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) cache = (; elements, interfaces, boundaries, cache_viscous) # Add specialized parts of the cache required to compute the mortars etc. From c1db7ac97963fd362e9807ba1f042bd951ddeb64 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 28 Jul 2023 13:17:50 +0200 Subject: [PATCH 42/74] Remove doubled implementations --- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 54 ---------------------- 1 file changed, 54 deletions(-) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 5370c927e05..6439cad69bb 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -563,60 +563,6 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, return nothing end -# # Function barrier for type stability -# !!! TODO: Figure out why this cannot removed eventhough it exists in the dg_2d_parabolic.jl file -function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh, - equations, surface_integral, dg::DG) - (; boundary_condition_types, boundary_indices) = boundary_conditions - - calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices, - Gradient(), mesh, equations, surface_integral, dg) - return nothing -end - -function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh, - equations, surface_integral, dg::DG) - (; boundary_condition_types, boundary_indices) = boundary_conditions - - calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices, - Divergence(), mesh, equations, surface_integral, dg) - return nothing -end - -# Iterate over tuples of boundary condition types and associated indices -# in a type-stable way using "lispy tuple programming". -function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, - BC_indices::NTuple{N, Vector{Int}}, - operator_type, - mesh::P4estMesh, - equations, surface_integral, dg::DG) where {N} - # Extract the boundary condition type and index vector - boundary_condition = first(BCs) - boundary_condition_indices = first(BC_indices) - # Extract the remaining types and indices to be processed later - remaining_boundary_conditions = Base.tail(BCs) - remaining_boundary_condition_indices = Base.tail(BC_indices) - - # process the first boundary condition type - calc_boundary_flux!(cache, t, boundary_condition, boundary_condition_indices, - operator_type, mesh, equations, surface_integral, dg) - - # recursively call this method with the unprocessed boundary types - calc_boundary_flux_by_type!(cache, t, remaining_boundary_conditions, - remaining_boundary_condition_indices, - operator_type, - mesh, equations, surface_integral, dg) - - return nothing -end - -# terminate the type-stable iteration over tuples -function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, - operator_type, mesh::P4estMesh, equations, - surface_integral, dg::DG) - nothing -end - function calc_boundary_flux!(cache, t, boundary_condition_parabolic, # works with Dict types boundary_condition_indices, From be3b0675ff98e908ef58413a0fd5b41dfb695ac4 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 28 Jul 2023 13:20:16 +0200 Subject: [PATCH 43/74] kepp main updated with true main --- src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl index 6439cad69bb..5370c927e05 100644 --- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl @@ -563,6 +563,60 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, return nothing end +# # Function barrier for type stability +# !!! TODO: Figure out why this cannot removed eventhough it exists in the dg_2d_parabolic.jl file +function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh, + equations, surface_integral, dg::DG) + (; boundary_condition_types, boundary_indices) = boundary_conditions + + calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices, + Gradient(), mesh, equations, surface_integral, dg) + return nothing +end + +function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh, + equations, surface_integral, dg::DG) + (; boundary_condition_types, boundary_indices) = boundary_conditions + + calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices, + Divergence(), mesh, equations, surface_integral, dg) + return nothing +end + +# Iterate over tuples of boundary condition types and associated indices +# in a type-stable way using "lispy tuple programming". +function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any}, + BC_indices::NTuple{N, Vector{Int}}, + operator_type, + mesh::P4estMesh, + equations, surface_integral, dg::DG) where {N} + # Extract the boundary condition type and index vector + boundary_condition = first(BCs) + boundary_condition_indices = first(BC_indices) + # Extract the remaining types and indices to be processed later + remaining_boundary_conditions = Base.tail(BCs) + remaining_boundary_condition_indices = Base.tail(BC_indices) + + # process the first boundary condition type + calc_boundary_flux!(cache, t, boundary_condition, boundary_condition_indices, + operator_type, mesh, equations, surface_integral, dg) + + # recursively call this method with the unprocessed boundary types + calc_boundary_flux_by_type!(cache, t, remaining_boundary_conditions, + remaining_boundary_condition_indices, + operator_type, + mesh, equations, surface_integral, dg) + + return nothing +end + +# terminate the type-stable iteration over tuples +function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}, + operator_type, mesh::P4estMesh, equations, + surface_integral, dg::DG) + nothing +end + function calc_boundary_flux!(cache, t, boundary_condition_parabolic, # works with Dict types boundary_condition_indices, From 3a82e4a857e44e7be840c5ccade8ee9b21cae3ae Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Sat, 29 Jul 2023 10:40:18 +0200 Subject: [PATCH 44/74] Add comment --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 97af6c6acd2..3d697930c3d 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -821,6 +821,7 @@ function calc_gradient!(gradients, u_transformed, t, end # Prolong solution to mortars + #NOTE: This re-uses the implementation for hyperoblic in "dg_2d.jl" @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg) From e66b83113497946f9967ae69386c7d74de0517c7 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Sat, 29 Jul 2023 12:26:51 +0200 Subject: [PATCH 45/74] comment parabolic 3d --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 2 +- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 3d697930c3d..1366dff3189 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -821,7 +821,7 @@ function calc_gradient!(gradients, u_transformed, t, end # Prolong solution to mortars - #NOTE: This re-uses the implementation for hyperoblic in "dg_2d.jl" + # NOTE: This re-uses the implementation for hyperoblic in "dg_2d.jl" @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 48178c764a0..024f06f18b9 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -982,6 +982,7 @@ function calc_gradient!(gradients, u_transformed, t, end # Prolong solution to mortars + #NOTE: This re-uses the implementation for hyperoblic in "dg_3d.jl" @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg) From b7bd74f38e711c65edd286f8d372d82bacf9f3a9 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Sun, 30 Jul 2023 19:17:08 +0200 Subject: [PATCH 46/74] whitespace --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index 024f06f18b9..f225b7a6a82 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -982,7 +982,7 @@ function calc_gradient!(gradients, u_transformed, t, end # Prolong solution to mortars - #NOTE: This re-uses the implementation for hyperoblic in "dg_3d.jl" + # NOTE: This re-uses the implementation for hyperoblic in "dg_3d.jl" @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg) From 06a419612e785b6bf62f8ac63f845a0300f6bf39 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 31 Jul 2023 16:50:56 +0200 Subject: [PATCH 47/74] Avoid allocations in parabolic boundary fluxes --- examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 3 ++- examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 36a9f52e39d..4208929f018 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -170,7 +170,8 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3]) +velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], + initial_condition_navier_stokes_convergence_test(x, t, equations)[3])) heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl index b32355c48df..7ca7ab35121 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl @@ -220,7 +220,10 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:4]) +velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], + initial_condition_navier_stokes_convergence_test(x, t, equations)[3], + initial_condition_navier_stokes_convergence_test(x, t, equations)[4]) + ) heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) From c9bb0ee20c45c11a696705c91168a765ec021790 Mon Sep 17 00:00:00 2001 From: Daniel Doehring Date: Tue, 1 Aug 2023 10:47:10 +0200 Subject: [PATCH 48/74] Update src/solvers/dgsem_tree/dg_2d_parabolic.jl Co-authored-by: Andrew Winters --- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 1366dff3189..0da25230380 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -821,7 +821,7 @@ function calc_gradient!(gradients, u_transformed, t, end # Prolong solution to mortars - # NOTE: This re-uses the implementation for hyperoblic in "dg_2d.jl" + # NOTE: This re-uses the implementation for hyperbolic terms in "dg_2d.jl" @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg) From d7f2568f42bd966651e997c32b6b2a54e4597a96 Mon Sep 17 00:00:00 2001 From: Daniel Doehring Date: Tue, 1 Aug 2023 10:47:55 +0200 Subject: [PATCH 49/74] Update src/solvers/dgsem_tree/dg_3d_parabolic.jl Co-authored-by: Andrew Winters --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index f225b7a6a82..c737674205a 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -601,7 +601,7 @@ function prolong2mortars!(cache, mortar_l2::LobattoLegendreMortarL2, surface_integral, dg::DGSEM) # temporary buffer for projections - @unpack fstar_tmp1_threaded = cache # CARE: Not sure if I have to do something with this (not present in 2D) + @unpack fstar_tmp1_threaded = cache flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous @threaded for mortar in eachmortar(dg, cache) From a1f3e9946aa07521410150499f6159a292349ea4 Mon Sep 17 00:00:00 2001 From: Daniel Doehring Date: Tue, 1 Aug 2023 10:48:08 +0200 Subject: [PATCH 50/74] Update src/solvers/dgsem_tree/dg_3d_parabolic.jl Co-authored-by: Andrew Winters --- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index c737674205a..2745d312b37 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -982,7 +982,7 @@ function calc_gradient!(gradients, u_transformed, t, end # Prolong solution to mortars - # NOTE: This re-uses the implementation for hyperoblic in "dg_3d.jl" + # NOTE: This re-uses the implementation for hyperbolic terms in "dg_3d.jl" @trixi_timeit timer() "prolong2mortars" begin prolong2mortars!(cache, u_transformed, mesh, equations_parabolic, dg.mortar, dg.surface_integral, dg) From 716283376af759a903bb1fffa0c0e5fb6a38a937 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 1 Aug 2023 11:47:24 +0200 Subject: [PATCH 51/74] try to find allocations --- .../elixir_navierstokes_convergence.jl | 2 +- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 82 ++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 4208929f018..c8e7d1dfd2d 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -195,7 +195,7 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` -tspan = (0.0, 0.5) +tspan = (0.0, 0.01) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 7393c685f5a..92e0d0fa504 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -249,6 +249,86 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, @unpack orientations, neighbor_sides = boundaries flux_viscous_x, flux_viscous_y = flux_viscous + @trixi_timeit timer() "for lvalue" begin + @threaded for boundary in eachboundary(dg, cache_parabolic) + element = boundaries.neighbor_ids[boundary] + + if orientations[boundary] == 1 + # boundary in x-direction + if neighbor_sides[boundary] == 1 + # element in -x direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + flux_viscous[1][v, nnodes(dg), l, + element] = 0.0 + end + else # Element in +x direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + flux_viscous[1][v, 1, l, element] = 0.0 + end + end + else # if orientations[boundary] == 2 + # boundary in y-direction + if neighbor_sides[boundary] == 1 + # element in -y direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + flux_viscous[2][v, l, nnodes(dg), + element] = 0.0 + end + else + # element in +y direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + flux_viscous[2][v, l, 1, element] = 0.0 + end + end + end + end + end + + @trixi_timeit timer() "for rvalue" begin + dummy = 0 + @threaded for boundary in eachboundary(dg, cache_parabolic) + element = boundaries.neighbor_ids[boundary] + + if orientations[boundary] == 1 + # boundary in x-direction + if neighbor_sides[boundary] == 1 + # element in -x direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + dummy = flux_viscous[1][v, nnodes(dg), l, + element] + end + else # Element in +x direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + dummy = flux_viscous[1][v, 1, l, element] + end + end + else # if orientations[boundary] == 2 + # boundary in y-direction + if neighbor_sides[boundary] == 1 + # element in -y direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + dummy = flux_viscous[2][v, l, nnodes(dg), + element] + end + else + # element in +y direction of boundary + for l in eachnode(dg), v in eachvariable(equations_parabolic) + # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! + dummy = flux_viscous[2][v, l, 1, element] + end + end + end + end + end + + @trixi_timeit timer() "for loop orig" begin @threaded for boundary in eachboundary(dg, cache_parabolic) element = boundaries.neighbor_ids[boundary] @@ -285,7 +365,7 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, end end end - + end return nothing end From 3c075d45e7fc1c3d5e34c8b3eeead05479fdac41 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 1 Aug 2023 15:19:22 +0200 Subject: [PATCH 52/74] Increase type stability --- src/solvers/dgsem_tree/cache_viscous.jl | 40 ++++++----- src/solvers/dgsem_tree/dg_2d_parabolic.jl | 88 +---------------------- 2 files changed, 24 insertions(+), 104 deletions(-) diff --git a/src/solvers/dgsem_tree/cache_viscous.jl b/src/solvers/dgsem_tree/cache_viscous.jl index 263282ccea0..55da18f7f48 100644 --- a/src/solvers/dgsem_tree/cache_viscous.jl +++ b/src/solvers/dgsem_tree/cache_viscous.jl @@ -1,7 +1,7 @@ mutable struct CacheViscous1D{uEltype <: Real} - u_transformed::Array{uEltype} - gradients::Array{uEltype} - flux_viscous::Array{uEltype} + u_transformed::Array{uEltype, 3} + gradients::Array{uEltype, 3} + flux_viscous::Array{uEltype, 3} # internal `resize!`able storage _u_transformed::Vector{uEltype} @@ -9,9 +9,9 @@ mutable struct CacheViscous1D{uEltype <: Real} _flux_viscous::Vector{uEltype} function CacheViscous1D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype}(undef, n_vars, n_nodes, n_elements), - Array{uEltype}(undef, n_vars, n_nodes, n_elements), - Array{uEltype}(undef, n_vars, n_nodes, n_elements), + new(Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), Vector{uEltype}(undef, n_vars * n_nodes * n_elements), Vector{uEltype}(undef, n_vars * n_nodes * n_elements), Vector{uEltype}(undef, n_vars * n_nodes * n_elements)) @@ -32,9 +32,10 @@ function Base.resize!(cache_viscous::CacheViscous1D, capacity) end mutable struct CacheViscous2D{uEltype <: Real} - u_transformed::Array{uEltype} - gradients::Vector{Array{uEltype}} - flux_viscous::Vector{Array{uEltype}} + u_transformed::Array{uEltype, 4} + # IDEA: Use SVector for fixed sized vectors? + gradients::Vector{Array{uEltype, 4}} + flux_viscous::Vector{Array{uEltype, 4}} # internal `resize!`able storage _u_transformed::Vector{uEltype} @@ -42,19 +43,20 @@ mutable struct CacheViscous2D{uEltype <: Real} _flux_viscous::Vector{Vector{uEltype}} function CacheViscous2D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements), - [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], - [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], + new(Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), + [Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], + [Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), [Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements) for _ in 1:2], [Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements) for _ in 1:2]) - end + end end mutable struct CacheViscous3D{uEltype <: Real} - u_transformed::Array{uEltype} - gradients::Vector{Array{uEltype}} - flux_viscous::Vector{Array{uEltype}} + u_transformed::Array{uEltype, 5} + # IDEA: Use SVector for fixed sized vectors? + gradients::Vector{Array{uEltype, 5}} + flux_viscous::Vector{Array{uEltype, 5}} # internal `resize!`able storage _u_transformed::Vector{uEltype} @@ -62,9 +64,9 @@ mutable struct CacheViscous3D{uEltype <: Real} _flux_viscous::Vector{Vector{uEltype}} function CacheViscous3D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), - [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], - [Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], + new(Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), + [Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], + [Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), [Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements) for _ in 1:3], [Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements) for _ in 1:3]) diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index 92e0d0fa504..326c368132e 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -17,7 +17,6 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - #(; u_transformed, gradients, flux_viscous) = cache_parabolic @unpack cache_viscous = cache_parabolic @unpack u_transformed, gradients, flux_viscous = cache_viscous @@ -125,7 +124,7 @@ function transform_variables!(u_transformed, u, mesh::Union{TreeMesh{2}, P4estMe for j in eachnode(dg), i in eachnode(dg) u_node = get_node_vars(u, equations_parabolic, dg, i, j, element) u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node, - equations_parabolic) + equations_parabolic) set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg, i, j, element) end @@ -249,86 +248,6 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, @unpack orientations, neighbor_sides = boundaries flux_viscous_x, flux_viscous_y = flux_viscous - @trixi_timeit timer() "for lvalue" begin - @threaded for boundary in eachboundary(dg, cache_parabolic) - element = boundaries.neighbor_ids[boundary] - - if orientations[boundary] == 1 - # boundary in x-direction - if neighbor_sides[boundary] == 1 - # element in -x direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous[1][v, nnodes(dg), l, - element] = 0.0 - end - else # Element in +x direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous[1][v, 1, l, element] = 0.0 - end - end - else # if orientations[boundary] == 2 - # boundary in y-direction - if neighbor_sides[boundary] == 1 - # element in -y direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous[2][v, l, nnodes(dg), - element] = 0.0 - end - else - # element in +y direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - flux_viscous[2][v, l, 1, element] = 0.0 - end - end - end - end - end - - @trixi_timeit timer() "for rvalue" begin - dummy = 0 - @threaded for boundary in eachboundary(dg, cache_parabolic) - element = boundaries.neighbor_ids[boundary] - - if orientations[boundary] == 1 - # boundary in x-direction - if neighbor_sides[boundary] == 1 - # element in -x direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - dummy = flux_viscous[1][v, nnodes(dg), l, - element] - end - else # Element in +x direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - dummy = flux_viscous[1][v, 1, l, element] - end - end - else # if orientations[boundary] == 2 - # boundary in y-direction - if neighbor_sides[boundary] == 1 - # element in -y direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - dummy = flux_viscous[2][v, l, nnodes(dg), - element] - end - else - # element in +y direction of boundary - for l in eachnode(dg), v in eachvariable(equations_parabolic) - # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*! - dummy = flux_viscous[2][v, l, 1, element] - end - end - end - end - end - - @trixi_timeit timer() "for loop orig" begin @threaded for boundary in eachboundary(dg, cache_parabolic) element = boundaries.neighbor_ids[boundary] @@ -365,7 +284,6 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, end end end - end return nothing end @@ -593,11 +511,11 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra end function prolong2mortars!(cache, - flux_viscous::Vector{Array{Float64}}, + flux_viscous::Vector{Array{uEltype, 4}}, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, surface_integral, - dg::DGSEM) + dg::DGSEM) where {uEltype <: Real} flux_viscous_x, flux_viscous_y = flux_viscous @threaded for mortar in eachmortar(dg, cache) large_element = cache.mortars.neighbor_ids[3, mortar] From f3ebbdf5aedc96e57099a77ec641af2da4378e02 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Tue, 1 Aug 2023 15:39:30 +0200 Subject: [PATCH 53/74] back to original time --- examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index c8e7d1dfd2d..4208929f018 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -195,7 +195,7 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # ODE solvers, callbacks etc. # Create ODE problem with time span `tspan` -tspan = (0.0, 0.01) +tspan = (0.0, 0.5) ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() From 9dff7def1f9cfade285f7f250d8646183cbb32dc Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Wed, 2 Aug 2023 08:44:33 +0200 Subject: [PATCH 54/74] revert alloc BC (other PR) --- examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl index 7ca7ab35121..b32355c48df 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl @@ -220,10 +220,7 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], - initial_condition_navier_stokes_convergence_test(x, t, equations)[3], - initial_condition_navier_stokes_convergence_test(x, t, equations)[4]) - ) +velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:4]) heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) From 0936b3badd646143f179c58606b9207de0ea5b9f Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Wed, 2 Aug 2023 08:45:04 +0200 Subject: [PATCH 55/74] Revert alloc BC (other PR) --- examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 4208929f018..36a9f52e39d 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -170,8 +170,7 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], - initial_condition_navier_stokes_convergence_test(x, t, equations)[3])) +velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3]) heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) From 67e064ad0cf10c9dacb897185f9c667ace8e7dea Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Wed, 2 Aug 2023 08:47:54 +0200 Subject: [PATCH 56/74] Name & News --- AUTHORS.md | 1 + NEWS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index abaa3e7e037..74bfaa9c852 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -28,6 +28,7 @@ are listed in alphabetical order: * Jesse Chan * Lars Christmann * Christof Czernik +* Daniel Doehring * Patrick Ersing * Erik Faulhaber * Gregor Gassner diff --git a/NEWS.md b/NEWS.md index 8e374d9ce99..9668bc33126 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ for human readability. #### Added - Experimental support for 3D parabolic diffusion terms has been added. +- Non-uniform `TreeMesh`es are now also available for hyperbolic-parabolic equations. - Capability to set truly discontinuous initial conditions in 1D. - Wetting and drying feature and examples for 1D and 2D shallow water equations From 0b51b32ed1b5704ad7a3d386e91a1cf244ee78b3 Mon Sep 17 00:00:00 2001 From: Daniel Doehring Date: Wed, 2 Aug 2023 23:00:41 +0200 Subject: [PATCH 57/74] Update NEWS.md Co-authored-by: Andrew Winters --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 9668bc33126..10125c40d17 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ for human readability. #### Added - Experimental support for 3D parabolic diffusion terms has been added. -- Non-uniform `TreeMesh`es are now also available for hyperbolic-parabolic equations. +- Non-uniform `TreeMesh` available for hyperbolic-parabolic equations. - Capability to set truly discontinuous initial conditions in 1D. - Wetting and drying feature and examples for 1D and 2D shallow water equations From 40b2796b78dbd9ef3de3e4216c81a603a3a779fd Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Thu, 3 Aug 2023 09:11:18 +0200 Subject: [PATCH 58/74] Correct shear layer IC --- examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl b/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl index a7cb2fc89f1..dd26fd8097b 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl @@ -14,14 +14,16 @@ equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(), Prandtl=prandtl_number()) function initial_condition_shear_layer(x, t, equations::CompressibleEulerEquations2D) + # Shear layer parameters k = 80 delta = 0.05 u0 = 1.0 + Ms = 0.1 # maximum Mach number rho = 1.0 - v1 = x[2] <= 0.5 ? u0*tanh(k*(x[2]*0.5 - 0.25)) : tanh(k*(0.75 -x[2]*0.5)) - v2 = u0*delta * sin(2*pi*(x[1]*0.5 + 0.25)) + v1 = x[2] <= 0.5 ? u0 * tanh(k*(x[2]*0.5 - 0.25)) : u0 * tanh(k*(0.75 -x[2]*0.5)) + v2 = u0 * delta * sin(2*pi*(x[1]*0.5 + 0.25)) p = (u0 / Ms)^2 * rho / equations.gamma # scaling to get Ms return prim2cons(SVector(rho, v1, v2, p), equations) From 43c0b684cfbf620b8e965a38ffa130c2d62ab844 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 4 Aug 2023 11:25:47 +0200 Subject: [PATCH 59/74] reset example --- examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 4208929f018..36a9f52e39d 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -170,8 +170,7 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], - initial_condition_navier_stokes_convergence_test(x, t, equations)[3])) +velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3]) heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) From e74ef9e3fea62f80f6a860c621b0f6484570aaa3 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 4 Aug 2023 11:26:27 +0200 Subject: [PATCH 60/74] reset excample --- .../elixir_navierstokes_convergence.jl | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl index 271cfe78663..b32355c48df 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl @@ -220,10 +220,7 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], - initial_condition_navier_stokes_convergence_test(x, t, equations)[3], - initial_condition_navier_stokes_convergence_test(x, t, equations)[4]) - ) +velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:4]) heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) @@ -252,36 +249,13 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # Create ODE problem with time span `tspan` tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan; split_form = false) +ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() alive_callback = AliveCallback(alive_interval=10) analysis_interval = 100 analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -#= -amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first), - base_level=2, - med_level=3, med_threshold=0.1, - max_level=4, max_threshold=0.6) -=# -amr_indicator = IndicatorHennemannGassner(semi, - alpha_max=1.0, - alpha_min=0.0001, - alpha_smooth=false, - variable=Trixi.density) - -amr_controller = ControllerThreeLevel(semi, amr_indicator, - base_level=2, - med_level =3, med_threshold=0.0003, # med_level = current level - max_level =4, max_threshold=0.003) - -amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true) - -callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, amr_callback) -#callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) +callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback) ############################################################################### # run the simulation From 50867a41638559f45557529a0a9005f7f67b520d Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 4 Aug 2023 11:27:55 +0200 Subject: [PATCH 61/74] resize now working in ODE diff eq --- examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl | 2 +- examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl index 2ac3fd88fbf..ecd2f72f37a 100644 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl +++ b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl @@ -60,7 +60,7 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabol # Create ODE problem with time span from 0.0 to 1.0 tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan; split_form = false); +ode = semidiscretize(semi, tspan); # At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup # and resets the timers diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl index cea81b6af71..a23639a16af 100644 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl +++ b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl @@ -54,7 +54,7 @@ semi = SemidiscretizationHyperbolicParabolic(mesh, # Create ODE problem with time span from 0.0 to 1.5 tspan = (0.0, 0.4) -ode = semidiscretize(semi, tspan; split_form = false) +ode = semidiscretize(semi, tspan) # At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup # and resets the timers From 522244876c83c8874a6d3da956ecbe776df5b628 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 4 Aug 2023 11:28:57 +0200 Subject: [PATCH 62/74] splitform now working in ODE jl --- ...semidiscretization_hyperbolic_parabolic.jl | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl index 2fd123e38fe..8f1e38c891b 100644 --- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl +++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl @@ -274,9 +274,8 @@ The parabolic right-hand side is the first function of the split ODE problem and will be used by default by the implicit part of IMEX methods from the SciML ecosystem. """ -function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan; - reset_threads = true, split_form::Bool=true) - +function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan; + reset_threads = true) # Optionally reset Polyester.jl threads. See # https://github.com/trixi-framework/Trixi.jl/issues/1583 # https://github.com/JuliaSIMD/Polyester.jl/issues/30 @@ -292,12 +291,7 @@ function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan; # Note that the IMEX time integration methods of OrdinaryDiffEq.jl treat the # first function implicitly and the second one explicitly. Thus, we pass the # stiffer parabolic function first. - if split_form - return SplitODEProblem{iip}(rhs_parabolic!, rhs!, u0_ode, tspan, semi) - else - specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) - return ODEProblem{iip, specialize}(rhs_hyperbolic_parabolic!, u0_ode, tspan, semi) - end + return SplitODEProblem{iip}(rhs_parabolic!, rhs!, u0_ode, tspan, semi) end function rhs!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) @@ -336,15 +330,4 @@ function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabol return nothing end - -function rhs_hyperbolic_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabolic, t) - @trixi_timeit timer() "hyperbolic-parabolic rhs!" begin - # TODO: Avoid allocations, make member variable of something? - # -> Could reside in (PERK) integrator, then pass in similar to indices of PERK - du_ode_hyp = similar(du_ode) - rhs!(du_ode_hyp, u_ode, semi, t) - rhs_parabolic!(du_ode, u_ode, semi, t) - du_ode .+= du_ode_hyp - end -end end # @muladd From 8db068daaad6cfa791b9e5eb22b2cc3f26a4d436 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 4 Aug 2023 11:33:33 +0200 Subject: [PATCH 63/74] Update news --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 10125c40d17..7729c7cf26a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,7 @@ for human readability. - Experimental support for 3D parabolic diffusion terms has been added. - Non-uniform `TreeMesh` available for hyperbolic-parabolic equations. +- AMR with `TreeMesh` available for hyperbolic-parabolic equations. - Capability to set truly discontinuous initial conditions in 1D. - Wetting and drying feature and examples for 1D and 2D shallow water equations From 80519b1a024243eec3ca4a635d5650d2d8db57de Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Mon, 7 Aug 2023 09:03:52 +0200 Subject: [PATCH 64/74] Whitespaces --- src/solvers/dgsem_tree/containers_2d.jl | 6 +++--- src/solvers/dgsem_tree/containers_3d.jl | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/solvers/dgsem_tree/containers_2d.jl b/src/solvers/dgsem_tree/containers_2d.jl index 5cf256d3499..d80522d42fd 100644 --- a/src/solvers/dgsem_tree/containers_2d.jl +++ b/src/solvers/dgsem_tree/containers_2d.jl @@ -764,10 +764,10 @@ end # Container data structure (structure-of-arrays style) for DG MPI interfaces mutable struct MPIInterfaceContainer2D{uEltype <: Real} <: AbstractContainer - u::Array{uEltype, 4} # [leftright, variables, i, interfaces] + u::Array{uEltype, 4} # [leftright, variables, i, interfaces] local_neighbor_ids::Vector{Int} # [interfaces] - orientations::Vector{Int} # [interfaces] - remote_sides::Vector{Int} # [interfaces] + orientations::Vector{Int} # [interfaces] + remote_sides::Vector{Int} # [interfaces] # internal `resize!`able storage _u::Vector{uEltype} end diff --git a/src/solvers/dgsem_tree/containers_3d.jl b/src/solvers/dgsem_tree/containers_3d.jl index 0318946e34d..5fc027ad001 100644 --- a/src/solvers/dgsem_tree/containers_3d.jl +++ b/src/solvers/dgsem_tree/containers_3d.jl @@ -520,14 +520,14 @@ end # Left and right are used *both* for the numbering of the mortar faces *and* for the position of the # elements with respect to the axis orthogonal to the mortar. mutable struct L2MortarContainer3D{uEltype <: Real} <: AbstractContainer - u_upper_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars] + u_upper_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars] u_upper_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars] - u_lower_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars] + u_lower_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars] u_lower_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars] - neighbor_ids::Array{Int, 2} # [position, mortars] + neighbor_ids::Array{Int, 2} # [position, mortars] # Large sides: left -> 1, right -> 2 large_sides::Vector{Int} # [mortars] - orientations::Vector{Int} # [mortars] + orientations::Vector{Int} # [mortars] # internal `resize!`able storage _u_upper_left::Vector{uEltype} _u_upper_right::Vector{uEltype} From 5638a019c6b01e3d277b09b8fa64b8ee5edfe623 Mon Sep 17 00:00:00 2001 From: Daniel Doehring Date: Tue, 8 Aug 2023 08:52:56 +0200 Subject: [PATCH 65/74] Update examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl Co-authored-by: Hendrik Ranocha --- examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl index 4208929f018..b68e9e6c97e 100644 --- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl @@ -170,8 +170,10 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], - initial_condition_navier_stokes_convergence_test(x, t, equations)[3])) +velocity_bc_top_bottom = NoSlip() do x, t, equations + u = initial_condition_navier_stokes_convergence_test(x, t, equations) + return SVector(u[2], u[3]) +end heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) From 034c53cf32a9338372c3f0010a8f561723458d12 Mon Sep 17 00:00:00 2001 From: Daniel Doehring Date: Tue, 8 Aug 2023 08:53:03 +0200 Subject: [PATCH 66/74] Update examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl Co-authored-by: Hendrik Ranocha --- examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl index 7ca7ab35121..ebb0137a1bb 100644 --- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl +++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl @@ -220,10 +220,10 @@ end initial_condition = initial_condition_navier_stokes_convergence_test # BC types -velocity_bc_top_bottom = NoSlip((x, t, equations) -> SVector(initial_condition_navier_stokes_convergence_test(x, t, equations)[2], - initial_condition_navier_stokes_convergence_test(x, t, equations)[3], - initial_condition_navier_stokes_convergence_test(x, t, equations)[4]) - ) +velocity_bc_top_bottom = NoSlip() do x, t, equations + u = initial_condition_navier_stokes_convergence_test(x, t, equations) + return SVector(u[2], u[3], u[4]) +end heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0) boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom) From 510e2362bcb14a504012da1faf13df34f659627c Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Wed, 9 Aug 2023 14:32:50 +0200 Subject: [PATCH 67/74] Fix typo --- src/semidiscretization/semidiscretization.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index fbdcd73e2a8..c784f716426 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -363,7 +363,7 @@ end # # In some sense, having plain multidimensional `Array`s not support `resize!` # isn't necessarily a bug (although it would be nice to add this possibility to -# base Julia) but can turn out to be a feature for us, because it will aloow us +# base Julia) but can turn out to be a feature for us, because it will allow us # more specializations. # Since we can use multiple dispatch, these kinds of specializations can be # tailored specifically to each combinations of mesh/solver etc. From a4931aa4c769e3dbcd05f190af52c1d7c12ec97b Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 10:38:15 +0200 Subject: [PATCH 68/74] comment --- src/solvers/dgsem_tree/dg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dgsem_tree/dg.jl b/src/solvers/dgsem_tree/dg.jl index fa2e866b709..9b0874e5ae0 100644 --- a/src/solvers/dgsem_tree/dg.jl +++ b/src/solvers/dgsem_tree/dg.jl @@ -54,7 +54,7 @@ include("containers.jl") # Dimension-agnostic parallel setup include("dg_parallel.jl") -# Helper struct for parabolic dg +# Helper struct for parabolic AMR include("cache_viscous.jl") # 1D DG implementation From 7f57ab11007a0f417914d8ce03b7c2caec65df7d Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 10:40:51 +0200 Subject: [PATCH 69/74] Restrict to 1D --- .../elixir_advection_diffusion_amr.jl | 97 ---------- src/callbacks_step/amr_dg2d.jl | 173 ----------------- src/callbacks_step/amr_dg3d.jl | 177 ------------------ src/solvers/dgsem_tree/dg_2d_parabolic.jl | 17 +- src/solvers/dgsem_tree/dg_3d_parabolic.jl | 15 +- 5 files changed, 17 insertions(+), 462 deletions(-) delete mode 100644 examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl deleted file mode 100644 index a23639a16af..00000000000 --- a/examples/tree_2d_dgsem/elixir_advection_diffusion_amr.jl +++ /dev/null @@ -1,97 +0,0 @@ -using OrdinaryDiffEq, Plots -using Trixi - -############################################################################### -# semidiscretization of the linear advection-diffusion equation - -advection_velocity = (1.5, 1.0) -equations = LinearScalarAdvectionEquation2D(advection_velocity) -diffusivity() = 5.0e-2 -equations_parabolic = LaplaceDiffusion2D(diffusivity(), equations) - -# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) - -coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) -coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y)) - -# Create a uniformly refined mesh with periodic boundaries -mesh = TreeMesh(coordinates_min, coordinates_max, - initial_refinement_level=4, - periodicity=true, - n_cells_max=30_000) # set maximum capacity of tree data structure - -# Define initial condition -function initial_condition_diffusive_convergence_test(x, t, equation::LinearScalarAdvectionEquation2D) - # Store translated coordinate for easy use of exact solution - x_trans = x - equation.advection_velocity * t - - nu = diffusivity() - c = 1.0 - A = 0.5 - L = 2 - f = 1/L - omega = 2 * pi * f - scalar = c + A * sin(omega * sum(x_trans)) * exp(-2 * nu * omega^2 * t) - return SVector(scalar) -end -initial_condition = initial_condition_diffusive_convergence_test - -# define periodic boundary conditions everywhere -boundary_conditions = boundary_condition_periodic -boundary_conditions_parabolic = boundary_condition_periodic - -# A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolicParabolic(mesh, - (equations, equations_parabolic), - initial_condition, solver; - boundary_conditions=(boundary_conditions, - boundary_conditions_parabolic)) - - -############################################################################### -# ODE solvers, callbacks etc. - -# Create ODE problem with time span from 0.0 to 1.5 -tspan = (0.0, 0.4) -ode = semidiscretize(semi, tspan) - -# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup -# and resets the timers -summary_callback = SummaryCallback() - -# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -# The AliveCallback prints short status information in regular intervals -alive_callback = AliveCallback(analysis_interval=analysis_interval) - -amr_controller = ControllerThreeLevel(semi, - IndicatorMax(semi, variable=first), - base_level=3, - med_level=4, med_threshold=0.8, - max_level=5, max_threshold=1.3) - -amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true) - -# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback) - - -############################################################################### -# run the simulation - -# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -alg = RDPK3SpFSAL49() -time_int_tol = 1.0e-11 -sol = solve(ode, alg; abstol=time_int_tol, reltol=time_int_tol, - ode_default_options()..., callback=callbacks) - -# Print the timer summary -summary_callback() -plot(sol) -pd = PlotData2D(sol) -plot!(getmesh(pd)) \ No newline at end of file diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl index 8e90eb0633f..1d37dfce034 100644 --- a/src/callbacks_step/amr_dg2d.jl +++ b/src/callbacks_step/amr_dg2d.jl @@ -136,88 +136,6 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estM return nothing end -# Refine elements in the DG solver based on a list of cell_ids that should be refined -function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{2}, P4estMesh{2}}, - equations, dg::DGSEM, cache, cache_parabolic, elements_to_refine) - # Return early if there is nothing to do - if isempty(elements_to_refine) - if mpi_isparallel() - # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do - # locally (there still might be other MPI ranks that have refined elements) - reinitialize_containers!(mesh, equations, dg, cache) - end - return - end - - # Determine for each existing element whether it needs to be refined - needs_refinement = falses(nelements(dg, cache)) - needs_refinement[elements_to_refine] .= true - - # Retain current solution data - old_n_elements = nelements(dg, cache) - old_u_ode = copy(u_ode) - GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - - reinitialize_containers!(mesh, equations, dg, cache) - reinitialize_containers!(mesh, equations, dg, cache_parabolic) - - resize!(u_ode, - nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) - - # Resize parabolic helper variables - @unpack cache_viscous = cache_parabolic - resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - - cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._u_transformed), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) - for dim in 1:2 - cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._gradients[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) - end - - # Loop over all elements in old container and either copy them or refine them - element_id = 1 - for old_element_id in 1:old_n_elements - if needs_refinement[old_element_id] - # Refine element and store solution directly in new data structure - refine_element!(u, element_id, old_u, old_element_id, - adaptor, equations, dg) - element_id += 2^ndims(mesh) - else - # Copy old element data to new element container - @views u[:, .., element_id] .= old_u[:, .., old_element_id] - element_id += 1 - end - end - # If everything is correct, we should have processed all elements. - # Depending on whether the last element processed above had to be refined or not, - # the counter `element_id` can have two different values at the end. - @assert element_id == - nelements(dg, cache) + - 1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" - @assert element_id == - nelements(dg, cache_parabolic) + - 1||element_id == nelements(dg, cache_parabolic) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" - end # GC.@preserve old_u_ode - - # Sanity check - if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 && - !mpi_isparallel() - @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - !mpi_isparallel() - @assert ninterfaces(cache_parabolic.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - end - - return nothing -end - # TODO: Taal compare performance of different implementations # Refine solution data u for an element, using L2 projection (interpolation) function refine_element!(u::AbstractArray{<:Any, 4}, element_id, @@ -357,97 +275,6 @@ function coarsen!(u_ode::AbstractVector, adaptor, return nothing end -# Coarsen elements in the DG solver based on a list of cell_ids that should be removed -function coarsen!(u_ode::AbstractVector, adaptor, - mesh::Union{TreeMesh{2}, P4estMesh{2}}, - equations, dg::DGSEM, cache, cache_parabolic, - elements_to_remove) - # Return early if there is nothing to do - if isempty(elements_to_remove) - if mpi_isparallel() - # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do - # locally (there still might be other MPI ranks that have coarsened elements) - reinitialize_containers!(mesh, equations, dg, cache) - end - return - end - - # Determine for each old element whether it needs to be removed - to_be_removed = falses(nelements(dg, cache)) - to_be_removed[elements_to_remove] .= true - - # Retain current solution data - old_n_elements = nelements(dg, cache) - old_u_ode = copy(u_ode) - GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - - reinitialize_containers!(mesh, equations, dg, cache) - reinitialize_containers!(mesh, equations, dg, cache_parabolic) - - resize!(u_ode, - nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) - - # Resize parabolic helper variables - @unpack cache_viscous = cache_parabolic - resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - - cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._u_transformed), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) - for dim in 1:2 - cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._gradients[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nelements(dg, cache))) - end - - # Loop over all elements in old container and either copy them or coarsen them - skip = 0 - element_id = 1 - for old_element_id in 1:old_n_elements - # If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements - if skip > 0 - skip -= 1 - continue - end - - if to_be_removed[old_element_id] - # If an element is to be removed, sanity check if the following elements - # are also marked - otherwise there would be an error in the way the - # cells/elements are sorted - @assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order" - - # Coarsen elements and store solution directly in new data structure - coarsen_elements!(u, element_id, old_u, old_element_id, - adaptor, equations, dg) - element_id += 1 - skip = 2^ndims(mesh) - 1 - else - # Copy old element data to new element container - @views u[:, .., element_id] .= old_u[:, .., old_element_id] - element_id += 1 - end - end - # If everything is correct, we should have processed all elements. - @assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" - @assert element_id==nelements(dg, cache_parabolic) + 1 "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" - end # GC.@preserve old_u_ode - - # Sanity check - if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 && - !mpi_isparallel() - @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - !mpi_isparallel() - @assert ninterfaces(cache_parabolic.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - end - - return nothing -end - # TODO: Taal compare performance of different implementations # Coarsen solution data u for four elements, using L2 projection function coarsen_elements!(u::AbstractArray{<:Any, 4}, element_id, diff --git a/src/callbacks_step/amr_dg3d.jl b/src/callbacks_step/amr_dg3d.jl index df7ecad57b0..c8abe6fdb05 100644 --- a/src/callbacks_step/amr_dg3d.jl +++ b/src/callbacks_step/amr_dg3d.jl @@ -69,91 +69,6 @@ function refine!(u_ode::AbstractVector, adaptor, return nothing end -function refine!(u_ode::AbstractVector, adaptor, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, - equations, dg::DGSEM, cache, cache_parabolic, - elements_to_refine) - # Return early if there is nothing to do - if isempty(elements_to_refine) - if mpi_isparallel() - # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do - # locally (there still might be other MPI ranks that have refined elements) - reinitialize_containers!(mesh, equations, dg, cache) - end - return - end - - # Determine for each existing element whether it needs to be refined - needs_refinement = falses(nelements(dg, cache)) - needs_refinement[elements_to_refine] .= true - - # Retain current solution data - old_n_elements = nelements(dg, cache) - old_u_ode = copy(u_ode) - GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - - reinitialize_containers!(mesh, equations, dg, cache) - reinitialize_containers!(mesh, equations, dg, cache_parabolic) - - resize!(u_ode, - nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) - - # Resize parabolic helper variables - @unpack cache_viscous = cache_parabolic - resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - - cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._u_transformed), - (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) - for dim in 1:3 - cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._gradients[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) - end - - # Loop over all elements in old container and either copy them or refine them - u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), - nnodes(dg), nnodes(dg)) - u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), - nnodes(dg), nnodes(dg)) - element_id = 1 - for old_element_id in 1:old_n_elements - if needs_refinement[old_element_id] - # Refine element and store solution directly in new data structure - refine_element!(u, element_id, old_u, old_element_id, - adaptor, equations, dg, u_tmp1, u_tmp2) - element_id += 2^ndims(mesh) - else - # Copy old element data to new element container - @views u[:, .., element_id] .= old_u[:, .., old_element_id] - element_id += 1 - end - end - # If everything is correct, we should have processed all elements. - # Depending on whether the last element processed above had to be refined or not, - # the counter `element_id` can have two different values at the end. - @assert element_id == - nelements(dg, cache) + - 1||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" - @assert element_id == - nelements(dg, cache_parabolic) + - 1||element_id == nelements(dg, cache_parabolic) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" - end # GC.@preserve old_u_ode - - # Sanity check - if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 - @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - end - - return nothing -end - # TODO: Taal compare performance of different implementations # Refine solution data u for an element, using L2 projection (interpolation) function refine_element!(u::AbstractArray{<:Any, 5}, element_id, @@ -260,96 +175,6 @@ function coarsen!(u_ode::AbstractVector, adaptor, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) u = wrap_array(u_ode, mesh, equations, dg, cache) - # Resize parabolic helper variables - @unpack cache_viscous = cache_parabolic - resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - - cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._u_transformed), - (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) - for dim in 1:3 - cache_parabolic.cache_viscous.gradients[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._gradients[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.flux_viscous[dim] = unsafe_wrap(Array, - pointer(cache_parabolic.cache_viscous._flux_viscous[dim]), - (nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg), nelements(dg, cache))) - end - - # Loop over all elements in old container and either copy them or coarsen them - u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), - nnodes(dg), nnodes(dg)) - u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), - nnodes(dg), nnodes(dg)) - skip = 0 - element_id = 1 - for old_element_id in 1:old_n_elements - # If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements - if skip > 0 - skip -= 1 - continue - end - - if to_be_removed[old_element_id] - # If an element is to be removed, sanity check if the following elements - # are also marked - otherwise there would be an error in the way the - # cells/elements are sorted - @assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order" - - # Coarsen elements and store solution directly in new data structure - coarsen_elements!(u, element_id, old_u, old_element_id, - adaptor, equations, dg, u_tmp1, u_tmp2) - element_id += 1 - skip = 2^ndims(mesh) - 1 - else - # Copy old element data to new element container - @views u[:, .., element_id] .= old_u[:, .., old_element_id] - element_id += 1 - end - end - # If everything is correct, we should have processed all elements. - @assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" - end # GC.@preserve old_u_ode - - # Sanity check - if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 - @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - end - - return nothing -end - -function coarsen!(u_ode::AbstractVector, adaptor, - mesh::Union{TreeMesh{3}, P4estMesh{3}}, - equations, dg::DGSEM, cache, cache_parabolic, - elements_to_remove) - # Return early if there is nothing to do - if isempty(elements_to_remove) - if mpi_isparallel() - # MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do - # locally (there still might be other MPI ranks that have coarsened elements) - reinitialize_containers!(mesh, equations, dg, cache) - end - return - end - - # Determine for each old element whether it needs to be removed - to_be_removed = falses(nelements(dg, cache)) - to_be_removed[elements_to_remove] .= true - - # Retain current solution data - old_n_elements = nelements(dg, cache) - old_u_ode = copy(u_ode) - GC.@preserve old_u_ode begin # OBS! If we don't GC.@preserve old_u_ode, it might be GC'ed - old_u = wrap_array(old_u_ode, mesh, equations, dg, cache) - - reinitialize_containers!(mesh, equations, dg, cache) - reinitialize_containers!(mesh, equations, dg, cache_parabolic) - - resize!(u_ode, - nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - u = wrap_array(u_ode, mesh, equations, dg, cache) - # Loop over all elements in old container and either copy them or coarsen them u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg), nnodes(dg), nnodes(dg)) @@ -383,13 +208,11 @@ function coarsen!(u_ode::AbstractVector, adaptor, end # If everything is correct, we should have processed all elements. @assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))" - @assert element_id==nelements(dg, cache_parabolic) + 1 "element_id = $element_id, nelements(dg, cache_parabolic) = $(nelements(dg, cache_parabolic))" end # GC.@preserve old_u_ode # Sanity check if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0 @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") - @assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache_parabolic) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements") end return nothing diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl index e3920859a33..0da25230380 100644 --- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl @@ -17,8 +17,7 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - @unpack cache_viscous = cache_parabolic - @unpack u_transformed, gradients, flux_viscous = cache_viscous + (; u_transformed, gradients, flux_viscous) = cache_parabolic # Convert conservative variables to a form more suitable for viscous flux calculations @trixi_timeit timer() "transform variables" begin @@ -124,7 +123,7 @@ function transform_variables!(u_transformed, u, mesh::Union{TreeMesh{2}, P4estMe for j in eachnode(dg), i in eachnode(dg) u_node = get_node_vars(u, equations_parabolic, dg, i, j, element) u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node, - equations_parabolic) + equations_parabolic) set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg, i, j, element) end @@ -284,6 +283,7 @@ function prolong2boundaries!(cache_parabolic, flux_viscous, end end end + return nothing end @@ -510,12 +510,11 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra return nothing end -function prolong2mortars!(cache, - flux_viscous::Vector{Array{uEltype, 4}}, +function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray}, mesh::TreeMesh{2}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, surface_integral, - dg::DGSEM) where {uEltype <: Real} + dg::DGSEM) flux_viscous_x, flux_viscous_y = flux_viscous @threaded for mortar in eachmortar(dg, cache) large_element = cache.mortars.neighbor_ids[3, mortar] @@ -910,7 +909,9 @@ function create_cache_parabolic(mesh::TreeMesh{2}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) - cache_viscous = CacheViscous2D{uEltype}(n_vars, n_nodes, n_elements) + u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_elements) + gradients = ntuple(_ -> similar(u_transformed), ndims(mesh)) + flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh)) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) @@ -919,7 +920,7 @@ function create_cache_parabolic(mesh::TreeMesh{2}, # mortars = init_mortars(leaf_cell_ids, mesh, elements, dg.mortar) # cache = (; elements, interfaces, boundaries, mortars) - cache = (; elements, interfaces, boundaries, cache_viscous) + cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) # Add specialized parts of the cache required to compute the mortars etc. # cache = (;cache..., create_cache(mesh, equations_parabolic, dg.mortar, uEltype)...) diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl index cf09c4ed42a..2745d312b37 100644 --- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl @@ -17,9 +17,7 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - #@unpack u_transformed, gradients, flux_viscous = cache_parabolic - @unpack cache_viscous = cache_parabolic - @unpack u_transformed, gradients, flux_viscous = cache_viscous + @unpack u_transformed, gradients, flux_viscous = cache_parabolic # Convert conservative variables to a form more suitable for viscous flux calculations @trixi_timeit timer() "transform variables" begin @@ -596,11 +594,12 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra end function prolong2mortars!(cache, - flux_viscous::Vector{Array{uEltype}}, + flux_viscous::Tuple{AbstractArray, AbstractArray, + AbstractArray}, mesh::TreeMesh{3}, equations_parabolic::AbstractEquationsParabolic, mortar_l2::LobattoLegendreMortarL2, - surface_integral, dg::DGSEM) where {uEltype <: Real} + surface_integral, dg::DGSEM) # temporary buffer for projections @unpack fstar_tmp1_threaded = cache @@ -1100,7 +1099,9 @@ function create_cache_parabolic(mesh::TreeMesh{3}, n_vars = nvariables(equations_hyperbolic) n_nodes = nnodes(elements) n_elements = nelements(elements) - cache_viscous = CacheViscous3D{uEltype}(n_vars, n_nodes, n_elements) + u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) + gradients = ntuple(_ -> similar(u_transformed), ndims(mesh)) + flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh)) interfaces = init_interfaces(leaf_cell_ids, mesh, elements) @@ -1109,7 +1110,7 @@ function create_cache_parabolic(mesh::TreeMesh{3}, # mortars = init_mortars(leaf_cell_ids, mesh, elements, dg.mortar) # cache = (; elements, interfaces, boundaries, mortars) - cache = (; elements, interfaces, boundaries, cache_viscous) + cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed) # Add specialized parts of the cache required to compute the mortars etc. # cache = (;cache..., create_cache(mesh, equations_parabolic, dg.mortar, uEltype)...) From d28fb458486a8a2c75531c96146d6aea21d1062b Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 10:45:18 +0200 Subject: [PATCH 70/74] simplifications 1D --- src/solvers/dgsem_tree/cache_viscous.jl | 57 ----------------------- src/solvers/dgsem_tree/containers.jl | 8 ++-- src/solvers/dgsem_tree/dg_1d_parabolic.jl | 1 - 3 files changed, 3 insertions(+), 63 deletions(-) diff --git a/src/solvers/dgsem_tree/cache_viscous.jl b/src/solvers/dgsem_tree/cache_viscous.jl index 55da18f7f48..a04038ab727 100644 --- a/src/solvers/dgsem_tree/cache_viscous.jl +++ b/src/solvers/dgsem_tree/cache_viscous.jl @@ -29,61 +29,4 @@ function Base.resize!(cache_viscous::CacheViscous1D, capacity) resize!(cache_viscous._flux_viscous, capacity) return nothing -end - -mutable struct CacheViscous2D{uEltype <: Real} - u_transformed::Array{uEltype, 4} - # IDEA: Use SVector for fixed sized vectors? - gradients::Vector{Array{uEltype, 4}} - flux_viscous::Vector{Array{uEltype, 4}} - - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - _gradients::Vector{Vector{uEltype}} - _flux_viscous::Vector{Vector{uEltype}} - - function CacheViscous2D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements), - [Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], - [Array{uEltype, 4}(undef, n_vars, n_nodes, n_nodes, n_elements) for _ in 1:2], - Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements), - [Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements) for _ in 1:2], - [Vector{uEltype}(undef, n_vars * n_nodes^2 * n_elements) for _ in 1:2]) - end -end - -mutable struct CacheViscous3D{uEltype <: Real} - u_transformed::Array{uEltype, 5} - # IDEA: Use SVector for fixed sized vectors? - gradients::Vector{Array{uEltype, 5}} - flux_viscous::Vector{Array{uEltype, 5}} - - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - _gradients::Vector{Vector{uEltype}} - _flux_viscous::Vector{Vector{uEltype}} - - function CacheViscous3D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements), - [Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], - [Array{uEltype, 5}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements) for _ in 1:3], - Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements), - [Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements) for _ in 1:3], - [Vector{uEltype}(undef, n_vars * n_nodes^3 * n_elements) for _ in 1:3]) - end -end - -# Only one-dimensional `Array`s are `resize!`able in Julia. -# Hence, we use `Vector`s as internal storage and `resize!` -# them whenever needed. Then, we reuse the same memory by -# `unsafe_wrap`ping multi-dimensional `Array`s around the -# internal storage. -function Base.resize!(cache_viscous::Union{CacheViscous2D, CacheViscous3D}, capacity) - resize!(cache_viscous._u_transformed, capacity) - for dim in 1:length(cache_viscous._gradients) - resize!(cache_viscous._gradients[dim], capacity) - resize!(cache_viscous._flux_viscous[dim], capacity) - end - - return nothing end \ No newline at end of file diff --git a/src/solvers/dgsem_tree/containers.jl b/src/solvers/dgsem_tree/containers.jl index 3f05daf81d8..bba8b83b23a 100644 --- a/src/solvers/dgsem_tree/containers.jl +++ b/src/solvers/dgsem_tree/containers.jl @@ -28,11 +28,9 @@ function reinitialize_containers!(mesh::TreeMesh, equations, dg::DGSEM, cache) init_boundaries!(boundaries, elements, mesh) # re-initialize mortars container - if hasproperty(cache, :mortars) # cache_parabolic does not carry mortars - @unpack mortars = cache - resize!(mortars, count_required_mortars(mesh, leaf_cell_ids)) - init_mortars!(mortars, elements, mesh) - end + @unpack mortars = cache + resize!(mortars, count_required_mortars(mesh, leaf_cell_ids)) + init_mortars!(mortars, elements, mesh) if mpi_isparallel() # re-initialize mpi_interfaces container diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl index 3d15b595bfa..8d9bf0aa199 100644 --- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl @@ -17,7 +17,6 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1}, equations_parabolic::AbstractEquationsParabolic, initial_condition, boundary_conditions_parabolic, source_terms, dg::DG, parabolic_scheme, cache, cache_parabolic) - #@unpack u_transformed, gradients, flux_viscous = cache_parabolic @unpack cache_viscous = cache_parabolic @unpack u_transformed, gradients, flux_viscous = cache_viscous From 18ef38dbd39fb32e550515343b671528e7dee4b3 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 11:16:13 +0200 Subject: [PATCH 71/74] Clean up --- Project.toml | 2 - .../elixir_advection_diffusion_amr.jl | 102 ----------- ...ixir_navierstokes_convergence_walls_amr.jl | 172 ++++++++++++++++++ test/test_parabolic_1d.jl | 19 ++ 4 files changed, 191 insertions(+), 104 deletions(-) delete mode 100644 examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl create mode 100644 examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl diff --git a/Project.toml b/Project.toml index 2ec0906de83..c22d4b90642 100644 --- a/Project.toml +++ b/Project.toml @@ -19,9 +19,7 @@ MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" Octavian = "6fd5a793-0b7e-452c-907f-f8bfe9c57db4" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" P4est = "7d669430-f675-4ae7-b43e-fab78ec5a902" -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" diff --git a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl b/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl deleted file mode 100644 index ecd2f72f37a..00000000000 --- a/examples/tree_1d_dgsem/elixir_advection_diffusion_amr.jl +++ /dev/null @@ -1,102 +0,0 @@ - -using OrdinaryDiffEq, Plots -using Trixi - -############################################################################### -# semidiscretization of the linear advection diffusion equation - -advection_velocity = 0.1 -equations = LinearScalarAdvectionEquation1D(advection_velocity) -diffusivity() = 0.1 -equations_parabolic = LaplaceDiffusion1D(diffusivity(), equations) - -# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) - -coordinates_min = -pi # minimum coordinate -coordinates_max = pi # maximum coordinate - -# Create a uniformly refined mesh with periodic boundaries -mesh = TreeMesh(coordinates_min, coordinates_max, - initial_refinement_level=4, - n_cells_max=30_000, # set maximum capacity of tree data structure - periodicity=true) - -function x_trans_periodic(x, domain_length = SVector(2*pi), center = SVector(0.0)) - x_normalized = x .- center - x_shifted = x_normalized .% domain_length - x_offset = ((x_shifted .< -0.5*domain_length) - (x_shifted .> 0.5*domain_length)) .* domain_length - return center + x_shifted + x_offset -end - -# Define initial condition -function initial_condition_diffusive_convergence_test(x, t, equation::LinearScalarAdvectionEquation1D) - # Store translated coordinate for easy use of exact solution - x_trans = x_trans_periodic(x - equation.advection_velocity * t) - - nu = diffusivity() - c = 0.0 - A = 1.0 - L = 2 - f = 1/L - omega = 1.0 - scalar = c + A * sin(omega * sum(x_trans)) * exp(-nu * omega^2 * t) - return SVector(scalar) -end -initial_condition = initial_condition_diffusive_convergence_test - -# define periodic boundary conditions everywhere -boundary_conditions = boundary_condition_periodic -boundary_conditions_parabolic = boundary_condition_periodic - -# A semidiscretization collects data structures and functions for the spatial discretization -semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), - initial_condition_diffusive_convergence_test, solver; - boundary_conditions=(boundary_conditions, boundary_conditions_parabolic)) - - -############################################################################### -# ODE solvers, callbacks etc. - -# Create ODE problem with time span from 0.0 to 1.0 -tspan = (0.0, 1.0) -ode = semidiscretize(semi, tspan); - -# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup -# and resets the timers -summary_callback = SummaryCallback() - -# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) - -# The AliveCallback prints short status information in regular intervals -alive_callback = AliveCallback(analysis_interval=100) - -amr_controller = ControllerThreeLevel(semi, - IndicatorMax(semi, variable=first), - base_level=4, - med_level=5, med_threshold=0.1, - max_level=6, max_threshold=0.6) - -amr_callback = AMRCallback(semi, amr_controller, - interval=5, - adapt_initial_condition=true) - -# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback) -#callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback) - -############################################################################### -# run the simulation - -# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -time_int_tol = 1.0e-10 -time_abs_tol = 1.0e-10 -sol = solve(ode, KenCarp4(autodiff=false), abstol=time_abs_tol, reltol=time_int_tol, - save_everystep=false, callback=callbacks) - -# Print the timer summary -summary_callback() -plot(sol) -pd = PlotData1D(sol) -plot!(getmesh(pd)) \ No newline at end of file diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl new file mode 100644 index 00000000000..1daeab04a71 --- /dev/null +++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl @@ -0,0 +1,172 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the ideal compressible Navier-Stokes equations + +prandtl_number() = 0.72 +mu() = 0.01 + +equations = CompressibleEulerEquations1D(1.4) +equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(), Prandtl=prandtl_number(), + gradient_variables=GradientVariablesEntropy()) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs, + volume_integral=VolumeIntegralWeakForm()) + +coordinates_min = -1.0 +coordinates_max = 1.0 + +# Create a uniformly refined mesh with periodic boundaries +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level=3, + periodicity=false, + n_cells_max=30_000) # set maximum capacity of tree data structure + +# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion1D` +# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion1D`) +# and by the initial condition (which passes in `CompressibleEulerEquations1D`). +# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000) +function initial_condition_navier_stokes_convergence_test(x, t, equations) + # Amplitude and shift + A = 0.5 + c = 2.0 + + # convenience values for trig. functions + pi_x = pi * x[1] + pi_t = pi * t + + rho = c + A * cos(pi_x) * cos(pi_t) + v1 = log(x[1] + 2.0) * (1.0 - exp(-A * (x[1] - 1.0)) ) * cos(pi_t) + p = rho^2 + + return prim2cons(SVector(rho, v1, p), equations) +end + +@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations) + x = x[1] + + # TODO: parabolic + # we currently need to hardcode these parameters until we fix the "combined equation" issue + # see also https://github.com/trixi-framework/Trixi.jl/pull/1160 + inv_gamma_minus_one = inv(equations.gamma - 1) + Pr = prandtl_number() + mu_ = mu() + + # Same settings as in `initial_condition` + # Amplitude and shift + A = 0.5 + c = 2.0 + + # convenience values for trig. functions + pi_x = pi * x + pi_t = pi * t + + # compute the manufactured solution and all necessary derivatives + rho = c + A * cos(pi_x) * cos(pi_t) + rho_t = -pi * A * cos(pi_x) * sin(pi_t) + rho_x = -pi * A * sin(pi_x) * cos(pi_t) + rho_xx = -pi * pi * A * cos(pi_x) * cos(pi_t) + + v1 = log(x + 2.0) * (1.0 - exp(-A * (x - 1.0))) * cos(pi_t) + v1_t = -pi * log(x + 2.0) * (1.0 - exp(-A * (x - 1.0))) * sin(pi_t) + v1_x = (A * log(x + 2.0) * exp(-A * (x - 1.0)) + (1.0 - exp(-A * (x - 1.0))) / (x + 2.0)) * cos(pi_t) + v1_xx = (( 2.0 * A * exp(-A * (x - 1.0)) / (x + 2.0) + - A * A * log(x + 2.0) * exp(-A * (x - 1.0)) + - (1.0 - exp(-A * (x - 1.0))) / ((x + 2.0) * (x + 2.0))) * cos(pi_t)) + + p = rho * rho + p_t = 2.0 * rho * rho_t + p_x = 2.0 * rho * rho_x + p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x + + # Note this simplifies slightly because the ansatz assumes that v1 = v2 + E = p * inv_gamma_minus_one + 0.5 * rho * v1^2 + E_t = p_t * inv_gamma_minus_one + 0.5 * rho_t * v1^2 + rho * v1 * v1_t + E_x = p_x * inv_gamma_minus_one + 0.5 * rho_x * v1^2 + rho * v1 * v1_x + + # Some convenience constants + T_const = equations.gamma * inv_gamma_minus_one / Pr + inv_rho_cubed = 1.0 / (rho^3) + + # compute the source terms + # density equation + du1 = rho_t + rho_x * v1 + rho * v1_x + + # y-momentum equation + du2 = ( rho_t * v1 + rho * v1_t + + p_x + rho_x * v1^2 + 2.0 * rho * v1 * v1_x + # stress tensor from y-direction + - v1_xx * mu_) + + # total energy equation + du3 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x) + # stress tensor and temperature gradient terms from x-direction + - v1_xx * v1 * mu_ + - v1_x * v1_x * mu_ + - T_const * inv_rho_cubed * ( p_xx * rho * rho + - 2.0 * p_x * rho * rho_x + + 2.0 * p * rho_x * rho_x + - p * rho * rho_xx ) * mu_ ) + + return SVector(du1, du2, du3) +end + +initial_condition = initial_condition_navier_stokes_convergence_test + +# BC types +velocity_bc_left_right = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2]) + +heat_bc_left = Isothermal((x, t, equations) -> + Trixi.temperature(initial_condition_navier_stokes_convergence_test(x, t, equations), + equations_parabolic)) +heat_bc_right = Adiabatic((x, t, equations) -> 0.0) + +boundary_condition_left = BoundaryConditionNavierStokesWall(velocity_bc_left_right, heat_bc_left) +boundary_condition_right = BoundaryConditionNavierStokesWall(velocity_bc_left_right, heat_bc_right) + +# define inviscid boundary conditions +boundary_conditions = (; x_neg = boundary_condition_slip_wall, + x_pos = boundary_condition_slip_wall) + +# define viscous boundary conditions +boundary_conditions_parabolic = (; x_neg = boundary_condition_left, + x_pos = boundary_condition_right) + +semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver; + boundary_conditions=(boundary_conditions, boundary_conditions_parabolic), + source_terms=source_terms_navier_stokes_convergence_test) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span `tspan` +tspan = (0.0, 1.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() +alive_callback = AliveCallback(alive_interval=10) +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +amr_controller = ControllerThreeLevel(semi, + IndicatorLöhner(semi, variable=Trixi.density), + base_level=3, + med_level=4, med_threshold=0.005, + max_level=5, max_threshold=0.01) + +amr_callback = AMRCallback(semi, amr_controller, + interval=5, + adapt_initial_condition=true) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback) + +############################################################################### +# run the simulation + +time_int_tol = 1e-8 +sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5, + ode_default_options()..., callback=callbacks) +summary_callback() # print the timer summary \ No newline at end of file diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl index 06a55100d62..ee8c9ea9d5e 100644 --- a/test/test_parabolic_1d.jl +++ b/test/test_parabolic_1d.jl @@ -53,6 +53,25 @@ isdir(outdir) && rm(outdir, recursive=true) linf = [0.002754803146635787, 0.0028567714697580906, 0.012941794048176192] ) end + + @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_walls_amr.jl" begin + @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls_amr.jl"), + equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(), + Prandtl=prandtl_number()), + l2 = [2.527877257772131e-5, 2.5539911566937718e-5, 0.0001211860451244785], + linf = [0.00014663867588948776, 0.00019422448348348196, 0.0009556439394007299] + ) + end + + @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_walls_amr.jl: GradientVariablesEntropy" begin + @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls_amr.jl"), + equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(), + Prandtl=prandtl_number(), + gradient_variables = GradientVariablesEntropy()), + l2 = [2.4593699163175966e-5, 2.392863645712634e-5, 0.00011252526651714956], + linf = [0.00011850555445525046, 0.0001898777490968537, 0.0009597561467877824] + ) + end end # Clean up afterwards: delete Trixi output directory From ed1d89e8556fa91a1edeaeafb88fec8ba85d56a4 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 11:26:43 +0200 Subject: [PATCH 72/74] format --- src/callbacks_step/amr.jl | 13 +++++++---- src/callbacks_step/amr_dg1d.jl | 42 ++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl index 134e8d53f13..d742b17d262 100644 --- a/src/callbacks_step/amr.jl +++ b/src/callbacks_step/amr.jl @@ -198,7 +198,8 @@ end kwargs...) # Note that we don't `wrap_array` the vector `u_ode` to be able to `resize!` # it when doing AMR while still dispatching on the `mesh` etc. - amr_callback(u_ode, mesh_equations_solver_cache(semi)..., semi.cache_parabolic, semi, t, iter; kwargs...) + amr_callback(u_ode, mesh_equations_solver_cache(semi)..., semi.cache_parabolic, + semi, t, iter; kwargs...) end # `passive_args` is currently used for Euler with self-gravity to adapt the gravity solver @@ -362,7 +363,7 @@ end # `passive_args` is expected to be an iterable of `Tuple`s of the form # `(p_u_ode, p_mesh, p_equations, p_dg, p_cache)`. function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, - equations, dg::DG, + equations, dg::DG, cache, cache_parabolic, semi::SemidiscretizationHyperbolicParabolic, t, iter; @@ -413,7 +414,8 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, # refine solver @trixi_timeit timer() "solver" refine!(u_ode, adaptor, mesh, equations, dg, - cache, cache_parabolic, elements_to_refine) + cache, cache_parabolic, + elements_to_refine) for (p_u_ode, p_mesh, p_equations, p_dg, p_cache) in passive_args @trixi_timeit timer() "passive solver" refine!(p_u_ode, adaptor, p_mesh, p_equations, p_dg, p_cache, @@ -481,8 +483,9 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh, # coarsen solver @trixi_timeit timer() "solver" coarsen!(u_ode, adaptor, mesh, equations, dg, - cache, cache_parabolic, elements_to_remove) - + cache, cache_parabolic, + elements_to_remove) + for (p_u_ode, p_mesh, p_equations, p_dg, p_cache) in passive_args @trixi_timeit timer() "passive solver" coarsen!(p_u_ode, adaptor, p_mesh, p_equations, p_dg, p_cache, diff --git a/src/callbacks_step/amr_dg1d.jl b/src/callbacks_step/amr_dg1d.jl index 2521a10ed05..e729c8eccc8 100644 --- a/src/callbacks_step/amr_dg1d.jl +++ b/src/callbacks_step/amr_dg1d.jl @@ -113,16 +113,23 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, u = wrap_array(u_ode, mesh, equations, dg, cache) # Resize parabolic helper variables - resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + resize!(cache_viscous, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._u_transformed), - (nvariables(equations), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.gradients = unsafe_wrap(Array, + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + cache_parabolic.cache_viscous.gradients = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._gradients), - (nvariables(equations), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.flux_viscous = unsafe_wrap(Array, + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._flux_viscous), - (nvariables(equations), nnodes(dg), nelements(dg, cache))) + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) # Loop over all elements in old container and either copy them or refine them element_id = 1 for old_element_id in 1:old_n_elements @@ -339,16 +346,23 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1}, u = wrap_array(u_ode, mesh, equations, dg, cache) # Resize parabolic helper variables - resize!(cache_viscous, nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) - cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, + resize!(cache_viscous, + nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)) + cache_parabolic.cache_viscous.u_transformed = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._u_transformed), - (nvariables(equations), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.gradients = unsafe_wrap(Array, + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + cache_parabolic.cache_viscous.gradients = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._gradients), - (nvariables(equations), nnodes(dg), nelements(dg, cache))) - cache_parabolic.cache_viscous.flux_viscous = unsafe_wrap(Array, + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) + cache_parabolic.cache_viscous.flux_viscous = unsafe_wrap(Array, pointer(cache_parabolic.cache_viscous._flux_viscous), - (nvariables(equations), nnodes(dg), nelements(dg, cache))) + (nvariables(equations), + nnodes(dg), + nelements(dg, cache))) # Loop over all elements in old container and either copy them or coarsen them skip = 0 From deace038e73d620fbf58c0d18e69c4b7f602f418 Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 11:27:25 +0200 Subject: [PATCH 73/74] format --- src/solvers/dgsem_tree/cache_viscous.jl | 41 ++++++++++++----------- src/solvers/dgsem_tree/dg_1d_parabolic.jl | 1 - 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/solvers/dgsem_tree/cache_viscous.jl b/src/solvers/dgsem_tree/cache_viscous.jl index a04038ab727..17a2774db06 100644 --- a/src/solvers/dgsem_tree/cache_viscous.jl +++ b/src/solvers/dgsem_tree/cache_viscous.jl @@ -1,21 +1,22 @@ mutable struct CacheViscous1D{uEltype <: Real} - u_transformed::Array{uEltype, 3} - gradients::Array{uEltype, 3} - flux_viscous::Array{uEltype, 3} + u_transformed::Array{uEltype, 3} + gradients::Array{uEltype, 3} + flux_viscous::Array{uEltype, 3} - # internal `resize!`able storage - _u_transformed::Vector{uEltype} - _gradients::Vector{uEltype} - _flux_viscous::Vector{uEltype} + # internal `resize!`able storage + _u_transformed::Vector{uEltype} + _gradients::Vector{uEltype} + _flux_viscous::Vector{uEltype} - function CacheViscous1D{uEltype}(n_vars::Integer, n_nodes::Integer, n_elements::Integer) where {uEltype <: Real} - new(Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), - Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), - Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), - Vector{uEltype}(undef, n_vars * n_nodes * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes * n_elements), - Vector{uEltype}(undef, n_vars * n_nodes * n_elements)) - end + function CacheViscous1D{uEltype}(n_vars::Integer, n_nodes::Integer, + n_elements::Integer) where {uEltype <: Real} + new(Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements), + Vector{uEltype}(undef, n_vars * n_nodes * n_elements)) + end end # Only one-dimensional `Array`s are `resize!`able in Julia. @@ -24,9 +25,9 @@ end # `unsafe_wrap`ping multi-dimensional `Array`s around the # internal storage. function Base.resize!(cache_viscous::CacheViscous1D, capacity) - resize!(cache_viscous._u_transformed, capacity) - resize!(cache_viscous._gradients, capacity) - resize!(cache_viscous._flux_viscous, capacity) + resize!(cache_viscous._u_transformed, capacity) + resize!(cache_viscous._gradients, capacity) + resize!(cache_viscous._flux_viscous, capacity) - return nothing -end \ No newline at end of file + return nothing +end diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl index 8d9bf0aa199..596a6a8acbf 100644 --- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl +++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl @@ -565,5 +565,4 @@ function apply_jacobian_parabolic!(du, mesh::TreeMesh{1}, return nothing end - end # @muladd From 421fc9515fefd9f465103eacfcf33ee6175df3bc Mon Sep 17 00:00:00 2001 From: Daniel_Doehring Date: Fri, 11 Aug 2023 11:27:52 +0200 Subject: [PATCH 74/74] format --- src/solvers/dg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index b67aa4e7b71..495e0ffc4a4 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -535,7 +535,7 @@ function allocate_coefficients(mesh::AbstractMesh, equations, dg::DG, cache) end @inline function wrap_array(u_ode::AbstractVector, mesh::AbstractMesh, equations, - dg::DGSEM, cache) + dg::DGSEM, cache) @boundscheck begin @assert length(u_ode) == nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache)