From 72ff5e2aa077d4914cfc51185b3c4350a6f32410 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 10:35:32 +0100 Subject: [PATCH 01/83] Corrected bugs from coupled to main merger. --- src/Trixi.jl | 13 ++- src/callbacks_step/analysis.jl | 103 ++++++++++++++++- src/callbacks_step/save_solution.jl | 141 +++++++++++++++++++---- src/callbacks_step/stepsize.jl | 30 ++++- src/equations/equations.jl | 137 ++++++++++++++++++++++ src/equations/hyperbolic_diffusion_2d.jl | 15 +++ src/meshes/mesh_io.jl | 9 +- src/solvers/dgsem_structured/dg.jl | 95 +++++++++++++++ src/solvers/dgsem_structured/dg_2d.jl | 17 +++ src/solvers/dgsem_structured/dg_3d.jl | 17 +++ src/visualization/types.jl | 36 ++++++ test/test_visualization.jl | 1 + 12 files changed, 583 insertions(+), 31 deletions(-) diff --git a/src/Trixi.jl b/src/Trixi.jl index b45edbbecd4..97dce671431 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -112,6 +112,7 @@ include("semidiscretization/semidiscretization.jl") include("semidiscretization/semidiscretization_hyperbolic.jl") include("semidiscretization/semidiscretization_hyperbolic_parabolic.jl") include("semidiscretization/semidiscretization_euler_acoustics.jl") +include("semidiscretization/semidiscretization_coupled.jl") include("callbacks_step/callbacks_step.jl") include("callbacks_stage/callbacks_stage.jl") include("semidiscretization/semidiscretization_euler_gravity.jl") @@ -128,6 +129,7 @@ include("visualization/visualization.jl") export AcousticPerturbationEquations2D, CompressibleEulerEquations1D, CompressibleEulerEquations2D, CompressibleEulerEquations3D, CompressibleEulerMulticomponentEquations1D, CompressibleEulerMulticomponentEquations2D, + PolytropicEulerEquations2D, IdealGlmMhdEquations1D, IdealGlmMhdEquations2D, IdealGlmMhdEquations3D, IdealGlmMhdMulticomponentEquations1D, IdealGlmMhdMulticomponentEquations2D, HyperbolicDiffusionEquations1D, HyperbolicDiffusionEquations2D, HyperbolicDiffusionEquations3D, @@ -136,7 +138,9 @@ export AcousticPerturbationEquations2D, LatticeBoltzmannEquations2D, LatticeBoltzmannEquations3D, ShallowWaterEquations1D, ShallowWaterEquations2D, ShallowWaterTwoLayerEquations1D, ShallowWaterTwoLayerEquations2D, - LinearizedEulerEquations2D + LinearizedEulerEquations2D, + CouplingLinearScalarAdvectionEquation2D, CouplingCompressibleEulerHyperbolicDiffusion2D, + CouplingPolytropicEuler2D export LaplaceDiffusion1D, LaplaceDiffusion2D, CompressibleNavierStokesDiffusion2D, CompressibleNavierStokesDiffusion3D @@ -174,11 +178,14 @@ export boundary_condition_do_nothing, boundary_condition_noslip_wall, boundary_condition_slip_wall, boundary_condition_wall, - BoundaryConditionNavierStokesWall, NoSlip, Adiabatic, Isothermal + BoundaryConditionNavierStokesWall, NoSlip, Adiabatic, Isothermal, + BoundaryConditionCoupled, + BoundaryConditionCoupledAB export initial_condition_convergence_test, source_terms_convergence_test export source_terms_harmonic export initial_condition_poisson_nonperiodic, source_terms_poisson_nonperiodic, boundary_condition_poisson_nonperiodic +export initial_condition_peak export initial_condition_eoc_test_coupled_euler_gravity, source_terms_eoc_test_coupled_euler_gravity, source_terms_eoc_test_euler export cons2cons, cons2prim, prim2cons, cons2macroscopic, cons2state, cons2mean, @@ -215,6 +222,8 @@ export SemidiscretizationEulerAcoustics export SemidiscretizationEulerGravity, ParametersEulerGravity, timestep_gravity_erk52_3Sstar!, timestep_gravity_carpenter_kennedy_erk54_2N! +export SemidiscretizationCoupled + export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback, SaveRestartCallback, SaveSolutionCallback, TimeSeriesCallback, VisualizationCallback, AveragingCallback, diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 79a442f06c3..8193c11ffb0 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -122,6 +122,61 @@ function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache; end +function AnalysisCallback(semi::SemidiscretizationCoupled; kwargs...) + _, equations, _, _ = mesh_equations_solver_cache(semi) + AnalysisCallback(semi, equations; kwargs...) +end + + +# The SemidiscretizationCoupled needs multiple analyzers (one for each mesh) +function AnalysisCallback(semi::SemidiscretizationCoupled, equations; + interval=0, + save_analysis=false, + output_directory="out", + analysis_filename="analysis.dat", + extra_analysis_errors=Symbol[], + analysis_errors=union(default_analysis_errors(equations), extra_analysis_errors), + extra_analysis_integrals=(), + analysis_integrals=union(default_analysis_integrals(equations), extra_analysis_integrals), + RealT=real(semi), + # uEltype=eltype(semi), + uEltype=eltype(semi.semis[1].cache.elements), + kwargs...) + + # print("typeof(semi) = ", typeof(semi), "\n") + # print("fieldnames(typeof(semi)) = ", fieldnames(typeof(semi)), "\n") + # print("typeof(semi.semis[1].cache.elements) = ", typeof(semi.semis[1].cache.elements), "\n") + + # when is the callback activated + condition = (u, t, integrator) -> interval > 0 && (integrator.iter % interval == 0 || + isfinished(integrator)) + + analyzers = map(semi.semis) do semi_ + _, _, solver, _ = mesh_equations_solver_cache(semi_) + + analyzer = SolutionAnalyzer(solver; kwargs...) + end + + caches_analysis = map(semi.semis) do semi_ + mesh, equations_, solver, cache = mesh_equations_solver_cache(semi_) + + analyzer = SolutionAnalyzer(solver; kwargs...) + cache_analysis = create_cache_analysis(analyzer, mesh, equations_, solver, cache, RealT, uEltype) + end + + analysis_callback = AnalysisCallback(0.0, 0.0, 0, 0.0, + interval, save_analysis, output_directory, analysis_filename, + analyzers, + analysis_errors, Tuple(analysis_integrals), + SVector(ntuple(_ -> zero(uEltype), Val(nvariables(equations)))), + caches_analysis) + + DiscreteCallback(condition, analysis_callback, + save_positions=(false,false), + initialize=initialize!) +end + + function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, t, integrator) where {Condition, Affect!<:AnalysisCallback} semi = integrator.p initial_state_integrals = integrate(u_ode, semi) @@ -250,7 +305,8 @@ function (analysis_callback::AnalysisCallback)(integrator) mpi_println() mpi_println("─"^100) # TODO: Taal refactor, polydeg is specific to DGSEM - mpi_println(" Simulation running '", get_name(equations), "' with ", summary(solver)) + # mpi_println(" Simulation running '", get_name(equations), "' with ", summary(solver)) + mpi_println(" Simulation running '", get_name(equations), "' with ", summary(semi)) mpi_println("─"^100) mpi_println(" #timesteps: " * @sprintf("% 14d", iter) * " " * @@ -270,7 +326,8 @@ function (analysis_callback::AnalysisCallback)(integrator) mpi_println(" #elements: " * @sprintf("% 14d", nelements(mesh, solver, cache))) # Level information (only show for AMR) - print_amr_information(integrator.opts.callback, mesh, solver, cache) + # print_amr_information(integrator.opts.callback, mesh, solver, cache) + print_amr_information(integrator.opts.callback, semi) mpi_println() # Open file for appending and store time step and time information @@ -464,6 +521,21 @@ function print_amr_information(callbacks, mesh, solver, cache) return nothing end +function print_amr_information(callbacks, semi) + # Return early if there is nothing to print + uses_amr(callbacks) || return nothing + + mesh, equations, solver, cache = mesh_equations_solver_cache(semi) + print_amr_information(mesh, equations, solver, cache) +end + +function print_amr_information(mesh::TreeMesh, equations, solver, cache) + levels = Vector{Int}(undef, nelements(solver, cache)) + min_level = typemax(Int) + max_level = typemin(Int) + return nothing +end + # Print level information only if AMR is enabled function print_amr_information(callbacks, mesh::P4estMesh, solver, cache) @@ -564,6 +636,33 @@ function analyze(quantity, du, u, t, mesh, equations, equations_parabolic, solve end +function analyze(quantity, du_ode, u_ode, t, semi::SemidiscretizationCoupled; normalize=true) + @unpack semis, u_indices = semi + + integral = sum(1:nmeshes(semi)) do i + mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) + # In the AnalysisCallback for SemidiscretizationCoupled, u_ode is never wrapped + du = wrap_array(du_ode[u_indices[i]], mesh, equations, solver, cache) + u = wrap_array(u_ode[u_indices[i]], mesh, equations, solver, cache) + + analyze(quantity, du, u, t, mesh, equations, solver, cache, normalize=false) + end + + if normalize + # Normalize with total volume + total_volume_ = total_volume(semi) + integral = integral / total_volume_ + end + + return integral +end + + +function analyze(quantity, du, u, t, mesh, equations, solver, cache; normalize=true) + integrate(quantity, u, mesh, equations, solver, cache, normalize=normalize) +end + + function entropy_timederivative end pretty_form_utf(::typeof(entropy_timederivative)) = "∑∂S/∂U ⋅ Uₜ" pretty_form_ascii(::typeof(entropy_timederivative)) = "dsdu_ut" diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 6cccbc9d3f9..65690b3103e 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -135,13 +135,14 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte mpi_isroot() && mkpath(solution_callback.output_directory) semi = integrator.p - mesh, _, _, _ = mesh_equations_solver_cache(semi) - @trixi_timeit timer() "I/O" begin - if mesh.unsaved_changes - mesh.current_filename = save_mesh_file(mesh, solution_callback.output_directory) - mesh.unsaved_changes = false - end - end + # mesh, _, _, _ = mesh_equations_solver_cache(semi) + # @trixi_timeit timer() "I/O" begin + # if mesh.unsaved_changes + # mesh.current_filename = save_mesh_file(mesh, solution_callback.output_directory) + # mesh.unsaved_changes = false + # end + # end + @trixi_timeit timer() "I/O" save_mesh(semi, solution_callback.output_directory) if solution_callback.save_initial_solution solution_callback(integrator) @@ -151,6 +152,28 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte end +function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep=0) + mesh, _, _, _ = mesh_equations_solver_cache(semi) + + if mesh.unsaved_changes + mesh.current_filename = save_mesh_file(mesh, output_directory) + mesh.unsaved_changes = false + end +end + + +function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) + for i in 1:nmeshes(semi) + mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) + + if mesh.unsaved_changes + mesh.current_filename = save_mesh_file(mesh, output_directory, system=i) + mesh.unsaved_changes = false + end + end +end + + # this method is called to determine whether the callback should be activated function (solution_callback::SaveSolutionCallback)(u, t, integrator) @unpack interval_or_dt, save_final_solution = solution_callback @@ -172,43 +195,119 @@ function (solution_callback::SaveSolutionCallback)(integrator) @unpack t, dt = integrator iter = integrator.stats.naccept semi = integrator.p - mesh, _, _, _ = mesh_equations_solver_cache(semi) + # mesh, _, _, _ = mesh_equations_solver_cache(semi) @trixi_timeit timer() "I/O" begin - @trixi_timeit timer() "save mesh" if mesh.unsaved_changes - mesh.current_filename = save_mesh_file(mesh, solution_callback.output_directory, iter) - mesh.unsaved_changes = false - end + # @trixi_timeit timer() "save mesh" if mesh.unsaved_changes + # mesh.current_filename = save_mesh_file(mesh, solution_callback.output_directory, iter) + # mesh.unsaved_changes = false + # end + @trixi_timeit timer() "save mesh" save_mesh(semi, solution_callback.output_directory) element_variables = Dict{Symbol, Any}() + # @trixi_timeit timer() "get element variables" begin + # # print("typeof(semi) = ", typeof(semi), "\n") + # get_element_variables!(element_variables, u_ode, semi) + # callbacks = integrator.opts.callback + # if callbacks isa CallbackSet + # for cb in callbacks.continuous_callbacks + # get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) + # end + # for cb in callbacks.discrete_callbacks + # get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) + # end + # end + # end + get_element_variables_semi!(semi, integrator, u_ode, element_variables) + + @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, solution_callback, element_variables, + integrator=integrator) + end + + # avoid re-evaluating possible FSAL stages + u_modified!(integrator, false) + return nothing +end + + +function get_element_variables_semi!(semi::AbstractSemidiscretization, integrator, u_ode, element_variables) + @trixi_timeit timer() "get element variables" begin + get_element_variables!(element_variables, u_ode, semi) + callbacks = integrator.opts.callback + if callbacks isa CallbackSet + for cb in callbacks.continuous_callbacks + get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) + end + for cb in callbacks.discrete_callbacks + get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) + end + end + end +end + + +function get_element_variables_semi!(semi::SemidiscretizationCoupled, integrator, u_ode, element_variables) + for i in 1:nmeshes(semi) @trixi_timeit timer() "get element variables" begin - get_element_variables!(element_variables, u_ode, semi) + get_element_variables!(element_variables, u_ode[semi.u_indices[i]], semi.semis[i]) callbacks = integrator.opts.callback if callbacks isa CallbackSet for cb in callbacks.continuous_callbacks - get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) + get_element_variables!(element_variables, u_ode[semi.u_indices[i]], semi.semis[i], cb; t=integrator.t, iter=integrator.stats.naccept) end for cb in callbacks.discrete_callbacks - get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) + get_element_variables!(element_variables, u_ode[semi.u_indices[i]], semi.semis[i], cb; t=integrator.t, iter=integrator.stats.naccept) end end end + end +end + - @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, solution_callback, element_variables) +@inline function save_solution_file(semi::AbstractSemidiscretization, u_ode, solution_callback, + integrator; system="") + @unpack t, dt = integrator + iter = integrator.destats.naccept + + element_variables = Dict{Symbol, Any}() + @trixi_timeit timer() "get element variables" begin + get_element_variables!(element_variables, u_ode, semi) + callbacks = integrator.opts.callback + if callbacks isa CallbackSet + for cb in callbacks.continuous_callbacks + get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=iter) + end + for cb in callbacks.discrete_callbacks + get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=iter) + end + end end - # avoid re-evaluating possible FSAL stages - u_modified!(integrator, false) - return nothing + @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, + solution_callback, element_variables, + system=system) +end + + +@inline function save_solution_file(u_ode, t, dt, iter, + semi::SemidiscretizationCoupled, solution_callback, + element_variables=Dict{Symbol,Any}(); + integrator) + @unpack semis, u_indices = semi + + for i in 1:nmeshes(semi) + save_solution_file(semis[i], u_ode[u_indices[i]], solution_callback, integrator, system=i) + end end @inline function save_solution_file(u_ode, t, dt, iter, semi::AbstractSemidiscretization, solution_callback, - element_variables=Dict{Symbol,Any}()) + element_variables=Dict{Symbol,Any}(); integrator=nothing, system="") mesh, equations, solver, cache = mesh_equations_solver_cache(semi) u = wrap_array_native(u_ode, mesh, equations, solver, cache) - save_solution_file(u, t, dt, iter, mesh, equations, solver, cache, solution_callback, element_variables) + save_solution_file(u, t, dt, iter, mesh, equations, solver, cache, solution_callback, + element_variables; system=system) end diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 13e4f9dfa54..1d3ecd8a039 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -70,11 +70,12 @@ end semi = integrator.p mesh, equations, solver, cache = mesh_equations_solver_cache(semi) @unpack cfl_number = stepsize_callback - u = wrap_array(u_ode, mesh, equations, solver, cache) + # u = wrap_array(u_ode, mesh, equations, solver, cache) - dt = @trixi_timeit timer() "calculate dt" cfl_number * max_dt(u, t, mesh, - have_constant_speed(equations), equations, - solver, cache) + # dt = @trixi_timeit timer() "calculate dt" cfl_number * max_dt(u, t, mesh, + # have_constant_speed(equations), equations, + # solver, cache) + dt = calculate_dt(u_ode, t, cfl_number, semi) set_proposed_dt!(integrator, dt) integrator.opts.dtmax = dt integrator.dtcache = dt @@ -86,6 +87,27 @@ end end +function calculate_dt(u_ode, t, cfl_number, semi) + mesh, equations, solver, cache = mesh_equations_solver_cache(semi) + u = wrap_array(u_ode, mesh, equations, solver, cache) + + dt = @trixi_timeit timer() "calculate dt" cfl_number * max_dt(u, t, mesh, + have_constant_speed(equations), equations, + solver, cache) +end + + +function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) + @unpack u_indices = semi + + dt = minimum(1:nmeshes(semi)) do i + calculate_dt(u_ode[u_indices[i]], t, cfl_number, semi.semis[i]) + end + + return dt +end + + # Time integration methods from the DiffEq ecosystem without adaptive time stepping on their own # such as `CarpenterKennedy2N54` require passing `dt=...` in `solve(ode, ...)`. Since we don't have # an integrator at this stage but only the ODE, this method will be used there. It's called in diff --git a/src/equations/equations.jl b/src/equations/equations.jl index c1669531def..ba332a5dc1c 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -288,6 +288,134 @@ The inverse conversion is performed by [`cons2entropy`](@ref). """ function entropy2cons end + +""" + BoundaryConditionCoupled(other_semi_index, indices, uEltype) +Boundary condition to glue two meshes together. Solution values at the boundary +of another mesh will be used as boundary values. This requires the use +of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`, +which is the index of the mesh in the tuple of semidiscretizations. +Note that the elements and nodes of the two meshes at the coupled boundary must coincide. +This is currently only implemented for [`StructuredMesh`](@ref). +# Arguments +- `other_semi_index`: the index in `SemidiscretizationCoupled` of the semidiscretization + from which the values are copied +- `indices::Tuple`: node/cell indices at the boundary of the mesh in the other + semidiscretization. See examples below. +- `uEltype::Type`: element type of solution +# Examples +'''julia +# Connect the left boundary of mesh 2 to our boundary such that our positive +# boundary direction will match the positive y direction of the other boundary +BoundaryConditionCoupled(2, (1, :i), Float64) +# Connect the same two boundaries oppositely oriented +BoundaryConditionCoupled(2, (1, :i_backwards), Float64) +# Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` +BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) +''' +!!! warning "Experimental code" + This is an experimental feature and can change any time. +""" +mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} + # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] + u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 + other_semi_index ::Int + other_orientation::Int + indices ::I + + function BoundaryConditionCoupled(other_semi_index, indices, uEltype) + NDIMS = length(indices) + u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) + + if indices[1] in (:begin, :end) + other_orientation = 1 + elseif indices[2] in (:begin, :end) + other_orientation = 2 + else + other_orientation = 3 + end + + new{NDIMS, NDIMS*2-1, uEltype, typeof(indices)}( + u_boundary, other_semi_index, other_orientation, indices) + end +end + +mutable struct BoundaryConditionCoupledAB{NDIMS, NDIMST2M1, uEltype<:Real, I, AbstractEquations, AbstractEquations} + # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] + u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 + other_semi_index ::Int + other_orientation::Int + indices ::I + equations_other ::AbstractEquations + equations_coupled::AbstractEquations + + function BoundaryConditionCoupledAB(other_semi_index, indices, uEltype, equations_other, equations_coupled) + NDIMS = length(indices) + u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) + inner_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) + equations_other = equations_other + equations_coupled = equations_coupled + + if indices[1] in (:begin, :end) + other_orientation = 1 + elseif indices[2] in (:begin, :end) + other_orientation = 2 + else + other_orientation = 3 + end + + new{NDIMS, NDIMS*2-1, uEltype, typeof(indices), AbstractEquations, AbstractEquations}( + u_boundary, other_semi_index, other_orientation, indices, equations_other, equations_coupled) + end +end + + +function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, + cell_indices, surface_node_indices, + surface_flux_function, equations) + # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), + # but we don't have a solver here + u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], + Val(nvariables(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, equations) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equations) + end + + return flux +end + +function (boundary_condition::BoundaryConditionCoupledAB)(u_inner, orientation, direction, + cell_indices, surface_node_indices, + surface_flux_function, equations) + # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), + # but we don't have a solver here + u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], + Val(nvariables(boundary_condition.equations_coupled)))) + + # TODO: This should be computed in the coupled equations module. + if boundary_condition.other_semi_index == 2 + u_inner_long = vcat(u_inner, zeros(3)) + else + u_inner_long = vcat(zeros(3), u_inner) + end + + # Calculate boundary flux + if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary + # flux = surface_flux_function(zeros(length(u_boundary)), u_boundary, orientation, boundary_condition.equations_coupled) + flux = surface_flux_function(u_inner_long, u_boundary, orientation, boundary_condition.equations_coupled) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + # flux = surface_flux_function(u_boundary, zeros(length(u_boundary)), orientation, boundary_condition.equations_coupled) + flux = surface_flux_function(u_boundary, u_inner_long, orientation, boundary_condition.equations_coupled) + end + + return flux +end + + #################################################################################################### # Include files with actual implementations for different systems of equations. @@ -322,6 +450,10 @@ abstract type AbstractCompressibleEulerMulticomponentEquations{NDIMS, NVARS, NCO include("compressible_euler_multicomponent_1d.jl") include("compressible_euler_multicomponent_2d.jl") +# PolytropicEulerEquations +abstract type AbstractPolytropicEulerEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end +include("polytropic_euler_2d.jl") + # Retrieve number of components from equation instance for the multicomponent case @inline ncomponents(::AbstractCompressibleEulerMulticomponentEquations{NDIMS, NVARS, NCOMP}) where {NDIMS, NVARS, NCOMP} = NCOMP """ @@ -374,6 +506,11 @@ include("acoustic_perturbation_2d.jl") abstract type AbstractLinearizedEulerEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end include("linearized_euler_2d.jl") +# Coupling equations. +abstract type AbstractCouplingEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end +include("coupled_compressible_euler_hyperbolic_diffusion_2d.jl") +include("coupled_polytropic_euler_2d.jl") + abstract type AbstractEquationsParabolic{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end end # @muladd diff --git a/src/equations/hyperbolic_diffusion_2d.jl b/src/equations/hyperbolic_diffusion_2d.jl index 0f24949faad..3b2ba39c13a 100644 --- a/src/equations/hyperbolic_diffusion_2d.jl +++ b/src/equations/hyperbolic_diffusion_2d.jl @@ -52,6 +52,14 @@ end return SVector(phi, q1, q2) end +@inline function initial_condition_peak(x, t, equations::HyperbolicDiffusionEquations2D) + # elliptic equation: -ν Δϕ = f in Ω, u = g on ∂Ω + phi = exp(-x[1]^2 - (x[2] - 0.5)^2) + q1 = exp(-x[1]^2 - (x[2] - 0.5)^2) + q2 = exp(-x[1]^2 - (x[2] - 0.5)^2) + return SVector(phi, q1, q2) +end + @inline function source_terms_poisson_nonperiodic(u, x, t, equations::HyperbolicDiffusionEquations2D) # elliptic equation: -ν Δϕ = f in Ω, u = g on ∂Ω # analytical solution: ϕ = 2cos(πx)sin(2πy) + 2 and f = 10π^2cos(πx)sin(2πy) @@ -164,6 +172,13 @@ end sqrt(equations.nu * equations.inv_Tr) * norm(normal_direction) end +@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, equations::HyperbolicDiffusionEquations2D) + return 0, sqrt(equations.nu * equations.inv_Tr) +end + +@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::HyperbolicDiffusionEquations2D) + return 0, sqrt(equations.nu * equations.inv_Tr) * norm(normal_direction) +end @inline function flux_godunov(u_ll, u_rr, orientation::Integer, equations::HyperbolicDiffusionEquations2D) # Obtain left and right fluxes diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index beef5341e26..1f1fd04c34e 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -95,11 +95,16 @@ end # of the mesh, like its size and the type of boundary mapping function. # Then, within Trixi2Vtk, the StructuredMesh and its node coordinates are reconstructured from # these attributes for plotting purposes -function save_mesh_file(mesh::StructuredMesh, output_directory) +function save_mesh_file(mesh::StructuredMesh, output_directory; system="") # Create output directory (if it does not exist) mkpath(output_directory) - filename = joinpath(output_directory, "mesh.h5") + # filename = joinpath(output_directory, "mesh.h5") + if isempty(system) + filename = joinpath(output_directory, "mesh.h5") + else + filename = joinpath(output_directory, @sprintf("mesh_%s.h5", system)) + end # Open file (clobber existing content) h5open(filename, "w") do file diff --git a/src/solvers/dgsem_structured/dg.jl b/src/solvers/dgsem_structured/dg.jl index c4ba534b496..8a39d0ed1dc 100644 --- a/src/solvers/dgsem_structured/dg.jl +++ b/src/solvers/dgsem_structured/dg.jl @@ -65,6 +65,101 @@ end end +@inline function calc_boundary_flux_by_direction!(surface_flux_values, u, t, orientation, + boundary_condition::BoundaryConditionCoupled, + mesh::StructuredMesh, equations, + surface_integral, dg::DG, cache, + direction, node_indices, surface_node_indices, element) + @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements + @unpack surface_flux = surface_integral + + cell_indices = get_boundary_indices(element, orientation, mesh) + + u_inner = get_node_vars(u, equations, dg, node_indices..., element) + + # If the mapping is orientation-reversing, the contravariant vectors' orientation + # is reversed as well. The normal vector must be oriented in the direction + # from `left_element` to `right_element`, or the numerical flux will be computed + # incorrectly (downwind direction). + sign_jacobian = sign(inverse_jacobian[node_indices..., element]) + + # Contravariant vector Ja^i is the normal vector + normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, + node_indices..., element) + + # If the mapping is orientation-reversing, the normal vector will be reversed (see above). + # However, the flux now has the wrong sign, since we need the physical flux in normal direction. + flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, + surface_node_indices, surface_flux, equations) + + for v in eachvariable(equations) + surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] + end +end + +@inline function calc_boundary_flux_by_direction!(surface_flux_values, u, t, orientation, + boundary_condition::BoundaryConditionCoupledAB, + mesh::StructuredMesh, equations, + surface_integral, dg::DG, cache, + direction, node_indices, surface_node_indices, element) + @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements + # Override the passed surface_integral. + @unpack surface_flux = surface_integral + + cell_indices = get_boundary_indices(element, orientation, mesh) + + u_inner = get_node_vars(u, equations, dg, node_indices..., element) + + # If the mapping is orientation-reversing, the contravariant vectors' orientation + # is reversed as well. The normal vector must be oriented in the direction + # from `left_element` to `right_element`, or the numerical flux will be computed + # incorrectly (downwind direction). + sign_jacobian = sign(inverse_jacobian[node_indices..., element]) + + # Contravariant vector Ja^i is the normal vector + normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, + node_indices..., element) + + # If the mapping is orientation-reversing, the normal vector will be reversed (see above). + # However, the flux now has the wrong sign, since we need the physical flux in normal direction. + flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, + surface_node_indices, surface_flux, equations) + for v in eachvariable(equations) + surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] + end +end + +function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) + cartesian_indices = CartesianIndices(size(mesh)) + if orientation == 1 + # Get index of element in y-direction + cell_indices = (cartesian_indices[element][2],) + else # orientation == 2 + # Get index of element in x-direction + cell_indices = (cartesian_indices[element][1],) + end + + return cell_indices +end + +function get_boundary_indices(element, orientation, mesh::StructuredMesh{3}) + cartesian_indices = CartesianIndices(size(mesh)) + # Boundary indices of element are the two indices in the other dimensions + if orientation == 1 + cell_indices = (cartesian_indices[element][2], cartesian_indices[element][3]) + elseif orientation == 2 + cell_indices = (cartesian_indices[element][1], cartesian_indices[element][3]) + else # orientation == 3 + cell_indices = (cartesian_indices[element][1], cartesian_indices[element][2]) + end + + return cell_indices +end + + +@inline ndofs(mesh::StructuredMesh, dg::DG, cache) = nelements(cache.elements) * nnodes(dg)^ndims(mesh) + + include("containers.jl") include("dg_1d.jl") include("dg_2d.jl") diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index 3a68bced409..9c1914d0a96 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -561,4 +561,21 @@ function apply_jacobian!(du, end +function total_volume(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, dg, cache) + @unpack weights = dg.basis + + total_volume = zero(real(mesh)) + + # Use quadrature to numerically integrate over entire domain + for element in eachelement(dg, cache) + for j in eachnode(dg), i in eachnode(dg) + volume_jacobian = abs(inv(cache.elements.inverse_jacobian[i, j, element])) + total_volume += volume_jacobian * weights[i] * weights[j] + end + end + + return total_volume +end + + end # @muladd diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 2c823042c7d..98d82f712b4 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -706,4 +706,21 @@ function apply_jacobian!(du, end +function total_volume(mesh::Union{StructuredMesh{3}, P4estMesh{3}}, dg, cache) + @unpack weights = dg.basis + + total_volume = zero(real(mesh)) + + # Use quadrature to numerically integrate over entire domain + for element in eachelement(dg, cache) + for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg) + volume_jacobian = abs(inv(cache.elements.inverse_jacobian[i, j, k, element])) + total_volume += volume_jacobian * weights[i] * weights[j] * weights[k] + end + end + + return total_volume +end + + end # @muladd diff --git a/src/visualization/types.jl b/src/visualization/types.jl index a83b5bc92c6..bdd2e1d20e2 100644 --- a/src/visualization/types.jl +++ b/src/visualization/types.jl @@ -345,6 +345,42 @@ function PlotData2D(u::StructArray, mesh, equations, dg::DGMulti, cache; return PlotData2DTriangulated(x_plot, y_plot, u_plot, t, x_face, y_face, face_data, variable_names) end +function PlotData2D(u_ode::AbstractVector, semi::SemidiscretizationCoupled; + solution_variables=nothing, nvisnodes=2*polydeg(semi), kwargs...) + @assert ndims(semi) == 2 "unsupported number of dimensions $ndims (must be 2)" + + x_vec = [] + y_vec = [] + data_vec = [] + pd = 0 + + @unpack semis, u_indices = semi + for i in 1:nmeshes(semi) + mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) + + u = wrap_array(u_ode[u_indices[i]], semis[i]) + + pd = PlotData2D(u, mesh::Union{StructuredMesh, UnstructuredMesh2D, P4estMesh}, equations, solver, cache; + solution_variables=solution_variables, nvisnodes=nvisnodes) + + push!(x_vec, pd.x) + push!(y_vec, pd.y) + push!(data_vec, pd.data) + end + + xplot = hcat(x_vec...) + yplot = hcat(y_vec...) + data = hcat(data_vec...) + + t = pd.t + xfp = pd.x_face + yfp = pd.y_face + ufp = pd.face_data + variable_names = pd.variable_names + + return PlotData2DTriangulated(xplot, yplot, data, t, xfp, yfp, ufp, variable_names) +end + # specializes the PlotData2D constructor to return an PlotData2DTriangulated for any type of mesh. function PlotData2DTriangulated(u, mesh, equations, dg::DGSEM, cache; solution_variables=nothing, nvisnodes=2*polydeg(dg)) diff --git a/test/test_visualization.jl b/test/test_visualization.jl index ab083489e21..3da2859433e 100644 --- a/test/test_visualization.jl +++ b/test/test_visualization.jl @@ -27,6 +27,7 @@ isdir(outdir) && rm(outdir, recursive=true) "UnstructuredMesh" => ("unstructured_2d_dgsem", "elixir_euler_basic.jl"), "P4estMesh" => ("p4est_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), "DGMulti" => ("dgmulti_2d", "elixir_euler_weakform.jl"), + "Coupled StructuredMesh" => ("structured_2d_dgsem", "elixir_euler_source_terms_ring_coupled.jl") ) @testset "PlotData2D, PlotDataSeries, PlotMesh with $mesh" for mesh in keys(test_examples_2d) From 5faea691344eb1b762f90e6996b8faf5dd1a5781 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 10:54:09 +0100 Subject: [PATCH 02/83] Added polytropic equation. --- src/equations/polytropic_euler_2d.jl | 674 +++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 src/equations/polytropic_euler_2d.jl diff --git a/src/equations/polytropic_euler_2d.jl b/src/equations/polytropic_euler_2d.jl new file mode 100644 index 00000000000..b622585f86b --- /dev/null +++ b/src/equations/polytropic_euler_2d.jl @@ -0,0 +1,674 @@ +# 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 + + +@doc raw""" + PolytropicEulerEquations2D(gamma, kappa) + +The compressible Euler equations +```math +\partial t +\begin{pmatrix} +\rho \\ \rho v_1 \\ \rho v_2 +\end{pmatrix} ++ +\partial x +\begin{pmatrix} + \rho v_1 \\ \rho v_1^2 + \kappa\rho^\gamma \\ \rho v_1 v_2 +\end{pmatrix} ++ +\partial y +\begin{pmatrix} +\rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + \kappa\rho^\gamma +\end{pmatrix} += +\begin{pmatrix} +0 \\ 0 \\ 0 +\end{pmatrix} +``` +for an ideal gas with ratio of specific heats `gamma` +in two space dimensions. +Here, ``\rho`` is the density and ``v_1`` and`v_2` the velocities and +```math +p = \kappa\rho^\gamma +``` +the pressure, which we replaced using this relation. + +""" +struct PolytropicEulerEquations2D{RealT<:Real, RealT<:Real} <: AbstractPolytropicEulerEquations{2, 3} + gamma::RealT # ratio of specific heats + kappa::RealT # fluid scaling factor + inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications + + function PolytropicEulerEquations2D(gamma, kappa) + new{typeof(gamma), typeof(kappa)}(gamma, kappa) + end +end + + +varnames(::typeof(cons2cons), ::PolytropicEulerEquations2D) = ("rho", "rho_v1", "rho_v2") +varnames(::typeof(cons2prim), ::PolytropicEulerEquations2D) = ("rho", "v1", "v2") + + +# Set initial conditions at physical location `x` for time `t` +""" + initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) + +A constant initial condition to test free-stream preservation. +""" +function initial_condition_constant(x, t, equations::PolytropicEulerEquations2D) + rho = 1.0 + rho_v1 = 0.1 + rho_v2 = -0.2 + + return SVector(rho, rho_v1, rho_v2) +end + + +""" + initial_condition_convergence_test(x, t, equations::PolytropicEulerEquations2D) + +A smooth initial condition used for convergence tests in combination with +[`source_terms_convergence_test`](@ref) +(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). +""" +function initial_condition_convergence_test(x, t, equations::PolytropicEulerEquations2D) + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + rho = ini + rho_v1 = ini + rho_v2 = ini + + return SVector(rho, rho_v1, rho_v2) +end + + +""" + initial_condition_density_wave(x, t, equations::PolytropicEulerEquations2D) + +A sine wave in the density with constant velocity and pressure; reduces the +compressible Euler equations to the linear advection equations. +This setup is the test case for stability of EC fluxes from paper +- Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) + Stability issues of entropy-stable and/or split-form high-order schemes + [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) +with the following parameters +- domain [-1, 1] +- mesh = 4x4 +- polydeg = 5 +""" +function initial_condition_density_wave(x, t, equations::PolytropicEulerEquations2D) + v1 = 0.1 + v2 = 0.2 + rho = 1 + 0.98 * sinpi(2 * (x[1] + x[2] - t * (v1 + v2))) + rho_v1 = rho * v1 + rho_v2 = rho * v2 + p = 20 + return SVector(rho, rho_v1, rho_v2) +end + + +""" + boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, + equations::PolytropicEulerEquations2D) + +Determine the boundary numerical surface flux for a slip wall condition. +Imposes a zero normal velocity at the wall. +Density is taken from the internal solution state and pressure is computed as an +exact solution of a 1D Riemann problem. Further details about this boundary state +are available in the paper: +- J. J. W. van der Vegt and H. van der Ven (2002) + Slip flow boundary conditions in discontinuous Galerkin discretizations of + the Euler equations of gas dynamics + [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) + +Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book +- Eleuterio F. Toro (2009) + Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + 3rd edition + [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + +Should be used together with [`UnstructuredMesh2D`](@ref). +""" +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + x, t, + surface_flux_function, + equations::PolytropicEulerEquations2D) + + norm_ = norm(normal_direction) + # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later + normal = normal_direction / norm_ + + # rotate the internal solution state + u_local = rotate_to_x(u_inner, normal, equations) + p_local = equations.kappa*rho^equations.gamma + + # compute the primitive variables + rho_local, v_normal, v_tangent = cons2prim(u_local, equations) + + # Get the solution of the pressure Riemann problem + # See Section 6.3.3 of + # Eleuterio F. Toro (2009) + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + if v_normal <= 0.0 + sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed + p_star = p_local * (1.0 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2.0 * equations.gamma * equations.inv_gamma_minus_one) + else # v_normal > 0.0 + A = 2.0 / ((equations.gamma + 1) * rho_local) + B = p_local * (equations.gamma - 1) / (equations.gamma + 1) + p_star = p_local + 0.5 * v_normal / A * (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) + end + + # For the slip wall we directly set the flux as the normal velocity is zero + return SVector(zero(eltype(u_inner)), + p_star * normal[1], + p_star * normal[2]) * norm_ +end + +""" + boundary_condition_slip_wall(u_inner, orientation, direction, x, t, + surface_flux_function, equations::PolytropicEulerEquations2D) + +Should be used together with [`TreeMesh`](@ref). +""" +@inline function boundary_condition_slip_wall(u_inner, orientation, + direction, x, t, + surface_flux_function, + equations::PolytropicEulerEquations2D) + # get the appropriate normal vector from the orientation + if orientation == 1 + normal = SVector(1, 0) + else # orientation == 2 + normal = SVector(0, 1) + end + + # compute and return the flux using `boundary_condition_slip_wall` routine above + return boundary_condition_slip_wall(u_inner, normal, x, t, surface_flux_function, equations) +end + +""" + boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, + surface_flux_function, equations::PolytropicEulerEquations2D) + +Should be used together with [`StructuredMesh`](@ref). +""" +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + direction, x, t, + surface_flux_function, + equations::PolytropicEulerEquations2D) + # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back + # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh + if isodd(direction) + boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, + x, t, surface_flux_function, equations) + else + boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, + x, t, surface_flux_function, equations) + end + + return boundary_flux +end + + +# Calculate 1D flux for a single point +@inline function flux(u, orientation::Integer, equations::PolytropicEulerEquations2D) + rho, rho_v1, rho_v2 = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = equations.kappa*rho^equations.gamma + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = rho_v1 * v2 + else + f1 = rho_v2 + f2 = rho_v2 * v1 + f3 = rho_v2 * v2 + p + end + return SVector(f1, f2, f3) +end + +# Calculate 1D flux for a single point in the normal direction +# Note, this directional vector is not normalized +@inline function flux(u, normal_direction::AbstractVector, equations::PolytropicEulerEquations2D) + rho_e = last(u) + rho, v1, v2 = cons2prim(u, equations) + p = equations.kappa*rho^equations.gamma + + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = rho_v_normal + f2 = rho_v_normal * v1 + p * normal_direction[1] + f3 = rho_v_normal * v2 + p * normal_direction[2] + return SVector(f1, f2, f3) +end + + +""" + flux_hllc(u_ll, u_rr, orientation, equations::PolytropicEulerEquations2D) + +Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro +[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf) +Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140) +""" +function flux_hllc(u_ll, u_rr, orientation::Integer, equations::PolytropicEulerEquations2D) + # Calculate primitive variables and speed of sound + rho_ll, rho_v1_ll, rho_v2_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + v2_ll = rho_v2_ll / rho_ll + p_ll = equations.kappa * rho_ll^equations.gamma + e_ll = p_ll * rho_ll / (equations.gamma - 1) + rho_e_ll = rho_ll * e_ll + c_ll = sqrt(equations.gamma*p_ll/rho_ll) + + v1_rr = rho_v1_rr / rho_rr + v2_rr = rho_v2_rr / rho_rr + p_rr = equations.kappa * rho_rr^equations.gamma + e_rr = p_rr * rho_rr / (equations.gamma - 1) + rho_e_rr = rho_rr * e_rr + c_rr = sqrt(equations.gamma*p_rr/rho_rr) + + # Obtain left and right fluxes + f_ll = flux(u_ll, orientation, equations) + f_rr = flux(u_rr, orientation, equations) + + # Compute Roe averages + sqrt_rho_ll = sqrt(rho_ll) + sqrt_rho_rr = sqrt(rho_rr) + sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr + if orientation == 1 # x-direction + vel_L = v1_ll + vel_R = v1_rr + ekin_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr)^2 + elseif orientation == 2 # y-direction + vel_L = v2_ll + vel_R = v2_rr + ekin_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr)^2 + end + vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho + ekin_roe = 0.5 * (vel_roe^2 + ekin_roe / sum_sqrt_rho^2) + H_ll = (rho_e_ll + p_ll) / rho_ll + H_rr = (rho_e_rr + p_rr) / rho_rr + H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho + c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe)) + Ssl = min(vel_L - c_ll, vel_roe - c_roe) + Ssr = max(vel_R + c_rr, vel_roe + c_roe) + sMu_L = Ssl - vel_L + sMu_R = Ssr - vel_R + + if Ssl >= 0.0 + f1 = f_ll[1] + f2 = f_ll[2] + f3 = f_ll[3] + elseif Ssr <= 0.0 + f1 = f_rr[1] + f2 = f_rr[2] + f3 = f_rr[3] + else + SStar = (p_rr - p_ll + rho_ll*vel_L*sMu_L - rho_rr*vel_R*sMu_R) / (rho_ll*sMu_L - rho_rr*sMu_R) + if Ssl <= 0.0 <= SStar + densStar = rho_ll*sMu_L / (Ssl-SStar) + enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L)) + UStar1 = densStar + UStar4 = densStar*enerStar + if orientation == 1 # x-direction + UStar2 = densStar*SStar + UStar3 = densStar*v2_ll + elseif orientation == 2 # y-direction + UStar2 = densStar*v1_ll + UStar3 = densStar*SStar + end + f1 = f_ll[1]+Ssl*(UStar1 - rho_ll) + f2 = f_ll[2]+Ssl*(UStar2 - rho_v1_ll) + f3 = f_ll[3]+Ssl*(UStar3 - rho_v2_ll) + else + densStar = rho_rr*sMu_R / (Ssr-SStar) + enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R)) + UStar1 = densStar + UStar4 = densStar*enerStar + if orientation == 1 # x-direction + UStar2 = densStar*SStar + UStar3 = densStar*v2_rr + elseif orientation == 2 # y-direction + UStar2 = densStar*v1_rr + UStar3 = densStar*SStar + end + f1 = f_rr[1]+Ssr*(UStar1 - rho_rr) + f2 = f_rr[2]+Ssr*(UStar2 - rho_v1_rr) + f3 = f_rr[3]+Ssr*(UStar3 - rho_v2_rr) + end + end + return SVector(f1, f2, f3) + end + + + +""" + flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, + equations::PolytropicEulerEquations2D) + +Entropy conserving and kinetic energy preserving two-point flux by +- Hendrik Ranocha (2018) + Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods + for Hyperbolic Balance Laws + [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) +See also +- Hendrik Ranocha (2020) + Entropy Conserving and Kinetic Energy Preserving Numerical Methods for + the Euler Equations Using Summation-by-Parts Operators + [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) +""" +@inline function flux_ranocha(u_ll, u_rr, orientation::Integer, equations::PolytropicEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) + p_ll = equations.kappa*rho_ll^equations.gamma + p_rr = equations.kappa*rho_rr^equations.gamma + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` + # in exact arithmetic since + # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) + # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) + inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_avg = 0.5 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll*v1_rr + v2_ll*v2_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * v2_avg + else + f1 = rho_mean * v2_avg + f2 = f1 * v1_avg + f3 = f1 * v2_avg + p_avg + end + + return SVector(f1, f2, f3) +end + + +@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector, equations::PolytropicEulerEquations2D) + # Unpack left and right state + rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) + p_ll = equations.kappa*rho_ll^equations.gamma + p_rr = equations.kappa*rho_rr^equations.gamma + v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` + # in exact arithmetic since + # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) + # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) + inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + p_avg = 0.5 * (p_ll + p_rr) + + # Calculate fluxes depending on normal_direction + f1 = rho_mean * 0.5 * (v_dot_n_ll + v_dot_n_rr) + f2 = f1 * v1_avg + p_avg * normal_direction[1] + f3 = f1 * v2_avg + p_avg * normal_direction[2] + + return SVector(f1, f2, f3) +end + + +# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the +# maximum velocity magnitude plus the maximum speed of sound +@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::PolytropicEulerEquations2D) + rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) + p_ll = equations.kappa*rho_ll^equations.gamma + p_rr = equations.kappa*rho_rr^equations.gamma + + # Get the velocity value in the appropriate direction + if orientation == 1 + v_ll = v1_ll + v_rr = v1_rr + else # orientation == 2 + v_ll = v2_ll + v_rr = v2_rr + end + # Calculate sound speeds + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + λ_max = max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) +end + + +@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::PolytropicEulerEquations2D) + rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) + p_ll = equations.kappa*rho_ll^equations.gamma + p_rr = equations.kappa*rho_rr^equations.gamma + + # Calculate normal velocities and sound speed + # left + v_ll = ( v1_ll * normal_direction[1] + + v2_ll * normal_direction[2] ) + c_ll = sqrt(equations.gamma * p_ll / rho_ll) + # right + v_rr = ( v1_rr * normal_direction[1] + + v2_rr * normal_direction[2] ) + c_rr = sqrt(equations.gamma * p_rr / rho_rr) + + return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) +end + + +# Calculate minimum and maximum wave speeds for HLL-type fluxes +@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, + equations::PolytropicEulerEquations2D) + rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) + p_ll = equations.kappa*rho_ll^equations.gamma + p_rr = equations.kappa*rho_rr^equations.gamma + + if orientation == 1 # x-direction + λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll) + λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr) + else # y-direction + λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll) + λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr) + end + + return λ_min, λ_max +end + +@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::PolytropicEulerEquations2D) + rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) + p_ll = equations.kappa*rho_ll^equations.gamma + p_rr = equations.kappa*rho_rr^equations.gamma + + v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + norm_ = norm(normal_direction) + # The v_normals are already scaled by the norm + λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_ + λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_ + + return λ_min, λ_max +end + + +# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction +# has been normalized prior to this rotation of the state vector +@inline function rotate_to_x(u, normal_vector, equations::PolytropicEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D rotation matrix with normal and tangent directions of the form + # [ 1 0 0 + # 0 n_1 t_1 + # 0 n_2 t_2 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] + s * u[3], + -s * u[2] + c * u[3]) +end + + +# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction +# has been normalized prior to this back-rotation of the state vector +@inline function rotate_from_x(u, normal_vector, equations::PolytropicEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D back-rotation matrix with normal and tangent directions of the form + # [ 1 0 0 + # 0 n_1 t_1 + # 0 n_2 t_2 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] - s * u[3], + s * u[2] + c * u[3]) +end + + +@inline function max_abs_speeds(u, equations::PolytropicEulerEquations2D) + rho, v1, v2 = cons2prim(u, equations) + c = sqrt(equations.gamma * equations.kappa*rho^(equations.gamma-1)) + + return abs(v1) + c, abs(v2) + c +end + + +# Convert conservative variables to primitive +@inline function cons2prim(u, equations::PolytropicEulerEquations2D) + rho, rho_v1, rho_v2 = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + return SVector(rho, v1, v2) +end + + +# Convert conservative variables to entropy +@inline function cons2entropy(u, equations::PolytropicEulerEquations2D) + rho, rho_v1, rho_v2 = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + v_square = v1^2 + v2^2 + p = equations.kappa * rho^equations.gamma + s = rho/2*v_square + rho*equations.kappa*rho^(equations.gamma-1)/(equations.gamma-1) + rho_p = rho / p + + w1 = (equations.gamma - s) * (equations.gamma - 1) - 0.5 * rho_p * v_square + w2 = rho_p * v1 + w3 = rho_p * v2 + + return SVector(w1, w2, w3) +end + +# TODO: Do we need this? (SC) +# @inline function entropy2cons(w, equations::PolytropicEulerEquations2D) +# # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD +# # [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1) +# @unpack gamma, kappa = equations + +# # convert to entropy `-rho * s` used by Hughes, France, Mallet (1986) +# # instead of `-rho * s / (gamma - 1)` +# V1, V2, V3, V5 = w .* (gamma-1) + +# # s = specific entropy, eq. (53) +# s = gamma - V1 + (V2^2 + V3^2)/(2*V5) + +# # eq. (52) +# rho_iota = ((gamma-1) / (-V5)^gamma)^(equations.inv_gamma_minus_one)*exp(-s * equations.inv_gamma_minus_one) + +# # eq. (51) +# rho = -rho_iota * V5 +# rho_v1 = rho_iota * V2 +# rho_v2 = rho_iota * V3 +# rho_e = rho_iota * (1-(V2^2 + V3^2)/(2*V5)) +# return SVector(rho, rho_v1, rho_v2, rho_e) +# end + + + + +# Convert primitive to conservative variables +@inline function prim2cons(prim, equations::PolytropicEulerEquations2D) + rho, v1, v2 = prim + rho_v1 = rho * v1 + rho_v2 = rho * v2 + return SVector(rho, rho_v1, rho_v2) +end + + +@inline function density(u, equations::PolytropicEulerEquations2D) + rho = u[1] + return rho +end + + +@inline function pressure(u, equations::PolytropicEulerEquations2D) + rho, rho_v1, rho_v2 = u + p = equations.kappa*rho^equations.gamma + return p +end + + +@inline function density_pressure(u, equations::PolytropicEulerEquations2D) + rho, rho_v1, rho_v2 = u + rho_times_p = equations.kappa*rho^(equations.gamma+1) + return rho_times_p +end + + +# Calculate thermodynamic entropy for a conservative state `cons` +@inline function entropy_thermodynamic(cons, equations::PolytropicEulerEquations2D) + # Pressure + p = equations.kappa*cons[1]^equations.gamma + + # Thermodynamic entropy + s = log(p) - equations.gamma*log(cons[1]) + + return s +end + + +# Calculate mathematical entropy for a conservative state `cons` +@inline function entropy_math(cons, equations::PolytropicEulerEquations2D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equations) * cons[1] * (equations.gamma - 1) + + return S +end + + +# Default entropy is the mathematical entropy +@inline entropy(cons, equations::PolytropicEulerEquations2D) = entropy_math(cons, equations) + +end # @muladd From 17d440ab49632e2814c82ecaed69272c58ab7ce4 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 10:54:36 +0100 Subject: [PATCH 03/83] Added further polytropic equations and examples. --- .../elixir_coupled_polytropic_euler.jl | 159 ++++++++++++++++++ src/equations/coupled_polytropic_euler_2d.jl | 140 +++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl create mode 100644 src/equations/coupled_polytropic_euler_2d.jl diff --git a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl new file mode 100644 index 00000000000..175dee1ebf1 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl @@ -0,0 +1,159 @@ +using Revise +using Infiltrator +using AbbreviatedStackTraces +using OrdinaryDiffEq +using Trixi2Vtk +using Trixi + + +""" +Coupled two polytropic Euler systems. +""" + + +############################################################################### +# define the initial conditions + +function initial_condition_wave_isotropic(x, t, equations::PolytropicEulerEquations2D) + gamma = 2.0 + kappa = 1.0 + + rho = 1.0 + v1 = 0.0 + if x[1] > 10.0 + rho = ((1.0 + 0.01*sin(x[1]*2*pi)) / kappa)^(1/gamma) + v1 = ((0.01*sin((x[1]-1/2)*2*pi)) / kappa) + end + v2 = 0.0 + + return prim2cons(SVector(rho, v1, v2), equations) +end + +function initial_condition_wave_polytropic(x, t, equations::PolytropicEulerEquations2D) + gamma = 2.0 + kappa = 1.0 + + rho = 1.0 + v1 = 0.0 + if x[1] > 0.0 + rho = ((1.0 + 0.01*sin(x[1]*2*pi)) / kappa)^(1/gamma) + v1 = ((0.01*sin((x[1]-1/2)*2*pi)) / kappa) + end + v2 = 0.0 + + return prim2cons(SVector(rho, v1, v2), equations) +end + +############################################################################### +# semidiscretization of the linear advection equation + +Trixi.wrap_array(u_ode::AbstractVector, mesh::Trixi.AbstractMesh, equations, dg::DGSEM, cache) = Trixi.wrap_array_native(u_ode, mesh, equations, dg, cache) + +# Define the equations involved. +gamma_A = 1.001 +kappa_A = 1.0 +equations_A = PolytropicEulerEquations2D(gamma_A, kappa_A) +gamma_B = 2.0 +kappa_B = 1.0 +equations_B = PolytropicEulerEquations2D(gamma_B, kappa_B) + +equations_coupling = CouplingPolytropicEuler2D(0.01) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +volume_flux = flux_ranocha +solver = DGSEM(polydeg=2, surface_flux=flux_hll, + volume_integral=VolumeIntegralFluxDifferencing(volume_flux)) + +coordinates_min_A = (-2.0, -1.0) # minimum coordinates (min(x), min(y)) +coordinates_max_A = ( 0.0, 1.0) # maximum coordinates (max(x), max(y)) +coordinates_min_B = ( 0.0, -1.0) # minimum coordinates (min(x), min(y)) +coordinates_max_B = ( 2.0, 1.0) # maximum coordinates (max(x), max(y)) + +cells_per_dimension = (32, 32) + +mesh_A = StructuredMesh(cells_per_dimension, + coordinates_min_A, + coordinates_max_A) +mesh_B = StructuredMesh(cells_per_dimension, + coordinates_min_B, + coordinates_max_B) + +# A semidiscretization collects data structures and functions for the spatial discretization. +semi_A = SemidiscretizationHyperbolic(mesh_A, equations_A, + initial_condition_wave_isotropic, solver, + boundary_conditions=( +# x_neg=BoundaryConditionCoupledAB(2, (:end, :i_forward), Float64, equations_B, equations_coupling), + x_neg=BoundaryConditionCoupled(2, (:end, :i_forward), Float64), +# x_neg=boundary_condition_periodic, +# x_pos=BoundaryConditionCoupledAB(2, (:begin, :i_forward), Float64, equations_B, equations_coupling), + x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64), +# x_pos=boundary_condition_periodic, + y_neg=boundary_condition_periodic, + y_pos=boundary_condition_periodic)) + +semi_B = SemidiscretizationHyperbolic(mesh_B, equations_B, + initial_condition_wave_polytropic, solver, + boundary_conditions=( +# x_neg=BoundaryConditionCoupledAB(1, (:end, :i_forward), Float64, equations_A, equations_coupling), + x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64), +# x_neg=boundary_condition_periodic, +# x_pos=BoundaryConditionCoupledAB(1, (:begin, :i_forward), Float64, equations_A, equations_coupling), + x_pos=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64), +# x_pos=boundary_condition_periodic, + y_neg=boundary_condition_periodic, + y_pos=boundary_condition_periodic)) + +# Define the indices of the 'other' system. +# This is not ideal. +other_list = [2, 1] + +# Create a semidiscretization that bundles semi1 and semi2 +semi_coupled = SemidiscretizationCoupled((semi_A, semi_B), other_list) + + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 30.0 +ode = semidiscretize(semi_coupled, (0.0, 3.0)) + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +resid_tol = 5.0e-12 +steady_state_callback = SteadyStateCallback(abstol=resid_tol, reltol=0.0) + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi_coupled, interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +# The SaveSolutionCallback allows to save the solution to a file in regular intervals +save_solution = SaveSolutionCallback(interval=5, + solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.0) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, + alive_callback, +# analysis_callback, + save_solution, + stepsize_callback) + + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() + +# Convert the snapshots into vtk format. +trixi2vtk("out/solution_*.h5") diff --git a/src/equations/coupled_polytropic_euler_2d.jl b/src/equations/coupled_polytropic_euler_2d.jl new file mode 100644 index 00000000000..cf70e90b651 --- /dev/null +++ b/src/equations/coupled_polytropic_euler_2d.jl @@ -0,0 +1,140 @@ +@muladd begin + + struct CouplingPolytropicEuler2D{RealT<:Real} <: AbstractCouplingEquations{2, 6} + coupling_nu::RealT # diffusion constant at the coupling interface + end + + + function CouplingPolytropicEuler2D(u::NTuple{6,<:Real}) + CouplingPolytropicEuler2D(SVector(u)) + end + + + function CouplingPolytropicEuler2D(u_polytropic_euler_A::NTuple{4,<:Real}, u_polytropic_euler_B::NTuple{3,<:Real}) + CouplingPolytropicEuler2D(vcat(u_polytropic_euler_A, u_polytropic_euler_B)) + end + + + varnames(::typeof(cons2cons), ::CouplingPolytropicEuler2D) = ("rho", "rho_v1", "rho_v2", "rho", "rho_v1", "rho_v2") + varnames(::typeof(cons2prim), ::CouplingPolytropicEuler2D) = ("rho", "v1", "v2", "rho", "v1", "v2") + + default_analysis_errors(::CouplingPolytropicEuler2D) = (:l2_error, :linf_error, :residual) + + + @inline function residual_steady_state(du, ::CouplingPolytropicEuler2D) + abs(du[1]) + end + + + # Calculate 1D flux in for a single point + @inline function flux(u, orientation::Integer, equations::CouplingPolytropicEuler2D) + rho_a, rho_v1_a, rho_v2_a, rho_b, rho_v1_b, rho_v2_b = u + v1_a = rho_v1_a / rho_a + v2_a = rho_v2_a / rho_a + v1_b = rho_v1_b / rho_b + v2_b = rho_v2_b / rho_b + + if orientation == 1 + f1 = rho_b + f2 = v1_b + f3 = v2_b + f4 = rho_a + f5 = v1_a + f6 = v2_a + else + f1 = rho_b + f2 = v1_b + f3 = v2_b + f4 = rho_a + f5 = v1_a + f6 = v2_a + end + + return SVector(f1, f2, f3, f4, f5, f6) + end + + + # Note, this directional vector is not normalized + @inline function flux(u, normal_direction::AbstractVector, equations::CouplingPolytropicEuler2D) + rho_a, rho_v1_a, rho_v2_a, rho_b, rho_v1_b, rho_v2_b = u + rho_a, v1_a, v2_a, rho_b, v1_b, v2_b = cons2prim(u, equations) + + v_normal_a = v1_a * normal_direction[1] + v2_a * normal_direction[2] + v_normal_b = v1_b * normal_direction[1] + v2_b * normal_direction[2] + + f1 = rho_b + f2 = v1_b + f3 = v2_b + f4 = rho_a + f5 = v1_a + f6 = v2_a + + return SVector(f1, f2, f3, f4, f5, f6) + end + + + # Calculate maximum wave speed for local Lax-Friedrichs-type dissipation + @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::CouplingPolytropicEuler2D) + return equations.coupling_nu + end + + + @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::CouplingPolytropicEuler2D) + # rho_ll, rho_v1_ll, rho_v2_ll, rho_rr, rho_v1_rr, rho_v2_rr = (u_ll + u_rr) + # rho, v1, v2, p, phi, q1, q2 = (cons2prim(u_ll, equations) + cons2prim(u_rr, equations))/2 + + # v = (q1 * normal_direction[1] + q2 * normal_direction[2]) + + # @infiltrate + + # Polytropic speed of sound is c = sqrt(gamma kappa rho^(gamma-1)). + return 1.0 + end + + + @inline have_constant_speed(::CouplingPolytropicEuler2D) = Val(true) + + + @inline function max_abs_speeds(eq::CouplingPolytropicEuler2D) + λ = eq.nu + return 1.0, 1.0 + end + + + # Convert conservative variables to primitive + @inline cons2prim(u, equations::CouplingPolytropicEuler2D) = u + + + # Calculate entropy for a conservative state `u` (here: same as total energy) + @inline entropy(u, equations::CouplingPolytropicEuler2D) = energy_total(u, equations) + + + # Calculate total energy for a conservative state `u` + @inline function energy_total(u, equations::CouplingPolytropicEuler2D) + # energy function as found in equations (2.5.12) in the book "I Do Like CFD, Vol. 1" + rho_a, rho_v1_a, rho_v2_a, rho_b, rho_v1_b, rho_v2_b = u + return (rho_v1_a^2 + rho_v2_a^2 + rho_v1_b^2 + rho_v2_b^2) / (2 * rho_a * rho_b) + end + + + # Calculate minimum and maximum wave speeds for HLL-type fluxes + @inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CouplingPolytropicEuler2D) + λ_min = 0 + λ_max = abs(equations.coupling_nu) + + return 1.0, 1.0 + end + + + @inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CouplingPolytropicEuler2D) + + λ_min = 0 + λ_max = abs(equations.coupling_nu) + + return 1.0, 1.0 + end + + end # @muladd + \ No newline at end of file From 2dd9306bc71bba6801b45410840362e6b9431291 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 10:55:27 +0100 Subject: [PATCH 04/83] Added coupling equations. --- .../elixir_advection_basic_coupled.jl | 86 ++++ .../semidiscretization_coupled.jl | 448 ++++++++++++++++++ 2 files changed, 534 insertions(+) create mode 100644 examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl create mode 100644 src/semidiscretization/semidiscretization_coupled.jl diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl new file mode 100644 index 00000000000..9134b9480d3 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -0,0 +1,86 @@ +using OrdinaryDiffEq +using Trixi + + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +# First mesh is the left half of a [-1,1]^2 square +coordinates_min1 = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) +coordinates_max1 = ( 0.0, 1.0) # maximum coordinates (max(x), max(y)) + +cells_per_dimension1 = (8, 16) + +mesh1 = StructuredMesh(cells_per_dimension1, coordinates_min1, coordinates_max1) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi1 = SemidiscretizationHyperbolic(mesh1, equations, initial_condition_convergence_test, solver, + boundary_conditions=( + # Connect left boundary with right boundary of right mesh + x_neg=BoundaryConditionCoupled(2, (:end, :i_forward), Float64), + # Connect right boundary with left boundary of right mesh + x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64), + y_neg=boundary_condition_periodic, + y_pos=boundary_condition_periodic)) + + +# Second mesh is the right half of a [-1,1]^2 square +coordinates_min2 = (0.0, -1.0) # minimum coordinates (min(x), min(y)) +coordinates_max2 = (1.0, 1.0) # maximum coordinates (max(x), max(y)) + +cells_per_dimension2 = (8, 16) + +mesh2 = StructuredMesh(cells_per_dimension2, coordinates_min2, coordinates_max2) + +semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition_convergence_test, solver, + boundary_conditions=( + # Connect left boundary with right boundary of left mesh + x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64), + # Connect right boundary with left boundary of left mesh + x_pos=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64), + y_neg=boundary_condition_periodic, + y_pos=boundary_condition_periodic)) + +# Create a semidiscretization that bundles semi1 and semi2 +semi = SemidiscretizationCoupled((semi1, semi2)) + +############################################################################### +# ODE solvers, callbacks etc. + +# Create ODE problem with time span from 0.0 to 1.0 +ode = semidiscretize(semi, (0.0, 1.0)); + +# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup +# and resets the timers +summary_callback = SummaryCallback() + +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback = AnalysisCallback(semi, interval=100) + +# The SaveSolutionCallback allows to save the solution to a file in regular intervals +save_solution = SaveSolutionCallback(interval=100, + solution_variables=cons2prim) + +# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step +stepsize_callback = StepsizeCallback(cfl=1.6) + +# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver +callbacks = CallbackSet(summary_callback, stepsize_callback, analysis_callback, save_solution) + + +############################################################################### +# run the simulation + +# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep=false, callback=callbacks); + +# Print the timer summary +summary_callback() diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl new file mode 100644 index 00000000000..291b7071155 --- /dev/null +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -0,0 +1,448 @@ +""" + SemidiscretizationCoupled + +A struct used to bundle multiple semidiscretizations. +`semidiscretize` will return an `ODEproblem` that synchronizes time steps between the semidiscretizations. +Each call of `rhs!` will call `rhs!` for each semidiscretization individually. +The semidiscretizations can be coupled by gluing meshes together using `BoundaryConditionCoupled`. + +!!! warning "Experimental code" + This is an experimental feature and can change any time. +""" +struct SemidiscretizationCoupled{S, I} <: AbstractSemidiscretization + semis::S + u_indices::I # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] + performance_counter::PerformanceCounter + equation_list::Vector +end + + +""" + SemidiscretizationCoupled(semis, other_list) + +Create a coupled semidiscretization that consists of the semidiscretizations contained in the tuple `semis`. +""" +function SemidiscretizationCoupled(semis, other_list) + # @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" + equation_list = [] + for i in 1:length(semis) + _, equations, _, _ = mesh_equations_solver_cache(semis[i]) + append!(equation_list, [equations]) + end + + # Number of coefficients as Vector + n_coeffs = zeros(Int64, length(semis)) + for i in 1:length(semis) + n_coeffs[i] = (semis[i] |> (x -> nvariables(equation_list[i]) * ndofs(x)) |> collect)[1] + end + u_indices = Vector{UnitRange{Int}}(undef, length(semis)) + + for i in 1:length(semis) + offset = sum(n_coeffs[1:i-1]) + 1 + u_indices[i] = range(offset, length=n_coeffs[i]) + + allocate_coupled_boundary_conditions(semis[i], semis[other_list[i]]) + end + + performance_counter = PerformanceCounter() + + SemidiscretizationCoupled{typeof(semis), typeof(u_indices)}(semis, u_indices, performance_counter, equation_list) +end + + +function Base.show(io::IO, semi::SemidiscretizationCoupled) + @nospecialize semi # reduce precompilation time + + print(io, "SemidiscretizationCoupled($(semi.semis))") +end + +function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) + @nospecialize semi # reduce precompilation time + + if get(io, :compact, false) + show(io, semi) + else + summary_header(io, "SemidiscretizationCoupled") + summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) + summary_line(io, "#meshes", nmeshes(semi)) + for i = 1:nmeshes(semi) + summary_line(io, "equations", mesh_equations_solver_cache(semi.semis[i])[2] |> typeof |> nameof) + summary_line(io, "initial conditions", semi.semis[i].initial_condition) + # TODO boundary conditions? That will be 36 BCs for a cubed sphere + summary_line(io, "source terms", semi.semis[i].source_terms) + summary_line(io, "solvers", mesh_equations_solver_cache(semi.semis[i])[3] |> typeof |> nameof) + end + summary_line(io, "total #DOFs", ndofs(semi)) + summary_footer(io) + end +end + + +function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_context) + show(io_context, MIME"text/plain"(), semi) + println(io, "\n") + for i = 1:nmeshes(semi) + mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) + show(io_context, MIME"text/plain"(), mesh) + println(io, "\n") + show(io_context, MIME"text/plain"(), equations) + println(io, "\n") + show(io_context, MIME"text/plain"(), solver) + println(io, "\n") + end +end + +function Base.summary(semi::SemidiscretizationCoupled) + _, _, solver, _ = mesh_equations_solver_cache(semi.semis[1]) + summary(solver) +end + + +@inline Base.ndims(semi::SemidiscretizationCoupled) = ndims(semi.semis[1]) + +@inline nmeshes(semi::SemidiscretizationCoupled) = length(semi.semis) + +@inline Base.real(semi::SemidiscretizationCoupled) = promote_type(real.(semi.semis)...) + +@inline Base.eltype(semi::SemidiscretizationCoupled) = promote_type(eltype.(semi.semis)...) + +@inline function ndofs(semi::SemidiscretizationCoupled) + sum(ndofs, semi.semis) +end + +# TODO: Ask bout polydeg. +@inline function polydeg(semi::SemidiscretizationCoupled) + _, _, solver, _ = mesh_equations_solver_cache(semi.semis[1]) + + polydeg(solver) +end + +@inline function nelements(semi::SemidiscretizationCoupled) + return sum(semi.semis) do semi_ + mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) + + nelements(mesh, solver, cache) + end +end + +# TODO: Find out where this is being used. +@inline function mesh_equations_solver_cache(semi::SemidiscretizationCoupled) + _, equations, _, _ = mesh_equations_solver_cache(semi.semis[1]) + + return nothing, equations, nothing, nothing +end + + +function calc_error_norms(func, u_ode::AbstractVector, t, analyzers, + semi::SemidiscretizationCoupled, caches_analysis; + normalize=true) + @unpack semis, u_indices = semi + + # Sum up L2 integrals, use max on Linf error + op(x, y) = (x[1] + y[1], max(x[2], y[2])) + + l2_error, linf_error = mapreduce(op, 1:nmeshes(semi)) do i + calc_error_norms(func, u_ode[u_indices[i]], t, analyzers[i], + semis[i], caches_analysis[i]; normalize=false) + end + + if normalize + # For L2 error, divide by total volume + total_volume_ = total_volume(semi) + l2_error = @. sqrt(l2_error / total_volume_) + end + + return l2_error, linf_error +end + + +function integrate(func::Func, u_ode::AbstractVector, semi::SemidiscretizationCoupled; normalize=true) where {Func} + @unpack semis, u_indices = semi + + integral = [] + for i = 1:nmeshes(semi) + mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) + u = wrap_array(u_ode[u_indices[i]], mesh, equations, solver, cache) + integral = vcat(integral, integrate(func, u, mesh, equations, solver, cache, normalize=false)) + end + + # Normalize with total volume + if normalize + total_volume_ = total_volume(semi) + integral = integral / total_volume_ + end + + return integral +end + + +function total_volume(semi::SemidiscretizationCoupled) + sum(semi.semis) do semi_ + mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) + total_volume(mesh, solver, cache) + end +end + + +function compute_coefficients(t, semi::SemidiscretizationCoupled) + @unpack u_indices = semi + + u_ode = Vector{real(semi)}(undef, u_indices[end][end]) + + for i in 1:nmeshes(semi) + # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` + u_ode[u_indices[i]] .= compute_coefficients(t, semi.semis[i]) + end + + return u_ode +end + + +# Don't do anything for other BCs than BoundaryConditionCoupled +function allocate_coupled_boundary_conditions(semi, semi_other) end + +function allocate_coupled_boundary_conditions(semi, semi_other) + n_boundaries = 2 * ndims(semi) + mesh, equations, solver, _ = mesh_equations_solver_cache(semi) + _, equations_other, _, _ = mesh_equations_solver_cache(semi_other) + + for direction in 1:n_boundaries + boundary_condition = semi.boundary_conditions[direction] + + allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, equations_other, solver) + end + +end + +function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, equations_other, solver) end + +# In 2D +function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{2}, direction, mesh, equations, equations_other, dg::DGSEM) + if direction in (1, 2) + cell_size = size(mesh, 2) + else + cell_size = size(mesh, 1) + end + + boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations), nnodes(dg), cell_size) +end + +function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupledAB{2}, direction, mesh, equations, equations_other, dg::DGSEM) + if direction in (1, 2) + cell_size = size(mesh, 2) + else + cell_size = size(mesh, 1) + end + + boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations) + nvariables(equations_other), nnodes(dg), cell_size) +end + +# In 3D +function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{3}, direction, mesh, equations, equations_other, dg::DGSEM) + if direction in (1, 2) + cell_size = (size(mesh, 2), size(mesh, 3)) + elseif direction in (3, 4) + cell_size = (size(mesh, 1), size(mesh, 3)) + else # direction in (5, 6) + (size(mesh, 1), size(mesh, 2)) + end + boundary_condition.u_boundary = Array{Float64, 5}(undef, nvariables(equations), nnodes(dg), nnodes(dg), cell_size...) +end + + +function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) + @unpack u_indices = semi + + time_start = time_ns() + + @trixi_timeit timer() "copy to coupled boundaries" begin + for semi_ in semi.semis + copy_to_coupled_boundary(semi_.boundary_conditions, u_ode, semi) + end + end + + # Call rhs! for each semidiscretization + for i in 1:nmeshes(semi) + u_loc = @view u_ode[u_indices[i]] + du_loc = @view du_ode[u_indices[i]] + + rhs!(du_loc, u_loc, semi.semis[i], t) + end + + runtime = time_ns() - time_start + put!(semi.performance_counter, runtime) + + return nothing +end + + +# Don't do anything for other BCs than BoundaryConditionCoupled +function copy_to_coupled_boundary(boundary_condition, u_ode, semi) end + +function copy_to_coupled_boundary(boundary_conditions::Union{Tuple, NamedTuple}, u_ode, semi) + for boundary_condition in boundary_conditions + copy_to_coupled_boundary(boundary_condition, u_ode, semi) + end +end + +# In 2D +function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{2}, u_ode, semi) + @unpack u_indices = semi + @unpack other_semi_index, other_orientation, indices = boundary_condition + + mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) + @views u = wrap_array(u_ode[u_indices[other_semi_index]], mesh, equations, solver, cache) + + linear_indices = LinearIndices(size(mesh)) + + if other_orientation == 1 + cells = axes(mesh, 2) + else # other_orientation == 2 + cells = axes(mesh, 1) + end + + # Copy solution data to the coupled boundary using "delayed indexing" with + # a start value and a step size to get the correct face and orientation. + node_index_range = eachnode(solver) + i_node_start, i_node_step = index_to_start_step_2d(indices[1], node_index_range) + j_node_start, j_node_step = index_to_start_step_2d(indices[2], node_index_range) + + i_cell_start, i_cell_step = index_to_start_step_2d(indices[1], axes(mesh, 1)) + j_cell_start, j_cell_step = index_to_start_step_2d(indices[2], axes(mesh, 2)) + + i_cell = i_cell_start + j_cell = j_cell_start + + for cell in cells + i_node = i_node_start + j_node = j_node_start + + for i in eachnode(solver) + for v in 1:size(u, 1) + boundary_condition.u_boundary[v, i, cell] = u[v, i_node, j_node, + linear_indices[i_cell, j_cell]] + end + i_node += i_node_step + j_node += j_node_step + end + i_cell += i_cell_step + j_cell += j_cell_step + end +end + +# In 2D +function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupledAB{2}, u_ode, semi) + # Copy the boundary condition from A to B (other to this). + @unpack u_indices = semi + @unpack other_semi_index, other_orientation, indices = boundary_condition + + mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) + @views u = wrap_array(u_ode[u_indices[other_semi_index]], mesh, equations, solver, cache) + + linear_indices = LinearIndices(size(mesh)) + + if other_orientation == 1 + cells = axes(mesh, 2) + else # other_orientation == 2 + cells = axes(mesh, 1) + end + + # Copy solution data to the coupled boundary using "delayed indexing" with + # a start value and a step size to get the correct face and orientation. + node_index_range = eachnode(solver) + i_node_start, i_node_step = index_to_start_step_2d(indices[1], node_index_range) + j_node_start, j_node_step = index_to_start_step_2d(indices[2], node_index_range) + + i_cell_start, i_cell_step = index_to_start_step_2d(indices[1], axes(mesh, 1)) + j_cell_start, j_cell_step = index_to_start_step_2d(indices[2], axes(mesh, 2)) + + i_cell = i_cell_start + j_cell = j_cell_start + + # TODO: This should be computed in the coupled equations module. + if boundary_condition.other_semi_index == 2 + coupled_index_offset = 3 + else + coupled_index_offset = 0 + end + + fill!(boundary_condition.u_boundary, zero(eltype(boundary_condition.u_boundary))) + for cell in cells + i_node = i_node_start + j_node = j_node_start + + for i in eachnode(solver) + for v in 1:size(u, 1) + boundary_condition.u_boundary[v+coupled_index_offset, i, cell] = u[v, i_node, j_node, + linear_indices[i_cell, j_cell]] + end + i_node += i_node_step + j_node += j_node_step + end + i_cell += i_cell_step + j_cell += j_cell_step + end +end + +# In 3D +function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{3}, u_ode, semi) + @unpack u_indices = semi + @unpack other_semi_index, other_orientation, indices = boundary_condition + + mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) + @views u = wrap_array(u_ode[u_indices[other_semi_index]], mesh, equations, solver, cache) + + linear_indices = LinearIndices(size(mesh)) + + if other_orientation == 1 + cells = (axes(mesh, 2), axes(mesh, 3)) + elseif other_orientation == 2 + cells = (axes(mesh, 1), axes(mesh, 3)) + else # other_orientation == 3 + cells = (axes(mesh, 1), axes(mesh, 2)) + end + + # Copy solution data to the coupled boundary using "delayed indexing" with + # a start value and a step size to get the correct face and orientation. + node_index_range = eachnode(solver) + i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(indices[1], node_index_range) + j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(indices[2], node_index_range) + k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(indices[3], node_index_range) + + i_cell_start, i_cell_step_i, i_cell_step_j = index_to_start_step_3d(indices[1], axes(mesh, 1)) + j_cell_start, j_cell_step_i, j_cell_step_j = index_to_start_step_3d(indices[2], axes(mesh, 2)) + k_cell_start, k_cell_step_i, k_cell_step_j = index_to_start_step_3d(indices[3], axes(mesh, 3)) + + i_cell = i_cell_start + j_cell = j_cell_start + k_cell = k_cell_start + + for cell_j in cells[2] + for cell_i in cells[1] + i_node = i_node_start + j_node = j_node_start + k_node = k_node_start + + for j in eachnode(solver) + for i in eachnode(solver) + for v in 1:size(u, 1) + boundary_condition.u_boundary[v, i, j, cell_i, cell_j] = u[v, i_node, j_node, k_node, + linear_indices[i_cell, j_cell, k_cell]] + end + i_node += i_node_step_i + j_node += j_node_step_i + k_node += k_node_step_i + end + i_node += i_node_step_j + j_node += j_node_step_j + k_node += k_node_step_j + end + i_cell += i_cell_step_i + j_cell += j_cell_step_i + k_cell += k_cell_step_i + end + i_cell += i_cell_step_j + j_cell += j_cell_step_j + k_cell += k_cell_step_j + end +end From 1ca87fb071048de513b92e172826ca402950bb5f Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 10:57:26 +0100 Subject: [PATCH 05/83] Added coupling equation between Euler. --- ...pressible_euler_hyperbolic_diffusion_2d.jl | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl diff --git a/src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl b/src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl new file mode 100644 index 00000000000..91a964c224b --- /dev/null +++ b/src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl @@ -0,0 +1,141 @@ +@muladd begin + + struct CouplingCompressibleEulerHyperbolicDiffusion2D{RealT<:Real} <: AbstractCouplingEquations{2, 7} + coupling_nu::RealT # diffusion constant at the coupling interface + end + + + function CouplingCompressibleEulerHyperbolicDiffusion2D(u::NTuple{7,<:Real}) + CouplingCompressibleEulerHyperbolicDiffusion2D(SVector(u)) + end + + + function CouplingCompressibleEulerHyperbolicDiffusion2D(u_compressible_euler::NTuple{4,<:Real}, u_hyperbolic_diffusion::NTuple{3,<:Real}) + CouplingCompressibleEulerHyperbolicDiffusion2D(vcat(u_compressible_euler, u_hyperbolic_diffusion)) + end + + + varnames(::typeof(cons2cons), ::CouplingCompressibleEulerHyperbolicDiffusion2D) = ("rho", "rho_v1", "rho_v2", "rho_e", "phi", "q1", "q2") + varnames(::typeof(cons2prim), ::CouplingCompressibleEulerHyperbolicDiffusion2D) = ("rho", "v1", "v2", "p", "phi", "q1", "q2") + + default_analysis_errors(::CouplingCompressibleEulerHyperbolicDiffusion2D) = (:l2_error, :linf_error, :residual) + + + @inline function residual_steady_state(du, ::CouplingCompressibleEulerHyperbolicDiffusion2D) + abs(du[1]) + end + + + # Calculate 1D flux in for a single point + @inline function flux(u, orientation::Integer, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + if orientation == 1 + f1 = 0.0 + f2 = 0.0 + f3 = 0.0 + f4 = -equations.coupling_nu*phi + f5 = equations.coupling_nu*phi + f6 = 0.0 + f7 = 0.0 + else + f1 = 0.0 + f2 = 0.0 + f3 = 0.0 + f4 = -equations.coupling_nu*phi + f5 = equations.coupling_nu*phi + f6 = 0.0 + f7 = 0.0 + end + + return SVector(f1, f2, f3, f4, f5, f6, f7) + end + + + # Note, this directional vector is not normalized + @inline function flux(u, normal_direction::AbstractVector, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = u + rho, v1, v2, p, phi, q1, q2 = cons2prim(u, equations) + + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = 0.0 + f2 = 0.0 + f3 = 0.0 + f4 = -equations.coupling_nu * phi + + f5 = equations.coupling_nu * phi + f6 = 0.0 + f7 = 0.0 + + return SVector(f1, f2, f3, f4, f5, f6, f7) + end + + + # Calculate maximum wave speed for local Lax-Friedrichs-type dissipation + @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + return equations.coupling_nu + end + + + @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + # rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = (u_ll + u_rr) + # rho, v1, v2, p, phi, q1, q2 = (cons2prim(u_ll, equations) + cons2prim(u_rr, equations))/2 + + # v = (q1 * normal_direction[1] + q2 * normal_direction[2]) + + # return abs(v) * norm(normal_direction) + + return equations.coupling_nu + end + + + @inline have_constant_speed(::CouplingCompressibleEulerHyperbolicDiffusion2D) = Val(true) + + + @inline function max_abs_speeds(eq::CouplingCompressibleEulerHyperbolicDiffusion2D) + λ = sqrt(eq.nu * eq.inv_Tr) + return λ, λ + end + + + # Convert conservative variables to primitive + @inline cons2prim(u, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) = u + + + # Calculate entropy for a conservative state `u` (here: same as total energy) + @inline entropy(u, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) = energy_total(u, equations) + + + # Calculate total energy for a conservative state `u` + @inline function energy_total(u, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + # energy function as found in equations (2.5.12) in the book "I Do Like CFD, Vol. 1" + rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = u + return (rho_v1^2 + rho_v2^2) / (2 * rho) + 0.5 * (phi^2 + equations.Lr^2 * (q1^2 + q2^2)) + end + + + # Calculate minimum and maximum wave speeds for HLL-type fluxes + @inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + λ_min = 0 + λ_max = abs(equations.coupling_nu) + + return λ_min, λ_max + end + + + @inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CouplingCompressibleEulerHyperbolicDiffusion2D) + + λ_min = 0 + λ_max = abs(equations.coupling_nu) + + return λ_min, λ_max + end + + end # @muladd + \ No newline at end of file From e85ed9608a869a6438ebd894c1ec79b9d2a7128a Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 11:12:16 +0100 Subject: [PATCH 06/83] Commented debugging bits, like infiltrator. --- .../elixir_coupled_polytropic_euler.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl index 175dee1ebf1..ef607ee1fb5 100644 --- a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl +++ b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl @@ -1,8 +1,8 @@ -using Revise -using Infiltrator -using AbbreviatedStackTraces -using OrdinaryDiffEq -using Trixi2Vtk +#using Revise +#using Infiltrator +#using AbbreviatedStackTraces +#using OrdinaryDiffEq +#using Trixi2Vtk using Trixi @@ -155,5 +155,5 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), # Print the timer summary summary_callback() -# Convert the snapshots into vtk format. -trixi2vtk("out/solution_*.h5") +## Convert the snapshots into vtk format. +#trixi2vtk("out/solution_*.h5") From 33b1906295f6e5ba59d8468d2ed09093e2fc865c Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 11 May 2023 16:20:47 +0200 Subject: [PATCH 07/83] Add missing `using` --- .../elixir_coupled_polytropic_euler.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl index ef607ee1fb5..beee32b20ca 100644 --- a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl +++ b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl @@ -1,7 +1,7 @@ #using Revise #using Infiltrator #using AbbreviatedStackTraces -#using OrdinaryDiffEq +using OrdinaryDiffEq #using Trixi2Vtk using Trixi @@ -115,15 +115,13 @@ semi_coupled = SemidiscretizationCoupled((semi_A, semi_B), other_list) # ODE solvers, callbacks etc. # Create ODE problem with time span from 0.0 to 30.0 -ode = semidiscretize(semi_coupled, (0.0, 3.0)) +tspan = (0.0, 3.0) +ode = semidiscretize(semi_coupled, tspan) # At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup # and resets the timers summary_callback = SummaryCallback() -resid_tol = 5.0e-12 -steady_state_callback = SteadyStateCallback(abstol=resid_tol, reltol=0.0) - # The AnalysisCallback allows to analyse the solution in regular intervals and prints the results analysis_interval = 100 analysis_callback = AnalysisCallback(semi_coupled, interval=analysis_interval) From 45959e19b2907d8cf43b131a0a2e12214d4578b8 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 11 May 2023 16:25:17 +0200 Subject: [PATCH 08/83] Fix `destats` deprecation warning --- src/callbacks_step/save_solution.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 65690b3103e..7d571c7296b 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -267,7 +267,7 @@ end @inline function save_solution_file(semi::AbstractSemidiscretization, u_ode, solution_callback, integrator; system="") @unpack t, dt = integrator - iter = integrator.destats.naccept + iter = integrator.stats.naccept element_variables = Dict{Symbol, Any}() @trixi_timeit timer() "get element variables" begin From cfe2f8bd1be13dab79103194d3019ef5078a9830 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Thu, 11 May 2023 16:04:59 +0100 Subject: [PATCH 09/83] Added coupled elixir. --- .../elixir_euler_source_terms_ring_coupled.jl | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl diff --git a/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl b/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl new file mode 100644 index 00000000000..0445df6dfd0 --- /dev/null +++ b/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl @@ -0,0 +1,143 @@ + +using OrdinaryDiffEq +using Trixi + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleEulerEquations2D(1.4) + +initial_condition = initial_condition_convergence_test +boundary_condition = BoundaryConditionDirichlet(initial_condition) + +source_terms = source_terms_convergence_test + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +# Gnomonic projection similar to how the coupled cubed sphere is created. +# Note that half the elements will have left-handed coordinates. +function gnomonic_projection(xi, eta, inner_radius, thickness, direction) + alpha = xi * pi/4 + + # Equiangular projection + x = tan(alpha) + + # Coordinates on unit square per direction + square_coordinates = [SVector(-1, x), + SVector( 1, x), + SVector(x, -1), + SVector(x, 1)] + + # Radius on square surface + r = sqrt(1 + x^2) + + # Radius of the sphere + R = inner_radius + thickness * (0.5 * (eta + 1)) + + # Projection onto the sphere + R / r * square_coordinates[direction] +end + +function ring_mapping(inner_radius, thickness, direction) + mapping(xi, eta) = gnomonic_projection(xi, eta, inner_radius, thickness, direction) +end + +mapping_as_string(direction) = """ +function gnomonic_projection(xi, eta, inner_radius, thickness, direction) + alpha = xi * pi/4 + + x = tan(alpha) + + r = sqrt(1 + x^2) + R = inner_radius + thickness * (0.5 * (eta + 1)) + + # Cube coordinates per direction + cube_coordinates = [SVector(-1, x), + SVector( 1, x), + SVector(x, -1), + SVector(x, 1)] + + R / r * cube_coordinates[direction] +end; function ring_mapping(inner_radius, thickness, direction) + mapping(xi, eta) = gnomonic_projection(xi, eta, inner_radius, thickness, direction) +end; mapping = ring_mapping(1, 1, $direction) +""" + + +mesh1 = StructuredMesh((8, 4), ring_mapping(1, 1, 1), + periodicity=false, mapping_as_string=mapping_as_string(1)) + +semi1 = SemidiscretizationHyperbolic(mesh1, equations, initial_condition, solver, + source_terms=source_terms, boundary_conditions=( + x_neg=BoundaryConditionCoupled(3, (:begin, :i_forward), Float64), + x_pos=BoundaryConditionCoupled(4, (:begin, :i_forward), Float64), + y_neg=boundary_condition, + y_pos=boundary_condition, + )) + +mesh2 = StructuredMesh((8, 4), ring_mapping(1, 1, 2), + periodicity=false, mapping_as_string=mapping_as_string(2)) + +semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition, solver, + source_terms=source_terms, boundary_conditions=( + x_neg=BoundaryConditionCoupled(3, (:end, :i_forward), Float64), + x_pos=BoundaryConditionCoupled(4, (:end, :i_forward), Float64), + y_neg=boundary_condition, + y_pos=boundary_condition, + )) + +mesh3 = StructuredMesh((8, 4), ring_mapping(1, 1, 3), + periodicity=false, mapping_as_string=mapping_as_string(3)) + +semi3 = SemidiscretizationHyperbolic(mesh3, equations, initial_condition, solver, + source_terms=source_terms, boundary_conditions=( + x_neg=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64), + x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64), + y_neg=boundary_condition, + y_pos=boundary_condition, + )) + +mesh4 = StructuredMesh((8, 4), ring_mapping(1, 1, 4), + periodicity=false, mapping_as_string=mapping_as_string(4)) + +semi4 = SemidiscretizationHyperbolic(mesh4, equations, initial_condition, solver, + source_terms=source_terms, boundary_conditions=( + x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64), + x_pos=BoundaryConditionCoupled(2, (:end, :i_forward), Float64), + y_neg=boundary_condition, + y_pos=boundary_condition, + )) + +semi = SemidiscretizationCoupled((semi1, semi2, semi3, semi4)) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1.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=1.0) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, + stepsize_callback) +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + 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 From 88884d80d4ed6819e9d8ed0a4a95bc1314bc6a65 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:18:11 +0100 Subject: [PATCH 10/83] Removed commented testing code. --- .../elixir_coupled_polytropic_euler.jl | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl index beee32b20ca..f2cdca5f903 100644 --- a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl +++ b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl @@ -1,8 +1,4 @@ -#using Revise -#using Infiltrator -#using AbbreviatedStackTraces using OrdinaryDiffEq -#using Trixi2Vtk using Trixi @@ -15,8 +11,7 @@ Coupled two polytropic Euler systems. # define the initial conditions function initial_condition_wave_isotropic(x, t, equations::PolytropicEulerEquations2D) - gamma = 2.0 - kappa = 1.0 + @unpack gamma, kappa = equations rho = 1.0 v1 = 0.0 @@ -30,8 +25,7 @@ function initial_condition_wave_isotropic(x, t, equations::PolytropicEulerEquati end function initial_condition_wave_polytropic(x, t, equations::PolytropicEulerEquations2D) - gamma = 2.0 - kappa = 1.0 + @unpack gamma, kappa = equations rho = 1.0 v1 = 0.0 @@ -82,24 +76,16 @@ mesh_B = StructuredMesh(cells_per_dimension, semi_A = SemidiscretizationHyperbolic(mesh_A, equations_A, initial_condition_wave_isotropic, solver, boundary_conditions=( -# x_neg=BoundaryConditionCoupledAB(2, (:end, :i_forward), Float64, equations_B, equations_coupling), x_neg=BoundaryConditionCoupled(2, (:end, :i_forward), Float64), -# x_neg=boundary_condition_periodic, -# x_pos=BoundaryConditionCoupledAB(2, (:begin, :i_forward), Float64, equations_B, equations_coupling), x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64), -# x_pos=boundary_condition_periodic, y_neg=boundary_condition_periodic, y_pos=boundary_condition_periodic)) semi_B = SemidiscretizationHyperbolic(mesh_B, equations_B, initial_condition_wave_polytropic, solver, boundary_conditions=( -# x_neg=BoundaryConditionCoupledAB(1, (:end, :i_forward), Float64, equations_A, equations_coupling), x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64), -# x_neg=boundary_condition_periodic, -# x_pos=BoundaryConditionCoupledAB(1, (:begin, :i_forward), Float64, equations_A, equations_coupling), x_pos=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64), -# x_pos=boundary_condition_periodic, y_neg=boundary_condition_periodic, y_pos=boundary_condition_periodic)) @@ -152,6 +138,3 @@ sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), # Print the timer summary summary_callback() - -## Convert the snapshots into vtk format. -#trixi2vtk("out/solution_*.h5") From e7b195aa046f58dcf4a7657f8b48a24960e2b84c Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:18:53 +0100 Subject: [PATCH 11/83] Added other_list to the coupled semi discretisation elixir. --- .../elixir_euler_source_terms_ring_coupled.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl b/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl index 0445df6dfd0..4018c31138d 100644 --- a/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl @@ -108,7 +108,11 @@ semi4 = SemidiscretizationHyperbolic(mesh4, equations, initial_condition, solver y_pos=boundary_condition, )) -semi = SemidiscretizationCoupled((semi1, semi2, semi3, semi4)) + +# TODO: Once we can properly couple with multiple systems we need to change this list. +other_list = [3, 4, 1, 2] + +semi = SemidiscretizationCoupled((semi1, semi2, semi3, semi4), other_list) ############################################################################### # ODE solvers, callbacks etc. From 47adec31401d93ec606ce31ffab4e673b3fecefe Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:25:06 +0100 Subject: [PATCH 12/83] Removed flux coupling equation. --- ...pressible_euler_hyperbolic_diffusion_2d.jl | 141 ------------------ 1 file changed, 141 deletions(-) delete mode 100644 src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl diff --git a/src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl b/src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl deleted file mode 100644 index 91a964c224b..00000000000 --- a/src/equations/coupled_compressible_euler_hyperbolic_diffusion_2d.jl +++ /dev/null @@ -1,141 +0,0 @@ -@muladd begin - - struct CouplingCompressibleEulerHyperbolicDiffusion2D{RealT<:Real} <: AbstractCouplingEquations{2, 7} - coupling_nu::RealT # diffusion constant at the coupling interface - end - - - function CouplingCompressibleEulerHyperbolicDiffusion2D(u::NTuple{7,<:Real}) - CouplingCompressibleEulerHyperbolicDiffusion2D(SVector(u)) - end - - - function CouplingCompressibleEulerHyperbolicDiffusion2D(u_compressible_euler::NTuple{4,<:Real}, u_hyperbolic_diffusion::NTuple{3,<:Real}) - CouplingCompressibleEulerHyperbolicDiffusion2D(vcat(u_compressible_euler, u_hyperbolic_diffusion)) - end - - - varnames(::typeof(cons2cons), ::CouplingCompressibleEulerHyperbolicDiffusion2D) = ("rho", "rho_v1", "rho_v2", "rho_e", "phi", "q1", "q2") - varnames(::typeof(cons2prim), ::CouplingCompressibleEulerHyperbolicDiffusion2D) = ("rho", "v1", "v2", "p", "phi", "q1", "q2") - - default_analysis_errors(::CouplingCompressibleEulerHyperbolicDiffusion2D) = (:l2_error, :linf_error, :residual) - - - @inline function residual_steady_state(du, ::CouplingCompressibleEulerHyperbolicDiffusion2D) - abs(du[1]) - end - - - # Calculate 1D flux in for a single point - @inline function flux(u, orientation::Integer, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = (equations.gamma - 1) * (rho_e - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - if orientation == 1 - f1 = 0.0 - f2 = 0.0 - f3 = 0.0 - f4 = -equations.coupling_nu*phi - f5 = equations.coupling_nu*phi - f6 = 0.0 - f7 = 0.0 - else - f1 = 0.0 - f2 = 0.0 - f3 = 0.0 - f4 = -equations.coupling_nu*phi - f5 = equations.coupling_nu*phi - f6 = 0.0 - f7 = 0.0 - end - - return SVector(f1, f2, f3, f4, f5, f6, f7) - end - - - # Note, this directional vector is not normalized - @inline function flux(u, normal_direction::AbstractVector, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = u - rho, v1, v2, p, phi, q1, q2 = cons2prim(u, equations) - - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = 0.0 - f2 = 0.0 - f3 = 0.0 - f4 = -equations.coupling_nu * phi - - f5 = equations.coupling_nu * phi - f6 = 0.0 - f7 = 0.0 - - return SVector(f1, f2, f3, f4, f5, f6, f7) - end - - - # Calculate maximum wave speed for local Lax-Friedrichs-type dissipation - @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - return equations.coupling_nu - end - - - @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - # rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = (u_ll + u_rr) - # rho, v1, v2, p, phi, q1, q2 = (cons2prim(u_ll, equations) + cons2prim(u_rr, equations))/2 - - # v = (q1 * normal_direction[1] + q2 * normal_direction[2]) - - # return abs(v) * norm(normal_direction) - - return equations.coupling_nu - end - - - @inline have_constant_speed(::CouplingCompressibleEulerHyperbolicDiffusion2D) = Val(true) - - - @inline function max_abs_speeds(eq::CouplingCompressibleEulerHyperbolicDiffusion2D) - λ = sqrt(eq.nu * eq.inv_Tr) - return λ, λ - end - - - # Convert conservative variables to primitive - @inline cons2prim(u, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) = u - - - # Calculate entropy for a conservative state `u` (here: same as total energy) - @inline entropy(u, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) = energy_total(u, equations) - - - # Calculate total energy for a conservative state `u` - @inline function energy_total(u, equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - # energy function as found in equations (2.5.12) in the book "I Do Like CFD, Vol. 1" - rho, rho_v1, rho_v2, rho_e, phi, q1, q2 = u - return (rho_v1^2 + rho_v2^2) / (2 * rho) + 0.5 * (phi^2 + equations.Lr^2 * (q1^2 + q2^2)) - end - - - # Calculate minimum and maximum wave speeds for HLL-type fluxes - @inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - λ_min = 0 - λ_max = abs(equations.coupling_nu) - - return λ_min, λ_max - end - - - @inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CouplingCompressibleEulerHyperbolicDiffusion2D) - - λ_min = 0 - λ_max = abs(equations.coupling_nu) - - return λ_min, λ_max - end - - end # @muladd - \ No newline at end of file From 4736cf2d3a41b10cc711a3a4f39ecfed752eb809 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:25:35 +0100 Subject: [PATCH 13/83] Removed surface coupling equation for polytropic Euler. --- src/equations/coupled_polytropic_euler_2d.jl | 140 ------------------- 1 file changed, 140 deletions(-) delete mode 100644 src/equations/coupled_polytropic_euler_2d.jl diff --git a/src/equations/coupled_polytropic_euler_2d.jl b/src/equations/coupled_polytropic_euler_2d.jl deleted file mode 100644 index cf70e90b651..00000000000 --- a/src/equations/coupled_polytropic_euler_2d.jl +++ /dev/null @@ -1,140 +0,0 @@ -@muladd begin - - struct CouplingPolytropicEuler2D{RealT<:Real} <: AbstractCouplingEquations{2, 6} - coupling_nu::RealT # diffusion constant at the coupling interface - end - - - function CouplingPolytropicEuler2D(u::NTuple{6,<:Real}) - CouplingPolytropicEuler2D(SVector(u)) - end - - - function CouplingPolytropicEuler2D(u_polytropic_euler_A::NTuple{4,<:Real}, u_polytropic_euler_B::NTuple{3,<:Real}) - CouplingPolytropicEuler2D(vcat(u_polytropic_euler_A, u_polytropic_euler_B)) - end - - - varnames(::typeof(cons2cons), ::CouplingPolytropicEuler2D) = ("rho", "rho_v1", "rho_v2", "rho", "rho_v1", "rho_v2") - varnames(::typeof(cons2prim), ::CouplingPolytropicEuler2D) = ("rho", "v1", "v2", "rho", "v1", "v2") - - default_analysis_errors(::CouplingPolytropicEuler2D) = (:l2_error, :linf_error, :residual) - - - @inline function residual_steady_state(du, ::CouplingPolytropicEuler2D) - abs(du[1]) - end - - - # Calculate 1D flux in for a single point - @inline function flux(u, orientation::Integer, equations::CouplingPolytropicEuler2D) - rho_a, rho_v1_a, rho_v2_a, rho_b, rho_v1_b, rho_v2_b = u - v1_a = rho_v1_a / rho_a - v2_a = rho_v2_a / rho_a - v1_b = rho_v1_b / rho_b - v2_b = rho_v2_b / rho_b - - if orientation == 1 - f1 = rho_b - f2 = v1_b - f3 = v2_b - f4 = rho_a - f5 = v1_a - f6 = v2_a - else - f1 = rho_b - f2 = v1_b - f3 = v2_b - f4 = rho_a - f5 = v1_a - f6 = v2_a - end - - return SVector(f1, f2, f3, f4, f5, f6) - end - - - # Note, this directional vector is not normalized - @inline function flux(u, normal_direction::AbstractVector, equations::CouplingPolytropicEuler2D) - rho_a, rho_v1_a, rho_v2_a, rho_b, rho_v1_b, rho_v2_b = u - rho_a, v1_a, v2_a, rho_b, v1_b, v2_b = cons2prim(u, equations) - - v_normal_a = v1_a * normal_direction[1] + v2_a * normal_direction[2] - v_normal_b = v1_b * normal_direction[1] + v2_b * normal_direction[2] - - f1 = rho_b - f2 = v1_b - f3 = v2_b - f4 = rho_a - f5 = v1_a - f6 = v2_a - - return SVector(f1, f2, f3, f4, f5, f6) - end - - - # Calculate maximum wave speed for local Lax-Friedrichs-type dissipation - @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::CouplingPolytropicEuler2D) - return equations.coupling_nu - end - - - @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::CouplingPolytropicEuler2D) - # rho_ll, rho_v1_ll, rho_v2_ll, rho_rr, rho_v1_rr, rho_v2_rr = (u_ll + u_rr) - # rho, v1, v2, p, phi, q1, q2 = (cons2prim(u_ll, equations) + cons2prim(u_rr, equations))/2 - - # v = (q1 * normal_direction[1] + q2 * normal_direction[2]) - - # @infiltrate - - # Polytropic speed of sound is c = sqrt(gamma kappa rho^(gamma-1)). - return 1.0 - end - - - @inline have_constant_speed(::CouplingPolytropicEuler2D) = Val(true) - - - @inline function max_abs_speeds(eq::CouplingPolytropicEuler2D) - λ = eq.nu - return 1.0, 1.0 - end - - - # Convert conservative variables to primitive - @inline cons2prim(u, equations::CouplingPolytropicEuler2D) = u - - - # Calculate entropy for a conservative state `u` (here: same as total energy) - @inline entropy(u, equations::CouplingPolytropicEuler2D) = energy_total(u, equations) - - - # Calculate total energy for a conservative state `u` - @inline function energy_total(u, equations::CouplingPolytropicEuler2D) - # energy function as found in equations (2.5.12) in the book "I Do Like CFD, Vol. 1" - rho_a, rho_v1_a, rho_v2_a, rho_b, rho_v1_b, rho_v2_b = u - return (rho_v1_a^2 + rho_v2_a^2 + rho_v1_b^2 + rho_v2_b^2) / (2 * rho_a * rho_b) - end - - - # Calculate minimum and maximum wave speeds for HLL-type fluxes - @inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CouplingPolytropicEuler2D) - λ_min = 0 - λ_max = abs(equations.coupling_nu) - - return 1.0, 1.0 - end - - - @inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CouplingPolytropicEuler2D) - - λ_min = 0 - λ_max = abs(equations.coupling_nu) - - return 1.0, 1.0 - end - - end # @muladd - \ No newline at end of file From 52abece79e90c9f06055b5a57c04a0bbad7c1598 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:28:16 +0100 Subject: [PATCH 14/83] Removed polytropic Euler equation. --- src/equations/polytropic_euler_2d.jl | 674 --------------------------- 1 file changed, 674 deletions(-) delete mode 100644 src/equations/polytropic_euler_2d.jl diff --git a/src/equations/polytropic_euler_2d.jl b/src/equations/polytropic_euler_2d.jl deleted file mode 100644 index b622585f86b..00000000000 --- a/src/equations/polytropic_euler_2d.jl +++ /dev/null @@ -1,674 +0,0 @@ -# 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 - - -@doc raw""" - PolytropicEulerEquations2D(gamma, kappa) - -The compressible Euler equations -```math -\partial t -\begin{pmatrix} -\rho \\ \rho v_1 \\ \rho v_2 -\end{pmatrix} -+ -\partial x -\begin{pmatrix} - \rho v_1 \\ \rho v_1^2 + \kappa\rho^\gamma \\ \rho v_1 v_2 -\end{pmatrix} -+ -\partial y -\begin{pmatrix} -\rho v_2 \\ \rho v_1 v_2 \\ \rho v_2^2 + \kappa\rho^\gamma -\end{pmatrix} -= -\begin{pmatrix} -0 \\ 0 \\ 0 -\end{pmatrix} -``` -for an ideal gas with ratio of specific heats `gamma` -in two space dimensions. -Here, ``\rho`` is the density and ``v_1`` and`v_2` the velocities and -```math -p = \kappa\rho^\gamma -``` -the pressure, which we replaced using this relation. - -""" -struct PolytropicEulerEquations2D{RealT<:Real, RealT<:Real} <: AbstractPolytropicEulerEquations{2, 3} - gamma::RealT # ratio of specific heats - kappa::RealT # fluid scaling factor - inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications - - function PolytropicEulerEquations2D(gamma, kappa) - new{typeof(gamma), typeof(kappa)}(gamma, kappa) - end -end - - -varnames(::typeof(cons2cons), ::PolytropicEulerEquations2D) = ("rho", "rho_v1", "rho_v2") -varnames(::typeof(cons2prim), ::PolytropicEulerEquations2D) = ("rho", "v1", "v2") - - -# Set initial conditions at physical location `x` for time `t` -""" - initial_condition_constant(x, t, equations::CompressibleEulerEquations2D) - -A constant initial condition to test free-stream preservation. -""" -function initial_condition_constant(x, t, equations::PolytropicEulerEquations2D) - rho = 1.0 - rho_v1 = 0.1 - rho_v2 = -0.2 - - return SVector(rho, rho_v1, rho_v2) -end - - -""" - initial_condition_convergence_test(x, t, equations::PolytropicEulerEquations2D) - -A smooth initial condition used for convergence tests in combination with -[`source_terms_convergence_test`](@ref) -(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains). -""" -function initial_condition_convergence_test(x, t, equations::PolytropicEulerEquations2D) - c = 2 - A = 0.1 - L = 2 - f = 1/L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - rho = ini - rho_v1 = ini - rho_v2 = ini - - return SVector(rho, rho_v1, rho_v2) -end - - -""" - initial_condition_density_wave(x, t, equations::PolytropicEulerEquations2D) - -A sine wave in the density with constant velocity and pressure; reduces the -compressible Euler equations to the linear advection equations. -This setup is the test case for stability of EC fluxes from paper -- Gregor J. Gassner, Magnus Svärd, Florian J. Hindenlang (2020) - Stability issues of entropy-stable and/or split-form high-order schemes - [arXiv: 2007.09026](https://arxiv.org/abs/2007.09026) -with the following parameters -- domain [-1, 1] -- mesh = 4x4 -- polydeg = 5 -""" -function initial_condition_density_wave(x, t, equations::PolytropicEulerEquations2D) - v1 = 0.1 - v2 = 0.2 - rho = 1 + 0.98 * sinpi(2 * (x[1] + x[2] - t * (v1 + v2))) - rho_v1 = rho * v1 - rho_v2 = rho * v2 - p = 20 - return SVector(rho, rho_v1, rho_v2) -end - - -""" - boundary_condition_slip_wall(u_inner, normal_direction, x, t, surface_flux_function, - equations::PolytropicEulerEquations2D) - -Determine the boundary numerical surface flux for a slip wall condition. -Imposes a zero normal velocity at the wall. -Density is taken from the internal solution state and pressure is computed as an -exact solution of a 1D Riemann problem. Further details about this boundary state -are available in the paper: -- J. J. W. van der Vegt and H. van der Ven (2002) - Slip flow boundary conditions in discontinuous Galerkin discretizations of - the Euler equations of gas dynamics - [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1) - -Details about the 1D pressure Riemann solution can be found in Section 6.3.3 of the book -- Eleuterio F. Toro (2009) - Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - 3rd edition - [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - -Should be used together with [`UnstructuredMesh2D`](@ref). -""" -@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - x, t, - surface_flux_function, - equations::PolytropicEulerEquations2D) - - norm_ = norm(normal_direction) - # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later - normal = normal_direction / norm_ - - # rotate the internal solution state - u_local = rotate_to_x(u_inner, normal, equations) - p_local = equations.kappa*rho^equations.gamma - - # compute the primitive variables - rho_local, v_normal, v_tangent = cons2prim(u_local, equations) - - # Get the solution of the pressure Riemann problem - # See Section 6.3.3 of - # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - if v_normal <= 0.0 - sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed - p_star = p_local * (1.0 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2.0 * equations.gamma * equations.inv_gamma_minus_one) - else # v_normal > 0.0 - A = 2.0 / ((equations.gamma + 1) * rho_local) - B = p_local * (equations.gamma - 1) / (equations.gamma + 1) - p_star = p_local + 0.5 * v_normal / A * (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) - end - - # For the slip wall we directly set the flux as the normal velocity is zero - return SVector(zero(eltype(u_inner)), - p_star * normal[1], - p_star * normal[2]) * norm_ -end - -""" - boundary_condition_slip_wall(u_inner, orientation, direction, x, t, - surface_flux_function, equations::PolytropicEulerEquations2D) - -Should be used together with [`TreeMesh`](@ref). -""" -@inline function boundary_condition_slip_wall(u_inner, orientation, - direction, x, t, - surface_flux_function, - equations::PolytropicEulerEquations2D) - # get the appropriate normal vector from the orientation - if orientation == 1 - normal = SVector(1, 0) - else # orientation == 2 - normal = SVector(0, 1) - end - - # compute and return the flux using `boundary_condition_slip_wall` routine above - return boundary_condition_slip_wall(u_inner, normal, x, t, surface_flux_function, equations) -end - -""" - boundary_condition_slip_wall(u_inner, normal_direction, direction, x, t, - surface_flux_function, equations::PolytropicEulerEquations2D) - -Should be used together with [`StructuredMesh`](@ref). -""" -@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - direction, x, t, - surface_flux_function, - equations::PolytropicEulerEquations2D) - # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back - # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh - if isodd(direction) - boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, - x, t, surface_flux_function, equations) - else - boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, - x, t, surface_flux_function, equations) - end - - return boundary_flux -end - - -# Calculate 1D flux for a single point -@inline function flux(u, orientation::Integer, equations::PolytropicEulerEquations2D) - rho, rho_v1, rho_v2 = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = equations.kappa*rho^equations.gamma - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = rho_v1 * v2 - else - f1 = rho_v2 - f2 = rho_v2 * v1 - f3 = rho_v2 * v2 + p - end - return SVector(f1, f2, f3) -end - -# Calculate 1D flux for a single point in the normal direction -# Note, this directional vector is not normalized -@inline function flux(u, normal_direction::AbstractVector, equations::PolytropicEulerEquations2D) - rho_e = last(u) - rho, v1, v2 = cons2prim(u, equations) - p = equations.kappa*rho^equations.gamma - - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = rho_v_normal - f2 = rho_v_normal * v1 + p * normal_direction[1] - f3 = rho_v_normal * v2 + p * normal_direction[2] - return SVector(f1, f2, f3) -end - - -""" - flux_hllc(u_ll, u_rr, orientation, equations::PolytropicEulerEquations2D) - -Computes the HLLC flux (HLL with Contact) for compressible Euler equations developed by E.F. Toro -[Lecture slides](http://www.prague-sum.com/download/2012/Toro_2-HLLC-RiemannSolver.pdf) -Signal speeds: [DOI: 10.1137/S1064827593260140](https://doi.org/10.1137/S1064827593260140) -""" -function flux_hllc(u_ll, u_rr, orientation::Integer, equations::PolytropicEulerEquations2D) - # Calculate primitive variables and speed of sound - rho_ll, rho_v1_ll, rho_v2_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr = u_rr - - v1_ll = rho_v1_ll / rho_ll - v2_ll = rho_v2_ll / rho_ll - p_ll = equations.kappa * rho_ll^equations.gamma - e_ll = p_ll * rho_ll / (equations.gamma - 1) - rho_e_ll = rho_ll * e_ll - c_ll = sqrt(equations.gamma*p_ll/rho_ll) - - v1_rr = rho_v1_rr / rho_rr - v2_rr = rho_v2_rr / rho_rr - p_rr = equations.kappa * rho_rr^equations.gamma - e_rr = p_rr * rho_rr / (equations.gamma - 1) - rho_e_rr = rho_rr * e_rr - c_rr = sqrt(equations.gamma*p_rr/rho_rr) - - # Obtain left and right fluxes - f_ll = flux(u_ll, orientation, equations) - f_rr = flux(u_rr, orientation, equations) - - # Compute Roe averages - sqrt_rho_ll = sqrt(rho_ll) - sqrt_rho_rr = sqrt(rho_rr) - sum_sqrt_rho = sqrt_rho_ll + sqrt_rho_rr - if orientation == 1 # x-direction - vel_L = v1_ll - vel_R = v1_rr - ekin_roe = (sqrt_rho_ll * v2_ll + sqrt_rho_rr * v2_rr)^2 - elseif orientation == 2 # y-direction - vel_L = v2_ll - vel_R = v2_rr - ekin_roe = (sqrt_rho_ll * v1_ll + sqrt_rho_rr * v1_rr)^2 - end - vel_roe = (sqrt_rho_ll * vel_L + sqrt_rho_rr * vel_R) / sum_sqrt_rho - ekin_roe = 0.5 * (vel_roe^2 + ekin_roe / sum_sqrt_rho^2) - H_ll = (rho_e_ll + p_ll) / rho_ll - H_rr = (rho_e_rr + p_rr) / rho_rr - H_roe = (sqrt_rho_ll * H_ll + sqrt_rho_rr * H_rr) / sum_sqrt_rho - c_roe = sqrt((equations.gamma - 1) * (H_roe - ekin_roe)) - Ssl = min(vel_L - c_ll, vel_roe - c_roe) - Ssr = max(vel_R + c_rr, vel_roe + c_roe) - sMu_L = Ssl - vel_L - sMu_R = Ssr - vel_R - - if Ssl >= 0.0 - f1 = f_ll[1] - f2 = f_ll[2] - f3 = f_ll[3] - elseif Ssr <= 0.0 - f1 = f_rr[1] - f2 = f_rr[2] - f3 = f_rr[3] - else - SStar = (p_rr - p_ll + rho_ll*vel_L*sMu_L - rho_rr*vel_R*sMu_R) / (rho_ll*sMu_L - rho_rr*sMu_R) - if Ssl <= 0.0 <= SStar - densStar = rho_ll*sMu_L / (Ssl-SStar) - enerStar = e_ll + (SStar - vel_L) * (SStar + p_ll / (rho_ll * sMu_L)) - UStar1 = densStar - UStar4 = densStar*enerStar - if orientation == 1 # x-direction - UStar2 = densStar*SStar - UStar3 = densStar*v2_ll - elseif orientation == 2 # y-direction - UStar2 = densStar*v1_ll - UStar3 = densStar*SStar - end - f1 = f_ll[1]+Ssl*(UStar1 - rho_ll) - f2 = f_ll[2]+Ssl*(UStar2 - rho_v1_ll) - f3 = f_ll[3]+Ssl*(UStar3 - rho_v2_ll) - else - densStar = rho_rr*sMu_R / (Ssr-SStar) - enerStar = e_rr + (SStar - vel_R) * (SStar + p_rr / (rho_rr * sMu_R)) - UStar1 = densStar - UStar4 = densStar*enerStar - if orientation == 1 # x-direction - UStar2 = densStar*SStar - UStar3 = densStar*v2_rr - elseif orientation == 2 # y-direction - UStar2 = densStar*v1_rr - UStar3 = densStar*SStar - end - f1 = f_rr[1]+Ssr*(UStar1 - rho_rr) - f2 = f_rr[2]+Ssr*(UStar2 - rho_v1_rr) - f3 = f_rr[3]+Ssr*(UStar3 - rho_v2_rr) - end - end - return SVector(f1, f2, f3) - end - - - -""" - flux_ranocha(u_ll, u_rr, orientation_or_normal_direction, - equations::PolytropicEulerEquations2D) - -Entropy conserving and kinetic energy preserving two-point flux by -- Hendrik Ranocha (2018) - Generalised Summation-by-Parts Operators and Entropy Stability of Numerical Methods - for Hyperbolic Balance Laws - [PhD thesis, TU Braunschweig](https://cuvillier.de/en/shop/publications/7743) -See also -- Hendrik Ranocha (2020) - Entropy Conserving and Kinetic Energy Preserving Numerical Methods for - the Euler Equations Using Summation-by-Parts Operators - [Proceedings of ICOSAHOM 2018](https://doi.org/10.1007/978-3-030-39647-3_42) -""" -@inline function flux_ranocha(u_ll, u_rr, orientation::Integer, equations::PolytropicEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) - p_ll = equations.kappa*rho_ll^equations.gamma - p_rr = equations.kappa*rho_rr^equations.gamma - - # Compute the necessary mean values - rho_mean = ln_mean(rho_ll, rho_rr) - # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` - # in exact arithmetic since - # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) - # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) - inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_avg = 0.5 * (p_ll + p_rr) - velocity_square_avg = 0.5 * (v1_ll*v1_rr + v2_ll*v2_rr) - - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_avg - f3 = f1 * v2_avg - else - f1 = rho_mean * v2_avg - f2 = f1 * v1_avg - f3 = f1 * v2_avg + p_avg - end - - return SVector(f1, f2, f3) -end - - -@inline function flux_ranocha(u_ll, u_rr, normal_direction::AbstractVector, equations::PolytropicEulerEquations2D) - # Unpack left and right state - rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) - p_ll = equations.kappa*rho_ll^equations.gamma - p_rr = equations.kappa*rho_rr^equations.gamma - v_dot_n_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_dot_n_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # Compute the necessary mean values - rho_mean = ln_mean(rho_ll, rho_rr) - # Algebraically equivalent to `inv_ln_mean(rho_ll / p_ll, rho_rr / p_rr)` - # in exact arithmetic since - # log((ϱₗ/pₗ) / (ϱᵣ/pᵣ)) / (ϱₗ/pₗ - ϱᵣ/pᵣ) - # = pₗ pᵣ log((ϱₗ pᵣ) / (ϱᵣ pₗ)) / (ϱₗ pᵣ - ϱᵣ pₗ) - inv_rho_p_mean = p_ll * p_rr * inv_ln_mean(rho_ll * p_rr, rho_rr * p_ll) - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - p_avg = 0.5 * (p_ll + p_rr) - - # Calculate fluxes depending on normal_direction - f1 = rho_mean * 0.5 * (v_dot_n_ll + v_dot_n_rr) - f2 = f1 * v1_avg + p_avg * normal_direction[1] - f3 = f1 * v2_avg + p_avg * normal_direction[2] - - return SVector(f1, f2, f3) -end - - -# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the -# maximum velocity magnitude plus the maximum speed of sound -@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::PolytropicEulerEquations2D) - rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) - p_ll = equations.kappa*rho_ll^equations.gamma - p_rr = equations.kappa*rho_rr^equations.gamma - - # Get the velocity value in the appropriate direction - if orientation == 1 - v_ll = v1_ll - v_rr = v1_rr - else # orientation == 2 - v_ll = v2_ll - v_rr = v2_rr - end - # Calculate sound speeds - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - λ_max = max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) -end - - -@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::PolytropicEulerEquations2D) - rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) - p_ll = equations.kappa*rho_ll^equations.gamma - p_rr = equations.kappa*rho_rr^equations.gamma - - # Calculate normal velocities and sound speed - # left - v_ll = ( v1_ll * normal_direction[1] - + v2_ll * normal_direction[2] ) - c_ll = sqrt(equations.gamma * p_ll / rho_ll) - # right - v_rr = ( v1_rr * normal_direction[1] - + v2_rr * normal_direction[2] ) - c_rr = sqrt(equations.gamma * p_rr / rho_rr) - - return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) -end - - -# Calculate minimum and maximum wave speeds for HLL-type fluxes -@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, - equations::PolytropicEulerEquations2D) - rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) - p_ll = equations.kappa*rho_ll^equations.gamma - p_rr = equations.kappa*rho_rr^equations.gamma - - if orientation == 1 # x-direction - λ_min = v1_ll - sqrt(equations.gamma * p_ll / rho_ll) - λ_max = v1_rr + sqrt(equations.gamma * p_rr / rho_rr) - else # y-direction - λ_min = v2_ll - sqrt(equations.gamma * p_ll / rho_ll) - λ_max = v2_rr + sqrt(equations.gamma * p_rr / rho_rr) - end - - return λ_min, λ_max -end - -@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::PolytropicEulerEquations2D) - rho_ll, v1_ll, v2_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr = cons2prim(u_rr, equations) - p_ll = equations.kappa*rho_ll^equations.gamma - p_rr = equations.kappa*rho_rr^equations.gamma - - v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - norm_ = norm(normal_direction) - # The v_normals are already scaled by the norm - λ_min = v_normal_ll - sqrt(equations.gamma * p_ll / rho_ll) * norm_ - λ_max = v_normal_rr + sqrt(equations.gamma * p_rr / rho_rr) * norm_ - - return λ_min, λ_max -end - - -# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction -# has been normalized prior to this rotation of the state vector -@inline function rotate_to_x(u, normal_vector, equations::PolytropicEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D rotation matrix with normal and tangent directions of the form - # [ 1 0 0 - # 0 n_1 t_1 - # 0 n_2 t_2 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] + s * u[3], - -s * u[2] + c * u[3]) -end - - -# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction -# has been normalized prior to this back-rotation of the state vector -@inline function rotate_from_x(u, normal_vector, equations::PolytropicEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D back-rotation matrix with normal and tangent directions of the form - # [ 1 0 0 - # 0 n_1 t_1 - # 0 n_2 t_2 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] - s * u[3], - s * u[2] + c * u[3]) -end - - -@inline function max_abs_speeds(u, equations::PolytropicEulerEquations2D) - rho, v1, v2 = cons2prim(u, equations) - c = sqrt(equations.gamma * equations.kappa*rho^(equations.gamma-1)) - - return abs(v1) + c, abs(v2) + c -end - - -# Convert conservative variables to primitive -@inline function cons2prim(u, equations::PolytropicEulerEquations2D) - rho, rho_v1, rho_v2 = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - return SVector(rho, v1, v2) -end - - -# Convert conservative variables to entropy -@inline function cons2entropy(u, equations::PolytropicEulerEquations2D) - rho, rho_v1, rho_v2 = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - v_square = v1^2 + v2^2 - p = equations.kappa * rho^equations.gamma - s = rho/2*v_square + rho*equations.kappa*rho^(equations.gamma-1)/(equations.gamma-1) - rho_p = rho / p - - w1 = (equations.gamma - s) * (equations.gamma - 1) - 0.5 * rho_p * v_square - w2 = rho_p * v1 - w3 = rho_p * v2 - - return SVector(w1, w2, w3) -end - -# TODO: Do we need this? (SC) -# @inline function entropy2cons(w, equations::PolytropicEulerEquations2D) -# # See Hughes, Franca, Mallet (1986) A new finite element formulation for CFD -# # [DOI: 10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1) -# @unpack gamma, kappa = equations - -# # convert to entropy `-rho * s` used by Hughes, France, Mallet (1986) -# # instead of `-rho * s / (gamma - 1)` -# V1, V2, V3, V5 = w .* (gamma-1) - -# # s = specific entropy, eq. (53) -# s = gamma - V1 + (V2^2 + V3^2)/(2*V5) - -# # eq. (52) -# rho_iota = ((gamma-1) / (-V5)^gamma)^(equations.inv_gamma_minus_one)*exp(-s * equations.inv_gamma_minus_one) - -# # eq. (51) -# rho = -rho_iota * V5 -# rho_v1 = rho_iota * V2 -# rho_v2 = rho_iota * V3 -# rho_e = rho_iota * (1-(V2^2 + V3^2)/(2*V5)) -# return SVector(rho, rho_v1, rho_v2, rho_e) -# end - - - - -# Convert primitive to conservative variables -@inline function prim2cons(prim, equations::PolytropicEulerEquations2D) - rho, v1, v2 = prim - rho_v1 = rho * v1 - rho_v2 = rho * v2 - return SVector(rho, rho_v1, rho_v2) -end - - -@inline function density(u, equations::PolytropicEulerEquations2D) - rho = u[1] - return rho -end - - -@inline function pressure(u, equations::PolytropicEulerEquations2D) - rho, rho_v1, rho_v2 = u - p = equations.kappa*rho^equations.gamma - return p -end - - -@inline function density_pressure(u, equations::PolytropicEulerEquations2D) - rho, rho_v1, rho_v2 = u - rho_times_p = equations.kappa*rho^(equations.gamma+1) - return rho_times_p -end - - -# Calculate thermodynamic entropy for a conservative state `cons` -@inline function entropy_thermodynamic(cons, equations::PolytropicEulerEquations2D) - # Pressure - p = equations.kappa*cons[1]^equations.gamma - - # Thermodynamic entropy - s = log(p) - equations.gamma*log(cons[1]) - - return s -end - - -# Calculate mathematical entropy for a conservative state `cons` -@inline function entropy_math(cons, equations::PolytropicEulerEquations2D) - # Mathematical entropy - S = -entropy_thermodynamic(cons, equations) * cons[1] * (equations.gamma - 1) - - return S -end - - -# Default entropy is the mathematical entropy -@inline entropy(cons, equations::PolytropicEulerEquations2D) = entropy_math(cons, equations) - -end # @muladd From 0fd06028628fbdba716ee6e3341ea334f0ddf311 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:37:35 +0100 Subject: [PATCH 15/83] Removed any code related to BoundaryConditionCoupledAB. --- src/equations/equations.jl | 66 -------------------------------------- 1 file changed, 66 deletions(-) diff --git a/src/equations/equations.jl b/src/equations/equations.jl index ba332a5dc1c..9794a76a82e 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -340,36 +340,6 @@ mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} end end -mutable struct BoundaryConditionCoupledAB{NDIMS, NDIMST2M1, uEltype<:Real, I, AbstractEquations, AbstractEquations} - # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] - u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 - other_semi_index ::Int - other_orientation::Int - indices ::I - equations_other ::AbstractEquations - equations_coupled::AbstractEquations - - function BoundaryConditionCoupledAB(other_semi_index, indices, uEltype, equations_other, equations_coupled) - NDIMS = length(indices) - u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) - inner_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) - equations_other = equations_other - equations_coupled = equations_coupled - - if indices[1] in (:begin, :end) - other_orientation = 1 - elseif indices[2] in (:begin, :end) - other_orientation = 2 - else - other_orientation = 3 - end - - new{NDIMS, NDIMS*2-1, uEltype, typeof(indices), AbstractEquations, AbstractEquations}( - u_boundary, other_semi_index, other_orientation, indices, equations_other, equations_coupled) - end -end - - function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, cell_indices, surface_node_indices, surface_flux_function, equations) @@ -388,33 +358,6 @@ function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, di return flux end -function (boundary_condition::BoundaryConditionCoupledAB)(u_inner, orientation, direction, - cell_indices, surface_node_indices, - surface_flux_function, equations) - # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), - # but we don't have a solver here - u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], - Val(nvariables(boundary_condition.equations_coupled)))) - - # TODO: This should be computed in the coupled equations module. - if boundary_condition.other_semi_index == 2 - u_inner_long = vcat(u_inner, zeros(3)) - else - u_inner_long = vcat(zeros(3), u_inner) - end - - # Calculate boundary flux - if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary - # flux = surface_flux_function(zeros(length(u_boundary)), u_boundary, orientation, boundary_condition.equations_coupled) - flux = surface_flux_function(u_inner_long, u_boundary, orientation, boundary_condition.equations_coupled) - else # u_boundary is "left" of boundary, u_inner is "right" of boundary - # flux = surface_flux_function(u_boundary, zeros(length(u_boundary)), orientation, boundary_condition.equations_coupled) - flux = surface_flux_function(u_boundary, u_inner_long, orientation, boundary_condition.equations_coupled) - end - - return flux -end - #################################################################################################### # Include files with actual implementations for different systems of equations. @@ -450,10 +393,6 @@ abstract type AbstractCompressibleEulerMulticomponentEquations{NDIMS, NVARS, NCO include("compressible_euler_multicomponent_1d.jl") include("compressible_euler_multicomponent_2d.jl") -# PolytropicEulerEquations -abstract type AbstractPolytropicEulerEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end -include("polytropic_euler_2d.jl") - # Retrieve number of components from equation instance for the multicomponent case @inline ncomponents(::AbstractCompressibleEulerMulticomponentEquations{NDIMS, NVARS, NCOMP}) where {NDIMS, NVARS, NCOMP} = NCOMP """ @@ -506,11 +445,6 @@ include("acoustic_perturbation_2d.jl") abstract type AbstractLinearizedEulerEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end include("linearized_euler_2d.jl") -# Coupling equations. -abstract type AbstractCouplingEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end -include("coupled_compressible_euler_hyperbolic_diffusion_2d.jl") -include("coupled_polytropic_euler_2d.jl") - abstract type AbstractEquationsParabolic{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end end # @muladd From 21a9af7794d8a3c72075891412b4347538e92fff Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:38:01 +0100 Subject: [PATCH 16/83] Removed flux coupling code. --- src/Trixi.jl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Trixi.jl b/src/Trixi.jl index 97dce671431..ef73aa16ddd 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -129,7 +129,6 @@ include("visualization/visualization.jl") export AcousticPerturbationEquations2D, CompressibleEulerEquations1D, CompressibleEulerEquations2D, CompressibleEulerEquations3D, CompressibleEulerMulticomponentEquations1D, CompressibleEulerMulticomponentEquations2D, - PolytropicEulerEquations2D, IdealGlmMhdEquations1D, IdealGlmMhdEquations2D, IdealGlmMhdEquations3D, IdealGlmMhdMulticomponentEquations1D, IdealGlmMhdMulticomponentEquations2D, HyperbolicDiffusionEquations1D, HyperbolicDiffusionEquations2D, HyperbolicDiffusionEquations3D, @@ -138,9 +137,7 @@ export AcousticPerturbationEquations2D, LatticeBoltzmannEquations2D, LatticeBoltzmannEquations3D, ShallowWaterEquations1D, ShallowWaterEquations2D, ShallowWaterTwoLayerEquations1D, ShallowWaterTwoLayerEquations2D, - LinearizedEulerEquations2D, - CouplingLinearScalarAdvectionEquation2D, CouplingCompressibleEulerHyperbolicDiffusion2D, - CouplingPolytropicEuler2D + LinearizedEulerEquations2D export LaplaceDiffusion1D, LaplaceDiffusion2D, CompressibleNavierStokesDiffusion2D, CompressibleNavierStokesDiffusion3D @@ -179,8 +176,7 @@ export boundary_condition_do_nothing, boundary_condition_slip_wall, boundary_condition_wall, BoundaryConditionNavierStokesWall, NoSlip, Adiabatic, Isothermal, - BoundaryConditionCoupled, - BoundaryConditionCoupledAB + BoundaryConditionCoupled export initial_condition_convergence_test, source_terms_convergence_test export source_terms_harmonic From 74e3280bc17f99b97c614e31dd46210a032106e0 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:43:26 +0100 Subject: [PATCH 17/83] Removed numerical fluxes for BoundaryConditionCoupledAB. --- src/solvers/dgsem_structured/dg.jl | 32 ------------------------------ 1 file changed, 32 deletions(-) diff --git a/src/solvers/dgsem_structured/dg.jl b/src/solvers/dgsem_structured/dg.jl index 8a39d0ed1dc..4395a4eeda3 100644 --- a/src/solvers/dgsem_structured/dg.jl +++ b/src/solvers/dgsem_structured/dg.jl @@ -97,38 +97,6 @@ end end end -@inline function calc_boundary_flux_by_direction!(surface_flux_values, u, t, orientation, - boundary_condition::BoundaryConditionCoupledAB, - mesh::StructuredMesh, equations, - surface_integral, dg::DG, cache, - direction, node_indices, surface_node_indices, element) - @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements - # Override the passed surface_integral. - @unpack surface_flux = surface_integral - - cell_indices = get_boundary_indices(element, orientation, mesh) - - u_inner = get_node_vars(u, equations, dg, node_indices..., element) - - # If the mapping is orientation-reversing, the contravariant vectors' orientation - # is reversed as well. The normal vector must be oriented in the direction - # from `left_element` to `right_element`, or the numerical flux will be computed - # incorrectly (downwind direction). - sign_jacobian = sign(inverse_jacobian[node_indices..., element]) - - # Contravariant vector Ja^i is the normal vector - normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, - node_indices..., element) - - # If the mapping is orientation-reversing, the normal vector will be reversed (see above). - # However, the flux now has the wrong sign, since we need the physical flux in normal direction. - flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, - surface_node_indices, surface_flux, equations) - for v in eachvariable(equations) - surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] - end -end - function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) cartesian_indices = CartesianIndices(size(mesh)) if orientation == 1 From 85ad01a22b38fb9a58d31ec07278ab02f04f5643 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:43:59 +0100 Subject: [PATCH 18/83] Removed surface fluxes for BoundaryConditionCoupledAB. --- .../semidiscretization_coupled.jl | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 291b7071155..4d631b76f59 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -227,16 +227,6 @@ function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditi boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations), nnodes(dg), cell_size) end -function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupledAB{2}, direction, mesh, equations, equations_other, dg::DGSEM) - if direction in (1, 2) - cell_size = size(mesh, 2) - else - cell_size = size(mesh, 1) - end - - boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations) + nvariables(equations_other), nnodes(dg), cell_size) -end - # In 3D function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{3}, direction, mesh, equations, equations_other, dg::DGSEM) if direction in (1, 2) @@ -330,59 +320,6 @@ function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{2 end end -# In 2D -function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupledAB{2}, u_ode, semi) - # Copy the boundary condition from A to B (other to this). - @unpack u_indices = semi - @unpack other_semi_index, other_orientation, indices = boundary_condition - - mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) - @views u = wrap_array(u_ode[u_indices[other_semi_index]], mesh, equations, solver, cache) - - linear_indices = LinearIndices(size(mesh)) - - if other_orientation == 1 - cells = axes(mesh, 2) - else # other_orientation == 2 - cells = axes(mesh, 1) - end - - # Copy solution data to the coupled boundary using "delayed indexing" with - # a start value and a step size to get the correct face and orientation. - node_index_range = eachnode(solver) - i_node_start, i_node_step = index_to_start_step_2d(indices[1], node_index_range) - j_node_start, j_node_step = index_to_start_step_2d(indices[2], node_index_range) - - i_cell_start, i_cell_step = index_to_start_step_2d(indices[1], axes(mesh, 1)) - j_cell_start, j_cell_step = index_to_start_step_2d(indices[2], axes(mesh, 2)) - - i_cell = i_cell_start - j_cell = j_cell_start - - # TODO: This should be computed in the coupled equations module. - if boundary_condition.other_semi_index == 2 - coupled_index_offset = 3 - else - coupled_index_offset = 0 - end - - fill!(boundary_condition.u_boundary, zero(eltype(boundary_condition.u_boundary))) - for cell in cells - i_node = i_node_start - j_node = j_node_start - - for i in eachnode(solver) - for v in 1:size(u, 1) - boundary_condition.u_boundary[v+coupled_index_offset, i, cell] = u[v, i_node, j_node, - linear_indices[i_cell, j_cell]] - end - i_node += i_node_step - j_node += j_node_step - end - i_cell += i_cell_step - j_cell += j_cell_step - end -end # In 3D function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{3}, u_ode, semi) From 6023045ca6be71d3cff5741c7282718a08b98735 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 08:49:18 +0100 Subject: [PATCH 19/83] Removed coupled elixir. --- .../elixir_euler_source_terms_ring_coupled.jl | 147 ------------------ 1 file changed, 147 deletions(-) delete mode 100644 examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl diff --git a/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl b/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl deleted file mode 100644 index 4018c31138d..00000000000 --- a/examples/structured_2d_dgsem/elixir_euler_source_terms_ring_coupled.jl +++ /dev/null @@ -1,147 +0,0 @@ - -using OrdinaryDiffEq -using Trixi - -############################################################################### -# semidiscretization of the compressible Euler equations - -equations = CompressibleEulerEquations2D(1.4) - -initial_condition = initial_condition_convergence_test -boundary_condition = BoundaryConditionDirichlet(initial_condition) - -source_terms = source_terms_convergence_test - -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) - -# Gnomonic projection similar to how the coupled cubed sphere is created. -# Note that half the elements will have left-handed coordinates. -function gnomonic_projection(xi, eta, inner_radius, thickness, direction) - alpha = xi * pi/4 - - # Equiangular projection - x = tan(alpha) - - # Coordinates on unit square per direction - square_coordinates = [SVector(-1, x), - SVector( 1, x), - SVector(x, -1), - SVector(x, 1)] - - # Radius on square surface - r = sqrt(1 + x^2) - - # Radius of the sphere - R = inner_radius + thickness * (0.5 * (eta + 1)) - - # Projection onto the sphere - R / r * square_coordinates[direction] -end - -function ring_mapping(inner_radius, thickness, direction) - mapping(xi, eta) = gnomonic_projection(xi, eta, inner_radius, thickness, direction) -end - -mapping_as_string(direction) = """ -function gnomonic_projection(xi, eta, inner_radius, thickness, direction) - alpha = xi * pi/4 - - x = tan(alpha) - - r = sqrt(1 + x^2) - R = inner_radius + thickness * (0.5 * (eta + 1)) - - # Cube coordinates per direction - cube_coordinates = [SVector(-1, x), - SVector( 1, x), - SVector(x, -1), - SVector(x, 1)] - - R / r * cube_coordinates[direction] -end; function ring_mapping(inner_radius, thickness, direction) - mapping(xi, eta) = gnomonic_projection(xi, eta, inner_radius, thickness, direction) -end; mapping = ring_mapping(1, 1, $direction) -""" - - -mesh1 = StructuredMesh((8, 4), ring_mapping(1, 1, 1), - periodicity=false, mapping_as_string=mapping_as_string(1)) - -semi1 = SemidiscretizationHyperbolic(mesh1, equations, initial_condition, solver, - source_terms=source_terms, boundary_conditions=( - x_neg=BoundaryConditionCoupled(3, (:begin, :i_forward), Float64), - x_pos=BoundaryConditionCoupled(4, (:begin, :i_forward), Float64), - y_neg=boundary_condition, - y_pos=boundary_condition, - )) - -mesh2 = StructuredMesh((8, 4), ring_mapping(1, 1, 2), - periodicity=false, mapping_as_string=mapping_as_string(2)) - -semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition, solver, - source_terms=source_terms, boundary_conditions=( - x_neg=BoundaryConditionCoupled(3, (:end, :i_forward), Float64), - x_pos=BoundaryConditionCoupled(4, (:end, :i_forward), Float64), - y_neg=boundary_condition, - y_pos=boundary_condition, - )) - -mesh3 = StructuredMesh((8, 4), ring_mapping(1, 1, 3), - periodicity=false, mapping_as_string=mapping_as_string(3)) - -semi3 = SemidiscretizationHyperbolic(mesh3, equations, initial_condition, solver, - source_terms=source_terms, boundary_conditions=( - x_neg=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64), - x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64), - y_neg=boundary_condition, - y_pos=boundary_condition, - )) - -mesh4 = StructuredMesh((8, 4), ring_mapping(1, 1, 4), - periodicity=false, mapping_as_string=mapping_as_string(4)) - -semi4 = SemidiscretizationHyperbolic(mesh4, equations, initial_condition, solver, - source_terms=source_terms, boundary_conditions=( - x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64), - x_pos=BoundaryConditionCoupled(2, (:end, :i_forward), Float64), - y_neg=boundary_condition, - y_pos=boundary_condition, - )) - - -# TODO: Once we can properly couple with multiple systems we need to change this list. -other_list = [3, 4, 1, 2] - -semi = SemidiscretizationCoupled((semi1, semi2, semi3, semi4), other_list) - -############################################################################### -# ODE solvers, callbacks etc. - -tspan = (0.0, 1.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=1.0) - -callbacks = CallbackSet(summary_callback, - analysis_callback, alive_callback, - save_solution, - stepsize_callback) -############################################################################### -# run the simulation - -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - 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 From bcec86aa11d550fea00b172345c67adc322901a8 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 10:05:30 +0100 Subject: [PATCH 20/83] Remove Coupled StructuredMesh from visualization test. --- test/test_visualization.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_visualization.jl b/test/test_visualization.jl index 3da2859433e..8e527878b26 100644 --- a/test/test_visualization.jl +++ b/test/test_visualization.jl @@ -26,8 +26,7 @@ isdir(outdir) && rm(outdir, recursive=true) "StructuredMesh" => ("structured_2d_dgsem", "elixir_euler_source_terms_waving_flag.jl"), "UnstructuredMesh" => ("unstructured_2d_dgsem", "elixir_euler_basic.jl"), "P4estMesh" => ("p4est_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), - "DGMulti" => ("dgmulti_2d", "elixir_euler_weakform.jl"), - "Coupled StructuredMesh" => ("structured_2d_dgsem", "elixir_euler_source_terms_ring_coupled.jl") + "DGMulti" => ("dgmulti_2d", "elixir_euler_weakform.jl") ) @testset "PlotData2D, PlotDataSeries, PlotMesh with $mesh" for mesh in keys(test_examples_2d) From 885b2dede5f46f536ea6028fa92dccf0672faf26 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 12 May 2023 12:31:34 +0200 Subject: [PATCH 21/83] Remove duplicate function definitions --- src/callbacks_step/analysis.jl | 9 ++------- src/semidiscretization/semidiscretization_coupled.jl | 3 --- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 8193c11ffb0..4838dd0634c 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -614,8 +614,8 @@ function analyze(quantity, du, u, t, semi::AbstractSemidiscretization) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) analyze(quantity, du, u, t, mesh, equations, solver, cache) end -function analyze(quantity, du, u, t, mesh, equations, solver, cache) - integrate(quantity, u, mesh, equations, solver, cache, normalize=true) +function analyze(quantity, du, u, t, mesh, equations, solver, cache; normalize=true) + integrate(quantity, u, mesh, equations, solver, cache, normalize=normalize) end pretty_form_utf(quantity) = get_name(quantity) pretty_form_ascii(quantity) = get_name(quantity) @@ -658,11 +658,6 @@ function analyze(quantity, du_ode, u_ode, t, semi::SemidiscretizationCoupled; no end -function analyze(quantity, du, u, t, mesh, equations, solver, cache; normalize=true) - integrate(quantity, u, mesh, equations, solver, cache, normalize=normalize) -end - - function entropy_timederivative end pretty_form_utf(::typeof(entropy_timederivative)) = "∑∂S/∂U ⋅ Uₜ" pretty_form_ascii(::typeof(entropy_timederivative)) = "dsdu_ut" diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 4d631b76f59..8e3e3c6b24b 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -198,9 +198,6 @@ function compute_coefficients(t, semi::SemidiscretizationCoupled) end -# Don't do anything for other BCs than BoundaryConditionCoupled -function allocate_coupled_boundary_conditions(semi, semi_other) end - function allocate_coupled_boundary_conditions(semi, semi_other) n_boundaries = 2 * ndims(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi) From 888f20f1ba5bfa3ae7b254c988c29d08d9d3910a Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 12 May 2023 12:47:30 +0200 Subject: [PATCH 22/83] make advection elixir go further --- examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 9134b9480d3..7071e3ea527 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -48,7 +48,8 @@ semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition_converg y_pos=boundary_condition_periodic)) # Create a semidiscretization that bundles semi1 and semi2 -semi = SemidiscretizationCoupled((semi1, semi2)) +other_list = [2, 1] +semi = SemidiscretizationCoupled((semi1, semi2), other_list) ############################################################################### # ODE solvers, callbacks etc. From 7ae61e8dfeb4896c5bb5e7ce170f4d33dad2f808 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 12:07:07 +0100 Subject: [PATCH 23/83] Removed initial_condition_peak. --- Project.toml | 4 ++++ src/Trixi.jl | 1 - src/equations/hyperbolic_diffusion_2d.jl | 8 -------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index 3ce9d0c559b..8974a797d61 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,8 @@ authors = ["Michael Schlottke-Lakemper ", "Gregor version = "0.5.20-pre" [deps] +AbbreviatedStackTraces = "ac637c84-cc71-43bf-9c33-c1b4316be3d4" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" @@ -12,6 +14,7 @@ FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" +Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890" @@ -19,6 +22,7 @@ MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" Octavian = "6fd5a793-0b7e-452c-907f-f8bfe9c57db4" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" P4est = "7d669430-f675-4ae7-b43e-fab78ec5a902" Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" diff --git a/src/Trixi.jl b/src/Trixi.jl index ef73aa16ddd..430a673bad3 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -181,7 +181,6 @@ export boundary_condition_do_nothing, export initial_condition_convergence_test, source_terms_convergence_test export source_terms_harmonic export initial_condition_poisson_nonperiodic, source_terms_poisson_nonperiodic, boundary_condition_poisson_nonperiodic -export initial_condition_peak export initial_condition_eoc_test_coupled_euler_gravity, source_terms_eoc_test_coupled_euler_gravity, source_terms_eoc_test_euler export cons2cons, cons2prim, prim2cons, cons2macroscopic, cons2state, cons2mean, diff --git a/src/equations/hyperbolic_diffusion_2d.jl b/src/equations/hyperbolic_diffusion_2d.jl index 3b2ba39c13a..7e4ea87da01 100644 --- a/src/equations/hyperbolic_diffusion_2d.jl +++ b/src/equations/hyperbolic_diffusion_2d.jl @@ -52,14 +52,6 @@ end return SVector(phi, q1, q2) end -@inline function initial_condition_peak(x, t, equations::HyperbolicDiffusionEquations2D) - # elliptic equation: -ν Δϕ = f in Ω, u = g on ∂Ω - phi = exp(-x[1]^2 - (x[2] - 0.5)^2) - q1 = exp(-x[1]^2 - (x[2] - 0.5)^2) - q2 = exp(-x[1]^2 - (x[2] - 0.5)^2) - return SVector(phi, q1, q2) -end - @inline function source_terms_poisson_nonperiodic(u, x, t, equations::HyperbolicDiffusionEquations2D) # elliptic equation: -ν Δϕ = f in Ω, u = g on ∂Ω # analytical solution: ϕ = 2cos(πx)sin(2πy) + 2 and f = 10π^2cos(πx)sin(2πy) From bd77fc93536bb6390ab7327bdb89b13377fe2dcf Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 12:09:06 +0100 Subject: [PATCH 24/83] Removed src/equations/hyperbolic_diffusion_2d.jl. --- src/equations/hyperbolic_diffusion_2d.jl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/equations/hyperbolic_diffusion_2d.jl b/src/equations/hyperbolic_diffusion_2d.jl index 7e4ea87da01..165cad40bc5 100644 --- a/src/equations/hyperbolic_diffusion_2d.jl +++ b/src/equations/hyperbolic_diffusion_2d.jl @@ -164,14 +164,6 @@ end sqrt(equations.nu * equations.inv_Tr) * norm(normal_direction) end -@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer, equations::HyperbolicDiffusionEquations2D) - return 0, sqrt(equations.nu * equations.inv_Tr) -end - -@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::HyperbolicDiffusionEquations2D) - return 0, sqrt(equations.nu * equations.inv_Tr) * norm(normal_direction) -end - @inline function flux_godunov(u_ll, u_rr, orientation::Integer, equations::HyperbolicDiffusionEquations2D) # Obtain left and right fluxes phi_ll, q1_ll, q2_ll = u_ll From e513aa078624087335ad055311ef825661e5f7ba Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 12:10:23 +0100 Subject: [PATCH 25/83] Removed 3d coupling. --- .../semidiscretization_coupled.jl | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 8e3e3c6b24b..4580c5d2e7a 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -317,66 +317,3 @@ function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{2 end end - -# In 3D -function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{3}, u_ode, semi) - @unpack u_indices = semi - @unpack other_semi_index, other_orientation, indices = boundary_condition - - mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) - @views u = wrap_array(u_ode[u_indices[other_semi_index]], mesh, equations, solver, cache) - - linear_indices = LinearIndices(size(mesh)) - - if other_orientation == 1 - cells = (axes(mesh, 2), axes(mesh, 3)) - elseif other_orientation == 2 - cells = (axes(mesh, 1), axes(mesh, 3)) - else # other_orientation == 3 - cells = (axes(mesh, 1), axes(mesh, 2)) - end - - # Copy solution data to the coupled boundary using "delayed indexing" with - # a start value and a step size to get the correct face and orientation. - node_index_range = eachnode(solver) - i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(indices[1], node_index_range) - j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(indices[2], node_index_range) - k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(indices[3], node_index_range) - - i_cell_start, i_cell_step_i, i_cell_step_j = index_to_start_step_3d(indices[1], axes(mesh, 1)) - j_cell_start, j_cell_step_i, j_cell_step_j = index_to_start_step_3d(indices[2], axes(mesh, 2)) - k_cell_start, k_cell_step_i, k_cell_step_j = index_to_start_step_3d(indices[3], axes(mesh, 3)) - - i_cell = i_cell_start - j_cell = j_cell_start - k_cell = k_cell_start - - for cell_j in cells[2] - for cell_i in cells[1] - i_node = i_node_start - j_node = j_node_start - k_node = k_node_start - - for j in eachnode(solver) - for i in eachnode(solver) - for v in 1:size(u, 1) - boundary_condition.u_boundary[v, i, j, cell_i, cell_j] = u[v, i_node, j_node, k_node, - linear_indices[i_cell, j_cell, k_cell]] - end - i_node += i_node_step_i - j_node += j_node_step_i - k_node += k_node_step_i - end - i_node += i_node_step_j - j_node += j_node_step_j - k_node += k_node_step_j - end - i_cell += i_cell_step_i - j_cell += j_cell_step_i - k_cell += k_cell_step_i - end - i_cell += i_cell_step_j - j_cell += j_cell_step_j - k_cell += k_cell_step_j - end -end From 2f054e56a5777425bf90ce84b94585c409830c18 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 12:12:52 +0100 Subject: [PATCH 26/83] Remopved 3d capability. --- src/solvers/dgsem_structured/dg.jl | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/solvers/dgsem_structured/dg.jl b/src/solvers/dgsem_structured/dg.jl index 4395a4eeda3..952fd449f35 100644 --- a/src/solvers/dgsem_structured/dg.jl +++ b/src/solvers/dgsem_structured/dg.jl @@ -110,20 +110,6 @@ function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) return cell_indices end -function get_boundary_indices(element, orientation, mesh::StructuredMesh{3}) - cartesian_indices = CartesianIndices(size(mesh)) - # Boundary indices of element are the two indices in the other dimensions - if orientation == 1 - cell_indices = (cartesian_indices[element][2], cartesian_indices[element][3]) - elseif orientation == 2 - cell_indices = (cartesian_indices[element][1], cartesian_indices[element][3]) - else # orientation == 3 - cell_indices = (cartesian_indices[element][1], cartesian_indices[element][2]) - end - - return cell_indices -end - @inline ndofs(mesh::StructuredMesh, dg::DG, cache) = nelements(cache.elements) * nnodes(dg)^ndims(mesh) From 3b4e6be95bbe4eb7281de21f2471ed6a9b18a5aa Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 12:13:36 +0100 Subject: [PATCH 27/83] Removed 3d capability. --- src/solvers/dgsem_structured/dg_3d.jl | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/solvers/dgsem_structured/dg_3d.jl b/src/solvers/dgsem_structured/dg_3d.jl index 98d82f712b4..2c823042c7d 100644 --- a/src/solvers/dgsem_structured/dg_3d.jl +++ b/src/solvers/dgsem_structured/dg_3d.jl @@ -706,21 +706,4 @@ function apply_jacobian!(du, end -function total_volume(mesh::Union{StructuredMesh{3}, P4estMesh{3}}, dg, cache) - @unpack weights = dg.basis - - total_volume = zero(real(mesh)) - - # Use quadrature to numerically integrate over entire domain - for element in eachelement(dg, cache) - for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg) - volume_jacobian = abs(inv(cache.elements.inverse_jacobian[i, j, k, element])) - total_volume += volume_jacobian * weights[i] * weights[j] * weights[k] - end - end - - return total_volume -end - - end # @muladd From ca6f6fd2af99b9c42681ed87589cae51f734e3f2 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 12:14:17 +0100 Subject: [PATCH 28/83] Removed 3d plotting of coupled data. --- src/visualization/types.jl | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/visualization/types.jl b/src/visualization/types.jl index bdd2e1d20e2..50749f8fd3b 100644 --- a/src/visualization/types.jl +++ b/src/visualization/types.jl @@ -345,41 +345,6 @@ function PlotData2D(u::StructArray, mesh, equations, dg::DGMulti, cache; return PlotData2DTriangulated(x_plot, y_plot, u_plot, t, x_face, y_face, face_data, variable_names) end -function PlotData2D(u_ode::AbstractVector, semi::SemidiscretizationCoupled; - solution_variables=nothing, nvisnodes=2*polydeg(semi), kwargs...) - @assert ndims(semi) == 2 "unsupported number of dimensions $ndims (must be 2)" - - x_vec = [] - y_vec = [] - data_vec = [] - pd = 0 - - @unpack semis, u_indices = semi - for i in 1:nmeshes(semi) - mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) - - u = wrap_array(u_ode[u_indices[i]], semis[i]) - - pd = PlotData2D(u, mesh::Union{StructuredMesh, UnstructuredMesh2D, P4estMesh}, equations, solver, cache; - solution_variables=solution_variables, nvisnodes=nvisnodes) - - push!(x_vec, pd.x) - push!(y_vec, pd.y) - push!(data_vec, pd.data) - end - - xplot = hcat(x_vec...) - yplot = hcat(y_vec...) - data = hcat(data_vec...) - - t = pd.t - xfp = pd.x_face - yfp = pd.y_face - ufp = pd.face_data - variable_names = pd.variable_names - - return PlotData2DTriangulated(xplot, yplot, data, t, xfp, yfp, ufp, variable_names) -end # specializes the PlotData2D constructor to return an PlotData2DTriangulated for any type of mesh. function PlotData2DTriangulated(u, mesh, equations, dg::DGSEM, cache; From 7dc5a806cc54a1bbd0c77679ea7bdf7103b09d79 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 12 May 2023 17:04:40 +0200 Subject: [PATCH 29/83] Remove extra dependencies --- Project.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Project.toml b/Project.toml index 8974a797d61..3ce9d0c559b 100644 --- a/Project.toml +++ b/Project.toml @@ -4,8 +4,6 @@ authors = ["Michael Schlottke-Lakemper ", "Gregor version = "0.5.20-pre" [deps] -AbbreviatedStackTraces = "ac637c84-cc71-43bf-9c33-c1b4316be3d4" -CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" @@ -14,7 +12,6 @@ FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" -Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890" @@ -22,7 +19,6 @@ MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" Octavian = "6fd5a793-0b7e-452c-907f-f8bfe9c57db4" OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" P4est = "7d669430-f675-4ae7-b43e-fab78ec5a902" Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" From 900c62d5d2539a20e1b14c6d5886ed01d737a39a Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 12 May 2023 17:07:09 +0200 Subject: [PATCH 30/83] Remove whitespace changes --- src/equations/hyperbolic_diffusion_2d.jl | 1 + src/visualization/types.jl | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equations/hyperbolic_diffusion_2d.jl b/src/equations/hyperbolic_diffusion_2d.jl index 165cad40bc5..0f24949faad 100644 --- a/src/equations/hyperbolic_diffusion_2d.jl +++ b/src/equations/hyperbolic_diffusion_2d.jl @@ -164,6 +164,7 @@ end sqrt(equations.nu * equations.inv_Tr) * norm(normal_direction) end + @inline function flux_godunov(u_ll, u_rr, orientation::Integer, equations::HyperbolicDiffusionEquations2D) # Obtain left and right fluxes phi_ll, q1_ll, q2_ll = u_ll diff --git a/src/visualization/types.jl b/src/visualization/types.jl index 50749f8fd3b..a83b5bc92c6 100644 --- a/src/visualization/types.jl +++ b/src/visualization/types.jl @@ -345,7 +345,6 @@ function PlotData2D(u::StructArray, mesh, equations, dg::DGMulti, cache; return PlotData2DTriangulated(x_plot, y_plot, u_plot, t, x_face, y_face, face_data, variable_names) end - # specializes the PlotData2D constructor to return an PlotData2DTriangulated for any type of mesh. function PlotData2DTriangulated(u, mesh, equations, dg::DGSEM, cache; solution_variables=nothing, nvisnodes=2*polydeg(dg)) From 54963c21e5237cd0615a140d4201fe84aaf4170b Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 12 May 2023 17:07:44 +0200 Subject: [PATCH 31/83] Fix type instability --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 4580c5d2e7a..87696c2f248 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -13,7 +13,7 @@ struct SemidiscretizationCoupled{S, I} <: AbstractSemidiscretization semis::S u_indices::I # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] performance_counter::PerformanceCounter - equation_list::Vector + equation_list::Vector{Int} end From 9211d9a2336e27d902755348a29f2d45d375af73 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Fri, 12 May 2023 18:14:02 +0100 Subject: [PATCH 32/83] Some temporary fixes. --- .../elixir_advection_basic_coupled.jl | 2 ++ src/callbacks_step/analysis.jl | 3 ++- .../semidiscretization_coupled.jl | 19 ++++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 7071e3ea527..2b2875fc75b 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -5,6 +5,8 @@ using Trixi ############################################################################### # semidiscretization of the linear advection equation +Trixi.wrap_array(u_ode::AbstractVector, mesh::Trixi.AbstractMesh, equations, dg::DGSEM, cache) = Trixi.wrap_array_native(u_ode, mesh, equations, dg, cache) + advection_velocity = (0.2, -0.7) equations = LinearScalarAdvectionEquation2D(advection_velocity) diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 4838dd0634c..518f3fd5286 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -123,7 +123,7 @@ end function AnalysisCallback(semi::SemidiscretizationCoupled; kwargs...) - _, equations, _, _ = mesh_equations_solver_cache(semi) + _, equations, _, _ = mesh_equations_solver_cache(semi.semis[1]) AnalysisCallback(semi, equations; kwargs...) end @@ -135,6 +135,7 @@ function AnalysisCallback(semi::SemidiscretizationCoupled, equations; output_directory="out", analysis_filename="analysis.dat", extra_analysis_errors=Symbol[], + # TODO: add default analysis errors for a set of equations analysis_errors=union(default_analysis_errors(equations), extra_analysis_errors), extra_analysis_integrals=(), analysis_integrals=union(default_analysis_integrals(equations), extra_analysis_integrals), diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 4580c5d2e7a..09dab4c6111 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -127,9 +127,9 @@ end # TODO: Find out where this is being used. @inline function mesh_equations_solver_cache(semi::SemidiscretizationCoupled) - _, equations, _, _ = mesh_equations_solver_cache(semi.semis[1]) + mesh, equations, solver, equations = mesh_equations_solver_cache(semi.semis[1]) - return nothing, equations, nothing, nothing + return mesh, equations, solver, equations end @@ -159,11 +159,17 @@ end function integrate(func::Func, u_ode::AbstractVector, semi::SemidiscretizationCoupled; normalize=true) where {Func} @unpack semis, u_indices = semi - integral = [] + # TODO: fix issue with integral being a vector. + # integral = [] for i = 1:nmeshes(semi) mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) - u = wrap_array(u_ode[u_indices[i]], mesh, equations, solver, cache) - integral = vcat(integral, integrate(func, u, mesh, equations, solver, cache, normalize=false)) + u = wrap_array(u_ode[u_indices[i]], mesh, equations, solver, cache) + if i == 1 + integral = integrate(func, u, mesh, equations, solver, cache, normalize=false) + else + integral = integral + integrate(func, u, mesh, equations, solver, cache, normalize=false) + end + # integral = vcat(integral, integrate(func, u, mesh, equations, solver, cache, normalize=false)) end # Normalize with total volume @@ -175,6 +181,9 @@ function integrate(func::Func, u_ode::AbstractVector, semi::SemidiscretizationCo return integral end +function integrate(u_ode::AbstractVector, semi::SemidiscretizationCoupled; normalize=true) + integrate(cons2cons, u_ode, semi; normalize=normalize) +end function total_volume(semi::SemidiscretizationCoupled) sum(semi.semis) do semi_ From cad962e41859b0174c8d79cde3911bc84f79b26e Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 13 May 2023 07:03:16 +0200 Subject: [PATCH 33/83] Fix type in semidiscretization --- src/semidiscretization/semidiscretization_coupled.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 1b315a6a091..3cc7ad11a6c 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -9,11 +9,11 @@ The semidiscretizations can be coupled by gluing meshes together using `Boundary !!! warning "Experimental code" This is an experimental feature and can change any time. """ -struct SemidiscretizationCoupled{S, I} <: AbstractSemidiscretization +struct SemidiscretizationCoupled{S, I, EquationList} <: AbstractSemidiscretization semis::S u_indices::I # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] performance_counter::PerformanceCounter - equation_list::Vector{Int} + equation_list::EquationList end @@ -46,7 +46,7 @@ function SemidiscretizationCoupled(semis, other_list) performance_counter = PerformanceCounter() - SemidiscretizationCoupled{typeof(semis), typeof(u_indices)}(semis, u_indices, performance_counter, equation_list) + SemidiscretizationCoupled{typeof(semis), typeof(u_indices), typeof(tuple(equation_list...))}(semis, u_indices, performance_counter, tuple(equation_list...)) end From 01d186fa7294ad778471d3a276d466a453900b48 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Mon, 15 May 2023 13:43:09 +0200 Subject: [PATCH 34/83] Removed analysis_callback for simple coupled elixir. --- examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 2b2875fc75b..0042151a8c6 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -74,7 +74,7 @@ save_solution = SaveSolutionCallback(interval=100, stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, stepsize_callback, analysis_callback, save_solution) +callbacks = CallbackSet(summary_callback, stepsize_callback, save_solution) ############################################################################### From 477816ec5b013216539723d835105f6104a8f2e8 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Wed, 17 May 2023 09:51:16 +0200 Subject: [PATCH 35/83] Removed analysis callbacks for the coupled case. --- src/callbacks_step/analysis.jl | 78 ---------------------------------- 1 file changed, 78 deletions(-) diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 518f3fd5286..969c6b808a8 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -122,62 +122,6 @@ function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache; end -function AnalysisCallback(semi::SemidiscretizationCoupled; kwargs...) - _, equations, _, _ = mesh_equations_solver_cache(semi.semis[1]) - AnalysisCallback(semi, equations; kwargs...) -end - - -# The SemidiscretizationCoupled needs multiple analyzers (one for each mesh) -function AnalysisCallback(semi::SemidiscretizationCoupled, equations; - interval=0, - save_analysis=false, - output_directory="out", - analysis_filename="analysis.dat", - extra_analysis_errors=Symbol[], - # TODO: add default analysis errors for a set of equations - analysis_errors=union(default_analysis_errors(equations), extra_analysis_errors), - extra_analysis_integrals=(), - analysis_integrals=union(default_analysis_integrals(equations), extra_analysis_integrals), - RealT=real(semi), - # uEltype=eltype(semi), - uEltype=eltype(semi.semis[1].cache.elements), - kwargs...) - - # print("typeof(semi) = ", typeof(semi), "\n") - # print("fieldnames(typeof(semi)) = ", fieldnames(typeof(semi)), "\n") - # print("typeof(semi.semis[1].cache.elements) = ", typeof(semi.semis[1].cache.elements), "\n") - - # when is the callback activated - condition = (u, t, integrator) -> interval > 0 && (integrator.iter % interval == 0 || - isfinished(integrator)) - - analyzers = map(semi.semis) do semi_ - _, _, solver, _ = mesh_equations_solver_cache(semi_) - - analyzer = SolutionAnalyzer(solver; kwargs...) - end - - caches_analysis = map(semi.semis) do semi_ - mesh, equations_, solver, cache = mesh_equations_solver_cache(semi_) - - analyzer = SolutionAnalyzer(solver; kwargs...) - cache_analysis = create_cache_analysis(analyzer, mesh, equations_, solver, cache, RealT, uEltype) - end - - analysis_callback = AnalysisCallback(0.0, 0.0, 0, 0.0, - interval, save_analysis, output_directory, analysis_filename, - analyzers, - analysis_errors, Tuple(analysis_integrals), - SVector(ntuple(_ -> zero(uEltype), Val(nvariables(equations)))), - caches_analysis) - - DiscreteCallback(condition, analysis_callback, - save_positions=(false,false), - initialize=initialize!) -end - - function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, t, integrator) where {Condition, Affect!<:AnalysisCallback} semi = integrator.p initial_state_integrals = integrate(u_ode, semi) @@ -637,28 +581,6 @@ function analyze(quantity, du, u, t, mesh, equations, equations_parabolic, solve end -function analyze(quantity, du_ode, u_ode, t, semi::SemidiscretizationCoupled; normalize=true) - @unpack semis, u_indices = semi - - integral = sum(1:nmeshes(semi)) do i - mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) - # In the AnalysisCallback for SemidiscretizationCoupled, u_ode is never wrapped - du = wrap_array(du_ode[u_indices[i]], mesh, equations, solver, cache) - u = wrap_array(u_ode[u_indices[i]], mesh, equations, solver, cache) - - analyze(quantity, du, u, t, mesh, equations, solver, cache, normalize=false) - end - - if normalize - # Normalize with total volume - total_volume_ = total_volume(semi) - integral = integral / total_volume_ - end - - return integral -end - - function entropy_timederivative end pretty_form_utf(::typeof(entropy_timederivative)) = "∑∂S/∂U ⋅ Uₜ" pretty_form_ascii(::typeof(entropy_timederivative)) = "dsdu_ut" From 4ba8fc5fc7498ecd580b41b631ba72077791e9bb Mon Sep 17 00:00:00 2001 From: SimonCan Date: Wed, 17 May 2023 09:55:48 +0200 Subject: [PATCH 36/83] Removed AnalysysCallback for coupled elixir. --- examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 0042151a8c6..7889c5fbd96 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -63,9 +63,6 @@ ode = semidiscretize(semi, (0.0, 1.0)); # and resets the timers summary_callback = SummaryCallback() -# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_callback = AnalysisCallback(semi, interval=100) - # The SaveSolutionCallback allows to save the solution to a file in regular intervals save_solution = SaveSolutionCallback(interval=100, solution_variables=cons2prim) From 531ec9e86100e71f2a19ff5e99afbfe4efd30423 Mon Sep 17 00:00:00 2001 From: SimonCan Date: Wed, 17 May 2023 10:02:31 +0200 Subject: [PATCH 37/83] Removed polytropic coupling elixir. --- .../elixir_coupled_polytropic_euler.jl | 140 ------------------ 1 file changed, 140 deletions(-) delete mode 100644 examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl diff --git a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl b/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl deleted file mode 100644 index f2cdca5f903..00000000000 --- a/examples/structured_2d_dgsem/elixir_coupled_polytropic_euler.jl +++ /dev/null @@ -1,140 +0,0 @@ -using OrdinaryDiffEq -using Trixi - - -""" -Coupled two polytropic Euler systems. -""" - - -############################################################################### -# define the initial conditions - -function initial_condition_wave_isotropic(x, t, equations::PolytropicEulerEquations2D) - @unpack gamma, kappa = equations - - rho = 1.0 - v1 = 0.0 - if x[1] > 10.0 - rho = ((1.0 + 0.01*sin(x[1]*2*pi)) / kappa)^(1/gamma) - v1 = ((0.01*sin((x[1]-1/2)*2*pi)) / kappa) - end - v2 = 0.0 - - return prim2cons(SVector(rho, v1, v2), equations) -end - -function initial_condition_wave_polytropic(x, t, equations::PolytropicEulerEquations2D) - @unpack gamma, kappa = equations - - rho = 1.0 - v1 = 0.0 - if x[1] > 0.0 - rho = ((1.0 + 0.01*sin(x[1]*2*pi)) / kappa)^(1/gamma) - v1 = ((0.01*sin((x[1]-1/2)*2*pi)) / kappa) - end - v2 = 0.0 - - return prim2cons(SVector(rho, v1, v2), equations) -end - -############################################################################### -# semidiscretization of the linear advection equation - -Trixi.wrap_array(u_ode::AbstractVector, mesh::Trixi.AbstractMesh, equations, dg::DGSEM, cache) = Trixi.wrap_array_native(u_ode, mesh, equations, dg, cache) - -# Define the equations involved. -gamma_A = 1.001 -kappa_A = 1.0 -equations_A = PolytropicEulerEquations2D(gamma_A, kappa_A) -gamma_B = 2.0 -kappa_B = 1.0 -equations_B = PolytropicEulerEquations2D(gamma_B, kappa_B) - -equations_coupling = CouplingPolytropicEuler2D(0.01) - -# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -volume_flux = flux_ranocha -solver = DGSEM(polydeg=2, surface_flux=flux_hll, - volume_integral=VolumeIntegralFluxDifferencing(volume_flux)) - -coordinates_min_A = (-2.0, -1.0) # minimum coordinates (min(x), min(y)) -coordinates_max_A = ( 0.0, 1.0) # maximum coordinates (max(x), max(y)) -coordinates_min_B = ( 0.0, -1.0) # minimum coordinates (min(x), min(y)) -coordinates_max_B = ( 2.0, 1.0) # maximum coordinates (max(x), max(y)) - -cells_per_dimension = (32, 32) - -mesh_A = StructuredMesh(cells_per_dimension, - coordinates_min_A, - coordinates_max_A) -mesh_B = StructuredMesh(cells_per_dimension, - coordinates_min_B, - coordinates_max_B) - -# A semidiscretization collects data structures and functions for the spatial discretization. -semi_A = SemidiscretizationHyperbolic(mesh_A, equations_A, - initial_condition_wave_isotropic, solver, - boundary_conditions=( - x_neg=BoundaryConditionCoupled(2, (:end, :i_forward), Float64), - x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64), - y_neg=boundary_condition_periodic, - y_pos=boundary_condition_periodic)) - -semi_B = SemidiscretizationHyperbolic(mesh_B, equations_B, - initial_condition_wave_polytropic, solver, - boundary_conditions=( - x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64), - x_pos=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64), - y_neg=boundary_condition_periodic, - y_pos=boundary_condition_periodic)) - -# Define the indices of the 'other' system. -# This is not ideal. -other_list = [2, 1] - -# Create a semidiscretization that bundles semi1 and semi2 -semi_coupled = SemidiscretizationCoupled((semi_A, semi_B), other_list) - - -############################################################################### -# ODE solvers, callbacks etc. - -# Create ODE problem with time span from 0.0 to 30.0 -tspan = (0.0, 3.0) -ode = semidiscretize(semi_coupled, tspan) - -# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup -# and resets the timers -summary_callback = SummaryCallback() - -# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results -analysis_interval = 100 -analysis_callback = AnalysisCallback(semi_coupled, interval=analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) - -# The SaveSolutionCallback allows to save the solution to a file in regular intervals -save_solution = SaveSolutionCallback(interval=5, - solution_variables=cons2prim) - -# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step -stepsize_callback = StepsizeCallback(cfl=1.0) - -# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, - alive_callback, -# analysis_callback, - save_solution, - stepsize_callback) - - -############################################################################### -# run the simulation - -# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); - -# Print the timer summary -summary_callback() From b47fa11641c9ee8e7aa65803407cd29e674eeb64 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 12:27:59 +0200 Subject: [PATCH 38/83] Update summary output --- src/callbacks_step/summary.jl | 23 ++++++++------ .../semidiscretization_coupled.jl | 30 ++++++++++++------- test/test_visualization.jl | 2 +- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/callbacks_step/summary.jl b/src/callbacks_step/summary.jl index 37428f49651..318d9f86809 100644 --- a/src/callbacks_step/summary.jl +++ b/src/callbacks_step/summary.jl @@ -149,15 +149,7 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) :indentation_level => 0) semi = integrator.p - show(io_context, MIME"text/plain"(), semi) - println(io, "\n") - mesh, equations, solver, _ = mesh_equations_solver_cache(semi) - show(io_context, MIME"text/plain"(), mesh) - println(io, "\n") - show(io_context, MIME"text/plain"(), equations) - println(io, "\n") - show(io_context, MIME"text/plain"(), solver) - println(io, "\n") + summary_semidiscretization(semi, io, io_context) callbacks = integrator.opts.callback if callbacks isa CallbackSet @@ -212,6 +204,19 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) end +function summary_semidiscretization(semi, io, io_context) + show(io_context, MIME"text/plain"(), semi) + println(io, "\n") + mesh, equations, solver, _ = mesh_equations_solver_cache(semi) + show(io_context, MIME"text/plain"(), mesh) + println(io, "\n") + show(io_context, MIME"text/plain"(), equations) + println(io, "\n") + show(io_context, MIME"text/plain"(), solver) + println(io, "\n") +end + + function (cb::DiscreteCallback{Condition,Affect!})(io::IO=stdout) where {Condition, Affect!<:typeof(summary_callback)} mpi_isroot() || return nothing diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 3cc7ad11a6c..14f7942d738 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -64,13 +64,16 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) else summary_header(io, "SemidiscretizationCoupled") summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) - summary_line(io, "#meshes", nmeshes(semi)) + summary_line(io, "#semidiscretizations", nmeshes(semi)) for i = 1:nmeshes(semi) - summary_line(io, "equations", mesh_equations_solver_cache(semi.semis[i])[2] |> typeof |> nameof) - summary_line(io, "initial conditions", semi.semis[i].initial_condition) + summary_line(io, "semidiscretization", i) + mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) + summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) + summary_line(increment_indent(io), "equations", equations |> typeof |> nameof) + summary_line(increment_indent(io), "initial condition", semi.semis[i].initial_condition) # TODO boundary conditions? That will be 36 BCs for a cubed sphere - summary_line(io, "source terms", semi.semis[i].source_terms) - summary_line(io, "solvers", mesh_equations_solver_cache(semi.semis[i])[3] |> typeof |> nameof) + summary_line(increment_indent(io), "source terms", semi.semis[i].source_terms) + summary_line(increment_indent(io), "solver", solver |> typeof |> nameof) end summary_line(io, "total #DOFs", ndofs(semi)) summary_footer(io) @@ -83,11 +86,18 @@ function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_cont println(io, "\n") for i = 1:nmeshes(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) - show(io_context, MIME"text/plain"(), mesh) - println(io, "\n") - show(io_context, MIME"text/plain"(), equations) - println(io, "\n") - show(io_context, MIME"text/plain"(), solver) + summary_header(io, "Semidiscretization #$i") + + summary_line(io_context, "mesh", mesh |> typeof |> nameof) + show(increment_indent(io_context), MIME"text/plain"(), mesh) + + summary_line(io_context, "equations", equations |> typeof |> nameof) + show(increment_indent(io_context), MIME"text/plain"(), equations) + + summary_line(io_context, "solver", solver |> typeof |> nameof) + show(increment_indent(io_context), MIME"text/plain"(), solver) + + summary_footer(io) println(io, "\n") end end diff --git a/test/test_visualization.jl b/test/test_visualization.jl index d0b3d256742..b700fc71a8f 100644 --- a/test/test_visualization.jl +++ b/test/test_visualization.jl @@ -25,7 +25,7 @@ isdir(outdir) && rm(outdir, recursive=true) "StructuredMesh" => ("structured_2d_dgsem", "elixir_euler_source_terms_waving_flag.jl"), "UnstructuredMesh" => ("unstructured_2d_dgsem", "elixir_euler_basic.jl"), "P4estMesh" => ("p4est_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), - "DGMulti" => ("dgmulti_2d", "elixir_euler_weakform.jl") + "DGMulti" => ("dgmulti_2d", "elixir_euler_weakform.jl"), ) @testset "PlotData2D, PlotDataSeries, PlotMesh with $mesh" for mesh in keys(test_examples_2d) From 5c8b4cb9746c62b706d3111d037977c76abac6e5 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 12:29:01 +0200 Subject: [PATCH 39/83] Update src/solvers/dgsem_structured/dg_2d.jl --- src/solvers/dgsem_structured/dg_2d.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index 8f5313ecd45..8db80436ce9 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -561,6 +561,7 @@ function apply_jacobian!(du, end +# Compute total volume of a given mesh function total_volume(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, dg, cache) @unpack weights = dg.basis From 29c122c68dd3c9bdd44c96e65f70fc7197bc22ab Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:05:12 +0200 Subject: [PATCH 40/83] Format summary --- src/semidiscretization/semidiscretization_coupled.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 14f7942d738..8b6c5e27813 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -64,9 +64,9 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) else summary_header(io, "SemidiscretizationCoupled") summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) - summary_line(io, "#semidiscretizations", nmeshes(semi)) + summary_line(io, "#system", nmeshes(semi)) for i = 1:nmeshes(semi) - summary_line(io, "semidiscretization", i) + summary_line(io, "system", i) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) summary_line(increment_indent(io), "equations", equations |> typeof |> nameof) @@ -86,7 +86,7 @@ function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_cont println(io, "\n") for i = 1:nmeshes(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) - summary_header(io, "Semidiscretization #$i") + summary_header(io, "System #$i") summary_line(io_context, "mesh", mesh |> typeof |> nameof) show(increment_indent(io_context), MIME"text/plain"(), mesh) From a82e9e3d04182235cb16db8a8f9c7c93a1881c12 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:05:25 +0200 Subject: [PATCH 41/83] Fix save solution callback --- src/callbacks_step/save_solution.jl | 52 +++++++---------------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 7d571c7296b..51128512bee 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -135,13 +135,6 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte mpi_isroot() && mkpath(solution_callback.output_directory) semi = integrator.p - # mesh, _, _, _ = mesh_equations_solver_cache(semi) - # @trixi_timeit timer() "I/O" begin - # if mesh.unsaved_changes - # mesh.current_filename = save_mesh_file(mesh, solution_callback.output_directory) - # mesh.unsaved_changes = false - # end - # end @trixi_timeit timer() "I/O" save_mesh(semi, solution_callback.output_directory) if solution_callback.save_initial_solution @@ -152,6 +145,7 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte end +# Save mesh for a general semidiscretization (default) function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep=0) mesh, _, _, _ = mesh_equations_solver_cache(semi) @@ -162,6 +156,7 @@ function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep= end +# Save mesh for a coupled semidiscretization, which contains multiple meshes internally function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) for i in 1:nmeshes(semi) mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) @@ -192,36 +187,13 @@ end # this method is called when the callback is activated function (solution_callback::SaveSolutionCallback)(integrator) u_ode = integrator.u - @unpack t, dt = integrator - iter = integrator.stats.naccept semi = integrator.p - # mesh, _, _, _ = mesh_equations_solver_cache(semi) + iter = integrator.stats.naccept @trixi_timeit timer() "I/O" begin - # @trixi_timeit timer() "save mesh" if mesh.unsaved_changes - # mesh.current_filename = save_mesh_file(mesh, solution_callback.output_directory, iter) - # mesh.unsaved_changes = false - # end - @trixi_timeit timer() "save mesh" save_mesh(semi, solution_callback.output_directory) - - element_variables = Dict{Symbol, Any}() - # @trixi_timeit timer() "get element variables" begin - # # print("typeof(semi) = ", typeof(semi), "\n") - # get_element_variables!(element_variables, u_ode, semi) - # callbacks = integrator.opts.callback - # if callbacks isa CallbackSet - # for cb in callbacks.continuous_callbacks - # get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) - # end - # for cb in callbacks.discrete_callbacks - # get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) - # end - # end - # end - get_element_variables_semi!(semi, integrator, u_ode, element_variables) - - @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, solution_callback, element_variables, - integrator=integrator) + # Call high-level functions that dispatch on semidiscretization type + @trixi_timeit timer() "save mesh" save_mesh(semi, solution_callback.output_directory, iter) + save_solution_file(semi, u_ode, solution_callback, integrator) end # avoid re-evaluating possible FSAL stages @@ -265,7 +237,7 @@ end @inline function save_solution_file(semi::AbstractSemidiscretization, u_ode, solution_callback, - integrator; system="") + integrator; system="") @unpack t, dt = integrator iter = integrator.stats.naccept @@ -284,14 +256,14 @@ end end @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, - solution_callback, element_variables, - system=system) + solution_callback, element_variables, + system=system) end -@inline function save_solution_file(u_ode, t, dt, iter, - semi::SemidiscretizationCoupled, solution_callback, - element_variables=Dict{Symbol,Any}(); + + +@inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode, solution_callback, integrator) @unpack semis, u_indices = semi From 3091bdc67da15b0c20af6ef23ce5aaf4bec71919 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:07:57 +0200 Subject: [PATCH 42/83] Remove unused code --- src/callbacks_step/save_solution.jl | 34 ----------------------------- 1 file changed, 34 deletions(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 51128512bee..4ef47b00e50 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -202,40 +202,6 @@ function (solution_callback::SaveSolutionCallback)(integrator) end -function get_element_variables_semi!(semi::AbstractSemidiscretization, integrator, u_ode, element_variables) - @trixi_timeit timer() "get element variables" begin - get_element_variables!(element_variables, u_ode, semi) - callbacks = integrator.opts.callback - if callbacks isa CallbackSet - for cb in callbacks.continuous_callbacks - get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) - end - for cb in callbacks.discrete_callbacks - get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=integrator.stats.naccept) - end - end - end -end - - -function get_element_variables_semi!(semi::SemidiscretizationCoupled, integrator, u_ode, element_variables) - for i in 1:nmeshes(semi) - @trixi_timeit timer() "get element variables" begin - get_element_variables!(element_variables, u_ode[semi.u_indices[i]], semi.semis[i]) - callbacks = integrator.opts.callback - if callbacks isa CallbackSet - for cb in callbacks.continuous_callbacks - get_element_variables!(element_variables, u_ode[semi.u_indices[i]], semi.semis[i], cb; t=integrator.t, iter=integrator.stats.naccept) - end - for cb in callbacks.discrete_callbacks - get_element_variables!(element_variables, u_ode[semi.u_indices[i]], semi.semis[i], cb; t=integrator.t, iter=integrator.stats.naccept) - end - end - end - end -end - - @inline function save_solution_file(semi::AbstractSemidiscretization, u_ode, solution_callback, integrator; system="") @unpack t, dt = integrator From 53ed4569be21a8e74a6823aa0c2e9027be993668 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:14:51 +0200 Subject: [PATCH 43/83] Move timeit call before dispatch on semi --- src/callbacks_step/stepsize.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 1d3ecd8a039..626a333e86a 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -70,12 +70,10 @@ end semi = integrator.p mesh, equations, solver, cache = mesh_equations_solver_cache(semi) @unpack cfl_number = stepsize_callback - # u = wrap_array(u_ode, mesh, equations, solver, cache) - # dt = @trixi_timeit timer() "calculate dt" cfl_number * max_dt(u, t, mesh, - # have_constant_speed(equations), equations, - # solver, cache) - dt = calculate_dt(u_ode, t, cfl_number, semi) + # Dispatch based on semidiscretization + dt = @trixi_timeit timer() "calculate dt" calculate_dt(u_ode, t, cfl_number, semi) + set_proposed_dt!(integrator, dt) integrator.opts.dtmax = dt integrator.dtcache = dt @@ -87,16 +85,18 @@ end end -function calculate_dt(u_ode, t, cfl_number, semi) +# General case for a single semidiscretization +function calculate_dt(u_ode, t, cfl_number, semi::AbstractSemidiscretization) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) u = wrap_array(u_ode, mesh, equations, solver, cache) - dt = @trixi_timeit timer() "calculate dt" cfl_number * max_dt(u, t, mesh, - have_constant_speed(equations), equations, - solver, cache) + dt = cfl_number * max_dt(u, t, mesh, + have_constant_speed(equations), equations, + solver, cache) end +# In case of coupled system, use minimum timestep over all systems function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) @unpack u_indices = semi From 9ff491a5761a3c78a3d1634c331416626d3b65c4 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:25:30 +0200 Subject: [PATCH 44/83] Avoid copy on calculcate_dt --- src/callbacks_step/stepsize.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 626a333e86a..e4ab8a59ede 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -101,7 +101,8 @@ function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) @unpack u_indices = semi dt = minimum(1:nmeshes(semi)) do i - calculate_dt(u_ode[u_indices[i]], t, cfl_number, semi.semis[i]) + u_ode_slice = @view u_ode[u_indices[i]] + calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) end return dt From e15d6e750237c41dc7f710b1bcba8c9ce8f5e588 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:27:22 +0200 Subject: [PATCH 45/83] Avoid copy on save_solution_file --- src/callbacks_step/save_solution.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 4ef47b00e50..1bf428021fd 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -234,7 +234,8 @@ end @unpack semis, u_indices = semi for i in 1:nmeshes(semi) - save_solution_file(semis[i], u_ode[u_indices[i]], solution_callback, integrator, system=i) + u_ode_slice = @view u_ode[u_indices[i]] + save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) end end From 7a8e433af3e9315b43d67049204af545ad3240a2 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:31:17 +0200 Subject: [PATCH 46/83] Remove unnnecessary override of wrap_array --- examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 7889c5fbd96..8d005ef0d69 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -5,8 +5,6 @@ using Trixi ############################################################################### # semidiscretization of the linear advection equation -Trixi.wrap_array(u_ode::AbstractVector, mesh::Trixi.AbstractMesh, equations, dg::DGSEM, cache) = Trixi.wrap_array_native(u_ode, mesh, equations, dg, cache) - advection_velocity = (0.2, -0.7) equations = LinearScalarAdvectionEquation2D(advection_velocity) From 4a15275edca751c017a74048cae1d8bed8371984 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 31 May 2023 15:36:12 +0200 Subject: [PATCH 47/83] Undo changes to analysis callback --- src/callbacks_step/analysis.jl | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index 68cd7af99f7..c6a2ee6fb95 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -255,8 +255,7 @@ function (analysis_callback::AnalysisCallback)(integrator) mpi_println() mpi_println("─"^100) # TODO: Taal refactor, polydeg is specific to DGSEM - # mpi_println(" Simulation running '", get_name(equations), "' with ", summary(solver)) - mpi_println(" Simulation running '", get_name(equations), "' with ", summary(semi)) + mpi_println(" Simulation running '", get_name(equations), "' with ", summary(solver)) mpi_println("─"^100) mpi_println(" #timesteps: " * @sprintf("% 14d", iter) * " " * @@ -276,8 +275,7 @@ function (analysis_callback::AnalysisCallback)(integrator) mpi_println(" #elements: " * @sprintf("% 14d", nelements(mesh, solver, cache))) # Level information (only show for AMR) - # print_amr_information(integrator.opts.callback, mesh, solver, cache) - print_amr_information(integrator.opts.callback, semi) + print_amr_information(integrator.opts.callback, mesh, solver, cache) mpi_println() # Open file for appending and store time step and time information @@ -471,21 +469,6 @@ function print_amr_information(callbacks, mesh, solver, cache) return nothing end -function print_amr_information(callbacks, semi) - # Return early if there is nothing to print - uses_amr(callbacks) || return nothing - - mesh, equations, solver, cache = mesh_equations_solver_cache(semi) - print_amr_information(mesh, equations, solver, cache) -end - -function print_amr_information(mesh::TreeMesh, equations, solver, cache) - levels = Vector{Int}(undef, nelements(solver, cache)) - min_level = typemax(Int) - max_level = typemin(Int) - return nothing -end - # Print level information only if AMR is enabled function print_amr_information(callbacks, mesh::P4estMesh, solver, cache) @@ -564,8 +547,8 @@ function analyze(quantity, du, u, t, semi::AbstractSemidiscretization) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) analyze(quantity, du, u, t, mesh, equations, solver, cache) end -function analyze(quantity, du, u, t, mesh, equations, solver, cache; normalize=true) - integrate(quantity, u, mesh, equations, solver, cache, normalize=normalize) +function analyze(quantity, du, u, t, mesh, equations, solver, cache) + integrate(quantity, u, mesh, equations, solver, cache, normalize=true) end pretty_form_utf(quantity) = get_name(quantity) pretty_form_ascii(quantity) = get_name(quantity) From 6e81315849f82164023666b660740718f9754825 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 09:18:51 +0200 Subject: [PATCH 48/83] Remove equations_list --- .../semidiscretization_coupled.jl | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 8b6c5e27813..178d07e0f0e 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -13,7 +13,6 @@ struct SemidiscretizationCoupled{S, I, EquationList} <: AbstractSemidiscretizati semis::S u_indices::I # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] performance_counter::PerformanceCounter - equation_list::EquationList end @@ -23,30 +22,27 @@ end Create a coupled semidiscretization that consists of the semidiscretizations contained in the tuple `semis`. """ function SemidiscretizationCoupled(semis, other_list) - # @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" - equation_list = [] + @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" + + # Number of coefficients for each semidiscretization + n_coefficients = zeros(Int64, length(semis)) for i in 1:length(semis) _, equations, _, _ = mesh_equations_solver_cache(semis[i]) - append!(equation_list, [equations]) + n_coefficients[i] = ndofs(semis[i]) * nvariables(equations) end - # Number of coefficients as Vector - n_coeffs = zeros(Int64, length(semis)) - for i in 1:length(semis) - n_coeffs[i] = (semis[i] |> (x -> nvariables(equation_list[i]) * ndofs(x)) |> collect)[1] - end + # Compute range of coefficients associated with each semidiscretization u_indices = Vector{UnitRange{Int}}(undef, length(semis)) - for i in 1:length(semis) - offset = sum(n_coeffs[1:i-1]) + 1 - u_indices[i] = range(offset, length=n_coeffs[i]) + offset = sum(n_coefficients[1:i-1]) + 1 + u_indices[i] = range(offset, length=n_coefficients[i]) allocate_coupled_boundary_conditions(semis[i], semis[other_list[i]]) end performance_counter = PerformanceCounter() - SemidiscretizationCoupled{typeof(semis), typeof(u_indices), typeof(tuple(equation_list...))}(semis, u_indices, performance_counter, tuple(equation_list...)) + SemidiscretizationCoupled{typeof(semis), typeof(u_indices), typeof(performance_counter)}(semis, u_indices, performance_counter) end From 7a59b144f5b3cc05447cd3643c7461a7396cefb4 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 09:29:24 +0200 Subject: [PATCH 49/83] Remove unused functions --- src/callbacks_step/stepsize.jl | 1 - .../semidiscretization_coupled.jl | 69 +------------------ 2 files changed, 1 insertion(+), 69 deletions(-) diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index e4ab8a59ede..890893b50ef 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -68,7 +68,6 @@ end t = integrator.t u_ode = integrator.u semi = integrator.p - mesh, equations, solver, cache = mesh_equations_solver_cache(semi) @unpack cfl_number = stepsize_callback # Dispatch based on semidiscretization diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 178d07e0f0e..71c7b868c2c 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -116,13 +116,6 @@ end sum(ndofs, semi.semis) end -# TODO: Ask bout polydeg. -@inline function polydeg(semi::SemidiscretizationCoupled) - _, _, solver, _ = mesh_equations_solver_cache(semi.semis[1]) - - polydeg(solver) -end - @inline function nelements(semi::SemidiscretizationCoupled) return sum(semi.semis) do semi_ mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) @@ -131,67 +124,7 @@ end end end -# TODO: Find out where this is being used. -@inline function mesh_equations_solver_cache(semi::SemidiscretizationCoupled) - mesh, equations, solver, equations = mesh_equations_solver_cache(semi.semis[1]) - - return mesh, equations, solver, equations -end - - -function calc_error_norms(func, u_ode::AbstractVector, t, analyzers, - semi::SemidiscretizationCoupled, caches_analysis; - normalize=true) - @unpack semis, u_indices = semi - - # Sum up L2 integrals, use max on Linf error - op(x, y) = (x[1] + y[1], max(x[2], y[2])) - - l2_error, linf_error = mapreduce(op, 1:nmeshes(semi)) do i - calc_error_norms(func, u_ode[u_indices[i]], t, analyzers[i], - semis[i], caches_analysis[i]; normalize=false) - end - - if normalize - # For L2 error, divide by total volume - total_volume_ = total_volume(semi) - l2_error = @. sqrt(l2_error / total_volume_) - end - - return l2_error, linf_error -end - - -function integrate(func::Func, u_ode::AbstractVector, semi::SemidiscretizationCoupled; normalize=true) where {Func} - @unpack semis, u_indices = semi - - # TODO: fix issue with integral being a vector. - # integral = [] - for i = 1:nmeshes(semi) - mesh, equations, solver, cache = mesh_equations_solver_cache(semis[i]) - u = wrap_array(u_ode[u_indices[i]], mesh, equations, solver, cache) - if i == 1 - integral = integrate(func, u, mesh, equations, solver, cache, normalize=false) - else - integral = integral + integrate(func, u, mesh, equations, solver, cache, normalize=false) - end - # integral = vcat(integral, integrate(func, u, mesh, equations, solver, cache, normalize=false)) - end - - # Normalize with total volume - if normalize - total_volume_ = total_volume(semi) - integral = integral / total_volume_ - end - - return integral -end - -function integrate(u_ode::AbstractVector, semi::SemidiscretizationCoupled; normalize=true) - integrate(cons2cons, u_ode, semi; normalize=normalize) -end - -function total_volume(semi::SemidiscretizationCoupled) +@inline function total_volume(semi::SemidiscretizationCoupled) sum(semi.semis) do semi_ mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) total_volume(mesh, solver, cache) From 4bdc4bf1fc0846ef2200a8d70b9c38a9a36d6f0a Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 09:30:31 +0200 Subject: [PATCH 50/83] nmeshes -> nsystems --- src/semidiscretization/semidiscretization_coupled.jl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 71c7b868c2c..aa0c5068c95 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -60,8 +60,8 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) else summary_header(io, "SemidiscretizationCoupled") summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) - summary_line(io, "#system", nmeshes(semi)) - for i = 1:nmeshes(semi) + summary_line(io, "#systems", nsystems(semi)) + for i = 1:nsystems(semi) summary_line(io, "system", i) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) @@ -80,7 +80,7 @@ end function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_context) show(io_context, MIME"text/plain"(), semi) println(io, "\n") - for i = 1:nmeshes(semi) + for i = 1:nsystems(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) summary_header(io, "System #$i") @@ -108,6 +108,8 @@ end @inline nmeshes(semi::SemidiscretizationCoupled) = length(semi.semis) +@inline nsystems(semi::SemidiscretizationCoupled) = length(semi.semis) + @inline Base.real(semi::SemidiscretizationCoupled) = promote_type(real.(semi.semis)...) @inline Base.eltype(semi::SemidiscretizationCoupled) = promote_type(eltype.(semi.semis)...) @@ -137,7 +139,7 @@ function compute_coefficients(t, semi::SemidiscretizationCoupled) u_ode = Vector{real(semi)}(undef, u_indices[end][end]) - for i in 1:nmeshes(semi) + for i in 1:nsystems(semi) # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` u_ode[u_indices[i]] .= compute_coefficients(t, semi.semis[i]) end @@ -197,7 +199,7 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) end # Call rhs! for each semidiscretization - for i in 1:nmeshes(semi) + for i in 1:nsystems(semi) u_loc = @view u_ode[u_indices[i]] du_loc = @view du_ode[u_indices[i]] From bc764a9786c71bb33a7f3d650048851b72f2f5ed Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 12:47:39 +0200 Subject: [PATCH 51/83] Further cleanup --- .../elixir_advection_basic_coupled.jl | 3 +- src/callbacks_step/save_solution.jl | 4 +- src/callbacks_step/stepsize.jl | 2 +- .../semidiscretization_coupled.jl | 38 +++++++------------ 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 8d005ef0d69..25515006660 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -48,8 +48,7 @@ semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition_converg y_pos=boundary_condition_periodic)) # Create a semidiscretization that bundles semi1 and semi2 -other_list = [2, 1] -semi = SemidiscretizationCoupled((semi1, semi2), other_list) +semi = SemidiscretizationCoupled((semi1, semi2)) ############################################################################### # ODE solvers, callbacks etc. diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 1bf428021fd..9b50d756a19 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -158,7 +158,7 @@ end # Save mesh for a coupled semidiscretization, which contains multiple meshes internally function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) - for i in 1:nmeshes(semi) + for i in 1:nsystems(semi) mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) if mesh.unsaved_changes @@ -233,7 +233,7 @@ end integrator) @unpack semis, u_indices = semi - for i in 1:nmeshes(semi) + for i in 1:nsystems(semi) u_ode_slice = @view u_ode[u_indices[i]] save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) end diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 890893b50ef..9059fa780ce 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -99,7 +99,7 @@ end function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) @unpack u_indices = semi - dt = minimum(1:nmeshes(semi)) do i + dt = minimum(1:nsystems(semi)) do i u_ode_slice = @view u_ode[u_indices[i]] calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) end diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index aa0c5068c95..01c1219c460 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -17,11 +17,11 @@ end """ - SemidiscretizationCoupled(semis, other_list) + SemidiscretizationCoupled(semis) Create a coupled semidiscretization that consists of the semidiscretizations contained in the tuple `semis`. """ -function SemidiscretizationCoupled(semis, other_list) +function SemidiscretizationCoupled(semis) @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" # Number of coefficients for each semidiscretization @@ -31,13 +31,13 @@ function SemidiscretizationCoupled(semis, other_list) n_coefficients[i] = ndofs(semis[i]) * nvariables(equations) end - # Compute range of coefficients associated with each semidiscretization + # Compute range of coefficients associated with each semidiscretization and allocate coupled BCs u_indices = Vector{UnitRange{Int}}(undef, length(semis)) for i in 1:length(semis) offset = sum(n_coefficients[1:i-1]) + 1 u_indices[i] = range(offset, length=n_coefficients[i]) - allocate_coupled_boundary_conditions(semis[i], semis[other_list[i]]) + allocate_coupled_boundary_conditions(semis[i]) end performance_counter = PerformanceCounter() @@ -106,8 +106,6 @@ end @inline Base.ndims(semi::SemidiscretizationCoupled) = ndims(semi.semis[1]) -@inline nmeshes(semi::SemidiscretizationCoupled) = length(semi.semis) - @inline nsystems(semi::SemidiscretizationCoupled) = length(semi.semis) @inline Base.real(semi::SemidiscretizationCoupled) = promote_type(real.(semi.semis)...) @@ -148,42 +146,32 @@ function compute_coefficients(t, semi::SemidiscretizationCoupled) end -function allocate_coupled_boundary_conditions(semi, semi_other) +function allocate_coupled_boundary_conditions(semi::AbstractSemidiscretization) n_boundaries = 2 * ndims(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi) - _, equations_other, _, _ = mesh_equations_solver_cache(semi_other) for direction in 1:n_boundaries boundary_condition = semi.boundary_conditions[direction] - allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, equations_other, solver) + allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, solver) end - end -function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, equations_other, solver) end +# Don't do anything for other BCs than BoundaryConditionCoupled +function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, solver) +end # In 2D -function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{2}, direction, mesh, equations, equations_other, dg::DGSEM) +function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{2}, + direction, mesh, equations, dg::DGSEM) if direction in (1, 2) cell_size = size(mesh, 2) else cell_size = size(mesh, 1) end - boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations), nnodes(dg), cell_size) -end - -# In 3D -function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{3}, direction, mesh, equations, equations_other, dg::DGSEM) - if direction in (1, 2) - cell_size = (size(mesh, 2), size(mesh, 3)) - elseif direction in (3, 4) - cell_size = (size(mesh, 1), size(mesh, 3)) - else # direction in (5, 6) - (size(mesh, 1), size(mesh, 2)) - end - boundary_condition.u_boundary = Array{Float64, 5}(undef, nvariables(equations), nnodes(dg), nnodes(dg), cell_size...) + boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations), nnodes(dg), + cell_size) end From cf97a85114d521b45829f2f4547a9991a9034fbe Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 14:18:17 +0200 Subject: [PATCH 52/83] Move BoundaryConditionCoupled to the correct location --- src/equations/equations.jl | 142 +++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 717e6c00352..d70651724a8 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -202,6 +202,79 @@ struct BoundaryConditionNeumann{B} boundary_normal_flux_function::B end +""" + BoundaryConditionCoupled(other_semi_index, indices, uEltype) + +Boundary condition to glue two meshes together. Solution values at the boundary +of another mesh will be used as boundary values. This requires the use +of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`, +which is the index of the mesh in the tuple of semidiscretizations. +Note that the elements and nodes of the two meshes at the coupled boundary must coincide. +This is currently only implemented for [`StructuredMesh`](@ref). + +# Arguments +- `other_semi_index`: the index in `SemidiscretizationCoupled` of the semidiscretization + from which the values are copied +- `indices::Tuple`: node/cell indices at the boundary of the mesh in the other + semidiscretization. See examples below. +- `uEltype::Type`: element type of solution + +# Examples +```julia +# Connect the left boundary of mesh 2 to our boundary such that our positive +# boundary direction will match the positive y direction of the other boundary +BoundaryConditionCoupled(2, (1, :i), Float64) +# Connect the same two boundaries oppositely oriented +BoundaryConditionCoupled(2, (1, :i_backwards), Float64) +# Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` +BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) +``` + +!!! warning "Experimental code" + This is an experimental feature and can change any time. +""" +mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} + # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] + u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 + other_semi_index ::Int + other_orientation::Int + indices ::I + + function BoundaryConditionCoupled(other_semi_index, indices, uEltype) + NDIMS = length(indices) + u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) + + if indices[1] in (:begin, :end) + other_orientation = 1 + elseif indices[2] in (:begin, :end) + other_orientation = 2 + else + other_orientation = 3 + end + + new{NDIMS, NDIMS*2-1, uEltype, typeof(indices)}( + u_boundary, other_semi_index, other_orientation, indices) + end +end + +function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, + cell_indices, surface_node_indices, + surface_flux_function, equations) + # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), + # but we don't have a solver here + u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], + Val(nvariables(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, equations) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equations) + end + + return flux +end + # set sensible default values that may be overwritten by specific equations """ have_nonconservative_terms(equations) @@ -293,75 +366,6 @@ The inverse conversion is performed by [`cons2entropy`](@ref). """ function entropy2cons end -""" - BoundaryConditionCoupled(other_semi_index, indices, uEltype) -Boundary condition to glue two meshes together. Solution values at the boundary -of another mesh will be used as boundary values. This requires the use -of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`, -which is the index of the mesh in the tuple of semidiscretizations. -Note that the elements and nodes of the two meshes at the coupled boundary must coincide. -This is currently only implemented for [`StructuredMesh`](@ref). -# Arguments -- `other_semi_index`: the index in `SemidiscretizationCoupled` of the semidiscretization - from which the values are copied -- `indices::Tuple`: node/cell indices at the boundary of the mesh in the other - semidiscretization. See examples below. -- `uEltype::Type`: element type of solution -# Examples -'''julia -# Connect the left boundary of mesh 2 to our boundary such that our positive -# boundary direction will match the positive y direction of the other boundary -BoundaryConditionCoupled(2, (1, :i), Float64) -# Connect the same two boundaries oppositely oriented -BoundaryConditionCoupled(2, (1, :i_backwards), Float64) -# Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` -BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) -''' -!!! warning "Experimental code" - This is an experimental feature and can change any time. -""" -mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} - # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] - u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 - other_semi_index ::Int - other_orientation::Int - indices ::I - - function BoundaryConditionCoupled(other_semi_index, indices, uEltype) - NDIMS = length(indices) - u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) - - if indices[1] in (:begin, :end) - other_orientation = 1 - elseif indices[2] in (:begin, :end) - other_orientation = 2 - else - other_orientation = 3 - end - - new{NDIMS, NDIMS*2-1, uEltype, typeof(indices)}( - u_boundary, other_semi_index, other_orientation, indices) - end -end - -function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, - cell_indices, surface_node_indices, - surface_flux_function, equations) - # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), - # but we don't have a solver here - u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], - Val(nvariables(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, equations) - else # u_boundary is "left" of boundary, u_inner is "right" of boundary - flux = surface_flux_function(u_boundary, u_inner, orientation, equations) - end - - return flux -end - """ energy_total(u, equations) From f450cbd78bd0456b94f6451bdd982b614aa4cf17 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 15:46:42 +0200 Subject: [PATCH 53/83] Visualize setup --- .../elixir_advection_basic_coupled.jl | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 25515006660..bb615b456dd 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -11,6 +11,28 @@ equations = LinearScalarAdvectionEquation2D(advection_velocity) # Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +# Setup overview +# (-1, 1) ( 1, 1) +# ┌────────────────────┬────────────────────┐ +# │ ↑ periodic ↑ │ ↑ periodic ↑ │ +# │ │ │ +# │ │ │ +# │ ========= │ ========= │ +# │ system #1 │ system #2 │ +# │ ========= │ ========= │ +# │ │ │ +# │ │ │ +# │ │ │ +# │ │ │ +# │ coupled -->│<-- coupled │ +# │ │ │ +# │<-- coupled │ coupled -->│ +# │ │ │ +# │ │ │ +# │ ↓ periodic ↓ │ ↓ periodic ↓ │ +# └────────────────────┴────────────────────┘ +# (-1, -1) ( 1, -1) + # First mesh is the left half of a [-1,1]^2 square coordinates_min1 = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) coordinates_max1 = ( 0.0, 1.0) # maximum coordinates (max(x), max(y)) From beaa817aa5f668c5a130b31c821a3561b9bb83c4 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 2 Jun 2023 16:06:41 +0200 Subject: [PATCH 54/83] Formatting improvmenets --- src/equations/equations.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/equations/equations.jl b/src/equations/equations.jl index d70651724a8..5c0fdd1300b 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -209,6 +209,7 @@ Boundary condition to glue two meshes together. Solution values at the boundary of another mesh will be used as boundary values. This requires the use of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`, which is the index of the mesh in the tuple of semidiscretizations. + Note that the elements and nodes of the two meshes at the coupled boundary must coincide. This is currently only implemented for [`StructuredMesh`](@ref). @@ -224,8 +225,10 @@ This is currently only implemented for [`StructuredMesh`](@ref). # Connect the left boundary of mesh 2 to our boundary such that our positive # boundary direction will match the positive y direction of the other boundary BoundaryConditionCoupled(2, (1, :i), Float64) + # Connect the same two boundaries oppositely oriented BoundaryConditionCoupled(2, (1, :i_backwards), Float64) + # Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) ``` @@ -257,6 +260,7 @@ mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} end end + function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, cell_indices, surface_node_indices, surface_flux_function, equations) @@ -275,6 +279,7 @@ function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, di return flux end + # set sensible default values that may be overwritten by specific equations """ have_nonconservative_terms(equations) From a746fed419db942e6ea0cda41791777c28d2cf82 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 13:51:11 +0200 Subject: [PATCH 55/83] Change 1:nsystems(semi) to eachsystem(semi) --- src/callbacks_step/save_solution.jl | 4 ++-- src/callbacks_step/stepsize.jl | 2 +- src/semidiscretization/semidiscretization_coupled.jl | 10 ++++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 9b50d756a19..625c0720c87 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -158,7 +158,7 @@ end # Save mesh for a coupled semidiscretization, which contains multiple meshes internally function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) - for i in 1:nsystems(semi) + for i in eachsystem(semi) mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) if mesh.unsaved_changes @@ -233,7 +233,7 @@ end integrator) @unpack semis, u_indices = semi - for i in 1:nsystems(semi) + for i in eachsystem(semi) u_ode_slice = @view u_ode[u_indices[i]] save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) end diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 9059fa780ce..0c90ed1b71f 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -99,7 +99,7 @@ end function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) @unpack u_indices = semi - dt = minimum(1:nsystems(semi)) do i + dt = minimum(eachsystem(semi)) do i u_ode_slice = @view u_ode[u_indices[i]] calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) end diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 01c1219c460..07b3a02c848 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -61,7 +61,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) summary_header(io, "SemidiscretizationCoupled") summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) summary_line(io, "#systems", nsystems(semi)) - for i = 1:nsystems(semi) + for i in eachsystem(semi) summary_line(io, "system", i) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) @@ -80,7 +80,7 @@ end function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_context) show(io_context, MIME"text/plain"(), semi) println(io, "\n") - for i = 1:nsystems(semi) + for i in eachsystem(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) summary_header(io, "System #$i") @@ -108,6 +108,8 @@ end @inline nsystems(semi::SemidiscretizationCoupled) = length(semi.semis) +@inline eachsystem(semi::SemidiscretizationCoupled) = Base.OneTo(nsystems(semi)) + @inline Base.real(semi::SemidiscretizationCoupled) = promote_type(real.(semi.semis)...) @inline Base.eltype(semi::SemidiscretizationCoupled) = promote_type(eltype.(semi.semis)...) @@ -137,7 +139,7 @@ function compute_coefficients(t, semi::SemidiscretizationCoupled) u_ode = Vector{real(semi)}(undef, u_indices[end][end]) - for i in 1:nsystems(semi) + for i in eachsystem(semi) # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` u_ode[u_indices[i]] .= compute_coefficients(t, semi.semis[i]) end @@ -187,7 +189,7 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) end # Call rhs! for each semidiscretization - for i in 1:nsystems(semi) + for i in eachsystem(semi) u_loc = @view u_ode[u_indices[i]] du_loc = @view du_ode[u_indices[i]] From 81e8b4a0c50ced3650baf86b34c6d8cbd2a2649a Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 13:58:29 +0200 Subject: [PATCH 56/83] Remove redundant ndofs(...) function --- src/solvers/dgsem_structured/dg.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/solvers/dgsem_structured/dg.jl b/src/solvers/dgsem_structured/dg.jl index 952fd449f35..5be1af834da 100644 --- a/src/solvers/dgsem_structured/dg.jl +++ b/src/solvers/dgsem_structured/dg.jl @@ -111,9 +111,6 @@ function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) end -@inline ndofs(mesh::StructuredMesh, dg::DG, cache) = nelements(cache.elements) * nnodes(dg)^ndims(mesh) - - include("containers.jl") include("dg_1d.jl") include("dg_2d.jl") From 71e7889317345b06ff5168ec05431d5668262ce3 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 14:13:45 +0200 Subject: [PATCH 57/83] copy_to_coupled_boundary --> copy_to_coupled_boundary! --- src/semidiscretization/semidiscretization_coupled.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 07b3a02c848..f0b7290e394 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -184,7 +184,7 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) @trixi_timeit timer() "copy to coupled boundaries" begin for semi_ in semi.semis - copy_to_coupled_boundary(semi_.boundary_conditions, u_ode, semi) + copy_to_coupled_boundary!(semi_.boundary_conditions, u_ode, semi) end end @@ -204,16 +204,16 @@ end # Don't do anything for other BCs than BoundaryConditionCoupled -function copy_to_coupled_boundary(boundary_condition, u_ode, semi) end +function copy_to_coupled_boundary!(boundary_condition, u_ode, semi) end -function copy_to_coupled_boundary(boundary_conditions::Union{Tuple, NamedTuple}, u_ode, semi) +function copy_to_coupled_boundary!(boundary_conditions::Union{Tuple, NamedTuple}, u_ode, semi) for boundary_condition in boundary_conditions - copy_to_coupled_boundary(boundary_condition, u_ode, semi) + copy_to_coupled_boundary!(boundary_condition, u_ode, semi) end end # In 2D -function copy_to_coupled_boundary(boundary_condition::BoundaryConditionCoupled{2}, u_ode, semi) +function copy_to_coupled_boundary!(boundary_condition::BoundaryConditionCoupled{2}, u_ode, semi) @unpack u_indices = semi @unpack other_semi_index, other_orientation, indices = boundary_condition From a556aa6341658280561e3114d0bf648c64007b0c Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 14:31:07 +0200 Subject: [PATCH 58/83] Move all SemidiscretizationCoupled-specific code to semi/semi_coupled.jl --- src/callbacks_step/save_solution.jl | 26 -- src/callbacks_step/stepsize.jl | 13 - src/equations/equations.jl | 77 ------ .../semidiscretization_coupled.jl | 230 ++++++++++++++++-- src/solvers/dgsem_structured/dg.jl | 46 ---- 5 files changed, 203 insertions(+), 189 deletions(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 625c0720c87..eb17a80dce2 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -156,19 +156,6 @@ function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep= end -# Save mesh for a coupled semidiscretization, which contains multiple meshes internally -function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) - for i in eachsystem(semi) - mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) - - if mesh.unsaved_changes - mesh.current_filename = save_mesh_file(mesh, output_directory, system=i) - mesh.unsaved_changes = false - end - end -end - - # this method is called to determine whether the callback should be activated function (solution_callback::SaveSolutionCallback)(u, t, integrator) @unpack interval_or_dt, save_final_solution = solution_callback @@ -227,19 +214,6 @@ end end - - -@inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode, solution_callback, - integrator) - @unpack semis, u_indices = semi - - for i in eachsystem(semi) - u_ode_slice = @view u_ode[u_indices[i]] - save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) - end -end - - @inline function save_solution_file(u_ode, t, dt, iter, semi::AbstractSemidiscretization, solution_callback, element_variables=Dict{Symbol,Any}(); integrator=nothing, system="") diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index 0c90ed1b71f..2e8f5a9af16 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -95,19 +95,6 @@ function calculate_dt(u_ode, t, cfl_number, semi::AbstractSemidiscretization) end -# In case of coupled system, use minimum timestep over all systems -function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) - @unpack u_indices = semi - - dt = minimum(eachsystem(semi)) do i - u_ode_slice = @view u_ode[u_indices[i]] - calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) - end - - return dt -end - - # Time integration methods from the DiffEq ecosystem without adaptive time stepping on their own # such as `CarpenterKennedy2N54` require passing `dt=...` in `solve(ode, ...)`. Since we don't have # an integrator at this stage but only the ODE, this method will be used there. It's called in diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 5c0fdd1300b..345830a8e60 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -202,83 +202,6 @@ struct BoundaryConditionNeumann{B} boundary_normal_flux_function::B end -""" - BoundaryConditionCoupled(other_semi_index, indices, uEltype) - -Boundary condition to glue two meshes together. Solution values at the boundary -of another mesh will be used as boundary values. This requires the use -of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`, -which is the index of the mesh in the tuple of semidiscretizations. - -Note that the elements and nodes of the two meshes at the coupled boundary must coincide. -This is currently only implemented for [`StructuredMesh`](@ref). - -# Arguments -- `other_semi_index`: the index in `SemidiscretizationCoupled` of the semidiscretization - from which the values are copied -- `indices::Tuple`: node/cell indices at the boundary of the mesh in the other - semidiscretization. See examples below. -- `uEltype::Type`: element type of solution - -# Examples -```julia -# Connect the left boundary of mesh 2 to our boundary such that our positive -# boundary direction will match the positive y direction of the other boundary -BoundaryConditionCoupled(2, (1, :i), Float64) - -# Connect the same two boundaries oppositely oriented -BoundaryConditionCoupled(2, (1, :i_backwards), Float64) - -# Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` -BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) -``` - -!!! warning "Experimental code" - This is an experimental feature and can change any time. -""" -mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} - # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] - u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 - other_semi_index ::Int - other_orientation::Int - indices ::I - - function BoundaryConditionCoupled(other_semi_index, indices, uEltype) - NDIMS = length(indices) - u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) - - if indices[1] in (:begin, :end) - other_orientation = 1 - elseif indices[2] in (:begin, :end) - other_orientation = 2 - else - other_orientation = 3 - end - - new{NDIMS, NDIMS*2-1, uEltype, typeof(indices)}( - u_boundary, other_semi_index, other_orientation, indices) - end -end - - -function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, - cell_indices, surface_node_indices, - surface_flux_function, equations) - # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), - # but we don't have a solver here - u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], - Val(nvariables(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, equations) - else # u_boundary is "left" of boundary, u_inner is "right" of boundary - flux = surface_flux_function(u_boundary, u_inner, orientation, equations) - end - - return flux -end - # set sensible default values that may be overwritten by specific equations """ diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index f0b7290e394..f8e372a01e5 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -148,6 +148,159 @@ function compute_coefficients(t, semi::SemidiscretizationCoupled) end +function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) + @unpack u_indices = semi + + time_start = time_ns() + + @trixi_timeit timer() "copy to coupled boundaries" begin + for semi_ in semi.semis + copy_to_coupled_boundary!(semi_.boundary_conditions, u_ode, semi) + end + end + + # Call rhs! for each semidiscretization + for i in eachsystem(semi) + u_loc = @view u_ode[u_indices[i]] + du_loc = @view du_ode[u_indices[i]] + + rhs!(du_loc, u_loc, semi.semis[i], t) + end + + runtime = time_ns() - time_start + put!(semi.performance_counter, runtime) + + return nothing +end + + +################################################################################ +### SaveSolutionCallback +################################################################################ + +# Save mesh for a coupled semidiscretization, which contains multiple meshes internally +function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) + for i in eachsystem(semi) + mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) + + if mesh.unsaved_changes + mesh.current_filename = save_mesh_file(mesh, output_directory, system=i) + mesh.unsaved_changes = false + end + end +end + + +@inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode, solution_callback, + integrator) + @unpack semis, u_indices = semi + + for i in eachsystem(semi) + u_ode_slice = @view u_ode[u_indices[i]] + save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) + end +end + + +################################################################################ +### StepsizeCallback +################################################################################ + +# In case of coupled system, use minimum timestep over all systems +function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) + @unpack u_indices = semi + + dt = minimum(eachsystem(semi)) do i + u_ode_slice = @view u_ode[u_indices[i]] + calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) + end + + return dt +end + + +################################################################################ +### Equations +################################################################################ + +""" + BoundaryConditionCoupled(other_semi_index, indices, uEltype) + +Boundary condition to glue two meshes together. Solution values at the boundary +of another mesh will be used as boundary values. This requires the use +of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`, +which is the index of the mesh in the tuple of semidiscretizations. + +Note that the elements and nodes of the two meshes at the coupled boundary must coincide. +This is currently only implemented for [`StructuredMesh`](@ref). + +# Arguments +- `other_semi_index`: the index in `SemidiscretizationCoupled` of the semidiscretization + from which the values are copied +- `indices::Tuple`: node/cell indices at the boundary of the mesh in the other + semidiscretization. See examples below. +- `uEltype::Type`: element type of solution + +# Examples +```julia +# Connect the left boundary of mesh 2 to our boundary such that our positive +# boundary direction will match the positive y direction of the other boundary +BoundaryConditionCoupled(2, (1, :i), Float64) + +# Connect the same two boundaries oppositely oriented +BoundaryConditionCoupled(2, (1, :i_backwards), Float64) + +# Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` +BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) +``` + +!!! warning "Experimental code" + This is an experimental feature and can change any time. +""" +mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} + # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] + u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 + other_semi_index ::Int + other_orientation::Int + indices ::I + + function BoundaryConditionCoupled(other_semi_index, indices, uEltype) + NDIMS = length(indices) + u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) + + if indices[1] in (:begin, :end) + other_orientation = 1 + elseif indices[2] in (:begin, :end) + other_orientation = 2 + else + other_orientation = 3 + end + + new{NDIMS, NDIMS*2-1, uEltype, typeof(indices)}( + u_boundary, other_semi_index, other_orientation, indices) + end +end + + +function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, + cell_indices, surface_node_indices, + surface_flux_function, equations) + # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), + # but we don't have a solver here + u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], + Val(nvariables(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, equations) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equations) + end + + return flux +end + + function allocate_coupled_boundary_conditions(semi::AbstractSemidiscretization) n_boundaries = 2 * ndims(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi) @@ -159,6 +312,7 @@ function allocate_coupled_boundary_conditions(semi::AbstractSemidiscretization) end end + # Don't do anything for other BCs than BoundaryConditionCoupled function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, solver) end @@ -176,33 +330,6 @@ function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditi cell_size) end - -function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) - @unpack u_indices = semi - - time_start = time_ns() - - @trixi_timeit timer() "copy to coupled boundaries" begin - for semi_ in semi.semis - copy_to_coupled_boundary!(semi_.boundary_conditions, u_ode, semi) - end - end - - # Call rhs! for each semidiscretization - for i in eachsystem(semi) - u_loc = @view u_ode[u_indices[i]] - du_loc = @view du_ode[u_indices[i]] - - rhs!(du_loc, u_loc, semi.semis[i], t) - end - - runtime = time_ns() - time_start - put!(semi.performance_counter, runtime) - - return nothing -end - - # Don't do anything for other BCs than BoundaryConditionCoupled function copy_to_coupled_boundary!(boundary_condition, u_ode, semi) end @@ -257,3 +384,52 @@ function copy_to_coupled_boundary!(boundary_condition::BoundaryConditionCoupled{ end end + +################################################################################ +### DGSEM/structured +################################################################################ + +@inline function calc_boundary_flux_by_direction!(surface_flux_values, u, t, orientation, + boundary_condition::BoundaryConditionCoupled, + mesh::StructuredMesh, equations, + surface_integral, dg::DG, cache, + direction, node_indices, surface_node_indices, element) + @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements + @unpack surface_flux = surface_integral + + cell_indices = get_boundary_indices(element, orientation, mesh) + + u_inner = get_node_vars(u, equations, dg, node_indices..., element) + + # If the mapping is orientation-reversing, the contravariant vectors' orientation + # is reversed as well. The normal vector must be oriented in the direction + # from `left_element` to `right_element`, or the numerical flux will be computed + # incorrectly (downwind direction). + sign_jacobian = sign(inverse_jacobian[node_indices..., element]) + + # Contravariant vector Ja^i is the normal vector + normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, + node_indices..., element) + + # If the mapping is orientation-reversing, the normal vector will be reversed (see above). + # However, the flux now has the wrong sign, since we need the physical flux in normal direction. + flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, + surface_node_indices, surface_flux, equations) + + for v in eachvariable(equations) + surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] + end +end + +function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) + cartesian_indices = CartesianIndices(size(mesh)) + if orientation == 1 + # Get index of element in y-direction + cell_indices = (cartesian_indices[element][2],) + else # orientation == 2 + # Get index of element in x-direction + cell_indices = (cartesian_indices[element][1],) + end + + return cell_indices +end diff --git a/src/solvers/dgsem_structured/dg.jl b/src/solvers/dgsem_structured/dg.jl index 5be1af834da..c4ba534b496 100644 --- a/src/solvers/dgsem_structured/dg.jl +++ b/src/solvers/dgsem_structured/dg.jl @@ -65,52 +65,6 @@ end end -@inline function calc_boundary_flux_by_direction!(surface_flux_values, u, t, orientation, - boundary_condition::BoundaryConditionCoupled, - mesh::StructuredMesh, equations, - surface_integral, dg::DG, cache, - direction, node_indices, surface_node_indices, element) - @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements - @unpack surface_flux = surface_integral - - cell_indices = get_boundary_indices(element, orientation, mesh) - - u_inner = get_node_vars(u, equations, dg, node_indices..., element) - - # If the mapping is orientation-reversing, the contravariant vectors' orientation - # is reversed as well. The normal vector must be oriented in the direction - # from `left_element` to `right_element`, or the numerical flux will be computed - # incorrectly (downwind direction). - sign_jacobian = sign(inverse_jacobian[node_indices..., element]) - - # Contravariant vector Ja^i is the normal vector - normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, - node_indices..., element) - - # If the mapping is orientation-reversing, the normal vector will be reversed (see above). - # However, the flux now has the wrong sign, since we need the physical flux in normal direction. - flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, - surface_node_indices, surface_flux, equations) - - for v in eachvariable(equations) - surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] - end -end - -function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) - cartesian_indices = CartesianIndices(size(mesh)) - if orientation == 1 - # Get index of element in y-direction - cell_indices = (cartesian_indices[element][2],) - else # orientation == 2 - # Get index of element in x-direction - cell_indices = (cartesian_indices[element][1],) - end - - return cell_indices -end - - include("containers.jl") include("dg_1d.jl") include("dg_2d.jl") From fdd3291bf2505d5bcd5144f86f7cdd2b19fc379a Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 14:46:30 +0200 Subject: [PATCH 59/83] Use uEltype for BCCoupled --- src/semidiscretization/semidiscretization_coupled.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index f8e372a01e5..8f958089a5a 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -281,6 +281,7 @@ mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} end end +Base.eltype(boundary_condition::BoundaryConditionCoupled) = eltype(boundary_condition.u_boundary) function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, cell_indices, surface_node_indices, @@ -326,7 +327,8 @@ function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditi cell_size = size(mesh, 1) end - boundary_condition.u_boundary = Array{Float64, 3}(undef, nvariables(equations), nnodes(dg), + uEltype = eltype(boundary_condition) + boundary_condition.u_boundary = Array{uEltype, 3}(undef, nvariables(equations), nnodes(dg), cell_size) end From 76df9c484c5086ab50f25a6661ed1e411f49a546 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 14:48:53 +0200 Subject: [PATCH 60/83] Add comment --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 8f958089a5a..a3013e3a7f3 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -272,7 +272,7 @@ mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} other_orientation = 1 elseif indices[2] in (:begin, :end) other_orientation = 2 - else + else # indices[3] in (:begin, :end) other_orientation = 3 end From b3761dc9bef1397f3f90a9a0d653d7f8b9507aec Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 14:50:43 +0200 Subject: [PATCH 61/83] I --> Indices --- src/semidiscretization/semidiscretization_coupled.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index a3013e3a7f3..ef33fec5e72 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -9,9 +9,9 @@ The semidiscretizations can be coupled by gluing meshes together using `Boundary !!! warning "Experimental code" This is an experimental feature and can change any time. """ -struct SemidiscretizationCoupled{S, I, EquationList} <: AbstractSemidiscretization +struct SemidiscretizationCoupled{S, Indices, EquationList} <: AbstractSemidiscretization semis::S - u_indices::I # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] + u_indices::Indices # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] performance_counter::PerformanceCounter end @@ -257,12 +257,12 @@ BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) !!! warning "Experimental code" This is an experimental feature and can change any time. """ -mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, I} +mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, Indices} # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 other_semi_index ::Int other_orientation::Int - indices ::I + indices ::Indices function BoundaryConditionCoupled(other_semi_index, indices, uEltype) NDIMS = length(indices) From cd68d0a081824887e49e6302d01d76dd9b807644 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 14:51:42 +0200 Subject: [PATCH 62/83] Add comment --- src/semidiscretization/semidiscretization_coupled.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index ef33fec5e72..ed4fc14c629 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -258,6 +258,7 @@ BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) This is an experimental feature and can change any time. """ mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, Indices} + # NDIMST2M1 == NDIMS * 2 - 1 # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 other_semi_index ::Int From 5b9b4ff2d040d99a9a63c0e4390d5ef874728bdd Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:05:58 +0200 Subject: [PATCH 63/83] Remove Base.summary for SemiCoupled since it appears to be unused --- src/semidiscretization/semidiscretization_coupled.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index ed4fc14c629..39a07ed5818 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -98,11 +98,6 @@ function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_cont end end -function Base.summary(semi::SemidiscretizationCoupled) - _, _, solver, _ = mesh_equations_solver_cache(semi.semis[1]) - summary(solver) -end - @inline Base.ndims(semi::SemidiscretizationCoupled) = ndims(semi.semis[1]) From 26f7bb6aaafb3b129316c9fcb17c2a8fe19a40f2 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:08:15 +0200 Subject: [PATCH 64/83] Add parens --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 39a07ed5818..740b4aa6d4b 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -34,7 +34,7 @@ function SemidiscretizationCoupled(semis) # Compute range of coefficients associated with each semidiscretization and allocate coupled BCs u_indices = Vector{UnitRange{Int}}(undef, length(semis)) for i in 1:length(semis) - offset = sum(n_coefficients[1:i-1]) + 1 + offset = sum(n_coefficients[1:(i-1)]) + 1 u_indices[i] = range(offset, length=n_coefficients[i]) allocate_coupled_boundary_conditions(semis[i]) From 27cb8aa799f3a5d82f821bb7ab731642b59f126b Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:09:36 +0200 Subject: [PATCH 65/83] Int64 -> Int --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 740b4aa6d4b..226eb6d9a27 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -25,7 +25,7 @@ function SemidiscretizationCoupled(semis) @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" # Number of coefficients for each semidiscretization - n_coefficients = zeros(Int64, length(semis)) + n_coefficients = zeros(Int, length(semis)) for i in 1:length(semis) _, equations, _, _ = mesh_equations_solver_cache(semis[i]) n_coefficients[i] = ndofs(semis[i]) * nvariables(equations) From e70fe1438c407d8814f1639fcaef0c391dd5c2e8 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:19:01 +0200 Subject: [PATCH 66/83] Add xref for Documenter.jl --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 226eb6d9a27..49b9f06b720 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -4,7 +4,7 @@ A struct used to bundle multiple semidiscretizations. `semidiscretize` will return an `ODEproblem` that synchronizes time steps between the semidiscretizations. Each call of `rhs!` will call `rhs!` for each semidiscretization individually. -The semidiscretizations can be coupled by gluing meshes together using `BoundaryConditionCoupled`. +The semidiscretizations can be coupled by gluing meshes together using [`BoundaryConditionCoupled`](@ref). !!! warning "Experimental code" This is an experimental feature and can change any time. From a00909c2dcb52bea5959c10b854e4d92c0ee93f7 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:21:10 +0200 Subject: [PATCH 67/83] Fixup comment --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 49b9f06b720..e195a1a8eeb 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -2,7 +2,7 @@ SemidiscretizationCoupled A struct used to bundle multiple semidiscretizations. -`semidiscretize` will return an `ODEproblem` that synchronizes time steps between the semidiscretizations. +[`semidiscretize`](@ref) will return an `ODEProblem` that synchronizes time steps between the semidiscretizations. Each call of `rhs!` will call `rhs!` for each semidiscretization individually. The semidiscretizations can be coupled by gluing meshes together using [`BoundaryConditionCoupled`](@ref). From fd98db479fa3f60570716e35c26f4f8b4d3da327 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:23:29 +0200 Subject: [PATCH 68/83] Remove unused `total_volume` --- src/solvers/dgsem_structured/dg_2d.jl | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl index 8db80436ce9..a8972dfe766 100644 --- a/src/solvers/dgsem_structured/dg_2d.jl +++ b/src/solvers/dgsem_structured/dg_2d.jl @@ -561,22 +561,4 @@ function apply_jacobian!(du, end -# Compute total volume of a given mesh -function total_volume(mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}}, dg, cache) - @unpack weights = dg.basis - - total_volume = zero(real(mesh)) - - # Use quadrature to numerically integrate over entire domain - for element in eachelement(dg, cache) - for j in eachnode(dg), i in eachnode(dg) - volume_jacobian = abs(inv(cache.elements.inverse_jacobian[i, j, element])) - total_volume += volume_jacobian * weights[i] * weights[j] - end - end - - return total_volume -end - - end # @muladd From a8a21c0031dfa4894d37ebe10f6f782c2afccd1a Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:32:19 +0200 Subject: [PATCH 69/83] Remove obsolete comment --- src/meshes/mesh_io.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index 1f1fd04c34e..4e90c8c97e7 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -99,7 +99,6 @@ function save_mesh_file(mesh::StructuredMesh, output_directory; system="") # Create output directory (if it does not exist) mkpath(output_directory) - # filename = joinpath(output_directory, "mesh.h5") if isempty(system) filename = joinpath(output_directory, "mesh.h5") else From ebdd060cf4ae13d088d538f3d840449626c7df37 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:34:50 +0200 Subject: [PATCH 70/83] summary_semi --> print_summary_semi for clarity --- src/callbacks_step/summary.jl | 4 ++-- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/callbacks_step/summary.jl b/src/callbacks_step/summary.jl index 318d9f86809..bd4b269bb84 100644 --- a/src/callbacks_step/summary.jl +++ b/src/callbacks_step/summary.jl @@ -149,7 +149,7 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) :indentation_level => 0) semi = integrator.p - summary_semidiscretization(semi, io, io_context) + print_summary_semidiscretization(semi, io, io_context) callbacks = integrator.opts.callback if callbacks isa CallbackSet @@ -204,7 +204,7 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) end -function summary_semidiscretization(semi, io, io_context) +function print_summary_semidiscretization(semi, io, io_context) show(io_context, MIME"text/plain"(), semi) println(io, "\n") mesh, equations, solver, _ = mesh_equations_solver_cache(semi) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index e195a1a8eeb..487b023351a 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -77,7 +77,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) end -function summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_context) +function print_summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_context) show(io_context, MIME"text/plain"(), semi) println(io, "\n") for i in eachsystem(semi) From 7d30df685f67850769950b760422f4bb162694fb Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:39:47 +0200 Subject: [PATCH 71/83] Make SemiCoupled ctor more convenient --- .../structured_2d_dgsem/elixir_advection_basic_coupled.jl | 2 +- src/semidiscretization/semidiscretization_coupled.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index bb615b456dd..351763ca123 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -70,7 +70,7 @@ semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition_converg y_pos=boundary_condition_periodic)) # Create a semidiscretization that bundles semi1 and semi2 -semi = SemidiscretizationCoupled((semi1, semi2)) +semi = SemidiscretizationCoupled(semi1, semi2) ############################################################################### # ODE solvers, callbacks etc. diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 487b023351a..9b727a38b7e 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -17,11 +17,11 @@ end """ - SemidiscretizationCoupled(semis) + SemidiscretizationCoupled(semis...) -Create a coupled semidiscretization that consists of the semidiscretizations contained in the tuple `semis`. +Create a coupled semidiscretization that consists of the semidiscretizations passed as arguments. """ -function SemidiscretizationCoupled(semis) +function SemidiscretizationCoupled(semis...) @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" # Number of coefficients for each semidiscretization From b1239d4d60556d4347bc94f92f57612cb6daa8b4 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:41:53 +0200 Subject: [PATCH 72/83] Fix docstring --- src/semidiscretization/semidiscretization_coupled.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 9b727a38b7e..e25291a90a1 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -240,10 +240,10 @@ This is currently only implemented for [`StructuredMesh`](@ref). ```julia # Connect the left boundary of mesh 2 to our boundary such that our positive # boundary direction will match the positive y direction of the other boundary -BoundaryConditionCoupled(2, (1, :i), Float64) +BoundaryConditionCoupled(2, (:begin, :i), Float64) # Connect the same two boundaries oppositely oriented -BoundaryConditionCoupled(2, (1, :i_backwards), Float64) +BoundaryConditionCoupled(2, (:begin, :i_backwards), Float64) # Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]` BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) From 653d855f255a3ec7bd8b7a137db03204aa750dad Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:51:17 +0200 Subject: [PATCH 73/83] Add description to elixir --- .../elixir_advection_basic_coupled.jl | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl index 351763ca123..343da476d40 100644 --- a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl @@ -3,15 +3,13 @@ using Trixi ############################################################################### -# semidiscretization of the linear advection equation - -advection_velocity = (0.2, -0.7) -equations = LinearScalarAdvectionEquation2D(advection_velocity) - -# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) - -# Setup overview +# Coupled semidiscretization of two linear advection systems, which are connected periodically +# +# In this elixir, we have a square domain that is divided into a left half and a right half. On each +# half of the domain, a completely independent SemidiscretizationHyperbolic is created for the +# linear advection equations. The two systems are coupled in the x-direction and have periodic +# boundaries in the y-direction. For a high-level overview, see also the figure below: +# # (-1, 1) ( 1, 1) # ┌────────────────────┬────────────────────┐ # │ ↑ periodic ↑ │ ↑ periodic ↑ │ @@ -33,6 +31,12 @@ solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) # └────────────────────┴────────────────────┘ # (-1, -1) ( 1, -1) +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + # First mesh is the left half of a [-1,1]^2 square coordinates_min1 = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) coordinates_max1 = ( 0.0, 1.0) # maximum coordinates (max(x), max(y)) From 9204d53132a66e7d9dc1ab4232dd19a38169fa56 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 9 Jun 2023 15:52:38 +0200 Subject: [PATCH 74/83] Rename elixir --- ...xir_advection_basic_coupled.jl => elixir_advection_coupled.jl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/structured_2d_dgsem/{elixir_advection_basic_coupled.jl => elixir_advection_coupled.jl} (100%) diff --git a/examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_coupled.jl similarity index 100% rename from examples/structured_2d_dgsem/elixir_advection_basic_coupled.jl rename to examples/structured_2d_dgsem/elixir_advection_coupled.jl From 06883f43f977471d631cd50322e30e60361a271f Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 10 Jun 2023 06:09:51 +0200 Subject: [PATCH 75/83] Remove unused kwarg --- src/callbacks_step/save_solution.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index eb17a80dce2..d41848ed7a2 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -216,7 +216,7 @@ end @inline function save_solution_file(u_ode, t, dt, iter, semi::AbstractSemidiscretization, solution_callback, - element_variables=Dict{Symbol,Any}(); integrator=nothing, system="") + element_variables=Dict{Symbol,Any}(); system="") mesh, equations, solver, cache = mesh_equations_solver_cache(semi) u = wrap_array_native(u_ode, mesh, equations, solver, cache) save_solution_file(u, t, dt, iter, mesh, equations, solver, cache, solution_callback, From bd10bf8bce09bdfe5fc3d04b3369d331d287d6fa Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 10 Jun 2023 06:15:26 +0200 Subject: [PATCH 76/83] Fix argument order and simplify interface for IO functions --- src/callbacks_step/summary.jl | 12 ++++++------ .../semidiscretization_coupled.jl | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/callbacks_step/summary.jl b/src/callbacks_step/summary.jl index bd4b269bb84..d41a26cbdb4 100644 --- a/src/callbacks_step/summary.jl +++ b/src/callbacks_step/summary.jl @@ -149,7 +149,7 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) :indentation_level => 0) semi = integrator.p - print_summary_semidiscretization(semi, io, io_context) + print_summary_semidiscretization(io_context, semi) callbacks = integrator.opts.callback if callbacks isa CallbackSet @@ -204,15 +204,15 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) end -function print_summary_semidiscretization(semi, io, io_context) - show(io_context, MIME"text/plain"(), semi) +function print_summary_semidiscretization(io::IO, semi::AbstractSemidiscretization) + show(io, MIME"text/plain"(), semi) println(io, "\n") mesh, equations, solver, _ = mesh_equations_solver_cache(semi) - show(io_context, MIME"text/plain"(), mesh) + show(io, MIME"text/plain"(), mesh) println(io, "\n") - show(io_context, MIME"text/plain"(), equations) + show(io, MIME"text/plain"(), equations) println(io, "\n") - show(io_context, MIME"text/plain"(), solver) + show(io, MIME"text/plain"(), solver) println(io, "\n") end diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index e25291a90a1..9c6c27ce145 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -77,21 +77,21 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) end -function print_summary_semidiscretization(semi::SemidiscretizationCoupled, io, io_context) - show(io_context, MIME"text/plain"(), semi) +function print_summary_semidiscretization(io::IO, semi::SemidiscretizationCoupled) + show(io, MIME"text/plain"(), semi) println(io, "\n") for i in eachsystem(semi) mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) summary_header(io, "System #$i") - summary_line(io_context, "mesh", mesh |> typeof |> nameof) - show(increment_indent(io_context), MIME"text/plain"(), mesh) + summary_line(io, "mesh", mesh |> typeof |> nameof) + show(increment_indent(io), MIME"text/plain"(), mesh) - summary_line(io_context, "equations", equations |> typeof |> nameof) - show(increment_indent(io_context), MIME"text/plain"(), equations) + summary_line(io, "equations", equations |> typeof |> nameof) + show(increment_indent(io), MIME"text/plain"(), equations) - summary_line(io_context, "solver", solver |> typeof |> nameof) - show(increment_indent(io_context), MIME"text/plain"(), solver) + summary_line(io, "solver", solver |> typeof |> nameof) + show(increment_indent(io), MIME"text/plain"(), solver) summary_footer(io) println(io, "\n") From 476d1b30eff86ceea6737e1add7618fe122212a8 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 10 Jun 2023 06:17:13 +0200 Subject: [PATCH 77/83] Explicitly return nothing in functions that should do - nothing --- src/semidiscretization/semidiscretization_coupled.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 9c6c27ce145..b400aec54b6 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -312,6 +312,7 @@ end # Don't do anything for other BCs than BoundaryConditionCoupled function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, solver) + return nothing end # In 2D @@ -329,7 +330,9 @@ function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditi end # Don't do anything for other BCs than BoundaryConditionCoupled -function copy_to_coupled_boundary!(boundary_condition, u_ode, semi) end +function copy_to_coupled_boundary!(boundary_condition, u_ode, semi) + return nothing +end function copy_to_coupled_boundary!(boundary_conditions::Union{Tuple, NamedTuple}, u_ode, semi) for boundary_condition in boundary_conditions From ddf13e3ef6cedaf99b9bc1571e3156460d039046 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 10 Jun 2023 06:18:31 +0200 Subject: [PATCH 78/83] Update comment --- src/semidiscretization/semidiscretization_coupled.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index b400aec54b6..a31d378875c 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -67,7 +67,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) summary_line(increment_indent(io), "equations", equations |> typeof |> nameof) summary_line(increment_indent(io), "initial condition", semi.semis[i].initial_condition) - # TODO boundary conditions? That will be 36 BCs for a cubed sphere + # no boundary conditions since that could be too much summary_line(increment_indent(io), "source terms", semi.semis[i].source_terms) summary_line(increment_indent(io), "solver", solver |> typeof |> nameof) end From 87e804e1bd4b7d2c5f9a7bd4ba1dff113b8e1f2c Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 10 Jun 2023 22:09:45 +0200 Subject: [PATCH 79/83] Add AnalysisCallback to coupled semidiscretization (#1505) * Add AnalysisCallback to coupled semidiscretization * First non-crashing version of the AnalysisCallbackCoupled * Added comment to offending line in the analysis callback. * Fix stupid bug * Rename variable for easier testing * Clean up code * Remove type instability * Prettify output * Add test * Enable `convergence_test` for SemidiscretizationCoupled * Increased the frequency of the solution write out for a more usable animation. * Reverted analysis intervals. * Updated the l2 and linf errors for the elixir_advection_basic_coupled.jl test to reflect the increased simulation time. * Corrected bracket typo in structured_2d test. * Renamed plural variable names to lists. * Further renaiming plural variable names. * Added convergence_test for elixir_advection_basic_coupled. * Fix coverage for convergence_test * Add test for analysis_callback(sol) * Split timers between systems * fully specialize on rhs and parameters when constructing an ODEProblem * switch example back to OrdinaryDiffEq.jl * Reverted coupled example to use Trixi.solve instead of OrdinaryDiffEq solve. This should fix issues with LoadError: Failed to precompile OrdinaryDiffEq in the thread_legacy test. * Changed Julia version in project toml to 1.9 to fix OrdinaryDiffEq issues on the github test. * Change 1:nsystems(semi) to eachsystem(semi) * Use `get_system_u_ode` * Move all SemidiscretizationCoupled-specific code to semi/semi_coupled.jl * Changed file name name of elixir_advection_basic_coupled.jl to elixir_advection_coupled.jl. * Reverted Julia version to 1.8 in Project toml file. * Apply suggestions from code review * -use type SciMLBase.FullSpecialize instead of instance * Use get_system_u_ode instead of manual view * Reorder elixir ingredients * Make comment reflect code again * Use solve from OrdinaryDiffEq * Use more precise type for array * Test EOCs for each system separately * Allow test to run for the full duration --------- Co-authored-by: SimonCan Co-authored-by: Hendrik Ranocha --- .../elixir_advection_coupled.jl | 18 +- src/Trixi.jl | 2 +- src/auxiliary/special_elixirs.jl | 15 +- src/callbacks_step/analysis.jl | 33 ++- src/semidiscretization/semidiscretization.jl | 6 +- .../semidiscretization_coupled.jl | 197 +++++++++++++++++- test/test_special_elixirs.jl | 7 + test/test_structured_2d.jl | 13 ++ 8 files changed, 264 insertions(+), 27 deletions(-) diff --git a/examples/structured_2d_dgsem/elixir_advection_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_coupled.jl index 343da476d40..1e54e411db6 100644 --- a/examples/structured_2d_dgsem/elixir_advection_coupled.jl +++ b/examples/structured_2d_dgsem/elixir_advection_coupled.jl @@ -41,7 +41,10 @@ solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) coordinates_min1 = (-1.0, -1.0) # minimum coordinates (min(x), min(y)) coordinates_max1 = ( 0.0, 1.0) # maximum coordinates (max(x), max(y)) -cells_per_dimension1 = (8, 16) +# Define identical resolution as a variable such that it is easier to change from `trixi_include` +cells_per_dimension = (8, 16) + +cells_per_dimension1 = cells_per_dimension mesh1 = StructuredMesh(cells_per_dimension1, coordinates_min1, coordinates_max1) @@ -60,7 +63,7 @@ semi1 = SemidiscretizationHyperbolic(mesh1, equations, initial_condition_converg coordinates_min2 = (0.0, -1.0) # minimum coordinates (min(x), min(y)) coordinates_max2 = (1.0, 1.0) # maximum coordinates (max(x), max(y)) -cells_per_dimension2 = (8, 16) +cells_per_dimension2 = cells_per_dimension mesh2 = StructuredMesh(cells_per_dimension2, coordinates_min2, coordinates_max2) @@ -79,13 +82,18 @@ semi = SemidiscretizationCoupled(semi1, semi2) ############################################################################### # ODE solvers, callbacks etc. -# Create ODE problem with time span from 0.0 to 1.0 -ode = semidiscretize(semi, (0.0, 1.0)); +# Create ODE problem with time span from 0.0 to 2.0 +ode = semidiscretize(semi, (0.0, 2.0)); # At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup # and resets the timers summary_callback = SummaryCallback() +# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results +analysis_callback1 = AnalysisCallback(semi1, interval=100) +analysis_callback2 = AnalysisCallback(semi2, interval=100) +analysis_callback = AnalysisCallbackCoupled(semi, analysis_callback1, analysis_callback2) + # The SaveSolutionCallback allows to save the solution to a file in regular intervals save_solution = SaveSolutionCallback(interval=100, solution_variables=cons2prim) @@ -94,7 +102,7 @@ save_solution = SaveSolutionCallback(interval=100, stepsize_callback = StepsizeCallback(cfl=1.6) # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver -callbacks = CallbackSet(summary_callback, stepsize_callback, save_solution) +callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback) ############################################################################### diff --git a/src/Trixi.jl b/src/Trixi.jl index b34334f73bd..7158a133978 100644 --- a/src/Trixi.jl +++ b/src/Trixi.jl @@ -227,7 +227,7 @@ export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback, AveragingCallback, AMRCallback, StepsizeCallback, GlmSpeedCallback, LBMCollisionCallback, EulerAcousticsCouplingCallback, - TrivialCallback + TrivialCallback, AnalysisCallbackCoupled export load_mesh, load_time diff --git a/src/auxiliary/special_elixirs.jl b/src/auxiliary/special_elixirs.jl index 0724c62bcba..aae4290d8a1 100644 --- a/src/auxiliary/special_elixirs.jl +++ b/src/auxiliary/special_elixirs.jl @@ -83,9 +83,20 @@ function convergence_test(mod::Module, elixir::AbstractString, iterations; kwarg println("#"^100) end - # number of variables - _, equations, _, _ = mesh_equations_solver_cache(mod.semi) + # Use raw error values to compute EOC + analyze_convergence(errors, iterations, mod.semi) +end + +# Analyze convergence for any semidiscretization +# Note: this intermediate method is to allow dispatching on the semidiscretization +function analyze_convergence(errors, iterations, semi::AbstractSemidiscretization) + _, equations, _, _ = mesh_equations_solver_cache(semi) variablenames = varnames(cons2cons, equations) + analyze_convergence(errors, iterations, variablenames) +end + +# This method is called with the collected error values to actually compute and print the EOC +function analyze_convergence(errors, iterations, variablenames::Union{Tuple,AbstractArray}) nvariables = length(variablenames) # Reshape errors to get a matrix where the i-th row represents the i-th iteration diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index c6a2ee6fb95..af8b6e15d25 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -84,11 +84,13 @@ function Base.show(io::IO, ::MIME"text/plain", cb::DiscreteCallback{<:Any, <:Ana end +# This is the convenience constructor that gets called from the elixirs function AnalysisCallback(semi::AbstractSemidiscretization; kwargs...) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) AnalysisCallback(mesh, equations, solver, cache; kwargs...) end +# This is the actual constructor function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache; interval=0, save_analysis=false, @@ -127,8 +129,17 @@ function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache; end +# This method gets called from OrdinaryDiffEq's `solve(...)` function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, t, integrator) where {Condition, Affect!<:AnalysisCallback} semi = integrator.p + du_ode = first(get_tmp_cache(integrator)) + initialize!(cb, u_ode, du_ode, t, integrator, semi) +end + + +# This is the actual initialization method +# Note: we have this indirection to allow initializing a callback from the AnalysisCallbackCoupled +function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, du_ode, t, integrator, semi) where {Condition, Affect!<:AnalysisCallback} initial_state_integrals = integrate(u_ode, semi) _, equations, _, _ = mesh_equations_solver_cache(semi) @@ -197,14 +208,21 @@ function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, t, integrat # Note: For details see the actual callback function below analysis_callback.start_gc_time = Base.gc_time_ns() - analysis_callback(integrator) + analysis_callback(u_ode, du_ode, integrator, semi) return nothing end - -# TODO: Taal refactor, allow passing an IO object (which could be devnull to avoid cluttering the console) +# This method gets called from OrdinaryDiffEq's `solve(...)` function (analysis_callback::AnalysisCallback)(integrator) semi = integrator.p + du_ode = first(get_tmp_cache(integrator)) + u_ode = integrator.u + analysis_callback(u_ode, du_ode, integrator, semi) +end + +# This method gets called internally as the main entry point to the AnalysiCallback +# TODO: Taal refactor, allow passing an IO object (which could be devnull to avoid cluttering the console) +function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi) mesh, equations, solver, cache = mesh_equations_solver_cache(semi) @unpack dt, t = integrator iter = integrator.stats.naccept @@ -289,15 +307,14 @@ function (analysis_callback::AnalysisCallback)(integrator) end # Calculate current time derivative (needed for semidiscrete entropy time derivative, residual, etc.) - du_ode = first(get_tmp_cache(integrator)) # `integrator.f` is usually just a call to `rhs!` # However, we want to allow users to modify the ODE RHS outside of Trixi.jl # and allow us to pass a combined ODE RHS to OrdinaryDiffEq, e.g., for # hyperbolic-parabolic systems. - @notimeit timer() integrator.f(du_ode, integrator.u, semi, t) - u = wrap_array(integrator.u, mesh, equations, solver, cache) - du = wrap_array(du_ode, mesh, equations, solver, cache) - l2_error, linf_error = analysis_callback(io, du, u, integrator.u, t, semi) + @notimeit timer() integrator.f(du_ode, u_ode, semi, t) + u = wrap_array(u_ode, mesh, equations, solver, cache) + du = wrap_array(du_ode, mesh, equations, solver, cache) + l2_error, linf_error = analysis_callback(io, du, u, u_ode, t, semi) mpi_println("─"^100) mpi_println() diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index ec4c33c5628..4c647d78990 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -73,7 +73,8 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan) # mpi_isparallel() && MPI.Barrier(mpi_comm()) # See https://github.com/trixi-framework/Trixi.jl/issues/328 iip = true # is-inplace, i.e., we modify a vector when calling rhs! - return ODEProblem{iip}(rhs!, u0_ode, tspan, semi) + specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) end @@ -90,7 +91,8 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan, restart_file::A # mpi_isparallel() && MPI.Barrier(mpi_comm()) # See https://github.com/trixi-framework/Trixi.jl/issues/328 iip = true # is-inplace, i.e., we modify a vector when calling rhs! - return ODEProblem{iip}(rhs!, u0_ode, tspan, semi) + specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) end diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index a31d378875c..a7e453e72da 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -143,6 +143,11 @@ function compute_coefficients(t, semi::SemidiscretizationCoupled) end +@inline function get_system_u_ode(u_ode, index, semi::SemidiscretizationCoupled) + @view u_ode[semi.u_indices[index]] +end + + function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) @unpack u_indices = semi @@ -156,10 +161,10 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) # Call rhs! for each semidiscretization for i in eachsystem(semi) - u_loc = @view u_ode[u_indices[i]] - du_loc = @view du_ode[u_indices[i]] + u_loc = get_system_u_ode(u_ode, i, semi) + du_loc = get_system_u_ode(du_ode, i, semi) - rhs!(du_loc, u_loc, semi.semis[i], t) + @trixi_timeit timer() "system #$i" rhs!(du_loc, u_loc, semi.semis[i], t) end runtime = time_ns() - time_start @@ -169,6 +174,129 @@ function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) end +################################################################################ +### AnalysisCallback +################################################################################ + +""" + AnalysisCallbackCoupled(semi, callbacks...) + +Combine multiple analysis callbacks for coupled simulations with a +[`SemidiscretizationCoupled`](@ref). For each coupled system, an indididual +[`AnalysisCallback`](@ref) **must** be created and passed to the `AnalysisCallbackCoupled` **in +order**, i.e., in the same sequence as the indidvidual semidiscretizations are stored in the +`SemidiscretizationCoupled`. + +!!! warning "Experimental code" + This is an experimental feature and can change any time. +""" +struct AnalysisCallbackCoupled{CB} + callbacks::CB +end + +function Base.show(io::IO, ::MIME"text/plain", cb_coupled::DiscreteCallback{<:Any, <:AnalysisCallbackCoupled}) + @nospecialize cb_coupled # reduce precompilation time + + if get(io, :compact, false) + show(io, cb_coupled) + else + analysis_callback_coupled = cb_coupled.affect! + + summary_header(io, "AnalysisCallbackCoupled") + for (i, cb) in enumerate(analysis_callback_coupled.callbacks) + summary_line(io, "Callback #$i", "") + show(increment_indent(io), MIME"text/plain"(), cb) + end + summary_footer(io) + end +end + + +# Convenience constructor for the coupled callback that gets called directly from the elixirs +function AnalysisCallbackCoupled(semi_coupled, callbacks...) + if length(callbacks) != nsystems(semi_coupled) + error("an AnalysisCallbackCoupled requires one AnalysisCallback for each semidiscretization") + end + + analysis_callback_coupled = AnalysisCallbackCoupled{typeof(callbacks)}(callbacks) + + # This callback is triggered if any of its subsidiary callbacks' condition is triggered + condition = (u, t, integrator) -> any(callbacks) do callback + callback.condition(u, t, integrator) + end + + DiscreteCallback(condition, analysis_callback_coupled, + save_positions=(false,false), + initialize=initialize!) +end + + +# This method gets called during initialization from OrdinaryDiffEq's `solve(...)` +function initialize!(cb_coupled::DiscreteCallback{Condition,Affect!}, u_ode_coupled, t, integrator) where {Condition, Affect!<:AnalysisCallbackCoupled} + analysis_callback_coupled = cb_coupled.affect! + semi_coupled = integrator.p + du_ode_coupled = first(get_tmp_cache(integrator)) + + # Loop over coupled systems' callbacks and initialize them individually + for i in eachsystem(semi_coupled) + cb = analysis_callback_coupled.callbacks[i] + semi = semi_coupled.semis[i] + u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) + du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled) + initialize!(cb, u_ode, du_ode, t, integrator, semi) + end +end + + +# This method gets called from OrdinaryDiffEq's `solve(...)` +function (analysis_callback_coupled::AnalysisCallbackCoupled)(integrator) + semi_coupled = integrator.p + u_ode_coupled = integrator.u + du_ode_coupled = first(get_tmp_cache(integrator)) + + # Loop over coupled systems' callbacks and call them individually + for i in eachsystem(semi_coupled) + @unpack condition = analysis_callback_coupled.callbacks[i] + analysis_callback = analysis_callback_coupled.callbacks[i].affect! + u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) + + # Check condition and skip callback if it is not yet its turn + if !condition(u_ode, integrator.t, integrator) + continue + end + + semi = semi_coupled.semis[i] + du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled) + analysis_callback(u_ode, du_ode, integrator, semi) + end +end + +# used for error checks and EOC analysis +function (cb::DiscreteCallback{Condition,Affect!})(sol) where {Condition, Affect!<:AnalysisCallbackCoupled} + semi_coupled = sol.prob.p + u_ode_coupled = sol.u[end] + @unpack callbacks = cb.affect! + + uEltype = real(semi_coupled) + l2_error_collection = uEltype[] + linf_error_collection = uEltype[] + for i in eachsystem(semi_coupled) + analysis_callback = callbacks[i].affect! + @unpack analyzer = analysis_callback + cache_analysis = analysis_callback.cache + + semi = semi_coupled.semis[i] + u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) + + l2_error, linf_error = calc_error_norms(u_ode, sol.t[end], analyzer, semi, cache_analysis) + append!(l2_error_collection, l2_error) + append!(linf_error_collection, linf_error) + end + + (; l2=l2_error_collection, linf=linf_error_collection) +end + + ################################################################################ ### SaveSolutionCallback ################################################################################ @@ -188,10 +316,10 @@ end @inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode, solution_callback, integrator) - @unpack semis, u_indices = semi + @unpack semis = semi for i in eachsystem(semi) - u_ode_slice = @view u_ode[u_indices[i]] + u_ode_slice = get_system_u_ode(u_ode, i, semi) save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) end end @@ -203,10 +331,8 @@ end # In case of coupled system, use minimum timestep over all systems function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) - @unpack u_indices = semi - dt = minimum(eachsystem(semi)) do i - u_ode_slice = @view u_ode[u_indices[i]] + u_ode_slice = get_system_u_ode(u_ode, i, semi) calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) end @@ -329,6 +455,7 @@ function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditi cell_size) end + # Don't do anything for other BCs than BoundaryConditionCoupled function copy_to_coupled_boundary!(boundary_condition, u_ode, semi) return nothing @@ -346,7 +473,7 @@ function copy_to_coupled_boundary!(boundary_condition::BoundaryConditionCoupled{ @unpack other_semi_index, other_orientation, indices = boundary_condition mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) - @views u = wrap_array(u_ode[u_indices[other_semi_index]], mesh, equations, solver, cache) + u = wrap_array(get_system_u_ode(u_ode, other_semi_index, semi), mesh, equations, solver, cache) linear_indices = LinearIndices(size(mesh)) @@ -434,3 +561,55 @@ function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) return cell_indices end + + +################################################################################ +### Special elixirs +################################################################################ + +# Analyze convergence for SemidiscretizationCoupled +function analyze_convergence(errors_coupled, iterations, semi_coupled::SemidiscretizationCoupled) + # Extract errors: the errors are currently stored as + # | iter 1 sys 1 var 1...n | iter 1 sys 2 var 1...n | ... | iter 2 sys 1 var 1...n | ... + # but for calling `analyze_convergence` below, we need the following layout + # sys n: | iter 1 var 1...n | iter 1 var 1...n | ... | iter 2 var 1...n | ... + # That is, we need to extract and join the data for a single system + errors = Dict{Symbol, Vector{Float64}}[] + for i in eachsystem(semi_coupled) + push!(errors, Dict(:l2 => Float64[], :linf => Float64[])) + end + offset = 0 + for iter in 1:iterations, i in eachsystem(semi_coupled) + # Extract information on current semi + semi = semi_coupled.semis[i] + _, equations, _, _ = mesh_equations_solver_cache(semi) + variablenames = varnames(cons2cons, equations) + + # Compute offset + first = offset + 1 + last = offset + length(variablenames) + offset += length(variablenames) + + # Append errors to appropriate storage + append!(errors[i][:l2], errors_coupled[:l2][first:last]) + append!(errors[i][:linf], errors_coupled[:linf][first:last]) + end + + eoc_mean_values = Vector{Dict{Symbol, Any}}(undef, nsystems(semi_coupled)) + for i in eachsystem(semi_coupled) + # Use visual cues to separate output from multiple systems + println() + println("="^100) + println("# System $i") + println("="^100) + + # Extract information on current semi + semi = semi_coupled.semis[i] + _, equations, _, _ = mesh_equations_solver_cache(semi) + variablenames = varnames(cons2cons, equations) + + eoc_mean_values[i] = analyze_convergence(errors[i], iterations, variablenames) + end + + return eoc_mean_values +end diff --git a/test/test_special_elixirs.jl b/test/test_special_elixirs.jl index 742a3abc376..23017059eaa 100644 --- a/test/test_special_elixirs.jl +++ b/test/test_special_elixirs.jl @@ -30,6 +30,12 @@ coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none", @test isapprox(mean_convergence[:l2], [4.0], rtol=0.05) end + @timed_testset "structured_2d_dgsem coupled" begin + mean_convergence = convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_coupled.jl"), 3) + @test isapprox(mean_convergence[1][:l2], [4.0], rtol=0.05) + @test isapprox(mean_convergence[2][:l2], [4.0], rtol=0.05) + end + @timed_testset "p4est_2d_dgsem" begin # Run convergence test on unrefined mesh no_refine = @cfunction((p4est, which_tree, quadrant) -> Cint(0), Cint, (Ptr{Trixi.p4est_t}, Ptr{Trixi.p4est_topidx_t}, Ptr{Trixi.p4est_quadrant_t})) @@ -57,6 +63,7 @@ coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none", @test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_basic.jl"), 2, tspan=(0.0, 0.01)) @test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_extended.jl"), 2, initial_refinement_level=0, tspan=(0.0, 0.1)) @test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_basic.jl"), 2, tspan=(0.0, 0.01)) + @test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_coupled.jl"), 2, tspan=(0.0, 0.01)) @test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_extended.jl"), 2, cells_per_dimension=(1, 1), tspan=(0.0, 0.1)) end end diff --git a/test/test_structured_2d.jl b/test/test_structured_2d.jl index feaf66c4a7f..16fc72f0a46 100644 --- a/test/test_structured_2d.jl +++ b/test/test_structured_2d.jl @@ -19,6 +19,19 @@ isdir(outdir) && rm(outdir, recursive=true) linf = [6.627000273229378e-5]) end + @trixi_testset "elixir_advection_coupled.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_coupled.jl"), + l2 = [7.816742843181738e-6, 7.816742843196112e-6], + linf = [6.314906965543265e-5, 6.314906965410039e-5], + coverage_override = (maxiters=10^5,)) + + @testset "analysis_callback(sol) for AnalysisCallbackCoupled" begin + errors = analysis_callback(sol) + @test errors.l2 ≈ [7.816742843181738e-6, 7.816742843196112e-6] rtol=1.0e-4 + @test errors.linf ≈ [6.314906965543265e-5, 6.314906965410039e-5] rtol=1.0e-4 + end + end + @trixi_testset "elixir_advection_extended.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"), l2 = [4.220397559713772e-6], From 101a12bc1c7a683f8442865760ca668675bd6953 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sun, 11 Jun 2023 09:30:03 +0200 Subject: [PATCH 80/83] Remove unused `total_volume(...)` --- src/semidiscretization/semidiscretization_coupled.jl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index a7e453e72da..74c516bb0de 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -121,13 +121,6 @@ end end end -@inline function total_volume(semi::SemidiscretizationCoupled) - sum(semi.semis) do semi_ - mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) - total_volume(mesh, solver, cache) - end -end - function compute_coefficients(t, semi::SemidiscretizationCoupled) @unpack u_indices = semi From a69bf6681c201ec059589ca0978bb0016f857e28 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sun, 11 Jun 2023 11:12:25 +0200 Subject: [PATCH 81/83] Make `save_solution_file` work for SemiEulerGravity again (and make it multi-system aware) --- .../semidiscretization_euler_gravity.jl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/semidiscretization/semidiscretization_euler_gravity.jl b/src/semidiscretization/semidiscretization_euler_gravity.jl index 1a8d7bfad9d..963788f0481 100644 --- a/src/semidiscretization/semidiscretization_euler_gravity.jl +++ b/src/semidiscretization/semidiscretization_euler_gravity.jl @@ -421,17 +421,28 @@ end # TODO: Taal decide, where should specific parts like these be? @inline function save_solution_file(u_ode, t, dt, iter, semi::SemidiscretizationEulerGravity, solution_callback, - element_variables=Dict{Symbol,Any}()) + element_variables=Dict{Symbol,Any}(); system="") + # If this is called already as part of a multi-system setup (i.e., system is non-empty), + # we build a combined system name + if system != "" + system_euler = system * "_euler" + system_gravity = system * "_gravity" + else + system_euler = "euler" + system_gravity = "gravity" + end u_euler = wrap_array_native(u_ode, semi.semi_euler) filename_euler = save_solution_file(u_euler, t, dt, iter, mesh_equations_solver_cache(semi.semi_euler)..., - solution_callback, element_variables, system="euler") + solution_callback, element_variables, + system=system_euler) u_gravity = wrap_array_native(semi.cache.u_ode, semi.semi_gravity) filename_gravity = save_solution_file(u_gravity, t, dt, iter, mesh_equations_solver_cache(semi.semi_gravity)..., - solution_callback, element_variables, system="gravity") + solution_callback, element_variables, + system=system_gravity) return filename_euler, filename_gravity end From d3613ef39bfeb1e6d6e5cd7cb7e591c10d404fed Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Mon, 12 Jun 2023 07:36:13 +0200 Subject: [PATCH 82/83] Update src/semidiscretization/semidiscretization_euler_gravity.jl Co-authored-by: Hendrik Ranocha --- src/semidiscretization/semidiscretization_euler_gravity.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semidiscretization/semidiscretization_euler_gravity.jl b/src/semidiscretization/semidiscretization_euler_gravity.jl index 963788f0481..a854978cf9f 100644 --- a/src/semidiscretization/semidiscretization_euler_gravity.jl +++ b/src/semidiscretization/semidiscretization_euler_gravity.jl @@ -424,7 +424,7 @@ end element_variables=Dict{Symbol,Any}(); system="") # If this is called already as part of a multi-system setup (i.e., system is non-empty), # we build a combined system name - if system != "" + if !isempty(system) system_euler = system * "_euler" system_gravity = system * "_gravity" else From c6276ad2d16b1b858fec9ea971586b0589f3b219 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 16 Jun 2023 15:42:26 +0200 Subject: [PATCH 83/83] Apply formatting --- src/auxiliary/special_elixirs.jl | 3 +- src/callbacks_step/analysis.jl | 222 +++--- src/callbacks_step/save_solution.jl | 94 ++- src/callbacks_step/stepsize.jl | 47 +- src/callbacks_step/summary.jl | 56 +- src/equations/equations.jl | 1 - src/meshes/mesh_io.jl | 16 +- src/semidiscretization/semidiscretization.jl | 31 +- .../semidiscretization_coupled.jl | 750 +++++++++--------- .../semidiscretization_euler_gravity.jl | 48 +- 10 files changed, 633 insertions(+), 635 deletions(-) diff --git a/src/auxiliary/special_elixirs.jl b/src/auxiliary/special_elixirs.jl index a47ac64d18d..25bca8939ce 100644 --- a/src/auxiliary/special_elixirs.jl +++ b/src/auxiliary/special_elixirs.jl @@ -98,7 +98,8 @@ function analyze_convergence(errors, iterations, semi::AbstractSemidiscretizatio end # This method is called with the collected error values to actually compute and print the EOC -function analyze_convergence(errors, iterations, variablenames::Union{Tuple,AbstractArray}) +function analyze_convergence(errors, iterations, + variablenames::Union{Tuple, AbstractArray}) nvariables = length(variablenames) # Reshape errors to get a matrix where the i-th row represents the i-th iteration diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl index baa33f07885..7fa2e21a244 100644 --- a/src/callbacks_step/analysis.jl +++ b/src/callbacks_step/analysis.jl @@ -134,25 +134,18 @@ function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache; initialize = initialize!) end +# This method gets called from OrdinaryDiffEq's `solve(...)` function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t, integrator) where {Condition, Affect! <: AnalysisCallback} semi = integrator.p - initial_state_integrals = integrate(u_ode, semi) - _, equations, _, _ = mesh_equations_solver_cache(semi) - -# This method gets called from OrdinaryDiffEq's `solve(...)` -function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, t, - integrator) where {Condition, Affect!<:AnalysisCallback} - semi = integrator.p du_ode = first(get_tmp_cache(integrator)) initialize!(cb, u_ode, du_ode, t, integrator, semi) end - # This is the actual initialization method # Note: we have this indirection to allow initializing a callback from the AnalysisCallbackCoupled -function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, du_ode, t, - integrator, semi) where {Condition, Affect!<:AnalysisCallback} +function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, du_ode, t, + integrator, semi) where {Condition, Affect! <: AnalysisCallback} initial_state_integrals = integrate(u_ode, semi) _, equations, _, _ = mesh_equations_solver_cache(semi) @@ -170,32 +163,32 @@ function initialize!(cb::DiscreteCallback{Condition,Affect!}, u_ode, du_ode, t, @printf(io, " %-14s", "dt") if :l2_error in analysis_errors for v in varnames(cons2cons, equations) - @printf(io, " %-14s", "l2_" * v) + @printf(io, " %-14s", "l2_"*v) end end if :linf_error in analysis_errors for v in varnames(cons2cons, equations) - @printf(io, " %-14s", "linf_" * v) + @printf(io, " %-14s", "linf_"*v) end end if :conservation_error in analysis_errors for v in varnames(cons2cons, equations) - @printf(io, " %-14s", "cons_" * v) + @printf(io, " %-14s", "cons_"*v) end end if :residual in analysis_errors for v in varnames(cons2cons, equations) - @printf(io, " %-14s", "res_" * v) + @printf(io, " %-14s", "res_"*v) end end if :l2_error_primitive in analysis_errors for v in varnames(cons2prim, equations) - @printf(io, " %-14s", "l2_" * v) + @printf(io, " %-14s", "l2_"*v) end end if :linf_error_primitive in analysis_errors for v in varnames(cons2prim, equations) - @printf(io, " %-14s", "linf_" * v) + @printf(io, " %-14s", "linf_"*v) end end @@ -226,121 +219,114 @@ end # This method gets called from OrdinaryDiffEq's `solve(...)` function (analysis_callback::AnalysisCallback)(integrator) - semi = integrator.p - du_ode = first(get_tmp_cache(integrator)) - u_ode = integrator.u - analysis_callback(u_ode, du_ode, integrator, semi) + semi = integrator.p + du_ode = first(get_tmp_cache(integrator)) + u_ode = integrator.u + analysis_callback(u_ode, du_ode, integrator, semi) end # This method gets called internally as the main entry point to the AnalysiCallback # TODO: Taal refactor, allow passing an IO object (which could be devnull to avoid cluttering the console) function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi) - mesh, equations, solver, cache = mesh_equations_solver_cache(semi) - @unpack dt, t = integrator - iter = integrator.stats.naccept - - # Record performance measurements and compute performance index (PID) - runtime_since_last_analysis = 1.0e-9 * (time_ns() - analysis_callback.start_time_last_analysis) - # PID is an MPI-aware measure of how much time per global degree of freedom (i.e., over all ranks) - # and per `rhs!` evaluation is required. MPI-aware means that it essentially adds up the time - # spent on each MPI rank. Thus, in an ideally parallelized program, the PID should be constant - # independent of the number of MPI ranks used, since, e.g., using 4x the number of ranks should - # divide the runtime on each rank by 4. See also the Trixi.jl docs ("Performance" section) for - # more information. - ncalls_rhs_since_last_analysis = (ncalls(semi.performance_counter) - - analysis_callback.ncalls_rhs_last_analysis) - performance_index = runtime_since_last_analysis * mpi_nranks() / (ndofsglobal(mesh, solver, cache) - * ncalls_rhs_since_last_analysis) - - # Compute the total runtime since the analysis callback has been initialized, in seconds - runtime_absolute = 1.0e-9 * (time_ns() - analysis_callback.start_time) - - # Compute the relative runtime as time spent in `rhs!` divided by the number of calls to `rhs!` - # and the number of local degrees of freedom - # OBS! This computation must happen *after* the PID computation above, since `take!(...)` - # will reset the number of calls to `rhs!` - runtime_relative = 1.0e-9 * take!(semi.performance_counter) / ndofs(semi) - - # Compute the total time spent in garbage collection since the analysis callback has been - # initialized, in seconds - # Note: `Base.gc_time_ns()` is not part of the public Julia API but has been available at least - # since Julia 1.6. Should this function be removed without replacement in a future Julia - # release, just delete this analysis quantity from the callback. - # Source: https://github.com/JuliaLang/julia/blob/b540315cb4bd91e6f3a3e4ab8129a58556947628/base/timing.jl#L83-L84 - gc_time_absolute = 1.0e-9 * (Base.gc_time_ns() - analysis_callback.start_gc_time) - - # Compute the percentage of total time that was spent in garbage collection - gc_time_percentage = gc_time_absolute / runtime_absolute - - # Obtain the current memory usage of the Julia garbage collector, in MiB, i.e., the total size of - # objects in memory that have been allocated by the JIT compiler or the user code. - # Note: `Base.gc_live_bytes()` is not part of the public Julia API but has been available at least - # since Julia 1.6. Should this function be removed without replacement in a future Julia - # release, just delete this analysis quantity from the callback. - # Source: https://github.com/JuliaLang/julia/blob/b540315cb4bd91e6f3a3e4ab8129a58556947628/base/timing.jl#L86-L97 - memory_use = Base.gc_live_bytes() / 2^20 # bytes -> MiB - - @trixi_timeit timer() "analyze solution" begin - # General information - mpi_println() - mpi_println("─"^100) - # TODO: Taal refactor, polydeg is specific to DGSEM - mpi_println(" Simulation running '", get_name(equations), "' with ", summary(solver)) - mpi_println("─"^100) - mpi_println(" #timesteps: " * @sprintf("% 14d", iter) * - " " * - " run time: " * @sprintf("%10.8e s", runtime_absolute)) - mpi_println(" Δt: " * @sprintf("%10.8e", dt) * - " " * - " └── GC time: " * @sprintf("%10.8e s (%5.3f%%)", gc_time_absolute, gc_time_percentage)) - mpi_println(" sim. time: " * @sprintf("%10.8e", t) * - " " * - " time/DOF/rhs!: " * @sprintf("%10.8e s", runtime_relative)) - mpi_println(" " * " " * - " " * - " PID: " * @sprintf("%10.8e s", performance_index)) - mpi_println(" #DOF: " * @sprintf("% 14d", ndofs(semi)) * - " " * - " alloc'd memory: " * @sprintf("%14.3f MiB", memory_use)) - mpi_println(" #elements: " * @sprintf("% 14d", nelements(mesh, solver, cache))) - - # Level information (only show for AMR) - print_amr_information(integrator.opts.callback, mesh, solver, cache) - mpi_println() - - # Open file for appending and store time step and time information - if mpi_isroot() && analysis_callback.save_analysis - io = open(joinpath(analysis_callback.output_directory, analysis_callback.analysis_filename), "a") - @printf(io, "% 9d", iter) - @printf(io, " %10.8e", t) - @printf(io, " %10.8e", dt) - else - io = devnull - end + mesh, equations, solver, cache = mesh_equations_solver_cache(semi) + @unpack dt, t = integrator + iter = integrator.stats.naccept + + # Record performance measurements and compute performance index (PID) + runtime_since_last_analysis = 1.0e-9 * (time_ns() - + analysis_callback.start_time_last_analysis) + # PID is an MPI-aware measure of how much time per global degree of freedom (i.e., over all ranks) + # and per `rhs!` evaluation is required. MPI-aware means that it essentially adds up the time + # spent on each MPI rank. Thus, in an ideally parallelized program, the PID should be constant + # independent of the number of MPI ranks used, since, e.g., using 4x the number of ranks should + # divide the runtime on each rank by 4. See also the Trixi.jl docs ("Performance" section) for + # more information. + ncalls_rhs_since_last_analysis = (ncalls(semi.performance_counter) + - + analysis_callback.ncalls_rhs_last_analysis) + performance_index = runtime_since_last_analysis * mpi_nranks() / + (ndofsglobal(mesh, solver, cache) + * + ncalls_rhs_since_last_analysis) + + # Compute the total runtime since the analysis callback has been initialized, in seconds + runtime_absolute = 1.0e-9 * (time_ns() - analysis_callback.start_time) + + # Compute the relative runtime as time spent in `rhs!` divided by the number of calls to `rhs!` + # and the number of local degrees of freedom + # OBS! This computation must happen *after* the PID computation above, since `take!(...)` + # will reset the number of calls to `rhs!` + runtime_relative = 1.0e-9 * take!(semi.performance_counter) / ndofs(semi) + + # Compute the total time spent in garbage collection since the analysis callback has been + # initialized, in seconds + # Note: `Base.gc_time_ns()` is not part of the public Julia API but has been available at least + # since Julia 1.6. Should this function be removed without replacement in a future Julia + # release, just delete this analysis quantity from the callback. + # Source: https://github.com/JuliaLang/julia/blob/b540315cb4bd91e6f3a3e4ab8129a58556947628/base/timing.jl#L83-L84 + gc_time_absolute = 1.0e-9 * (Base.gc_time_ns() - analysis_callback.start_gc_time) + + # Compute the percentage of total time that was spent in garbage collection + gc_time_percentage = gc_time_absolute / runtime_absolute + + # Obtain the current memory usage of the Julia garbage collector, in MiB, i.e., the total size of + # objects in memory that have been allocated by the JIT compiler or the user code. + # Note: `Base.gc_live_bytes()` is not part of the public Julia API but has been available at least + # since Julia 1.6. Should this function be removed without replacement in a future Julia + # release, just delete this analysis quantity from the callback. + # Source: https://github.com/JuliaLang/julia/blob/b540315cb4bd91e6f3a3e4ab8129a58556947628/base/timing.jl#L86-L97 + memory_use = Base.gc_live_bytes() / 2^20 # bytes -> MiB + + @trixi_timeit timer() "analyze solution" begin + # General information + mpi_println() + mpi_println("─"^100) + mpi_println(" Simulation running '", get_name(equations), "' with ", + summary(solver)) + mpi_println("─"^100) + mpi_println(" #timesteps: " * @sprintf("% 14d", iter) * + " " * + " run time: " * @sprintf("%10.8e s", runtime_absolute)) + mpi_println(" Δt: " * @sprintf("%10.8e", dt) * + " " * + " └── GC time: " * + @sprintf("%10.8e s (%5.3f%%)", gc_time_absolute, gc_time_percentage)) + mpi_println(" sim. time: " * @sprintf("%10.8e", t) * + " " * + " time/DOF/rhs!: " * @sprintf("%10.8e s", runtime_relative)) + mpi_println(" " * " " * + " " * + " PID: " * @sprintf("%10.8e s", performance_index)) + mpi_println(" #DOF: " * @sprintf("% 14d", ndofs(semi)) * + " " * + " alloc'd memory: " * @sprintf("%14.3f MiB", memory_use)) + mpi_println(" #elements: " * + @sprintf("% 14d", nelements(mesh, solver, cache))) + + # Level information (only show for AMR) + print_amr_information(integrator.opts.callback, mesh, solver, cache) + mpi_println() - # Calculate current time derivative (needed for semidiscrete entropy time derivative, residual, etc.) - # `integrator.f` is usually just a call to `rhs!` - # However, we want to allow users to modify the ODE RHS outside of Trixi.jl - # and allow us to pass a combined ODE RHS to OrdinaryDiffEq, e.g., for - # hyperbolic-parabolic systems. - @notimeit timer() integrator.f(du_ode, u_ode, semi, t) - u = wrap_array(u_ode, mesh, equations, solver, cache) - du = wrap_array(du_ode, mesh, equations, solver, cache) - l2_error, linf_error = analysis_callback(io, du, u, u_ode, t, semi) - - mpi_println("─"^100) - mpi_println() + # Open file for appending and store time step and time information + if mpi_isroot() && analysis_callback.save_analysis + io = open(joinpath(analysis_callback.output_directory, + analysis_callback.analysis_filename), "a") + @printf(io, "% 9d", iter) + @printf(io, " %10.8e", t) + @printf(io, " %10.8e", dt) + else + io = devnull + end # Calculate current time derivative (needed for semidiscrete entropy time derivative, residual, etc.) - du_ode = first(get_tmp_cache(integrator)) # `integrator.f` is usually just a call to `rhs!` # However, we want to allow users to modify the ODE RHS outside of Trixi.jl # and allow us to pass a combined ODE RHS to OrdinaryDiffEq, e.g., for # hyperbolic-parabolic systems. - @notimeit timer() integrator.f(du_ode, integrator.u, semi, t) - u = wrap_array(integrator.u, mesh, equations, solver, cache) + @notimeit timer() integrator.f(du_ode, u_ode, semi, t) + u = wrap_array(u_ode, mesh, equations, solver, cache) du = wrap_array(du_ode, mesh, equations, solver, cache) - l2_error, linf_error = analysis_callback(io, du, u, integrator.u, t, semi) + l2_error, linf_error = analysis_callback(io, du, u, u_ode, t, semi) mpi_println("─"^100) mpi_println() diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl index 44873650061..1fe0d6b1e15 100644 --- a/src/callbacks_step/save_solution.jl +++ b/src/callbacks_step/save_solution.jl @@ -138,10 +138,10 @@ function initialize_save_cb!(cb, u, t, integrator) end function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, integrator) - mpi_isroot() && mkpath(solution_callback.output_directory) + mpi_isroot() && mkpath(solution_callback.output_directory) - semi = integrator.p - @trixi_timeit timer() "I/O" save_mesh(semi, solution_callback.output_directory) + semi = integrator.p + @trixi_timeit timer() "I/O" save_mesh(semi, solution_callback.output_directory) if solution_callback.save_initial_solution solution_callback(integrator) @@ -151,13 +151,13 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte end # Save mesh for a general semidiscretization (default) -function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep=0) - mesh, _, _, _ = mesh_equations_solver_cache(semi) +function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep = 0) + mesh, _, _, _ = mesh_equations_solver_cache(semi) - if mesh.unsaved_changes - mesh.current_filename = save_mesh_file(mesh, output_directory) - mesh.unsaved_changes = false - end + if mesh.unsaved_changes + mesh.current_filename = save_mesh_file(mesh, output_directory) + mesh.unsaved_changes = false + end end # this method is called to determine whether the callback should be activated @@ -176,52 +176,60 @@ end # this method is called when the callback is activated function (solution_callback::SaveSolutionCallback)(integrator) - u_ode = integrator.u - semi = integrator.p - iter = integrator.stats.naccept - - @trixi_timeit timer() "I/O" begin - # Call high-level functions that dispatch on semidiscretization type - @trixi_timeit timer() "save mesh" save_mesh(semi, solution_callback.output_directory, iter) - save_solution_file(semi, u_ode, solution_callback, integrator) - end + u_ode = integrator.u + semi = integrator.p + iter = integrator.stats.naccept + + @trixi_timeit timer() "I/O" begin + # Call high-level functions that dispatch on semidiscretization type + @trixi_timeit timer() "save mesh" save_mesh(semi, + solution_callback.output_directory, + iter) + save_solution_file(semi, u_ode, solution_callback, integrator) + end # avoid re-evaluating possible FSAL stages u_modified!(integrator, false) return nothing end -@inline function save_solution_file(semi::AbstractSemidiscretization, u_ode, solution_callback, - integrator; system="") - @unpack t, dt = integrator - iter = integrator.stats.naccept - - element_variables = Dict{Symbol, Any}() - @trixi_timeit timer() "get element variables" begin - get_element_variables!(element_variables, u_ode, semi) - callbacks = integrator.opts.callback - if callbacks isa CallbackSet - for cb in callbacks.continuous_callbacks - get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=iter) - end - for cb in callbacks.discrete_callbacks - get_element_variables!(element_variables, u_ode, semi, cb; t=integrator.t, iter=iter) - end +@inline function save_solution_file(semi::AbstractSemidiscretization, u_ode, + solution_callback, + integrator; system = "") + @unpack t, dt = integrator + iter = integrator.stats.naccept + + element_variables = Dict{Symbol, Any}() + @trixi_timeit timer() "get element variables" begin + get_element_variables!(element_variables, u_ode, semi) + callbacks = integrator.opts.callback + if callbacks isa CallbackSet + for cb in callbacks.continuous_callbacks + get_element_variables!(element_variables, u_ode, semi, cb; + t = integrator.t, iter = iter) + end + for cb in callbacks.discrete_callbacks + get_element_variables!(element_variables, u_ode, semi, cb; + t = integrator.t, iter = iter) + end + end end - end - @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, - solution_callback, element_variables, - system=system) + @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi, + solution_callback, + element_variables, + system = system) end @inline function save_solution_file(u_ode, t, dt, iter, semi::AbstractSemidiscretization, solution_callback, - element_variables=Dict{Symbol,Any}(); system="") - mesh, equations, solver, cache = mesh_equations_solver_cache(semi) - u = wrap_array_native(u_ode, mesh, equations, solver, cache) - save_solution_file(u, t, dt, iter, mesh, equations, solver, cache, solution_callback, - element_variables; system=system) + element_variables = Dict{Symbol, Any}(); + system = "") + mesh, equations, solver, cache = mesh_equations_solver_cache(semi) + u = wrap_array_native(u_ode, mesh, equations, solver, cache) + save_solution_file(u, t, dt, iter, mesh, equations, solver, cache, + solution_callback, + element_variables; system = system) end # TODO: Taal refactor, move save_mesh_file? diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl index c2dbcfc56f7..8b5cb958318 100644 --- a/src/callbacks_step/stepsize.jl +++ b/src/callbacks_step/stepsize.jl @@ -59,38 +59,37 @@ end # This method is called as callback during the time integration. @inline function (stepsize_callback::StepsizeCallback)(integrator) - # TODO: Taal decide, shall we set the time step even if the integrator is adaptive? - if !integrator.opts.adaptive - t = integrator.t - u_ode = integrator.u - semi = integrator.p - @unpack cfl_number = stepsize_callback - - # Dispatch based on semidiscretization - dt = @trixi_timeit timer() "calculate dt" calculate_dt(u_ode, t, cfl_number, semi) - - set_proposed_dt!(integrator, dt) - integrator.opts.dtmax = dt - integrator.dtcache = dt - end + # TODO: Taal decide, shall we set the time step even if the integrator is adaptive? + if !integrator.opts.adaptive + t = integrator.t + u_ode = integrator.u + semi = integrator.p + @unpack cfl_number = stepsize_callback + + # Dispatch based on semidiscretization + dt = @trixi_timeit timer() "calculate dt" calculate_dt(u_ode, t, cfl_number, + semi) + + set_proposed_dt!(integrator, dt) + integrator.opts.dtmax = dt + integrator.dtcache = dt + end - # avoid re-evaluating possible FSAL stages - u_modified!(integrator, false) - return nothing + # avoid re-evaluating possible FSAL stages + u_modified!(integrator, false) + return nothing end - # General case for a single semidiscretization function calculate_dt(u_ode, t, cfl_number, semi::AbstractSemidiscretization) - mesh, equations, solver, cache = mesh_equations_solver_cache(semi) - u = wrap_array(u_ode, mesh, equations, solver, cache) + mesh, equations, solver, cache = mesh_equations_solver_cache(semi) + u = wrap_array(u_ode, mesh, equations, solver, cache) - dt = cfl_number * max_dt(u, t, mesh, - have_constant_speed(equations), equations, - solver, cache) + dt = cfl_number * max_dt(u, t, mesh, + have_constant_speed(equations), equations, + solver, cache) end - # Time integration methods from the DiffEq ecosystem without adaptive time stepping on their own # such as `CarpenterKennedy2N54` require passing `dt=...` in `solve(ode, ...)`. Since we don't have # an integrator at this stage but only the ODE, this method will be used there. It's called in diff --git a/src/callbacks_step/summary.jl b/src/callbacks_step/summary.jl index 51465ec9a89..08e13d0b98d 100644 --- a/src/callbacks_step/summary.jl +++ b/src/callbacks_step/summary.jl @@ -140,29 +140,29 @@ end # Print information about the current simulation setup # Note: This is called *after* all initialization is done, but *before* the first time step function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) - mpi_isroot() || return nothing + mpi_isroot() || return nothing - print_startup_message() + print_startup_message() - io = stdout - io_context = IOContext(io, - :compact => false, - :key_width => 30, - :total_width => 100, - :indentation_level => 0) + io = stdout + io_context = IOContext(io, + :compact => false, + :key_width => 30, + :total_width => 100, + :indentation_level => 0) - semi = integrator.p - print_summary_semidiscretization(io_context, semi) + semi = integrator.p + print_summary_semidiscretization(io_context, semi) - callbacks = integrator.opts.callback - if callbacks isa CallbackSet - for cb in callbacks.continuous_callbacks - show(io_context, MIME"text/plain"(), cb) - println(io, "\n") - end - for cb in callbacks.discrete_callbacks - # Do not show ourselves - cb.affect! === summary_callback && continue + callbacks = integrator.opts.callback + if callbacks isa CallbackSet + for cb in callbacks.continuous_callbacks + show(io_context, MIME"text/plain"(), cb) + println(io, "\n") + end + for cb in callbacks.discrete_callbacks + # Do not show ourselves + cb.affect! === summary_callback && continue show(io_context, MIME"text/plain"(), cb) println(io, "\n") @@ -201,15 +201,15 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator) end function print_summary_semidiscretization(io::IO, semi::AbstractSemidiscretization) - show(io, MIME"text/plain"(), semi) - println(io, "\n") - mesh, equations, solver, _ = mesh_equations_solver_cache(semi) - show(io, MIME"text/plain"(), mesh) - println(io, "\n") - show(io, MIME"text/plain"(), equations) - println(io, "\n") - show(io, MIME"text/plain"(), solver) - println(io, "\n") + show(io, MIME"text/plain"(), semi) + println(io, "\n") + mesh, equations, solver, _ = mesh_equations_solver_cache(semi) + show(io, MIME"text/plain"(), mesh) + println(io, "\n") + show(io, MIME"text/plain"(), equations) + println(io, "\n") + show(io, MIME"text/plain"(), solver) + println(io, "\n") end function (cb::DiscreteCallback{Condition, Affect!})(io::IO = stdout) where {Condition, diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 17fa99d174f..90b2cd62191 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -202,7 +202,6 @@ struct BoundaryConditionNeumann{B} boundary_normal_flux_function::B end - # set sensible default values that may be overwritten by specific equations """ have_nonconservative_terms(equations) diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl index fa1f086559d..ede85d80106 100644 --- a/src/meshes/mesh_io.jl +++ b/src/meshes/mesh_io.jl @@ -95,15 +95,15 @@ end # of the mesh, like its size and the type of boundary mapping function. # Then, within Trixi2Vtk, the StructuredMesh and its node coordinates are reconstructured from # these attributes for plotting purposes -function save_mesh_file(mesh::StructuredMesh, output_directory; system="") - # Create output directory (if it does not exist) - mkpath(output_directory) +function save_mesh_file(mesh::StructuredMesh, output_directory; system = "") + # Create output directory (if it does not exist) + mkpath(output_directory) - if isempty(system) - filename = joinpath(output_directory, "mesh.h5") - else - filename = joinpath(output_directory, @sprintf("mesh_%s.h5", system)) - end + if isempty(system) + filename = joinpath(output_directory, "mesh.h5") + else + filename = joinpath(output_directory, @sprintf("mesh_%s.h5", system)) + end # Open file (clobber existing content) h5open(filename, "w") do file diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl index 4597e5c5894..ac312c57c89 100644 --- a/src/semidiscretization/semidiscretization.jl +++ b/src/semidiscretization/semidiscretization.jl @@ -71,13 +71,13 @@ Wrap the semidiscretization `semi` as an ODE problem in the time interval `tspan that can be passed to `solve` from the [SciML ecosystem](https://diffeq.sciml.ai/latest/). """ function semidiscretize(semi::AbstractSemidiscretization, tspan) - u0_ode = compute_coefficients(first(tspan), semi) - # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using - # mpi_isparallel() && MPI.Barrier(mpi_comm()) - # See https://github.com/trixi-framework/Trixi.jl/issues/328 - iip = true # is-inplace, i.e., we modify a vector when calling rhs! - specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) - return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) + u0_ode = compute_coefficients(first(tspan), semi) + # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using + # mpi_isparallel() && MPI.Barrier(mpi_comm()) + # See https://github.com/trixi-framework/Trixi.jl/issues/328 + iip = true # is-inplace, i.e., we modify a vector when calling rhs! + specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) end """ @@ -87,14 +87,15 @@ Wrap the semidiscretization `semi` as an ODE problem in the time interval `tspan that can be passed to `solve` from the [SciML ecosystem](https://diffeq.sciml.ai/latest/). The initial condition etc. is taken from the `restart_file`. """ -function semidiscretize(semi::AbstractSemidiscretization, tspan, restart_file::AbstractString) - u0_ode = load_restart_file(semi, restart_file) - # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using - # mpi_isparallel() && MPI.Barrier(mpi_comm()) - # See https://github.com/trixi-framework/Trixi.jl/issues/328 - iip = true # is-inplace, i.e., we modify a vector when calling rhs! - specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) - return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) +function semidiscretize(semi::AbstractSemidiscretization, tspan, + restart_file::AbstractString) + u0_ode = load_restart_file(semi, restart_file) + # TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using + # mpi_isparallel() && MPI.Barrier(mpi_comm()) + # See https://github.com/trixi-framework/Trixi.jl/issues/328 + iip = true # is-inplace, i.e., we modify a vector when calling rhs! + specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi) + return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi) end """ diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl index 74c516bb0de..b7adff78425 100644 --- a/src/semidiscretization/semidiscretization_coupled.jl +++ b/src/semidiscretization/semidiscretization_coupled.jl @@ -10,95 +10,93 @@ The semidiscretizations can be coupled by gluing meshes together using [`Boundar This is an experimental feature and can change any time. """ struct SemidiscretizationCoupled{S, Indices, EquationList} <: AbstractSemidiscretization - semis::S - u_indices::Indices # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] - performance_counter::PerformanceCounter + semis::S + u_indices::Indices # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i] + performance_counter::PerformanceCounter end - """ SemidiscretizationCoupled(semis...) Create a coupled semidiscretization that consists of the semidiscretizations passed as arguments. """ function SemidiscretizationCoupled(semis...) - @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" + @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!" - # Number of coefficients for each semidiscretization - n_coefficients = zeros(Int, length(semis)) - for i in 1:length(semis) - _, equations, _, _ = mesh_equations_solver_cache(semis[i]) - n_coefficients[i] = ndofs(semis[i]) * nvariables(equations) - end + # Number of coefficients for each semidiscretization + n_coefficients = zeros(Int, length(semis)) + for i in 1:length(semis) + _, equations, _, _ = mesh_equations_solver_cache(semis[i]) + n_coefficients[i] = ndofs(semis[i]) * nvariables(equations) + end - # Compute range of coefficients associated with each semidiscretization and allocate coupled BCs - u_indices = Vector{UnitRange{Int}}(undef, length(semis)) - for i in 1:length(semis) - offset = sum(n_coefficients[1:(i-1)]) + 1 - u_indices[i] = range(offset, length=n_coefficients[i]) + # Compute range of coefficients associated with each semidiscretization and allocate coupled BCs + u_indices = Vector{UnitRange{Int}}(undef, length(semis)) + for i in 1:length(semis) + offset = sum(n_coefficients[1:(i - 1)]) + 1 + u_indices[i] = range(offset, length = n_coefficients[i]) - allocate_coupled_boundary_conditions(semis[i]) - end + allocate_coupled_boundary_conditions(semis[i]) + end - performance_counter = PerformanceCounter() + performance_counter = PerformanceCounter() - SemidiscretizationCoupled{typeof(semis), typeof(u_indices), typeof(performance_counter)}(semis, u_indices, performance_counter) + SemidiscretizationCoupled{typeof(semis), typeof(u_indices), typeof(performance_counter) + }(semis, u_indices, performance_counter) end - function Base.show(io::IO, semi::SemidiscretizationCoupled) - @nospecialize semi # reduce precompilation time + @nospecialize semi # reduce precompilation time - print(io, "SemidiscretizationCoupled($(semi.semis))") + print(io, "SemidiscretizationCoupled($(semi.semis))") end function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled) - @nospecialize semi # reduce precompilation time - - if get(io, :compact, false) - show(io, semi) - else - summary_header(io, "SemidiscretizationCoupled") - summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) - summary_line(io, "#systems", nsystems(semi)) - for i in eachsystem(semi) - summary_line(io, "system", i) - mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) - summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) - summary_line(increment_indent(io), "equations", equations |> typeof |> nameof) - summary_line(increment_indent(io), "initial condition", semi.semis[i].initial_condition) - # no boundary conditions since that could be too much - summary_line(increment_indent(io), "source terms", semi.semis[i].source_terms) - summary_line(increment_indent(io), "solver", solver |> typeof |> nameof) + @nospecialize semi # reduce precompilation time + + if get(io, :compact, false) + show(io, semi) + else + summary_header(io, "SemidiscretizationCoupled") + summary_line(io, "#spatial dimensions", ndims(semi.semis[1])) + summary_line(io, "#systems", nsystems(semi)) + for i in eachsystem(semi) + summary_line(io, "system", i) + mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) + summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof) + summary_line(increment_indent(io), "equations", equations |> typeof |> nameof) + summary_line(increment_indent(io), "initial condition", + semi.semis[i].initial_condition) + # no boundary conditions since that could be too much + summary_line(increment_indent(io), "source terms", semi.semis[i].source_terms) + summary_line(increment_indent(io), "solver", solver |> typeof |> nameof) + end + summary_line(io, "total #DOFs", ndofs(semi)) + summary_footer(io) end - summary_line(io, "total #DOFs", ndofs(semi)) - summary_footer(io) - end end - function print_summary_semidiscretization(io::IO, semi::SemidiscretizationCoupled) - show(io, MIME"text/plain"(), semi) - println(io, "\n") - for i in eachsystem(semi) - mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) - summary_header(io, "System #$i") + show(io, MIME"text/plain"(), semi) + println(io, "\n") + for i in eachsystem(semi) + mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i]) + summary_header(io, "System #$i") - summary_line(io, "mesh", mesh |> typeof |> nameof) - show(increment_indent(io), MIME"text/plain"(), mesh) + summary_line(io, "mesh", mesh |> typeof |> nameof) + show(increment_indent(io), MIME"text/plain"(), mesh) - summary_line(io, "equations", equations |> typeof |> nameof) - show(increment_indent(io), MIME"text/plain"(), equations) + summary_line(io, "equations", equations |> typeof |> nameof) + show(increment_indent(io), MIME"text/plain"(), equations) - summary_line(io, "solver", solver |> typeof |> nameof) - show(increment_indent(io), MIME"text/plain"(), solver) + summary_line(io, "solver", solver |> typeof |> nameof) + show(increment_indent(io), MIME"text/plain"(), solver) - summary_footer(io) - println(io, "\n") - end + summary_footer(io) + println(io, "\n") + end end - @inline Base.ndims(semi::SemidiscretizationCoupled) = ndims(semi.semis[1]) @inline nsystems(semi::SemidiscretizationCoupled) = length(semi.semis) @@ -110,63 +108,59 @@ end @inline Base.eltype(semi::SemidiscretizationCoupled) = promote_type(eltype.(semi.semis)...) @inline function ndofs(semi::SemidiscretizationCoupled) - sum(ndofs, semi.semis) + sum(ndofs, semi.semis) end @inline function nelements(semi::SemidiscretizationCoupled) - return sum(semi.semis) do semi_ - mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) + return sum(semi.semis) do semi_ + mesh, equations, solver, cache = mesh_equations_solver_cache(semi_) - nelements(mesh, solver, cache) - end + nelements(mesh, solver, cache) + end end - function compute_coefficients(t, semi::SemidiscretizationCoupled) - @unpack u_indices = semi + @unpack u_indices = semi - u_ode = Vector{real(semi)}(undef, u_indices[end][end]) + u_ode = Vector{real(semi)}(undef, u_indices[end][end]) - for i in eachsystem(semi) - # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` - u_ode[u_indices[i]] .= compute_coefficients(t, semi.semis[i]) - end + for i in eachsystem(semi) + # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl` + u_ode[u_indices[i]] .= compute_coefficients(t, semi.semis[i]) + end - return u_ode + return u_ode end - @inline function get_system_u_ode(u_ode, index, semi::SemidiscretizationCoupled) - @view u_ode[semi.u_indices[index]] + @view u_ode[semi.u_indices[index]] end - function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t) - @unpack u_indices = semi + @unpack u_indices = semi - time_start = time_ns() + time_start = time_ns() - @trixi_timeit timer() "copy to coupled boundaries" begin - for semi_ in semi.semis - copy_to_coupled_boundary!(semi_.boundary_conditions, u_ode, semi) + @trixi_timeit timer() "copy to coupled boundaries" begin + for semi_ in semi.semis + copy_to_coupled_boundary!(semi_.boundary_conditions, u_ode, semi) + end end - end - # Call rhs! for each semidiscretization - for i in eachsystem(semi) - u_loc = get_system_u_ode(u_ode, i, semi) - du_loc = get_system_u_ode(du_ode, i, semi) + # Call rhs! for each semidiscretization + for i in eachsystem(semi) + u_loc = get_system_u_ode(u_ode, i, semi) + du_loc = get_system_u_ode(du_ode, i, semi) - @trixi_timeit timer() "system #$i" rhs!(du_loc, u_loc, semi.semis[i], t) - end + @trixi_timeit timer() "system #$i" rhs!(du_loc, u_loc, semi.semis[i], t) + end - runtime = time_ns() - time_start - put!(semi.performance_counter, runtime) + runtime = time_ns() - time_start + put!(semi.performance_counter, runtime) - return nothing + return nothing end - ################################################################################ ### AnalysisCallback ################################################################################ @@ -184,155 +178,154 @@ order**, i.e., in the same sequence as the indidvidual semidiscretizations are s This is an experimental feature and can change any time. """ struct AnalysisCallbackCoupled{CB} - callbacks::CB + callbacks::CB end -function Base.show(io::IO, ::MIME"text/plain", cb_coupled::DiscreteCallback{<:Any, <:AnalysisCallbackCoupled}) - @nospecialize cb_coupled # reduce precompilation time - - if get(io, :compact, false) - show(io, cb_coupled) - else - analysis_callback_coupled = cb_coupled.affect! - - summary_header(io, "AnalysisCallbackCoupled") - for (i, cb) in enumerate(analysis_callback_coupled.callbacks) - summary_line(io, "Callback #$i", "") - show(increment_indent(io), MIME"text/plain"(), cb) +function Base.show(io::IO, ::MIME"text/plain", + cb_coupled::DiscreteCallback{<:Any, <:AnalysisCallbackCoupled}) + @nospecialize cb_coupled # reduce precompilation time + + if get(io, :compact, false) + show(io, cb_coupled) + else + analysis_callback_coupled = cb_coupled.affect! + + summary_header(io, "AnalysisCallbackCoupled") + for (i, cb) in enumerate(analysis_callback_coupled.callbacks) + summary_line(io, "Callback #$i", "") + show(increment_indent(io), MIME"text/plain"(), cb) + end + summary_footer(io) end - summary_footer(io) - end end - # Convenience constructor for the coupled callback that gets called directly from the elixirs function AnalysisCallbackCoupled(semi_coupled, callbacks...) - if length(callbacks) != nsystems(semi_coupled) - error("an AnalysisCallbackCoupled requires one AnalysisCallback for each semidiscretization") - end + if length(callbacks) != nsystems(semi_coupled) + error("an AnalysisCallbackCoupled requires one AnalysisCallback for each semidiscretization") + end - analysis_callback_coupled = AnalysisCallbackCoupled{typeof(callbacks)}(callbacks) + analysis_callback_coupled = AnalysisCallbackCoupled{typeof(callbacks)}(callbacks) - # This callback is triggered if any of its subsidiary callbacks' condition is triggered - condition = (u, t, integrator) -> any(callbacks) do callback - callback.condition(u, t, integrator) - end + # This callback is triggered if any of its subsidiary callbacks' condition is triggered + condition = (u, t, integrator) -> any(callbacks) do callback + callback.condition(u, t, integrator) + end - DiscreteCallback(condition, analysis_callback_coupled, - save_positions=(false,false), - initialize=initialize!) + DiscreteCallback(condition, analysis_callback_coupled, + save_positions = (false, false), + initialize = initialize!) end - # This method gets called during initialization from OrdinaryDiffEq's `solve(...)` -function initialize!(cb_coupled::DiscreteCallback{Condition,Affect!}, u_ode_coupled, t, integrator) where {Condition, Affect!<:AnalysisCallbackCoupled} - analysis_callback_coupled = cb_coupled.affect! - semi_coupled = integrator.p - du_ode_coupled = first(get_tmp_cache(integrator)) - - # Loop over coupled systems' callbacks and initialize them individually - for i in eachsystem(semi_coupled) - cb = analysis_callback_coupled.callbacks[i] - semi = semi_coupled.semis[i] - u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) - du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled) - initialize!(cb, u_ode, du_ode, t, integrator, semi) - end +function initialize!(cb_coupled::DiscreteCallback{Condition, Affect!}, u_ode_coupled, t, + integrator) where {Condition, Affect! <: AnalysisCallbackCoupled} + analysis_callback_coupled = cb_coupled.affect! + semi_coupled = integrator.p + du_ode_coupled = first(get_tmp_cache(integrator)) + + # Loop over coupled systems' callbacks and initialize them individually + for i in eachsystem(semi_coupled) + cb = analysis_callback_coupled.callbacks[i] + semi = semi_coupled.semis[i] + u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) + du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled) + initialize!(cb, u_ode, du_ode, t, integrator, semi) + end end - # This method gets called from OrdinaryDiffEq's `solve(...)` function (analysis_callback_coupled::AnalysisCallbackCoupled)(integrator) - semi_coupled = integrator.p - u_ode_coupled = integrator.u - du_ode_coupled = first(get_tmp_cache(integrator)) - - # Loop over coupled systems' callbacks and call them individually - for i in eachsystem(semi_coupled) - @unpack condition = analysis_callback_coupled.callbacks[i] - analysis_callback = analysis_callback_coupled.callbacks[i].affect! - u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) - - # Check condition and skip callback if it is not yet its turn - if !condition(u_ode, integrator.t, integrator) - continue + semi_coupled = integrator.p + u_ode_coupled = integrator.u + du_ode_coupled = first(get_tmp_cache(integrator)) + + # Loop over coupled systems' callbacks and call them individually + for i in eachsystem(semi_coupled) + @unpack condition = analysis_callback_coupled.callbacks[i] + analysis_callback = analysis_callback_coupled.callbacks[i].affect! + u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) + + # Check condition and skip callback if it is not yet its turn + if !condition(u_ode, integrator.t, integrator) + continue + end + + semi = semi_coupled.semis[i] + du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled) + analysis_callback(u_ode, du_ode, integrator, semi) end - - semi = semi_coupled.semis[i] - du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled) - analysis_callback(u_ode, du_ode, integrator, semi) - end end # used for error checks and EOC analysis -function (cb::DiscreteCallback{Condition,Affect!})(sol) where {Condition, Affect!<:AnalysisCallbackCoupled} - semi_coupled = sol.prob.p - u_ode_coupled = sol.u[end] - @unpack callbacks = cb.affect! - - uEltype = real(semi_coupled) - l2_error_collection = uEltype[] - linf_error_collection = uEltype[] - for i in eachsystem(semi_coupled) - analysis_callback = callbacks[i].affect! - @unpack analyzer = analysis_callback - cache_analysis = analysis_callback.cache - - semi = semi_coupled.semis[i] - u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) - - l2_error, linf_error = calc_error_norms(u_ode, sol.t[end], analyzer, semi, cache_analysis) - append!(l2_error_collection, l2_error) - append!(linf_error_collection, linf_error) - end - - (; l2=l2_error_collection, linf=linf_error_collection) -end +function (cb::DiscreteCallback{Condition, Affect!})(sol) where {Condition, + Affect! <: + AnalysisCallbackCoupled} + semi_coupled = sol.prob.p + u_ode_coupled = sol.u[end] + @unpack callbacks = cb.affect! + + uEltype = real(semi_coupled) + l2_error_collection = uEltype[] + linf_error_collection = uEltype[] + for i in eachsystem(semi_coupled) + analysis_callback = callbacks[i].affect! + @unpack analyzer = analysis_callback + cache_analysis = analysis_callback.cache + + semi = semi_coupled.semis[i] + u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled) + + l2_error, linf_error = calc_error_norms(u_ode, sol.t[end], analyzer, semi, + cache_analysis) + append!(l2_error_collection, l2_error) + append!(linf_error_collection, linf_error) + end + (; l2 = l2_error_collection, linf = linf_error_collection) +end ################################################################################ ### SaveSolutionCallback ################################################################################ # Save mesh for a coupled semidiscretization, which contains multiple meshes internally -function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep=0) - for i in eachsystem(semi) - mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) +function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep = 0) + for i in eachsystem(semi) + mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i]) - if mesh.unsaved_changes - mesh.current_filename = save_mesh_file(mesh, output_directory, system=i) - mesh.unsaved_changes = false + if mesh.unsaved_changes + mesh.current_filename = save_mesh_file(mesh, output_directory, system = i) + mesh.unsaved_changes = false + end end - end end - -@inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode, solution_callback, +@inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode, + solution_callback, integrator) - @unpack semis = semi + @unpack semis = semi - for i in eachsystem(semi) - u_ode_slice = get_system_u_ode(u_ode, i, semi) - save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system=i) - end + for i in eachsystem(semi) + u_ode_slice = get_system_u_ode(u_ode, i, semi) + save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system = i) + end end - ################################################################################ ### StepsizeCallback ################################################################################ # In case of coupled system, use minimum timestep over all systems function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled) - dt = minimum(eachsystem(semi)) do i - u_ode_slice = get_system_u_ode(u_ode, i, semi) - calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) - end + dt = minimum(eachsystem(semi)) do i + u_ode_slice = get_system_u_ode(u_ode, i, semi) + calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i]) + end - return dt + return dt end - ################################################################################ ### Equations ################################################################################ @@ -371,140 +364,148 @@ BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64) !!! warning "Experimental code" This is an experimental feature and can change any time. """ -mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype<:Real, Indices} - # NDIMST2M1 == NDIMS * 2 - 1 - # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] - u_boundary ::Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 - other_semi_index ::Int - other_orientation::Int - indices ::Indices - - function BoundaryConditionCoupled(other_semi_index, indices, uEltype) - NDIMS = length(indices) - u_boundary = Array{uEltype, NDIMS*2-1}(undef, ntuple(_ -> 0, NDIMS*2-1)) - - if indices[1] in (:begin, :end) - other_orientation = 1 - elseif indices[2] in (:begin, :end) - other_orientation = 2 - else # indices[3] in (:begin, :end) - other_orientation = 3 +mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype <: Real, Indices} + # NDIMST2M1 == NDIMS * 2 - 1 + # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j] + u_boundary :: Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1 + other_semi_index :: Int + other_orientation :: Int + indices :: Indices + + function BoundaryConditionCoupled(other_semi_index, indices, uEltype) + NDIMS = length(indices) + u_boundary = Array{uEltype, NDIMS * 2 - 1}(undef, ntuple(_ -> 0, NDIMS * 2 - 1)) + + if indices[1] in (:begin, :end) + other_orientation = 1 + elseif indices[2] in (:begin, :end) + other_orientation = 2 + else # indices[3] in (:begin, :end) + other_orientation = 3 + end + + new{NDIMS, NDIMS * 2 - 1, uEltype, typeof(indices)}(u_boundary, other_semi_index, + other_orientation, indices) end - - new{NDIMS, NDIMS*2-1, uEltype, typeof(indices)}( - u_boundary, other_semi_index, other_orientation, indices) - end end -Base.eltype(boundary_condition::BoundaryConditionCoupled) = eltype(boundary_condition.u_boundary) +function Base.eltype(boundary_condition::BoundaryConditionCoupled) + eltype(boundary_condition.u_boundary) +end function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction, cell_indices, surface_node_indices, surface_flux_function, equations) - # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), - # but we don't have a solver here - u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, surface_node_indices..., cell_indices...], - Val(nvariables(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, equations) - else # u_boundary is "left" of boundary, u_inner is "right" of boundary - flux = surface_flux_function(u_boundary, u_inner, orientation, equations) - end - - return flux -end + # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...), + # but we don't have a solver here + u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v, + surface_node_indices..., + cell_indices...], + Val(nvariables(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, equations) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equations) + end + return flux +end function allocate_coupled_boundary_conditions(semi::AbstractSemidiscretization) - n_boundaries = 2 * ndims(semi) - mesh, equations, solver, _ = mesh_equations_solver_cache(semi) + n_boundaries = 2 * ndims(semi) + mesh, equations, solver, _ = mesh_equations_solver_cache(semi) - for direction in 1:n_boundaries - boundary_condition = semi.boundary_conditions[direction] + for direction in 1:n_boundaries + boundary_condition = semi.boundary_conditions[direction] - allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, solver) - end + allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, + solver) + end end - # Don't do anything for other BCs than BoundaryConditionCoupled -function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, solver) - return nothing +function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations, + solver) + return nothing end # In 2D -function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{2}, +function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{2 + }, direction, mesh, equations, dg::DGSEM) - if direction in (1, 2) - cell_size = size(mesh, 2) - else - cell_size = size(mesh, 1) - end - - uEltype = eltype(boundary_condition) - boundary_condition.u_boundary = Array{uEltype, 3}(undef, nvariables(equations), nnodes(dg), - cell_size) -end + if direction in (1, 2) + cell_size = size(mesh, 2) + else + cell_size = size(mesh, 1) + end + uEltype = eltype(boundary_condition) + boundary_condition.u_boundary = Array{uEltype, 3}(undef, nvariables(equations), + nnodes(dg), + cell_size) +end # Don't do anything for other BCs than BoundaryConditionCoupled function copy_to_coupled_boundary!(boundary_condition, u_ode, semi) - return nothing + return nothing end -function copy_to_coupled_boundary!(boundary_conditions::Union{Tuple, NamedTuple}, u_ode, semi) - for boundary_condition in boundary_conditions - copy_to_coupled_boundary!(boundary_condition, u_ode, semi) - end +function copy_to_coupled_boundary!(boundary_conditions::Union{Tuple, NamedTuple}, u_ode, + semi) + for boundary_condition in boundary_conditions + copy_to_coupled_boundary!(boundary_condition, u_ode, semi) + end end # In 2D -function copy_to_coupled_boundary!(boundary_condition::BoundaryConditionCoupled{2}, u_ode, semi) - @unpack u_indices = semi - @unpack other_semi_index, other_orientation, indices = boundary_condition - - mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) - u = wrap_array(get_system_u_ode(u_ode, other_semi_index, semi), mesh, equations, solver, cache) - - linear_indices = LinearIndices(size(mesh)) - - if other_orientation == 1 - cells = axes(mesh, 2) - else # other_orientation == 2 - cells = axes(mesh, 1) - end - - # Copy solution data to the coupled boundary using "delayed indexing" with - # a start value and a step size to get the correct face and orientation. - node_index_range = eachnode(solver) - i_node_start, i_node_step = index_to_start_step_2d(indices[1], node_index_range) - j_node_start, j_node_step = index_to_start_step_2d(indices[2], node_index_range) - - i_cell_start, i_cell_step = index_to_start_step_2d(indices[1], axes(mesh, 1)) - j_cell_start, j_cell_step = index_to_start_step_2d(indices[2], axes(mesh, 2)) - - i_cell = i_cell_start - j_cell = j_cell_start - - for cell in cells - i_node = i_node_start - j_node = j_node_start - - for i in eachnode(solver) - for v in 1:size(u, 1) - boundary_condition.u_boundary[v, i, cell] = u[v, i_node, j_node, - linear_indices[i_cell, j_cell]] - end - i_node += i_node_step - j_node += j_node_step +function copy_to_coupled_boundary!(boundary_condition::BoundaryConditionCoupled{2}, u_ode, + semi) + @unpack u_indices = semi + @unpack other_semi_index, other_orientation, indices = boundary_condition + + mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index]) + u = wrap_array(get_system_u_ode(u_ode, other_semi_index, semi), mesh, equations, solver, + cache) + + linear_indices = LinearIndices(size(mesh)) + + if other_orientation == 1 + cells = axes(mesh, 2) + else # other_orientation == 2 + cells = axes(mesh, 1) end - i_cell += i_cell_step - j_cell += j_cell_step - end -end + # Copy solution data to the coupled boundary using "delayed indexing" with + # a start value and a step size to get the correct face and orientation. + node_index_range = eachnode(solver) + i_node_start, i_node_step = index_to_start_step_2d(indices[1], node_index_range) + j_node_start, j_node_step = index_to_start_step_2d(indices[2], node_index_range) + + i_cell_start, i_cell_step = index_to_start_step_2d(indices[1], axes(mesh, 1)) + j_cell_start, j_cell_step = index_to_start_step_2d(indices[2], axes(mesh, 2)) + + i_cell = i_cell_start + j_cell = j_cell_start + + for cell in cells + i_node = i_node_start + j_node = j_node_start + + for i in eachnode(solver) + for v in 1:size(u, 1) + boundary_condition.u_boundary[v, i, cell] = u[v, i_node, j_node, + linear_indices[i_cell, + j_cell]] + end + i_node += i_node_step + j_node += j_node_step + end + i_cell += i_cell_step + j_cell += j_cell_step + end +end ################################################################################ ### DGSEM/structured @@ -514,95 +515,96 @@ end boundary_condition::BoundaryConditionCoupled, mesh::StructuredMesh, equations, surface_integral, dg::DG, cache, - direction, node_indices, surface_node_indices, element) - @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements - @unpack surface_flux = surface_integral + direction, node_indices, + surface_node_indices, element) + @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements + @unpack surface_flux = surface_integral - cell_indices = get_boundary_indices(element, orientation, mesh) + cell_indices = get_boundary_indices(element, orientation, mesh) - u_inner = get_node_vars(u, equations, dg, node_indices..., element) + u_inner = get_node_vars(u, equations, dg, node_indices..., element) - # If the mapping is orientation-reversing, the contravariant vectors' orientation - # is reversed as well. The normal vector must be oriented in the direction - # from `left_element` to `right_element`, or the numerical flux will be computed - # incorrectly (downwind direction). - sign_jacobian = sign(inverse_jacobian[node_indices..., element]) + # If the mapping is orientation-reversing, the contravariant vectors' orientation + # is reversed as well. The normal vector must be oriented in the direction + # from `left_element` to `right_element`, or the numerical flux will be computed + # incorrectly (downwind direction). + sign_jacobian = sign(inverse_jacobian[node_indices..., element]) - # Contravariant vector Ja^i is the normal vector - normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, - node_indices..., element) + # Contravariant vector Ja^i is the normal vector + normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors, + node_indices..., element) - # If the mapping is orientation-reversing, the normal vector will be reversed (see above). - # However, the flux now has the wrong sign, since we need the physical flux in normal direction. - flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, - surface_node_indices, surface_flux, equations) + # If the mapping is orientation-reversing, the normal vector will be reversed (see above). + # However, the flux now has the wrong sign, since we need the physical flux in normal direction. + flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices, + surface_node_indices, surface_flux, equations) - for v in eachvariable(equations) - surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] - end + for v in eachvariable(equations) + surface_flux_values[v, surface_node_indices..., direction, element] = flux[v] + end end function get_boundary_indices(element, orientation, mesh::StructuredMesh{2}) - cartesian_indices = CartesianIndices(size(mesh)) - if orientation == 1 - # Get index of element in y-direction - cell_indices = (cartesian_indices[element][2],) - else # orientation == 2 - # Get index of element in x-direction - cell_indices = (cartesian_indices[element][1],) - end - - return cell_indices -end + cartesian_indices = CartesianIndices(size(mesh)) + if orientation == 1 + # Get index of element in y-direction + cell_indices = (cartesian_indices[element][2],) + else # orientation == 2 + # Get index of element in x-direction + cell_indices = (cartesian_indices[element][1],) + end + return cell_indices +end ################################################################################ ### Special elixirs ################################################################################ # Analyze convergence for SemidiscretizationCoupled -function analyze_convergence(errors_coupled, iterations, semi_coupled::SemidiscretizationCoupled) - # Extract errors: the errors are currently stored as - # | iter 1 sys 1 var 1...n | iter 1 sys 2 var 1...n | ... | iter 2 sys 1 var 1...n | ... - # but for calling `analyze_convergence` below, we need the following layout - # sys n: | iter 1 var 1...n | iter 1 var 1...n | ... | iter 2 var 1...n | ... - # That is, we need to extract and join the data for a single system - errors = Dict{Symbol, Vector{Float64}}[] - for i in eachsystem(semi_coupled) - push!(errors, Dict(:l2 => Float64[], :linf => Float64[])) - end - offset = 0 - for iter in 1:iterations, i in eachsystem(semi_coupled) - # Extract information on current semi - semi = semi_coupled.semis[i] - _, equations, _, _ = mesh_equations_solver_cache(semi) - variablenames = varnames(cons2cons, equations) - - # Compute offset - first = offset + 1 - last = offset + length(variablenames) - offset += length(variablenames) - - # Append errors to appropriate storage - append!(errors[i][:l2], errors_coupled[:l2][first:last]) - append!(errors[i][:linf], errors_coupled[:linf][first:last]) - end - - eoc_mean_values = Vector{Dict{Symbol, Any}}(undef, nsystems(semi_coupled)) - for i in eachsystem(semi_coupled) - # Use visual cues to separate output from multiple systems - println() - println("="^100) - println("# System $i") - println("="^100) - - # Extract information on current semi - semi = semi_coupled.semis[i] - _, equations, _, _ = mesh_equations_solver_cache(semi) - variablenames = varnames(cons2cons, equations) - - eoc_mean_values[i] = analyze_convergence(errors[i], iterations, variablenames) - end - - return eoc_mean_values +function analyze_convergence(errors_coupled, iterations, + semi_coupled::SemidiscretizationCoupled) + # Extract errors: the errors are currently stored as + # | iter 1 sys 1 var 1...n | iter 1 sys 2 var 1...n | ... | iter 2 sys 1 var 1...n | ... + # but for calling `analyze_convergence` below, we need the following layout + # sys n: | iter 1 var 1...n | iter 1 var 1...n | ... | iter 2 var 1...n | ... + # That is, we need to extract and join the data for a single system + errors = Dict{Symbol, Vector{Float64}}[] + for i in eachsystem(semi_coupled) + push!(errors, Dict(:l2 => Float64[], :linf => Float64[])) + end + offset = 0 + for iter in 1:iterations, i in eachsystem(semi_coupled) + # Extract information on current semi + semi = semi_coupled.semis[i] + _, equations, _, _ = mesh_equations_solver_cache(semi) + variablenames = varnames(cons2cons, equations) + + # Compute offset + first = offset + 1 + last = offset + length(variablenames) + offset += length(variablenames) + + # Append errors to appropriate storage + append!(errors[i][:l2], errors_coupled[:l2][first:last]) + append!(errors[i][:linf], errors_coupled[:linf][first:last]) + end + + eoc_mean_values = Vector{Dict{Symbol, Any}}(undef, nsystems(semi_coupled)) + for i in eachsystem(semi_coupled) + # Use visual cues to separate output from multiple systems + println() + println("="^100) + println("# System $i") + println("="^100) + + # Extract information on current semi + semi = semi_coupled.semis[i] + _, equations, _, _ = mesh_equations_solver_cache(semi) + variablenames = varnames(cons2cons, equations) + + eoc_mean_values[i] = analyze_convergence(errors[i], iterations, variablenames) + end + + return eoc_mean_values end diff --git a/src/semidiscretization/semidiscretization_euler_gravity.jl b/src/semidiscretization/semidiscretization_euler_gravity.jl index c656251b721..8fe9de1d2b2 100644 --- a/src/semidiscretization/semidiscretization_euler_gravity.jl +++ b/src/semidiscretization/semidiscretization_euler_gravity.jl @@ -475,31 +475,33 @@ end # TODO: Taal decide, where should specific parts like these be? @inline function save_solution_file(u_ode, t, dt, iter, - semi::SemidiscretizationEulerGravity, solution_callback, - element_variables=Dict{Symbol,Any}(); system="") - # If this is called already as part of a multi-system setup (i.e., system is non-empty), - # we build a combined system name - if !isempty(system) - system_euler = system * "_euler" - system_gravity = system * "_gravity" - else - system_euler = "euler" - system_gravity = "gravity" - end - - u_euler = wrap_array_native(u_ode, semi.semi_euler) - filename_euler = save_solution_file(u_euler, t, dt, iter, - mesh_equations_solver_cache(semi.semi_euler)..., - solution_callback, element_variables, - system=system_euler) - - u_gravity = wrap_array_native(semi.cache.u_ode, semi.semi_gravity) - filename_gravity = save_solution_file(u_gravity, t, dt, iter, - mesh_equations_solver_cache(semi.semi_gravity)..., + semi::SemidiscretizationEulerGravity, + solution_callback, + element_variables = Dict{Symbol, Any}(); + system = "") + # If this is called already as part of a multi-system setup (i.e., system is non-empty), + # we build a combined system name + if !isempty(system) + system_euler = system * "_euler" + system_gravity = system * "_gravity" + else + system_euler = "euler" + system_gravity = "gravity" + end + + u_euler = wrap_array_native(u_ode, semi.semi_euler) + filename_euler = save_solution_file(u_euler, t, dt, iter, + mesh_equations_solver_cache(semi.semi_euler)..., solution_callback, element_variables, - system=system_gravity) + system = system_euler) + + u_gravity = wrap_array_native(semi.cache.u_ode, semi.semi_gravity) + filename_gravity = save_solution_file(u_gravity, t, dt, iter, + mesh_equations_solver_cache(semi.semi_gravity)..., + solution_callback, element_variables, + system = system_gravity) - return filename_euler, filename_gravity + return filename_euler, filename_gravity end @inline function (amr_callback::AMRCallback)(u_ode,