diff --git a/examples/structured_2d_dgsem/elixir_euler_convergence_wavingflag_IDP.jl b/examples/structured_2d_dgsem/elixir_euler_convergence_wavingflag_IDP.jl new file mode 100644 index 00000000000..57c2d2a7801 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_convergence_wavingflag_IDP.jl @@ -0,0 +1,76 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_density_wave_highdensity + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_idp = SubcellLimiterIDP(equations, basis; + positivity_variables_cons = [1], + positivity_variables_nonlinear = (pressure,), + positivity_correction_factor = 0.1, + spec_entropy = false, + max_iterations_newton = 10, + newton_tolerances = (1.0e-12, 1.0e-14), + bar_states = true, + smoothness_indicator = false) + +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# Deformed rectangle that looks like a waving flag, +# lower and upper faces are sinus curves, left and right are vertical lines. +f1(s) = SVector(-1.0, s - 1.0) +f2(s) = SVector(1.0, s + 1.0) +f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) + +cells_per_dimension = (4, 4) + +mesh = StructuredMesh(cells_per_dimension, (f1, f2, f3, f4)) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/structured_2d_dgsem/elixir_euler_convergence_wavingflag_MCL.jl b/examples/structured_2d_dgsem/elixir_euler_convergence_wavingflag_MCL.jl new file mode 100644 index 00000000000..20a20a9c20b --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_convergence_wavingflag_MCL.jl @@ -0,0 +1,76 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_density_wave_highdensity + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = false, + DensityAlphaForAll = false, + SequentialLimiter = false, + ConservativeLimiter = false, + DensityPositivityLimiter = true, + PressurePositivityLimiterKuzmin = true, + PressurePositivityLimiterKuzminExact = true, + Plotting = true) + +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# Deformed rectangle that looks like a waving flag, +# lower and upper faces are sinus curves, left and right are vertical lines. +f1(s) = SVector(-1.0, s - 1.0) +f2(s) = SVector(1.0, s + 1.0) +f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) + +cells_per_dimension = (4, 4) + +mesh = StructuredMesh(cells_per_dimension, (f1, f2, f3, f4)) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/structured_2d_dgsem/elixir_euler_double_mach.jl b/examples/structured_2d_dgsem/elixir_euler_double_mach.jl new file mode 100644 index 00000000000..d306b70cb75 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_double_mach.jl @@ -0,0 +1,78 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +initial_condition = Trixi.initial_condition_double_mach_reflection + +boundary_condition_inflow_outflow = BoundaryConditionCharacteristic(initial_condition) + +boundary_conditions = (y_neg = Trixi.boundary_condition_mixed_dirichlet_wall, + y_pos = boundary_condition_inflow_outflow, + x_pos = boundary_condition_inflow_outflow, + x_neg = boundary_condition_inflow_outflow) + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) + +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + spec_entropy = true, + positivity_correction_factor = 0.1, + max_iterations_newton = 100, + bar_states = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +initial_refinement_level = 6 +cells_per_dimension = (4 * 2^initial_refinement_level, 2^initial_refinement_level) +coordinates_min = (0.0, 0.0) +coordinates_max = (4.0, 1.0) +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, + periodicity = false) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.2) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 500 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/structured_2d_dgsem/elixir_euler_double_mach_MCL.jl b/examples/structured_2d_dgsem/elixir_euler_double_mach_MCL.jl new file mode 100644 index 00000000000..c7df12f27af --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_double_mach_MCL.jl @@ -0,0 +1,81 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +initial_condition = Trixi.initial_condition_double_mach_reflection + +boundary_condition_inflow_outflow = BoundaryConditionCharacteristic(initial_condition) + +boundary_conditions = (y_neg = Trixi.boundary_condition_mixed_dirichlet_wall, + y_pos = boundary_condition_inflow_outflow, + x_pos = boundary_condition_inflow_outflow, + x_neg = boundary_condition_inflow_outflow) + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) + +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = true, + DensityAlphaForAll = false, + SequentialLimiter = true, + ConservativeLimiter = false, + DensityPositivityLimiter = false, + PressurePositivityLimiterKuzmin = false, + SemiDiscEntropyLimiter = false, + Plotting = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +initial_refinement_level = 6 +cells_per_dimension = (4 * 2^initial_refinement_level, 2^initial_refinement_level) +coordinates_min = (0.0, 0.0) +coordinates_max = (4.0, 1.0) +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, + periodicity = false) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.2) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 500 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/structured_2d_dgsem/elixir_euler_free_stream_MCL.jl b/examples/structured_2d_dgsem/elixir_euler_free_stream_MCL.jl new file mode 100644 index 00000000000..163cc6519a9 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_free_stream_MCL.jl @@ -0,0 +1,92 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_constant + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = false, + DensityAlphaForAll = false, + SequentialLimiter = false, + ConservativeLimiter = false, + PressurePositivityLimiterKuzmin = true, + PressurePositivityLimiterKuzminExact = true, + DensityPositivityLimiter = true, + SemiDiscEntropyLimiter = false, + smoothness_indicator = false, + Plotting = true) + +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but reduced to 2D. +# This particular mesh is unstructured in the yz-plane, but extruded in x-direction. +# Apply the warping mapping in the yz-plane to get a curved 2D mesh that is extruded +# in x-direction to ensure free stream preservation on a non-conforming mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. + +# Mapping as described in https://arxiv.org/abs/2012.12040, but reduced to 2D +function mapping(xi_, eta_) + # Transform input variables between -1 and 1 onto [0,3] + xi = 1.5 * xi_ + 1.5 + eta = 1.5 * eta_ + 1.5 + + y = eta + 3 / 8 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3)) + + x = xi + 3 / 8 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3)) + + return SVector(x, y) +end + +cells_per_dimension = (32, 32) +mesh = StructuredMesh(cells_per_dimension, mapping, periodicity = true) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 10000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/structured_2d_dgsem/elixir_euler_free_stream_sc_subcell.jl b/examples/structured_2d_dgsem/elixir_euler_free_stream_sc_subcell.jl new file mode 100644 index 00000000000..639fb928754 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_free_stream_sc_subcell.jl @@ -0,0 +1,90 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_constant + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_idp = SubcellLimiterIDP(equations, basis; + positivity_variables_cons = [1], + positivity_variables_nonlinear = (pressure,), + positivity_correction_factor = 0.1, + spec_entropy = false, + smoothness_indicator = false, + bar_states = true, + max_iterations_newton = 10, + newton_tolerances = (1.0e-12, 1.0e-14)) + +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# Mapping as described in https://arxiv.org/abs/2012.12040 but reduced to 2D. +# This particular mesh is unstructured in the yz-plane, but extruded in x-direction. +# Apply the warping mapping in the yz-plane to get a curved 2D mesh that is extruded +# in x-direction to ensure free stream preservation on a non-conforming mesh. +# See https://doi.org/10.1007/s10915-018-00897-9, Section 6. + +# Mapping as described in https://arxiv.org/abs/2012.12040, but reduced to 2D +function mapping(xi_, eta_) + # Transform input variables between -1 and 1 onto [0,3] + xi = 1.5 * xi_ + 1.5 + eta = 1.5 * eta_ + 1.5 + + y = eta + 3 / 8 * (cos(1.5 * pi * (2 * xi - 3) / 3) * + cos(0.5 * pi * (2 * eta - 3) / 3)) + + x = xi + 3 / 8 * (cos(0.5 * pi * (2 * xi - 3) / 3) * + cos(2 * pi * (2 * y - 3) / 3)) + + return SVector(x, y) +end + +cells_per_dimension = (32, 32) +mesh = StructuredMesh(cells_per_dimension, mapping, periodicity = true) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 10000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/structured_2d_dgsem/elixir_euler_shock_upstream_MCL.jl b/examples/structured_2d_dgsem/elixir_euler_shock_upstream_MCL.jl new file mode 100644 index 00000000000..06eaceb4f7c --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_shock_upstream_MCL.jl @@ -0,0 +1,145 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) + +A version of the classical Kelvin-Helmholtz instability based on +- Andrés M. Rueda-Ramírez, Gregor J. Gassner (2021) + A Subcell Finite Volume Positivity-Preserving Limiter for DGSEM Discretizations + of the Euler Equations + [arXiv: 2102.06017](https://arxiv.org/abs/2102.06017) +""" +function initial_condition_inviscid_bow(x, t, equations::CompressibleEulerEquations2D) + rho = 1.4 + p = 1.0 + v1 = 4.0 + v2 = 0.0 + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_inviscid_bow + +boundary_condition = BoundaryConditionCharacteristic(initial_condition) +boundary_conditions = (x_neg = boundary_condition, + x_pos = boundary_condition_slip_wall, + y_neg = boundary_condition, + y_pos = boundary_condition) + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 5 +basis = LobattoLegendreBasis(polydeg) + +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = true, + DensityAlphaForAll = false, + SequentialLimiter = true, + ConservativeLimiter = false, + PressurePositivityLimiterKuzmin = false, + PressurePositivityLimiterKuzminExact = false, + DensityPositivityLimiter = false, + SemiDiscEntropyLimiter = false, + Plotting = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# domain +# ,, +# , | +# , | f4 of length a-1 +#f1 , | +# , ,` f2 /alpha +# , |_(0,0)___________/_______(3.85,0) +# , | +# , `, +# , `| +# , | f3 +# ,| +# l = circumference of quarter circle / length of egg-shaped form +a = sqrt(5.9^2 - 3.85^2) +alpha = acos(3.85 / 5.9) +l = (pi / 4) / (pi / 2 + 1) +f1(s) = SVector(5.9 * cos(pi - s * alpha) + 3.85, 5.9 * sin(pi - s * alpha)) # left +function f2(s) # right + t = 0.5 * s + 0.5 # in [0,1] + if 0 <= t <= l + beta = t / l # in [0,1] + return SVector(0.5 * cos(1.5 * pi - beta * 0.5 * pi), + 0.5 * sin(1.5 * pi - beta * 0.5 * pi) - 0.5) + elseif l < t <= 1 - l # 0 <= t - l <= 1-2l + beta = (t - l) / (1 - 2 * l) # in [0,1] + return SVector(-0.5, -0.5 + beta) + else # 1 - l < t <= 1 + beta = (t + l - 1) / l # in [0,1] + return SVector(0.5 * cos(pi - beta * 0.5 * pi), + 0.5 * sin(pi - beta * 0.5 * pi) + 0.5) + end +end +f3(s) = SVector(0.0, (a - 1.0) * 0.5 * (s + 1.0) - a) # bottom +f4(s) = SVector(0.0, -(a - 1.0) * 0.5 * (s + 1.0) + a) # top +faces = (f1, f2, f3, f4) + +# This creates a mapping that transforms [-1, 1]^2 to the domain with the faces defined above. +Trixi.validate_faces(faces) +mapping_bow = Trixi.transfinite_mapping(faces) + +mapping_as_string = "a = sqrt(5.9^2 - 3.85^2); alpha = acos(3.85 / 5.9); l = (pi / 4) / (pi / 2 + 1); " * + "f1(s) = SVector(5.9 * cos(pi - s * alpha) + 3.85, 5.9 * sin(pi - s * alpha)); " * + "function f2(s); t = 0.5 * s + 0.5; " * + "if 0 <= t <= l; beta = t / l; return SVector(0.5 * cos(1.5 * pi - beta * 0.5 * pi), 0.5 * sin(1.5 * pi - beta * 0.5 * pi) - 0.5); " * + "elseif l < t <= 1 - l; beta = (t - l) / (1 - 2 * l); return SVector(-0.5, -0.5 + beta); " * + "else beta = (t + l - 1) / l; return SVector(0.5 * cos(pi - beta * 0.5 * pi), 0.5 * sin(pi - beta * 0.5 * pi) + 0.5); end; end; " * + "f3(s) = SVector(0.0, (a - 1.0) * 0.5 * (s + 1.0) - a); " * + "f4(s) = SVector(0.0, -(a - 1.0) * 0.5 * (s + 1.0) + a); " * + "faces = (f1, f2, f3, f4); mapping = Trixi.transfinite_mapping(faces)" + +cells_per_dimension = (24, 36) + +mesh = StructuredMesh(cells_per_dimension, mapping_bow, + mapping_as_string = mapping_as_string, periodicity = false) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 10.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 2000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/structured_2d_dgsem/elixir_euler_shock_upstream_sc_subcell.jl b/examples/structured_2d_dgsem/elixir_euler_shock_upstream_sc_subcell.jl new file mode 100644 index 00000000000..def69ecc282 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_shock_upstream_sc_subcell.jl @@ -0,0 +1,140 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) + +A version of the classical Kelvin-Helmholtz instability based on +- Andrés M. Rueda-Ramírez, Gregor J. Gassner (2021) + A Subcell Finite Volume Positivity-Preserving Limiter for DGSEM Discretizations + of the Euler Equations + [arXiv: 2102.06017](https://arxiv.org/abs/2102.06017) +""" +function initial_condition_inviscid_bow(x, t, equations::CompressibleEulerEquations2D) + rho = 1.4 + p = 1.0 + v1 = 4.0 + v2 = 0.0 + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_inviscid_bow + +boundary_condition = BoundaryConditionCharacteristic(initial_condition) +boundary_conditions = (x_neg = boundary_condition, + x_pos = boundary_condition_slip_wall, + y_neg = boundary_condition, + y_pos = boundary_condition) + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 5 +basis = LobattoLegendreBasis(polydeg) + +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + spec_entropy = true, + max_iterations_newton = 100, + bar_states = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# domain +# ,, +# , | +# , | f4 of length a-1 +#f1 , | +# , ,` f2 /alpha +# , |_(0,0)___________/_______(3.85,0) +# , | +# , `, +# , `| +# , | f3 +# ,| +# l = circumference of quarter circle / length of egg-shaped form +a = sqrt(5.9^2 - 3.85^2) +alpha = acos(3.85 / 5.9) +l = (pi / 4) / (pi / 2 + 1) +f1(s) = SVector(5.9 * cos(pi - s * alpha) + 3.85, 5.9 * sin(pi - s * alpha)) # left +function f2(s) # right + t = 0.5 * s + 0.5 # in [0,1] + if 0 <= t <= l + beta = t / l # in [0,1] + return SVector(0.5 * cos(1.5 * pi - beta * 0.5 * pi), + 0.5 * sin(1.5 * pi - beta * 0.5 * pi) - 0.5) + elseif l < t <= 1 - l # 0 <= t - l <= 1-2l + beta = (t - l) / (1 - 2 * l) # in [0,1] + return SVector(-0.5, -0.5 + beta) + else # 1 - l < t <= 1 + beta = (t + l - 1) / l # in [0,1] + return SVector(0.5 * cos(pi - beta * 0.5 * pi), + 0.5 * sin(pi - beta * 0.5 * pi) + 0.5) + end +end +f3(s) = SVector(0.0, (a - 1.0) * 0.5 * (s + 1.0) - a) # bottom +f4(s) = SVector(0.0, -(a - 1.0) * 0.5 * (s + 1.0) + a) # top +faces = (f1, f2, f3, f4) + +# This creates a mapping that transforms [-1, 1]^2 to the domain with the faces defined above. +Trixi.validate_faces(faces) +mapping_bow = Trixi.transfinite_mapping(faces) + +mapping_as_string = "a = sqrt(5.9^2 - 3.85^2); alpha = acos(3.85 / 5.9); l = (pi / 4) / (pi / 2 + 1); " * + "f1(s) = SVector(5.9 * cos(pi - s * alpha) + 3.85, 5.9 * sin(pi - s * alpha)); " * + "function f2(s); t = 0.5 * s + 0.5; " * + "if 0 <= t <= l; beta = t / l; return SVector(0.5 * cos(1.5 * pi - beta * 0.5 * pi), 0.5 * sin(1.5 * pi - beta * 0.5 * pi) - 0.5); " * + "elseif l < t <= 1 - l; beta = (t - l) / (1 - 2 * l); return SVector(-0.5, -0.5 + beta); " * + "else beta = (t + l - 1) / l; return SVector(0.5 * cos(pi - beta * 0.5 * pi), 0.5 * sin(pi - beta * 0.5 * pi) + 0.5); end; end; " * + "f3(s) = SVector(0.0, (a - 1.0) * 0.5 * (s + 1.0) - a); " * + "f4(s) = SVector(0.0, -(a - 1.0) * 0.5 * (s + 1.0) + a); " * + "faces = (f1, f2, f3, f4); mapping = Trixi.transfinite_mapping(faces)" + +cells_per_dimension = (24, 36) + +mesh = StructuredMesh(cells_per_dimension, mapping_bow, + mapping_as_string = mapping_as_string, periodicity = false) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 10.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 2000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/structured_2d_dgsem/elixir_euler_source_terms_sc_subcell.jl b/examples/structured_2d_dgsem/elixir_euler_source_terms_sc_subcell.jl new file mode 100644 index 00000000000..53c07261c36 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_source_terms_sc_subcell.jl @@ -0,0 +1,73 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_convergence_test +source_terms = source_terms_convergence_test + +# Get the DG approximation space +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + spec_entropy = true, + bar_states = false) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +# Waving flag +f1(s) = SVector(-1.0, s - 1.0) +f2(s) = SVector(1.0, s + 1.0) +f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s)) +f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s)) +mapping = Trixi.transfinite_mapping((f1, f2, f3, f4)) + +cells_per_dimension = (16, 16) +mesh = StructuredMesh(cells_per_dimension, mapping, periodicity = true) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms = source_terms) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.5) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_astro_jet_MCL.jl b/examples/tree_2d_dgsem/elixir_euler_astro_jet_MCL.jl new file mode 100644 index 00000000000..69148f5bdb6 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_astro_jet_MCL.jl @@ -0,0 +1,100 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 5 / 3 +equations = CompressibleEulerEquations2D(gamma) + +# Initial condition adopted from +# - Yong Liu, Jianfang Lu, and Chi-Wang Shu +# An oscillation free discontinuous Galerkin method for hyperbolic systems +# https://tinyurl.com/c76fjtx4 +# Mach = 2000 jet +function initial_condition_astro_jet(x, t, equations::CompressibleEulerEquations2D) + @unpack gamma = equations + rho = 0.5 + v1 = 0 + v2 = 0 + p = 0.4127 + # add inflow for t>0 at x=-0.5 + # domain size is [-0.5,+0.5]^2 + if (x[1] ≈ -0.5) && (abs(x[2]) < 0.05) + rho = 5 + v1 = 800 # about Mach number Ma = 2000 + v2 = 0 + p = 0.4127 + end + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_astro_jet + +boundary_conditions = (x_neg = BoundaryConditionCharacteristic(initial_condition_astro_jet), + x_pos = BoundaryConditionCharacteristic(initial_condition_astro_jet), + y_neg = boundary_condition_periodic, + y_pos = boundary_condition_periodic) + +surface_flux = flux_lax_friedrichs # HLLC needs more shock capturing (alpha_max) +volume_flux = flux_chandrashekar # works with Ranocha flux as well +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) + +# shock capturing necessary for this tough example +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = true, + DensityAlphaForAll = true, + SequentialLimiter = true, + PressurePositivityLimiterKuzmin = true, + DensityPositivityLimiter = false, + SemiDiscEntropyLimiter = false, + Plotting = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-0.5, -0.5) +coordinates_max = (0.5, 0.5) + +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 8, + periodicity = (false, true), + n_cells_max = 100_000) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.001) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 5000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + maxiters = 1e6, dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/tree_2d_dgsem/elixir_euler_astro_jet_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_astro_jet_subcell.jl new file mode 100644 index 00000000000..3bf4f230d44 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_astro_jet_subcell.jl @@ -0,0 +1,96 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 5 / 3 +equations = CompressibleEulerEquations2D(gamma) + +# Initial condition adopted from +# - Yong Liu, Jianfang Lu, and Chi-Wang Shu +# An oscillation free discontinuous Galerkin method for hyperbolic systems +# https://tinyurl.com/c76fjtx4 +# Mach = 2000 jet +function initial_condition_astro_jet(x, t, equations::CompressibleEulerEquations2D) + @unpack gamma = equations + rho = 0.5 + v1 = 0 + v2 = 0 + p = 0.4127 + # add inflow for t>0 at x=-0.5 + # domain size is [-0.5,+0.5]^2 + if (x[1] ≈ -0.5) && (abs(x[2]) < 0.05) + rho = 5 + v1 = 800 # about Mach number Ma = 2000 + v2 = 0 + p = 0.4127 + end + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_astro_jet + +boundary_conditions = (x_neg = BoundaryConditionCharacteristic(initial_condition_astro_jet), + x_pos = BoundaryConditionCharacteristic(initial_condition_astro_jet), + y_neg = boundary_condition_periodic, + y_pos = boundary_condition_periodic) + +surface_flux = flux_lax_friedrichs # HLLC needs more shock capturing (alpha_max) +volume_flux = flux_chandrashekar # works with Ranocha flux as well +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) + +# shock capturing necessary for this tough example +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + spec_entropy = true, + bar_states = true, + max_iterations_newton = 25) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-0.5, -0.5) +coordinates_max = (0.5, 0.5) + +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 8, + periodicity = (false, true), + n_cells_max = 100_000) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions = boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.001) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) + +############################################################################### +# run the simulation +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + maxiters = 1e6, dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/tree_2d_dgsem/elixir_euler_blast_wave_MCL.jl b/examples/tree_2d_dgsem/elixir_euler_blast_wave_MCL.jl new file mode 100644 index 00000000000..3e90ef7da2e --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_blast_wave_MCL.jl @@ -0,0 +1,98 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +""" + initial_condition_blast_wave(x, t, equations::CompressibleEulerEquations2D) + +A medium 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_blast_wave(x, t, equations::CompressibleEulerEquations2D) + # Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> "medium blast wave" + # 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.0E-3 : 1.245 + + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_blast_wave + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +basis = LobattoLegendreBasis(3) +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = true, + DensityAlphaForAll = true, + SequentialLimiter = false, + ConservativeLimiter = true, + DensityPositivityLimiter = true, + PressurePositivityLimiterKuzmin = true, + PressurePositivityLimiterKuzminExact = false, + SemiDiscEntropyLimiter = true, + smoothness_indicator = true, + Plotting = false) +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-2.0, -2.0) +coordinates_max = (2.0, 2.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 6, + n_cells_max = 10_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 500, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_blast_wave_sc_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_blast_wave_sc_subcell.jl new file mode 100644 index 00000000000..fab3a86e531 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_blast_wave_sc_subcell.jl @@ -0,0 +1,92 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +""" + initial_condition_blast_wave(x, t, equations::CompressibleEulerEquations2D) + +A medium 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_blast_wave(x, t, equations::CompressibleEulerEquations2D) + # Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> "medium blast wave" + # 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.0E-3 : 1.245 + + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_blast_wave + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +basis = LobattoLegendreBasis(3) +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + math_entropy = true, + bar_states = false, + smoothness_indicator = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-2.0, -2.0) +coordinates_max = (2.0, 2.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 6, + n_cells_max = 10_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.6) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_convergence_IDP.jl b/examples/tree_2d_dgsem/elixir_euler_convergence_IDP.jl new file mode 100644 index 00000000000..eccf13c6d2c --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_convergence_IDP.jl @@ -0,0 +1,71 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_density_wave_highdensity + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_idp = SubcellLimiterIDP(equations, basis; + positivity_variables_cons = [1], + positivity_variables_nonlinear = (pressure,), + positivity_correction_factor = 0.1, + spec_entropy = false, + max_iterations_newton = 10, + newton_tolerances = (1.0e-12, 1.0e-14), + bar_states = true, + smoothness_indicator = false) + +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-1.0, -1.0) +coordinates_max = (1.0, 1.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 2, + n_cells_max = 10_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_convergence_MCL.jl b/examples/tree_2d_dgsem/elixir_euler_convergence_MCL.jl new file mode 100644 index 00000000000..d82aed8ba32 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_convergence_MCL.jl @@ -0,0 +1,71 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_density_wave_highdensity + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = false, + DensityAlphaForAll = false, + SequentialLimiter = false, + ConservativeLimiter = false, + DensityPositivityLimiter = true, + PressurePositivityLimiterKuzmin = true, + PressurePositivityLimiterKuzminExact = true, + Plotting = true) + +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-1.0, -1.0) +coordinates_max = (1.0, 1.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 2, + n_cells_max = 10_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_MCL.jl b/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_MCL.jl new file mode 100644 index 00000000000..548485a1abc --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_MCL.jl @@ -0,0 +1,98 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) + +A version of the classical Kelvin-Helmholtz instability based on +- Andrés M. Rueda-Ramírez, Gregor J. Gassner (2021) + A Subcell Finite Volume Positivity-Preserving Limiter for DGSEM Discretizations + of the Euler Equations + [arXiv: 2102.06017](https://arxiv.org/abs/2102.06017) +""" +function initial_condition_kelvin_helmholtz_instability(x, t, + equations::CompressibleEulerEquations2D) + # change discontinuity to tanh + # typical resolution 128^2, 256^2 + # domain size is [-1,+1]^2 + slope = 15 + amplitude = 0.02 + B = tanh(slope * x[2] + 7.5) - tanh(slope * x[2] - 7.5) + rho = 0.5 + 0.75 * B + v1 = 0.5 * (B - 1) + v2 = 0.1 * sin(2 * pi * x[1]) + p = 1.0 + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_kelvin_helmholtz_instability + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) + +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = false, + DensityAlphaForAll = false, + SequentialLimiter = false, + ConservativeLimiter = false, + PressurePositivityLimiterKuzmin = true, + PressurePositivityLimiterKuzminExact = true, + DensityPositivityLimiter = true, + SemiDiscEntropyLimiter = false, + Plotting = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-1.0, -1.0) +coordinates_max = (1.0, 1.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 6, + n_cells_max = 100_000) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 3.7) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 5000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +save_restart = SaveRestartCallback(interval = 50000, + save_final_restart = true) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_restart, save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (BoundsCheckCallback(save_errors = false),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + maxiters = 1e7, callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_restart.jl b/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_restart.jl new file mode 100644 index 00000000000..dc54bb1fb3d --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_restart.jl @@ -0,0 +1,32 @@ +using OrdinaryDiffEq +using Trixi + +path = "out" + +restart_file = "restart_063585.h5" +restart_filename = joinpath(path, restart_file) + +new_path = path + +tspan = (load_time(restart_filename), 6.7) +ode = semidiscretize(semi, tspan, restart_filename); + +save_solution = SaveSolutionCallback(output_directory = path, + interval = 5000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +save_restart = SaveRestartCallback(output_directory = path, + interval = 50000, + save_final_restart = true) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_restart, save_solution) + +sol = Trixi.solve(ode, + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_sc_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_sc_subcell.jl new file mode 100644 index 00000000000..8f07b7ca920 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_kelvin_helmholtz_instability_sc_subcell.jl @@ -0,0 +1,93 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D) + +A version of the classical Kelvin-Helmholtz instability based on +- Andrés M. Rueda-Ramírez, Gregor J. Gassner (2021) + A Subcell Finite Volume Positivity-Preserving Limiter for DGSEM Discretizations + of the Euler Equations + [arXiv: 2102.06017](https://arxiv.org/abs/2102.06017) +""" +function initial_condition_kelvin_helmholtz_instability(x, t, + equations::CompressibleEulerEquations2D) + # change discontinuity to tanh + # typical resolution 128^2, 256^2 + # domain size is [-1,+1]^2 + slope = 15 + amplitude = 0.02 + B = tanh(slope * x[2] + 7.5) - tanh(slope * x[2] - 7.5) + rho = 0.5 + 0.75 * B + v1 = 0.5 * (B - 1) + v2 = 0.1 * sin(2 * pi * x[1]) + p = 1.0 + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_kelvin_helmholtz_instability + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) + +limiter_idp = SubcellLimiterIDP(equations, basis; + positivity_variables_cons = [1], + positivity_variables_nonlinear = (pressure,), + spec_entropy = false, + bar_states = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-1.0, -1.0) +coordinates_max = (1.0, 1.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 6, + n_cells_max = 100_000) +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 3.7) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 5000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +save_restart = SaveRestartCallback(interval = 50000, + save_final_restart = true) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_restart, save_solution) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + callback = callbacks); +summary_callback() # print the timer summary diff --git a/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_MCL.jl b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_MCL.jl new file mode 100644 index 00000000000..a5e7532854e --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_MCL.jl @@ -0,0 +1,105 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) + +The Sedov blast wave setup based on Flash +- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 +""" +function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) + # 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) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) + # r0 = 0.5 # = more reasonable setup + E = 1.0 + p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2) + p0_outer = 1.0e-5 # = true Sedov setup + # p0_outer = 1.0e-3 # = more reasonable setup + + # Calculate primitive variables + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + p = r > r0 ? p0_outer : p0_inner + + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_sedov_blast_wave + +surface_flux = flux_lax_friedrichs +volume_flux = flux_chandrashekar +basis = LobattoLegendreBasis(3) +limiter_mcl = SubcellLimiterMCL(equations, basis; + DensityLimiter = true, + DensityAlphaForAll = false, + SequentialLimiter = true, + ConservativeLimiter = false, + DensityPositivityLimiter = false, + PressurePositivityLimiterKuzmin = true, + PressurePositivityLimiterKuzminExact = true, + SemiDiscEntropyLimiter = true, + smoothness_indicator = false, + Plotting = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_mcl; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-2.0, -2.0) +coordinates_max = (2.0, 2.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 3, + n_cells_max = 100_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 3.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +limiting_analysis_callback = LimitingAnalysisCallback(output_directory = "out", + interval = 1) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, limiting_analysis_callback, + save_solution) +############################################################################### +# run the simulation + +output_directory = "out" +stage_callbacks = (BoundsCheckCallback(save_errors = true, interval = 100, + output_directory = output_directory),) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_sc_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_sc_subcell.jl new file mode 100644 index 00000000000..b460b7a507b --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_sc_subcell.jl @@ -0,0 +1,98 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations +gamma = 1.4 +equations = CompressibleEulerEquations2D(gamma) + +""" + initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) + +The Sedov blast wave setup based on Flash +- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 +""" +function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D) + # 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) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) + # r0 = 0.5 # = more reasonable setup + E = 1.0 + p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2) + p0_outer = 1.0e-5 # = true Sedov setup + # p0_outer = 1.0e-3 # = more reasonable setup + + # Calculate primitive variables + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + p = r > r0 ? p0_outer : p0_inner + + return prim2cons(SVector(rho, v1, v2, p), equations) +end +initial_condition = initial_condition_sedov_blast_wave + +surface_flux = flux_lax_friedrichs +volume_flux = flux_chandrashekar +basis = LobattoLegendreBasis(3) +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + spec_entropy = true, + smoothness_indicator = false, + bar_states = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-2.0, -2.0) +coordinates_max = (2.0, 2.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 3, + n_cells_max = 100_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 3.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +limiting_analysis_callback = LimitingAnalysisCallback(output_directory = "out", + interval = 1) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + limiting_analysis_callback, + save_solution) +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl index 3fea48b30da..4e64cb3a91c 100644 --- a/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl +++ b/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl @@ -40,7 +40,8 @@ volume_flux = flux_ranocha basis = LobattoLegendreBasis(3) limiter_idp = SubcellLimiterIDP(equations, basis; positivity_variables_cons = [1], - positivity_correction_factor = 0.5) + positivity_correction_factor = 0.5, + bar_states = false) volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; volume_flux_dg = volume_flux, volume_flux_fv = surface_flux) diff --git a/examples/tree_2d_dgsem/elixir_euler_source_terms_sc_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_source_terms_sc_subcell.jl new file mode 100644 index 00000000000..6d5281fcbc0 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_euler_source_terms_sc_subcell.jl @@ -0,0 +1,68 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_convergence_test + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [1], + positivity_variables_cons = [1], + positivity_variables_nonlinear = (pressure,), + bar_states = true, + smoothness_indicator = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (0.0, 0.0) +coordinates_max = (2.0, 2.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 4, + n_cells_max = 10_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms = source_terms_convergence_test) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.6) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + stepsize_callback, + save_solution) +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_minmax.jl b/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_minmax.jl new file mode 100644 index 00000000000..4a8a70b5663 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_minmax.jl @@ -0,0 +1,145 @@ +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler multicomponent equations + +# 1) Dry Air 2) Helium + 28% Air +equations = CompressibleEulerMulticomponentEquations2D(gammas = (1.4, 1.648), + gas_constants = (0.287, 1.578)) + +""" + initial_condition_shock_bubble(x, t, equations::CompressibleEulerMulticomponentEquations2D{5, 2}) + +A shock-bubble testcase for multicomponent Euler equations +- Ayoub Gouasmi, Karthik Duraisamy, Scott Murman + Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations + [arXiv: 1904.00972](https://arxiv.org/abs/1904.00972) +""" +function initial_condition_shock_bubble(x, t, + equations::CompressibleEulerMulticomponentEquations2D{ + 5, + 2 + }) + # bubble test case, see Gouasmi et al. https://arxiv.org/pdf/1904.00972 + # other reference: https://www.researchgate.net/profile/Pep_Mulet/publication/222675930_A_flux-split_algorithm_applied_to_conservative_models_for_multicomponent_compressible_flows/links/568da54508aeaa1481ae7af0.pdf + # typical domain is rectangular, we change it to a square, as Trixi can only do squares + @unpack gas_constants = equations + + # Positivity Preserving Parameter, can be set to zero if scheme is positivity preserving + delta = 0.03 + + # Region I + rho1_1 = delta + rho2_1 = 1.225 * gas_constants[1] / gas_constants[2] - delta + v1_1 = zero(delta) + v2_1 = zero(delta) + p_1 = 101325 + + # Region II + rho1_2 = 1.225 - delta + rho2_2 = delta + v1_2 = zero(delta) + v2_2 = zero(delta) + p_2 = 101325 + + # Region III + rho1_3 = 1.6861 - delta + rho2_3 = delta + v1_3 = -113.5243 + v2_3 = zero(delta) + p_3 = 159060 + + # Set up Region I & II: + inicenter = SVector(zero(delta), zero(delta)) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + + if (x[1] > 0.50) + # Set up Region III + rho1 = rho1_3 + rho2 = rho2_3 + v1 = v1_3 + v2 = v2_3 + p = p_3 + elseif (r < 0.25) + # Set up Region I + rho1 = rho1_1 + rho2 = rho2_1 + v1 = v1_1 + v2 = v2_1 + p = p_1 + else + # Set up Region II + rho1 = rho1_2 + rho2 = rho2_2 + v1 = v1_2 + v2 = v2_2 + p = p_2 + end + + return prim2cons(SVector(v1, v2, p, rho1, rho2), equations) +end +initial_condition = initial_condition_shock_bubble + +surface_flux = flux_lax_friedrichs +volume_flux = flux_ranocha +basis = LobattoLegendreBasis(3) + +limiter_idp = SubcellLimiterIDP(equations, basis; + local_minmax_variables_cons = [ + (i + 3 for i in eachcomponent(equations))..., + ], + spec_entropy = false, + bar_states = true) +volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; + volume_flux_dg = volume_flux, + volume_flux_fv = surface_flux) + +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (-2.25, -2.225) +coordinates_max = (2.20, 2.225) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 3, + n_cells_max = 1_000_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 0.01) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 300 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (Trixi.density,)) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 600, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +stepsize_callback = StepsizeCallback(cfl = 0.9) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +stage_callbacks = (SubcellLimiterIDPCorrection(), BoundsCheckCallback(save_errors = false)) + +sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks = stage_callbacks); + dt = 1.0, # 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 diff --git a/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl b/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl index 6241ca30938..5f6cdb1e9c0 100644 --- a/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl +++ b/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl @@ -90,7 +90,11 @@ basis = LobattoLegendreBasis(3) limiter_idp = SubcellLimiterIDP(equations, basis; positivity_variables_cons = [ (i + 3 for i in eachcomponent(equations))..., - ]) + ], + positivity_variables_nonlinear = (), + positivity_correction_factor = 0.1, + spec_entropy = false, + bar_states = false) volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; volume_flux_dg = volume_flux, diff --git a/examples/tree_2d_dgsem/elixir_mhd_shockcapturing_subcell.jl b/examples/tree_2d_dgsem/elixir_mhd_shockcapturing_subcell.jl index b2cdff2ab53..84361ef4f87 100644 --- a/examples/tree_2d_dgsem/elixir_mhd_shockcapturing_subcell.jl +++ b/examples/tree_2d_dgsem/elixir_mhd_shockcapturing_subcell.jl @@ -22,7 +22,7 @@ function initial_condition_blast_wave(x, t, equations::IdealGlmMhdEquations2D) r = sqrt(x[1]^2 + x[2]^2) pmax = 10.0 - pmin = 1.0 + pmin = 0.01 rhomax = 1.0 rhomin = 0.01 if r <= 0.09 @@ -52,7 +52,9 @@ basis = LobattoLegendreBasis(3) limiter_idp = SubcellLimiterIDP(equations, basis; positivity_variables_cons = [1], - positivity_correction_factor = 0.5) + positivity_variables_nonlinear = [pressure], + positivity_correction_factor = 0.1, + bar_states = false) volume_integral = VolumeIntegralSubcellLimiting(limiter_idp; volume_flux_dg = volume_flux, volume_flux_fv = surface_flux) @@ -84,7 +86,7 @@ save_solution = SaveSolutionCallback(interval = 100, save_final_solution = true, solution_variables = cons2prim) -cfl = 0.5 +cfl = 0.4 stepsize_callback = StepsizeCallback(cfl = cfl) glm_speed_callback = GlmSpeedCallback(glm_scale = 0.5, cfl = cfl) diff --git a/src/Trixi.jl b/src/Trixi.jl index 2aa47e04de0..1f7692d707c 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -133,7 +133,6 @@ include("auxiliary/special_elixirs.jl") include("visualization/visualization.jl") # export types/functions that define the public API of Trixi.jl - export AcousticPerturbationEquations2D, CompressibleEulerEquations1D, CompressibleEulerEquations2D, CompressibleEulerEquations3D, @@ -190,11 +189,13 @@ export splitting_steger_warming, splitting_vanleer_haenel, export initial_condition_constant, initial_condition_gauss, initial_condition_density_wave, + initial_condition_density_wave_highdensity, initial_condition_weak_blast_wave export boundary_condition_do_nothing, boundary_condition_periodic, BoundaryConditionDirichlet, + BoundaryConditionCharacteristic, BoundaryConditionNeumann, boundary_condition_noslip_wall, boundary_condition_slip_wall, @@ -220,7 +221,7 @@ export entropy, energy_total, energy_kinetic, energy_internal, energy_magnetic, enstrophy export lake_at_rest_error export ncomponents, eachcomponent -export get_component +export get_component, set_component!, magnetic_field, MVector export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh @@ -239,7 +240,8 @@ export DG, MortarL2 export VolumeIntegralSubcellLimiting, BoundsCheckCallback, - SubcellLimiterIDP, SubcellLimiterIDPCorrection + SubcellLimiterIDP, SubcellLimiterIDPCorrection, + SubcellLimiterMCL export nelements, nnodes, nvariables, eachelement, eachnode, eachvariable @@ -258,7 +260,7 @@ export SemidiscretizationCoupled export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback, SaveRestartCallback, SaveSolutionCallback, TimeSeriesCallback, VisualizationCallback, AveragingCallback, - AMRCallback, StepsizeCallback, + AMRCallback, StepsizeCallback, LimitingAnalysisCallback, GlmSpeedCallback, LBMCollisionCallback, EulerAcousticsCouplingCallback, TrivialCallback, AnalysisCallbackCoupled diff --git a/src/auxiliary/special_elixirs.jl b/src/auxiliary/special_elixirs.jl index 25bca8939ce..919eddb18fa 100644 --- a/src/auxiliary/special_elixirs.jl +++ b/src/auxiliary/special_elixirs.jl @@ -20,7 +20,7 @@ providing examples with sensible default values for users. Before replacing assignments in `elixir`, the keyword argument `maxiters` is inserted into calls to `solve` and `Trixi.solve` with it's default value used in the SciML ecosystem -for ODEs, see the "Miscellaneous" section of the +for ODEs, see the "Miscellaneous" section of the [documentation](https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/). # Examples diff --git a/src/callbacks_stage/subcell_bounds_check.jl b/src/callbacks_stage/subcell_bounds_check.jl index c86f266147c..527bcb0750a 100644 --- a/src/callbacks_stage/subcell_bounds_check.jl +++ b/src/callbacks_stage/subcell_bounds_check.jl @@ -8,10 +8,10 @@ """ BoundsCheckCallback(; output_directory="out", save_errors=false, interval=1) -Subcell limiting techniques with [`SubcellLimiterIDP`](@ref) are constructed to adhere certain -local or global bounds. To make sure that these bounds are actually met, this callback calculates -the maximum deviation from the bounds. The maximum deviation per applied bound is printed to -the screen at the end of the simulation. +Subcell limiting techniques with [`SubcellLimiterIDP`](@ref) and [`SubcellLimiterMCL`](@ref) are +constructed to adhere certain local or global bounds. To make sure that these bounds are actually +met, this callback calculates the maximum deviation from the bounds. The maximum deviation per +applied bound is printed to the screen at the end of the simulation. For more insights, when setting `save_errors=true` the occurring errors are exported every `interval` time steps during the simulation. Then, the maximum deviations since the last export are saved in "`output_directory`/deviations.txt". @@ -77,17 +77,35 @@ function init_callback(callback::BoundsCheckCallback, semi, limiter::SubcellLimi return nothing end - (; positivity) = limiter + (; local_minmax, positivity, spec_entropy, math_entropy) = limiter (; output_directory) = callback variables = varnames(cons2cons, semi.equations) mkpath(output_directory) open("$output_directory/deviations.txt", "a") do f print(f, "# iter, simu_time") + if local_minmax + for v in limiter.local_minmax_variables_cons + variable_string = string(variables[v]) + print(f, ", " * variable_string * "_min, " * variable_string * "_max") + end + end + if spec_entropy + print(f, ", specEntr_min") + end + if math_entropy + print(f, ", mathEntr_max") + end if positivity for v in limiter.positivity_variables_cons + if v in limiter.local_minmax_variables_cons + continue + end print(f, ", " * string(variables[v]) * "_min") end + for variable in limiter.positivity_variables_nonlinear + print(f, ", " * string(variable) * "_min") + end end println(f) end @@ -95,6 +113,26 @@ function init_callback(callback::BoundsCheckCallback, semi, limiter::SubcellLimi return nothing end +function init_callback(callback::BoundsCheckCallback, semi, limiter::SubcellLimiterMCL) + if !callback.save_errors || (callback.interval == 0) + return nothing + end + + @unpack output_directory = callback + mkpath(output_directory) + open("$output_directory/deviations.txt", "a") do f + print(f, "# iter, simu_time", + join(", $(v)_min, $(v)_max" for v in varnames(cons2cons, semi.equations))) + if limiter.PressurePositivityLimiterKuzmin + print(f, ", pressure_min") + end + # TODO: Bounds check for entropy limiting + println(f) + end + + return nothing +end + function finalize_callback(callback::BoundsCheckCallback, semi) finalize_callback(callback, semi, semi.solver.volume_integral) end @@ -108,18 +146,67 @@ end @inline function finalize_callback(callback::BoundsCheckCallback, semi, limiter::SubcellLimiterIDP) - (; positivity) = limiter + (; local_minmax, positivity, spec_entropy, math_entropy) = limiter (; idp_bounds_delta) = limiter.cache variables = varnames(cons2cons, semi.equations) println("─"^100) println("Maximum deviation from bounds:") println("─"^100) + if local_minmax + for v in limiter.local_minmax_variables_cons + v_string = string(v) + println("$(variables[v]):") + println("-lower bound: ", idp_bounds_delta[Symbol(v_string, "_min")][2]) + println("-upper bound: ", idp_bounds_delta[Symbol(v_string, "_max")][2]) + end + end + if spec_entropy + println("spec. entropy:\n- lower bound: ", + idp_bounds_delta[:spec_entropy_min][2]) + end + if math_entropy + println("math. entropy:\n- upper bound: ", + idp_bounds_delta[:math_entropy_max][2]) + end if positivity for v in limiter.positivity_variables_cons + if v in limiter.local_minmax_variables_cons + continue + end println(string(variables[v]) * ":\n- positivity: ", idp_bounds_delta[Symbol(string(v), "_min")][2]) end + for variable in limiter.positivity_variables_nonlinear + variable_string = string(variable) + println(variable_string * ":\n- positivity: ", + idp_bounds_delta[Symbol(variable_string, "_min")][2]) + end + end + println("─"^100 * "\n") + + return nothing +end + +@inline function finalize_callback(callback::BoundsCheckCallback, semi, + limiter::SubcellLimiterMCL) + @unpack mcl_bounds_delta = limiter.cache + + println("─"^100) + println("Maximum deviation from bounds:") + println("─"^100) + variables = varnames(cons2cons, semi.equations) + for v in eachvariable(semi.equations) + println(variables[v], ":\n- lower bound: ", mcl_bounds_delta[2, 1, v], + "\n- upper bound: ", mcl_bounds_delta[2, 2, v]) + end + if limiter.PressurePositivityLimiterKuzmin + println("pressure:\n- positivity: ", + mcl_bounds_delta[2, 1, nvariables(semi.equations) + 1]) + end + if limiter.SemiDiscEntropyLimiter + # TODO: Bounds check for entropy limiting + println("\nWARNING: No bounds check for the entropy limiter.") end println("─"^100 * "\n") diff --git a/src/callbacks_stage/subcell_bounds_check_2d.jl b/src/callbacks_stage/subcell_bounds_check_2d.jl index 8159becb503..cf47c0fc534 100644 --- a/src/callbacks_stage/subcell_bounds_check_2d.jl +++ b/src/callbacks_stage/subcell_bounds_check_2d.jl @@ -8,12 +8,59 @@ @inline function check_bounds(u, mesh::AbstractMesh{2}, equations, solver, cache, limiter::SubcellLimiterIDP, time, iter, output_directory, save_errors) - (; positivity) = solver.volume_integral.limiter + (; local_minmax, positivity, spec_entropy, math_entropy) = solver.volume_integral.limiter (; variable_bounds) = limiter.cache.subcell_limiter_coefficients (; idp_bounds_delta) = limiter.cache + if local_minmax + for v in limiter.local_minmax_variables_cons + v_string = string(v) + key_min = Symbol(v_string, "_min") + key_max = Symbol(v_string, "_max") + deviation_min = idp_bounds_delta[key_min] + deviation_max = idp_bounds_delta[key_max] + for element in eachelement(solver, cache), j in eachnode(solver), + i in eachnode(solver) + + var = u[v, i, j, element] + deviation_min[1] = max(deviation_min[1], + variable_bounds[key_min][i, j, element] - var) + deviation_max[1] = max(deviation_max[1], + var - variable_bounds[key_max][i, j, element]) + end + deviation_min[2] = max(deviation_min[2], deviation_min[1]) + deviation_max[2] = max(deviation_max[2], deviation_max[1]) + end + end + if spec_entropy + key = :spec_entropy_min + deviation = idp_bounds_delta[key] + for element in eachelement(solver, cache), j in eachnode(solver), + i in eachnode(solver) + + s = entropy_spec(get_node_vars(u, equations, solver, i, j, element), + equations) + deviation[1] = max(deviation[1], variable_bounds[key][i, j, element] - s) + end + deviation[2] = max(deviation[2], deviation[1]) + end + if math_entropy + key = :math_entropy_max + deviation = idp_bounds_delta[key] + for element in eachelement(solver, cache), j in eachnode(solver), + i in eachnode(solver) + + s = entropy_math(get_node_vars(u, equations, solver, i, j, element), + equations) + deviation[1] = max(deviation[1], s - variable_bounds[key][i, j, element]) + end + deviation[2] = max(deviation[2], deviation[1]) + end if positivity for v in limiter.positivity_variables_cons + if v in limiter.local_minmax_variables_cons + continue + end key = Symbol(string(v), "_min") deviation = idp_bounds_delta[key] for element in eachelement(solver, cache), j in eachnode(solver), @@ -25,15 +72,47 @@ end deviation[2] = max(deviation[2], deviation[1]) end + for variable in limiter.positivity_variables_nonlinear + key = Symbol(string(variable), "_min") + deviation = idp_bounds_delta[key] + for element in eachelement(solver, cache), j in eachnode(solver), + i in eachnode(solver) + + var = variable(get_node_vars(u, equations, solver, i, j, element), + equations) + deviation[1] = max(deviation[1], + variable_bounds[key][i, j, element] - var) + end + deviation[2] = max(deviation[2], deviation[1]) + end end if save_errors # Print to output file open("$output_directory/deviations.txt", "a") do f print(f, iter, ", ", time) + if local_minmax + for v in limiter.local_minmax_variables_cons + v_string = string(v) + print(f, ", ", idp_bounds_delta[Symbol(v_string, "_min")][1], + idp_bounds_delta[Symbol(v_string, "_max")][1]) + end + end + if spec_entropy + print(f, ", ", idp_bounds_delta[:spec_entropy_min][1]) + end + if math_entropy + print(f, ", ", idp_bounds_delta[:math_entropy_max][1]) + end if positivity for v in limiter.positivity_variables_cons - key = Symbol(string(v), "_min") - print(f, ", ", idp_bounds_delta[key][1]) + if v in limiter.local_minmax_variables_cons + continue + end + print(f, ", ", idp_bounds_delta[Symbol(string(v), "_min")][1]) + end + for variable in limiter.positivity_variables_nonlinear + print(f, ", ", + idp_bounds_delta[Symbol(string(variable), "_min")][1]) end end println(f) @@ -46,4 +125,509 @@ return nothing end + +@inline function check_bounds(u, mesh::AbstractMesh{2}, equations, solver, cache, + limiter::SubcellLimiterMCL, + time, iter, output_directory, save_errors) + (; var_min, var_max) = limiter.cache.subcell_limiter_coefficients + (; bar_states1, bar_states2, lambda1, lambda2) = limiter.cache.container_bar_states + (; mcl_bounds_delta) = limiter.cache + (; antidiffusive_flux1_L, antidiffusive_flux2_L) = cache.antidiffusive_fluxes + + n_vars = nvariables(equations) + + if limiter.DensityLimiter + # New solution u^{n+1} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], + var_min[1, i, j, element] - + u[1, i, j, element]) + mcl_bounds_delta[1, 2, 1] = max(mcl_bounds_delta[1, 2, 1], + u[1, i, j, element] - + var_max[1, i, j, element]) + end + end + + # Limited bar states \bar{u}^{Lim} = \bar{u} + Δf^{Lim} / λ + # Checking the bounds for... + # - density (rho): + # \bar{rho}^{min} <= \bar{rho}^{Lim} <= \bar{rho}^{max} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + # -x + rho_limited = bar_states1[1, i, j, element] - + antidiffusive_flux1_L[1, i, j, element] / + lambda1[i, j, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], + var_min[1, i, j, element] - rho_limited) + mcl_bounds_delta[1, 2, 1] = max(mcl_bounds_delta[1, 2, 1], + rho_limited - var_max[1, i, j, element]) + # +x + rho_limited = bar_states1[1, i + 1, j, element] + + antidiffusive_flux1_L[1, i + 1, j, element] / + lambda1[i + 1, j, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], + var_min[1, i, j, element] - rho_limited) + mcl_bounds_delta[1, 2, 1] = max(mcl_bounds_delta[1, 2, 1], + rho_limited - var_max[1, i, j, element]) + # -y + rho_limited = bar_states2[1, i, j, element] - + antidiffusive_flux2_L[1, i, j, element] / + lambda2[i, j, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], + var_min[1, i, j, element] - rho_limited) + mcl_bounds_delta[1, 2, 1] = max(mcl_bounds_delta[1, 2, 1], + rho_limited - var_max[1, i, j, element]) + # +y + rho_limited = bar_states2[1, i, j + 1, element] + + antidiffusive_flux2_L[1, i, j + 1, element] / + lambda2[i, j + 1, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], + var_min[1, i, j, element] - rho_limited) + mcl_bounds_delta[1, 2, 1] = max(mcl_bounds_delta[1, 2, 1], + rho_limited - var_max[1, i, j, element]) + end + end + end # limiter.DensityLimiter + + if limiter.SequentialLimiter + # New solution u^{n+1} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + for v in 2:n_vars + var_limited = u[v, i, j, element] / u[1, i, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited - + var_max[v, i, j, element]) + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure = 0.5 * + (u[2, i, j, element]^2 + u[3, i, j, element]^2) - + u[1, i, j, element] * u[4, i, j, element] + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + end + end + end + + # Limited bar states \bar{u}^{Lim} = \bar{u} + Δf^{Lim} / λ + # Checking the bounds for... + # - velocities and energy (phi): + # \bar{phi}^{min} <= \bar{phi}^{Lim} / \bar{rho}^{Lim} <= \bar{phi}^{max} + # - pressure (p): + # \bar{rho}^{Lim} \bar{rho * E}^{Lim} >= |\bar{rho * v}^{Lim}|^2 / 2 + var_limited = zero(eltype(mcl_bounds_delta)) + error_pressure = zero(eltype(mcl_bounds_delta)) + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + # -x + rho_limited = bar_states1[1, i, j, element] - + antidiffusive_flux1_L[1, i, j, element] / + lambda1[i, j, element] + for v in 2:n_vars + var_limited = bar_states1[v, i, j, element] - + antidiffusive_flux1_L[v, i, j, element] / + lambda1[i, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited / rho_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited / rho_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + # +x + rho_limited = bar_states1[1, i + 1, j, element] + + antidiffusive_flux1_L[1, i + 1, j, element] / + lambda1[i + 1, j, element] + for v in 2:n_vars + var_limited = bar_states1[v, i + 1, j, element] + + antidiffusive_flux1_L[v, i + 1, j, element] / + lambda1[i + 1, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited / rho_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited / rho_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + # -y + rho_limited = bar_states2[1, i, j, element] - + antidiffusive_flux2_L[1, i, j, element] / + lambda2[i, j, element] + for v in 2:n_vars + var_limited = bar_states2[v, i, j, element] - + antidiffusive_flux2_L[v, i, j, element] / + lambda2[i, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited / rho_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited / rho_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + # +y + rho_limited = bar_states2[1, i, j + 1, element] + + antidiffusive_flux2_L[1, i, j + 1, element] / + lambda2[i, j + 1, element] + for v in 2:n_vars + var_limited = bar_states2[v, i, j + 1, element] + + antidiffusive_flux2_L[v, i, j + 1, element] / + lambda2[i, j + 1, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited / rho_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited / rho_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + end + end + elseif limiter.ConservativeLimiter + # New solution u^{n+1} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + for v in 2:n_vars + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + u[v, i, j, element]) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + u[v, i, j, element] - + var_max[v, i, j, element]) + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure = 0.5 * + (u[2, i, j, element]^2 + u[3, i, j, element]^2) - + u[1, i, j, element] * u[4, i, j, element] + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + end + end + end + + # Limited bar states \bar{u}^{Lim} = \bar{u} + Δf^{Lim} / λ + # Checking the bounds for... + # - conservative variables (phi): + # \bar{rho*phi}^{min} <= \bar{rho*phi}^{Lim} <= \bar{rho*phi}^{max} + # - pressure (p): + # \bar{rho}^{Lim} \bar{rho * E}^{Lim} >= |\bar{rho * v}^{Lim}|^2 / 2 + var_limited = zero(eltype(mcl_bounds_delta)) + error_pressure = zero(eltype(mcl_bounds_delta)) + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + # -x + rho_limited = bar_states1[1, i, j, element] - + antidiffusive_flux1_L[1, i, j, element] / + lambda1[i, j, element] + for v in 2:n_vars + var_limited = bar_states1[v, i, j, element] - + antidiffusive_flux1_L[v, i, j, element] / + lambda1[i, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + # +x + rho_limited = bar_states1[1, i + 1, j, element] + + antidiffusive_flux1_L[1, i + 1, j, element] / + lambda1[i + 1, j, element] + for v in 2:n_vars + var_limited = bar_states1[v, i + 1, j, element] + + antidiffusive_flux1_L[v, i + 1, j, element] / + lambda1[i + 1, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + # -y + rho_limited = bar_states2[1, i, j, element] - + antidiffusive_flux2_L[1, i, j, element] / + lambda2[i, j, element] + for v in 2:n_vars + var_limited = bar_states2[v, i, j, element] - + antidiffusive_flux2_L[v, i, j, element] / + lambda2[i, j, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + # +y + rho_limited = bar_states2[1, i, j + 1, element] + + antidiffusive_flux2_L[1, i, j + 1, element] / + lambda2[i, j + 1, element] + for v in 2:n_vars + var_limited = bar_states2[v, i, j + 1, element] + + antidiffusive_flux2_L[v, i, j + 1, element] / + lambda2[i, j + 1, element] + mcl_bounds_delta[1, 1, v] = max(mcl_bounds_delta[1, 1, v], + var_min[v, i, j, element] - + var_limited) + mcl_bounds_delta[1, 2, v] = max(mcl_bounds_delta[1, 2, v], + var_limited - + var_max[v, i, j, element]) + if limiter.PressurePositivityLimiterKuzmin && (v == 2 || v == 3) + error_pressure += 0.5 * var_limited^2 + end + end + if limiter.PressurePositivityLimiterKuzmin + error_pressure -= var_limited * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + error_pressure = zero(eltype(mcl_bounds_delta)) + end + end + end + elseif limiter.PressurePositivityLimiterKuzmin + # New solution u^{n+1} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + error_pressure = 0.5 * (u[2, i, j, element]^2 + u[3, i, j, element]^2) - + u[1, i, j, element] * u[4, i, j, element] + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + end + end + + # Limited bar states \bar{u}^{Lim} = \bar{u} + Δf^{Lim} / λ + # Checking the bounds for... + # - pressure (p): + # \bar{rho}^{Lim} \bar{rho * E}^{Lim} >= |\bar{rho * v}^{Lim}|^2 / 2 + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + # -x + rho_limited = bar_states1[1, i, j, element] - + antidiffusive_flux1_L[1, i, j, element] / + lambda1[i, j, element] + error_pressure = 0.5 * + (bar_states1[2, i, j, element] - + antidiffusive_flux1_L[2, i, j, element] / + lambda1[i, j, element])^2 + + 0.5 * + (bar_states1[3, i, j, element] - + antidiffusive_flux1_L[3, i, j, element] / + lambda1[i, j, element])^2 - + (bar_states1[4, i, j, element] - + antidiffusive_flux1_L[4, i, j, element] / + lambda1[i, j, element]) * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + # +x + rho_limited = bar_states1[1, i + 1, j, element] + + antidiffusive_flux1_L[1, i + 1, j, element] / + lambda1[i + 1, j, element] + error_pressure = 0.5 * + (bar_states1[2, i + 1, j, element] + + antidiffusive_flux1_L[2, i + 1, j, element] / + lambda1[i + 1, j, element])^2 + + 0.5 * + (bar_states1[3, i + 1, j, element] + + antidiffusive_flux1_L[3, i + 1, j, element] / + lambda1[i + 1, j, element])^2 - + (bar_states1[4, i + 1, j, element] + + antidiffusive_flux1_L[4, i + 1, j, element] / + lambda1[i + 1, j, element]) * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + # -y + rho_limited = bar_states2[1, i, j, element] - + antidiffusive_flux2_L[1, i, j, element] / + lambda2[i, j, element] + error_pressure = 0.5 * + (bar_states2[2, i, j, element] - + antidiffusive_flux2_L[2, i, j, element] / + lambda2[i, j, element])^2 + + 0.5 * + (bar_states2[3, i, j, element] - + antidiffusive_flux2_L[3, i, j, element] / + lambda2[i, j, element])^2 - + (bar_states2[4, i, j, element] - + antidiffusive_flux2_L[4, i, j, element] / + lambda2[i, j, element]) * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + # +y + rho_limited = bar_states2[1, i, j + 1, element] + + antidiffusive_flux2_L[1, i, j + 1, element] / + lambda2[i, j + 1, element] + error_pressure = 0.5 * + (bar_states2[2, i, j + 1, element] + + antidiffusive_flux2_L[2, i, j + 1, element] / + lambda2[i, j + 1, element])^2 + + 0.5 * + (bar_states2[3, i, j + 1, element] + + antidiffusive_flux2_L[3, i, j + 1, element] / + lambda2[i, j + 1, element])^2 - + (bar_states2[4, i, j + 1, element] + + antidiffusive_flux2_L[4, i, j + 1, element] / + lambda2[i, j + 1, element]) * rho_limited + mcl_bounds_delta[1, 1, n_vars + 1] = max(mcl_bounds_delta[1, 1, + n_vars + 1], + error_pressure) + end + end + end # limiter.PressurePositivityLimiterKuzmin + + if limiter.DensityPositivityLimiter + # New solution u^{n+1} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], + -u[1, i, j, element]) + end + end + + # Limited bar states \bar{u}^{Lim} = \bar{u} + Δf^{Lim} / λ + beta = limiter.DensityPositivityCorrectionFactor + # Checking the bounds for... + # - density (rho): + # beta * \bar{rho} <= \bar{rho}^{Lim} + for element in eachelement(solver, cache) + for j in eachnode(solver), i in eachnode(solver) + # -x + rho_limited = (1 - beta) * bar_states1[1, i, j, element] - + antidiffusive_flux1_L[1, i, j, element] / + lambda1[i, j, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], -rho_limited) + # +x + rho_limited = (1 - beta) * bar_states1[1, i + 1, j, element] + + antidiffusive_flux1_L[1, i + 1, j, element] / + lambda1[i + 1, j, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], -rho_limited) + # -y + rho_limited = (1 - beta) * bar_states2[1, i, j, element] - + antidiffusive_flux2_L[1, i, j, element] / + lambda2[i, j, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], -rho_limited) + # +y + rho_limited = (1 - beta) * bar_states2[1, i, j + 1, element] + + antidiffusive_flux2_L[1, i, j + 1, element] / + lambda2[i, j + 1, element] + mcl_bounds_delta[1, 1, 1] = max(mcl_bounds_delta[1, 1, 1], -rho_limited) + end + end + end # limiter.DensityPositivityLimiter + + for v in eachvariable(equations) + mcl_bounds_delta[2, 1, v] = max(mcl_bounds_delta[2, 1, v], + mcl_bounds_delta[1, 1, v]) + mcl_bounds_delta[2, 2, v] = max(mcl_bounds_delta[2, 2, v], + mcl_bounds_delta[1, 2, v]) + end + if limiter.PressurePositivityLimiterKuzmin + mcl_bounds_delta[2, 1, n_vars + 1] = max(mcl_bounds_delta[2, 1, n_vars + 1], + mcl_bounds_delta[1, 1, n_vars + 1]) + end + + if !save_errors + return nothing + end + open("$output_directory/deviations.txt", "a") do f + print(f, iter, ", ", time) + for v in eachvariable(equations) + print(f, ", ", mcl_bounds_delta[1, 1, v], ", ", mcl_bounds_delta[1, 2, v]) + end + if limiter.PressurePositivityLimiterKuzmin + print(f, ", ", mcl_bounds_delta[1, 1, n_vars + 1]) + end + println(f) + end + for v in eachvariable(equations) + mcl_bounds_delta[1, 1, v] = zero(eltype(mcl_bounds_delta)) + mcl_bounds_delta[1, 2, v] = zero(eltype(mcl_bounds_delta)) + end + if limiter.PressurePositivityLimiterKuzmin + mcl_bounds_delta[1, 1, n_vars + 1] = zero(eltype(mcl_bounds_delta)) + end + + return nothing +end end # @muladd diff --git a/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl b/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl index 6f1723e2a98..95a841b4064 100644 --- a/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl +++ b/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl @@ -9,8 +9,14 @@ function perform_idp_correction!(u, dt, mesh::TreeMesh2D, equations, dg, cache) @unpack inverse_weights = dg.basis @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes @unpack alpha1, alpha2 = dg.volume_integral.limiter.cache.subcell_limiter_coefficients + if dg.volume_integral.limiter.smoothness_indicator + elements = cache.element_ids_dgfv + else + elements = eachelement(dg, cache) + end - @threaded for element in eachelement(dg, cache) + # Loop over blended DG-FV elements + @threaded for element in elements # Sign switch as in apply_jacobian! inverse_jacobian = -cache.elements.inverse_jacobian[element] @@ -41,4 +47,47 @@ function perform_idp_correction!(u, dt, mesh::TreeMesh2D, equations, dg, cache) return nothing end + +function perform_idp_correction!(u, dt, mesh::StructuredMesh{2}, equations, dg, cache) + @unpack inverse_weights = dg.basis + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + @unpack alpha1, alpha2 = dg.volume_integral.limiter.cache.subcell_limiter_coefficients + + if dg.volume_integral.limiter.smoothness_indicator + elements = cache.element_ids_dgfv + else + elements = eachelement(dg, cache) + end + + @threaded for element in elements + for j in eachnode(dg), i in eachnode(dg) + # Sign switch as in apply_jacobian! + inverse_jacobian = -cache.elements.inverse_jacobian[i, j, element] + + # Note: antidiffusive_flux1[v, i, xi, element] = antidiffusive_flux2[v, xi, i, element] = 0 for all i in 1:nnodes and xi in {1, nnodes+1} + alpha_flux1 = (1 - alpha1[i, j, element]) * + get_node_vars(antidiffusive_flux1_R, equations, dg, i, j, + element) + alpha_flux1_ip1 = (1 - alpha1[i + 1, j, element]) * + get_node_vars(antidiffusive_flux1_L, equations, dg, i + 1, + j, element) + alpha_flux2 = (1 - alpha2[i, j, element]) * + get_node_vars(antidiffusive_flux2_R, equations, dg, i, j, + element) + alpha_flux2_jp1 = (1 - alpha2[i, j + 1, element]) * + get_node_vars(antidiffusive_flux2_L, equations, dg, i, + j + 1, element) + + for v in eachvariable(equations) + u[v, i, j, element] += dt * inverse_jacobian * + (inverse_weights[i] * + (alpha_flux1_ip1[v] - alpha_flux1[v]) + + inverse_weights[j] * + (alpha_flux2_jp1[v] - alpha_flux2[v])) + end + end + end + + return nothing +end end # @muladd diff --git a/src/callbacks_step/callbacks_step.jl b/src/callbacks_step/callbacks_step.jl index 09d197bf225..37b49767791 100644 --- a/src/callbacks_step/callbacks_step.jl +++ b/src/callbacks_step/callbacks_step.jl @@ -61,6 +61,7 @@ include("averaging.jl") include("amr.jl") include("stepsize.jl") +include("limiting_analysis.jl") include("glm_speed.jl") include("lbm_collision.jl") include("euler_acoustics_coupling.jl") diff --git a/src/callbacks_step/limiting_analysis.jl b/src/callbacks_step/limiting_analysis.jl new file mode 100644 index 00000000000..55e48cbaf83 --- /dev/null +++ b/src/callbacks_step/limiting_analysis.jl @@ -0,0 +1,205 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +""" + LimitingAnalysisCallback(; output_directory="out", interval=1) + +Analyze the subcell blending coefficient of IDP limiting ([`SubcellLimiterIDP`](@ref)) and +monolithic convex limiting (MCL) ([`SubcellLimiterMCL`](@ref)) in the last RK stage of every +`interval` time steps. This contains a volume-weighted average of the node coefficients. For MCL, +the node coefficients are calculated using either the minimum or the mean of the adjacent subcell +interfaces. The results are saved in `alphas.txt` (for IDP limiting), `alpha_min.txt` and +`alphas_mean.txt` (for MCL) in `output_directory`. +""" +struct LimitingAnalysisCallback + output_directory::String + interval::Int +end + +function Base.show(io::IO, ::MIME"text/plain", + cb::DiscreteCallback{<:Any, <:LimitingAnalysisCallback}) + return nothing +end + +function LimitingAnalysisCallback(; output_directory = "out", interval = 1) + condition = (u, t, integrator) -> interval > 0 && + ((integrator.stats.naccept % interval == 0 && + !(integrator.stats.naccept == 0 && integrator.iter > 0)) || + isfinished(integrator)) + + limiting_analysis_callback = LimitingAnalysisCallback(output_directory, interval) + + DiscreteCallback(condition, limiting_analysis_callback, + save_positions = (false, false), + initialize = initialize!) +end + +function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t, + integrator) where {Condition, Affect! <: LimitingAnalysisCallback} + if cb.affect!.interval == 0 + return nothing + end + + initialize!(cb, u_ode, t, integrator, integrator.p.solver.volume_integral) + + return nothing +end + +function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t, integrator, + volume_integral::AbstractVolumeIntegral) where {Condition, + Affect! <: + LimitingAnalysisCallback + } + return nothing +end + +function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t, integrator, + volume_integral::VolumeIntegralSubcellLimiting) where {Condition, + Affect! <: + LimitingAnalysisCallback + } + initialize!(cb, u_ode, t, integrator, volume_integral.limiter, + cb.affect!.output_directory) + + return nothing +end + +function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t, integrator, + limiter::SubcellLimiterIDP, + output_directory) where {Condition, + Affect! <: LimitingAnalysisCallback} + mkpath(output_directory) + open("$output_directory/alphas.txt", "a") do f + println(f, "# iter, simu_time, alpha_max, alpha_avg") + end + + return nothing +end + +function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t, integrator, + limiter::SubcellLimiterMCL, + output_directory) where {Condition, + Affect! <: LimitingAnalysisCallback} + vars = varnames(cons2cons, integrator.p.equations) + + mkpath(output_directory) + for file in ["alphas_min.txt", "alphas_mean.txt"] + open("$output_directory/$file", "a") do f + print(f, "# iter, simu_time", + join(", alpha_min_$v, alpha_avg_$v" for v in vars)) + if limiter.PressurePositivityLimiterKuzmin + print(f, ", alpha_min_pressure, alpha_avg_pressure") + end + if limiter.SemiDiscEntropyLimiter + print(f, ", alpha_min_entropy, alpha_avg_entropy") + end + println(f) + end + end + + return nothing +end + +@inline function (limiting_analysis_callback::LimitingAnalysisCallback)(integrator) + mesh, equations, solver, cache = mesh_equations_solver_cache(integrator.p) + @unpack t = integrator + iter = integrator.stats.naccept + + limiting_analysis_callback(mesh, equations, solver, cache, solver.volume_integral, + t, iter) +end + +@inline function (limiting_analysis_callback::LimitingAnalysisCallback)(mesh, equations, + solver, cache, + volume_integral::AbstractVolumeIntegral, + t, iter) + return nothing +end + +@inline function (limiting_analysis_callback::LimitingAnalysisCallback)(mesh, equations, + solver, cache, + volume_integral::VolumeIntegralSubcellLimiting, + t, iter) + if limiting_analysis_callback.interval == 0 || + (iter % limiting_analysis_callback.interval != 0) + return nothing + end + + @trixi_timeit timer() "limiting_analysis_callback" limiting_analysis_callback(mesh, + equations, + solver, + cache, + volume_integral.limiter, + t, + iter) +end + +@inline function (limiting_analysis_callback::LimitingAnalysisCallback)(mesh, equations, + dg, cache, + limiter::SubcellLimiterIDP, + time, iter) + @unpack output_directory = limiting_analysis_callback + @unpack alpha = limiter.cache.subcell_limiter_coefficients + + alpha_avg = analyze_coefficient_IDP(mesh, equations, dg, cache, limiter) + + open("$output_directory/alphas.txt", "a") do f + println(f, iter, ", ", time, ", ", maximum(alpha), ", ", alpha_avg) + end +end + +@inline function (limiting_analysis_callback::LimitingAnalysisCallback)(mesh, equations, + dg, cache, + limiter::SubcellLimiterMCL, + time, iter) + @unpack output_directory = limiting_analysis_callback + @unpack weights = dg.basis + @unpack alpha, alpha_pressure, alpha_entropy, + alpha_mean, alpha_mean_pressure, alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + + n_vars = nvariables(equations) + + alpha_min_avg, alpha_mean_avg = analyze_coefficient_MCL(mesh, equations, dg, cache, + limiter) + + open("$output_directory/alphas_min.txt", "a") do f + print(f, iter, ", ", time) + for v in eachvariable(equations) + print(f, ", ", minimum(view(alpha, v, ntuple(_ -> :, n_vars - 1)...))) + print(f, ", ", alpha_min_avg[v]) + end + if limiter.PressurePositivityLimiterKuzmin + print(f, ", ", minimum(alpha_pressure), ", ", alpha_min_avg[n_vars + 1]) + end + if limiter.SemiDiscEntropyLimiter + k = n_vars + limiter.PressurePositivityLimiterKuzmin + 1 + print(f, ", ", minimum(alpha_entropy), ", ", alpha_min_avg[k]) + end + println(f) + end + open("$output_directory/alphas_mean.txt", "a") do f + print(f, iter, ", ", time) + for v in eachvariable(equations) + print(f, ", ", minimum(view(alpha_mean, v, ntuple(_ -> :, n_vars - 1)...))) + print(f, ", ", alpha_mean_avg[v]) + end + if limiter.PressurePositivityLimiterKuzmin + print(f, ", ", minimum(alpha_mean_pressure), ", ", + alpha_mean_avg[n_vars + 1]) + end + if limiter.SemiDiscEntropyLimiter + k = n_vars + limiter.PressurePositivityLimiterKuzmin + 1 + print(f, ", ", minimum(alpha_mean_entropy), ", ", alpha_mean_avg[k]) + end + println(f) + end + + return nothing +end +end # @muladd + +include("limiting_analysis_2d.jl") diff --git a/src/callbacks_step/limiting_analysis_2d.jl b/src/callbacks_step/limiting_analysis_2d.jl new file mode 100644 index 00000000000..a6b5c498c3d --- /dev/null +++ b/src/callbacks_step/limiting_analysis_2d.jl @@ -0,0 +1,130 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +function analyze_coefficient_IDP(mesh::TreeMesh2D, equations, dg, cache, limiter) + @unpack weights = dg.basis + @unpack alpha = limiter.cache.subcell_limiter_coefficients + + alpha_avg = zero(eltype(alpha)) + total_volume = zero(eltype(alpha)) + for element in eachelement(dg, cache) + jacobian = inv(cache.elements.inverse_jacobian[element]) + for j in eachnode(dg), i in eachnode(dg) + alpha_avg += jacobian * weights[i] * weights[j] * alpha[i, j, element] + total_volume += jacobian * weights[i] * weights[j] + end + end + + return alpha_avg / total_volume +end + +function analyze_coefficient_IDP(mesh::StructuredMesh{2}, equations, dg, cache, limiter) + @unpack weights = dg.basis + @unpack alpha = limiter.cache.subcell_limiter_coefficients + + alpha_avg = zero(eltype(alpha)) + total_volume = zero(eltype(alpha)) + for element in eachelement(dg, cache) + for j in eachnode(dg), i in eachnode(dg) + jacobian = inv(cache.elements.inverse_jacobian[i, j, element]) + alpha_avg += jacobian * weights[i] * weights[j] * alpha[i, j, element] + total_volume += jacobian * weights[i] * weights[j] + end + end + + return alpha_avg / total_volume +end + +function analyze_coefficient_MCL(mesh::TreeMesh2D, equations, dg, cache, limiter) + @unpack weights = dg.basis + @unpack alpha, alpha_mean, alpha_pressure, + alpha_mean_pressure, alpha_entropy, alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + + n_vars = nvariables(equations) + + alpha_avg = zeros(eltype(alpha), + n_vars + limiter.PressurePositivityLimiterKuzmin + + limiter.SemiDiscEntropyLimiter) + alpha_mean_avg = zeros(eltype(alpha), + n_vars + limiter.PressurePositivityLimiterKuzmin + + limiter.SemiDiscEntropyLimiter) + total_volume = zero(eltype(alpha)) + + for element in eachelement(dg, cache) + jacobian = inv(cache.elements.inverse_jacobian[element]) + for j in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations) + alpha_avg[v] += jacobian * weights[i] * weights[j] * + alpha[v, i, j, element] + alpha_mean_avg[v] += jacobian * weights[i] * weights[j] * + alpha_mean[v, i, j, element] + end + if limiter.PressurePositivityLimiterKuzmin + alpha_avg[n_vars + 1] += jacobian * weights[i] * weights[j] * + alpha_pressure[i, j, element] + alpha_mean_avg[n_vars + 1] += jacobian * weights[i] * weights[j] * + alpha_mean_pressure[i, j, element] + end + if limiter.SemiDiscEntropyLimiter + k = n_vars + limiter.PressurePositivityLimiterKuzmin + 1 + alpha_avg[k] += jacobian * weights[i] * weights[j] * + alpha_entropy[i, j, element] + alpha_mean_avg[k] += jacobian * weights[i] * weights[j] * + alpha_mean_entropy[i, j, element] + end + total_volume += jacobian * weights[i] * weights[j] + end + end + + return alpha_avg ./ total_volume, alpha_mean_avg ./ total_volume +end + +function analyze_coefficient_MCL(mesh::StructuredMesh{2}, equations, dg, cache, + limiter) + @unpack weights = dg.basis + @unpack alpha, alpha_mean, alpha_pressure, + alpha_mean_pressure, alpha_entropy, alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + + n_vars = nvariables(equations) + + alpha_avg = zeros(eltype(alpha), + n_vars + limiter.PressurePositivityLimiterKuzmin + + limiter.SemiDiscEntropyLimiter) + alpha_mean_avg = zeros(eltype(alpha), + n_vars + limiter.PressurePositivityLimiterKuzmin + + limiter.SemiDiscEntropyLimiter) + total_volume = zero(eltype(alpha)) + + for element in eachelement(dg, cache) + for j in eachnode(dg), i in eachnode(dg) + jacobian = inv(cache.elements.inverse_jacobian[i, j, element]) + for v in eachvariable(equations) + alpha_avg[v] += jacobian * weights[i] * weights[j] * + alpha[v, i, j, element] + alpha_mean_avg[v] += jacobian * weights[i] * weights[j] * + alpha_mean[v, i, j, element] + end + if limiter.PressurePositivityLimiterKuzmin + alpha_avg[n_vars + 1] += jacobian * weights[i] * weights[j] * + alpha_pressure[i, j, element] + alpha_mean_avg[n_vars + 1] += jacobian * weights[i] * weights[j] * + alpha_mean_pressure[i, j, element] + end + if limiter.SemiDiscEntropyLimiter + k = n_vars + limiter.PressurePositivityLimiterKuzmin + 1 + alpha_avg[k] += jacobian * weights[i] * weights[j] * + alpha_entropy[i, j, element] + alpha_mean_avg[k] += jacobian * weights[i] * weights[j] * + alpha_mean_entropy[i, j, element] + end + total_volume += jacobian * weights[i] * weights[j] + end + end + + return alpha_avg ./ total_volume, alpha_mean_avg ./ total_volume +end +end # @muladd diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 8b5cb958318..5c11494ac98 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -87,7 +87,24 @@ function calculate_dt(u_ode, t, cfl_number, semi::AbstractSemidiscretization) dt = cfl_number * max_dt(u, t, mesh, have_constant_speed(equations), equations, - solver, cache) + semi, solver, cache, solver.volume_integral) +end + +function max_dt(u, t, mesh, constant_speed, equations, semi, solver, cache, + volume_integral::AbstractVolumeIntegral) + max_dt(u, t, mesh, constant_speed, equations, solver, cache) +end + +@inline function max_dt(u, t, mesh, + constant_speed, equations, semi, solver, cache, + volume_integral::VolumeIntegralSubcellLimiting) + @unpack limiter = volume_integral + if limiter isa SubcellLimiterIDP && !limiter.bar_states + return max_dt(u, t, mesh, constant_speed, equations, solver, cache) + else + return max_dt(u, t, mesh, constant_speed, equations, semi, solver, cache, + limiter) + end end # Time integration methods from the DiffEq ecosystem without adaptive time stepping on their own diff --git a/src/callbacks_step/stepsize_dg2d.jl b/src/callbacks_step/stepsize_dg2d.jl index 673c3ba6aa6..1bf7402ae78 100644 --- a/src/callbacks_step/stepsize_dg2d.jl +++ b/src/callbacks_step/stepsize_dg2d.jl @@ -43,6 +43,107 @@ function max_dt(u, t, mesh::TreeMesh{2}, return 2 / (nnodes(dg) * max_scaled_speed) end +@inline function max_dt(u, t, mesh::Union{TreeMesh, StructuredMesh}, + constant_speed::False, equations, semi, dg::DG, cache, + limiter::Union{SubcellLimiterIDP, SubcellLimiterMCL}) + @unpack inverse_weights = dg.basis + @trixi_timeit timer() "calc_lambda!" calc_lambdas_bar_states!(u, t, mesh, + have_nonconservative_terms(equations), + equations, + limiter, dg, cache, + semi.boundary_conditions; + calc_bar_states = false) + @unpack lambda1, lambda2 = limiter.cache.container_bar_states + + maxdt = typemax(eltype(u)) + if limiter.smoothness_indicator + @unpack element_ids_dg, element_ids_dgfv = cache + alpha_element = @trixi_timeit timer() "element-wise blending factors" limiter.IndicatorHG(u, + mesh, + equations, + dg, + cache) + pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha_element, + dg, cache) + else + element_ids_dgfv = eachelement(dg, cache) + end + + for idx_element in eachindex(element_ids_dgfv) + element = element_ids_dgfv[idx_element] + if mesh isa TreeMesh + J = 1 / cache.elements.inverse_jacobian[element] + end + for j in eachnode(dg), i in eachnode(dg) + if mesh isa StructuredMesh{2} + J = 1 / cache.elements.inverse_jacobian[i, j, element] + end + denom = inverse_weights[i] * + (lambda1[i, j, element] + lambda1[i + 1, j, element]) + + inverse_weights[j] * + (lambda2[i, j, element] + lambda2[i, j + 1, element]) + maxdt = min(maxdt, J / denom) + end + end + + if limiter.smoothness_indicator && !isempty(element_ids_dg) + maxdt = min(maxdt, + max_dt_RK(u, t, mesh, constant_speed, equations, dg, cache, + limiter, element_ids_dg)) + end + + return maxdt +end + +@inline function max_dt_RK(u, t, mesh::TreeMesh2D, constant_speed, equations, dg::DG, + cache, limiter, element_ids_dg) + max_scaled_speed = nextfloat(zero(t)) + for idx_element in eachindex(element_ids_dg) + element = element_ids_dg[idx_element] + max_λ1 = max_λ2 = zero(max_scaled_speed) + for j in eachnode(dg), i in eachnode(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + λ1, λ2 = max_abs_speeds(u_node, equations) + max_λ1 = max(max_λ1, λ1) + max_λ2 = max(max_λ2, λ2) + end + inv_jacobian = cache.elements.inverse_jacobian[element] + max_scaled_speed = max(max_scaled_speed, inv_jacobian * (max_λ1 + max_λ2)) + end + + return 2 / (nnodes(dg) * max_scaled_speed) +end + +@inline function max_dt_RK(u, t, mesh::StructuredMesh{2}, constant_speed, equations, + dg::DG, cache, limiter, element_ids_dg) + @unpack contravariant_vectors, inverse_jacobian = cache.elements + + max_scaled_speed = nextfloat(zero(t)) + for element in element_ids_dg + max_λ1 = max_λ2 = zero(max_scaled_speed) + for j in eachnode(dg), i in eachnode(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + λ1, λ2 = max_abs_speeds(u_node, equations) + + # Local speeds transformed to the reference element + Ja11, Ja12 = get_contravariant_vector(1, contravariant_vectors, i, j, + element) + λ1_transformed = abs(Ja11 * λ1 + Ja12 * λ2) + Ja21, Ja22 = get_contravariant_vector(2, contravariant_vectors, i, j, + element) + λ2_transformed = abs(Ja21 * λ1 + Ja22 * λ2) + + inv_jacobian = abs(inverse_jacobian[i, j, element]) + + max_λ1 = max(max_λ1, λ1_transformed * inv_jacobian) + max_λ2 = max(max_λ2, λ2_transformed * inv_jacobian) + end + max_scaled_speed = max(max_scaled_speed, max_λ1 + max_λ2) + end + + return 2 / (nnodes(dg) * max_scaled_speed) +end + function max_dt(u, t, mesh::ParallelTreeMesh{2}, constant_speed::False, equations, dg::DG, cache) # call the method accepting a general `mesh::TreeMesh{2}` diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl index 6c8f2e1e848..00e1a12063d 100644 --- a/src/equations/compressible_euler_2d.jl +++ b/src/equations/compressible_euler_2d.jl @@ -149,6 +149,25 @@ function initial_condition_density_wave(x, t, equations::CompressibleEulerEquati return SVector(rho, rho_v1, rho_v2, rho_e) end +""" + initial_condition_density_wave_highdensity(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. +High density version of [`initial_condition_density_wave`](@ref). +""" +function initial_condition_density_wave_highdensity(x, t, + equations::CompressibleEulerEquations2D) + v1 = 0.1 + v2 = 0.2 + rho = 2.0 + 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) @@ -375,6 +394,174 @@ Should be used together with [`StructuredMesh`](@ref). return boundary_flux end +@inline function characteristic_boundary_value_function(outer_boundary_value_function, + u_inner, orientation::Integer, + direction, x, t, equations) + # Get inverse of density + srho = 1 / u_inner[1] + + # Get normal velocity + if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary + factor = 1 + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + factor = -1 + end + if orientation == 1 + vn = factor * u_inner[2] * srho + else + vn = factor * u_inner[3] * srho + end + + # get pressure and Mach from state + p = pressure(u_inner, equations) + a = sqrt(equations.gamma * p * srho) + normalMachNo = abs(vn / a) + + if vn < 0 # inflow + if normalMachNo < 1.0 + # subsonic inflow: All variables from outside but pressure + cons = outer_boundary_value_function(x, t, equations) + + prim = cons2prim(cons, equations) + prim = SVector(view(prim, 1:3)..., p) + cons = prim2cons(prim, equations) + else + # supersonic inflow: All variables from outside + cons = outer_boundary_value_function(x, t, equations) + end + else # outflow + if normalMachNo < 1.0 + # subsonic outflow: All variables from inside but pressure + cons = outer_boundary_value_function(x, t, equations) + + prim = cons2prim(u_inner, equations) + prim = SVector(view(prim, 1:3)..., pressure(cons, equations)) + cons = prim2cons(prim, equations) + else + # supersonic outflow: All variables from inside + cons = u_inner + end + end + + return cons +end + +@inline function characteristic_boundary_value_function(outer_boundary_value_function, + u_inner, + normal_direction::AbstractVector, + direction, x, t, equations) + # Get inverse of density + srho = 1 / u_inner[1] + + # Get normal velocity + if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary + factor = 1 + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + factor = -1 + end + vn = factor * + (normal_direction[1] * u_inner[2] + normal_direction[2] * u_inner[3]) / + norm(normal_direction) + + # get pressure and Mach from state + p = pressure(u_inner, equations) + a = sqrt(equations.gamma * p * srho) + normalMachNo = abs(vn / a) + + if vn < 0 # inflow + if normalMachNo < 1.0 + # subsonic inflow: All variables from outside but pressure + cons = outer_boundary_value_function(x, t, equations) + + prim = cons2prim(cons, equations) + prim = SVector(view(prim, 1:3)..., p) + cons = prim2cons(prim, equations) + else + # supersonic inflow: All variables from outside + cons = outer_boundary_value_function(x, t, equations) + end + else # outflow + if normalMachNo < 1.0 + # subsonic outflow: All variables from inside but pressure + cons = outer_boundary_value_function(x, t, equations) + + prim = cons2prim(u_inner, equations) + prim = SVector(view(prim, 1:3)..., pressure(cons, equations)) + cons = prim2cons(prim, equations) + else + # supersonic outflow: All variables from inside + cons = u_inner + end + end + + return cons +end + +""" + initial_condition_double_mach_reflection(x, t, equations::CompressibleEulerEquations2D) + +Compressible Euler setup for a double Mach reflection problem. +Involves strong shock interactions as well as steady / unsteady flow structures. +Also exercises special boundary conditions along the bottom of the domain that is a mixture of +Dirichlet and slip wall. +See Section IV c on the paper below for details. + +- Paul Woodward and Phillip Colella (1984) + The Numerical Simulation of Two-Dimensional Fluid Flows with Strong Shocks. + [DOI: 10.1016/0021-9991(84)90142-6](https://doi.org/10.1016/0021-9991(84)90142-6) +""" +@inline function initial_condition_double_mach_reflection(x, t, + equations::CompressibleEulerEquations2D) + if x[1] < 1 / 6 + (x[2] + 20 * t) / sqrt(3) + phi = pi / 6 + sin_phi, cos_phi = sincos(phi) + + rho = 8.0 + v1 = 8.25 * cos_phi + v2 = -8.25 * sin_phi + p = 116.5 + else + rho = 1.4 + v1 = 0.0 + v2 = 0.0 + p = 1.0 + end + + prim = SVector(rho, v1, v2, p) + return prim2cons(prim, equations) +end + +# Special mixed boundary condition type for the :Bottom of the domain. +# It is charachteristic when x < 1/6 and a slip wall when x >= 1/6 +@inline function boundary_condition_mixed_dirichlet_wall(u_inner, + normal_direction::AbstractVector, + direction, + x, t, surface_flux_function, + equations::CompressibleEulerEquations2D) + # Note: Only for StructuredMesh + if x[1] < 1 / 6 + # # From the BoundaryConditionDirichlet + # # get the external value of the solution + # u_boundary = initial_condition_double_mach_reflection(x, t, equations) + + # From the BoundaryConditionCharacteristic + # get the external state of the solution + u_boundary = Trixi.characteristic_boundary_value_function(initial_condition_double_mach_reflection, + u_inner, + normal_direction, + direction, x, t, + equations) + # Calculate boundary flux + flux = surface_flux_function(u_boundary, u_inner, normal_direction, equations) + else # x[1] >= 1 / 6 + # Use the free slip wall BC otherwise + flux = boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, + surface_flux_function, equations) + end + + return 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 @@ -1403,6 +1590,45 @@ end return SVector(w1, w2, w3, w4) end +@inline entropy_math(u, equations, derivative::True) = cons2entropy(u, equations) + +# Transformation from conservative variables u to entropy vector dSdu, S = -rho*s/(gamma-1), s=ln(p)-gamma*ln(rho) +@inline function cons2entropy_spec(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 + inv_rho_gammap1 = (1 / rho)^(equations.gamma + 1.0) + + # The derivative vector for the modified specific entropy of Guermond et al. + w1 = inv_rho_gammap1 * + (0.5 * rho * (equations.gamma + 1.0) * v_square - equations.gamma * rho_e) + w2 = -rho_v1 * inv_rho_gammap1 + w3 = -rho_v2 * inv_rho_gammap1 + w4 = (1 / rho)^equations.gamma + + # The derivative vector for other specific entropy + # sp = 1.0/(gammam1 * (rho_e - 0.5 * rho * v_square) + # w1 = gammam1 * 0.5 * v_square * sp - gamma / rho + # w2 = -gammam1 * v1 * sp + # w3 = -gammam1 * v2 * sp + # w4 = gammam1 * sp + + return SVector(w1, w2, w3, w4) +end +@inline entropy_spec(u, equations, derivative::True) = cons2entropy_spec(u, equations) + +# Transformation from conservative variables u to d(p)/d(u) +@inline function pressure(u, equations::CompressibleEulerEquations2D, derivative::True) + rho, rho_v1, rho_v2, rho_e = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + v_square = v1^2 + v2^2 + + return (equations.gamma - 1.0) * SVector(0.5 * v_square, -v1, -v2, 1.0) +end @inline function entropy2cons(w, equations::CompressibleEulerEquations2D) # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD @@ -1428,6 +1654,14 @@ end return SVector(rho, rho_v1, rho_v2, rho_e) end +@inline function is_valid_state(cons, equations::CompressibleEulerEquations2D) + p = pressure(cons, equations) + if cons[1] <= 0.0 || p <= 0.0 + return false + end + return true +end + # Convert primitive to conservative variables @inline function prim2cons(prim, equations::CompressibleEulerEquations2D) rho, v1, v2, p = prim @@ -1497,6 +1731,19 @@ end return S end +# Calculate specific entropy for conservative variable u +@inline function entropy_spec(u, equations::CompressibleEulerEquations2D) + rho, rho_v1, rho_v2, rho_e = u + + # Modified specific entropy from Guermond et al. (2019) + s = (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) * (1 / rho)^equations.gamma + + # Other specific entropy + # rho_sp = rho/((equations.gamma - 1.0) * (rho_e - 0.5 * rho * v_square)) + # s = log(p) - (equaions.gamma + 1) * log(rho) + return s +end + # Default entropy is the mathematical entropy @inline function entropy(cons, equations::CompressibleEulerEquations2D) entropy_math(cons, equations) diff --git a/src/equations/equations.jl b/src/equations/equations.jl index a74ba22bfd6..c8ec4937512 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -188,6 +188,44 @@ end return flux end +""" + BoundaryConditionCharacteristic(outer_boundary_value_function) + +""" +struct BoundaryConditionCharacteristic{B, C} + outer_boundary_value_function::B + boundary_value_function::C +end + +function BoundaryConditionCharacteristic(outer_boundary_value_function) + BoundaryConditionCharacteristic{typeof(outer_boundary_value_function), + typeof(characteristic_boundary_value_function)}(outer_boundary_value_function, + characteristic_boundary_value_function) +end + +# Dirichlet-type boundary condition for use with TreeMesh or StructuredMesh +@inline function (boundary_condition::BoundaryConditionCharacteristic)(u_inner, + orientation_or_normal, + direction, + x, t, + surface_flux_function, + equations) + u_boundary = characteristic_boundary_value_function(boundary_condition.outer_boundary_value_function, + u_inner, orientation_or_normal, + direction, x, t, equations) + + # Calculate boundary flux + if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation_or_normal, + equations) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation_or_normal, + equations) + end + + return flux +end + # operator types used for dispatch on parabolic boundary fluxes struct Gradient end struct Divergence end diff --git a/src/equations/ideal_glm_mhd_2d.jl b/src/equations/ideal_glm_mhd_2d.jl index e8de0cedde1..c2d28b7a10c 100644 --- a/src/equations/ideal_glm_mhd_2d.jl +++ b/src/equations/ideal_glm_mhd_2d.jl @@ -1012,6 +1012,19 @@ end return SVector(w1, w2, w3, w4, w5, w6, w7, w8, w9) end +# Transformation from conservative variables u to d(p)/d(u) +@inline function pressure(u, equations::IdealGlmMhdEquations2D, derivative::True) + rho, rho_v1, rho_v2, rho_v3, rho_e, B1, B2, B3, psi = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + v3 = rho_v3 / rho + v_square = v1^2 + v2^2 + v3^2 + + return (equations.gamma - 1.0) * + SVector(0.5 * v_square, -v1, -v2, -v3, 1.0, -B1, -B2, -B3, -psi) +end + # Convert entropy variables to conservative variables @inline function entropy2cons(w, equations::IdealGlmMhdEquations2D) w1, w2, w3, w4, w5, w6, w7, w8, w9 = w @@ -1039,6 +1052,14 @@ end return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations) end +@inline function is_valid_state(cons, equations::IdealGlmMhdEquations2D) + p = pressure(cons, equations) + if cons[1] <= 0.0 || p <= 0.0 + return false + end + return true +end + # Convert primitive to conservative variables @inline function prim2cons(prim, equations::IdealGlmMhdEquations2D) rho, v1, v2, v3, p, B1, B2, B3, psi = prim diff --git a/src/equations/ideal_mhd_multiion_2d.jl b/src/equations/ideal_mhd_multiion_2d.jl index 7f1de005979..8ae239220fe 100644 --- a/src/equations/ideal_mhd_multiion_2d.jl +++ b/src/equations/ideal_mhd_multiion_2d.jl @@ -94,6 +94,7 @@ end end have_nonconservative_terms(::IdealMhdMultiIonEquations2D) = True() +n_nonconservative_terms(::IdealMhdMultiIonEquations2D) = 4 function varnames(::typeof(cons2cons), equations::IdealMhdMultiIonEquations2D) cons = ("B1", "B2", "B3") @@ -443,9 +444,10 @@ function electron_pressure_zero(u, equations::IdealMhdMultiIonEquations2D) end """ + flux_nonconservative_ruedaramirez_etal(u_ll, u_rr, orientation::Integer, equations::IdealMhdMultiIonEquations2D) Total entropy-conserving non-conservative two-point "flux"" as described in - Rueda-Ramírez et al. (2023) -The term is composed of three parts +The flux is composed of three terms that can be written as the product of local and symmetric parts * The Powell term: Implemented * The MHD term: Implemented * The "term 3": Implemented @@ -489,10 +491,10 @@ The term is composed of three parts f = zero(MVector{nvariables(equations), eltype(u_ll)}) if orientation == 1 - # Entries of Powell term for induction equation (already in Trixi's non-conservative form) - f[1] = v1_plus_ll * B1_rr - f[2] = v2_plus_ll * B1_rr - f[3] = v3_plus_ll * B1_rr + # Entries of Powell term for induction equation + f[1] = v1_plus_ll * B1_avg + f[2] = v2_plus_ll * B1_avg + f[3] = v3_plus_ll * B1_avg for k in eachcomponent(equations) # Compute term 2 (MHD) @@ -514,29 +516,22 @@ The term is composed of three parts f5 += (B2_ll * (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) + B3_ll * (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg)) - # Adjust non-conservative terms 2 and 3 to Trixi discretization: CHANGE!?! - f2 = 2 * f2 - charge_ratio_ll[k] * (0.5 * mag_norm_ll - B1_ll * B1_ll) - f3 = 2 * f3 + charge_ratio_ll[k] * B1_ll * B2_ll - f4 = 2 * f4 + charge_ratio_ll[k] * B1_ll * B3_ll - f5 = (2 * f5 - B2_ll * (vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) - - - B3_ll * (vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll)) - - # Compute Powell term (already consistent with Trixi's non-conservative discretization) - f2 += charge_ratio_ll[k] * B1_ll * B1_rr - f3 += charge_ratio_ll[k] * B2_ll * B1_rr - f4 += charge_ratio_ll[k] * B3_ll * B1_rr - f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * B1_rr + # Compute Powell term + f2 += charge_ratio_ll[k] * B1_ll * B1_avg + f3 += charge_ratio_ll[k] * B2_ll * B1_avg + f4 += charge_ratio_ll[k] * B3_ll * B1_avg + f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * + B1_avg # Append to the flux vector set_component!(f, k, 0, f2, f3, f4, f5, equations) end else #if orientation == 2 - # Entries of Powell term for induction equation (already in Trixi's non-conservative form) - f[1] = v1_plus_ll * B2_rr - f[2] = v2_plus_ll * B2_rr - f[3] = v3_plus_ll * B2_rr + # Entries of Powell term for induction equation + f[1] = v1_plus_ll * B2_avg + f[2] = v2_plus_ll * B2_avg + f[3] = v3_plus_ll * B2_avg for k in eachcomponent(equations) # Compute term 2 (MHD) @@ -558,25 +553,331 @@ The term is composed of three parts f5 += (B1_ll * (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) + B3_ll * (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg)) - # Adjust non-conservative terms 2 and 3 to Trixi discretization: CHANGE!?! - f2 = 2 * f2 + charge_ratio_ll[k] * B2_ll * B1_ll - f3 = 2 * f3 - charge_ratio_ll[k] * (0.5 * mag_norm_ll - B2_ll * B2_ll) - f4 = 2 * f4 + charge_ratio_ll[k] * B2_ll * B3_ll - f5 = (2 * f5 - B1_ll * (vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) - - - B3_ll * (vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll)) - - # Compute Powell term (already consistent with Trixi's non-conservative discretization) - f2 += charge_ratio_ll[k] * B1_ll * B2_rr - f3 += charge_ratio_ll[k] * B2_ll * B2_rr - f4 += charge_ratio_ll[k] * B3_ll * B2_rr - f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * B2_rr + # Compute Powell term + f2 += charge_ratio_ll[k] * B1_ll * B2_avg + f3 += charge_ratio_ll[k] * B2_ll * B2_avg + f4 += charge_ratio_ll[k] * B3_ll * B2_avg + f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * + B2_avg # Append to the flux vector set_component!(f, k, 0, f2, f3, f4, f5, equations) end end + # We multiply by 2 to compensate the multiplication by 0.5 in the Trixi routines + return SVector(2 * f) +end +""" + flux_nonconservative_ruedaramirez_etal(u_ll, orientation::Integer, + equations::IdealMhdMultiIonEquations2D, + nonconservative_type::NonConservativeLocal, + noncons_term::Integer) +Local part of the non-conservative two-point "flux"" as described in +- Rueda-Ramírez et al. (2023) +The flux is composed of three/four terms that can be written as the product of local and symmetric parts +* The Powell term: Implemented +* The MHD term: Implemented +* The "term 3/4": Implemented +""" +@inline function flux_nonconservative_ruedaramirez_etal(u_ll, orientation::Integer, + equations::IdealMhdMultiIonEquations2D, + nonconservative_type::NonConservativeLocal, + noncons_term::Integer) + @unpack charge_to_mass = equations + # Unpack left and right states to get the magnetic field + B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations) + + # Compute charge ratio of u_ll + charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)}) + total_electron_charge = zero(eltype(u_ll)) + for k in eachcomponent(equations) + rho_k = u_ll[3 + (k - 1) * 5 + 1] + charge_ratio_ll[k] = rho_k * charge_to_mass[k] + total_electron_charge += charge_ratio_ll[k] + end + charge_ratio_ll ./= total_electron_charge + + # Compute auxiliary variables + v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll, + equations) + + f = zero(MVector{nvariables(equations), eltype(u_ll)}) + + if noncons_term == 1 + # Powell term + ############# + if orientation == 1 + # Entries of Powell term for induction equation + f[1] = v1_plus_ll + f[2] = v2_plus_ll + f[3] = v3_plus_ll + + for k in eachcomponent(equations) + # Compute Powell term + f2 = charge_ratio_ll[k] * B1_ll + f3 = charge_ratio_ll[k] * B2_ll + f4 = charge_ratio_ll[k] * B3_ll + f5 = (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) + + # Append to the flux vector + set_component!(f, k, 0, f2, f3, f4, f5, equations) + end + + else #if orientation == 2 + # Entries of Powell term for induction equation + f[1] = v1_plus_ll + f[2] = v2_plus_ll + f[3] = v3_plus_ll + + for k in eachcomponent(equations) + # Compute Powell term + f2 = charge_ratio_ll[k] * B1_ll + f3 = charge_ratio_ll[k] * B2_ll + f4 = charge_ratio_ll[k] * B3_ll + f5 = (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) + + # Append to the flux vector + set_component!(f, k, 0, f2, f3, f4, f5, equations) + end + end + elseif noncons_term == 2 + # Term "2" (MHD) + ################ + if orientation == 1 + for k in eachcomponent(equations) + # Compute term 2 (MHD) + f2 = charge_ratio_ll[k] + f3 = charge_ratio_ll[k] + f4 = charge_ratio_ll[k] + f5 = vk1_plus_ll[k] + + # Append to the flux vector + set_component!(f, k, 0, f2, f3, f4, f5, equations) + end + + else #if orientation == 2 + for k in eachcomponent(equations) + # Compute term 2 (MHD) + f2 = charge_ratio_ll[k] + f3 = charge_ratio_ll[k] + f4 = charge_ratio_ll[k] + f5 = vk2_plus_ll[k] + + # Append to the flux vector + set_component!(f, k, 0, f2, f3, f4, f5, equations) + end + end + elseif noncons_term == 3 + # Term "3" (multi-ion) + ###################### + if orientation == 1 + for k in eachcomponent(equations) + # Compute term 3 (only needed for NCOMP>1) + f5 = B2_ll + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + + else #if orientation == 2 + for k in eachcomponent(equations) + # Compute term 3 (only needed for NCOMP>1) + f5 = B1_ll + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + end + elseif noncons_term == 4 + # Term "4" (multi-ion) + ###################### + if orientation == 1 + for k in eachcomponent(equations) + # Compute term 4 (only needed for NCOMP>1) + f5 = B3_ll + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + + else #if orientation == 2 + for k in eachcomponent(equations) + # Compute term 4 (only needed for NCOMP>1) + f5 = B3_ll + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + end + end + # We multiply by 2 to compensate the multiplication by 0.5 in the Trixi routines + return SVector(2 * f) +end +""" + flux_nonconservative_ruedaramirez_etal(u_ll, u_rr, orientation::Integer, + equations::IdealGlmMhdEquations2D, + nonconservative_type::NonConservativeSymmetric, + noncons_term::Integer) +Symmetric part of non-conservative two-point "flux"" as described in +- Rueda-Ramírez et al. (2023) +The flux is composed of three/four terms that can be written as the product of local and symmetric parts +* The Powell term: Implemented +* The MHD term: Implemented +* The "term 3/4": Implemented +""" +@inline function flux_nonconservative_ruedaramirez_etal(u_ll, u_rr, + orientation::Integer, + equations::IdealMhdMultiIonEquations2D, + nonconservative_type::NonConservativeSymmetric, + noncons_term::Integer) + @unpack charge_to_mass = equations + # Unpack left and right states to get the magnetic field + B1_ll, B2_ll, B3_ll = magnetic_field(u_ll, equations) + B1_rr, B2_rr, B3_rr = magnetic_field(u_rr, equations) + + # Compute important averages + B1_avg = 0.5 * (B1_ll + B1_rr) + B2_avg = 0.5 * (B2_ll + B2_rr) + B3_avg = 0.5 * (B3_ll + B3_rr) + mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2 + mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2 + mag_norm_avg = 0.5 * (mag_norm_ll + mag_norm_rr) + + # Mean electron pressure + pe_mean = 0.5 * (equations.electron_pressure(u_ll, equations) + + equations.electron_pressure(u_rr, equations)) + + # Compute auxiliary variables + v1_plus_ll, v2_plus_ll, v3_plus_ll, vk1_plus_ll, vk2_plus_ll, vk3_plus_ll = charge_averaged_velocities(u_ll, + equations) + v1_plus_rr, v2_plus_rr, v3_plus_rr, vk1_plus_rr, vk2_plus_rr, vk3_plus_rr = charge_averaged_velocities(u_rr, + equations) + + f = zero(MVector{nvariables(equations), eltype(u_ll)}) + + if noncons_term == 1 + # Powell term + ############# + if orientation == 1 + # Entries of Powell term for induction equation + f[1] = B1_avg + f[2] = B1_avg + f[3] = B1_avg + for k in eachcomponent(equations) + # Append to the flux vector + set_component!(f, k, 0, B1_avg, B1_avg, B1_avg, B1_avg, equations) + end + + else #if orientation == 2 + # Entries of Powell term for induction equation + f[1] = B2_avg + f[2] = B2_avg + f[3] = B2_avg + for k in eachcomponent(equations) + # Append to the flux vector + set_component!(f, k, 0, B2_avg, B2_avg, B2_avg, B2_avg, equations) + end + end + elseif noncons_term == 2 + # Term 2 (MHD) + ############## + if orientation == 1 + for k in eachcomponent(equations) + # Compute term 2 (MHD) + f2 = (0.5 * mag_norm_avg - B1_avg * B1_avg + pe_mean) + f3 = (-B1_avg * B2_avg) + f4 = (-B1_avg * B3_avg) + f5 = pe_mean + + # Append to the flux vector + set_component!(f, k, 0, f2, f3, f4, f5, equations) + end + + else #if orientation == 2 + for k in eachcomponent(equations) + # Compute term 2 (MHD) + f2 = (-B2_avg * B1_avg) + f3 = (-B2_avg * B2_avg + 0.5 * mag_norm_avg + pe_mean) + f4 = (-B2_avg * B3_avg) + f5 = pe_mean + + # Append to the flux vector + set_component!(f, k, 0, f2, f3, f4, f5, equations) + end + end + elseif noncons_term == 3 + # Term 3 (multi-ion) + #################### + if orientation == 1 + for k in eachcomponent(equations) + # Compute term 3 (only needed for NCOMP>1) + vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k] + vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k] + vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k] + vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k] + vk1_minus_avg = 0.5 * (vk1_minus_ll + vk1_minus_rr) + vk2_minus_avg = 0.5 * (vk2_minus_ll + vk2_minus_rr) + + f5 = (vk1_minus_avg * B2_avg - vk2_minus_avg * B1_avg) + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + + else #if orientation == 2 + for k in eachcomponent(equations) + # Compute term 3 (only needed for NCOMP>1) + vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k] + vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k] + vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k] + vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k] + vk1_minus_avg = 0.5 * (vk1_minus_ll + vk1_minus_rr) + vk2_minus_avg = 0.5 * (vk2_minus_ll + vk2_minus_rr) + + f5 = (vk2_minus_avg * B1_avg - vk1_minus_avg * B2_avg) + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + end + elseif noncons_term == 4 + # Term 4 (multi-ion) + #################### + if orientation == 1 + for k in eachcomponent(equations) + # Compute term 4 (only needed for NCOMP>1) + vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k] + vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k] + vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k] + vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k] + vk1_minus_avg = 0.5 * (vk1_minus_ll + vk1_minus_rr) + vk3_minus_avg = 0.5 * (vk3_minus_ll + vk3_minus_rr) + + f5 = (vk1_minus_avg * B3_avg - vk3_minus_avg * B1_avg) + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + + else #if orientation == 2 + for k in eachcomponent(equations) + # Compute term 4 (only needed for NCOMP>1) + vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k] + vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k] + vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k] + vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k] + vk2_minus_avg = 0.5 * (vk2_minus_ll + vk2_minus_rr) + vk3_minus_avg = 0.5 * (vk3_minus_ll + vk3_minus_rr) + + f5 = (vk2_minus_avg * B3_avg - vk3_minus_avg * B2_avg) + + # Append to the flux vector + set_component!(f, k, 0, 0, 0, 0, f5, equations) + end + end + end + # We don't multiply by 2 because the local term is already multiplied by 2 return SVector(f) end @@ -596,9 +897,13 @@ The term is composed of three parts # Compute important averages mag_norm_rr = B1_rr^2 + B2_rr^2 + B3_rr^2 + mag_norm_ll = B1_ll^2 + B2_ll^2 + B3_ll^2 + mag_norm_avg = 0.5 * (mag_norm_ll + mag_norm_rr) # Electron pressure pe_rr = equations.electron_pressure(u_rr, equations) + pe_ll = equations.electron_pressure(u_ll, equations) + pe_avg = 0.5 * (pe_ll + pe_rr) # Compute charge ratio of u_ll charge_ratio_ll = zero(MVector{ncomponents(equations), eltype(u_ll)}) @@ -619,69 +924,83 @@ The term is composed of three parts f = zero(MVector{nvariables(equations), eltype(u_ll)}) if orientation == 1 - # Entries of Powell term for induction equation (already in Trixi's non-conservative form) - f[1] = v1_plus_ll * B1_rr - f[2] = v2_plus_ll * B1_rr - f[3] = v3_plus_ll * B1_rr + B1_avg = 0.5 * (B1_ll + B1_rr) + # Entries of Powell term for induction equation + f[1] = v1_plus_ll * B1_avg + f[2] = v2_plus_ll * B1_avg + f[3] = v3_plus_ll * B1_avg for k in eachcomponent(equations) # Compute term 2 (MHD) - f2 = charge_ratio_ll[k] * (0.5 * mag_norm_rr - B1_rr * B1_rr + pe_rr) - f3 = charge_ratio_ll[k] * (-B1_rr * B2_rr) - f4 = charge_ratio_ll[k] * (-B1_rr * B3_rr) - f5 = vk1_plus_ll[k] * pe_rr + f2 = charge_ratio_ll[k] * + (0.5 * mag_norm_avg - 0.5 * B1_ll * B1_ll - 0.5 * B1_rr * B1_rr + + pe_avg) + f3 = charge_ratio_ll[k] * (-0.5 * B1_ll * B2_ll - 0.5 * B1_rr * B2_rr) + f4 = charge_ratio_ll[k] * (-0.5 * B1_ll * B3_ll - 0.5 * B1_rr * B3_rr) + f5 = vk1_plus_ll[k] * pe_avg # Compute term 3 (only needed for NCOMP>1) + vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k] + vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k] + vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k] vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k] vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k] vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k] - f5 += (B2_ll * (vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr) + - B3_ll * (vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr)) - - # Compute Powell term (already consistent with Trixi's non-conservative discretization) - f2 += charge_ratio_ll[k] * B1_ll * B1_rr - f3 += charge_ratio_ll[k] * B2_ll * B1_rr - f4 += charge_ratio_ll[k] * B3_ll * B1_rr - f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * B1_rr - - # It's not needed to adjust to Trixi's non-conservative form + f5 += (B2_ll * (0.5 * (vk1_minus_ll * B2_ll - vk2_minus_ll * B1_ll) + + 0.5 * (vk1_minus_rr * B2_rr - vk2_minus_rr * B1_rr)) + + B3_ll * (0.5 * (vk1_minus_ll * B3_ll - vk3_minus_ll * B1_ll) + + 0.5 * (vk1_minus_rr * B3_rr - vk3_minus_rr * B1_rr))) + + # Compute Powell term + f2 += charge_ratio_ll[k] * B1_ll * B1_avg + f3 += charge_ratio_ll[k] * B2_ll * B1_avg + f4 += charge_ratio_ll[k] * B3_ll * B1_avg + f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * + B1_avg # Append to the flux vector set_component!(f, k, 0, f2, f3, f4, f5, equations) end else #if orientation == 2 - # Entries of Powell term for induction equation (already in Trixi's non-conservative form) - f[1] = v1_plus_ll * B2_rr - f[2] = v2_plus_ll * B2_rr - f[3] = v3_plus_ll * B2_rr + B2_avg = 0.5 * (B2_ll + B2_rr) + # Entries of Powell term for induction equation + f[1] = v1_plus_ll * B2_avg + f[2] = v2_plus_ll * B2_avg + f[3] = v3_plus_ll * B2_avg for k in eachcomponent(equations) # Compute term 2 (MHD) - f2 = charge_ratio_ll[k] * (-B2_rr * B1_rr) - f3 = charge_ratio_ll[k] * (-B2_rr * B2_rr + 0.5 * mag_norm_rr + pe_rr) - f4 = charge_ratio_ll[k] * (-B2_rr * B3_rr) - f5 = vk2_plus_ll[k] * pe_rr + f2 = charge_ratio_ll[k] * (-0.5 * B2_ll * B1_ll - 0.5 * B2_rr * B1_rr) + f3 = charge_ratio_ll[k] * + (-0.5 * B2_ll * B2_ll - 0.5 * B2_rr * B2_rr + 0.5 * mag_norm_avg + + pe_avg) + f4 = charge_ratio_ll[k] * (-0.5 * B2_ll * B3_ll - 0.5 * B2_rr * B3_rr) + f5 = vk2_plus_ll[k] * pe_avg # Compute term 3 (only needed for NCOMP>1) + vk1_minus_ll = v1_plus_ll - vk1_plus_ll[k] + vk2_minus_ll = v2_plus_ll - vk2_plus_ll[k] + vk3_minus_ll = v3_plus_ll - vk3_plus_ll[k] vk1_minus_rr = v1_plus_rr - vk1_plus_rr[k] vk2_minus_rr = v2_plus_rr - vk2_plus_rr[k] vk3_minus_rr = v3_plus_rr - vk3_plus_rr[k] - f5 += (B1_ll * (vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr) + - B3_ll * (vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr)) - - # Compute Powell term (already consistent with Trixi's non-conservative discretization) - f2 += charge_ratio_ll[k] * B1_ll * B2_rr - f3 += charge_ratio_ll[k] * B2_ll * B2_rr - f4 += charge_ratio_ll[k] * B3_ll * B2_rr - f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * B2_rr - - # It's not needed to adjust to Trixi's non-conservative form + f5 += (B1_ll * (0.5 * (vk2_minus_ll * B1_ll - vk1_minus_ll * B2_ll) + + 0.5 * (vk2_minus_rr * B1_rr - vk1_minus_rr * B2_rr)) + + B3_ll * (0.5 * (vk2_minus_ll * B3_ll - vk3_minus_ll * B2_ll) + + 0.5 * (vk2_minus_rr * B3_rr - vk3_minus_rr * B2_rr))) + + # Compute Powell term + f2 += charge_ratio_ll[k] * B1_ll * B2_avg + f3 += charge_ratio_ll[k] * B2_ll * B2_avg + f4 += charge_ratio_ll[k] * B3_ll * B2_avg + f5 += (v1_plus_ll * B1_ll + v2_plus_ll * B2_ll + v3_plus_ll * B3_ll) * + B2_avg # Append to the flux vector set_component!(f, k, 0, f2, f3, f4, f5, equations) end end - return SVector(f) + return SVector(2 * f) end """ @@ -980,6 +1299,19 @@ function cons2prim(u, equations::IdealMhdMultiIonEquations2D) return SVector(prim) end +@inline function is_valid_state(cons, equations::IdealMhdMultiIonEquations2D) + prim = cons2prim(cons, equations) + + for k in eachcomponent(equations) + rho, _, _, _, p = get_component(k, prim, equations) + if rho <= 0.0 || p <= 0.0 + return false + end + end + + return true +end + """ Convert conservative variables to entropy """ diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl index 91ad59b76b6..6e9e41b376e 100644 --- a/src/solvers/dg.jl +++ b/src/solvers/dg.jl @@ -189,7 +189,8 @@ end volume_flux_dg, volume_flux_fv) A subcell limiting volume integral type for DG methods based on subcell blending approaches -with a low-order FV method. Used with limiter [`SubcellLimiterIDP`](@ref). +with a low-order FV method. Used with the limiters [`SubcellLimiterIDP`](@ref) and +[`SubcellLimiterMCL`](@ref). !!! warning "Experimental implementation" This is an experimental feature and may change in future releases. @@ -224,6 +225,17 @@ function Base.show(io::IO, mime::MIME"text/plain", end end +function get_element_variables!(element_variables, u, mesh, equations, + volume_integral::VolumeIntegralSubcellLimiting, dg, + cache) + if volume_integral.limiter.smoothness_indicator + # call the element-wise limiter to get up-to-date values for IO + volume_integral.limiter.IndicatorHG(u, mesh, equations, dg, cache) + get_element_variables!(element_variables, volume_integral.limiter, + volume_integral) + end +end + function get_node_variables!(node_variables, mesh, equations, volume_integral::VolumeIntegralSubcellLimiting, dg, cache) # While for the element-wise limiting with `VolumeIntegralShockCapturingHG` the indicator is diff --git a/src/solvers/dgsem_structured/dg.jl b/src/solvers/dgsem_structured/dg.jl index 5cf4c4ef78c..de4601a2203 100644 --- a/src/solvers/dgsem_structured/dg.jl +++ b/src/solvers/dgsem_structured/dg.jl @@ -80,6 +80,9 @@ include("indicators_1d.jl") include("indicators_2d.jl") include("indicators_3d.jl") +include("subcell_limiters_2d.jl") +include("dg_2d_subcell_limiters.jl") + # Specialized implementations used to improve performance include("dg_2d_compressible_euler.jl") include("dg_3d_compressible_euler.jl") diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index 25a0eea096f..467b92a7fce 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -16,7 +16,8 @@ function rhs!(du, u, t, @trixi_timeit timer() "volume integral" begin calc_volume_integral!(du, u, mesh, have_nonconservative_terms(equations), equations, - dg.volume_integral, dg, cache) + dg.volume_integral, dg, cache, + t, boundary_conditions) end # Calculate interface fluxes diff --git a/src/solvers/dgsem_structured/dg_2d_subcell_limiters.jl b/src/solvers/dgsem_structured/dg_2d_subcell_limiters.jl new file mode 100644 index 00000000000..8d0894f2516 --- /dev/null +++ b/src/solvers/dgsem_structured/dg_2d_subcell_limiters.jl @@ -0,0 +1,340 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +@inline function calcflux_fhat!(fhat1_L, fhat1_R, fhat2_L, fhat2_R, u, + mesh::StructuredMesh{2}, nonconservative_terms::False, + equations, + volume_flux, dg::DGSEM, element, cache) + @unpack contravariant_vectors = cache.elements + @unpack weights, derivative_split = dg.basis + @unpack flux_temp_threaded = cache + + flux_temp = flux_temp_threaded[Threads.threadid()] + + # The FV-form fluxes are calculated in a recursive manner, i.e.: + # fhat_(0,1) = w_0 * FVol_0, + # fhat_(j,j+1) = fhat_(j-1,j) + w_j * FVol_j, for j=1,...,N-1, + # with the split form volume fluxes FVol_j = -2 * sum_i=0^N D_ji f*_(j,i). + + # To use the symmetry of the `volume_flux`, the split form volume flux is precalculated + # like in `calc_volume_integral!` for the `VolumeIntegralFluxDifferencing` + # and saved in in `flux_temp`. + + # Split form volume flux in orientation 1: x direction + flux_temp .= zero(eltype(flux_temp)) + + for j in eachnode(dg), i in eachnode(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + + # pull the contravariant vectors in each coordinate direction + Ja1_node = get_contravariant_vector(1, contravariant_vectors, i, j, element) # x direction + + # All diagonal entries of `derivative_split` are zero. Thus, we can skip + # the computation of the diagonal terms. In addition, we use the symmetry + # of the `volume_flux` to save half of the possible two-point flux + # computations. + + # x direction + for ii in (i + 1):nnodes(dg) + u_node_ii = get_node_vars(u, equations, dg, ii, j, element) + # pull the contravariant vectors and compute the average + Ja1_node_ii = get_contravariant_vector(1, contravariant_vectors, ii, j, + element) + Ja1_avg = 0.5 * (Ja1_node + Ja1_node_ii) + + # compute the contravariant sharp flux in the direction of the averaged contravariant vector + fluxtilde1 = volume_flux(u_node, u_node_ii, Ja1_avg, equations) + multiply_add_to_node_vars!(flux_temp, derivative_split[i, ii], fluxtilde1, + equations, dg, i, j) + multiply_add_to_node_vars!(flux_temp, derivative_split[ii, i], fluxtilde1, + equations, dg, ii, j) + end + end + + # FV-form flux `fhat` in x direction + fhat1_L[:, 1, :] .= zero(eltype(fhat1_L)) + fhat1_L[:, nnodes(dg) + 1, :] .= zero(eltype(fhat1_L)) + fhat1_R[:, 1, :] .= zero(eltype(fhat1_R)) + fhat1_R[:, nnodes(dg) + 1, :] .= zero(eltype(fhat1_R)) + + for j in eachnode(dg), i in 1:(nnodes(dg) - 1), v in eachvariable(equations) + fhat1_L[v, i + 1, j] = fhat1_L[v, i, j] + weights[i] * flux_temp[v, i, j] + fhat1_R[v, i + 1, j] = fhat1_L[v, i + 1, j] + end + + # Split form volume flux in orientation 2: y direction + flux_temp .= zero(eltype(flux_temp)) + + for j in eachnode(dg), i in eachnode(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + + # pull the contravariant vectors in each coordinate direction + Ja2_node = get_contravariant_vector(2, contravariant_vectors, i, j, element) + + # y direction + for jj in (j + 1):nnodes(dg) + u_node_jj = get_node_vars(u, equations, dg, i, jj, element) + # pull the contravariant vectors and compute the average + Ja2_node_jj = get_contravariant_vector(2, contravariant_vectors, i, jj, + element) + Ja2_avg = 0.5 * (Ja2_node + Ja2_node_jj) + # compute the contravariant sharp flux in the direction of the averaged contravariant vector + fluxtilde2 = volume_flux(u_node, u_node_jj, Ja2_avg, equations) + multiply_add_to_node_vars!(flux_temp, derivative_split[j, jj], fluxtilde2, + equations, dg, i, j) + multiply_add_to_node_vars!(flux_temp, derivative_split[jj, j], fluxtilde2, + equations, dg, i, jj) + end + end + + # FV-form flux `fhat` in y direction + fhat2_L[:, :, 1] .= zero(eltype(fhat2_L)) + fhat2_L[:, :, nnodes(dg) + 1] .= zero(eltype(fhat2_L)) + fhat2_R[:, :, 1] .= zero(eltype(fhat2_R)) + fhat2_R[:, :, nnodes(dg) + 1] .= zero(eltype(fhat2_R)) + + for j in 1:(nnodes(dg) - 1), i in eachnode(dg), v in eachvariable(equations) + fhat2_L[v, i, j + 1] = fhat2_L[v, i, j] + weights[j] * flux_temp[v, i, j] + fhat2_R[v, i, j + 1] = fhat2_L[v, i, j + 1] + end + + return nothing +end + +@inline function calc_lambdas_bar_states!(u, t, mesh::StructuredMesh, + nonconservative_terms, equations, limiter, + dg, cache, boundary_conditions; + calc_bar_states = true) + if limiter isa SubcellLimiterIDP && !limiter.bar_states + return nothing + end + @unpack lambda1, lambda2, bar_states1, bar_states2 = limiter.cache.container_bar_states + @unpack contravariant_vectors = cache.elements + + @unpack normal_direction_xi, normal_direction_eta = limiter.cache.container_bar_states + + # Calc lambdas and bar states inside elements + @threaded for element in eachelement(dg, cache) + for j in eachnode(dg), i in 2:nnodes(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + u_node_im1 = get_node_vars(u, equations, dg, i - 1, j, element) + + normal_direction = get_node_coords(normal_direction_xi, equations, dg, + i - 1, j, element) + + lambda1[i, j, element] = max_abs_speed_naive(u_node_im1, u_node, + normal_direction, equations) + + !calc_bar_states && continue + + flux1 = flux(u_node, normal_direction, equations) + flux1_im1 = flux(u_node_im1, normal_direction, equations) + for v in eachvariable(equations) + bar_states1[v, i, j, element] = 0.5 * (u_node[v] + u_node_im1[v]) - + 0.5 * (flux1[v] - flux1_im1[v]) / + lambda1[i, j, element] + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + u_node_jm1 = get_node_vars(u, equations, dg, i, j - 1, element) + + normal_direction = get_node_coords(normal_direction_eta, equations, dg, i, + j - 1, element) + + lambda2[i, j, element] = max_abs_speed_naive(u_node_jm1, u_node, + normal_direction, equations) + + !calc_bar_states && continue + + flux2 = flux(u_node, normal_direction, equations) + flux2_jm1 = flux(u_node_jm1, normal_direction, equations) + for v in eachvariable(equations) + bar_states2[v, i, j, element] = 0.5 * (u_node[v] + u_node_jm1[v]) - + 0.5 * (flux2[v] - flux2_jm1[v]) / + lambda2[i, j, element] + end + end + end + + # Calc lambdas and bar states at interfaces and periodic boundaries + @threaded for element in eachelement(dg, cache) + # Get neighboring element ids + left = cache.elements.left_neighbors[1, element] + lower = cache.elements.left_neighbors[2, element] + + if left != 0 + for i in eachnode(dg) + u_left = get_node_vars(u, equations, dg, nnodes(dg), i, left) + u_element = get_node_vars(u, equations, dg, 1, i, element) + + Ja1 = get_contravariant_vector(1, contravariant_vectors, 1, i, element) + lambda = max_abs_speed_naive(u_left, u_element, Ja1, equations) + + lambda1[nnodes(dg) + 1, i, left] = lambda + lambda1[1, i, element] = lambda + + !calc_bar_states && continue + + flux_left = flux(u_left, Ja1, equations) + flux_element = flux(u_element, Ja1, equations) + bar_state = 0.5 * (u_element + u_left) - + 0.5 * (flux_element - flux_left) / lambda + for v in eachvariable(equations) + bar_states1[v, nnodes(dg) + 1, i, left] = bar_state[v] + bar_states1[v, 1, i, element] = bar_state[v] + end + end + end + if lower != 0 + for i in eachnode(dg) + u_lower = get_node_vars(u, equations, dg, i, nnodes(dg), lower) + u_element = get_node_vars(u, equations, dg, i, 1, element) + + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, 1, element) + lambda = max_abs_speed_naive(u_lower, u_element, Ja2, equations) + + lambda2[i, nnodes(dg) + 1, lower] = lambda + lambda2[i, 1, element] = lambda + + !calc_bar_states && continue + + flux_lower = flux(u_lower, Ja2, equations) + flux_element = flux(u_element, Ja2, equations) + bar_state = 0.5 * (u_element + u_lower) - + 0.5 * (flux_element - flux_lower) / lambda + for v in eachvariable(equations) + bar_states2[v, i, nnodes(dg) + 1, lower] = bar_state[v] + bar_states2[v, i, 1, element] = bar_state[v] + end + end + end + end + + # Calc lambdas and bar states at physical boundaries + if isperiodic(mesh) + return nothing + end + linear_indices = LinearIndices(size(mesh)) + if !isperiodic(mesh, 1) + # - xi direction + for cell_y in axes(mesh, 2) + element = linear_indices[begin, cell_y] + for j in eachnode(dg) + Ja1 = get_contravariant_vector(1, contravariant_vectors, 1, j, element) + u_inner = get_node_vars(u, equations, dg, 1, j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[1], Ja1, 1, + equations, dg, 1, j, element) + lambda1[1, j, element] = max_abs_speed_naive(u_inner, u_outer, Ja1, + equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, Ja1, equations) + flux_outer = flux(u_outer, Ja1, equations) + for v in eachvariable(equations) + bar_states1[v, 1, j, element] = 0.5 * (u_inner[v] + u_outer[v]) - + 0.5 * + (flux_inner[v] - flux_outer[v]) / + lambda1[1, j, element] + end + end + end + # + xi direction + for cell_y in axes(mesh, 2) + element = linear_indices[end, cell_y] + for j in eachnode(dg) + Ja1 = get_contravariant_vector(1, contravariant_vectors, nnodes(dg), j, + element) + u_inner = get_node_vars(u, equations, dg, nnodes(dg), j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[2], Ja1, 2, + equations, dg, nnodes(dg), j, + element) + lambda1[nnodes(dg) + 1, j, element] = max_abs_speed_naive(u_inner, + u_outer, Ja1, + equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, Ja1, equations) + flux_outer = flux(u_outer, Ja1, equations) + for v in eachvariable(equations) + bar_states1[v, nnodes(dg) + 1, j, element] = 0.5 * (u_inner[v] + + u_outer[v]) - + 0.5 * + (flux_outer[v] - + flux_inner[v]) / + lambda1[nnodes(dg) + 1, + j, element] + end + end + end + end + if !isperiodic(mesh, 2) + # - eta direction + for cell_x in axes(mesh, 1) + element = linear_indices[cell_x, begin] + for i in eachnode(dg) + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, 1, element) + u_inner = get_node_vars(u, equations, dg, i, 1, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[3], Ja2, 3, + equations, dg, i, 1, element) + lambda2[i, 1, element] = max_abs_speed_naive(u_inner, u_outer, Ja2, + equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, Ja2, equations) + flux_outer = flux(u_outer, Ja2, equations) + for v in eachvariable(equations) + bar_states2[v, i, 1, element] = 0.5 * (u_inner[v] + u_outer[v]) - + 0.5 * + (flux_inner[v] - flux_outer[v]) / + lambda2[i, 1, element] + end + end + end + # + eta direction + for cell_x in axes(mesh, 1) + element = linear_indices[cell_x, end] + for i in eachnode(dg) + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, nnodes(dg), + element) + u_inner = get_node_vars(u, equations, dg, i, nnodes(dg), element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[4], Ja2, 4, + equations, dg, i, nnodes(dg), + element) + lambda2[i, nnodes(dg) + 1, element] = max_abs_speed_naive(u_inner, + u_outer, Ja2, + equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, Ja2, equations) + flux_outer = flux(u_outer, Ja2, equations) + for v in eachvariable(equations) + bar_states2[v, i, nnodes(dg) + 1, element] = 0.5 * (u_outer[v] + + u_inner[v]) - + 0.5 * + (flux_outer[v] - + flux_inner[v]) / + lambda2[i, + nnodes(dg) + 1, + element] + end + end + end + end + + return nothing +end +end # @muladd diff --git a/src/solvers/dgsem_structured/subcell_limiters_2d.jl b/src/solvers/dgsem_structured/subcell_limiters_2d.jl new file mode 100644 index 00000000000..8faa10cb18d --- /dev/null +++ b/src/solvers/dgsem_structured/subcell_limiters_2d.jl @@ -0,0 +1,244 @@ +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin +#! format: noindent + +function calc_bounds_2sided_interface!(var_min, var_max, variable, u, t, semi, + mesh::StructuredMesh{2}) + _, equations, dg, cache = mesh_equations_solver_cache(semi) + @unpack boundary_conditions = semi + @unpack contravariant_vectors = cache.elements + # Calc bounds at interfaces and periodic boundaries + for element in eachelement(dg, cache) + # Get neighboring element ids + left = cache.elements.left_neighbors[1, element] + lower = cache.elements.left_neighbors[2, element] + + if left != 0 + for j in eachnode(dg) + var_left = u[variable, nnodes(dg), j, left] + var_element = u[variable, 1, j, element] + + var_min[1, j, element] = min(var_min[1, j, element], var_left) + var_max[1, j, element] = max(var_max[1, j, element], var_left) + + var_min[nnodes(dg), j, left] = min(var_min[nnodes(dg), j, left], + var_element) + var_max[nnodes(dg), j, left] = max(var_max[nnodes(dg), j, left], + var_element) + end + end + if lower != 0 + for i in eachnode(dg) + var_lower = u[variable, i, nnodes(dg), lower] + var_element = u[variable, i, 1, element] + + var_min[i, 1, element] = min(var_min[i, 1, element], var_lower) + var_max[i, 1, element] = max(var_max[i, 1, element], var_lower) + + var_min[i, nnodes(dg), lower] = min(var_min[i, nnodes(dg), lower], + var_element) + var_max[i, nnodes(dg), lower] = max(var_max[i, nnodes(dg), lower], + var_element) + end + end + end + + # Calc bounds at physical boundaries + if isperiodic(mesh) + return nothing + end + linear_indices = LinearIndices(size(mesh)) + if !isperiodic(mesh, 1) + # - xi direction + for cell_y in axes(mesh, 2) + element = linear_indices[begin, cell_y] + for j in eachnode(dg) + Ja1 = get_contravariant_vector(1, contravariant_vectors, 1, j, element) + u_inner = get_node_vars(u, equations, dg, 1, j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[1], Ja1, 1, + equations, dg, 1, j, element) + var_outer = u_outer[variable] + + var_min[1, j, element] = min(var_min[1, j, element], var_outer) + var_max[1, j, element] = max(var_max[1, j, element], var_outer) + end + end + # + xi direction + for cell_y in axes(mesh, 2) + element = linear_indices[end, cell_y] + for j in eachnode(dg) + Ja1 = get_contravariant_vector(1, contravariant_vectors, nnodes(dg), j, + element) + u_inner = get_node_vars(u, equations, dg, nnodes(dg), j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[2], Ja1, 2, + equations, dg, nnodes(dg), j, + element) + var_outer = u_outer[variable] + + var_min[nnodes(dg), j, element] = min(var_min[nnodes(dg), j, element], + var_outer) + var_max[nnodes(dg), j, element] = max(var_max[nnodes(dg), j, element], + var_outer) + end + end + end + if !isperiodic(mesh, 2) + # - eta direction + for cell_x in axes(mesh, 1) + element = linear_indices[cell_x, begin] + for i in eachnode(dg) + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, 1, element) + u_inner = get_node_vars(u, equations, dg, i, 1, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[3], Ja2, 3, + equations, dg, i, 1, element) + var_outer = u_outer[variable] + + var_min[i, 1, element] = min(var_min[i, 1, element], var_outer) + var_max[i, 1, element] = max(var_max[i, 1, element], var_outer) + end + end + # - eta direction + for cell_x in axes(mesh, 1) + element = linear_indices[cell_x, end] + for i in eachnode(dg) + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, nnodes(dg), + element) + u_inner = get_node_vars(u, equations, dg, i, nnodes(dg), element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[4], Ja2, 4, + equations, dg, i, nnodes(dg), + element) + var_outer = u_outer[variable] + + var_min[i, nnodes(dg), element] = min(var_min[i, nnodes(dg), element], + var_outer) + var_max[i, nnodes(dg), element] = max(var_max[i, nnodes(dg), element], + var_outer) + end + end + end + + return nothing +end + +function calc_bounds_1sided_interface!(var_minmax, minmax, variable, u, t, semi, + mesh::StructuredMesh{2}) + _, equations, dg, cache = mesh_equations_solver_cache(semi) + @unpack boundary_conditions = semi + @unpack contravariant_vectors = cache.elements + # Calc bounds at interfaces and periodic boundaries + for element in eachelement(dg, cache) + # Get neighboring element ids + left = cache.elements.left_neighbors[1, element] + lower = cache.elements.left_neighbors[2, element] + + if left != 0 + for j in eachnode(dg) + var_left = variable(get_node_vars(u, equations, dg, nnodes(dg), j, + left), equations) + var_element = variable(get_node_vars(u, equations, dg, 1, j, element), + equations) + + var_minmax[1, j, element] = minmax(var_minmax[1, j, element], var_left) + var_minmax[nnodes(dg), j, left] = minmax(var_minmax[nnodes(dg), j, + left], var_element) + end + end + if lower != 0 + for i in eachnode(dg) + var_lower = variable(get_node_vars(u, equations, dg, i, nnodes(dg), + lower), equations) + var_element = variable(get_node_vars(u, equations, dg, i, 1, element), + equations) + + var_minmax[i, 1, element] = minmax(var_minmax[i, 1, element], var_lower) + var_minmax[i, nnodes(dg), lower] = minmax(var_minmax[i, nnodes(dg), + lower], + var_element) + end + end + end + + # Calc bounds at physical boundaries + if isperiodic(mesh) + return nothing + end + linear_indices = LinearIndices(size(mesh)) + if !isperiodic(mesh, 1) + # - xi direction + for cell_y in axes(mesh, 2) + element = linear_indices[begin, cell_y] + for j in eachnode(dg) + Ja1 = get_contravariant_vector(1, contravariant_vectors, 1, j, element) + u_inner = get_node_vars(u, equations, dg, 1, j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[1], Ja1, 1, + equations, dg, 1, j, element) + var_outer = variable(u_outer, equations) + + var_minmax[1, j, element] = minmax(var_minmax[1, j, element], var_outer) + end + end + # + xi direction + for cell_y in axes(mesh, 2) + element = linear_indices[end, cell_y] + for j in eachnode(dg) + Ja1 = get_contravariant_vector(1, contravariant_vectors, nnodes(dg), j, + element) + u_inner = get_node_vars(u, equations, dg, nnodes(dg), j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[2], Ja1, 2, + equations, dg, nnodes(dg), j, + element) + var_outer = variable(u_outer, equations) + + var_minmax[nnodes(dg), j, element] = minmax(var_minmax[nnodes(dg), j, + element], + var_outer) + end + end + end + if !isperiodic(mesh, 2) + # - eta direction + for cell_x in axes(mesh, 1) + element = linear_indices[cell_x, begin] + for i in eachnode(dg) + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, 1, element) + u_inner = get_node_vars(u, equations, dg, i, 1, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[3], Ja2, 3, + equations, dg, i, 1, element) + var_outer = variable(u_outer, equations) + + var_minmax[i, 1, element] = minmax(var_minmax[i, 1, element], var_outer) + end + end + # + eta direction + for cell_x in axes(mesh, 1) + element = linear_indices[cell_x, end] + for i in eachnode(dg) + Ja2 = get_contravariant_vector(2, contravariant_vectors, i, nnodes(dg), + element) + u_inner = get_node_vars(u, equations, dg, i, nnodes(dg), element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[4], Ja2, 4, + equations, dg, i, nnodes(dg), + element) + var_outer = variable(u_outer, equations) + + var_minmax[i, nnodes(dg), element] = minmax(var_minmax[i, nnodes(dg), + element], + var_outer) + end + end + end + + return nothing +end +end # @muladd diff --git a/src/solvers/dgsem_tree/containers_2d.jl b/src/solvers/dgsem_tree/containers_2d.jl index 4bfbddead9a..2d084710b74 100644 --- a/src/solvers/dgsem_tree/containers_2d.jl +++ b/src/solvers/dgsem_tree/containers_2d.jl @@ -1414,4 +1414,223 @@ function Base.resize!(container::ContainerSubcellLimiterIDP2D, capacity) return nothing end + +# Container data structure (structure-of-arrays style) for variables used for MCL limiting +mutable struct ContainerSubcellLimiterMCL2D{uEltype <: Real} + var_min::Array{uEltype, 4} # [variable, i, j, element] + var_max::Array{uEltype, 4} # [variable, i, j, element] + alpha::Array{uEltype, 4} # [variable, i, j, element] + alpha_pressure::Array{uEltype, 3} # [i, j, element] + alpha_entropy::Array{uEltype, 3} # [i, j, element] + alpha_mean::Array{uEltype, 4} # [variable, i, j, element] + alpha_mean_pressure::Array{uEltype, 3} # [i, j, element] + alpha_mean_entropy::Array{uEltype, 3} # [i, j, element] + # internal `resize!`able storage + _var_min::Vector{uEltype} + _var_max::Vector{uEltype} + _alpha::Vector{uEltype} + _alpha_pressure::Vector{uEltype} + _alpha_entropy::Vector{uEltype} + _alpha_mean::Vector{uEltype} + _alpha_mean_pressure::Vector{uEltype} + _alpha_mean_entropy::Vector{uEltype} +end + +function ContainerSubcellLimiterMCL2D{uEltype}(capacity::Integer, n_variables, + n_nodes) where {uEltype <: Real} + nan_uEltype = convert(uEltype, NaN) + + _var_min = Vector{uEltype}(undef, n_variables * n_nodes^2 * capacity) + var_min = unsafe_wrap(Array, pointer(_var_min), + (n_variables, n_nodes, n_nodes, capacity)) + + _var_max = Vector{uEltype}(undef, n_variables * n_nodes^2 * capacity) + var_max = unsafe_wrap(Array, pointer(_var_max), + (n_variables, n_nodes, n_nodes, capacity)) + + _alpha = fill(nan_uEltype, n_variables * n_nodes * n_nodes * capacity) + alpha = unsafe_wrap(Array, pointer(_alpha), + (n_variables, n_nodes, n_nodes, capacity)) + + _alpha_pressure = fill(nan_uEltype, n_nodes * n_nodes * capacity) + alpha_pressure = unsafe_wrap(Array, pointer(_alpha_pressure), + (n_nodes, n_nodes, capacity)) + + _alpha_entropy = fill(nan_uEltype, n_nodes * n_nodes * capacity) + alpha_entropy = unsafe_wrap(Array, pointer(_alpha_entropy), + (n_nodes, n_nodes, capacity)) + + _alpha_mean = fill(nan_uEltype, n_variables * n_nodes * n_nodes * capacity) + alpha_mean = unsafe_wrap(Array, pointer(_alpha_mean), + (n_variables, n_nodes, n_nodes, capacity)) + + _alpha_mean_pressure = fill(nan_uEltype, n_nodes * n_nodes * capacity) + alpha_mean_pressure = unsafe_wrap(Array, pointer(_alpha_mean_pressure), + (n_nodes, n_nodes, capacity)) + + _alpha_mean_entropy = fill(nan_uEltype, n_nodes * n_nodes * capacity) + alpha_mean_entropy = unsafe_wrap(Array, pointer(_alpha_mean_entropy), + (n_nodes, n_nodes, capacity)) + + return ContainerSubcellLimiterMCL2D{uEltype}(var_min, var_max, alpha, + alpha_pressure, alpha_entropy, + alpha_mean, + alpha_mean_pressure, + alpha_mean_entropy, + _var_min, _var_max, _alpha, + _alpha_pressure, + _alpha_entropy, + _alpha_mean, + _alpha_mean_pressure, + _alpha_mean_entropy) +end + +function nvariables(container::ContainerSubcellLimiterMCL2D) + size(container.var_min, 1) +end +nnodes(container::ContainerSubcellLimiterMCL2D) = size(container.var_min, 2) + +# 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!(container::ContainerSubcellLimiterMCL2D, capacity) + n_variables = nvariables(container) + n_nodes = nnodes(container) + + @unpack _var_min, _var_max = container + resize!(_var_min, n_variables * n_nodes * n_nodes * capacity) + container.var_min = unsafe_wrap(Array, pointer(_var_min), + (n_variables, n_nodes, n_nodes, capacity)) + resize!(_var_max, n_variables * n_nodes * n_nodes * capacity) + container.var_max = unsafe_wrap(Array, pointer(_var_max), + (n_variables, n_nodes, n_nodes, capacity)) + + @unpack _alpha = container + resize!(_alpha, n_variables * n_nodes * n_nodes * capacity) + container.alpha = unsafe_wrap(Array, pointer(_alpha), + (n_variables, n_nodes, n_nodes, capacity)) + + @unpack _alpha_pressure = container + resize!(_alpha_pressure, n_nodes * n_nodes * capacity) + container.alpha_pressure = unsafe_wrap(Array, pointer(_alpha_pressure), + (n_nodes, n_nodes, capacity)) + + @unpack _alpha_entropy = container + resize!(_alpha_entropy, n_nodes * n_nodes * capacity) + container.alpha_entropy = unsafe_wrap(Array, pointer(_alpha_entropy), + (n_nodes, n_nodes, capacity)) + + @unpack _alpha_mean = container + resize!(_alpha_mean, n_variables * n_nodes * n_nodes * capacity) + container.alpha_mean = unsafe_wrap(Array, pointer(_alpha_mean), + (n_variables, n_nodes, n_nodes, capacity)) + + @unpack _alpha_mean_pressure = container + resize!(_alpha_mean_pressure, n_nodes * n_nodes * capacity) + container.alpha_mean_pressure = unsafe_wrap(Array, pointer(_alpha_mean_pressure), + (n_nodes, n_nodes, capacity)) + + @unpack _alpha_mean_entropy = container + resize!(_alpha_mean_entropy, n_nodes * n_nodes * capacity) + container.alpha_mean_entropy = unsafe_wrap(Array, pointer(_alpha_mean_entropy), + (n_nodes, n_nodes, capacity)) + + return nothing +end + +# Container data structure (structure-of-arrays style) for variables used for subcell limiting using bar states +mutable struct ContainerBarStates{uEltype <: Real} + bar_states1::Array{uEltype, 4} # [variable, i, j, element] + bar_states2::Array{uEltype, 4} # [variable, i, j, element] + lambda1::Array{uEltype, 3} # [i, j, element] + lambda2::Array{uEltype, 3} + normal_direction_xi::Array{uEltype, 4} # [index, i, j, elements] + normal_direction_eta::Array{uEltype, 4} # [index, i, j, elements] + # internal `resize!`able storage + _bar_states1::Vector{uEltype} + _bar_states2::Vector{uEltype} + _lambda1::Vector{uEltype} + _lambda2::Vector{uEltype} + _normal_direction_xi::Vector{uEltype} + _normal_direction_eta::Vector{uEltype} +end + +function ContainerBarStates{uEltype}(capacity::Integer, n_variables, + n_nodes) where {uEltype <: Real} + nan_uEltype = convert(uEltype, NaN) + + # Initialize fields with defaults + _bar_states1 = fill(nan_uEltype, n_variables * (n_nodes + 1) * n_nodes * capacity) + bar_states1 = unsafe_wrap(Array, pointer(_bar_states1), + (n_variables, n_nodes + 1, n_nodes, capacity)) + _bar_states2 = fill(nan_uEltype, n_variables * n_nodes * (n_nodes + 1) * capacity) + bar_states2 = unsafe_wrap(Array, pointer(_bar_states2), + (n_variables, n_nodes, n_nodes + 1, capacity)) + + _lambda1 = fill(nan_uEltype, (n_nodes + 1) * n_nodes * capacity) + lambda1 = unsafe_wrap(Array, pointer(_lambda1), (n_nodes + 1, n_nodes, capacity)) + _lambda2 = fill(nan_uEltype, n_nodes * (n_nodes + 1) * capacity) + lambda2 = unsafe_wrap(Array, pointer(_lambda2), (n_nodes, n_nodes + 1, capacity)) + + _normal_direction_xi = fill(nan_uEltype, + (n_variables - 2) * (n_nodes - 1) * n_nodes * capacity) + normal_direction_xi = unsafe_wrap(Array, pointer(_normal_direction_xi), + (n_variables - 2, n_nodes - 1, n_nodes, capacity)) + + _normal_direction_eta = fill(nan_uEltype, + (n_variables - 2) * n_nodes * (n_nodes - 1) * capacity) + normal_direction_eta = unsafe_wrap(Array, pointer(_normal_direction_eta), + (n_variables - 2, n_nodes, n_nodes - 1, + capacity)) + + return ContainerBarStates{uEltype}(bar_states1, bar_states2, lambda1, lambda2, + normal_direction_xi, normal_direction_eta, + _bar_states1, _bar_states2, _lambda1, _lambda2, + _normal_direction_xi, _normal_direction_eta) +end + +nvariables(container::ContainerBarStates) = size(container.bar_states1, 1) +nnodes(container::ContainerBarStates) = size(container.bar_states1, 3) + +# 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!(container::ContainerBarStates, capacity) + n_variables = nvariables(container) + n_nodes = nnodes(container) + + @unpack _bar_states1, _bar_states2 = container + resize!(_bar_states1, n_variables * (n_nodes + 1) * n_nodes * capacity) + container.bar_states1 = unsafe_wrap(Array, pointer(_bar_states1), + (n_variables, n_nodes + 1, n_nodes, capacity)) + resize!(_bar_states2, n_variables * n_nodes * (n_nodes + 1) * capacity) + container.bar_states2 = unsafe_wrap(Array, pointer(_bar_states2), + (n_variables, n_nodes, n_nodes + 1, capacity)) + + @unpack _lambda1, _lambda2 = container + resize!(_lambda1, (n_nodes + 1) * n_nodes * capacity) + container.lambda1 = unsafe_wrap(Array, pointer(_lambda1), + (n_nodes + 1, n_nodes, capacity)) + resize!(_lambda2, n_nodes * (n_nodes + 1) * capacity) + container.lambda2 = unsafe_wrap(Array, pointer(_lambda2), + (n_nodes, n_nodes + 1, capacity)) + + @unpack _normal_direction_xi, _normal_direction_eta = container + resize!(_normal_direction_xi, + (n_variables - 2) * (n_nodes - 1) * n_nodes * capacity) + container.normal_direction_xi = unsafe_wrap(Array, pointer(_normal_direction_xi), + (n_variables - 2, n_nodes - 1, n_nodes, + capacity)) + resize!(_normal_direction_eta, + (n_variables - 2) * n_nodes * (n_nodes - 1) * capacity) + container.normal_direction_eta = unsafe_wrap(Array, pointer(_normal_direction_eta), + (n_variables - 2, n_nodes, n_nodes - 1, + capacity)) + + return nothing +end end # @muladd diff --git a/src/solvers/dgsem_tree/dg.jl b/src/solvers/dgsem_tree/dg.jl index ef9a42b4c1a..c16ba5d8b4d 100644 --- a/src/solvers/dgsem_tree/dg.jl +++ b/src/solvers/dgsem_tree/dg.jl @@ -27,7 +27,12 @@ function pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha, for element in eachelement(dg, cache) # Clip blending factor for values close to zero (-> pure DG) - dg_only = isapprox(alpha[element], 0, atol = 1e-12) + if dg.volume_integral isa VolumeIntegralSubcellLimiting + tol = dg.volume_integral.limiter.threshold_smoothness_indicator + else + tol = 1e-12 + end + dg_only = isapprox(alpha[element], 0, atol = tol) if dg_only push!(element_ids_dg, element) else diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl index 7ecf4c00032..fd990c6aaba 100644 --- a/src/solvers/dgsem_tree/dg_2d.jl +++ b/src/solvers/dgsem_tree/dg_2d.jl @@ -120,7 +120,8 @@ function rhs!(du, u, t, @trixi_timeit timer() "volume integral" begin calc_volume_integral!(du, u, mesh, have_nonconservative_terms(equations), equations, - dg.volume_integral, dg, cache) + dg.volume_integral, dg, cache, + t, boundary_conditions) end # Prolong solution to interfaces @@ -178,6 +179,17 @@ function rhs!(du, u, t, return nothing end +function calc_volume_integral!(du, u, mesh, + nonconservative_terms, equations, + volume_integral::AbstractVolumeIntegral, + dg, cache, t, boundary_conditions) + calc_volume_integral!(du, u, mesh, + nonconservative_terms, equations, + volume_integral, dg, cache) + + return nothing +end + function calc_volume_integral!(du, u, mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}, @@ -537,6 +549,7 @@ end return nothing end +# We pass the `surface_integral` argument solely for dispatch function prolong2interfaces!(cache, u, mesh::TreeMesh{2}, equations, surface_integral, dg::DG) @unpack interfaces = cache diff --git a/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl b/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl index 97843db7743..ed9c193725c 100644 --- a/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl +++ b/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl @@ -5,11 +5,16 @@ @muladd begin #! format: noindent -function create_cache(mesh::TreeMesh{2}, equations, +function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}}, equations, volume_integral::VolumeIntegralSubcellLimiting, dg::DG, uEltype) cache = create_cache(mesh, equations, VolumeIntegralPureLGLFiniteVolume(volume_integral.volume_flux_fv), dg, uEltype) + if volume_integral.limiter.smoothness_indicator + element_ids_dg = Int[] + element_ids_dgfv = Int[] + cache = (; cache..., element_ids_dg, element_ids_dgfv) + end A3dp1_x = Array{uEltype, 3} A3dp1_y = Array{uEltype, 3} @@ -56,22 +61,71 @@ function create_cache(mesh::TreeMesh{2}, equations, end function calc_volume_integral!(du, u, - mesh::TreeMesh{2}, + mesh::Union{TreeMesh{2}, StructuredMesh{2}}, nonconservative_terms, equations, volume_integral::VolumeIntegralSubcellLimiting, - dg::DGSEM, cache) + dg::DGSEM, cache, t, boundary_conditions) @unpack limiter = volume_integral - @threaded for element in eachelement(dg, cache) - subcell_limiting_kernel!(du, u, element, mesh, - nonconservative_terms, equations, - volume_integral, limiter, - dg, cache) + # Calculate lambdas and bar states + @trixi_timeit timer() "calc_lambdas_bar_states!" calc_lambdas_bar_states!(u, t, + mesh, + nonconservative_terms, + equations, + limiter, + dg, cache, + boundary_conditions) + # Calculate boundaries + @trixi_timeit timer() "calc_variable_bounds!" calc_variable_bounds!(u, mesh, + nonconservative_terms, + equations, + limiter, dg, + cache) + + if limiter.smoothness_indicator + @unpack element_ids_dg, element_ids_dgfv = cache + # Calculate element-wise blending factors α + alpha_element = @trixi_timeit timer() "element-wise blending factors" limiter.IndicatorHG(u, + mesh, + equations, + dg, + cache) + + # Determine element ids for DG-only and subcell-wise blended DG-FV volume integral + pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha_element, + dg, cache) + + # Loop over pure DG elements + @trixi_timeit timer() "pure DG" @threaded for idx_element in eachindex(element_ids_dg) + element = element_ids_dg[idx_element] + flux_differencing_kernel!(du, u, element, mesh, + nonconservative_terms, equations, + volume_integral.volume_flux_dg, dg, cache) + end + + # Loop over blended DG-FV elements + @trixi_timeit timer() "subcell-wise blended DG-FV" @threaded for idx_element in eachindex(element_ids_dgfv) + element = element_ids_dgfv[idx_element] + subcell_limiting_kernel!(du, u, element, mesh, + nonconservative_terms, equations, + volume_integral, limiter, + dg, cache) + end + else # limiter.smoothness_indicator == false + # Loop over all elements + @trixi_timeit timer() "subcell-wise blended DG-FV" @threaded for element in eachelement(dg, + cache) + subcell_limiting_kernel!(du, u, element, mesh, + nonconservative_terms, equations, + volume_integral, limiter, + dg, cache) + end end end @inline function subcell_limiting_kernel!(du, u, - element, mesh::TreeMesh{2}, + element, + mesh::Union{TreeMesh{2}, StructuredMesh{2}}, nonconservative_terms, equations, volume_integral, limiter::SubcellLimiterIDP, dg::DGSEM, cache) @@ -119,6 +173,66 @@ end return nothing end +@inline function subcell_limiting_kernel!(du, u, + element, + mesh::Union{TreeMesh{2}, StructuredMesh{2}}, + nonconservative_terms::False, equations, + volume_integral, limiter::SubcellLimiterMCL, + dg::DGSEM, cache) + @unpack inverse_weights = dg.basis + @unpack volume_flux_dg, volume_flux_fv = volume_integral + + # high-order DG fluxes + @unpack fhat1_L_threaded, fhat1_R_threaded, fhat2_L_threaded, fhat2_R_threaded = cache + fhat1_L = fhat1_L_threaded[Threads.threadid()] + fhat1_R = fhat1_R_threaded[Threads.threadid()] + fhat2_L = fhat2_L_threaded[Threads.threadid()] + fhat2_R = fhat2_R_threaded[Threads.threadid()] + calcflux_fhat!(fhat1_L, fhat1_R, fhat2_L, fhat2_R, u, mesh, + nonconservative_terms, equations, volume_flux_dg, dg, element, + cache) + + # low-order FV fluxes + @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded = cache + fstar1_L = fstar1_L_threaded[Threads.threadid()] + fstar2_L = fstar2_L_threaded[Threads.threadid()] + fstar1_R = fstar1_R_threaded[Threads.threadid()] + fstar2_R = fstar2_R_threaded[Threads.threadid()] + calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u, mesh, + nonconservative_terms, equations, volume_flux_fv, dg, element, + cache) + + # antidiffusive flux + calcflux_antidiffusive!(fhat1_L, fhat1_R, fhat2_L, fhat2_R, + fstar1_L, fstar1_R, fstar2_L, fstar2_R, + u, mesh, nonconservative_terms, equations, limiter, dg, + element, cache) + + # limit antidiffusive flux + calcflux_antidiffusive_limited!(u, mesh, nonconservative_terms, equations, + limiter, dg, element, cache, + fstar1_L, fstar2_L) + + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + for j in eachnode(dg), i in eachnode(dg) + for v in eachvariable(equations) + du[v, i, j, element] += inverse_weights[i] * + (fstar1_L[v, i + 1, j] - fstar1_R[v, i, j]) + + inverse_weights[j] * + (fstar2_L[v, i, j + 1] - fstar2_R[v, i, j]) + + du[v, i, j, element] += inverse_weights[i] * + (-antidiffusive_flux1_L[v, i + 1, j, element] + + antidiffusive_flux1_R[v, i, j, element]) + + inverse_weights[j] * + (-antidiffusive_flux2_L[v, i, j + 1, element] + + antidiffusive_flux2_R[v, i, j, element]) + end + end + + return nothing +end + # Calculate the DG staggered volume fluxes `fhat` in subcell FV-form inside the element # (**without non-conservative terms**). # @@ -463,4 +577,1300 @@ end return nothing end + +@inline function calcflux_antidiffusive!(fhat1_L, fhat1_R, fhat2_L, fhat2_R, + fstar1_L, fstar1_R, fstar2_L, fstar2_R, + u, mesh, + nonconservative_terms::False, equations, + limiter::SubcellLimiterMCL, dg, element, cache) + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + + for j in eachnode(dg), i in 2:nnodes(dg) + for v in eachvariable(equations) + antidiffusive_flux1_L[v, i, j, element] = -(fhat1_L[v, i, j] - + fstar1_L[v, i, j]) + antidiffusive_flux1_R[v, i, j, element] = antidiffusive_flux1_L[v, i, j, + element] + end + end + for j in 2:nnodes(dg), i in eachnode(dg) + for v in eachvariable(equations) + antidiffusive_flux2_L[v, i, j, element] = -(fhat2_L[v, i, j] - + fstar2_L[v, i, j]) + antidiffusive_flux2_R[v, i, j, element] = antidiffusive_flux2_L[v, i, j, + element] + end + end + + antidiffusive_flux1_L[:, 1, :, element] .= zero(eltype(antidiffusive_flux1_L)) + antidiffusive_flux1_L[:, nnodes(dg) + 1, :, element] .= zero(eltype(antidiffusive_flux1_L)) + antidiffusive_flux1_R[:, 1, :, element] .= zero(eltype(antidiffusive_flux1_R)) + antidiffusive_flux1_R[:, nnodes(dg) + 1, :, element] .= zero(eltype(antidiffusive_flux1_R)) + + antidiffusive_flux2_L[:, :, 1, element] .= zero(eltype(antidiffusive_flux2_L)) + antidiffusive_flux2_L[:, :, nnodes(dg) + 1, element] .= zero(eltype(antidiffusive_flux2_L)) + antidiffusive_flux2_R[:, :, 1, element] .= zero(eltype(antidiffusive_flux2_R)) + antidiffusive_flux2_R[:, :, nnodes(dg) + 1, element] .= zero(eltype(antidiffusive_flux2_R)) + + return nothing +end + +@inline function calcflux_antidiffusive!(fhat1_L, fhat1_R, fhat2_L, fhat2_R, + fstar1_L, fstar1_R, fstar2_L, fstar2_R, + u, mesh, + nonconservative_terms::True, equations, + limiter::SubcellLimiterMCL, dg, element, cache) + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + + for j in eachnode(dg), i in 2:nnodes(dg) + for v in eachvariable(equations) + antidiffusive_flux1_L[v, i, j, element] = -(fhat1_L[v, i, j] - + fstar1_L[v, i, j]) + antidiffusive_flux1_R[v, i, j, element] = -(fhat1_R[v, i, j] - + fstar1_R[v, i, j]) + end + end + for j in 2:nnodes(dg), i in eachnode(dg) + for v in eachvariable(equations) + antidiffusive_flux2_L[v, i, j, element] = -(fhat2_L[v, i, j] - + fstar2_L[v, i, j]) + antidiffusive_flux2_R[v, i, j, element] = -(fhat2_R[v, i, j] - + fstar2_R[v, i, j]) + end + end + + antidiffusive_flux1_L[:, 1, :, element] .= zero(eltype(antidiffusive_flux1_L)) + antidiffusive_flux1_L[:, nnodes(dg) + 1, :, element] .= zero(eltype(antidiffusive_flux1_L)) + antidiffusive_flux1_R[:, 1, :, element] .= zero(eltype(antidiffusive_flux1_R)) + antidiffusive_flux1_R[:, nnodes(dg) + 1, :, element] .= zero(eltype(antidiffusive_flux1_R)) + + antidiffusive_flux2_L[:, :, 1, element] .= zero(eltype(antidiffusive_flux2_L)) + antidiffusive_flux2_L[:, :, nnodes(dg) + 1, element] .= zero(eltype(antidiffusive_flux2_L)) + antidiffusive_flux2_R[:, :, 1, element] .= zero(eltype(antidiffusive_flux2_R)) + antidiffusive_flux2_R[:, :, nnodes(dg) + 1, element] .= zero(eltype(antidiffusive_flux2_R)) + + return nothing +end + +@inline function calc_lambdas_bar_states!(u, t, mesh::TreeMesh, + nonconservative_terms, equations, limiter, + dg, cache, boundary_conditions; + calc_bar_states = true) + if limiter isa SubcellLimiterIDP && !limiter.bar_states + return nothing + end + @unpack lambda1, lambda2, bar_states1, bar_states2 = limiter.cache.container_bar_states + + # Calc lambdas and bar states inside elements + @threaded for element in eachelement(dg, cache) + for j in eachnode(dg), i in 2:nnodes(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + u_node_im1 = get_node_vars(u, equations, dg, i - 1, j, element) + lambda1[i, j, element] = max_abs_speed_naive(u_node_im1, u_node, 1, + equations) + + !calc_bar_states && continue + + flux1 = flux(u_node, 1, equations) + flux1_im1 = flux(u_node_im1, 1, equations) + for v in eachvariable(equations) + bar_states1[v, i, j, element] = 0.5 * (u_node[v] + u_node_im1[v]) - + 0.5 * (flux1[v] - flux1_im1[v]) / + lambda1[i, j, element] + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + u_node = get_node_vars(u, equations, dg, i, j, element) + u_node_jm1 = get_node_vars(u, equations, dg, i, j - 1, element) + lambda2[i, j, element] = max_abs_speed_naive(u_node_jm1, u_node, 2, + equations) + + !calc_bar_states && continue + + flux2 = flux(u_node, 2, equations) + flux2_jm1 = flux(u_node_jm1, 2, equations) + for v in eachvariable(equations) + bar_states2[v, i, j, element] = 0.5 * (u_node[v] + u_node_jm1[v]) - + 0.5 * (flux2[v] - flux2_jm1[v]) / + lambda2[i, j, element] + end + end + end + + # Calc lambdas and bar states at interfaces and periodic boundaries + @threaded for interface in eachinterface(dg, cache) + # Get neighboring element ids + left_id = cache.interfaces.neighbor_ids[1, interface] + right_id = cache.interfaces.neighbor_ids[2, interface] + + orientation = cache.interfaces.orientations[interface] + + if orientation == 1 + for j in eachnode(dg) + u_left = get_node_vars(u, equations, dg, nnodes(dg), j, left_id) + u_right = get_node_vars(u, equations, dg, 1, j, right_id) + lambda = max_abs_speed_naive(u_left, u_right, orientation, equations) + + lambda1[nnodes(dg) + 1, j, left_id] = lambda + lambda1[1, j, right_id] = lambda + + !calc_bar_states && continue + + flux_left = flux(u_left, orientation, equations) + flux_right = flux(u_right, orientation, equations) + bar_state = 0.5 * (u_left + u_right) - + 0.5 * (flux_right - flux_left) / lambda + for v in eachvariable(equations) + bar_states1[v, nnodes(dg) + 1, j, left_id] = bar_state[v] + bar_states1[v, 1, j, right_id] = bar_state[v] + end + end + else # orientation == 2 + for i in eachnode(dg) + u_left = get_node_vars(u, equations, dg, i, nnodes(dg), left_id) + u_right = get_node_vars(u, equations, dg, i, 1, right_id) + lambda = max_abs_speed_naive(u_left, u_right, orientation, equations) + + lambda2[i, nnodes(dg) + 1, left_id] = lambda + lambda2[i, 1, right_id] = lambda + + !calc_bar_states && continue + + flux_left = flux(u_left, orientation, equations) + flux_right = flux(u_right, orientation, equations) + bar_state = 0.5 * (u_left + u_right) - + 0.5 * (flux_right - flux_left) / lambda + for v in eachvariable(equations) + bar_states2[v, i, nnodes(dg) + 1, left_id] = bar_state[v] + bar_states2[v, i, 1, right_id] = bar_state[v] + end + end + end + end + + # Calc lambdas and bar states at physical boundaries + @threaded for boundary in eachboundary(dg, cache) + element = cache.boundaries.neighbor_ids[boundary] + orientation = cache.boundaries.orientations[boundary] + neighbor_side = cache.boundaries.neighbor_sides[boundary] + + if orientation == 1 + if neighbor_side == 2 # Element is on the right, boundary on the left + for j in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, 1, j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[1], + orientation, 1, + equations, dg, 1, j, element) + lambda1[1, j, element] = max_abs_speed_naive(u_inner, u_outer, + orientation, equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, orientation, equations) + flux_outer = flux(u_outer, orientation, equations) + bar_state = 0.5 * (u_inner + u_outer) - + 0.5 * (flux_inner - flux_outer) / lambda1[1, j, element] + for v in eachvariable(equations) + bar_states1[v, 1, j, element] = bar_state[v] + end + end + else # Element is on the left, boundary on the right + for j in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, nnodes(dg), j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[2], + orientation, 2, + equations, dg, nnodes(dg), j, + element) + lambda1[nnodes(dg) + 1, j, element] = max_abs_speed_naive(u_inner, + u_outer, + orientation, + equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, orientation, equations) + flux_outer = flux(u_outer, orientation, equations) + bar_state = 0.5 * (u_inner + u_outer) - + 0.5 * (flux_outer - flux_inner) / + lambda1[nnodes(dg) + 1, j, element] + for v in eachvariable(equations) + bar_states1[v, nnodes(dg) + 1, j, element] = bar_state[v] + end + end + end + else # orientation == 2 + if neighbor_side == 2 # Element is on the right, boundary on the left + for i in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, i, 1, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[3], + orientation, 3, + equations, dg, i, 1, element) + lambda2[i, 1, element] = max_abs_speed_naive(u_inner, u_outer, + orientation, equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, orientation, equations) + flux_outer = flux(u_outer, orientation, equations) + bar_state = 0.5 * (u_inner + u_outer) - + 0.5 * (flux_inner - flux_outer) / lambda2[i, 1, element] + for v in eachvariable(equations) + bar_states2[v, i, 1, element] = bar_state[v] + end + end + else # Element is on the left, boundary on the right + for i in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, i, nnodes(dg), element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[4], + orientation, 4, + equations, dg, i, nnodes(dg), + element) + lambda2[i, nnodes(dg) + 1, element] = max_abs_speed_naive(u_inner, + u_outer, + orientation, + equations) + + !calc_bar_states && continue + + flux_inner = flux(u_inner, orientation, equations) + flux_outer = flux(u_outer, orientation, equations) + bar_state = 0.5 * (u_inner + u_outer) - + 0.5 * (flux_outer - flux_inner) / + lambda2[i, nnodes(dg) + 1, element] + for v in eachvariable(equations) + bar_states2[v, i, nnodes(dg) + 1, element] = bar_state[v] + end + end + end + end + end + + return nothing +end + +@inline function calc_variable_bounds!(u, mesh, nonconservative_terms, equations, + limiter::SubcellLimiterIDP, dg, cache) + if !limiter.bar_states + return nothing + end + @unpack variable_bounds = limiter.cache.subcell_limiter_coefficients + @unpack bar_states1, bar_states2 = limiter.cache.container_bar_states + + # state variables + if limiter.local_minmax + for index in limiter.local_minmax_variables_cons + var_min = variable_bounds[Symbol("$(index)_min")] + var_max = variable_bounds[Symbol("$(index)_max")] + @threaded for element in eachelement(dg, cache) + var_min[:, :, element] .= typemax(eltype(var_min)) + var_max[:, :, element] .= typemin(eltype(var_max)) + for j in eachnode(dg), i in eachnode(dg) + var_min[i, j, element] = min(var_min[i, j, element], + u[index, i, j, element]) + var_max[i, j, element] = max(var_max[i, j, element], + u[index, i, j, element]) + # TODO: Add source term! + # - xi direction + var_min[i, j, element] = min(var_min[i, j, element], + bar_states1[index, i, j, element]) + var_max[i, j, element] = max(var_max[i, j, element], + bar_states1[index, i, j, element]) + # + xi direction + var_min[i, j, element] = min(var_min[i, j, element], + bar_states1[index, i + 1, j, element]) + var_max[i, j, element] = max(var_max[i, j, element], + bar_states1[index, i + 1, j, element]) + # - eta direction + var_min[i, j, element] = min(var_min[i, j, element], + bar_states2[index, i, j, element]) + var_max[i, j, element] = max(var_max[i, j, element], + bar_states2[index, i, j, element]) + # + eta direction + var_min[i, j, element] = min(var_min[i, j, element], + bar_states2[index, i, j + 1, element]) + var_max[i, j, element] = max(var_max[i, j, element], + bar_states2[index, i, j + 1, element]) + end + end + end + end + # Specific Entropy + if limiter.spec_entropy + s_min = variable_bounds[:spec_entropy_min] + @threaded for element in eachelement(dg, cache) + s_min[:, :, element] .= typemax(eltype(s_min)) + for j in eachnode(dg), i in eachnode(dg) + s = entropy_spec(get_node_vars(u, equations, dg, i, j, element), + equations) + s_min[i, j, element] = min(s_min[i, j, element], s) + # TODO: Add source? + # - xi direction + s = entropy_spec(get_node_vars(bar_states1, equations, dg, i, j, + element), equations) + s_min[i, j, element] = min(s_min[i, j, element], s) + # + xi direction + s = entropy_spec(get_node_vars(bar_states1, equations, dg, i + 1, j, + element), equations) + s_min[i, j, element] = min(s_min[i, j, element], s) + # - eta direction + s = entropy_spec(get_node_vars(bar_states2, equations, dg, i, j, + element), equations) + s_min[i, j, element] = min(s_min[i, j, element], s) + # + eta direction + s = entropy_spec(get_node_vars(bar_states2, equations, dg, i, j + 1, + element), equations) + s_min[i, j, element] = min(s_min[i, j, element], s) + end + end + end + # Mathematical entropy + if limiter.math_entropy + s_max = variable_bounds[:math_entropy_max] + @threaded for element in eachelement(dg, cache) + s_max[:, :, element] .= typemin(eltype(s_max)) + for j in eachnode(dg), i in eachnode(dg) + s = entropy_math(get_node_vars(u, equations, dg, i, j, element), + equations) + s_max[i, j, element] = max(s_max[i, j, element], s) + # - xi direction + s = entropy_math(get_node_vars(bar_states1, equations, dg, i, j, + element), equations) + s_max[i, j, element] = max(s_max[i, j, element], s) + # + xi direction + s = entropy_math(get_node_vars(bar_states1, equations, dg, i + 1, j, + element), equations) + s_max[i, j, element] = max(s_max[i, j, element], s) + # - eta direction + s = entropy_math(get_node_vars(bar_states2, equations, dg, i, j, + element), equations) + s_max[i, j, element] = max(s_max[i, j, element], s) + # + eta direction + s = entropy_math(get_node_vars(bar_states2, equations, dg, i, j + 1, + element), equations) + s_max[i, j, element] = max(s_max[i, j, element], s) + end + end + end + + return nothing +end + +@inline function calc_variable_bounds!(u, mesh, nonconservative_terms, equations, + limiter::SubcellLimiterMCL, dg, cache) + @unpack var_min, var_max = limiter.cache.subcell_limiter_coefficients + @unpack bar_states1, bar_states2, lambda1, lambda2 = limiter.cache.container_bar_states + + @threaded for element in eachelement(dg, cache) + for v in eachvariable(equations) + var_min[v, :, :, element] .= typemax(eltype(var_min)) + var_max[v, :, :, element] .= typemin(eltype(var_max)) + end + + if limiter.DensityLimiter + for j in eachnode(dg), i in eachnode(dg) + # Previous solution + var_min[1, i, j, element] = min(var_min[1, i, j, element], + u[1, i, j, element]) + var_max[1, i, j, element] = max(var_max[1, i, j, element], + u[1, i, j, element]) + # - xi direction + bar_state_rho = bar_states1[1, i, j, element] + var_min[1, i, j, element] = min(var_min[1, i, j, element], + bar_state_rho) + var_max[1, i, j, element] = max(var_max[1, i, j, element], + bar_state_rho) + # + xi direction + bar_state_rho = bar_states1[1, i + 1, j, element] + var_min[1, i, j, element] = min(var_min[1, i, j, element], + bar_state_rho) + var_max[1, i, j, element] = max(var_max[1, i, j, element], + bar_state_rho) + # - eta direction + bar_state_rho = bar_states2[1, i, j, element] + var_min[1, i, j, element] = min(var_min[1, i, j, element], + bar_state_rho) + var_max[1, i, j, element] = max(var_max[1, i, j, element], + bar_state_rho) + # + eta direction + bar_state_rho = bar_states2[1, i, j + 1, element] + var_min[1, i, j, element] = min(var_min[1, i, j, element], + bar_state_rho) + var_max[1, i, j, element] = max(var_max[1, i, j, element], + bar_state_rho) + end + end #limiter.DensityLimiter + + if limiter.SequentialLimiter + for j in eachnode(dg), i in eachnode(dg) + # Previous solution + for v in 2:nvariables(equations) + phi = u[v, i, j, element] / u[1, i, j, element] + var_min[v, i, j, element] = min(var_min[v, i, j, element], phi) + var_max[v, i, j, element] = max(var_max[v, i, j, element], phi) + end + # - xi direction + bar_state_rho = bar_states1[1, i, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states1[v, i, j, element] / bar_state_rho + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_phi) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_phi) + end + # + xi direction + bar_state_rho = bar_states1[1, i + 1, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states1[v, i + 1, j, element] / bar_state_rho + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_phi) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_phi) + end + # - eta direction + bar_state_rho = bar_states2[1, i, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states2[v, i, j, element] / bar_state_rho + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_phi) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_phi) + end + # + eta direction + bar_state_rho = bar_states2[1, i, j + 1, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states2[v, i, j + 1, element] / bar_state_rho + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_phi) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_phi) + end + end + elseif limiter.ConservativeLimiter + for j in eachnode(dg), i in eachnode(dg) + # Previous solution + for v in 2:nvariables(equations) + var_min[v, i, j, element] = min(var_min[v, i, j, element], + u[v, i, j, element]) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + u[v, i, j, element]) + end + # - xi direction + for v in 2:nvariables(equations) + bar_state_rho = bar_states1[v, i, j, element] + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_rho) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_rho) + end + # + xi direction + for v in 2:nvariables(equations) + bar_state_rho = bar_states1[v, i + 1, j, element] + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_rho) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_rho) + end + # - eta direction + for v in 2:nvariables(equations) + bar_state_rho = bar_states2[v, i, j, element] + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_rho) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_rho) + end + # + eta direction + for v in 2:nvariables(equations) + bar_state_rho = bar_states2[v, i, j + 1, element] + var_min[v, i, j, element] = min(var_min[v, i, j, element], + bar_state_rho) + var_max[v, i, j, element] = max(var_max[v, i, j, element], + bar_state_rho) + end + end + end + end + + return nothing +end + +@inline function calcflux_antidiffusive_limited!(u, mesh, nonconservative_terms::False, + equations, limiter, dg, element, + cache, + fstar1, fstar2) + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + @unpack var_min, var_max = limiter.cache.subcell_limiter_coefficients + @unpack bar_states1, bar_states2, lambda1, lambda2 = limiter.cache.container_bar_states + + if limiter.Plotting + @unpack alpha, alpha_pressure, alpha_entropy, + alpha_mean, alpha_mean_pressure, alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + for j in eachnode(dg), i in eachnode(dg) + alpha_mean[:, i, j, element] .= zero(eltype(alpha_mean)) + alpha[:, i, j, element] .= one(eltype(alpha)) + if limiter.PressurePositivityLimiterKuzmin + alpha_mean_pressure[i, j, element] = zero(eltype(alpha_mean_pressure)) + alpha_pressure[i, j, element] = one(eltype(alpha_pressure)) + end + if limiter.SemiDiscEntropyLimiter + alpha_mean_entropy[i, j, element] = zero(eltype(alpha_mean_entropy)) + alpha_entropy[i, j, element] = one(eltype(alpha_entropy)) + end + end + end + + # The antidiffuse flux can have very small absolute values. This can lead to values of f_min which are zero up to machine accuracy. + # To avoid further calculations with these values, we replace them by 0. + # It can also happen that the limited flux changes its sign (for instance to -1e-13). + # This does not really make sense in theory and causes problems for the visualization. + # Therefore we make sure that the flux keeps its sign during limiting. + + # Density limiter + if limiter.DensityLimiter + for j in eachnode(dg), i in 2:nnodes(dg) + lambda = lambda1[i, j, element] + bar_state_rho = bar_states1[1, i, j, element] + + # Limit density + if antidiffusive_flux1_L[1, i, j, element] > 0 + f_max = lambda * min(var_max[1, i - 1, j, element] - bar_state_rho, + bar_state_rho - var_min[1, i, j, element]) + f_max = isapprox(f_max, 0.0, atol = eps()) ? 0.0 : f_max + flux_limited = min(antidiffusive_flux1_L[1, i, j, element], + max(f_max, 0.0)) + else + f_min = lambda * max(var_min[1, i - 1, j, element] - bar_state_rho, + bar_state_rho - var_max[1, i, j, element]) + f_min = isapprox(f_min, 0.0, atol = eps()) ? 0.0 : f_min + flux_limited = max(antidiffusive_flux1_L[1, i, j, element], + min(f_min, 0.0)) + end + + if limiter.Plotting || limiter.DensityAlphaForAll + if isapprox(antidiffusive_flux1_L[1, i, j, element], 0.0, atol = eps()) + coefficient = 1.0 # flux_limited is zero as well + else + coefficient = min(1, + (flux_limited + sign(flux_limited) * eps()) / + (antidiffusive_flux1_L[1, i, j, element] + + sign(flux_limited) * eps())) + end + + if limiter.Plotting + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[1, i - 1, j, element] = min(alpha[1, i - 1, j, element], + coefficient) + alpha[1, i, j, element] = min(alpha[1, i, j, element], coefficient) + alpha_mean[1, i - 1, j, element] += coefficient + alpha_mean[1, i, j, element] += coefficient + end + end + antidiffusive_flux1_L[1, i, j, element] = flux_limited + + #Limit all quantities with the same alpha + if limiter.DensityAlphaForAll + for v in 2:nvariables(equations) + antidiffusive_flux1_L[v, i, j, element] = coefficient * + antidiffusive_flux1_L[v, + i, + j, + element] + end + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + lambda = lambda2[i, j, element] + bar_state_rho = bar_states2[1, i, j, element] + + # Limit density + if antidiffusive_flux2_L[1, i, j, element] > 0 + f_max = lambda * min(var_max[1, i, j - 1, element] - bar_state_rho, + bar_state_rho - var_min[1, i, j, element]) + f_max = isapprox(f_max, 0.0, atol = eps()) ? 0.0 : f_max + flux_limited = min(antidiffusive_flux2_L[1, i, j, element], + max(f_max, 0.0)) + else + f_min = lambda * max(var_min[1, i, j - 1, element] - bar_state_rho, + bar_state_rho - var_max[1, i, j, element]) + f_min = isapprox(f_min, 0.0, atol = eps()) ? 0.0 : f_min + flux_limited = max(antidiffusive_flux2_L[1, i, j, element], + min(f_min, 0.0)) + end + + if limiter.Plotting || limiter.DensityAlphaForAll + if isapprox(antidiffusive_flux2_L[1, i, j, element], 0.0, atol = eps()) + coefficient = 1.0 # flux_limited is zero as well + else + coefficient = min(1, + (flux_limited + sign(flux_limited) * eps()) / + (antidiffusive_flux2_L[1, i, j, element] + + sign(flux_limited) * eps())) + end + + if limiter.Plotting + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[1, i, j - 1, element] = min(alpha[1, i, j - 1, element], + coefficient) + alpha[1, i, j, element] = min(alpha[1, i, j, element], coefficient) + alpha_mean[1, i, j - 1, element] += coefficient + alpha_mean[1, i, j, element] += coefficient + end + end + antidiffusive_flux2_L[1, i, j, element] = flux_limited + + #Limit all quantities with the same alpha + if limiter.DensityAlphaForAll + for v in 2:nvariables(equations) + antidiffusive_flux2_L[v, i, j, element] = coefficient * + antidiffusive_flux2_L[v, + i, + j, + element] + end + end + end + end # if limiter.DensityLimiter + + # Sequential limiter + if limiter.SequentialLimiter + for j in eachnode(dg), i in 2:nnodes(dg) + lambda = lambda1[i, j, element] + bar_state_rho = bar_states1[1, i, j, element] + + # Limit velocity and total energy + rho_limited_iim1 = lambda * bar_state_rho - + antidiffusive_flux1_L[1, i, j, element] + rho_limited_im1i = lambda * bar_state_rho + + antidiffusive_flux1_L[1, i, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states1[v, i, j, element] + + phi = bar_state_phi / bar_state_rho + + g = antidiffusive_flux1_L[v, i, j, element] + + (lambda * bar_state_phi - rho_limited_im1i * phi) + + if g > 0 + g_max = min(rho_limited_im1i * + (var_max[v, i - 1, j, element] - phi), + rho_limited_iim1 * (phi - var_min[v, i, j, element])) + g_max = isapprox(g_max, 0.0, atol = eps()) ? 0.0 : g_max + g_limited = min(g, max(g_max, 0.0)) + else + g_min = max(rho_limited_im1i * + (var_min[v, i - 1, j, element] - phi), + rho_limited_iim1 * (phi - var_max[v, i, j, element])) + g_min = isapprox(g_min, 0.0, atol = eps()) ? 0.0 : g_min + g_limited = max(g, min(g_min, 0.0)) + end + if limiter.Plotting + if isapprox(g, 0.0, atol = eps()) + coefficient = 1.0 # g_limited is zero as well + else + coefficient = min(1, + (g_limited + sign(g_limited) * eps()) / + (g + sign(g_limited) * eps())) + end + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[v, i - 1, j, element] = min(alpha[v, i - 1, j, element], + coefficient) + alpha[v, i, j, element] = min(alpha[v, i, j, element], coefficient) + alpha_mean[v, i - 1, j, element] += coefficient + alpha_mean[v, i, j, element] += coefficient + end + antidiffusive_flux1_L[v, i, j, element] = (rho_limited_im1i * phi - + lambda * bar_state_phi) + + g_limited + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + lambda = lambda2[i, j, element] + bar_state_rho = bar_states2[1, i, j, element] + + # Limit velocity and total energy + rho_limited_jjm1 = lambda * bar_state_rho - + antidiffusive_flux2_L[1, i, j, element] + rho_limited_jm1j = lambda * bar_state_rho + + antidiffusive_flux2_L[1, i, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states2[v, i, j, element] + + phi = bar_state_phi / bar_state_rho + + g = antidiffusive_flux2_L[v, i, j, element] + + (lambda * bar_state_phi - rho_limited_jm1j * phi) + + if g > 0 + g_max = min(rho_limited_jm1j * + (var_max[v, i, j - 1, element] - phi), + rho_limited_jjm1 * (phi - var_min[v, i, j, element])) + g_max = isapprox(g_max, 0.0, atol = eps()) ? 0.0 : g_max + g_limited = min(g, max(g_max, 0.0)) + else + g_min = max(rho_limited_jm1j * + (var_min[v, i, j - 1, element] - phi), + rho_limited_jjm1 * (phi - var_max[v, i, j, element])) + g_min = isapprox(g_min, 0.0, atol = eps()) ? 0.0 : g_min + g_limited = max(g, min(g_min, 0.0)) + end + if limiter.Plotting + if isapprox(g, 0.0, atol = eps()) + coefficient = 1.0 # g_limited is zero as well + else + coefficient = min(1, + (g_limited + sign(g_limited) * eps()) / + (g + sign(g_limited) * eps())) + end + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[v, i, j - 1, element] = min(alpha[v, i, j - 1, element], + coefficient) + alpha[v, i, j, element] = min(alpha[v, i, j, element], coefficient) + alpha_mean[v, i, j - 1, element] += coefficient + alpha_mean[v, i, j, element] += coefficient + end + + antidiffusive_flux2_L[v, i, j, element] = (rho_limited_jm1j * phi - + lambda * bar_state_phi) + + g_limited + end + end + # Conservative limiter + elseif limiter.ConservativeLimiter + for j in eachnode(dg), i in 2:nnodes(dg) + lambda = lambda1[i, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states1[v, i, j, element] + # Limit density + if antidiffusive_flux1_L[v, i, j, element] > 0 + f_max = lambda * min(var_max[v, i - 1, j, element] - bar_state_phi, + bar_state_phi - var_min[v, i, j, element]) + f_max = isapprox(f_max, 0.0, atol = eps()) ? 0.0 : f_max + flux_limited = min(antidiffusive_flux1_L[v, i, j, element], + max(f_max, 0.0)) + else + f_min = lambda * max(var_min[v, i - 1, j, element] - bar_state_phi, + bar_state_phi - var_max[v, i, j, element]) + f_min = isapprox(f_min, 0.0, atol = eps()) ? 0.0 : f_min + flux_limited = max(antidiffusive_flux1_L[v, i, j, element], + min(f_min, 0.0)) + end + + if limiter.Plotting + if isapprox(antidiffusive_flux1_L[v, i, j, element], 0.0, + atol = eps()) + coefficient = 1.0 # flux_limited is zero as well + else + coefficient = min(1, + (flux_limited + sign(flux_limited) * eps()) / + (antidiffusive_flux1_L[v, i, j, element] + + sign(flux_limited) * eps())) + end + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[v, i - 1, j, element] = min(alpha[v, i - 1, j, element], + coefficient) + alpha[v, i, j, element] = min(alpha[v, i, j, element], coefficient) + alpha_mean[v, i - 1, j, element] += coefficient + alpha_mean[v, i, j, element] += coefficient + end + antidiffusive_flux1_L[v, i, j, element] = flux_limited + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + lambda = lambda2[i, j, element] + for v in 2:nvariables(equations) + bar_state_phi = bar_states2[v, i, j, element] + # Limit density + if antidiffusive_flux2_L[v, i, j, element] > 0 + f_max = lambda * min(var_max[v, i, j - 1, element] - bar_state_phi, + bar_state_phi - var_min[v, i, j, element]) + f_max = isapprox(f_max, 0.0, atol = eps()) ? 0.0 : f_max + flux_limited = min(antidiffusive_flux2_L[v, i, j, element], + max(f_max, 0.0)) + else + f_min = lambda * max(var_min[v, i, j - 1, element] - bar_state_phi, + bar_state_phi - var_max[v, i, j, element]) + f_min = isapprox(f_min, 0.0, atol = eps()) ? 0.0 : f_min + flux_limited = max(antidiffusive_flux2_L[v, i, j, element], + min(f_min, 0.0)) + end + + if limiter.Plotting + if isapprox(antidiffusive_flux2_L[v, i, j, element], 0.0, + atol = eps()) + coefficient = 1.0 # flux_limited is zero as well + else + coefficient = min(1, + (flux_limited + sign(flux_limited) * eps()) / + (antidiffusive_flux2_L[v, i, j, element] + + sign(flux_limited) * eps())) + end + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[v, i, j - 1, element] = min(alpha[v, i, j - 1, element], + coefficient) + alpha[v, i, j, element] = min(alpha[v, i, j, element], coefficient) + alpha_mean[v, i, j - 1, element] += coefficient + alpha_mean[v, i, j, element] += coefficient + end + antidiffusive_flux2_L[v, i, j, element] = flux_limited + end + end + end # limiter.SequentialLimiter and limiter.ConservativeLimiter + + # Density positivity limiter + if limiter.DensityPositivityLimiter + beta = limiter.DensityPositivityCorrectionFactor + for j in eachnode(dg), i in 2:nnodes(dg) + lambda = lambda1[i, j, element] + bar_state_rho = bar_states1[1, i, j, element] + # Limit density + if antidiffusive_flux1_L[1, i, j, element] > 0 + f_max = (1 - beta) * lambda * bar_state_rho + f_max = isapprox(f_max, 0.0, atol = eps()) ? 0.0 : f_max + flux_limited = min(antidiffusive_flux1_L[1, i, j, element], + max(f_max, 0.0)) + else + f_min = -(1 - beta) * lambda * bar_state_rho + f_min = isapprox(f_min, 0.0, atol = eps()) ? 0.0 : f_min + flux_limited = max(antidiffusive_flux1_L[1, i, j, element], + min(f_min, 0.0)) + end + + if limiter.Plotting || limiter.DensityAlphaForAll + if isapprox(antidiffusive_flux1_L[1, i, j, element], 0.0, atol = eps()) + coefficient = 1.0 # flux_limited is zero as well + else + coefficient = flux_limited / antidiffusive_flux1_L[1, i, j, element] + end + + if limiter.Plotting + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[1, i - 1, j, element] = min(alpha[1, i - 1, j, element], + coefficient) + alpha[1, i, j, element] = min(alpha[1, i, j, element], coefficient) + if !limiter.DensityLimiter + alpha_mean[1, i - 1, j, element] += coefficient + alpha_mean[1, i, j, element] += coefficient + end + end + end + antidiffusive_flux1_L[1, i, j, element] = flux_limited + + #Limit all quantities with the same alpha + if limiter.DensityAlphaForAll + for v in 2:nvariables(equations) + antidiffusive_flux1_L[v, i, j, element] = coefficient * + antidiffusive_flux1_L[v, + i, + j, + element] + end + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + lambda = lambda2[i, j, element] + bar_state_rho = bar_states2[1, i, j, element] + # Limit density + if antidiffusive_flux2_L[1, i, j, element] > 0 + f_max = (1 - beta) * lambda * bar_state_rho + f_max = isapprox(f_max, 0.0, atol = eps()) ? 0.0 : f_max + flux_limited = min(antidiffusive_flux2_L[1, i, j, element], + max(f_max, 0.0)) + else + f_min = -(1 - beta) * lambda * bar_state_rho + f_min = isapprox(f_min, 0.0, atol = eps()) ? 0.0 : f_min + flux_limited = max(antidiffusive_flux2_L[1, i, j, element], + min(f_min, 0.0)) + end + + if limiter.Plotting || limiter.DensityAlphaForAll + if isapprox(antidiffusive_flux2_L[1, i, j, element], 0.0, atol = eps()) + coefficient = 1.0 # flux_limited is zero as well + else + coefficient = flux_limited / antidiffusive_flux2_L[1, i, j, element] + end + + if limiter.Plotting + @unpack alpha, alpha_mean = limiter.cache.subcell_limiter_coefficients + alpha[1, i, j - 1, element] = min(alpha[1, i, j - 1, element], + coefficient) + alpha[1, i, j, element] = min(alpha[1, i, j, element], coefficient) + if !limiter.DensityLimiter + alpha_mean[1, i, j - 1, element] += coefficient + alpha_mean[1, i, j, element] += coefficient + end + end + end + antidiffusive_flux2_L[1, i, j, element] = flux_limited + + #Limit all quantities with the same alpha + if limiter.DensityAlphaForAll + for v in 2:nvariables(equations) + antidiffusive_flux2_L[v, i, j, element] = coefficient * + antidiffusive_flux2_L[v, + i, + j, + element] + end + end + end + end #if limiter.DensityPositivityLimiter + + # Divide alpha_mean by number of additions + if limiter.Plotting + @unpack alpha_mean = limiter.cache.subcell_limiter_coefficients + # Interfaces contribute with 1.0 + if limiter.DensityLimiter || limiter.DensityPositivityLimiter + for i in eachnode(dg) + alpha_mean[1, i, 1, element] += 1.0 + alpha_mean[1, i, nnodes(dg), element] += 1.0 + alpha_mean[1, 1, i, element] += 1.0 + alpha_mean[1, nnodes(dg), i, element] += 1.0 + end + for j in eachnode(dg), i in eachnode(dg) + alpha_mean[1, i, j, element] /= 4 + end + end + if limiter.SequentialLimiter || limiter.ConservativeLimiter + for v in 2:nvariables(equations) + for i in eachnode(dg) + alpha_mean[v, i, 1, element] += 1.0 + alpha_mean[v, i, nnodes(dg), element] += 1.0 + alpha_mean[v, 1, i, element] += 1.0 + alpha_mean[v, nnodes(dg), i, element] += 1.0 + end + for j in eachnode(dg), i in eachnode(dg) + alpha_mean[v, i, j, element] /= 4 + end + end + end + end + + # Limit pressure à la Kuzmin + if limiter.PressurePositivityLimiterKuzmin + @unpack alpha_pressure, alpha_mean_pressure = limiter.cache.subcell_limiter_coefficients + for j in eachnode(dg), i in 2:nnodes(dg) + bar_state_velocity = bar_states1[2, i, j, element]^2 + + bar_states1[3, i, j, element]^2 + flux_velocity = antidiffusive_flux1_L[2, i, j, element]^2 + + antidiffusive_flux1_L[3, i, j, element]^2 + + Q = lambda1[i, j, element]^2 * + (bar_states1[1, i, j, element] * bar_states1[4, i, j, element] - + 0.5 * bar_state_velocity) + + if limiter.PressurePositivityLimiterKuzminExact + # exact calculation of max(R_ij, R_ji) + R_max = lambda1[i, j, element] * + abs(bar_states1[2, i, j, element] * + antidiffusive_flux1_L[2, i, j, element] + + bar_states1[3, i, j, element] * + antidiffusive_flux1_L[3, i, j, element] - + bar_states1[1, i, j, element] * + antidiffusive_flux1_L[4, i, j, element] - + bar_states1[4, i, j, element] * + antidiffusive_flux1_L[1, i, j, element]) + R_max += max(0, + 0.5 * flux_velocity - + antidiffusive_flux1_L[4, i, j, element] * + antidiffusive_flux1_L[1, i, j, element]) + else + # approximation R_max + R_max = lambda1[i, j, element] * + (sqrt(bar_state_velocity * flux_velocity) + + abs(bar_states1[1, i, j, element] * + antidiffusive_flux1_L[4, i, j, element]) + + abs(bar_states1[4, i, j, element] * + antidiffusive_flux1_L[1, i, j, element])) + R_max += max(0, + 0.5 * flux_velocity - + antidiffusive_flux1_L[4, i, j, element] * + antidiffusive_flux1_L[1, i, j, element]) + end + alpha = 1 # Initialize alpha for plotting + if R_max > Q + alpha = Q / R_max + for v in eachvariable(equations) + antidiffusive_flux1_L[v, i, j, element] *= alpha + end + end + if limiter.Plotting + alpha_pressure[i - 1, j, element] = min(alpha_pressure[i - 1, j, + element], alpha) + alpha_pressure[i, j, element] = min(alpha_pressure[i, j, element], + alpha) + alpha_mean_pressure[i - 1, j, element] += alpha + alpha_mean_pressure[i, j, element] += alpha + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + bar_state_velocity = bar_states2[2, i, j, element]^2 + + bar_states2[3, i, j, element]^2 + flux_velocity = antidiffusive_flux2_L[2, i, j, element]^2 + + antidiffusive_flux2_L[3, i, j, element]^2 + + Q = lambda2[i, j, element]^2 * + (bar_states2[1, i, j, element] * bar_states2[4, i, j, element] - + 0.5 * bar_state_velocity) + + if limiter.PressurePositivityLimiterKuzminExact + # exact calculation of max(R_ij, R_ji) + R_max = lambda2[i, j, element] * + abs(bar_states2[2, i, j, element] * + antidiffusive_flux2_L[2, i, j, element] + + bar_states2[3, i, j, element] * + antidiffusive_flux2_L[3, i, j, element] - + bar_states2[1, i, j, element] * + antidiffusive_flux2_L[4, i, j, element] - + bar_states2[4, i, j, element] * + antidiffusive_flux2_L[1, i, j, element]) + R_max += max(0, + 0.5 * flux_velocity - + antidiffusive_flux2_L[4, i, j, element] * + antidiffusive_flux2_L[1, i, j, element]) + else + # approximation R_max + R_max = lambda2[i, j, element] * + (sqrt(bar_state_velocity * flux_velocity) + + abs(bar_states2[1, i, j, element] * + antidiffusive_flux2_L[4, i, j, element]) + + abs(bar_states2[4, i, j, element] * + antidiffusive_flux2_L[1, i, j, element])) + R_max += max(0, + 0.5 * flux_velocity - + antidiffusive_flux2_L[4, i, j, element] * + antidiffusive_flux2_L[1, i, j, element]) + end + alpha = 1 # Initialize alpha for plotting + if R_max > Q + alpha = Q / R_max + for v in eachvariable(equations) + antidiffusive_flux2_L[v, i, j, element] *= alpha + end + end + if limiter.Plotting + alpha_pressure[i, j - 1, element] = min(alpha_pressure[i, j - 1, + element], alpha) + alpha_pressure[i, j, element] = min(alpha_pressure[i, j, element], + alpha) + alpha_mean_pressure[i, j - 1, element] += alpha + alpha_mean_pressure[i, j, element] += alpha + end + end + if limiter.Plotting + @unpack alpha_mean_pressure = limiter.cache.subcell_limiter_coefficients + # Interfaces contribute with 1.0 + for i in eachnode(dg) + alpha_mean_pressure[i, 1, element] += 1.0 + alpha_mean_pressure[i, nnodes(dg), element] += 1.0 + alpha_mean_pressure[1, i, element] += 1.0 + alpha_mean_pressure[nnodes(dg), i, element] += 1.0 + end + for j in eachnode(dg), i in eachnode(dg) + alpha_mean_pressure[i, j, element] /= 4 + end + end + end + + # Limit entropy + # TODO: This is a very inefficient function. We compute the entropy four times at each node. + # TODO: For now, this only works for Cartesian meshes. + if limiter.SemiDiscEntropyLimiter + for j in eachnode(dg), i in 2:nnodes(dg) + antidiffusive_flux_local = get_node_vars(antidiffusive_flux1_L, equations, + dg, + i, j, element) + u_local = get_node_vars(u, equations, dg, i, j, element) + u_local_m1 = get_node_vars(u, equations, dg, i - 1, j, element) + + # Using mathematic entropy + v_local = cons2entropy(u_local, equations) + v_local_m1 = cons2entropy(u_local_m1, equations) + + q_local = u_local[2] / u_local[1] * entropy(u_local, equations) + q_local_m1 = u_local_m1[2] / u_local_m1[1] * entropy(u_local_m1, equations) + + f_local = flux(u_local, 1, equations) + f_local_m1 = flux(u_local_m1, 1, equations) + + psi_local = dot(v_local, f_local) - q_local + psi_local_m1 = dot(v_local_m1, f_local_m1) - q_local_m1 + + delta_v = v_local - v_local_m1 + delta_psi = psi_local - psi_local_m1 + + entProd_FV = dot(delta_v, view(fstar1, :, i, j)) - delta_psi + delta_entProd = dot(delta_v, antidiffusive_flux_local) + + alpha = 1 # Initialize alpha for plotting + if (entProd_FV + delta_entProd > 0.0) && (delta_entProd != 0.0) + alpha = min(1.0, + (abs(entProd_FV) + eps()) / (abs(delta_entProd) + eps())) + for v in eachvariable(equations) + antidiffusive_flux1_L[v, i, j, element] = alpha * + antidiffusive_flux1_L[v, + i, + j, + element] + end + end + if limiter.Plotting + @unpack alpha_entropy, alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + alpha_entropy[i - 1, j, element] = min(alpha_entropy[i - 1, j, element], + alpha) + alpha_entropy[i, j, element] = min(alpha_entropy[i, j, element], alpha) + alpha_mean_entropy[i - 1, j, element] += alpha + alpha_mean_entropy[i, j, element] += alpha + end + end + + for j in 2:nnodes(dg), i in eachnode(dg) + antidiffusive_flux_local = get_node_vars(antidiffusive_flux2_L, equations, + dg, + i, j, element) + u_local = get_node_vars(u, equations, dg, i, j, element) + u_local_m1 = get_node_vars(u, equations, dg, i, j - 1, element) + + # Using mathematic entropy + v_local = cons2entropy(u_local, equations) + v_local_m1 = cons2entropy(u_local_m1, equations) + + q_local = u_local[3] / u_local[1] * entropy(u_local, equations) + q_local_m1 = u_local_m1[3] / u_local_m1[1] * entropy(u_local_m1, equations) + + f_local = flux(u_local, 2, equations) + f_local_m1 = flux(u_local_m1, 2, equations) + + psi_local = dot(v_local, f_local) - q_local + psi_local_m1 = dot(v_local_m1, f_local_m1) - q_local_m1 + + delta_v = v_local - v_local_m1 + delta_psi = psi_local - psi_local_m1 + + entProd_FV = dot(delta_v, view(fstar2, :, i, j)) - delta_psi + delta_entProd = dot(delta_v, antidiffusive_flux_local) + + alpha = 1 # Initialize alpha for plotting + if (entProd_FV + delta_entProd > 0.0) && (delta_entProd != 0.0) + alpha = min(1.0, + (abs(entProd_FV) + eps()) / (abs(delta_entProd) + eps())) + for v in eachvariable(equations) + antidiffusive_flux2_L[v, i, j, element] = alpha * + antidiffusive_flux2_L[v, + i, + j, + element] + end + end + if limiter.Plotting + @unpack alpha_entropy, alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + alpha_entropy[i, j - 1, element] = min(alpha_entropy[i, j - 1, element], + alpha) + alpha_entropy[i, j, element] = min(alpha_entropy[i, j, element], alpha) + alpha_mean_entropy[i, j - 1, element] += alpha + alpha_mean_entropy[i, j, element] += alpha + end + end + if limiter.Plotting + @unpack alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + # Interfaces contribute with 1.0 + for i in eachnode(dg) + alpha_mean_entropy[i, 1, element] += 1.0 + alpha_mean_entropy[i, nnodes(dg), element] += 1.0 + alpha_mean_entropy[1, i, element] += 1.0 + alpha_mean_entropy[nnodes(dg), i, element] += 1.0 + end + for j in eachnode(dg), i in eachnode(dg) + alpha_mean_entropy[i, j, element] /= 4 + end + end + end + + # Copy antidiffusive fluxes left to antidifussive fluxes right + for j in eachnode(dg), i in 2:nnodes(dg), v in eachvariable(equations) + antidiffusive_flux1_R[v, i, j, element] = antidiffusive_flux1_L[v, i, j, + element] + end + for j in 2:nnodes(dg), i in eachnode(dg), v in eachvariable(equations) + antidiffusive_flux2_R[v, i, j, element] = antidiffusive_flux2_L[v, i, j, + element] + end + + return nothing +end + +@inline function get_boundary_outer_state(u_inner, cache, t, boundary_condition, + orientation_or_normal, direction, equations, + dg, indices...) + if boundary_condition == boundary_condition_slip_wall #boundary_condition_reflecting_euler_wall + if orientation_or_normal isa AbstractArray + u_rotate = rotate_to_x(u_inner, orientation_or_normal, equations) + + return SVector(u_inner[1], + u_inner[2] - 2.0 * u_rotate[2], + u_inner[3] - 2.0 * u_rotate[3], + u_inner[4]) + else # orientation_or_normal isa Integer + return SVector(u_inner[1], -u_inner[2], -u_inner[3], u_inner[4]) + end + elseif boundary_condition == boundary_condition_mixed_dirichlet_wall + x = get_node_coords(cache.elements.node_coordinates, equations, dg, indices...) + if x[1] < 1 / 6 # BoundaryConditionCharacteristic + u_outer = Trixi.characteristic_boundary_value_function(initial_condition_double_mach_reflection, + u_inner, + orientation_or_normal, + direction, x, t, + equations) + + return u_outer + else # x[1] >= 1 / 6 # boundary_condition_slip_wall + if orientation_or_normal isa AbstractArray + u_rotate = rotate_to_x(u_inner, orientation_or_normal, equations) + + return SVector(u_inner[1], + u_inner[2] - 2.0 * u_rotate[2], + u_inner[3] - 2.0 * u_rotate[3], + u_inner[4]) + else # orientation_or_normal isa Integer + return SVector(u_inner[1], -u_inner[2], -u_inner[3], u_inner[4]) + end + end + end + + return u_inner +end + +@inline function get_boundary_outer_state(u_inner, cache, t, + boundary_condition::BoundaryConditionDirichlet, + orientation_or_normal, direction, equations, + dg, indices...) + @unpack node_coordinates = cache.elements + + x = get_node_coords(node_coordinates, equations, dg, indices...) + u_outer = boundary_condition.boundary_value_function(x, t, equations) + + return u_outer +end + +@inline function get_boundary_outer_state(u_inner, cache, t, + boundary_condition::BoundaryConditionCharacteristic, + orientation_or_normal, direction, equations, + dg, indices...) + @unpack node_coordinates = cache.elements + + x = get_node_coords(node_coordinates, equations, dg, indices...) + u_outer = boundary_condition.boundary_value_function(boundary_condition.outer_boundary_value_function, + u_inner, orientation_or_normal, + direction, x, t, equations) + + return u_outer +end end # @muladd diff --git a/src/solvers/dgsem_tree/subcell_limiters.jl b/src/solvers/dgsem_tree/subcell_limiters.jl index 55d402954bf..6a10f92b891 100644 --- a/src/solvers/dgsem_tree/subcell_limiters.jl +++ b/src/solvers/dgsem_tree/subcell_limiters.jl @@ -12,17 +12,43 @@ function create_cache(typ::Type{LimiterType}, create_cache(typ, mesh_equations_solver_cache(semi)...) end +function get_element_variables!(element_variables, limiter::AbstractSubcellLimiter, + ::VolumeIntegralSubcellLimiting) + element_variables[:smooth_indicator_elementwise] = limiter.IndicatorHG.cache.alpha + return nothing +end + """ SubcellLimiterIDP(equations::AbstractEquations, basis; + local_minmax_variables_cons = [], positivity_variables_cons = [], - positivity_correction_factor = 0.1) + positivity_variables_nonlinear = (), + positivity_correction_factor = 0.1, + spec_entropy = false, + math_entropy = false, + bar_states = true, + max_iterations_newton = 10, + newton_tolerances = (1.0e-12, 1.0e-14), + gamma_constant_newton = 2 * ndims(equations), + smoothness_indicator = false, + threshold_smoothness_indicator = 0.1, + variable_smoothness_indicator = density_pressure) Subcell invariant domain preserving (IDP) limiting used with [`VolumeIntegralSubcellLimiting`](@ref) including: -- positivity limiting for conservative variables (`positivity_variables_cons`) +- maximum/minimum Zalesak-type limiting for conservative variables (`local_minmax_variables_cons`) +- positivity limiting for conservative (`positivity_variables_cons`) and non-linear variables (`positivity_variables_nonlinear`) +- one-sided limiting for specific and mathematical entropy (`spec_entropy`, `math_entropy`) -The bounds are calculated using the low-order FV solution. The positivity limiter uses -`positivity_correction_factor` such that `u^new >= positivity_correction_factor * u^FV`. +The bounds can be calculated using the `bar_states` or the low-order FV solution. The positivity +limiter uses `positivity_correction_factor` such that `u^new >= positivity_correction_factor * u^FV`. +The Newton-bisection method for the limiting of non-linear variables uses maximal `max_iterations_newton` +iterations, tolerances `newton_tolerances` and the gamma constant `gamma_constant_newton` +(gamma_constant_newton>=2*d, where d=#dimensions). + +A hard-switch [`IndicatorHennemannGassner`](@ref) can be activated (`smoothness_indicator`) with +`variable_smoothness_indicator`, which disables subcell blending for element-wise +indicator values <= `threshold_smoothness_indicator`. !!! note This limiter and the correction callback [`SubcellLimiterIDPCorrection`](@ref) only work together. @@ -40,71 +66,379 @@ The bounds are calculated using the low-order FV solution. The positivity limite !!! warning "Experimental implementation" This is an experimental feature and may change in future releases. """ -struct SubcellLimiterIDP{RealT <: Real, Cache} <: AbstractSubcellLimiter +struct SubcellLimiterIDP{RealT <: Real, LimitingVariablesNonlinear, + Cache, Indicator} <: AbstractSubcellLimiter + local_minmax::Bool + local_minmax_variables_cons::Vector{Int} # Local mininum/maximum principles for conservative variables positivity::Bool positivity_variables_cons::Vector{Int} # Positivity for conservative variables + positivity_variables_nonlinear::LimitingVariablesNonlinear # Positivity for nonlinear variables positivity_correction_factor::RealT + spec_entropy::Bool + math_entropy::Bool + bar_states::Bool cache::Cache + max_iterations_newton::Int + newton_tolerances::Tuple{RealT, RealT} # Relative and absolute tolerances for Newton's method + gamma_constant_newton::RealT # Constant for the subcell limiting of convex (nonlinear) constraints + smoothness_indicator::Bool + threshold_smoothness_indicator::RealT + IndicatorHG::Indicator end -# this method is used when the indicator is constructed as for shock-capturing volume integrals +# this method is used when the limiter is constructed as for shock-capturing volume integrals function SubcellLimiterIDP(equations::AbstractEquations, basis; + local_minmax_variables_cons = [], positivity_variables_cons = [], - positivity_correction_factor = 0.1) - positivity = (length(positivity_variables_cons) > 0) + positivity_variables_nonlinear = (), + positivity_correction_factor = 0.1, + spec_entropy = false, + math_entropy = false, + bar_states = true, + max_iterations_newton = 10, + newton_tolerances = (1.0e-12, 1.0e-14), + gamma_constant_newton = 2 * ndims(equations), + smoothness_indicator = false, + threshold_smoothness_indicator = 0.1, + variable_smoothness_indicator = density_pressure) + local_minmax = (length(local_minmax_variables_cons) > 0) + positivity = (length(positivity_variables_cons) + + length(positivity_variables_nonlinear) > 0) + if math_entropy && spec_entropy + error("Only one of the two can be selected: math_entropy/spec_entropy") + end - bound_keys = Tuple(Symbol(string(v), "_min") for v in positivity_variables_cons) + bound_keys = () + if local_minmax + for v in local_minmax_variables_cons + v_string = string(v) + bound_keys = (bound_keys..., Symbol(v_string, "_min"), + Symbol(v_string, "_max")) + end + end + if spec_entropy + bound_keys = (bound_keys..., :spec_entropy_min) + end + if math_entropy + bound_keys = (bound_keys..., :math_entropy_max) + end + for v in positivity_variables_cons + if !(v in local_minmax_variables_cons) + bound_keys = (bound_keys..., Symbol(string(v), "_min")) + end + end + for variable in positivity_variables_nonlinear + bound_keys = (bound_keys..., Symbol(string(variable), "_min")) + end - cache = create_cache(SubcellLimiterIDP, equations, basis, bound_keys) + cache = create_cache(SubcellLimiterIDP, equations, basis, bound_keys, bar_states) - SubcellLimiterIDP{typeof(positivity_correction_factor), typeof(cache)}(positivity, - positivity_variables_cons, - positivity_correction_factor, - cache) + if smoothness_indicator + IndicatorHG = IndicatorHennemannGassner(equations, basis, alpha_max = 1.0, + alpha_smooth = false, + variable = variable_smoothness_indicator) + else + IndicatorHG = nothing + end + SubcellLimiterIDP{typeof(positivity_correction_factor), + typeof(positivity_variables_nonlinear), + typeof(cache), typeof(IndicatorHG)}(local_minmax, + local_minmax_variables_cons, + positivity, + positivity_variables_cons, + positivity_variables_nonlinear, + positivity_correction_factor, + spec_entropy, + math_entropy, + bar_states, + cache, + max_iterations_newton, + newton_tolerances, + gamma_constant_newton, + smoothness_indicator, + threshold_smoothness_indicator, + IndicatorHG) end function Base.show(io::IO, limiter::SubcellLimiterIDP) @nospecialize limiter # reduce precompilation time - @unpack positivity = limiter + @unpack local_minmax, positivity, spec_entropy, math_entropy = limiter print(io, "SubcellLimiterIDP(") - if !(positivity) + if !(local_minmax || positivity || spec_entropy || math_entropy) print(io, "No limiter selected => pure DG method") else print(io, "limiter=(") - positivity && print(io, "positivity") + local_minmax && print(io, "min/max limiting, ") + positivity && print(io, "positivity, ") + spec_entropy && print(io, "specific entropy, ") + math_entropy && print(io, "mathematical entropy, ") print(io, "), ") end + limiter.smoothness_indicator && + print(io, ", Smoothness indicator: ", limiter.IndicatorHG, + " with threshold ", limiter.threshold_smoothness_indicator, "), ") + print(io, + "Local bounds with $(limiter.bar_states ? "Bar States" : "FV solution")") print(io, ")") end function Base.show(io::IO, ::MIME"text/plain", limiter::SubcellLimiterIDP) @nospecialize limiter # reduce precompilation time - @unpack positivity = limiter + @unpack local_minmax, positivity, spec_entropy, math_entropy = limiter if get(io, :compact, false) show(io, limiter) else - if !(positivity) + if !(local_minmax || positivity || spec_entropy || math_entropy) setup = ["limiter" => "No limiter selected => pure DG method"] else setup = ["limiter" => ""] + if local_minmax + setup = [ + setup..., + "" => "local maximum/minimum bounds for conservative variables $(limiter.local_minmax_variables_cons)", + ] + end if positivity - string = "positivity with conservative variables $(limiter.positivity_variables_cons)" + string = "positivity for conservative variables $(limiter.positivity_variables_cons) and $(limiter.positivity_variables_nonlinear)" setup = [setup..., "" => string] setup = [ setup..., "" => " positivity correction factor = $(limiter.positivity_correction_factor)", ] end + if spec_entropy + setup = [setup..., "" => "local minimum bound for specific entropy"] + end + if math_entropy + setup = [setup..., "" => "local maximum bound for mathematical entropy"] + end + setup = [ + setup..., + "Local bounds" => (limiter.bar_states ? "Bar States" : "FV solution"), + ] + if limiter.smoothness_indicator + setup = [ + setup..., + "Smoothness indicator" => "$(limiter.IndicatorHG) using threshold $(limiter.threshold_smoothness_indicator)", + ] + end + summary_box(io, "SubcellLimiterIDP", setup) end - summary_box(io, "SubcellLimiterIDP", setup) end end function get_node_variables!(node_variables, limiter::SubcellLimiterIDP, ::VolumeIntegralSubcellLimiting, equations) - node_variables[:limiting_coefficient] = limiter.cache.subcell_limiter_coefficients.alpha + node_variables[:alpha_limiter] = limiter.cache.subcell_limiter_coefficients.alpha + + return nothing +end + +""" + SubcellLimiterMCL(equations::AbstractEquations, basis; + DensityLimiter = true, + DensityAlphaForAll = false, + SequentialLimiter = true, + ConservativeLimiter = false, + PressurePositivityLimiterKuzmin = false, + PressurePositivityLimiterKuzminExact = true, + DensityPositivityLimiter = false, + DensityPositivityCorrectionFactor = 0.0, + SemiDiscEntropyLimiter = false, + smoothness_indicator = false, + threshold_smoothness_indicator = 0.1, + variable_smoothness_indicator = density_pressure, + Plotting = true) + +Subcell monolithic convex limiting (MCL) used with [`VolumeIntegralSubcellLimiting`](@ref) including: +- local two-sided limiting for `cons(1)` (`DensityLimiter`) +- transfer amount of `DensityLimiter` to all quantities (`DensityAlphaForAll`) +- local two-sided limiting for variables `phi:=cons(i)/cons(1)` (`SequentialLimiter`) +- local two-sided limiting for conservative variables (`ConservativeLimiter`) +- positivity limiting for `cons(1)` (`DensityPositivityLimiter`) and pressure (`PressurePositivityLimiterKuzmin`) +- semidiscrete entropy fix (`SemiDiscEntropyLimiter`) + +The pressure positivity limiting preserves a sharp version (`PressurePositivityLimiterKuzminExact`) +and a more cautious one. The density positivity limiter uses a `DensityPositivityCorrectionFactor` +such that `u^new >= positivity_correction_factor * u^FV`. All additional analyses for plotting routines +can be disabled via `Plotting=false` (see `save_alpha` and `update_alpha_max_avg!`). + +A hard-switch [`IndicatorHennemannGassner`](@ref) can be activated (`smoothness_indicator`) with +`variable_smoothness_indicator`, which disables subcell blending for element-wise +indicator values <= `threshold_smoothness_indicator`. + +## References + +- Rueda-Ramírez, Bolm, Kuzmin, Gassner (2023) + Monolithic Convex Limiting for Legendre-Gauss-Lobatto Discontinuous Galerkin Spectral Element Methods + [arXiv:2303.00374](https://doi.org/10.48550/arXiv.2303.00374) +- Kuzmin (2020) + Monolithic convex limiting for continuous finite element discretizations of hyperbolic conservation laws + [DOI: 10.1016/j.cma.2019.112804](https://doi.org/10.1016/j.cma.2019.112804) + +!!! warning "Experimental implementation" + This is an experimental feature and may change in future releases. +""" +struct SubcellLimiterMCL{RealT <: Real, Cache, Indicator} <: AbstractSubcellLimiter + cache::Cache + DensityLimiter::Bool # Impose local maximum/minimum for cons(1) based on bar states + DensityAlphaForAll::Bool # Use the cons(1) blending coefficient for all quantities + SequentialLimiter::Bool # Impose local maximum/minimum for variables phi:=cons(i)/cons(1) i 2:nvariables based on bar states + ConservativeLimiter::Bool # Impose local maximum/minimum for conservative variables 2:nvariables based on bar states + PressurePositivityLimiterKuzmin::Bool # Impose positivity for pressure â la Kuzmin + PressurePositivityLimiterKuzminExact::Bool # Only for PressurePositivityLimiterKuzmin=true: Use the exact calculation of alpha + DensityPositivityLimiter::Bool # Impose positivity for cons(1) + DensityPositivityCorrectionFactor::RealT # Correction Factor for DensityPositivityLimiter in [0,1) + SemiDiscEntropyLimiter::Bool # synchronized semidiscrete entropy fix + smoothness_indicator::Bool # activates smoothness indicator: IndicatorHennemannGassner + threshold_smoothness_indicator::RealT # threshold for smoothness indicator + IndicatorHG::Indicator + Plotting::Bool +end + +# this method is used when the limiter is constructed as for shock-capturing volume integrals +function SubcellLimiterMCL(equations::AbstractEquations, basis; + DensityLimiter = true, + DensityAlphaForAll = false, + SequentialLimiter = true, + ConservativeLimiter = false, + PressurePositivityLimiterKuzmin = false, + PressurePositivityLimiterKuzminExact = true, + DensityPositivityLimiter = false, + DensityPositivityCorrectionFactor = 0.0, + SemiDiscEntropyLimiter = false, + smoothness_indicator = false, + threshold_smoothness_indicator = 0.1, + variable_smoothness_indicator = density_pressure, + Plotting = true) + if SequentialLimiter && ConservativeLimiter + error("Only one of the two can be selected: SequentialLimiter/ConservativeLimiter") + end + cache = create_cache(SubcellLimiterMCL, equations, basis, + PressurePositivityLimiterKuzmin) + if smoothness_indicator + IndicatorHG = IndicatorHennemannGassner(equations, basis, alpha_smooth = false, + variable = variable_smoothness_indicator) + else + IndicatorHG = nothing + end + SubcellLimiterMCL{typeof(threshold_smoothness_indicator), typeof(cache), + typeof(IndicatorHG)}(cache, + DensityLimiter, DensityAlphaForAll, + SequentialLimiter, ConservativeLimiter, + PressurePositivityLimiterKuzmin, + PressurePositivityLimiterKuzminExact, + DensityPositivityLimiter, + DensityPositivityCorrectionFactor, + SemiDiscEntropyLimiter, + smoothness_indicator, + threshold_smoothness_indicator, IndicatorHG, + Plotting) +end + +function Base.show(io::IO, limiter::SubcellLimiterMCL) + @nospecialize limiter # reduce precompilation time + + print(io, "SubcellLimiterMCL(") + limiter.DensityLimiter && print(io, "; dens") + limiter.DensityAlphaForAll && print(io, "; dens alpha ∀") + limiter.SequentialLimiter && print(io, "; seq") + limiter.ConservativeLimiter && print(io, "; cons") + if limiter.PressurePositivityLimiterKuzmin + print(io, + "; $(limiter.PressurePositivityLimiterKuzminExact ? "pres (Kuzmin ex)" : "pres (Kuzmin)")") + end + limiter.DensityPositivityLimiter && print(io, "; dens pos") + if limiter.DensityPositivityCorrectionFactor != 0 + print(io, + " with correction factor $(limiter.DensityPositivityCorrectionFactor)") + end + limiter.SemiDiscEntropyLimiter && print(io, "; semid. entropy") + limiter.smoothness_indicator && + print(io, "; Smoothness indicator: ", limiter.IndicatorHG, + " with threshold ", limiter.threshold_smoothness_indicator) + print(io, ")") +end + +function Base.show(io::IO, ::MIME"text/plain", limiter::SubcellLimiterMCL) + @nospecialize limiter # reduce precompilation time + @unpack DensityLimiter, DensityAlphaForAll, SequentialLimiter, ConservativeLimiter, + PressurePositivityLimiterKuzminExact, DensityPositivityLimiter, SemiDiscEntropyLimiter = limiter + + if get(io, :compact, false) + show(io, limiter) + else + setup = ["limiter" => ""] + DensityLimiter && (setup = [setup..., "" => "DensityLimiter"]) + DensityAlphaForAll && (setup = [setup..., "" => "DensityAlphaForAll"]) + SequentialLimiter && (setup = [setup..., "" => "SequentialLimiter"]) + ConservativeLimiter && (setup = [setup..., "" => "ConservativeLimiter"]) + if limiter.PressurePositivityLimiterKuzmin + setup = [ + setup..., + "" => "PressurePositivityLimiterKuzmin $(PressurePositivityLimiterKuzminExact ? "(exact)" : "")", + ] + end + if DensityPositivityLimiter + if limiter.DensityPositivityCorrectionFactor != 0.0 + setup = [ + setup..., + "" => "DensityPositivityLimiter with correction factor $(limiter.DensityPositivityCorrectionFactor)", + ] + else + setup = [setup..., "" => "DensityPositivityLimiter"] + end + end + SemiDiscEntropyLimiter && (setup = [setup..., "" => "SemiDiscEntropyLimiter"]) + if limiter.smoothness_indicator + setup = [ + setup..., + "Smoothness indicator" => "$(limiter.IndicatorHG) using threshold $(limiter.threshold_smoothness_indicator)", + ] + end + summary_box(io, "SubcellLimiterMCL", setup) + end +end + +function get_node_variables!(node_variables, limiter::SubcellLimiterMCL, + ::VolumeIntegralSubcellLimiting, equations) + if !limiter.Plotting + return nothing + end + @unpack alpha = limiter.cache.subcell_limiter_coefficients + variables = varnames(cons2cons, equations) + for v in eachvariable(equations) + s = Symbol("alpha_", variables[v]) + node_variables[s] = alpha[v, ntuple(_ -> :, size(alpha, 2) + 1)...] + end + + if limiter.PressurePositivityLimiterKuzmin + @unpack alpha_pressure = limiter.cache.subcell_limiter_coefficients + node_variables[:alpha_pressure] = alpha_pressure + end + + if limiter.SemiDiscEntropyLimiter + @unpack alpha_entropy = limiter.cache.subcell_limiter_coefficients + node_variables[:alpha_entropy] = alpha_entropy + end + + for v in eachvariable(equations) + @unpack alpha_mean = limiter.cache.subcell_limiter_coefficients + s = Symbol("alpha_mean_", variables[v]) + node_variables[s] = copy(alpha_mean[v, ntuple(_ -> :, size(alpha, 2) + 1)...]) + end + + if limiter.PressurePositivityLimiterKuzmin + @unpack alpha_mean_pressure = limiter.cache.subcell_limiter_coefficients + node_variables[:alpha_mean_pressure] = alpha_mean_pressure + end + + if limiter.SemiDiscEntropyLimiter + @unpack alpha_mean_entropy = limiter.cache.subcell_limiter_coefficients + node_variables[:alpha_mean_entropy] = alpha_mean_entropy + end return nothing end diff --git a/src/solvers/dgsem_tree/subcell_limiters_2d.jl b/src/solvers/dgsem_tree/subcell_limiters_2d.jl index 0a72b79ea3f..c724ada5041 100644 --- a/src/solvers/dgsem_tree/subcell_limiters_2d.jl +++ b/src/solvers/dgsem_tree/subcell_limiters_2d.jl @@ -7,12 +7,20 @@ # this method is used when the limiter is constructed as for shock-capturing volume integrals function create_cache(limiter::Type{SubcellLimiterIDP}, equations::AbstractEquations{2}, - basis::LobattoLegendreBasis, bound_keys) + basis::LobattoLegendreBasis, bound_keys, bar_states) subcell_limiter_coefficients = Trixi.ContainerSubcellLimiterIDP2D{real(basis) }(0, nnodes(basis), bound_keys) + cache = (;) + if bar_states + container_bar_states = Trixi.ContainerBarStates{real(basis)}(0, + nvariables(equations), + nnodes(basis)) + cache = (; cache..., container_bar_states) + end + # Memory for bounds checking routine with `BoundsCheckCallback`. # The first entry of each vector contains the maximum deviation since the last export. # The second one contains the total maximum deviation. @@ -21,7 +29,7 @@ function create_cache(limiter::Type{SubcellLimiterIDP}, equations::AbstractEquat idp_bounds_delta[key] = zeros(real(basis), 2) end - return (; subcell_limiter_coefficients, idp_bounds_delta) + return (; cache..., subcell_limiter_coefficients, idp_bounds_delta) end function (limiter::SubcellLimiterIDP)(u::AbstractArray{<:Any, 4}, semi, dg::DGSEM, t, @@ -29,14 +37,36 @@ function (limiter::SubcellLimiterIDP)(u::AbstractArray{<:Any, 4}, semi, dg::DGSE kwargs...) @unpack alpha = limiter.cache.subcell_limiter_coefficients alpha .= zero(eltype(alpha)) + if limiter.smoothness_indicator + elements = semi.cache.element_ids_dgfv + else + elements = eachelement(dg, semi.cache) + end + if limiter.local_minmax + @trixi_timeit timer() "local min/max limiting" idp_local_minmax!(alpha, + limiter, u, + t, dt, + semi, elements) + end if limiter.positivity - @trixi_timeit timer() "positivity" idp_positivity!(alpha, limiter, u, dt, semi) + @trixi_timeit timer() "positivity" idp_positivity!(alpha, limiter, u, dt, + semi, elements) + end + if limiter.spec_entropy + @trixi_timeit timer() "spec_entropy" idp_spec_entropy!(alpha, limiter, u, t, + dt, + semi, elements) + end + if limiter.math_entropy + @trixi_timeit timer() "math_entropy" idp_math_entropy!(alpha, limiter, u, t, + dt, + semi, elements) end # Calculate alpha1 and alpha2 @unpack alpha1, alpha2 = limiter.cache.subcell_limiter_coefficients - @threaded for element in eachelement(dg, semi.cache) + @threaded for element in elements for j in eachnode(dg), i in 2:nnodes(dg) alpha1[i, j, element] = max(alpha[i - 1, j, element], alpha[i, j, element]) end @@ -52,16 +82,381 @@ function (limiter::SubcellLimiterIDP)(u::AbstractArray{<:Any, 4}, semi, dg::DGSE return nothing end -@inline function idp_positivity!(alpha, limiter, u, dt, semi) +@inline function calc_bounds_2sided!(var_min, var_max, variable, u, t, semi) + mesh, equations, dg, cache = mesh_equations_solver_cache(semi) + # Calc bounds inside elements + @threaded for element in eachelement(dg, cache) + var_min[:, :, element] .= typemax(eltype(var_min)) + var_max[:, :, element] .= typemin(eltype(var_max)) + # Calculate bounds at Gauss-Lobatto nodes using u + for j in eachnode(dg), i in eachnode(dg) + var = u[variable, i, j, element] + var_min[i, j, element] = min(var_min[i, j, element], var) + var_max[i, j, element] = max(var_max[i, j, element], var) + + if i > 1 + var_min[i - 1, j, element] = min(var_min[i - 1, j, element], var) + var_max[i - 1, j, element] = max(var_max[i - 1, j, element], var) + end + if i < nnodes(dg) + var_min[i + 1, j, element] = min(var_min[i + 1, j, element], var) + var_max[i + 1, j, element] = max(var_max[i + 1, j, element], var) + end + if j > 1 + var_min[i, j - 1, element] = min(var_min[i, j - 1, element], var) + var_max[i, j - 1, element] = max(var_max[i, j - 1, element], var) + end + if j < nnodes(dg) + var_min[i, j + 1, element] = min(var_min[i, j + 1, element], var) + var_max[i, j + 1, element] = max(var_max[i, j + 1, element], var) + end + end + end + + # Values at element boundary + calc_bounds_2sided_interface!(var_min, var_max, variable, u, t, semi, mesh) +end + +@inline function calc_bounds_2sided_interface!(var_min, var_max, variable, u, t, semi, + mesh::TreeMesh2D) + _, equations, dg, cache = mesh_equations_solver_cache(semi) + @unpack boundary_conditions = semi + # Calc bounds at interfaces and periodic boundaries + for interface in eachinterface(dg, cache) + # Get neighboring element ids + left = cache.interfaces.neighbor_ids[1, interface] + right = cache.interfaces.neighbor_ids[2, interface] + + orientation = cache.interfaces.orientations[interface] + + for i in eachnode(dg) + index_left = (nnodes(dg), i) + index_right = (1, i) + if orientation == 2 + index_left = reverse(index_left) + index_right = reverse(index_right) + end + var_left = u[variable, index_left..., left] + var_right = u[variable, index_right..., right] + + var_min[index_right..., right] = min(var_min[index_right..., right], + var_left) + var_max[index_right..., right] = max(var_max[index_right..., right], + var_left) + + var_min[index_left..., left] = min(var_min[index_left..., left], var_right) + var_max[index_left..., left] = max(var_max[index_left..., left], var_right) + end + end + + # Calc bounds at physical boundaries + for boundary in eachboundary(dg, cache) + element = cache.boundaries.neighbor_ids[boundary] + orientation = cache.boundaries.orientations[boundary] + neighbor_side = cache.boundaries.neighbor_sides[boundary] + + for i in eachnode(dg) + if neighbor_side == 2 # Element is on the right, boundary on the left + index = (1, i) + boundary_index = 1 + else # Element is on the left, boundary on the right + index = (nnodes(dg), i) + boundary_index = 2 + end + if orientation == 2 + index = reverse(index) + boundary_index += 2 + end + u_inner = get_node_vars(u, equations, dg, index..., element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[boundary_index], + orientation, boundary_index, + equations, dg, index..., element) + var_outer = u_outer[variable] + + var_min[index..., element] = min(var_min[index..., element], var_outer) + var_max[index..., element] = max(var_max[index..., element], var_outer) + end + end + + return nothing +end + +@inline function calc_bounds_1sided!(var_minmax, minmax, typeminmax, variable, u, t, + semi) + mesh, equations, dg, cache = mesh_equations_solver_cache(semi) + # Calc bounds inside elements + @threaded for element in eachelement(dg, cache) + var_minmax[:, :, element] .= typeminmax(eltype(var_minmax)) + + # Calculate bounds at Gauss-Lobatto nodes using u + for j in eachnode(dg), i in eachnode(dg) + var = variable(get_node_vars(u, equations, dg, i, j, element), equations) + var_minmax[i, j, element] = minmax(var_minmax[i, j, element], var) + + if i > 1 + var_minmax[i - 1, j, element] = minmax(var_minmax[i - 1, j, element], + var) + end + if i < nnodes(dg) + var_minmax[i + 1, j, element] = minmax(var_minmax[i + 1, j, element], + var) + end + if j > 1 + var_minmax[i, j - 1, element] = minmax(var_minmax[i, j - 1, element], + var) + end + if j < nnodes(dg) + var_minmax[i, j + 1, element] = minmax(var_minmax[i, j + 1, element], + var) + end + end + end + + # Values at element boundary + calc_bounds_1sided_interface!(var_minmax, minmax, variable, u, t, semi, mesh) +end + +@inline function calc_bounds_1sided_interface!(var_minmax, minmax, variable, u, t, semi, + mesh::TreeMesh2D) + _, equations, dg, cache = mesh_equations_solver_cache(semi) + @unpack boundary_conditions = semi + # Calc bounds at interfaces and periodic boundaries + for interface in eachinterface(dg, cache) + # Get neighboring element ids + left = cache.interfaces.neighbor_ids[1, interface] + right = cache.interfaces.neighbor_ids[2, interface] + + orientation = cache.interfaces.orientations[interface] + + if orientation == 1 + for j in eachnode(dg) + var_left = variable(get_node_vars(u, equations, dg, nnodes(dg), j, + left), equations) + var_right = variable(get_node_vars(u, equations, dg, 1, j, right), + equations) + + var_minmax[1, j, right] = minmax(var_minmax[1, j, right], var_left) + var_minmax[nnodes(dg), j, left] = minmax(var_minmax[nnodes(dg), j, + left], var_right) + end + else # orientation == 2 + for i in eachnode(dg) + var_left = variable(get_node_vars(u, equations, dg, i, nnodes(dg), + left), equations) + var_right = variable(get_node_vars(u, equations, dg, i, 1, right), + equations) + + var_minmax[i, 1, right] = minmax(var_minmax[i, 1, right], var_left) + var_minmax[i, nnodes(dg), left] = minmax(var_minmax[i, nnodes(dg), + left], var_right) + end + end + end + + # Calc bounds at physical boundaries + for boundary in eachboundary(dg, cache) + element = cache.boundaries.neighbor_ids[boundary] + orientation = cache.boundaries.orientations[boundary] + neighbor_side = cache.boundaries.neighbor_sides[boundary] + + if orientation == 1 + if neighbor_side == 2 # Element is on the right, boundary on the left + for j in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, 1, j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[1], + orientation, 1, + equations, dg, 1, j, element) + var_outer = variable(u_outer, equations) + + var_minmax[1, j, element] = minmax(var_minmax[1, j, element], + var_outer) + end + else # Element is on the left, boundary on the right + for j in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, nnodes(dg), j, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[2], + orientation, 2, + equations, dg, nnodes(dg), j, + element) + var_outer = variable(u_outer, equations) + + var_minmax[nnodes(dg), j, element] = minmax(var_minmax[nnodes(dg), + j, element], + var_outer) + end + end + else # orientation == 2 + if neighbor_side == 2 # Element is on the right, boundary on the left + for i in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, i, 1, element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[3], + orientation, 3, + equations, dg, i, 1, element) + var_outer = variable(u_outer, equations) + + var_minmax[i, 1, element] = minmax(var_minmax[i, 1, element], + var_outer) + end + else # Element is on the left, boundary on the right + for i in eachnode(dg) + u_inner = get_node_vars(u, equations, dg, i, nnodes(dg), element) + u_outer = get_boundary_outer_state(u_inner, cache, t, + boundary_conditions[4], + orientation, 4, + equations, dg, i, nnodes(dg), + element) + var_outer = variable(u_outer, equations) + + var_minmax[i, nnodes(dg), element] = minmax(var_minmax[i, + nnodes(dg), + element], + var_outer) + end + end + end + end + + return nothing +end + +@inline function idp_local_minmax!(alpha, limiter, u, t, dt, semi, elements) + for variable in limiter.local_minmax_variables_cons + idp_local_minmax!(alpha, limiter, u, t, dt, semi, elements, variable) + end + + return nothing +end + +@inline function idp_local_minmax!(alpha, limiter, u, t, dt, semi, elements, variable) + mesh, _, dg, cache = mesh_equations_solver_cache(semi) + (; variable_bounds) = limiter.cache.subcell_limiter_coefficients + var_min = variable_bounds[Symbol(string(variable), "_min")] + var_max = variable_bounds[Symbol(string(variable), "_max")] + if !limiter.bar_states + calc_bounds_2sided!(var_min, var_max, variable, u, t, semi) + end + + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + @unpack inverse_weights = dg.basis + + @threaded for element in elements + if mesh isa TreeMesh + inverse_jacobian = cache.elements.inverse_jacobian[element] + end + for j in eachnode(dg), i in eachnode(dg) + if mesh isa StructuredMesh + inverse_jacobian = cache.elements.inverse_jacobian[i, j, element] + end + var = u[variable, i, j, element] + # Real Zalesak type limiter + # * Zalesak (1979). "Fully multidimensional flux-corrected transport algorithms for fluids" + # * Kuzmin et al. (2010). "Failsafe flux limiting and constrained data projections for equations of gas dynamics" + # Note: The Zalesak limiter has to be computed, even if the state is valid, because the correction is + # for each interface, not each node + + Qp = max(0, (var_max[i, j, element] - var) / dt) + Qm = min(0, (var_min[i, j, element] - var) / dt) + + # Calculate Pp and Pm + # Note: Boundaries of antidiffusive_flux1/2 are constant 0, so they make no difference here. + val_flux1_local = inverse_weights[i] * + antidiffusive_flux1_R[variable, i, j, element] + val_flux1_local_ip1 = -inverse_weights[i] * + antidiffusive_flux1_L[variable, i + 1, j, element] + val_flux2_local = inverse_weights[j] * + antidiffusive_flux2_R[variable, i, j, element] + val_flux2_local_jp1 = -inverse_weights[j] * + antidiffusive_flux2_L[variable, i, j + 1, element] + + Pp = max(0, val_flux1_local) + max(0, val_flux1_local_ip1) + + max(0, val_flux2_local) + max(0, val_flux2_local_jp1) + Pm = min(0, val_flux1_local) + min(0, val_flux1_local_ip1) + + min(0, val_flux2_local) + min(0, val_flux2_local_jp1) + + Qp = max(0, (var_max[i, j, element] - var) / dt) + Qm = min(0, (var_min[i, j, element] - var) / dt) + + Pp = inverse_jacobian * Pp + Pm = inverse_jacobian * Pm + + # Compute blending coefficient avoiding division by zero + # (as in paper of [Guermond, Nazarov, Popov, Thomas] (4.8)) + Qp = abs(Qp) / + (abs(Pp) + eps(typeof(Qp)) * 100 * abs(var_max[i, j, element])) + Qm = abs(Qm) / + (abs(Pm) + eps(typeof(Qm)) * 100 * abs(var_max[i, j, element])) + + # Calculate alpha at nodes + alpha[i, j, element] = max(alpha[i, j, element], 1 - min(1, Qp, Qm)) + end + end + + return nothing +end + +@inline function idp_spec_entropy!(alpha, limiter, u, t, dt, semi, elements) + mesh, equations, dg, cache = mesh_equations_solver_cache(semi) + (; variable_bounds) = limiter.cache.subcell_limiter_coefficients + s_min = variable_bounds[:spec_entropy_min] + if !limiter.bar_states + calc_bounds_1sided!(s_min, min, typemax, entropy_spec, u, t, semi) + end + + # Perform Newton's bisection method to find new alpha + @threaded for element in elements + for j in eachnode(dg), i in eachnode(dg) + u_local = get_node_vars(u, equations, dg, i, j, element) + newton_loops_alpha!(alpha, s_min[i, j, element], u_local, i, j, element, + entropy_spec, initial_check_entropy_spec, + final_check_standard, + dt, mesh, equations, dg, cache, limiter) + end + end + + return nothing +end + +@inline function idp_math_entropy!(alpha, limiter, u, t, dt, semi, elements) + mesh, equations, dg, cache = mesh_equations_solver_cache(semi) + (; variable_bounds) = limiter.cache.subcell_limiter_coefficients + s_max = variable_bounds[:math_entropy_max] + if !limiter.bar_states + calc_bounds_1sided!(s_max, max, typemin, entropy_math, u, t, semi) + end + + # Perform Newton's bisection method to find new alpha + @threaded for element in elements + for j in eachnode(dg), i in eachnode(dg) + u_local = get_node_vars(u, equations, dg, i, j, element) + newton_loops_alpha!(alpha, s_max[i, j, element], u_local, i, j, element, + entropy_math, initial_check_entropy_math, + final_check_standard, + dt, mesh, equations, dg, cache, limiter) + end + end + + return nothing +end + +@inline function idp_positivity!(alpha, limiter, u, dt, semi, elements) # Conservative variables for variable in limiter.positivity_variables_cons - idp_positivity!(alpha, limiter, u, dt, semi, variable) + idp_positivity!(alpha, limiter, u, dt, semi, elements, variable) + end + + # Nonlinear variables + for variable in limiter.positivity_variables_nonlinear + idp_positivity_nonlinear!(alpha, limiter, u, dt, semi, elements, variable) end return nothing end -@inline function idp_positivity!(alpha, limiter, u, dt, semi, variable) +@inline function idp_positivity!(alpha, limiter, u, dt, semi, elements, variable) mesh, equations, dg, cache = mesh_equations_solver_cache(semi) (; antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R) = cache.antidiffusive_fluxes (; inverse_weights) = dg.basis @@ -70,16 +465,27 @@ end (; variable_bounds) = limiter.cache.subcell_limiter_coefficients var_min = variable_bounds[Symbol(string(variable), "_min")] - @threaded for element in eachelement(dg, semi.cache) - inverse_jacobian = cache.elements.inverse_jacobian[element] + @threaded for element in elements + if mesh isa TreeMesh + inverse_jacobian = cache.elements.inverse_jacobian[element] + end for j in eachnode(dg), i in eachnode(dg) + if mesh isa StructuredMesh + inverse_jacobian = cache.elements.inverse_jacobian[i, j, element] + end + var = u[variable, i, j, element] if var < 0 error("Safe $variable is not safe. element=$element, node: $i $j, value=$var") end # Compute bound - var_min[i, j, element] = positivity_correction_factor * var + if limiter.local_minmax + var_min[i, j, element] = max(var_min[i, j, element], + positivity_correction_factor * var) + else + var_min[i, j, element] = positivity_correction_factor * var + end # Real one-sided Zalesak-type limiter # * Zalesak (1979). "Fully multidimensional flux-corrected transport algorithms for fluids" @@ -114,4 +520,216 @@ end return nothing end + +@inline function idp_positivity_nonlinear!(alpha, limiter, u, dt, semi, elements, + variable) + mesh, equations, dg, cache = mesh_equations_solver_cache(semi) + (; positivity_correction_factor) = limiter + + (; variable_bounds) = limiter.cache.subcell_limiter_coefficients + var_min = variable_bounds[Symbol(string(variable), "_min")] + + @threaded for element in elements + for j in eachnode(dg), i in eachnode(dg) + # Compute bound + u_local = get_node_vars(u, equations, dg, i, j, element) + var = variable(u_local, equations) + if var < 0 + error("Safe $variable is not safe. element=$element, node: $i $j, value=$var") + end + var_min[i, j, element] = positivity_correction_factor * var + + # Perform Newton's bisection method to find new alpha + newton_loops_alpha!(alpha, var_min[i, j, element], u_local, i, j, element, + variable, initial_check_nonnegative, + final_check_nonnegative, + dt, mesh, equations, dg, cache, limiter) + end + end + + return nothing +end + +@inline function newton_loops_alpha!(alpha, bound, u, i, j, element, variable, + initial_check, final_check, dt, mesh, equations, + dg, cache, limiter) + @unpack inverse_weights = dg.basis + @unpack antidiffusive_flux1_L, antidiffusive_flux2_L, antidiffusive_flux1_R, antidiffusive_flux2_R = cache.antidiffusive_fluxes + if mesh isa TreeMesh + inverse_jacobian = cache.elements.inverse_jacobian[element] + else # mesh isa StructuredMesh + inverse_jacobian = cache.elements.inverse_jacobian[i, j, element] + end + + @unpack gamma_constant_newton = limiter + + # negative xi direction + antidiffusive_flux = gamma_constant_newton * inverse_jacobian * inverse_weights[i] * + get_node_vars(antidiffusive_flux1_R, equations, dg, i, j, + element) + newton_loop!(alpha, bound, u, i, j, element, variable, initial_check, final_check, + equations, dt, limiter, antidiffusive_flux) + + # positive xi direction + antidiffusive_flux = -gamma_constant_newton * inverse_jacobian * + inverse_weights[i] * + get_node_vars(antidiffusive_flux1_L, equations, dg, i + 1, j, + element) + newton_loop!(alpha, bound, u, i, j, element, variable, initial_check, final_check, + equations, dt, limiter, antidiffusive_flux) + + # negative eta direction + antidiffusive_flux = gamma_constant_newton * inverse_jacobian * inverse_weights[j] * + get_node_vars(antidiffusive_flux2_R, equations, dg, i, j, + element) + newton_loop!(alpha, bound, u, i, j, element, variable, initial_check, final_check, + equations, dt, limiter, antidiffusive_flux) + + # positive eta direction + antidiffusive_flux = -gamma_constant_newton * inverse_jacobian * + inverse_weights[j] * + get_node_vars(antidiffusive_flux2_L, equations, dg, i, j + 1, + element) + newton_loop!(alpha, bound, u, i, j, element, variable, initial_check, final_check, + equations, dt, limiter, antidiffusive_flux) + + return nothing +end + +@inline function newton_loop!(alpha, bound, u, i, j, element, variable, initial_check, + final_check, equations, dt, limiter, antidiffusive_flux) + newton_reltol, newton_abstol = limiter.newton_tolerances + + beta = 1 - alpha[i, j, element] + + beta_L = 0 # alpha = 1 + beta_R = beta # No higher beta (lower alpha) than the current one + + u_curr = u + beta * dt * antidiffusive_flux + + # If state is valid, perform initial check and return if correction is not needed + if is_valid_state(u_curr, equations) + as = goal_function(variable, bound, u_curr, equations) + + initial_check(bound, as, newton_abstol) && return nothing + end + + # Newton iterations + for iter in 1:(limiter.max_iterations_newton) + beta_old = beta + + # If the state is valid, evaluate d(goal)/d(beta) + if is_valid_state(u_curr, equations) + dSdbeta = dgoal_function(variable, u_curr, dt, antidiffusive_flux, + equations) + else # Otherwise, perform a bisection step + dSdbeta = 0 + end + + if dSdbeta != 0 + # Update beta with Newton's method + beta = beta - as / dSdbeta + end + + # Check bounds + if (beta < beta_L) || (beta > beta_R) || (dSdbeta == 0) || isnan(beta) + # Out of bounds, do a bisection step + beta = 0.5 * (beta_L + beta_R) + # Get new u + u_curr = u + beta * dt * antidiffusive_flux + + # If the state is invalid, finish bisection step without checking tolerance and iterate further + if !is_valid_state(u_curr, equations) + beta_R = beta + continue + end + + # Check new beta for condition and update bounds + as = goal_function(variable, bound, u_curr, equations) + if initial_check(bound, as, newton_abstol) + # New beta fulfills condition + beta_L = beta + else + # New beta does not fulfill condition + beta_R = beta + end + else + # Get new u + u_curr = u + beta * dt * antidiffusive_flux + + # If the state is invalid, redefine right bound without checking tolerance and iterate further + if !is_valid_state(u_curr, equations) + beta_R = beta + continue + end + + # Evaluate goal function + as = goal_function(variable, bound, u_curr, equations) + end + + # Check relative tolerance + if abs(beta_old - beta) <= newton_reltol + break + end + + # Check absolute tolerance + if final_check(bound, as, newton_abstol) + break + end + end + + new_alpha = 1 - beta + if alpha[i, j, element] > new_alpha + newton_abstol + error("Alpha is getting smaller. old: $(alpha[i, j, element]), new: $new_alpha") + else + alpha[i, j, element] = new_alpha + end + + return nothing +end + +# Initial checks +@inline function initial_check_entropy_spec(bound, goal, newton_abstol) + goal <= max(newton_abstol, abs(bound) * newton_abstol) +end + +@inline function initial_check_entropy_math(bound, goal, newton_abstol) + goal >= -max(newton_abstol, abs(bound) * newton_abstol) +end + +@inline initial_check_nonnegative(bound, goal, newton_abstol) = goal <= 0 + +# Goal and d(Goal)d(u) function +@inline goal_function(variable, bound, u, equations) = bound - variable(u, equations) +@inline function dgoal_function(variable, u, dt, antidiffusive_flux, equations) + -dot(variable(u, equations, True()), dt * antidiffusive_flux) +end + +# Final check +@inline function final_check_standard(bound, goal, newton_abstol) + abs(goal) < max(newton_abstol, abs(bound) * newton_abstol) +end + +@inline function final_check_nonnegative(bound, goal, newton_abstol) + (goal <= eps()) && (goal > -max(newton_abstol, abs(bound) * newton_abstol)) +end + +# this method is used when the limiter is constructed as for shock-capturing volume integrals +function create_cache(limiter::Type{SubcellLimiterMCL}, equations::AbstractEquations{2}, + basis::LobattoLegendreBasis, PressurePositivityLimiterKuzmin) + subcell_limiter_coefficients = Trixi.ContainerSubcellLimiterMCL2D{real(basis) + }(0, + nvariables(equations), + nnodes(basis)) + container_bar_states = Trixi.ContainerBarStates{real(basis)}(0, + nvariables(equations), + nnodes(basis)) + + # Memory for bounds checking routine with `BoundsCheckCallback`. + # [maximum since the last export / total maximum, min / max, variable] + mcl_bounds_delta = zeros(real(basis), 2, 2, + nvariables(equations) + PressurePositivityLimiterKuzmin) + + return (; subcell_limiter_coefficients, container_bar_states, mcl_bounds_delta) +end end # @muladd diff --git a/src/time_integration/methods_SSP.jl b/src/time_integration/methods_SSP.jl index dbb9e51121b..3aacd4a5dab 100644 --- a/src/time_integration/methods_SSP.jl +++ b/src/time_integration/methods_SSP.jl @@ -254,5 +254,60 @@ function Base.resize!(semi, volume_integral::VolumeIntegralSubcellLimiting, new_ # Resize container subcell_limiter_coefficients @unpack limiter = volume_integral resize!(limiter.cache.subcell_limiter_coefficients, new_size) + # Calc subcell normal directions before StepsizeCallback + + if limiter isa SubcellLimiterMCL || + (limiter isa SubcellLimiterIDP && limiter.bar_states) + resize!(limiter.cache.container_bar_states, new_size) + calc_normal_directions!(limiter.cache.container_bar_states, + mesh_equations_solver_cache(semi)...) + end +end + +function calc_normal_directions!(container_bar_states, mesh::TreeMesh, equations, dg, + cache) + nothing +end + +function calc_normal_directions!(container_bar_states, mesh::StructuredMesh, equations, + dg, cache) + @unpack weights, derivative_matrix = dg.basis + @unpack contravariant_vectors = cache.elements + + @unpack normal_direction_xi, normal_direction_eta = container_bar_states + @threaded for element in eachelement(dg, cache) + for j in eachnode(dg) + normal_direction = get_contravariant_vector(1, contravariant_vectors, 1, j, + element) + for i in 2:nnodes(dg) + for m in 1:nnodes(dg) + normal_direction += weights[i - 1] * derivative_matrix[i - 1, m] * + get_contravariant_vector(1, + contravariant_vectors, + m, j, element) + end + for v in 1:(nvariables(equations) - 2) + normal_direction_xi[v, i - 1, j, element] = normal_direction[v] + end + end + end + for i in eachnode(dg) + normal_direction = get_contravariant_vector(2, contravariant_vectors, i, 1, + element) + for j in 2:nnodes(dg) + for m in 1:nnodes(dg) + normal_direction += weights[j - 1] * derivative_matrix[j - 1, m] * + get_contravariant_vector(2, + contravariant_vectors, + i, m, element) + end + for v in 1:(nvariables(equations) - 2) + normal_direction_eta[v, i, j - 1, element] = normal_direction[v] + end + end + end + end + + return nothing end end # @muladd diff --git a/test/test_structured_2d.jl b/test/test_structured_2d.jl index dd2248e10b2..405709242c6 100644 --- a/test/test_structured_2d.jl +++ b/test/test_structured_2d.jl @@ -238,6 +238,58 @@ end end end +@trixi_testset "elixir_euler_convergence_wavingflag_IDP.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_convergence_wavingflag_IDP.jl"), + l2=[ + 0.3398358793878119, + 0.03398358793878129, + 0.06796717587756244, + 0.008495896984696072, + ], + linf=[ + 0.8360446582060936, + 0.08360446582060972, + 0.16720893164122444, + 0.02090111645397741, + ], + tspan=(0.0, 0.5)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + +@trixi_testset "elixir_euler_convergence_wavingflag_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_convergence_wavingflag_MCL.jl"), + l2=[ + 0.33983417649330827, + 0.033983417649330924, + 0.06796683529866161, + 0.008495854412336827, + ], + linf=[ + 0.8360446582068146, + 0.083604465820679, + 0.16720893164136671, + 0.02090111645399162, + ], + tspan=(0.0, 0.5)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + @trixi_testset "elixir_euler_source_terms.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms.jl"), # Expected errors are exactly the same as with TreeMesh! @@ -398,6 +450,32 @@ end end end +@trixi_testset "elixir_euler_source_terms_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_source_terms_sc_subcell.jl"), + l2=[ + 0.008160127825550706, + 0.008658254974279198, + 0.009351901915798305, + 0.0277570186711509, + ], + linf=[ + 0.027225588710793502, + 0.040734035190958195, + 0.03819406890281263, + 0.08080654623152705, + ], + tspan=(0.0, 0.5)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + @trixi_testset "elixir_euler_source_terms_waving_flag.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_waving_flag.jl"), @@ -448,6 +526,59 @@ end end end +@trixi_testset "elixir_euler_free_stream_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_free_stream_sc_subcell.jl"), + l2=[ + 2.6224749465938795e-14, + 1.6175366858083413e-14, + 2.358782725951525e-14, + 5.910156539173304e-14, + ], + linf=[ + 1.1546319456101628e-14, + 1.084687895058778e-13, + 1.7050250100680842e-13, + 2.0250467969162855e-13, + ], + atol=1.0e-13, + cells_per_dimension=(8, 8)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + +@trixi_testset "elixir_euler_free_stream_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream_MCL.jl"), + l2=[ + 3.532639560334565e-14, + 1.4787576718355913e-14, + 2.109573923923632e-14, + 2.54649935281524e-14, + ], + linf=[ + 7.993605777301127e-15, + 1.1611545058798356e-13, + 1.7619239400801234e-13, + 2.007283228522283e-13, + ], + atol=1.0e-13, + cells_per_dimension=(8, 8)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + @trixi_testset "elixir_euler_free_stream.jl with FluxRotated(flux_lax_friedrichs)" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"), surface_flux=FluxRotated(flux_lax_friedrichs), @@ -474,6 +605,103 @@ end end end +@trixi_testset "elixir_euler_double_mach.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_double_mach.jl"), + l2=[ + 0.8955457632754655, + 6.8117495933240235, + 3.2697118944675716, + 77.5174041919109, + ], + linf=[ + 10.16165871096883, + 133.2522870057006, + 38.23157147773949, + 1470.3950960145828, + ], + initial_refinement_level=3, + tspan=(0.0, 0.05)) +end + +@trixi_testset "elixir_euler_double_mach_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_double_mach_MCL.jl"), + l2=[ + 0.9266313242695542, + 7.071517579972717, + 3.2627078543492787, + 80.24631724351916, + ], + linf=[ + 14.244598580563007, + 138.4745277257612, + 38.69633620234036, + 1574.6686216469134, + ], + initial_refinement_level=3, + tspan=(0.0, 0.05)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + +@trixi_testset "elixir_euler_shock_upstream_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_shock_upstream_sc_subcell.jl"), + l2=[ + 1.2351468819080416, + 1.1269856428294935, + 1.7239124305681928, + 11.715260007491556, + ], + linf=[ + 5.385493056976312, + 6.575446146030286, + 10.06523457762742, + 51.00903155017642, + ], + cells_per_dimension=(8, 12), + tspan=(0.0, 0.5)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + +@trixi_testset "elixir_euler_shock_upstream_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shock_upstream_MCL.jl"), + l2=[ + 1.2607430289877726, + 1.1565837325291355, + 1.7791790302458714, + 11.891223800389232, + ], + linf=[ + 5.68876088477983, + 8.165554425950146, + 10.859100194836538, + 50.25822408989214, + ], + cells_per_dimension=(8, 12), + tspan=(0.0, 0.5)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 10000 + end +end + @trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms_nonperiodic.jl"), diff --git a/test/test_tree_2d_euler.jl b/test/test_tree_2d_euler.jl index db36cb7d79f..77df1ba45bf 100644 --- a/test/test_tree_2d_euler.jl +++ b/test/test_tree_2d_euler.jl @@ -34,6 +34,31 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_dgsem") end end +@trixi_testset "elixir_euler_source_terms_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_source_terms_sc_subcell.jl"), + l2=[ + 2.0633069593983843e-6, + 1.9337331005472223e-6, + 1.9337331005227536e-6, + 5.885362117543159e-6, + ], + linf=[ + 1.636984098429828e-5, + 1.5579038690871627e-5, + 1.557903868998345e-5, + 5.260532107742577e-5, + ]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + @trixi_testset "elixir_euler_convergence_pure_fv.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_convergence_pure_fv.jl"), l2=[ @@ -58,6 +83,30 @@ end end end +@trixi_testset "elixir_euler_convergence_IDP.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_convergence_IDP.jl"), + l2=[ + 0.1289984161854359, + 0.012899841618543363, + 0.025799683237087086, + 0.003224960404636081, + ], + linf=[ + 0.9436588685021441, + 0.0943658868502173, + 0.1887317737004306, + 0.02359147170911058, + ]) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + @trixi_testset "elixir_euler_density_wave.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_density_wave.jl"), l2=[ @@ -396,6 +445,60 @@ end end end +@trixi_testset "elixir_euler_blast_wave_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_sc_subcell.jl"), + l2=[ + 0.30785002807815187, + 0.1759956703391451, + 0.1759417566220675, + 0.6141201710105174, + ], + linf=[ + 1.2971792413978331, + 1.1057407237412735, + 1.1057665512872346, + 2.436409926521213, + ], + tspan=(0.0, 0.5), + initial_refinement_level=4, + coverage_override=(maxiters = 6,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + +@trixi_testset "elixir_euler_blast_wave_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_MCL.jl"), + l2=[ + 0.32716628280821736, + 0.17711362716405113, + 0.17710881738119433, + 0.6192141753914343, + ], + linf=[ + 1.3147680231795071, + 1.1313232952582144, + 1.1308868661560831, + 2.4962119219206, + ], + tspan=(0.0, 0.5), + initial_refinement_level=4, + coverage_override=(maxiters = 6,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + @trixi_testset "elixir_euler_sedov_blast_wave.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave.jl"), l2=[ @@ -448,6 +551,91 @@ end end end +@trixi_testset "elixir_euler_sedov_blast_wave_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_sedov_blast_wave_sc_subcell.jl"), + l2=[ + 0.47651273561515994, + 0.16605194156429376, + 0.16605194156447747, + 0.6184646142923547, + ], + linf=[ + 2.559717182592356, + 1.3594817545576394, + 1.3594817545666105, + 6.451896959781657, + ], + tspan=(0.0, 1.0), + initial_refinement_level=4, + coverage_override=(maxiters = 6,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + +@trixi_testset "elixir_euler_sedov_blast_wave_MCL.jl" begin + rm("out/deviations.txt", force = true) + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave_MCL.jl"), + l2=[ + 0.4740321851943766, + 0.15889871334104985, + 0.15889871334104988, + 0.6190405536267991, + ], + linf=[ + 4.011954283668753, + 1.8527131099524292, + 1.8527131099524277, + 6.465833729130187, + ], + tspan=(0.0, 1.0), + initial_refinement_level=4, + coverage_override=(maxiters = 6,)) + lines = readlines("out/deviations.txt") + @test lines[1] == + "# iter, simu_time, rho_min, rho_max, rho_v1_min, rho_v1_max, rho_v2_min, rho_v2_max, rho_e_min, rho_e_max, pressure_min" + @test startswith(lines[end], "349") || startswith(lines[end], "1") + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + +@trixi_testset "elixir_euler_sedov_blast_wave.jl (HLLE)" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave.jl"), + l2=[ + 0.35267161504176747, + 0.17218309138797958, + 0.17218307467125854, + 0.6236143054619037, + ], + linf=[ + 2.77484045816607, + 1.8281111268370718, + 1.8281110470490887, + 6.24263735888126, + ], + tspan=(0.0, 0.5), + surface_flux=flux_hlle) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + end +end + @trixi_testset "elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl"), @@ -624,6 +812,62 @@ end end end +@trixi_testset "elixir_euler_kelvin_helmholtz_instability_sc_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_kelvin_helmholtz_instability_sc_subcell.jl"), + l2=[ + 0.055703165296633834, + 0.032987233605927, + 0.05224472051711956, + 0.08011565264331237, + ], + linf=[ + 0.24091018397460595, + 0.1660190071332282, + 0.12356154893467916, + 0.2695167937393226, + ], + tspan=(0.0, 0.2), + initial_refinement_level=5, + coverage_override=(maxiters = 2,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + +@trixi_testset "elixir_euler_kelvin_helmholtz_instability_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_euler_kelvin_helmholtz_instability_MCL.jl"), + l2=[ + 0.055703165296633834, + 0.032987233605927, + 0.05224472051711956, + 0.08011565264331237, + ], + linf=[ + 0.24091018397460595, + 0.1660190071332282, + 0.12356154893467916, + 0.2695167937393226, + ], + tspan=(0.0, 0.2), + initial_refinement_level=5, + coverage_override=(maxiters = 2,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + @trixi_testset "elixir_euler_kelvin_helmholtz_instability_amr_neuralnetwork_perssonperaire.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_kelvin_helmholtz_instability_amr_neuralnetwork_perssonperaire.jl"), @@ -713,6 +957,60 @@ end end end +@trixi_testset "elixir_euler_astro_jet_subcell.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_astro_jet_subcell.jl"), + l2=[ + 0.4186473232186195, + 341.42386623555944, + 12.913743102619245, + 135260.31735534978, + ], + linf=[ + 6.594617349637199, + 5225.251243383396, + 417.4788228266706, + 2.0263599311276933e6, + ], + initial_refinement_level=5, + tspan=(0.0, 1.0e-4), + coverage_override=(maxiters = 6,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + +@trixi_testset "elixir_euler_astro_jet_MCL.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_astro_jet_MCL.jl"), + l2=[ + 0.4142490642847159, + 339.10045752248817, + 12.41716316125269, + 134277.32794840127, + ], + linf=[ + 5.649893737038036, + 4628.887032664001, + 373.39317079274724, + 1.8133961097673306e6, + ], + initial_refinement_level=5, + tspan=(0.0, 1.0e-4), + coverage_override=(maxiters = 6,)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + @trixi_testset "elixir_euler_vortex.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_vortex.jl"), l2=[ diff --git a/test/test_tree_2d_eulermulti.jl b/test/test_tree_2d_eulermulti.jl index 2e808af6473..77cf3a5d7d2 100644 --- a/test/test_tree_2d_eulermulti.jl +++ b/test/test_tree_2d_eulermulti.jl @@ -75,6 +75,35 @@ end end end +@trixi_testset "elixir_eulermulti_shock_bubble_shockcapturing_subcell_minmax.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_eulermulti_shock_bubble_shockcapturing_subcell_minmax.jl"), + l2=[ + 76.59096367977872, + 1.9879932386864356, + 59851.34515039375, + 0.18710988181124935, + 0.010631432251136084, + ], + linf=[ + 212.71245739310544, + 27.399221359958894, + 158389.9681231281, + 0.6524718882809865, + 0.10630137919864985, + ], + initial_refinement_level=3, + tspan=(0.0, 0.001)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 15000 + end +end + @trixi_testset "elixir_eulermulti_ec.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_ec.jl"), l2=[ diff --git a/test/test_tree_2d_mhd.jl b/test/test_tree_2d_mhd.jl index bd6a95bba50..6e2f8852b53 100644 --- a/test/test_tree_2d_mhd.jl +++ b/test/test_tree_2d_mhd.jl @@ -331,24 +331,28 @@ end @trixi_testset "elixir_mhd_shockcapturing_subcell.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_shockcapturing_subcell.jl"), - l2=[2.9974425783503109e-02, - 7.2849646345685956e-02, - 7.2488477174662239e-02, + l2=[ + 3.2064026219236076e-02, + 7.2461094392606618e-02, + 7.2380202888062711e-02, 0.0000000000000000e+00, - 1.2507971380965512e+00, - 1.8929505145499678e-02, - 1.2218606317164420e-02, + 8.6293936673145932e-01, + 8.4091669534557805e-03, + 5.2156364913231732e-03, 0.0000000000000000e+00, - 3.0154796910479838e-03], - linf=[3.2147382412340830e-01, - 1.3709471664007811e+00, - 1.3465154685288383e+00, + 2.0786952301129021e-04, + ], + linf=[ + 3.8778760255775635e-01, + 9.4666683953698927e-01, + 9.4618924645661928e-01, 0.0000000000000000e+00, - 1.6051257523415284e+01, - 3.0564266749926644e-01, - 2.3908016329805595e-01, + 1.0980297261521951e+01, + 1.0264404591009069e-01, + 1.0655686942176350e-01, 0.0000000000000000e+00, - 1.3711262178549158e-01], + 6.1013422157115546e-03, + ], tspan=(0.0, 0.003)) # Ensure that we do not have excessive memory allocations # (e.g., from type instabilities) diff --git a/test/test_unit.jl b/test/test_unit.jl index a73dfab5504..3d36fb3b446 100644 --- a/test/test_unit.jl +++ b/test/test_unit.jl @@ -296,9 +296,11 @@ end end Trixi.move_connectivity!(c::MyContainer, first, last, destination) = c Trixi.delete_connectivity!(c::MyContainer, first, last) = c - Trixi.reset_data_structures!(c::MyContainer) = (c.data = Vector{Int}(undef, - c.capacity + 1); - c) + function Trixi.reset_data_structures!(c::MyContainer) + (c.data = Vector{Int}(undef, + c.capacity + 1); + c) + end function Base.:(==)(c1::MyContainer, c2::MyContainer) return (c1.capacity == c2.capacity && c1.length == c2.length && @@ -414,8 +416,14 @@ end indicator_hg = IndicatorHennemannGassner(1.0, 0.0, true, "variable", "cache") @test_nowarn show(stdout, indicator_hg) - limiter_idp = SubcellLimiterIDP(true, [1], 0.1, "cache") - @test_nowarn show(stdout, limiter_idp) + indicator_idp = SubcellLimiterIDP(true, [1], true, [1], ("variable",), 0.1, true, + true, true, "cache", 1, (1.0, 1.0), 1.0, true, + 1.0, nothing) + @test_nowarn show(stdout, indicator_idp) + + indicator_mcl = SubcellLimiterMCL("cache", true, true, true, true, true, true, true, + 1.0, true, true, 1.0, nothing, true) + @test_nowarn show(stdout, indicator_mcl) # TODO: TrixiShallowWater: move unit test indicator_hg_swe = IndicatorHennemannGassnerShallowWater(1.0, 0.0, true, "variable", diff --git a/utils/trixi-format.jl b/utils/trixi-format.jl old mode 100755 new mode 100644