From 98874638caa9010da00b00e24d431d9d9f660ed1 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Fri, 18 Sep 2020 11:33:54 +0200 Subject: [PATCH 01/55] Support for 1D --- examples/1d/parameters.toml | 47 + examples/1d/parameters_source_terms.toml | 42 + examples/2d/parameters.toml | 6 +- src/Project.toml | 0 src/equations/1d/linear_scalar_advection.jl | 113 ++ src/equations/equations.jl | 8 +- src/io/io.jl | 16 +- src/run.jl | 93 +- src/solvers/dg/1d/amr.jl | 526 ++++++ src/solvers/dg/1d/containers.jl | 93 + src/solvers/dg/1d/dg.jl | 1747 +++++++++++++++++++ src/solvers/dg/dg.jl | 5 + src/solvers/solvers.jl | 4 +- 13 files changed, 2661 insertions(+), 39 deletions(-) create mode 100644 examples/1d/parameters.toml create mode 100644 examples/1d/parameters_source_terms.toml create mode 100644 src/Project.toml create mode 100644 src/equations/1d/linear_scalar_advection.jl create mode 100644 src/solvers/dg/1d/amr.jl create mode 100644 src/solvers/dg/1d/containers.jl create mode 100644 src/solvers/dg/1d/dg.jl diff --git a/examples/1d/parameters.toml b/examples/1d/parameters.toml new file mode 100644 index 00000000000..5bcfd83e6d2 --- /dev/null +++ b/examples/1d/parameters.toml @@ -0,0 +1,47 @@ +#################################################################################################### +# Simulation +ndims = 1 +# equations = "CompressibleEulerEquations" +equations = "LinearScalarAdvectionEquation" +advectionvelocity = [1.0] # Need only for linarscalaradvection + +initial_conditions = "initial_conditions_convergence_test" +#initial_conditions = "initial_conditions_constant" +#initial_conditions = "initial_conditions_linear_x_y" +surface_flux = "flux_lax_friedrichs" +# source_terms = +t_start = 0.0 +t_end = 1.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.2 +n_steps_max = 10000 +analysis_interval = 100 +extra_analysis_quantities = ["entropy", "energy_total"] + + +#################################################################################################### +# Mesh +n_cells_max = 30000 +coordinates_min = [-1] +coordinates_max = [1] +initial_refinement_level = 4 +# refinement_patches = [ +# {type = "box", coordinates_min = [-0.5, -0.5], coordinates_max = [0.5, 0.5]}, +# {type = "box", coordinates_min = [-0.1, -0.1], coordinates_max = [0.1, 0.1]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 10 diff --git a/examples/1d/parameters_source_terms.toml b/examples/1d/parameters_source_terms.toml new file mode 100644 index 00000000000..99b4a74c141 --- /dev/null +++ b/examples/1d/parameters_source_terms.toml @@ -0,0 +1,42 @@ +#################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" + +initial_conditions = "initial_conditions_convergence_test" +# initial_conditions = "initial_conditions_constant" +surface_flux = "flux_lax_friedrichs" +source_terms = "source_terms_convergence_test" +t_start = 0.0 +t_end = 2.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.6 +n_steps_max = 10000 +analysis_interval = 100 + + +#################################################################################################### +# Mesh +n_cells_max = 10000 +coordinates_min = [0] +coordinates_max = [2] +initial_refinement_level = 4 +# refinement_patches = [ +# {type = "box", coordinates_min = 0, coordinates_max = 16}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 100 diff --git a/examples/2d/parameters.toml b/examples/2d/parameters.toml index 252bde76061..a97ba0eb1ae 100644 --- a/examples/2d/parameters.toml +++ b/examples/2d/parameters.toml @@ -6,8 +6,8 @@ equations = "LinearScalarAdvectionEquation" advectionvelocity = [1.0, 1.0] # Need only for linarscalaradvection initial_conditions = "initial_conditions_convergence_test" -# initial_conditions = "initial_conditions_constant" -# initial_conditions = "initial_conditions_linear_x_y" +#initial_conditions = "initial_conditions_constant" +#initial_conditions = "initial_conditions_linear_x_y" surface_flux = "flux_lax_friedrichs" # source_terms = t_start = 0.0 @@ -21,7 +21,7 @@ t_end = 1.0 # Solver solver = "dg" polydeg = 3 -cfl = 0.8 +cfl = 0.2 n_steps_max = 10000 analysis_interval = 100 extra_analysis_quantities = ["entropy", "energy_total"] diff --git a/src/Project.toml b/src/Project.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/equations/1d/linear_scalar_advection.jl b/src/equations/1d/linear_scalar_advection.jl new file mode 100644 index 00000000000..cf28aa78519 --- /dev/null +++ b/src/equations/1d/linear_scalar_advection.jl @@ -0,0 +1,113 @@ + +@doc raw""" + LinearScalarAdvectionEquation1D + +The linear scalar advection equation +```math +\partial_t u + a \partial_1 u = 0 +``` +in one space dimensions with constant velocity `a`. +""" +struct LinearScalarAdvectionEquation1D <: AbstractLinearScalarAdvectionEquation{1, 1} + sources::String + advectionvelocity::SVector{1, Float64} +end + +function LinearScalarAdvectionEquation1D() + sources = parameter("sources", "none") + a = convert(SVector{1,Float64}, parameter("advectionvelocity")) + LinearScalarAdvectionEquation1D(sources, a) +end + + +get_name(::LinearScalarAdvectionEquation1D) = "LinearScalarAdvectionEquation1D" +varnames_cons(::LinearScalarAdvectionEquation1D) = SVector("scalar") +varnames_prim(::LinearScalarAdvectionEquation1D) = SVector("scalar") + + +# Set initial conditions at physical location `x` for time `t` +function initial_conditions_gauss(x, t, equation::LinearScalarAdvectionEquation1D) + # Store translated coordinate for easy use of exact solution + x_trans = x - equation.advectionvelocity * t + + return @SVector [exp(-(x_trans[1]^2 ))] +end + +function initial_conditions_convergence_test(x, t, equation::LinearScalarAdvectionEquation1D) + # Store translated coordinate for easy use of exact solution + x_trans = x - equation.advectionvelocity * t + + c = 1.0 + A = 0.5 + L = 2 + f = 1/L + omega = 2 * pi * f + scalar = c + A * sin(omega * sum(x_trans)) + return @SVector [scalar] +end + +function initial_conditions_sin(x, t, equation::LinearScalarAdvectionEquation1D) + # Store translated coordinate for easy use of exact solution + x_trans = x - equation.advectionvelocity * t + + scalar = sin(2 * pi * x_trans[1]) + return @SVector [scalar] +end + +function initial_conditions_constant(x, t, equation::LinearScalarAdvectionEquation1D) + # Store translated coordinate for easy use of exact solution + x_trans = x - equation.advectionvelocity * t + + return @SVector [2.0] +end + + +function initial_conditions_linear_x(x, t, equation::LinearScalarAdvectionEquation1D) + # Store translated coordinate for easy use of exact solution + x_trans = x - equation.advectionvelocity * t + + return @SVector [x_trans[1]] +end + + + +# Pre-defined source terms should be implemented as +# function source_terms_WHATEVER(ut, u, x, element_id, t, n_nodes, equation::LinearScalarAdvectionEquation2D) + + +# Calculate 1D flux in for a single point +@inline function calcflux(u, orientation, equation::LinearScalarAdvectionEquation1D) + a = equation.advectionvelocity[orientation] + return a * u +end + + +function flux_lax_friedrichs(u_ll, u_rr, orientation, equation::LinearScalarAdvectionEquation1D) + a = equation.advectionvelocity[orientation] + return 0.5 * ( a * (u_ll + u_rr) - abs(a) * (u_rr - u_ll) ) +end + + +# Determine maximum stable time step based on polynomial degree and CFL number +function calc_max_dt(u, element_id, invjacobian, cfl, + equation::LinearScalarAdvectionEquation1D, dg) + λ_max = maximum(abs, equation.advectionvelocity) + return cfl * 2 / (nnodes(dg) * invjacobian * λ_max) +end + + +# Convert conservative variables to primitive +@inline cons2prim(u, equation::LinearScalarAdvectionEquation1D) = u + +# Convert conservative variables to entropy variables +@inline cons2entropy(u, equation::LinearScalarAdvectionEquation1D) = u + + +# Calculate entropy for a conservative state `cons` +@inline entropy(u::Real, ::LinearScalarAdvectionEquation1D) = 0.5 * u^2 +@inline entropy(u, equation::LinearScalarAdvectionEquation1D) = entropy(u[1], equation) + + +# Calculate total energy for a conservative state `cons` +@inline energy_total(u::Real, ::LinearScalarAdvectionEquation1D) = 0.5 * u^2 +@inline energy_total(u, equation::LinearScalarAdvectionEquation1D) = energy_total(u[1], equation) diff --git a/src/equations/equations.jl b/src/equations/equations.jl index fd522b9d501..8256c12efda 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -22,7 +22,9 @@ end # Create an instance of a system of equation type based on a given name function make_equations(name::String, ndims_) if name == "LinearScalarAdvectionEquation" - if ndims_ == 2 + if ndims_ == 1 + return LinearScalarAdvectionEquation1D() + elseif ndims_ == 2 return LinearScalarAdvectionEquation2D() elseif ndims_ == 3 return LinearScalarAdvectionEquation3D() @@ -30,6 +32,8 @@ function make_equations(name::String, ndims_) error("Unsupported number of spatial dimensions: ", ndims_) end elseif name == "CompressibleEulerEquations" + #if ndims_ == 1 + # return CompressibleEulerEquations1D() if ndims_ == 2 return CompressibleEulerEquations2D() elseif ndims_ == 3 @@ -88,11 +92,13 @@ end # Linear scalar advection abstract type AbstractLinearScalarAdvectionEquation{NDIMS, NVARS} <: AbstractEquation{NDIMS, NVARS} end +include("1d/linear_scalar_advection.jl") include("2d/linear_scalar_advection.jl") include("3d/linear_scalar_advection.jl") # CompressibleEulerEquations abstract type AbstractCompressibleEulerEquations{NDIMS, NVARS} <: AbstractEquation{NDIMS, NVARS} end +#include("1d/compressible_euler.jl") include("2d/compressible_euler.jl") include("3d/compressible_euler.jl") diff --git a/src/io/io.jl b/src/io/io.jl index 7c320f0055a..5215bfec2b8 100644 --- a/src/io/io.jl +++ b/src/io/io.jl @@ -38,7 +38,9 @@ function load_restart_file!(dg::AbstractDg, restart_filename) # Read variable println("Reading variables_$v ($name)...") - if ndims(dg) == 2 + if ndims(dg) == 1 + dg.elements.u[v, :, :] = read(file["variables_$v"]) + elseif ndims(dg) == 2 dg.elements.u[v, :, :, :] = read(file["variables_$v"]) elseif ndims(dg) == 3 dg.elements.u[v, :, :, :, :] = read(file["variables_$v"]) @@ -88,7 +90,9 @@ function save_restart_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep) # Store each variable of the solution for v in 1:nvariables(dg) # Convert to 1D array - if ndims(dg) == 2 + if ndims(dg) == 1 + file["variables_$v"] = vec(data[v, :, :]) + elseif ndims(dg) == 2 file["variables_$v"] = vec(data[v, :, :, :]) elseif ndims(dg) == 3 file["variables_$v"] = vec(data[v, :, :, :, :]) @@ -138,7 +142,9 @@ function save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep, attrs(file)["timestep"] = timestep # Add coordinates as 1D arrays - if ndims(dg) == 2 + if ndims(dg) == 1 + file["x"] = vec(dg.elements.node_coordinates[1, :, :]) + elseif ndims(dg) == 2 file["x"] = vec(dg.elements.node_coordinates[1, :, :, :]) file["y"] = vec(dg.elements.node_coordinates[2, :, :, :]) elseif ndims(dg) == 3 @@ -168,7 +174,9 @@ function save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep, # Store each variable of the solution for v in 1:nvariables(dg) # Convert to 1D array - if ndims(dg) == 2 + if ndims(dg) == 1 + file["variables_$v"] = vec(data[v, :, :]) + elseif ndims(dg) == 2 file["variables_$v"] = vec(data[v, :, :, :]) elseif ndims(dg) == 3 file["variables_$v"] = vec(data[v, :, :, :, :]) diff --git a/src/run.jl b/src/run.jl index 21c0934fcb2..50571a6d87a 100644 --- a/src/run.jl +++ b/src/run.jl @@ -195,35 +195,68 @@ function init_simulation() s *= "| | AMR interval: $amr_interval\n" s *= "| | adapt ICs: $(adapt_initial_conditions ? "yes" : "no")\n" end - s *= """| n_steps_max: $n_steps_max - | time integration: $(get_name(time_integration_function)) - | restart interval: $restart_interval - | solution interval: $solution_interval - | #parallel threads: $(Threads.nthreads()) - | - | Solver - | | solver: $solver_name - | | polydeg: $polydeg - | | CFL: $cfl - | | volume integral: $(get_name(solver.volume_integral_type)) - | | volume flux: $(get_name(solver.volume_flux_function)) - | | surface flux: $(get_name(solver.surface_flux_function)) - | | #elements: $(solver.n_elements) - | | #interfaces: $(solver.n_interfaces) - | | #boundaries: $(solver.n_boundaries) - | | #l2mortars: $(solver.n_l2mortars) - | | #DOFs: $(ndofs(solver)) - | - | Mesh - | | #cells: $(length(mesh.tree)) - | | #leaf cells: $n_leaf_cells - | | minimum level: $min_level - | | maximum level: $max_level - | | domain center: $(join(domain_center, ", ")) - | | domain length: $domain_length - | | minimum dx: $min_dx - | | maximum dx: $max_dx - """ + if ndims_ > 1 + s *= """| n_steps_max: $n_steps_max + | time integration: $(get_name(time_integration_function)) + | restart interval: $restart_interval + | solution interval: $solution_interval + | #parallel threads: $(Threads.nthreads()) + | + | Solver + | | solver: $solver_name + | | polydeg: $polydeg + | | CFL: $cfl + | | volume integral: $(get_name(solver.volume_integral_type)) + | | volume flux: $(get_name(solver.volume_flux_function)) + | | surface flux: $(get_name(solver.surface_flux_function)) + | | #elements: $(solver.n_elements) + | | #interfaces: $(solver.n_interfaces) + | | #boundaries: $(solver.n_boundaries) + | | #l2mortars: $(solver.n_l2mortars) + | | #DOFs: $(ndofs(solver)) + | + | Mesh + | | #cells: $(length(mesh.tree)) + | | #leaf cells: $n_leaf_cells + | | minimum level: $min_level + | | maximum level: $max_level + | | domain center: $(join(domain_center, ", ")) + | | domain length: $domain_length + | | minimum dx: $min_dx + | | maximum dx: $max_dx + """ + else + s *= """| n_steps_max: $n_steps_max + | time integration: $(get_name(time_integration_function)) + | restart interval: $restart_interval + | solution interval: $solution_interval + | #parallel threads: $(Threads.nthreads()) + | + | Solver + | | solver: $solver_name + | | polydeg: $polydeg + | | CFL: $cfl + | | volume integral: $(get_name(solver.volume_integral_type)) + | | volume flux: $(get_name(solver.volume_flux_function)) + | | surface flux: $(get_name(solver.surface_flux_function)) + | | #elements: $(solver.n_elements) + | | #interfaces: $(solver.n_interfaces) + | | #boundaries: $(solver.n_boundaries) + | | #DOFs: $(ndofs(solver)) + | + | Mesh + | | #cells: $(length(mesh.tree)) + | | #leaf cells: $n_leaf_cells + | | minimum level: $min_level + | | maximum level: $max_level + | | domain center: $(join(domain_center, ", ")) + | | domain length: $domain_length + | | minimum dx: $min_dx + | | maximum dx: $max_dx + """ + + end + println() println(s) @@ -592,7 +625,7 @@ function compute_jacobian_dg(parameters_file; verbose=false, parameters...) res_m = vec(dg.elements.u_t) |> copy # restore linearisation state dg.elements.u[idx] = u0[idx] - # central second order finite difference + # central second order finite difference J[:,idx] = (res_p - res_m) / (2 * epsilon) end diff --git a/src/solvers/dg/1d/amr.jl b/src/solvers/dg/1d/amr.jl new file mode 100644 index 00000000000..40f04b2d98d --- /dev/null +++ b/src/solvers/dg/1d/amr.jl @@ -0,0 +1,526 @@ +# This file contains functions that are related to the AMR capabilities of the DG solver + +# Refine elements in the DG solver based on a list of cell_ids that should be refined +function refine!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, + cells_to_refine::AbstractArray{Int}) where {Eqn, NVARS, POLYDEG} + # Return early if there is nothing to do + if isempty(cells_to_refine) + return + end + + # Determine for each existing element whether it needs to be refined + needs_refinement = falses(nelements(dg.elements)) + tree = mesh.tree + # The "Ref(...)" is such that we can vectorize the search but not the array that is searched + elements_to_refine = searchsortedfirst.(Ref(dg.elements.cell_ids[1:nelements(dg.elements)]), + cells_to_refine) + needs_refinement[elements_to_refine] .= true + + # Retain current solution data + old_n_elements = nelements(dg.elements) + old_u = dg.elements.u + + # Get new list of leaf cells + leaf_cell_ids = leaf_cells(tree) + + # Initialize new elements container + elements = init_elements(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG)) + n_elements = nelements(elements) + + # Loop over all elements in old container and either copy them or refine them + element_id = 1 + for old_element_id in 1:old_n_elements + if needs_refinement[old_element_id] + # Refine element and store solution directly in new data structure + refine_element!(elements.u, element_id, old_u, old_element_id, dg) + element_id += 2^ndims(dg) + else + # Copy old element data to new element container + @views elements.u[:, :, element_id] .= old_u[:, :, old_element_id] + element_id += 1 + end + end + + # Initialize new interfaces container + interfaces = init_interfaces(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + n_interfaces = ninterfaces(interfaces) + + # Initialize boundaries + boundaries = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + n_boundaries = nboundaries(boundaries) + + + + # Sanity check + if isperiodic(mesh.tree) #&& n_l2mortars == 0 && n_ecmortars == 0 + @assert n_interfaces == 1*n_elements ("For 1D and periodic domains and conforming elements, " + * "n_surf must be the same as 1*n_elem") + end + + # Update DG instance with new data + dg.elements = elements + dg.n_elements = n_elements + dg.interfaces = interfaces + dg.n_interfaces = n_interfaces + dg.boundaries = boundaries + dg.n_boundaries = n_boundaries +end + + +# Refine solution data u for an element, using L2 projection (interpolation) +function refine_element!(u, element_id, old_u, old_element_id, dg::Dg1D) + # Store new element ids + lower_left_id = element_id + lower_right_id = element_id + 1 + #upper_left_id = element_id + 2 + #upper_right_id = element_id + 3 + + # Interpolate to lower left element + for i in 1:nnodes(dg) + acc = zero(get_node_vars(u, dg, i, element_id)) + for k in 1:nnodes(dg) + acc += get_node_vars(old_u, dg, k, old_element_id) # * forward_lower[i, k] #TODO + end + set_node_vars!(u, acc, dg, i, lower_left_id) + end + + # Interpolate to bottom lower left element + #multiply_dimensionwise!( + # view(u, :, :, lower_left_id), forward_lower, + # view(old_u, :, :, old_element_id)) + + # Interpolate to lower right element + for i in 1:nnodes(dg) + acc = zero(get_node_vars(u, dg, i, element_id)) + for k in 1:nnodes(dg) + acc += get_node_vars(old_u, dg, k, old_element_id)# * forward_upper[i, k] + end + set_node_vars!(u, acc, dg, i, lower_right_id) + end + + # Interpolate to lower right element + #multiply_dimensionwise!( + # view(u, :, :, lower_right_id), forward_upper, + # view(old_u, :, :, old_element_id)) + + # Interpolate to upper left element + #for j in 1:nnodes(dg), i in 1:nnodes(dg) + # acc = zero(get_node_vars(u, dg, i, j, element_id)) + # for l in 1:nnodes(dg), k in 1:nnodes(dg) + # acc += get_node_vars(old_u, dg, k, l, old_element_id) * forward_lower[i, k] * forward_upper[j, l] + # end + # set_node_vars!(u, acc, dg, i, j, upper_left_id) + #end + + # Interpolate to upper right element + #for j in 1:nnodes(dg), i in 1:nnodes(dg) + # acc = zero(get_node_vars(u, dg, i, j, element_id)) + # for l in 1:nnodes(dg), k in 1:nnodes(dg) + # acc += get_node_vars(old_u, dg, k, l, old_element_id) * forward_upper[i, k] * forward_upper[j, l] + # end + # set_node_vars!(u, acc, dg, i, j, upper_right_id) + #end +end + + +# Coarsen elements in the DG solver based on a list of cell_ids that should be removed +function coarsen!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, + child_cells_to_coarsen::AbstractArray{Int}) where {Eqn, NVARS, POLYDEG} + # Return early if there is nothing to do + if isempty(child_cells_to_coarsen) + return + end + + # Determine for each old element whether it needs to be removed + to_be_removed = falses(nelements(dg.elements)) + # The "Ref(...)" is such that we can vectorize the search but not the array that is searched + elements_to_remove = searchsortedfirst.(Ref(dg.elements.cell_ids[1:nelements(dg.elements)]), + child_cells_to_coarsen) + to_be_removed[elements_to_remove] .= true + + # Retain current solution data + old_n_elements = nelements(dg.elements) + old_u = dg.elements.u + + # Get new list of leaf cells + leaf_cell_ids = leaf_cells(mesh.tree) + + # Initialize new elements container + elements = init_elements(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG)) + n_elements = nelements(elements) + + # Loop over all elements in old container and either copy them or coarsen them + skip = 0 + element_id = 1 + for old_element_id in 1:old_n_elements + # If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements + if skip > 0 + skip -= 1 + continue + end + + if to_be_removed[old_element_id] + # If an element is to be removed, sanity check if the following elements + # are also marked - otherwise there would be an error in the way the + # cells/elements are sorted + @assert all(to_be_removed[old_element_id:(old_element_id+2^ndims(dg)-1)]) "bad cell/element order" + + # Coarsen elements and store solution directly in new data structure + coarsen_elements!(elements.u, element_id, old_u, old_element_id, dg) # dg.l2mortar_reverse_upper, dg.l2mortar_reverse_lower, dg + element_id += 1 + skip = 2^ndims(dg) - 1 + else + # Copy old element data to new element container + @views elements.u[:, :, element_id] .= old_u[:, :, old_element_id] + element_id += 1 + end + end + + # Initialize new interfaces container + interfaces = init_interfaces(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + n_interfaces = ninterfaces(interfaces) + + # Initialize boundaries + boundaries = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + n_boundaries = nboundaries(boundaries) + + # Sanity check + if isperiodic(mesh.tree) #&& n_l2mortars == 0 && n_ecmortars == 0 + @assert n_interfaces == 1*n_elements ("For 1D and periodic domains and conforming elements, " + * "n_surf must be the same as 1*n_elem") + end + + # Update DG instance with new data + dg.elements = elements + dg.n_elements = n_elements + dg.interfaces = interfaces + dg.n_interfaces = n_interfaces + dg.boundaries = boundaries + dg.n_boundaries = n_boundaries +end + + +# Coarsen solution data u for four elements, using L2 projection +function coarsen_elements!(u, element_id, old_u, old_element_id, dg::Dg1D) + # Store old element ids + lower_left_id = old_element_id + lower_right_id = old_element_id + 1 + #upper_left_id = old_element_id + 2 + #upper_right_id = old_element_id + 3 + + for i in 1:nnodes(dg) + acc = zero(get_node_vars(u, dg, i, element_id)) + + # Project from lower left element + for k in 1:nnodes(dg) + acc += get_node_vars(old_u, dg, k, lower_left_id) #* reverse_lower[i, k] #* reverse_lower[j, l] #TODO + end + + # Project from lower right element + for k in 1:nnodes(dg) + acc += get_node_vars(old_u, dg, k, lower_right_id) #* reverse_upper[i, k] #* reverse_lower[j, l] + end + + # Project from upper left element + #for l in 1:nnodes(dg), k in 1:nnodes(dg) + # acc += get_node_vars(old_u, dg, k, l, upper_left_id) * reverse_lower[i, k] * reverse_upper[j, l] + #end + + # Project from upper right element + #for l in 1:nnodes(dg), k in 1:nnodes(dg) + # acc += get_node_vars(old_u, dg, k, l, upper_right_id) * reverse_upper[i, k] * reverse_upper[j, l] + #end + + # Update value + set_node_vars!(u, acc, dg, i, element_id) + end +end + + +# Calculate an AMR indicator value for each element/leaf cell +# +# The indicator value λ ∈ [-1,1] is ≈ -1 for cells that should be coarsened, ≈ +# 0 for cells that should remain as-is, and ≈ 1 for cells that should be +# refined. +# +# Note: The implementation here implicitly assumes that we have an element for +# each leaf cell and that they are in the same order. +# +# FIXME: This is currently implemented for each test case - we need something +# appropriate that is both equation and test case independent +function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) + lambda = zeros(dg.n_elements) + + if dg.amr_indicator === :gauss + base_level = 4 + max_level = 6 + threshold_high = 0.6 + threshold_low = 0.1 + + # Iterate over all elements + for element_id in 1:dg.n_elements + # Determine target level from peak value + peak = maximum(dg.elements.u[:, :, element_id]) + if peak > threshold_high + target_level = max_level + elseif peak > threshold_low + target_level = max_level - 1 + else + target_level = base_level + end + + # Compare target level with actual level to set indicator + cell_id = dg.elements.cell_ids[element_id] + actual_level = mesh.tree.levels[cell_id] + if actual_level < target_level + lambda[element_id] = 1.0 + elseif actual_level > target_level + lambda[element_id] = -1.0 + else + lambda[element_id] = 0.0 + end + end + elseif dg.amr_indicator === :isentropic_vortex + base_level = 3 + max_level = 5 + radius_high = 2 + radius_low = 3 + + # Domain size needed to handle periodicity + domain_length = mesh.tree.length_level_0 + + # Get analytical vortex center (based on assumption that center=[0.0] + # at t=0.0 and that we stop after one period) + if time < domain_length/2 + center = Float64[time] + else + center = Float64[time-domain_length] + end + + # Iterate over all elements + for element_id in 1:dg.n_elements + cell_id = dg.elements.cell_ids[element_id] + r = periodic_distance_1d(mesh.tree.coordinates[:, cell_id], center, domain_length) + if r < radius_high + target_level = max_level + elseif r < radius_low + target_level = max_level - 1 + else + target_level = base_level + end + + # Compare target level with actual level to set indicator + cell_id = dg.elements.cell_ids[element_id] + actual_level = mesh.tree.levels[cell_id] + if actual_level < target_level + lambda[element_id] = 1.0 + elseif actual_level > target_level + lambda[element_id] = -1.0 + else + lambda[element_id] = 0.0 + end + end + elseif dg.amr_indicator === :khi + base_level = 4 + max_level = 6 + # to make the simulation smaller and quicker wall clock time, choose super_max_level = 6 + super_max_level = 7 + blending_factor_threshold0 = 0.3 + blending_factor_threshold1 = 0.003 + blending_factor_threshold2 = 0.0003 + + # (Re-)initialize element variable storage for blending factor + if (!haskey(dg.element_variables, :amr_indicator_values) || + length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) + dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) + end + if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || + length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) + dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) + end + + alpha = dg.element_variables[:amr_indicator_values] + alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] + calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, false, + Val(:density), dg.thread_cache, dg) + + # Iterate over all elements + for element_id in 1:dg.n_elements + cell_id = dg.elements.cell_ids[element_id] + actual_level = mesh.tree.levels[cell_id] + target_level = actual_level + if alpha[element_id] >= blending_factor_threshold0 + target_level = super_max_level + elseif alpha[element_id] >= blending_factor_threshold1 + target_level = max_level + elseif alpha[element_id] <= blending_factor_threshold2 + target_level = base_level + end + + # Compare target level with actual level to set indicator + if actual_level < target_level + lambda[element_id] = 1.0 + elseif actual_level > target_level + lambda[element_id] = -1.0 + else + lambda[element_id] = 0.0 + end + end + elseif dg.amr_indicator === :blob + base_level = 4 + max_level = 7 + super_max_level = 7 + blending_factor_threshold0 = 0.3 + blending_factor_threshold1 = 0.003 + blending_factor_threshold2 = 0.0003 + + # (Re-)initialize element variable storage for blending factor + if (!haskey(dg.element_variables, :amr_indicator_values) || + length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) + dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) + end + if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || + length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) + dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) + end + + alpha = dg.element_variables[:amr_indicator_values] + alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] + calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, false, + Val(:density), dg.thread_cache, dg) + + # (Re-)initialize element variable storage for blending factor + if (!haskey(dg.element_variables, :blending_factor) || + length(dg.element_variables[:blending_factor]) != dg.n_elements) + dg.element_variables[:blending_factor] = Vector{Float64}(undef, dg.n_elements) + end + if (!haskey(dg.element_variables, :blending_factor_tmp) || + length(dg.element_variables[:blending_factor_tmp]) != dg.n_elements) + dg.element_variables[:blending_factor_tmp] = Vector{Float64}(undef, dg.n_elements) + end + + alpha1 = dg.element_variables[:blending_factor] + alpha1_tmp = dg.element_variables[:blending_factor_tmp] + calc_blending_factors!(alpha1, alpha1_tmp, dg.elements.u, dg.shock_alpha_max, dg.shock_alpha_min, true, + dg.shock_indicator_variable, dg.thread_cache, dg) + + # Iterate over all elements + for element_id in 1:dg.n_elements + cell_id = dg.elements.cell_ids[element_id] + actual_level = mesh.tree.levels[cell_id] + target_level = actual_level + # adapt for the amr indicator + if alpha[element_id] >= blending_factor_threshold0 + target_level = super_max_level + elseif alpha[element_id] >= blending_factor_threshold1 + target_level = max_level + elseif alpha[element_id] <= blending_factor_threshold2 + target_level = base_level + end + # make sure that a highly troubled shock cell is not coarsened + if isapprox.(dg.shock_alpha_max, alpha1[element_id], atol=1e-12) + target_level = max_level + end + # Compare target level with actual level to set indicator + if actual_level < target_level + lambda[element_id] = 1.0 + elseif actual_level > target_level + lambda[element_id] = -1.0 + else + lambda[element_id] = 0.0 + end + end + elseif dg.amr_indicator === :blast_wave + base_level = 4 + max_level = 6 + blending_factor_threshold = 0.01 + + # (Re-)initialize element variable storage for blending factor + if (!haskey(dg.element_variables, :amr_indicator_values) || + length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) + dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) + end + if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || + length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) + dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) + end + + alpha = dg.element_variables[:amr_indicator_values] + alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] + calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, dg.amr_alpha_smooth, + Val(:density_pressure), dg.thread_cache, dg) + + # Iterate over all elements + for element_id in 1:dg.n_elements + if alpha[element_id] > blending_factor_threshold + target_level = max_level + else + target_level = base_level + end + + # Compare target level with actual level to set indicator + cell_id = dg.elements.cell_ids[element_id] + actual_level = mesh.tree.levels[cell_id] + if actual_level < target_level + lambda[element_id] = 1.0 + elseif actual_level > target_level + lambda[element_id] = -1.0 + else + lambda[element_id] = 0.0 + end + end + elseif dg.amr_indicator === :sedov_self_gravity + base_level = 2 + max_level = 8 + blending_factor_threshold = 0.0003 + + # (Re-)initialize element variable storage for blending factor + if (!haskey(dg.element_variables, :amr_indicator_values) || + length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) + dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) + end + if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || + length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) + dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) + end + + alpha = dg.element_variables[:amr_indicator_values] + alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] + calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, dg.amr_alpha_smooth, + Val(:density_pressure), dg.thread_cache, dg) + + # Iterate over all elements + for element_id in 1:dg.n_elements + if alpha[element_id] > blending_factor_threshold + target_level = max_level + else + target_level = base_level + end + + # Compare target level with actual level to set indicator + cell_id = dg.elements.cell_ids[element_id] + actual_level = mesh.tree.levels[cell_id] + if actual_level < target_level + lambda[element_id] = 1.0 + elseif actual_level > target_level + lambda[element_id] = -1.0 + else + lambda[element_id] = 0.0 + end + end + else + error("unknown AMR indicator '$(dg.amr_indicator)'") + end + + return lambda +end + + +# For periodic domains, distance between two points must take into account +# periodic extensions of the domain +function periodic_distance_1d(coordinates, center, domain_length) + dx = abs.(coordinates - center) + dx_periodic = min.(dx, domain_length .- dx) + return abs(dx_periodic) + #sqrt(sum(dx_periodic.^2)) +end diff --git a/src/solvers/dg/1d/containers.jl b/src/solvers/dg/1d/containers.jl new file mode 100644 index 00000000000..77ed2b0baa9 --- /dev/null +++ b/src/solvers/dg/1d/containers.jl @@ -0,0 +1,93 @@ + +# Container data structure (structure-of-arrays style) for DG elements +struct ElementContainer1D{NVARS, POLYDEG} <: AbstractContainer + u::Array{Float64, 3} # [variables, i, elements] + u_t::Array{Float64, 3} # [variables, i, elements] + u_tmp2::Array{Float64, 3} # [variables, i, elements] + u_tmp3::Array{Float64, 3} # [variables, i, elements] + inverse_jacobian::Vector{Float64} # [elements] + node_coordinates::Array{Float64, 3} # [orientation, i, elements] + surface_ids::Matrix{Int} # [direction, elements] + surface_flux_values::Array{Float64, 3} # [variables, direction, elements] #i weg ? + cell_ids::Vector{Int} # [elements] +end + + +function ElementContainer1D{NVARS, POLYDEG}(capacity::Integer) where {NVARS, POLYDEG} + # Initialize fields with defaults + n_nodes = POLYDEG + 1 + u = fill(NaN, NVARS, n_nodes, capacity) + u_t = fill(NaN, NVARS, n_nodes, capacity) + # u_rungakutta is initialized to non-NaN since it is used directly + u_tmp2 = fill(0.0, NVARS, n_nodes, capacity) + u_tmp3 = fill(0.0, NVARS, n_nodes, capacity) + inverse_jacobian = fill(NaN, capacity) + node_coordinates = fill(NaN, 1, n_nodes, capacity) + surface_ids = fill(typemin(Int), 2 * 1, capacity) + surface_flux_values = fill(NaN, NVARS, 2 * 1, capacity) + cell_ids = fill(typemin(Int), capacity) + + elements = ElementContainer1D{NVARS, POLYDEG}(u, u_t, u_tmp2, u_tmp3, inverse_jacobian, node_coordinates, + surface_ids, surface_flux_values, cell_ids) + + return elements +end + + +# Return number of elements +nelements(elements::ElementContainer1D) = length(elements.cell_ids) + + +# Container data structure (structure-of-arrays style) for DG interfaces +struct InterfaceContainer1D{NVARS, POLYDEG} <: AbstractContainer + u::Array{Float64, 3} # [leftright, variables, i, interfaces] + neighbor_ids::Matrix{Int} # [leftright, interfaces] + orientations::Vector{Int} # [interfaces] +end + + +function InterfaceContainer1D{NVARS, POLYDEG}(capacity::Integer) where {NVARS, POLYDEG} + # Initialize fields with defaults + n_nodes = POLYDEG + 1 + u = fill(NaN, 2, NVARS, capacity) + neighbor_ids = fill(typemin(Int), 2, capacity) + orientations = fill(typemin(Int), capacity) + + interfaces = InterfaceContainer1D{NVARS, POLYDEG}(u, neighbor_ids, orientations) + + return interfaces +end + + +# Return number of interfaces +ninterfaces(interfaces::InterfaceContainer1D) = length(interfaces.orientations) + + +# Container data structure (structure-of-arrays style) for DG boundaries +struct BoundaryContainer1D{NVARS, POLYDEG} <: AbstractContainer + u::Array{Float64, 3} # [leftright, variables,boundaries] + neighbor_ids::Vector{Int} # [boundaries] + orientations::Vector{Int} # [boundaries] + neighbor_sides::Vector{Int} # [boundaries] + node_coordinates::Array{Float64, 2} # [orientation, elements] +end + + +function BoundaryContainer1D{NVARS, POLYDEG}(capacity::Integer) where {NVARS, POLYDEG} + # Initialize fields with defaults + n_nodes = POLYDEG + 1 + u = fill(NaN, 2, NVARS, capacity) + neighbor_ids = fill(typemin(Int), capacity) + orientations = fill(typemin(Int), capacity) + neighbor_sides = fill(typemin(Int), capacity) + node_coordinates = fill(NaN, 1, capacity) + + boundaries = BoundaryContainer1D{NVARS, POLYDEG}(u, neighbor_ids, orientations, neighbor_sides, + node_coordinates) + + return boundaries +end + + +# Return number of boundaries +nboundaries(boundaries::BoundaryContainer1D) = length(boundaries.orientations) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl new file mode 100644 index 00000000000..96beec6aa0e --- /dev/null +++ b/src/solvers/dg/1d/dg.jl @@ -0,0 +1,1747 @@ +# Main DG data structure that contains all relevant data for the DG solver +mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, + SurfaceFlux, VolumeFlux, InitialConditions, SourceTerms, + VolumeIntegralType, ShockIndicatorVariable, + VectorNnodes, MatrixNnodes, MatrixNnodes2, + InverseVandermondeLegendre, + VectorAnalysisNnodes, AnalysisVandermonde} <: AbstractDg{1, POLYDEG} + equations::Eqn + + surface_flux_function::SurfaceFlux + volume_flux_function::VolumeFlux + + initial_conditions::InitialConditions + source_terms::SourceTerms + + elements::ElementContainer1D{NVARS, POLYDEG} + n_elements::Int + + interfaces::InterfaceContainer1D{NVARS, POLYDEG} + n_interfaces::Int + + boundaries::BoundaryContainer1D{NVARS, POLYDEG} + n_boundaries::Int + + nodes::VectorNnodes + weights::VectorNnodes + inverse_weights::VectorNnodes + inverse_vandermonde_legendre::InverseVandermondeLegendre + lhat::MatrixNnodes2 + + volume_integral_type::VolumeIntegralType + dhat::MatrixNnodes + dsplit::MatrixNnodes + dsplit_transposed::MatrixNnodes + + + analysis_nodes::VectorAnalysisNnodes + analysis_weights::VectorAnalysisNnodes + analysis_weights_volume::VectorAnalysisNnodes + analysis_vandermonde::AnalysisVandermonde + analysis_total_volume::Float64 + analysis_quantities::Vector{Symbol} + save_analysis::Bool + analysis_filename::String + + shock_indicator_variable::ShockIndicatorVariable + shock_alpha_max::Float64 + shock_alpha_min::Float64 + shock_alpha_smooth::Bool + amr_indicator::Symbol + amr_alpha_max::Float64 + amr_alpha_min::Float64 + amr_alpha_smooth::Bool + + element_variables::Dict{Symbol, Union{Vector{Float64}, Vector{Int}}} + cache::Dict{Symbol, Any} + thread_cache::Any # to make fully-typed output more readable + initial_state_integrals::Vector{Float64} +end + + +# Convenience constructor to create DG solver instance +function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, volume_flux_function, initial_conditions, source_terms, mesh::TreeMesh{NDIMS}, POLYDEG) where {NDIMS, NVARS} + # Get cells for which an element needs to be created (i.e., all leaf cells) + leaf_cell_ids = leaf_cells(mesh.tree) + + # Initialize element container + elements = init_elements(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG)) + n_elements = nelements(elements) + + # Initialize interface container + interfaces = init_interfaces(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + n_interfaces = ninterfaces(interfaces) + + # Initialize boundaries + boundaries = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + n_boundaries = nboundaries(boundaries) + + + # Sanity checks + if isperiodic(mesh.tree) #&& n_l2mortars == 0 && n_ecmortars == 0 + @assert n_interfaces == 1*n_elements ("For 1D and periodic domains and conforming elements, " + * "n_surf must be the same as 1*n_elem") + end + + # Initialize interpolation data structures + n_nodes = POLYDEG + 1 + nodes, weights = gauss_lobatto_nodes_weights(n_nodes) + inverse_weights = 1 ./ weights + _, inverse_vandermonde_legendre = vandermonde_legendre(nodes) + lhat = zeros(n_nodes, 2) + lhat[:, 1] = calc_lhat(-1.0, nodes, weights) + lhat[:, 2] = calc_lhat( 1.0, nodes, weights) + + # Initialize differentiation operator + volume_integral_type = Val(Symbol(parameter("volume_integral_type", "weak_form", + valid=["weak_form", "split_form", "shock_capturing"]))) + # FIXME: This should be removed as soon as it possible to set solver-specific parameters + if equation isa HyperbolicDiffusionEquations2D && globals[:euler_gravity] + volume_integral_type = Val(:weak_form) + end + dhat = calc_dhat(nodes, weights) + dsplit = calc_dsplit(nodes, weights) + dsplit_transposed = transpose(calc_dsplit(nodes, weights)) + + + # Initialize data structures for error analysis (by default, we use twice the + # number of analysis nodes as the normal solution) + analysis_polydeg = 2 * (n_nodes) - 1 + analysis_nodes, analysis_weights = gauss_lobatto_nodes_weights(analysis_polydeg + 1) + analysis_weights_volume = analysis_weights + analysis_vandermonde = polynomial_interpolation_matrix(nodes, analysis_nodes) + analysis_total_volume = mesh.tree.length_level_0^ndims(mesh) + + # Store which quantities should be analyzed in `analyze_solution` + if parameter_exists("extra_analysis_quantities") + extra_analysis_quantities = Symbol.(parameter("extra_analysis_quantities")) + else + extra_analysis_quantities = Symbol[] + end + analysis_quantities = vcat(collect(Symbol.(default_analysis_quantities(equation))), + extra_analysis_quantities) + + # If analysis should be saved to file, create file with header + save_analysis = parameter("save_analysis", false) + if save_analysis + # Create output directory (if it does not exist) + output_directory = parameter("output_directory", "out") + mkpath(output_directory) + + # Determine filename + analysis_filename = joinpath(output_directory, "analysis.dat") + + # Open file and write header + save_analysis_header(analysis_filename, analysis_quantities, equation) + else + analysis_filename = "" + end + + # Initialize AMR + amr_indicator = Symbol(parameter("amr_indicator", "n/a", + valid=["n/a", "gauss", "isentropic_vortex", "blast_wave", "khi", "blob", "sedov_self_gravity"])) + + # Initialize storage for element variables + element_variables = Dict{Symbol, Union{Vector{Float64}, Vector{Int}}}() + if amr_indicator === :khi || amr_indicator === :blob + element_variables[:amr_indicator_values] = zeros(n_elements) + end + # maximum and minimum alpha for shock capturing + shock_alpha_max = parameter("shock_alpha_max", 0.5) + shock_alpha_min = parameter("shock_alpha_min", 0.001) + shock_alpha_smooth = parameter("shock_alpha_smooth", true) + + # variable used to compute the shock capturing indicator + shock_indicator_variable = Val(Symbol(parameter("shock_indicator_variable", "density_pressure", + valid=["density", "density_pressure", "pressure"]))) + + # maximum and minimum alpha for amr control + amr_alpha_max = parameter("amr_alpha_max", 0.5) + amr_alpha_min = parameter("amr_alpha_min", 0.001) + amr_alpha_smooth = parameter("amr_alpha_smooth", false) + + # Initialize element variables such that they are available in the first solution file + if volume_integral_type === Val(:shock_capturing) + element_variables[:blending_factor] = zeros(n_elements) + end + + # Initialize storage for the cache + cache = Dict{Symbol, Any}() + thread_cache = create_thread_cache_1d(NVARS, POLYDEG+1) + + # Store initial state integrals for conservation error calculation + initial_state_integrals = Vector{Float64}() + + # Create actual DG solver instance + dg = Dg1D( + equation, + surface_flux_function, volume_flux_function, + initial_conditions, source_terms, + elements, n_elements, + interfaces, n_interfaces, + boundaries, n_boundaries, + SVector{POLYDEG+1}(nodes), SVector{POLYDEG+1}(weights), SVector{POLYDEG+1}(inverse_weights), + inverse_vandermonde_legendre, SMatrix{POLYDEG+1,2}(lhat), + volume_integral_type, + SMatrix{POLYDEG+1,POLYDEG+1}(dhat), SMatrix{POLYDEG+1,POLYDEG+1}(dsplit), SMatrix{POLYDEG+1,POLYDEG+1}(dsplit_transposed), + SVector{analysis_polydeg+1}(analysis_nodes), SVector{analysis_polydeg+1}(analysis_weights), SVector{analysis_polydeg+1}(analysis_weights_volume), + analysis_vandermonde, analysis_total_volume, + analysis_quantities, save_analysis, analysis_filename, + shock_indicator_variable, shock_alpha_max, shock_alpha_min, shock_alpha_smooth, + amr_indicator, amr_alpha_max, amr_alpha_min, amr_alpha_smooth, + element_variables, cache, thread_cache, + initial_state_integrals) + + return dg +end + + +function create_thread_cache_1d(n_variables, n_nodes) + # Type alias only for convenience + A3d = Array{Float64, 3} + A2d = Array{Float64, 2} + A2dp1_x = Array{Float64, 2} + + MA1d = MArray{Tuple{n_variables, n_nodes}, Float64} + A1d = Array{Float64, 1} + + # Pre-allocate data structures to speed up computation (thread-safe) #TODO + f1_threaded = A3d[A3d(undef, n_variables, n_nodes, n_nodes) for _ in 1:Threads.nthreads()] + #f2_threaded = A4d[A4d(undef, n_variables, n_nodes, n_nodes, n_nodes) for _ in 1:Threads.nthreads()] + fstar1_threaded = A2dp1_x[A2dp1_x(undef, n_variables, n_nodes+1) for _ in 1:Threads.nthreads()] + #fstar2_threaded = A3dp1_y[A3dp1_y(undef, n_variables, n_nodes, n_nodes+1) for _ in 1:Threads.nthreads()] + fstar_upper_threaded = [MA1d(undef) for _ in 1:Threads.nthreads()] + #fstar_lower_threaded = [MA2d(undef) for _ in 1:Threads.nthreads()] + noncons_diamond_upper_threaded = [MA1d(undef) for _ in 1:Threads.nthreads()] +# noncons_diamond_lower_threaded = [MA2d(undef) for _ in 1:Threads.nthreads()] + + indicator_threaded = [A2d(undef, 1, n_nodes) for _ in 1:Threads.nthreads()] + modal_threaded = [A2d(undef, 1, n_nodes) for _ in 1:Threads.nthreads()] + modal_tmp1_threaded = [A2d(undef, 1, n_nodes) for _ in 1:Threads.nthreads()] + + return (; f1_threaded, + fstar1_threaded, + fstar_upper_threaded, #fstar_lower_threaded, + noncons_diamond_upper_threaded, #noncons_diamond_lower_threaded, + indicator_threaded, modal_threaded, modal_tmp1_threaded) +end + + +# Count the number of interfaces that need to be created +function count_required_interfaces(mesh::TreeMesh{1}, cell_ids) + count = 0 + + # Iterate over all cells + for cell_id in cell_ids + for direction in 1:n_directions(mesh.tree) + # Only count interfaces in positive direction to avoid double counting + if direction % 2 == 1 + continue + end + + # If no neighbor exists, current cell is small or at boundary and thus we need a mortar + if !has_neighbor(mesh.tree, cell_id, direction) + continue + end + + # Skip if neighbor has children + neighbor_id = mesh.tree.neighbor_ids[direction, cell_id] + if has_children(mesh.tree, neighbor_id) + continue + end + + count += 1 + end + end + + return count +end + + +# Count the number of boundaries that need to be created +function count_required_boundaries(mesh::TreeMesh{1}, cell_ids) + count = 0 + + # Iterate over all cells + for cell_id in cell_ids + for direction in 1:n_directions(mesh.tree) + # If neighbor exists, current cell is not at a boundary + if has_neighbor(mesh.tree, cell_id, direction) + continue + end + + # If coarse neighbor exists, current cell is not at a boundary + if has_coarse_neighbor(mesh.tree, cell_id, direction) + continue + end + + # No neighbor exists in this direction -> must be a boundary + count += 1 + end + end + + return count +end + + +# Create element container, initialize element data, and return element container for further use +# +# NVARS: number of variables +# POLYDEG: polynomial degree +function init_elements(cell_ids, mesh::TreeMesh{1}, ::Val{NVARS}, ::Val{POLYDEG}) where {NVARS, POLYDEG} + # Initialize container + n_elements = length(cell_ids) + elements = ElementContainer1D{NVARS, POLYDEG}(n_elements) + + # Store cell ids + elements.cell_ids .= cell_ids + + # Determine node locations + n_nodes = POLYDEG + 1 + nodes, _ = gauss_lobatto_nodes_weights(n_nodes) + + # Calculate inverse Jacobian and node coordinates + for element_id in 1:nelements(elements) + # Get cell id + cell_id = cell_ids[element_id] + + # Get cell length + dx = length_at_cell(mesh.tree, cell_id) + + # Calculate inverse Jacobian as 1/(h/2) + elements.inverse_jacobian[element_id] = 2/dx + + # Calculate node coordinates + for i in 1:n_nodes + elements.node_coordinates[1, i, element_id] = ( + mesh.tree.coordinates[1, cell_id] + dx/2 * nodes[i]) + #elements.node_coordinates[2, i, j, element_id] = ( + # mesh.tree.coordinates[2, cell_id] + dx/2 * nodes[j]) + end + end + + return elements +end + + +# Create interface container, initialize interface data, and return interface container for further use +# +# NVARS: number of variables +# POLYDEG: polynomial degree +function init_interfaces(cell_ids, mesh::TreeMesh{1}, ::Val{NVARS}, ::Val{POLYDEG}, elements) where {NVARS, POLYDEG} + # Initialize container + n_interfaces = count_required_interfaces(mesh, cell_ids) + interfaces = InterfaceContainer1D{NVARS, POLYDEG}(n_interfaces) + + # Connect elements with interfaces + init_interface_connectivity!(elements, interfaces, mesh) + + return interfaces +end + + +# Create boundaries container, initialize boundary data, and return boundaries container +# +# NVARS: number of variables +# POLYDEG: polynomial degree +function init_boundaries(cell_ids, mesh::TreeMesh{1}, ::Val{NVARS}, ::Val{POLYDEG}, elements) where {NVARS, POLYDEG} + # Initialize container + n_boundaries = count_required_boundaries(mesh, cell_ids) + boundaries = BoundaryContainer1D{NVARS, POLYDEG}(n_boundaries) + + # Connect elements with boundaries + init_boundary_connectivity!(elements, boundaries, mesh) + + return boundaries +end + + +# Initialize connectivity between elements and interfaces +function init_interface_connectivity!(elements, interfaces, mesh::TreeMesh{1}) + # Construct cell -> element mapping for easier algorithm implementation + tree = mesh.tree + c2e = zeros(Int, length(tree)) + for element_id in 1:nelements(elements) + c2e[elements.cell_ids[element_id]] = element_id + end + + # Reset interface count + count = 0 + + # Iterate over all elements to find neighbors and to connect via interfaces + for element_id in 1:nelements(elements) + # Get cell id + cell_id = elements.cell_ids[element_id] + + # Loop over directions + for direction in 1:n_directions(mesh.tree) + # Only create interfaces in positive direction + if direction % 2 == 1 + continue + end + + # If no neighbor exists, current cell is small and thus we need a mortar + if !has_neighbor(mesh.tree, cell_id, direction) + continue + end + + # Skip if neighbor has children + neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id] + if has_children(mesh.tree, neighbor_cell_id) + continue + end + + # Create interface between elements (1 -> "left" of interface, 2 -> "right" of interface) + count += 1 + interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id] + interfaces.neighbor_ids[1, count] = element_id + + # Set orientation (x -> 1, y -> 2) + interfaces.orientations[count] = div(direction, 2) + end + end + + @assert count == ninterfaces(interfaces) ("Actual interface count ($count) does not match " * + "expectations $(ninterfaces(interfaces))") +end + + +# Initialize connectivity between elements and boundaries +function init_boundary_connectivity!(elements, boundaries, mesh::TreeMesh{1}) + # Reset boundaries count + count = 0 + + # Iterate over all elements to find missing neighbors and to connect to boundaries + for element_id in 1:nelements(elements) + # Get cell id + cell_id = elements.cell_ids[element_id] + + # Loop over directions + for direction in 1:n_directions(mesh.tree) + # If neighbor exists, current cell is not at a boundary + if has_neighbor(mesh.tree, cell_id, direction) + continue + end + + # If coarse neighbor exists, current cell is not at a boundary + if has_coarse_neighbor(mesh.tree, cell_id, direction) #TODO + continue + end + + # Create boundary + count += 1 + + # Set neighbor element id + boundaries.neighbor_ids[count] = element_id + + # Set neighbor side, which denotes the direction (1 -> negative, 2 -> positive) of the element + if direction in (2) + boundaries.neighbor_sides[count] = 1 + else + boundaries.neighbor_sides[count] = 2 + end + + # Set orientation (x -> 1, y -> 2) + if direction in (1, 2) + boundaries.orientations[count] = 1 + else + boundaries.orientations[count] = 2 + end + + # Store node coordinates + enc = elements.node_coordinates + if direction == 1 # -x direction + boundaries.node_coordinates[:, count] .= enc[:, 1, element_id] + elseif direction == 2 # +x direction + boundaries.node_coordinates[:, count] .= enc[:, end, element_id] + #elseif direction == 3 # -y direction + # boundaries.node_coordinates[:, :, count] .= enc[:, :, 1, element_id] + #elseif direction == 4 # +y direction + # boundaries.node_coordinates[:, :, count] .= enc[:, :, end, element_id] + else + error("should not happen") + end + end + end + + @assert count == nboundaries(boundaries) ("Actual boundaries count ($count) does not match " * + "expectations $(nboundaries(boundaries))") +end + + +""" + integrate(func, dg::Dg1D, args...; normalize=true) + +Call function `func` for each DG node and integrate the result over the computational domain. + +The function `func` is called as `func(i, j, element_id, dg, args...)` for each +volume node `(i, j)` and each `element_id`. Additional positional +arguments `args...` are passed along as well. If `normalize` is true, the result +is divided by the total volume of the computational domain. + +# Examples +Calculate the integral of the time derivative of the entropy, i.e., +∫(∂S/∂t)dΩ = ∫(∂S/∂u ⋅ ∂u/∂t)dΩ: +```julia +# Calculate integral of entropy time derivative +dsdu_ut = integrate(dg, dg.elements.u, dg.elements.u_t) do i, element_id, dg, u, u_t + u_node = get_node_vars(u, dg, i, element_id) + u_t_node = get_node_vars(u_t, dg, i, element_id) + dot(cons2entropy(u_node, equations(dg)), u_t_node) +end +``` +""" +function integrate(func, dg::Dg1D, args...; normalize=true) + # Initialize integral with zeros of the right shape + integral = zero(func(1, 1, dg, args...)) + + # Use quadrature to numerically integrate over entire domain + for element_id in 1:dg.n_elements + jacobian_volume = inv(dg.elements.inverse_jacobian[element_id])^ndims(dg) + #for j in 1:nnodes(dg) + for i in 1:nnodes(dg) + integral += jacobian_volume * dg.weights[i] * func(i, element_id, dg, args...) + end + #end + end + + # Normalize with total volume + if normalize + integral = integral/dg.analysis_total_volume + end + + return integral +end + + +""" + integrate(func, u, dg::Dg1D; normalize=true) + integrate(u, dg::Dg1D; normalize=true) + +Call function `func` for each DG node and integrate the result over the computational domain. + +The function `func` is called as `func(u_local)` for each volume node `(i, j)` +and each `element_id`, where `u_local` is an `SVector`ized copy of +`u[:, i, j, element_id]`. If `normalize` is true, the result is divided by the +total volume of the computational domain. If `func` is omitted, it defaults to +`identity`. + +# Examples +Calculate the integral over all conservative variables: +```julia +state_integrals = integrate(dg.elements.u, dg) +``` +""" +function integrate(func, u, dg::Dg1D; normalize=true) + func_wrapped = function(i, element_id, dg, u) + u_local = get_node_vars(u, dg, i, element_id) + return func(u_local) + end + return integrate(func_wrapped, dg, u; normalize=normalize) +end +integrate(u, dg::Dg1D; normalize=true) = integrate(identity, u, dg; normalize=normalize) + + +# Calculate L2/Linf error norms based on "exact solution" +function calc_error_norms(func, dg::Dg1D, t) + # Gather necessary information + equation = equations(dg) + n_nodes_analysis = size(dg.analysis_vandermonde, 1) + + # pre-allocate buffers + u = zeros(eltype(dg.elements.u), + nvariables(dg), size(dg.analysis_vandermonde, 1)) + #u_tmp1 = similar(u, + # nvariables(dg), size(dg.analysis_vandermonde, 1), size(dg.analysis_vandermonde, 2)) + x = zeros(eltype(dg.elements.node_coordinates), + 1, size(dg.analysis_vandermonde, 1)) + #x_tmp1 = similar(x, + # 2, size(dg.analysis_vandermonde, 1), size(dg.analysis_vandermonde, 2)) + + # Set up data structures + l2_error = zero(func(get_node_vars(dg.elements.u, dg, 1, 1), equation)) + linf_error = zero(func(get_node_vars(dg.elements.u, dg, 1, 1), equation)) + + # Iterate over all elements for error calculations + for element_id in 1:dg.n_elements + # Interpolate solution and node locations to analysis nodes + multiply_dimensionwise!(u, dg.analysis_vandermonde, view(dg.elements.u, :, :, element_id)) + multiply_dimensionwise!(x, dg.analysis_vandermonde, view(dg.elements.node_coordinates, :, :, element_id)) + + # Calculate errors at each analysis node + weights = dg.analysis_weights_volume + jacobian_volume = inv(dg.elements.inverse_jacobian[element_id])^ndims(dg) + for i in 1:n_nodes_analysis + u_exact = dg.initial_conditions(get_node_coords(x, dg, i), t, equation) + diff = func(u_exact, equation) - func(get_node_vars(u, dg, i), equation) + l2_error += diff.^2 * (weights[i] * jacobian_volume) + linf_error = @. max(linf_error, abs(diff)) + end + end + + # For L2 error, divide by total volume + l2_error = @. sqrt(l2_error / dg.analysis_total_volume) + + return l2_error, linf_error +end + + +# Integrate ∂S/∂u ⋅ ∂u/∂t over the entire domain +function calc_entropy_timederivative(dg::Dg1D, t) + # Compute ut = rhs(u) with current solution u + @notimeit timer() rhs!(dg, t) + + # Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ + dsdu_ut = integrate(dg, dg.elements.u, dg.elements.u_t) do i, element_id, dg, u, u_t + u_node = get_node_vars(u, dg, i, element_id) + u_t_node = get_node_vars(u_t, dg, i, element_id) + dot(cons2entropy(u_node, equations(dg)), u_t_node) + end + + return dsdu_ut +end + + +# Calculate L2/Linf norms of a solenoidal condition ∇ ⋅ B = 0 +# OBS! This works only when the problem setup is designed such that ∂B₁/∂x + ∂B₂/∂y = 0. Cannot +# compute the full 3D divergence from the given data +function calc_mhd_solenoid_condition(dg::Dg1D, t::Float64) + @assert equations(dg) isa IdealGlmMhdEquations2D "Only relevant for MHD" + + # Local copy of standard derivative matrix + d = polynomial_derivative_matrix(dg.nodes) + # Quadrature weights + weights = dg.weights + # integrate over all elements to get the divergence-free condition errors + linf_divb = 0.0 + l2_divb = 0.0 + for element_id in 1:dg.n_elements + jacobian_volume = (1.0/dg.elements.inverse_jacobian[element_id])^ndims(dg) + #for j in 1:nnodes(dg) + for i in 1:nnodes(dg) + divb = 0.0 + for k in 1:nnodes(dg) + divb += d[i,k]*dg.elements.u[6,k,element_id] #+ d[j,k]*dg.elements.u[7,i,k,element_id] + end + divb *= dg.elements.inverse_jacobian[element_id] + linf_divb = max(linf_divb,abs(divb)) + l2_divb += jacobian_volume*weights[i]*divb^2 + end + #end + end + l2_divb = sqrt(l2_divb/dg.analysis_total_volume) + + return l2_divb, linf_divb +end + + + +""" + analyze_solution(dg::Dg1D, mesh::TreeMesh, time, dt, step, runtime_absolute, runtime_relative) + +Calculate error norms and other analysis quantities to analyze the solution +during a simulation, and return the L2 and Linf errors. `dg` and `mesh` are the +DG and the mesh instance, respectively. `time`, `dt`, and `step` refer to the +current simulation time, the last time step size, and the current time step +count. The run time (in seconds) is given in `runtime_absolute`, while the +performance index is specified in `runtime_relative`. + +**Note:** Keep order of analysis quantities in sync with + [`save_analysis_header`](@ref) when adding or changing quantities. +""" +function analyze_solution(dg::Dg1D, mesh::TreeMesh, time::Real, dt::Real, step::Integer, + runtime_absolute::Real, runtime_relative::Real; solver_gravity=nothing) + equation = equations(dg) + + # General information + println() + println("-"^80) + println(" Simulation running '", get_name(equation), "' with POLYDEG = ", polydeg(dg)) + println("-"^80) + println(" #timesteps: " * @sprintf("% 14d", step) * + " " * + " run time: " * @sprintf("%10.8e s", runtime_absolute)) + println(" dt: " * @sprintf("%10.8e", dt) * + " " * + " Time/DOF/step: " * @sprintf("%10.8e s", runtime_relative)) + println(" sim. time: " * @sprintf("%10.8e", time)) + + # Level information (only show for AMR) + if parameter("amr_interval", 0)::Int > 0 + levels = Vector{Int}(undef, dg.n_elements) + for element_id in 1:dg.n_elements + levels[element_id] = mesh.tree.levels[dg.elements.cell_ids[element_id]] + end + min_level = minimum(levels) + max_level = maximum(levels) + + println(" #elements: " * @sprintf("% 14d", dg.n_elements)) + for level = max_level:-1:min_level+1 + println(" ├── level $level: " * @sprintf("% 14d", count(x->x==level, levels))) + end + println(" └── level $min_level: " * @sprintf("% 14d", count(x->x==min_level, levels))) + end + println() + + # Open file for appending and store time step and time information + if dg.save_analysis + f = open(dg.analysis_filename, "a") + @printf(f, "% 9d", step) + @printf(f, " %10.8e", time) + @printf(f, " %10.8e", dt) + end + + # Calculate and print derived quantities (error norms, entropy etc.) + # Variable names required for L2 error, Linf error, and conservation error + if any(q in dg.analysis_quantities for q in + (:l2_error, :linf_error, :conservation_error, :residual)) + print(" Variable: ") + for v in 1:nvariables(equation) + @printf(" %-14s", varnames_cons(equation)[v]) + end + println() + end + + # Calculate L2/Linf errors, which are also returned by analyze_solution + l2_error, linf_error = calc_error_norms(dg, time) + + # L2 error + if :l2_error in dg.analysis_quantities + print(" L2 error: ") + for v in 1:nvariables(equation) + @printf(" % 10.8e", l2_error[v]) + dg.save_analysis && @printf(f, " % 10.8e", l2_error[v]) + end + println() + end + + # Linf error + if :linf_error in dg.analysis_quantities + print(" Linf error: ") + for v in 1:nvariables(equation) + @printf(" % 10.8e", linf_error[v]) + dg.save_analysis && @printf(f, " % 10.8e", linf_error[v]) + end + println() + end + + # Conservation errror + if :conservation_error in dg.analysis_quantities + # Calculate state integrals + state_integrals = integrate(dg.elements.u, dg) + + # Store initial state integrals at first invocation + if isempty(dg.initial_state_integrals) + dg.initial_state_integrals = zeros(nvariables(equation)) + dg.initial_state_integrals .= state_integrals + end + + print(" |∑U - ∑U₀|: ") + for v in 1:nvariables(equation) + err = abs(state_integrals[v] - dg.initial_state_integrals[v]) + @printf(" % 10.8e", err) + dg.save_analysis && @printf(f, " % 10.8e", err) + end + println() + end + + # Residual (defined here as the vector maximum of the absolute values of the time derivatives) + if :residual in dg.analysis_quantities + print(" max(|Uₜ|): ") + for v in 1:nvariables(equation) + # Calculate maximum absolute value of Uₜ + @views res = maximum(abs, view(dg.elements.u_t, v, :, :)) + @printf(" % 10.8e", res) + dg.save_analysis && @printf(f, " % 10.8e", res) + end + println() + end + + # L2/L∞ errors of the primitive variables + if :l2_error_primitive in dg.analysis_quantities || :linf_error_primitive in dg.analysis_quantities + l2_error_prim, linf_error_prim = calc_error_norms(cons2prim, dg, time) + + print(" Variable: ") + for v in 1:nvariables(equation) + @printf(" %-14s", varnames_prim(equation)[v]) + end + println() + + # L2 error + if :l2_error_primitive in dg.analysis_quantities + print(" L2 error prim.: ") + for v in 1:nvariables(equation) + @printf("%10.8e ", l2_error_prim[v]) + dg.save_analysis && @printf(f, " % 10.8e", l2_error_prim[v]) + end + println() + end + + # L∞ error + if :linf_error_primitive in dg.analysis_quantities + print(" Linf error pri.:") + for v in 1:nvariables(equation) + @printf("%10.8e ", linf_error_prim[v]) + dg.save_analysis && @printf(f, " % 10.8e", linf_error_prim[v]) + end + println() + end + end + + # Entropy time derivative + if :dsdu_ut in dg.analysis_quantities + duds_ut = calc_entropy_timederivative(dg, time) + print(" ∑∂S/∂U ⋅ Uₜ: ") + @printf(" % 10.8e", duds_ut) + dg.save_analysis && @printf(f, " % 10.8e", duds_ut) + println() + end + + # Entropy + if :entropy in dg.analysis_quantities + s = integrate(dg, dg.elements.u) do i, element_id, dg, u + cons = get_node_vars(u, dg, i, element_id) + return entropy(cons, equations(dg)) + end + print(" ∑S: ") + @printf(" % 10.8e", s) + dg.save_analysis && @printf(f, " % 10.8e", s) + println() + end + + # Total energy + if :energy_total in dg.analysis_quantities + e_total = integrate(dg, dg.elements.u) do i, element_id, dg, u + cons = get_node_vars(u, dg, i, element_id) + return energy_total(cons, equations(dg)) + end + print(" ∑e_total: ") + @printf(" % 10.8e", e_total) + dg.save_analysis && @printf(f, " % 10.8e", e_total) + println() + end + + # Kinetic energy + if :energy_kinetic in dg.analysis_quantities + e_kinetic = integrate(dg, dg.elements.u) do i, element_id, dg, u + cons = get_node_vars(u, dg, i, element_id) + return energy_kinetic(cons, equations(dg)) + end + print(" ∑e_kinetic: ") + @printf(" % 10.8e", e_kinetic) + dg.save_analysis && @printf(f, " % 10.8e", e_kinetic) + println() + end + + # Internal energy + if :energy_internal in dg.analysis_quantities + e_internal = integrate(dg, dg.elements.u) do i, element_id, dg, u + cons = get_node_vars(u, dg, i, element_id) + return energy_internal(cons, equations(dg)) + end + print(" ∑e_internal: ") + @printf(" % 10.8e", e_internal) + dg.save_analysis && @printf(f, " % 10.8e", e_internal) + println() + end + + # Magnetic energy + if :energy_magnetic in dg.analysis_quantities + e_magnetic = integrate(dg, dg.elements.u) do i, element_id, dg, u + cons = get_node_vars(u, dg, i, element_id) + return energy_magnetic(cons, equations(dg)) + end + print(" ∑e_magnetic: ") + @printf(" % 10.8e", e_magnetic) + dg.save_analysis && @printf(f, " % 10.8e", e_magnetic) + println() + end + + # Potential energy + if :energy_potential in dg.analysis_quantities + # FIXME: This should be implemented properly for multiple coupled solvers + @assert !isnothing(solver_gravity) "Only works if gravity solver is supplied" + @assert dg.initial_conditions == initial_conditions_jeans_instability "Only works with Jeans instability setup" + + e_potential = integrate(dg, dg.elements.u, solver_gravity.elements.u) do i, element_id, dg, u_euler, u_gravity + cons_euler = get_node_vars(u_euler, dg, i, element_id) + cons_gravity = get_node_vars(u_gravity, solver_gravity, i, element_id) + # OBS! subtraction is specific to Jeans instability test where rho_0 = 1.5e7 + return (cons_euler[1] - 1.5e7) * cons_gravity[1] + end + print(" ∑e_pot: ") + @printf(" % 10.8e", e_potential) + dg.save_analysis && @printf(f, " % 10.8e", e_potential) + println() + end + + # Solenoidal condition ∇ ⋅ B = 0 + if :l2_divb in dg.analysis_quantities || :linf_divb in dg.analysis_quantities + l2_divb, linf_divb = calc_mhd_solenoid_condition(dg, time) + end + # L2 norm of ∇ ⋅ B + if :l2_divb in dg.analysis_quantities + print(" L2 ∇ ⋅B: ") + @printf(" % 10.8e", l2_divb) + dg.save_analysis && @printf(f, " % 10.8e", l2_divb) + println() + end + # Linf norm of ∇ ⋅ B + if :linf_divb in dg.analysis_quantities + print(" Linf ∇ ⋅B: ") + @printf(" % 10.8e", linf_divb) + dg.save_analysis && @printf(f, " % 10.8e", linf_divb) + println() + end + + # Cross helicity + if :cross_helicity in dg.analysis_quantities + h_c = integrate(dg, dg.elements.u) do i, element_id, dg, u + cons = get_node_vars(u, dg, i, element_id) + return cross_helicity(cons, equations(dg)) + end + print(" ∑H_c: ") + @printf(" % 10.8e", h_c) + dg.save_analysis && @printf(f, " % 10.8e", h_c) + println() + end + + println("-"^80) + println() + + # Add line break and close analysis file if it was opened + if dg.save_analysis + println(f) + close(f) + end + + # Return errors for EOC analysis + return l2_error, linf_error +end + + +""" + save_analysis_header(filename, quantities, equation) + +Truncate file `filename` and save a header with the names of the quantities +`quantities` that will subsequently written to `filename` by +[`analyze_solution`](@ref). Since some quantities are equation-specific, the +system of equations instance is passed in `equation`. + +**Note:** Keep order of analysis quantities in sync with + [`analyze_solution`](@ref) when adding or changing quantities. +""" +function save_analysis_header(filename, quantities, equation::AbstractEquation{1}) + open(filename, "w") do f + @printf(f, "#%-8s", "timestep") + @printf(f, " %-14s", "time") + @printf(f, " %-14s", "dt") + if :l2_error in quantities + for v in varnames_cons(equation) + @printf(f, " %-14s", "l2_" * v) + end + end + if :linf_error in quantities + for v in varnames_cons(equation) + @printf(f, " %-14s", "linf_" * v) + end + end + if :conservation_error in quantities + for v in varnames_cons(equation) + @printf(f, " %-14s", "cons_" * v) + end + end + if :residual in quantities + for v in varnames_cons(equation) + @printf(f, " %-14s", "res_" * v) + end + end + if :l2_error_primitive in quantities + for v in varnames_prim(equation) + @printf(f, " %-14s", "l2_" * v) + end + end + if :linf_error_primitive in quantities + for v in varnames_prim(equation) + @printf(f, " %-14s", "linf_" * v) + end + end + if :dsdu_ut in quantities + @printf(f, " %-14s", "dsdu_ut") + end + if :entropy in quantities + @printf(f, " %-14s", "entropy") + end + if :energy_total in quantities + @printf(f, " %-14s", "e_total") + end + if :energy_kinetic in quantities + @printf(f, " %-14s", "e_kinetic") + end + if :energy_internal in quantities + @printf(f, " %-14s", "e_internal") + end + if :energy_magnetic in quantities + @printf(f, " %-14s", "e_magnetic") + end + if :energy_potential in quantities + @printf(f, " %-14s", "e_potential") + end + if :l2_divb in quantities + @printf(f, " %-14s", "l2_divb") + end + if :linf_divb in quantities + @printf(f, " %-14s", "linf_divb") + end + if :cross_helicity in quantities + @printf(f, " %-14s", "cross_helicity") + end + println(f) + end +end + + +# Call equation-specific initial conditions functions and apply to all elements +function set_initial_conditions!(dg::Dg1D, time) + equation = equations(dg) + # make sure that the random number generator is reseted and the ICs are reproducible in the julia REPL/interactive mode + seed!(0) + for element_id in 1:dg.n_elements + #for j in 1:nnodes(dg) + for i in 1:nnodes(dg) + dg.elements.u[:, i, element_id] .= dg.initial_conditions( + dg.elements.node_coordinates[:, i, element_id], time, equation) + end + #end + end +end + + +# Calculate time derivative +function rhs!(dg::Dg1D, t_stage) + # Reset u_t + @timeit timer() "reset ∂u/∂t" dg.elements.u_t .= 0 + + # Calculate volume integral + @timeit timer() "volume integral" calc_volume_integral!(dg) + + # Prolong solution to interfaces + @timeit timer() "prolong2interfaces" prolong2interfaces!(dg) + + # Calculate interface fluxes + @timeit timer() "interface flux" calc_interface_flux!(dg) + + # Prolong solution to boundaries + @timeit timer() "prolong2boundaries" prolong2boundaries!(dg) + + # Calculate boundary fluxes + @timeit timer() "boundary flux" calc_boundary_flux!(dg, t_stage) + + # Prolong solution to mortars + #@timeit timer() "prolong2mortars" prolong2mortars!(dg) + + # Calculate mortar fluxes + #@timeit timer() "mortar flux" calc_mortar_flux!(dg) + + # Calculate surface integrals + @timeit timer() "surface integral" calc_surface_integral!(dg) + + # Apply Jacobian from mapping to reference element + @timeit timer() "Jacobian" apply_jacobian!(dg) + + # Calculate source terms + @timeit timer() "source terms" calc_sources!(dg, dg.source_terms, t_stage) +end + +# TODO: implement 2D!!! +# Apply positivity limiter of Zhang and Shu to nodal values elements.u +function apply_positivity_preserving_limiter!(dg::Dg1D) +end + +# Calculate volume integral and update u_t +calc_volume_integral!(dg::Dg1D) = calc_volume_integral!(dg.elements.u_t, dg.volume_integral_type, dg) + + +# Calculate 2D twopoint flux (element version) +@inline function calcflux_twopoint!(f1, u, element_id, dg::Dg1D) + @unpack volume_flux_function = dg + + #for j in 1:nnodes(dg) + for i in 1:nnodes(dg) + # Set diagonal entries (= regular volume fluxes due to consistency) + u_node = get_node_vars(u, dg, i, element_id) + flux1 = calcflux(u_node, 1, equations(dg)) + #flux2 = calcflux(u_node, 2, equations(dg)) + set_node_vars!(f1, flux1, dg, i, j) + #set_node_vars!(f2, flux2, dg, j, i, j) + + # Flux in x-direction + for l in (i+1):nnodes(dg) + u_ll = get_node_vars(u, dg, i, element_id) + u_rr = get_node_vars(u, dg, l, element_id) + flux = volume_flux_function(u_ll, u_rr, 1, equations(dg)) # 1-> x-direction + for v in 1:nvariables(dg) + f1[v, i, l] = f1[v, l, i] = flux[v] + end + end + + # Flux in y-direction + #for l in (j+1):nnodes(dg) + # u_ll = get_node_vars(u, dg, i, j, element_id) + # u_rr = get_node_vars(u, dg, i, l, element_id) + # flux = volume_flux_function(u_ll, u_rr, 2, equations(dg)) # 2 -> y-direction + # for v in 1:nvariables(dg) + # f2[v, j, i, l] = f2[v, l, i, j] = flux[v] + # end + #end + end + #end + + calcflux_twopoint_nonconservative!(f1, u, element_id, have_nonconservative_terms(equations(dg)), dg) +end + +function calcflux_twopoint_nonconservative!(f1, u, element_id, nonconservative_terms::Val{false}, dg::Dg1D) + return nothing +end + +function calcflux_twopoint_nonconservative!(f1, u, element_id, nonconservative_terms::Val{true}, dg::Dg1D) + #TODO: Create a unified interface, e.g. using non-symmetric two-point (extended) volume fluxes + # For now, just dispatch to an existing function for the IdealMhdEquations + calcflux_twopoint_nonconservative!(f1, u, element_id, equations(dg), dg) +end + + +# Calculate volume integral (DGSEM in weak form) +function calc_volume_integral!(u_t, ::Val{:weak_form}, dg::Dg1D) + @unpack dhat = dg + + Threads.@threads for element_id in 1:dg.n_elements + # Calculate volume integral + #for j in 1:nnodes(dg) + for i in 1:nnodes(dg) + u_node = get_node_vars(dg.elements.u, dg, i, element_id) + + flux1 = calcflux(u_node, 1, equations(dg)) + for l in 1:nnodes(dg) + integral_contribution = dhat[l, i] * flux1 + add_to_node_vars!(u_t, integral_contribution, dg, l, element_id) + end + + #flux2 = calcflux(u_node, 2, equations(dg)) + #for l in 1:nnodes(dg) + # integral_contribution = dhat[l, j] * flux2 + # add_to_node_vars!(u_t, integral_contribution, dg, i, l, element_id) + #end + end + #end + end +end + + +# Calculate volume integral (DGSEM in split form) +@inline function calc_volume_integral!(u_t, volume_integral_type::Val{:split_form}, dg::Dg1D) + calc_volume_integral!(u_t, volume_integral_type, have_nonconservative_terms(equations(dg)), dg.thread_cache, dg) +end + + +function calc_volume_integral!(u_t, ::Val{:split_form}, nonconservative_terms, cache, dg::Dg1D) + Threads.@threads for element_id in 1:dg.n_elements + split_form_kernel!(u_t, element_id, nonconservative_terms, cache, dg) + end +end + +@inline function split_form_kernel!(u_t, element_id, nonconservative_terms::Val{false}, cache, dg::Dg1D, alpha=true) + # true * [some floating point value] == [exactly the same floating point value] + # This can get optimized away due to constant propagation. + @unpack volume_flux_function, dsplit = dg + + # Calculate volume integral in one element + for i in 1:nnodes(dg) + u_node = get_node_vars(dg.elements.u, dg, i, element_id) + + # x direction + # use consistency of the volume flux to make this evaluation cheaper + flux = calcflux(u_node, 1, equations(dg)) + integral_contribution = alpha * dsplit[i, i] * flux + add_to_node_vars!(u_t, integral_contribution, dg, i, element_id) + # use symmetry of the volume flux for the remaining terms + for ii in (i+1):nnodes(dg) + u_node_ii = get_node_vars(dg.elements.u, dg, ii, element_id) + flux = volume_flux_function(u_node, u_node_ii, 1, equations(dg)) + integral_contribution = alpha * dsplit[i, ii] * flux + add_to_node_vars!(u_t, integral_contribution, dg, i, element_id) + integral_contribution = alpha * dsplit[ii, i] * flux + add_to_node_vars!(u_t, integral_contribution, dg, ii, element_id) + end + + # y direction + # use consistency of the volume flux to make this evaluation cheaper + #flux = calcflux(u_node, 2, equations(dg)) + #integral_contribution = alpha * dsplit[j, j] * flux + #add_to_node_vars!(u_t, integral_contribution, dg, i, j, element_id) + # use symmetry of the volume flux for the remaining terms + #for jj in (j+1):nnodes(dg) + # u_node_jj = get_node_vars(dg.elements.u, dg, i, jj, element_id) + # flux = volume_flux_function(u_node, u_node_jj, 2, equations(dg)) + # integral_contribution = alpha * dsplit[j, jj] * flux + # add_to_node_vars!(u_t, integral_contribution, dg, i, j, element_id) + # integral_contribution = alpha * dsplit[jj, j] * flux + # add_to_node_vars!(u_t, integral_contribution, dg, i, jj, element_id) + #end + end +end + +@inline function split_form_kernel!(u_t, element_id, nonconservative_terms::Val{true}, thread_cache, dg::Dg1D, alpha=true) + @unpack volume_flux_function, dsplit_transposed = dg + @unpack f1_threaded = thread_cache + + # Choose thread-specific pre-allocated container + f1 = f1_threaded[Threads.threadid()] + #f2 = f2_threaded[Threads.threadid()] + + # Calculate volume fluxes (one more dimension than weak form) + calcflux_twopoint!(f1, dg.elements.u, element_id, dg) + + # Calculate volume integral in one element + for i in 1:nnodes(dg) + for v in 1:nvariables(dg) + # Use local accumulator to improve performance + acc = zero(eltype(u_t)) + for l in 1:nnodes(dg) + acc += dsplit_transposed[l, i] * f1[v, l, i] #+ dsplit_transposed[l, j] * f2[v, l, i, j] + end + u_t[v, i, element_id] += alpha * acc + end + end +end + + +# Calculate volume integral (DGSEM in split form with shock capturing) +function calc_volume_integral!(u_t, ::Val{:shock_capturing}, dg::Dg1D) + # (Re-)initialize element variable storage for blending factor + if (!haskey(dg.element_variables, :blending_factor) || + length(dg.element_variables[:blending_factor]) != dg.n_elements) + dg.element_variables[:blending_factor] = Vector{Float64}(undef, dg.n_elements) + end + if (!haskey(dg.element_variables, :blending_factor_tmp) || + length(dg.element_variables[:blending_factor_tmp]) != dg.n_elements) + dg.element_variables[:blending_factor_tmp] = Vector{Float64}(undef, dg.n_elements) + end + + # Initialize element variable storage for the cache + if (!haskey(dg.cache, :element_ids_dg)) + dg.cache[:element_ids_dg] = Int[] + sizehint!(dg.cache[:element_ids_dg], dg.n_elements) + end + if (!haskey(dg.cache, :element_ids_dgfv)) + dg.cache[:element_ids_dgfv] = Int[] + sizehint!(dg.cache[:element_ids_dgfv], dg.n_elements) + end + + calc_volume_integral!(u_t, Val(:shock_capturing), + dg.element_variables[:blending_factor], dg.element_variables[:blending_factor_tmp], + dg.cache[:element_ids_dg], dg.cache[:element_ids_dgfv], + dg.thread_cache, + dg) +end + +function calc_volume_integral!(u_t, ::Val{:shock_capturing}, alpha, alpha_tmp, + element_ids_dg, element_ids_dgfv, thread_cache, dg::Dg1D) + @unpack dsplit_transposed, inverse_weights = dg + @unpack fstar1_threaded = thread_cache + + # Calculate blending factors α: u = u_DG * (1 - α) + u_FV * α + @timeit timer() "blending factors" calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, + dg.shock_alpha_max, + dg.shock_alpha_min, + dg.shock_alpha_smooth, + dg.shock_indicator_variable, thread_cache, dg) + + # Determine element ids for DG-only and blended DG-FV volume integral + pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha, dg) + + # Loop over pure DG elements + @timeit timer() "pure DG" Threads.@threads for element_id in element_ids_dg + split_form_kernel!(u_t, element_id, have_nonconservative_terms(equations(dg)), thread_cache, dg) + end + + # Loop over blended DG-FV elements + @timeit timer() "blended DG-FV" Threads.@threads for element_id in element_ids_dgfv + # Calculate DG volume integral contribution + split_form_kernel!(u_t, element_id, have_nonconservative_terms(equations(dg)), thread_cache, dg, 1 - alpha[element_id]) + + # Calculate FV two-point fluxes + fstar1 = fstar1_threaded[Threads.threadid()] + #fstar2 = fstar2_threaded[Threads.threadid()] + calcflux_fv!(fstar1, dg.elements.u, element_id, dg) + + # Calculate FV volume integral contribution + for i in 1:nnodes(dg) + for v in 1:nvariables(dg) + u_t[v, i, element_id] += ( alpha[element_id] * + (inverse_weights[i] * (fstar1[v, i+1] - fstar1[v, i]) ) ) + + end + end + end +end + + +""" + calcflux_fv!(fstar1, u_leftright, u, element_id, dg::Dg1D) + +Calculate the finite volume fluxes inside the elements. + +# Arguments +- `fstar1::AbstractArray{T} where T<:Real`: +- `fstar2::AbstractArray{T} where T<:Real` +- `dg::Dg1D` +- `u::AbstractArray{T} where T<:Real` +- `element_id::Integer` +""" +@inline function calcflux_fv!(fstar1, u, element_id, dg::Dg1D) + @unpack surface_flux_function = dg + + fstar1[:, 1 ] .= zero(eltype(fstar1)) + fstar1[:, nnodes(dg)+1] .= zero(eltype(fstar1)) + + #for j in 1:nnodes(dg) + for i in 2:nnodes(dg) + u_ll = get_node_vars(u, dg, i-1, element_id) + u_rr = get_node_vars(u, dg, i, element_id) + flux = surface_flux_function(u_ll, u_rr, 1, equations(dg)) # orientation 1: x direction + set_node_vars!(fstar1, flux, dg, i) + end + #end + + #fstar2[:, :, 1 ] .= zero(eltype(fstar2)) + #fstar2[:, :, nnodes(dg)+1] .= zero(eltype(fstar2)) + + #for j in 2:nnodes(dg) + # for i in 1:nnodes(dg) + # u_ll = get_node_vars(u, dg, i, j-1, element_id) + # u_rr = get_node_vars(u, dg, i, j, element_id) + # flux = surface_flux_function(u_ll, u_rr, 2, equations(dg)) # orientation 2: y direction + # set_node_vars!(fstar2, flux, dg, i, j) + # end + #end +end + + +# Prolong solution to interfaces (for GL nodes: just a copy) +function prolong2interfaces!(dg::Dg1D) + equation = equations(dg) + + Threads.@threads for s in 1:dg.n_interfaces + left_element_id = dg.interfaces.neighbor_ids[1, s] + right_element_id = dg.interfaces.neighbor_ids[2, s] + if dg.interfaces.orientations[s] == 1 + # interface in x-direction + for v in 1:nvariables(dg) + dg.interfaces.u[1, v, s] = dg.elements.u[v, nnodes(dg), left_element_id] + dg.interfaces.u[2, v, s] = dg.elements.u[v, 1, right_element_id] + end + #else + # # interface in y-direction + # for i in 1:nnodes(dg), v in 1:nvariables(dg) + # dg.interfaces.u[1, v, i, s] = dg.elements.u[v, i, nnodes(dg), left_element_id] + # dg.interfaces.u[2, v, i, s] = dg.elements.u[v, i, 1, right_element_id] + # end + end + end +end + + +# Prolong solution to boundaries (for GL nodes: just a copy) +function prolong2boundaries!(dg::Dg1D) + equation = equations(dg) + + for b in 1:dg.n_boundaries + element_id = dg.boundaries.neighbor_ids[b] + if dg.boundaries.orientations[b] == 1 # Boundary in x-direction + if dg.boundaries.neighbor_sides[b] == 1 # Element in -x direction of boundary + for v in 1:nvariables(dg) + dg.boundaries.u[1, v, b] = dg.elements.u[v, nnodes(dg), element_id] + end + else # Element in +x direction of boundary + for v in 1:nvariables(dg) + dg.boundaries.u[2, v, b] = dg.elements.u[v, 1, element_id] + end + end + # else # Boundary in y-direction + # if dg.boundaries.neighbor_sides[b] == 1 # Element in -y direction of boundary + # for l in 1:nnodes(dg), v in 1:nvariables(dg) + # dg.boundaries.u[1, v, l, b] = dg.elements.u[v, l, nnodes(dg), element_id] + # end + # else # Element in +y direction of boundary + # for l in 1:nnodes(dg), v in 1:nvariables(dg) + # dg.boundaries.u[2, v, l, b] = dg.elements.u[v, l, 1, element_id] + # end + # end + end + end +end + + + +""" + calc_fstar!(destination, u_interfaces_left, u_interfaces_right, interface_id, orientations, dg::Dg1D) + +Calculate the surface flux across interface with different states given by +`u_interfaces_left, u_interfaces_right` on both sides (EC mortar version). + +# Arguments +- `destination::AbstractArray{T,3} where T<:Real`: + The array of surface flux values (updated inplace). +- `u_interfaces_left::AbstractArray{T,3} where T<:Real`` +- `u_interfaces_right::AbstractArray{T,3} where T<:Real`` +- `interface_id::Integer` +- `orientations::Vector{T} where T<:Integer` +- `dg::Dg2D` +""" +#TODO +function calc_fstar!(destination, u_interfaces_left, u_interfaces_right, interface_id, orientations, dg::Dg1D) + @unpack surface_flux_function = dg + + # Call pointwise two-point numerical flux function + # i -> left, j -> right + for i in 1:nnodes(dg) + u_ll = get_node_vars(u_interfaces_left, dg, i, interface_id) + u_rr = get_node_vars(u_interfaces_right, dg, i, interface_id) + flux = surface_flux_function(u_ll, u_rr, orientations[interface_id], equations(dg)) + + # Copy flux back to actual flux array + set_node_vars!(destination, flux, dg, i) + end +end + +""" + calc_fstar!(destination, u_interfaces, interface_id, orientations, dg::Dg2D) + +Calculate the surface flux across interface with different states given by +`u_interfaces_left, u_interfaces_right` on both sides (interface version). + +# Arguments +- `destination::AbstractArray{T,2} where T<:Real`: + The array of surface flux values (updated inplace). +- `u_interfaces::AbstractArray{T,4} where T<:Real`` +- `interface_id::Integer` +- `orientations::Vector{T} where T<:Integer` +- `dg::Dg2D` +""" +function calc_fstar!(destination, u_interfaces, interface_id, orientations, dg::Dg1D) + @unpack surface_flux_function = dg + + for i in 1:nnodes(dg) + # Call pointwise two-point numerical flux function + u_ll, u_rr = get_surface_node_vars(u_interfaces, dg, i, interface_id) + flux = surface_flux_function(u_ll, u_rr, orientations[interface_id], equations(dg)) + + # Copy flux to left and right element storage + set_node_vars!(destination, flux, dg, i) + end +end + + +# Calculate and store the surface fluxes (standard Riemann and nonconservative parts) at an interface +# OBS! Regarding the nonconservative terms: 1) currently only needed for the MHD equations +# 2) not implemented for boundaries +calc_interface_flux!(dg::Dg1D) = calc_interface_flux!(dg.elements.surface_flux_values, + have_nonconservative_terms(dg.equations), dg) + +function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{false}, dg::Dg1D) + @unpack surface_flux_function = dg + @unpack u, neighbor_ids, orientations = dg.interfaces + + Threads.@threads for s in 1:dg.n_interfaces + # Get neighboring elements + left_id = neighbor_ids[1, s] + right_id = neighbor_ids[2, s] + + # Determine interface direction with respect to elements: + # orientation = 1: left -> 2, right -> 1 + # orientation = 2: left -> 4, right -> 3 + left_direction = 2 * orientations[s] + right_direction = 2 * orientations[s] - 1 + + #for i in 1:nnodes(dg) + # Call pointwise Riemann solver + u_ll, u_rr = get_surface_node_vars(u, dg, s) + flux = surface_flux_function(u_ll, u_rr, orientations[s], equations(dg)) + + # Copy flux to left and right element storage + for v in 1:nvariables(dg) + surface_flux_values[v, left_direction, left_id] = surface_flux_values[v, right_direction, right_id] = flux[v] + end + #end + end +end + +# Calculate and store Riemann and nonconservative fluxes across interfaces +function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{true}, dg::Dg1D) + #TODO temporary workaround while implementing the other stuff + calc_interface_flux!(surface_flux_values, dg.interfaces.neighbor_ids, dg.interfaces.u, + nonconservative_terms, dg.interfaces.orientations, dg, dg.thread_cache) +end + +function calc_interface_flux!(surface_flux_values, neighbor_ids, + u_interfaces, nonconservative_terms::Val{true}, + orientations, dg::Dg1D, thread_cache) + fstar_threaded = thread_cache.fstar_upper_threaded + noncons_diamond_primary_threaded = thread_cache.noncons_diamond_upper_threaded + noncons_diamond_secondary_threaded = thread_cache.noncons_diamond_lower_threaded + + Threads.@threads for s in 1:dg.n_interfaces + # Choose thread-specific pre-allocated container + fstar = fstar_threaded[Threads.threadid()] + noncons_diamond_primary = noncons_diamond_primary_threaded[Threads.threadid()] + noncons_diamond_secondary = noncons_diamond_secondary_threaded[Threads.threadid()] + + # Calculate flux + calc_fstar!(fstar, u_interfaces, s, orientations, dg) + + # Compute the nonconservative numerical "flux" along an interface + # Done twice because left/right orientation matters så + # 1 -> primary element and 2 -> secondary element + # See Bohm et al. 2018 for details on the nonconservative diamond "flux" + #for i in 1:nnodes(dg) + # Call pointwise nonconservative term + u_ll, u_rr = get_surface_node_vars(u_interfaces, dg, s) + noncons_primary = noncons_interface_flux(u_ll, u_rr, orientations[s], equations(dg)) + noncons_secondary = noncons_interface_flux(u_rr, u_ll, orientations[s], equations(dg)) + # Save to primary and secondary temporay storage + set_node_vars!(noncons_diamond_primary, noncons_primary, dg) + set_node_vars!(noncons_diamond_secondary, noncons_secondary, dg) + #end + + # Get neighboring elements + left_neighbor_id = neighbor_ids[1, s] + right_neighbor_id = neighbor_ids[2, s] + + # Determine interface direction with respect to elements: + # orientation = 1: left -> 2, right -> 1 + # orientation = 2: left -> 4, right -> 3 + left_neighbor_direction = 2 * orientations[s] + right_neighbor_direction = 2 * orientations[s] - 1 + + # Copy flux to left and right element storage + #for i in 1:nnodes(dg) + for v in 1:nvariables(dg) + surface_flux_values[v, left_neighbor_direction, left_neighbor_id] = (fstar[v] + + noncons_diamond_primary[v]) + surface_flux_values[v, right_neighbor_direction, right_neighbor_id] = (fstar[v] + + noncons_diamond_secondary[v]) + end + #end + end +end + + +# Calculate and store boundary flux across domain boundaries +#NOTE: Do we need to dispatch on have_nonconservative_terms(dg.equations)? +calc_boundary_flux!(dg::Dg1D, time) = calc_boundary_flux!(dg.elements.surface_flux_values, + dg, time) +function calc_boundary_flux!(surface_flux_values, dg::Dg1D, time) + @unpack surface_flux_function = dg + @unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = dg.boundaries + + Threads.@threads for b in 1:dg.n_boundaries + # Fill outer boundary state + # FIXME: This should be replaced by a proper boundary condition + #for i in 1:nnodes(dg) + @views u[3 - neighbor_sides[b], :, b] .= dg.initial_conditions( + node_coordinates[:, b], time, equations(dg)) + #end + + # Get neighboring element + neighbor_id = neighbor_ids[b] + + # Determine boundary direction with respect to elements: + # orientation = 1: left -> 2, right -> 1 + # orientation = 2: left -> 4, right -> 3 + if orientations[b] == 1 # Boundary in x-direction + if neighbor_sides[b] == 1 # Element is on the left, boundary on the right + direction = 2 + else # Element is on the right, boundary on the left + direction = 1 + end + #else # Boundary in y-direction + # if neighbor_sides[b] == 1 # Element is below, boundary is above + # direction = 4 + # else # Element is above, boundary is below + # direction = 3 + # end + end + + #for i in 1:nnodes(dg) + # Call pointwise Riemann solver + u_ll, u_rr = get_surface_node_vars(u, dg, b) + flux = surface_flux_function(u_ll, u_rr, orientations[b], equations(dg)) + + # Copy flux to left and right element storage + for v in 1:nvariables(dg) + surface_flux_values[v, direction, neighbor_id] = flux[v] + end + #end + end +end + + + +# Calculate surface integrals and update u_t +calc_surface_integral!(dg::Dg1D) = calc_surface_integral!(dg.elements.u_t, dg.elements.surface_flux_values, dg) +function calc_surface_integral!(u_t, surface_flux_values, dg::Dg1D) + @unpack lhat = dg + + Threads.@threads for element_id in 1:dg.n_elements + #for l in 1:nnodes(dg) + for v in 1:nvariables(dg) + # surface at -x + u_t[v, 1, element_id] -= surface_flux_values[v, 1, element_id] * lhat[1, 1] + # surface at +x + u_t[v, nnodes(dg), element_id] += surface_flux_values[v, 2, element_id] * lhat[nnodes(dg), 2] + # surface at -y + #u_t[v, l, 1, element_id] -= surface_flux_values[v, 3, element_id] * lhat[1, 1] + # surface at +y + #u_t[v, l, nnodes(dg), element_id] += surface_flux_values[v, 4, element_id] * lhat[nnodes(dg), 2] + end + #end + end +end + + +# Apply Jacobian from mapping to reference element +function apply_jacobian!(dg::Dg1D) + Threads.@threads for element_id in 1:dg.n_elements + factor = -dg.elements.inverse_jacobian[element_id] + #for j in 1:nnodes(dg) + for i in 1:nnodes(dg) + for v in 1:nvariables(dg) + dg.elements.u_t[v, i, element_id] *= factor + end + end + #end + end +end + + +# Calculate source terms and apply them to u_t +function calc_sources!(dg::Dg1D, source_terms::Nothing, t) + return nothing +end + +function calc_sources!(dg::Dg1D, source_terms, t) + Threads.@threads for element_id in 1:dg.n_elements + source_terms(dg.elements.u_t, dg.elements.u, + dg.elements.node_coordinates, element_id, t, nnodes(dg), equations(dg)) + end +end + + +# Calculate stable time step size +function calc_dt(dg::Dg1D, cfl) + min_dt = Inf + for element_id in 1:dg.n_elements + dt = calc_max_dt(dg.elements.u, element_id, + dg.elements.inverse_jacobian[element_id], cfl, equations(dg), dg) + min_dt = min(min_dt, dt) + end + + return min_dt +end + +# Calculate blending factors used for shock capturing, or amr control +function calc_blending_factors!(alpha, alpha_pre_smooth, u, + alpha_max, alpha_min, do_smoothing, + indicator_variable, thread_cache, dg::Dg1D) + # temporary buffers + @unpack indicator_threaded, modal_threaded, modal_tmp1_threaded = thread_cache + # magic parameters + threshold = 0.5 * 10^(-1.8 * (nnodes(dg))^0.25) + parameter_s = log((1 - 0.0001)/0.0001) + + Threads.@threads for element_id in 1:dg.n_elements + indicator = indicator_threaded[Threads.threadid()] + modal = modal_threaded[Threads.threadid()] + modal_tmp1 = modal_tmp1_threaded[Threads.threadid()] + + # Calculate indicator variables at Gauss-Lobatto nodes + cons2indicator!(indicator, u, element_id, nnodes(dg), indicator_variable, equations(dg)) + + # Convert to modal representation + multiply_dimensionwise!(modal, dg.inverse_vandermonde_legendre, indicator, modal_tmp1) + + # Calculate total energies for all modes, without highest, without two highest + total_energy = 0.0 + for i in 1:nnodes(dg) + total_energy += modal[1, i]^2 + end + total_energy_clip1 = 0.0 + for i in 1:(nnodes(dg)-1) + total_energy_clip1 += modal[1, i]^2 + end + total_energy_clip2 = 0.0 + for i in 1:(nnodes(dg)-2) + total_energy_clip2 += modal[1, i]^2 + end + + # Calculate energy in lower modes + energy = max((total_energy - total_energy_clip1)/total_energy, + (total_energy_clip1 - total_energy_clip2)/total_energy_clip1) + + alpha[element_id] = 1 / (1 + exp(-parameter_s/threshold * (energy - threshold))) + + # Take care of the case close to pure DG + if (alpha[element_id] < alpha_min) + alpha[element_id] = 0. + end + + # Take care of the case close to pure FV + if (alpha[element_id] > 1-alpha_min) + alpha[element_id] = 1. + end + + # Clip the maximum amount of FV allowed + alpha[element_id] = min(alpha_max, alpha[element_id]) + end + + if (do_smoothing) + # Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha + # Copy alpha values such that smoothing is indpedenent of the element access order + alpha_pre_smooth .= alpha + + # Loop over interfaces + for interface_id in 1:dg.n_interfaces + # Get neighboring element ids + left = dg.interfaces.neighbor_ids[1, interface_id] + right = dg.interfaces.neighbor_ids[2, interface_id] + + # Apply smoothing + alpha[left] = max(alpha_pre_smooth[left], 0.5 * alpha_pre_smooth[right], alpha[left]) + alpha[right] = max(alpha_pre_smooth[right], 0.5 * alpha_pre_smooth[left], alpha[right]) + end + end +end + + +""" + pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha, dg) + +Given blending factors `alpha` and the solver `dg`, fill +`element_ids_dg` with the IDs of elements using a pure DG scheme and +`element_ids_dgfv` with the IDs of elements using a blended DG-FV scheme. +""" +function pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha, dg::Dg1D) + empty!(element_ids_dg) + empty!(element_ids_dgfv) + + for element_id in 1:dg.n_elements + # Clip blending factor for values close to zero (-> pure DG) + dg_only = isapprox(alpha[element_id], 0, atol=1e-12) + if dg_only + push!(element_ids_dg, element_id) + else + push!(element_ids_dgfv, element_id) + end + end +end diff --git a/src/solvers/dg/dg.jl b/src/solvers/dg/dg.jl index 65a4bff6daa..817e5fb98ca 100644 --- a/src/solvers/dg/dg.jl +++ b/src/solvers/dg/dg.jl @@ -58,6 +58,11 @@ end include("interpolation.jl") include("l2projection.jl") +# Include 1D implementation +include("1d/containers.jl") +include("1d/dg.jl") +include("1d/amr.jl") + # Include 2D implementation include("2d/containers.jl") include("2d/dg.jl") diff --git a/src/solvers/solvers.jl b/src/solvers/solvers.jl index e185d4df3b7..cd7f27c7974 100644 --- a/src/solvers/solvers.jl +++ b/src/solvers/solvers.jl @@ -25,7 +25,9 @@ function make_solver(name::String, equations::AbstractEquation, mesh::TreeMesh; source_terms_type = Symbol(parameter("source_terms", "nothing")) source_terms = eval(source_terms_type) - if ndims(equations) == 2 + if ndims(equations) == 1 + return Dg1D(equations, surface_flux_function, volume_flux_function, initial_conditions, source_terms, mesh, parameter("polydeg")) + elseif ndims(equations) == 2 return Dg2D(equations, surface_flux_function, volume_flux_function, initial_conditions, source_terms, mesh, parameter("polydeg")) elseif ndims(equations) == 3 return Dg3D(equations, surface_flux_function, volume_flux_function, initial_conditions, source_terms, mesh, parameter("polydeg")) From 715b2bca436416389b080247a91a1b113f595965 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Fri, 18 Sep 2020 19:56:42 +0200 Subject: [PATCH 02/55] Support for 1D --- Project.toml | 2 + examples/1d/parameters.toml | 2 +- examples/1d/parameters_density_wave.toml | 41 ++ src/equations/1d/compressible_euler.jl | 847 +++++++++++++++++++++++ src/equations/equations.jl | 8 +- src/run.jl | 5 +- src/solvers/dg/1d/containers.jl | 2 +- src/solvers/dg/1d/dg.jl | 2 + utils/euler-manufactured.jl | 20 +- 9 files changed, 920 insertions(+), 9 deletions(-) create mode 100644 examples/1d/parameters_density_wave.toml create mode 100644 src/equations/1d/compressible_euler.jl diff --git a/Project.toml b/Project.toml index f07bee5cfed..e8a9cbfd438 100644 --- a/Project.toml +++ b/Project.toml @@ -12,6 +12,8 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Reduce = "93e0c654-6965-5f22-aba9-9c1ae6b3c259" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" Tullio = "bc48ee85-29a4-5162-ae0b-a64e1601d4bc" diff --git a/examples/1d/parameters.toml b/examples/1d/parameters.toml index 5bcfd83e6d2..4bbc165a436 100644 --- a/examples/1d/parameters.toml +++ b/examples/1d/parameters.toml @@ -20,7 +20,7 @@ t_end = 1.0 #################################################################################################### # Solver solver = "dg" -polydeg = 3 +polydeg = 4 cfl = 0.2 n_steps_max = 10000 analysis_interval = 100 diff --git a/examples/1d/parameters_density_wave.toml b/examples/1d/parameters_density_wave.toml new file mode 100644 index 00000000000..bc115fdc04d --- /dev/null +++ b/examples/1d/parameters_density_wave.toml @@ -0,0 +1,41 @@ +#################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" +initial_conditions = "initial_conditions_density_wave" +surface_flux = "flux_central" +volume_flux = "flux_central" +t_start = 0.0 +t_end = 2.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 5 +cfl = 0.8 +n_steps_max = 10000 +analysis_interval = 100 + + +#################################################################################################### +# Mesh +n_cells_max = 30000 +coordinates_min = [-1] +coordinates_max = [1] +initial_refinement_level = 2 +# refinement_patches = [ +# {type = "box", coordinates_min = [-0.5, -0.5], coordinates_max = [0.5, 0.5]}, +# {type = "box", coordinates_min = [-0.1, -0.1], coordinates_max = [0.1, 0.1]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 10 diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl new file mode 100644 index 00000000000..bac5bf0d5d0 --- /dev/null +++ b/src/equations/1d/compressible_euler.jl @@ -0,0 +1,847 @@ +@doc raw""" + CompressibleEulerEquations1D + +The compressible Euler equations for an ideal gas in two space dimensions. +""" +struct CompressibleEulerEquations1D <: AbstractCompressibleEulerEquations{1, 3} + gamma::Float64 +end + +function CompressibleEulerEquations1D() + gamma = parameter("gamma", 1.4) + + CompressibleEulerEquations1D(gamma) +end + + +get_name(::CompressibleEulerEquations1D) = "CompressibleEulerEquations1D" +varnames_cons(::CompressibleEulerEquations1D) = @SVector ["rho", "rho_v1", "rho_e"] +varnames_prim(::CompressibleEulerEquations1D) = @SVector ["rho", "v1", "p"] + + +# Set initial conditions at physical location `x` for time `t` +function initial_conditions_density_pulse(x, t, equation::CompressibleEulerEquations1D) + rho = 1 + exp(-(x[1]^2 ))/2 + v1 = 1 + rho_v1 = rho * v1 + p = 1 + rho_e = p/(equation.gamma - 1) + 1/2 * rho * v1^2 + return @SVector [rho, rho_v1, rho_e] +end + +""" + initial_conditions_density_wave(x, t, equation::CompressibleEulerEquations1D) + +test case for stability of EC fluxes from paper: https://arxiv.org/pdf/2007.09026.pdf +domain [-1, 1] +mesh = 4x4, polydeg = 5 +""" + +function initial_conditions_density_wave(x, t, equation::CompressibleEulerEquations1D) + v1 = 0.1 + rho = 1 + 0.98 * sinpi(2 * (x[1] - t * v1)) + rho_v1 = rho * v1 + p = 20 + rho_e = p / (equation.gamma - 1) + 1/2 * rho * v1^2 + return @SVector [rho, rho_v1, rho_e] +end +#= +function initial_conditions_pressure_pulse(x, t, equation::CompressibleEulerEquations1D) + rho = 1 + v1 = 1 + rho_v1 = rho * v1 + p = 1 + exp(-(x[1]^2))/2 + rho_e = p/(equation.gamma - 1) + 1/2 * rho * v1^2 + return @SVector [rho, rho_v1, rho_e] +end + +function initial_conditions_density_pressure_pulse(x, t, equation::CompressibleEulerEquations1D) + rho = 1 + exp(-(x[1]^2))/2 + v1 = 1 + rho_v1 = rho * v1 + p = 1 + exp(-(x[1]^2))/2 + rho_e = p/(equation.gamma - 1) + 1/2 * rho * v1^2 + return @SVector [rho, rho_v1, rho_e] +end + +function initial_conditions_constant(x, t, equation::CompressibleEulerEquations1D) + rho = 1.0 + rho_v1 = 0.1 + rho_e = 10.0 + return @SVector [rho, rho_v1, rho_e] +end +=# +function initial_conditions_convergence_test(x, t, equation::CompressibleEulerEquations1D) + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] - t)) + + rho = ini + rho_v1 = ini + rho_e = ini^2 + + return @SVector [rho, rho_v1, rho_e] +end +#= +function initial_conditions_isentropic_vortex(x, t, equation::CompressibleEulerEquations1D) + # needs appropriate mesh size, e.g. [-10,-10]x[10,10] + # make sure that the inicenter does not exit the domain, e.g. T=10.0 + # initial center of the vortex + inicenter = SVector(0.0, 0.0) + # size and strength of the vortex + iniamplitude = 0.2 + # base flow + rho = 1.0 + v1 = 1.0 + v2 = 1.0 + vel = SVector(v1, v2) + p = 10.0 + vec = SVector(v1, v2) + rt = p / rho # ideal gas equation + cent = inicenter + vel*t # advection of center + cent = x - cent # distance to centerpoint + #cent=cross(iniaxis,cent) # distance to axis, tangent vector, length r + # cross product with iniaxis = [0,0,1] + cent = SVector(-cent[2], cent[1]) + r2 = cent[1]^2 + cent[2]^2 + du = iniamplitude/(2*π)*exp(0.5*(1-r2)) # vel. perturbation + dtemp = -(equation.gamma-1)/(2*equation.gamma*rt)*du^2 # isentrop + rho = rho * (1+dtemp)^(1\(equation.gamma-1)) + vel = vel + du*cent + v1, v2 = vel + p = p * (1+dtemp)^(equation.gamma/(equation.gamma-1)) + prim = SVector(rho, v1, v2, p) + return prim2cons(prim, equation) +end + +function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEquations2D) + # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + phi = atan(y_norm, x_norm) + sin_phi, cos_phi = sincos(phi) + + # Calculate primitive variables + rho = r > 0.5 ? 1.0 : 1.1691 + v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi + v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi + p = r > 0.5 ? 1.0 : 1.245 + + return prim2cons(SVector(rho, v1, v2, p), equation) +end + +function initial_conditions_blast_wave(x, t, equation::CompressibleEulerEquations2D) + # Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> "medium blast wave" + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + phi = atan(y_norm, x_norm) + sin_phi, cos_phi = sincos(phi) + + # Calculate primitive variables + rho = r > 0.5 ? 1.0 : 1.1691 + v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi + v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi + p = r > 0.5 ? 1.0E-3 : 1.245 + + return prim2cons(SVector(rho, v1, v2, p), equation) +end + +function initial_conditions_sedov_blast_wave(x, t, equation::CompressibleEulerEquations2D) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) + # r0 = 0.5 # = more reasonable setup + E = 1.0 + p0_inner = 3 * (equation.gamma - 1) * E / (3 * pi * r0^2) + p0_outer = 1.0e-5 # = true Sedov setup + # p0_outer = 1.0e-3 # = more reasonable setup + + # Calculate primitive variables + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + p = r > r0 ? p0_outer : p0_inner + + return prim2cons(SVector(rho, v1, v2, p), equation) +end + +function initial_conditions_medium_sedov_blast_wave(x, t, equation::CompressibleEulerEquations2D) + # Set up polar coordinates + inicenter = SVector(0.0, 0.0) + x_norm = x[1] - inicenter[1] + y_norm = x[2] - inicenter[2] + r = sqrt(x_norm^2 + y_norm^2) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 + r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) + # r0 = 0.5 # = more reasonable setup + E = 1.0 + p0_inner = 3 * (equation.gamma - 1) * E / (3 * pi * r0^2) + # p0_outer = 1.0e-5 # = true Sedov setup + p0_outer = 1.0e-3 # = more reasonable setup + + # Calculate primitive variables + rho = 1.0 + v1 = 0.0 + v2 = 0.0 + p = r > r0 ? p0_outer : p0_inner + + return prim2cons(SVector(rho, v1, v2, p), equation) +end + +function initial_conditions_khi(x, t, equation::CompressibleEulerEquations2D) + # https://rsaa.anu.edu.au/research/established-projects/fyris/2-d-kelvin-helmholtz-test + # change discontinuity to tanh + # typical resolution 128^2, 256^2 + # domain size is [-0.5,0.5]^2 + dens0 = 1.0 # outside density + dens1 = 2.0 # inside density + velx0 = -0.5 # outside velocity + velx1 = 0.5 # inside velocity + slope = 50 # used for tanh instead of discontinuous initial condition + # pressure equilibrium + p = 2.5 + # y velocity v2 is only white noise + v2 = 0.01*(rand(Float64,1)[1]-0.5) + # density + rho = dens0 + (dens1-dens0) * 0.5*(1+(tanh(slope*(x[2]+0.25)) - (tanh(slope*(x[2]-0.25)) + 1))) + # x velocity is also augmented with noise + v1 = velx0 + (velx1-velx0) * 0.5*(1+(tanh(slope*(x[2]+0.25)) - (tanh(slope*(x[2]-0.25)) + 1)))+0.01*(rand(Float64,1)[1]-0.5) + return prim2cons(SVector(rho, v1, v2, p), equation) +end + +function initial_conditions_blob(x, t, equation::CompressibleEulerEquations2D) + # blob test case, see Agertz et al. https://arxiv.org/pdf/astro-ph/0610051.pdf + # other reference: https://arxiv.org/pdf/astro-ph/0610051.pdf + # change discontinuity to tanh + # typical domain is rectangular, we change it to a square, as Trixi can only do squares + # resolution 128^2, 256^2 + # domain size is [-20.0,20.0]^2 + # gamma = 5/3 for this test case + R = 1.0 # radius of the blob + # background density + dens0 = 1.0 + Chi = 10.0 # density contrast + # reference time of characteristic growth of KH instability equal to 1.0 + tau_kh = 1.0 + tau_cr = tau_kh/1.6 # crushing time + # determine background velocity + velx0 = 2*R*sqrt(Chi)/tau_cr + vely0 = 0.0 + Ma0 = 2.7 # background flow Mach number Ma=v/c + c = velx0/Ma0 # sound speed + # use perfect gas assumption to compute background pressure via the sound speed c^2 = gamma * pressure/density + p0 = c*c*dens0/equation.gamma + # initial center of the blob + inicenter = [-15,0] + x_rel = x-inicenter + r = sqrt(x_rel[1]^2 + x_rel[2]^2) + # steepness of the tanh transition zone + slope = 2 + # density blob + dens = dens0 + (Chi-1) * 0.5*(1+(tanh(slope*(r+R)) - (tanh(slope*(r-R)) + 1))) + # velocity blob is zero + velx = velx0 - velx0 * 0.5*(1+(tanh(slope*(r+R)) - (tanh(slope*(r-R)) + 1))) + return prim2cons(SVector(dens, velx, vely0, p0), equation) +end + +function initial_conditions_jeans_instability(x, t, equation::CompressibleEulerEquations2D) + # Jeans gravitational instability test case + # see Derigs et al. https://arxiv.org/abs/1605.03572; Sec. 4.6 + # OBS! this uses cgs (centimeter, gram, second) units + # periodic boundaries + # domain size [0,L]^2 depends on the wave number chosen for the perturbation + # OBS! Be very careful here L must be chosen such that problem is periodic + # typical final time is T = 5 + # gamma = 5/3 + dens0 = 1.5e7 # g/cm^3 + pres0 = 1.5e7 # dyn/cm^2 + delta0 = 1e-3 + # set wave vector values for pertubation (units 1/cm) + # see FLASH manual: https://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel.pdf + kx = 2.0*pi/0.5 # 2π/λ_x, λ_x = 0.5 + ky = 0.0 # 2π/λ_y, λ_y = 1e10 + k_dot_x = kx*x[1] + ky*x[2] + # perturb density and pressure away from reference states ρ_0 and p_0 + dens = dens0*(1.0 + delta0*cos(k_dot_x)) # g/cm^3 + pres = pres0*(1.0 + equation.gamma*delta0*cos(k_dot_x)) # dyn/cm^2 + # flow starts as stationary + velx = 0.0 # cm/s + vely = 0.0 # cm/s + return prim2cons(SVector(dens, velx, vely, pres), equation) +end + +function initial_conditions_eoc_test_coupled_euler_gravity(x, t, equation::CompressibleEulerEquations2D) + # OBS! this assumes that γ = 2 other manufactured source terms are incorrect + if equation.gamma != 2.0 + error("adiabatic constant must be 2 for the coupling convergence test") + end + c = 2.0 + A = 0.1 + ini = c + A * sin(pi * (x[1] + x[2] - t)) + G = 1.0 # gravitational constant + + rho = ini + v1 = 1.0 + v2 = 1.0 + p = ini^2 * G / pi # * 2 / ndims, but ndims==2 here + + return prim2cons(SVector(rho, v1, v2, p), equation) +end + +function initial_conditions_sedov_self_gravity(x, t, equation::CompressibleEulerEquations2D) + # Set up polar coordinates + r = sqrt(x[1]^2 + x[2]^2) + + # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000 + r0 = 0.125 # = 4.0 * smallest dx (for domain length=8 and max-ref=8) + E = 1.0 + p_inner = (equation.gamma - 1) * E / (pi * r0^2) + p_ambient = 1e-5 # = true Sedov setup + + # Calculate primitive variables + # use a logistic function to tranfer density value smoothly + L = 1.0 # maximum of function + x0 = 1.0 # center point of function + k = -150.0 # sharpness of transfer + logistic_function_rho = L/(1.0 + exp(-k*(r - x0))) + rho_ambient = 1e-5 + rho = max(logistic_function_rho, rho_ambient) # clip background density to not be so tiny + + # velocities are zero + v1 = 0.0 + v2 = 0.0 + + # use a logistic function to tranfer pressure value smoothly + logistic_function_p = p_inner/(1.0 + exp(-k*(r - r0))) + p = max(logistic_function_p, p_ambient) + + return prim2cons(SVector(rho, v1, v2, p), equation) +end +=# +# Apply source terms +function source_terms_convergence_test(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations1D) + # Same settings as in `initial_conditions` + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + γ = equation.gamma + + for i in 1:n_nodes + x1 = x[1, i, element_id] + + si, co = sincos((x1 - t)*ω) + tmp1 = co * A * ω + tmp2 = si * A + tmp3 = γ - 1 + tmp4 = (2*c - 1)*tmp3 + tmp5 = (2*tmp2*γ - 2*tmp2 + tmp4 + 1)*tmp1 + tmp6 = tmp2 + c + + ut[1, i, element_id] += tmp1 + ut[2, i, element_id] += tmp5 + #ut[3, i, j, element_id] += tmp5 + ut[3, i, element_id] += 2*((tmp6 - 1)*tmp3 + tmp6*γ)*tmp1 #TODO change from 2D to 1D + + # Original terms (without performanc enhancements) + # ut[1, i, element_id] += cos((x1 - t)*ω)*A*ω + # ut[2, i, element_id] += (2*sin((x1 - t)*ω)*A*γ - 2*sin((x1 - t)*ω)*A + + # 2*c*γ - 2*c - γ + 2)*cos((x1 - t)*ω)*A*ω + # ut[3, i, element_id] += (2*sin((x1 - t)*ω)*A*γ - 2*sin((x1 - t)*ω)*A + + # 2*c*γ - 2*c - γ + 2)*cos((x1 - t)*ω)*A*ω + # ut[4, i, element_id] += 2*((c - 1 + sin((x1 + x2 - t)*ω)*A)*(γ - 1) + + # (sin((x1 + x2 - t)*ω)*A + c)*γ)*cos((x1 + x2 - t)*ω)*A*ω + end + + return nothing +end +#= +function source_terms_eoc_test_coupled_euler_gravity(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations2D) + # Same settings as in `initial_conditions_eoc_test_coupled_euler_gravity` + c = 2.0 + A = 0.1 + G = 1.0 # gravitational constant, must match coupling solver + C_grav = -2.0 * G / pi # 2 == 4 / ndims + + for j in 1:n_nodes, i in 1:n_nodes + x1 = x[1, i, j, element_id] + x2 = x[2, i, j, element_id] + si, co = sincos(pi * (x1 + x2 - t)) + rhox = A * pi * co + rho = c + A * si + + ut[1, i, j, element_id] += rhox + ut[2, i, j, element_id] += rhox + ut[3, i, j, element_id] += rhox + ut[4, i, j, element_id] += (1.0 - C_grav*rho)*rhox + end + + return nothing +end + +function source_terms_eoc_test_euler(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations2D) + # Same settings as in `initial_conditions_eoc_test_coupled_euler_gravity` + c = 2.0 + A = 0.1 + G = 1.0 + C_grav = -2 * G / pi # 2 == 4 / ndims + + for j in 1:n_nodes, i in 1:n_nodes + x1 = x[1, i, j, element_id] + x2 = x[2, i, j, element_id] + si, co = sincos(pi * (x1 + x2 - t)) + rhox = A * pi * co + rho = c + A * si + + ut[1, i, j, element_id] += rhox + ut[2, i, j, element_id] += rhox * (1 - C_grav * rho) + ut[3, i, j, element_id] += rhox * (1 - C_grav * rho) + ut[4, i, j, element_id] += rhox * (1 - 3 * C_grav * rho) + end + + return nothing +end +=# +# Empty source terms required for coupled Euler-gravity simulations +function source_terms_harmonic(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations1D) + # OBS! used for the Jeans instability as well as self-gravitating Sedov blast + # TODO: make this cleaner and let each solver have a different source term name + return nothing +end + +# Calculate 1D flux for a single point +@inline function calcflux(u, orientation, equation::CompressibleEulerEquations1D) + rho, rho_v1, rho_e = u + v1 = rho_v1/rho + p = (equation.gamma - 1) * (rho_e - 1/2 * rho * v1^2) + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = (rho_e + p) * v1 + end + return SVector(f1, f2, f3) +end + + +""" + function flux_shima_etal(u_ll, u_rr, orientation, equation::CompressibleEulerEquations2D) + +This flux is is a modification of the original kinetic energy preserving two-point flux by +Kuya, Totani and Kawai (2018) + Kinetic energy and entropy preserving schemes for compressible flows + by split convective forms + [DOI: 10.1016/j.jcp.2018.08.058](https://doi.org/10.1016/j.jcp.2018.08.058) +The modification is in the energy flux to guarantee pressure equilibrium and was developed by +Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) + Preventing spurious pressure oscillations in split convective form discretizations for + compressible flows +""" +@inline function flux_shima_etal(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) + # Unpack left and right state + rho_ll, rho_v1_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + p_ll = (equation.gamma - 1) * (rho_e_ll - 1/2 * rho_ll * (v1_ll^2)) + p_rr = (equation.gamma - 1) * (rho_e_rr - 1/2 * rho_rr * (v1_rr^2)) + + # Average each factor of products in flux + rho_avg = 1/2 * (rho_ll + rho_rr) + v1_avg = 1/2 * ( v1_ll + v1_rr) + p_avg = 1/2 * ( p_ll + p_rr) + kin_avg = 1/2 * (v1_ll*v1_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + pv1_avg = 1/2 * (p_ll*v1_rr + p_rr*v1_ll) + f1 = rho_avg * v1_avg + f2 = rho_avg * v1_avg * v1_avg + p_avg + f3 = p_avg*v1_avg/(equation.gamma-1) + rho_avg*v1_avg*kin_avg + pv1_avg + end + + return SVector(f1, f2, f3) +end + + +""" + flux_kennedy_gruber(u_ll, u_rr, orientation, equation::CompressibleEulerEquations2D) + +Kinetic energy preserving two-point flux by Kennedy and Gruber (2008) + Reduced aliasing formulations of the convective terms within the + Navier-Stokes equations for a compressible fluid +[DOI: 10.1016/j.jcp.2007.09.020](https://doi.org/10.1016/j.jcp.2007.09.020) +""" +@inline function flux_kennedy_gruber(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) + # Unpack left and right state + rho_ll, rho_v1_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll/rho_ll + v1_rr = rho_v1_rr/rho_rr + + # Average each factor of products in flux + rho_avg = 1/2 * (rho_ll + rho_rr) + v1_avg = 1/2 * (v1_ll + v1_rr) + p_avg = 1/2 * ((equation.gamma - 1) * (rho_e_ll - 1/2 * rho_ll * (v1_ll^2)) + + (equation.gamma - 1) * (rho_e_rr - 1/2 * rho_rr * (v1_rr^2))) + e_avg = 1/2 * (rho_e_ll/rho_ll + rho_e_rr/rho_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_avg * v1_avg + f2 = rho_avg * v1_avg * v1_avg + p_avg + f3 = (rho_avg * e_avg + p_avg) * v1_avg + end + + return SVector(f1, f2, f3) +end + + +""" + flux_chandrashekar(u_ll, u_rr, orientation, equation::CompressibleEulerEquations2D) + +Entropy conserving two-point flux by Chandrashekar (2013) + Kinetic Energy Preserving and Entropy Stable Finite Volume Schemes + for Compressible Euler and Navier-Stokes Equations +[DOI: 10.4208/cicp.170712.010313a](https://doi.org/10.4208/cicp.170712.010313a) +""" +@inline function flux_chandrashekar(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) + # Unpack left and right state + rho_ll, rho_v1_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll/rho_ll + v1_rr = rho_v1_rr/rho_rr + p_ll = (equation.gamma - 1) * (rho_e_ll - 1/2 * rho_ll * (v1_ll^2)) + p_rr = (equation.gamma - 1) * (rho_e_rr - 1/2 * rho_rr * (v1_rr^2)) + beta_ll = 0.5*rho_ll/p_ll + beta_rr = 0.5*rho_rr/p_rr + specific_kin_ll = 0.5*(v1_ll^2) + specific_kin_rr = 0.5*(v1_rr^2) + + # Compute the necessary mean values + rho_avg = 0.5*(rho_ll+rho_rr) + rho_mean = ln_mean(rho_ll,rho_rr) + beta_mean = ln_mean(beta_ll,beta_rr) + beta_avg = 0.5*(beta_ll+beta_rr) + v1_avg = 0.5*(v1_ll+v1_rr) + p_mean = 0.5*rho_avg/beta_avg + velocity_square_avg = specific_kin_ll + specific_kin_rr + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_mean + f3 = f1 * 0.5*(1/(equation.gamma-1)/beta_mean - velocity_square_avg)+f2*v1_avg + end + + return SVector(f1, f2, f3) +end + + +""" + flux_ranocha(u_ll, u_rr, orientation, equation::CompressibleEulerEquations2D) + +Entropy conserving and kinetic energy preserving two-point flux by 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 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, equation::CompressibleEulerEquations1D) + # Unpack left and right state + rho_ll, rho_v1_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + p_ll = (equation.gamma - 1) * (rho_e_ll - 0.5 * rho_ll * (v1_ll^2)) + p_rr = (equation.gamma - 1) * (rho_e_rr - 0.5 * rho_rr * (v1_rr^2 )) + + # Compute the necessary mean values + rho_mean = ln_mean(rho_ll, rho_rr) + rho_p_mean = ln_mean(rho_ll / p_ll, rho_rr / p_rr) + v1_avg = 0.5 * (v1_ll + v1_rr) + p_avg = 0.5 * (p_ll + p_rr) + velocity_square_avg = 0.5 * (v1_ll*v1_rr) + + # Calculate fluxes depending on orientation + if orientation == 1 + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * ( velocity_square_avg + 1 / ((equation.gamma-1) * rho_p_mean) ) + 0.5 * (p_ll*v1_rr + p_rr*v1_ll) + end + + return SVector(f1, f2, f3) +end + + +function flux_lax_friedrichs(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) + # Calculate primitive variables and speed of sound + rho_ll, rho_v1_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + v_mag_ll = abs(v1_ll) + p_ll = (equation.gamma - 1) * (rho_e_ll - 1/2 * rho_ll * v_mag_ll^2) + c_ll = sqrt(equation.gamma * p_ll / rho_ll) + v1_rr = rho_v1_rr / rho_rr + v_mag_rr = abs(v1_rr) + p_rr = (equation.gamma - 1) * (rho_e_rr - 1/2 * rho_rr * v_mag_rr^2) + c_rr = sqrt(equation.gamma * p_rr / rho_rr) + + # Obtain left and right fluxes + f_ll = calcflux(u_ll, orientation, equation) + f_rr = calcflux(u_rr, orientation, equation) + + λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) + f1 = 1/2 * (f_ll[1] + f_rr[1]) - 1/2 * λ_max * (rho_rr - rho_ll) + f2 = 1/2 * (f_ll[2] + f_rr[2]) - 1/2 * λ_max * (rho_v1_rr - rho_v1_ll) + f3 = 1/2 * (f_ll[3] + f_rr[3]) - 1/2 * λ_max * (rho_e_rr - rho_e_ll) + + return SVector(f1, f2, f3) +end + + +function flux_hll(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) + # Calculate primitive variables and speed of sound + rho_ll, rho_v1_ll, rho_e_ll = u_ll + rho_rr, rho_v1_rr, rho_e_rr = u_rr + + v1_ll = rho_v1_ll / rho_ll + p_ll = (equation.gamma - 1) * (rho_e_ll - 1/2 * rho_ll * v1_ll^2) + + v1_rr = rho_v1_rr / rho_rr + p_rr = (equation.gamma - 1) * (rho_e_rr - 1/2 * rho_rr * v1_rr^2) + + # Obtain left and right fluxes + f_ll = calcflux(u_ll, orientation, equation) + f_rr = calcflux(u_rr, orientation, equation) + + if orientation == 1 # x-direction + Ssl = v1_ll - sqrt(equation.gamma * p_ll / rho_ll) + Ssr = v1_rr + sqrt(equation.gamma * p_rr / rho_rr) + end + + if Ssl >= 0.0 && Ssr > 0.0 + f1 = f_ll[1] + f2 = f_ll[2] + f3 = f_ll[3] + #f4 = f_ll[4] + elseif Ssr <= 0.0 && Ssl < 0.0 + f1 = f_rr[1] + f2 = f_rr[2] + f3 = f_rr[3] + #f4 = f_rr[4] + else + f1 = (Ssr*f_ll[1] - Ssl*f_rr[1] + Ssl*Ssr*(rho_rr[1] - rho_ll[1]))/(Ssr - Ssl) + f2 = (Ssr*f_ll[2] - Ssl*f_rr[2] + Ssl*Ssr*(rho_v1_rr[1] - rho_v1_ll[1]))/(Ssr - Ssl) + f3 = (Ssr*f_ll[3] - Ssl*f_rr[3] + Ssl*Ssr*(rho_e_rr[1] - rho_e_ll[1]))/(Ssr - Ssl) + end + + return SVector(f1, f2, f3) +end + + +# Determine maximum stable time step based on polynomial degree and CFL number +function calc_max_dt(u, element_id, invjacobian, cfl, + equation::CompressibleEulerEquations1D, dg) + λ_max = 0.0 + for i in 1:nnodes(dg) + rho, rho_v1, rho_e = get_node_vars(u, dg, i, element_id) + v1 = rho_v1 / rho + v_mag = abs(v1) + p = (equation.gamma - 1) * (rho_e - 1/2 * rho * v_mag^2) + c = sqrt(equation.gamma * p / rho) + λ_max = max(λ_max, v_mag + c) + end + + dt = cfl * 2 / (nnodes(dg) * invjacobian * λ_max) + + return dt +end + + +# Convert conservative variables to primitive +@inline function cons2prim(u, equation::CompressibleEulerEquations1D) + rho, rho_v1, rho_e = u + + v1 = rho_v1 / rho + p = (equation.gamma - 1) * (rho_e - 0.5 * rho * (v1^2)) + + return SVector(rho, v1, p) +end + + +# Convert conservative variables to entropy +@inline function cons2entropy(u, equation::CompressibleEulerEquations1D) + rho, rho_v1, rho_e = u + + v1 = rho_v1 / rho + v_square = v1^2 + p = (equation.gamma - 1) * (rho_e - 0.5 * rho * v_square) + s = log(p) - equation.gamma*log(rho) + rho_p = rho / p + + w1 = (equation.gamma - s) / (equation.gamma-1) - 0.5 * rho_p * v_square + w2 = rho_p * v1 + w3 = -rho_p + + return SVector(w1, w2, w3) +end + + +# Convert primitive to conservative variables +@inline function prim2cons(prim, equation::CompressibleEulerEquations1D) + rho, v1, p = prim + rho_v1 = rho * v1 + rho_e = p/(equation.gamma-1) + 0.5 * (rho_v1 * v1) + return SVector(rho, rho_v1, rho_e) +end + + +#@inline function density(u, equation::CompressibleEulerEquations2D) +# rho = u[1] +# return rho +#end + + +#@inline function pressure(u, equation::CompressibleEulerEquations2D) +# rho, rho_v1, rho_v2, rho_e = u +# p = (equation.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) +# return p +#end + + +#@inline function density_pressure(u, equation::CompressibleEulerEquations2D) +# rho, rho_v1, rho_v2, rho_e = u +# rho_times_p = (equation.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2 + rho_v2^2)) +# return rho_times_p +#end + + +# Convert conservative variables to indicator variable for discontinuities (elementwise version) +@inline function cons2indicator!(indicator, cons, element_id, n_nodes, indicator_variable, + equation::CompressibleEulerEquations1D) + for i in 1:n_nodes + indicator[1, i] = cons2indicator(cons[1, i, element_id], cons[2, i, element_id], + cons[3, i, element_id], + indicator_variable, equation) + end +end + + +# Convert conservative variables to indicator variable for discontinuities (pointwise version) +@inline function cons2indicator(rho, rho_v1, rho_e, ::Val{:density}, + equation::CompressibleEulerEquations1D) + # Indicator variable is rho + return rho +end + + +# Convert conservative variables to indicator variable for discontinuities (pointwise version) +@inline function cons2indicator(rho, rho_v1, rho_e, ::Val{:density_pressure}, + equation::CompressibleEulerEquations1D) + v1 = rho_v1/rho + + # Calculate pressure + p = (equation.gamma - 1) * (rho_e - 1/2 * rho * (v1^2)) + + # Indicator variable is rho * p + return rho * p +end + + +# Convert conservative variables to indicator variable for discontinuities (pointwise version) +@inline function cons2indicator(rho, rho_v1, rho_e, ::Val{:pressure}, + equation::CompressibleEulerEquations1D) + v1 = rho_v1/rho + + # Indicator variable is p + return (equation.gamma - 1) * (rho_e - 1/2 * rho * (v1^2)) +end + + +# Calculates the entropy flux in direction "orientation" and the entropy variables for a state cons +# NOTE: This method seems to work currently (b82534e) but is never used anywhere. Thus it is +# commented here until someone uses it or writes a test for it. +# @inline function cons2entropyvars_and_flux(gamma::Float64, cons, orientation::Int) +# entropy = MVector{4, Float64}(undef) +# v = (cons[2] / cons[1] , cons[3] / cons[1]) +# v_square= v[1]*v[1]+v[2]*v[2] +# p = (gamma - 1) * (cons[4] - 1/2 * (cons[2] * v[1] + cons[3] * v[2])) +# rho_p = cons[1] / p +# # thermodynamic entropy +# s = log(p) - gamma*log(cons[1]) +# # mathematical entropy +# S = - s*cons[1]/(gamma-1) +# # entropy variables +# entropy[1] = (gamma - s)/(gamma-1) - 0.5*rho_p*v_square +# entropy[2] = rho_p*v[1] +# entropy[3] = rho_p*v[2] +# entropy[4] = -rho_p +# # entropy flux +# entropy_flux = S*v[orientation] +# return entropy, entropy_flux +# end + + +# Calculate thermodynamic entropy for a conservative state `cons` +@inline function entropy_thermodynamic(cons, equation::CompressibleEulerEquations1D) + # Pressure + p = (equation.gamma - 1) * (cons[3] - 1/2 * (cons[2]^2) / cons[1]) + + # Thermodynamic entropy + s = log(p) - equation.gamma*log(cons[1]) + + return s +end + + +# Calculate mathematical entropy for a conservative state `cons` +@inline function entropy_math(cons, equation::CompressibleEulerEquations1D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equation) * cons[1] / (equation.gamma - 1) + + return S +end + + +# Default entropy is the mathematical entropy +@inline entropy(cons, equation::CompressibleEulerEquations1D) = entropy_math(cons, equation) + + +# Calculate total energy for a conservative state `cons` +@inline energy_total(cons, ::CompressibleEulerEquations1D) = cons[3] + + +# Calculate kinetic energy for a conservative state `cons` +@inline function energy_kinetic(cons, equation::CompressibleEulerEquations1D) + return 0.5 * (cons[2]^2)/cons[1] +end + + +# Calculate internal energy for a conservative state `cons` +@inline function energy_internal(cons, equation::CompressibleEulerEquations1D) + return energy_total(cons, equation) - energy_kinetic(cons, equation) +end diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 8256c12efda..42739a91e0c 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -32,9 +32,9 @@ function make_equations(name::String, ndims_) error("Unsupported number of spatial dimensions: ", ndims_) end elseif name == "CompressibleEulerEquations" - #if ndims_ == 1 - # return CompressibleEulerEquations1D() - if ndims_ == 2 + if ndims_ == 1 + return CompressibleEulerEquations1D() + elseif ndims_ == 2 return CompressibleEulerEquations2D() elseif ndims_ == 3 return CompressibleEulerEquations3D() @@ -98,7 +98,7 @@ include("3d/linear_scalar_advection.jl") # CompressibleEulerEquations abstract type AbstractCompressibleEulerEquations{NDIMS, NVARS} <: AbstractEquation{NDIMS, NVARS} end -#include("1d/compressible_euler.jl") +include("1d/compressible_euler.jl") include("2d/compressible_euler.jl") include("3d/compressible_euler.jl") diff --git a/src/run.jl b/src/run.jl index 50571a6d87a..c56a0921e93 100644 --- a/src/run.jl +++ b/src/run.jl @@ -562,7 +562,10 @@ function compute_linear_structure(parameters_file, source_terms=nothing; verbose b = vec(-solver.elements.u_t) |> copy # set the source terms to zero to extract the linear operator - if solver isa Dg2D + if solver isa Dg1D + solver = Dg1D(solver.equations, solver.surface_flux_function, solver.volume_flux_function, solver.initial_conditions, + source_terms, mesh, polydeg(solver)) + elseif solver isa Dg2D solver = Dg2D(solver.equations, solver.surface_flux_function, solver.volume_flux_function, solver.initial_conditions, source_terms, mesh, polydeg(solver)) elseif solver isa Dg3D diff --git a/src/solvers/dg/1d/containers.jl b/src/solvers/dg/1d/containers.jl index 77ed2b0baa9..7df6c71ba10 100644 --- a/src/solvers/dg/1d/containers.jl +++ b/src/solvers/dg/1d/containers.jl @@ -8,7 +8,7 @@ struct ElementContainer1D{NVARS, POLYDEG} <: AbstractContainer inverse_jacobian::Vector{Float64} # [elements] node_coordinates::Array{Float64, 3} # [orientation, i, elements] surface_ids::Matrix{Int} # [direction, elements] - surface_flux_values::Array{Float64, 3} # [variables, direction, elements] #i weg ? + surface_flux_values::Array{Float64, 3} # [variables, direction, elements] cell_ids::Vector{Int} # [elements] end diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 96beec6aa0e..3169111193f 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -1477,6 +1477,8 @@ function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{fa end end +#TODO 1D? + # Calculate and store Riemann and nonconservative fluxes across interfaces function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{true}, dg::Dg1D) #TODO temporary workaround while implementing the other stuff diff --git a/utils/euler-manufactured.jl b/utils/euler-manufactured.jl index 9fdeb33ce4b..763b02c76fc 100644 --- a/utils/euler-manufactured.jl +++ b/utils/euler-manufactured.jl @@ -17,7 +17,6 @@ julia> euler2d() julia> euler3d() =# - using Reduce @force using Reduce.Algebra @@ -41,6 +40,24 @@ source_rho_v2 := df(rho_v2, t) + df(rho * v1 * v2, x) + df(rho * v2^2 + p, y) source_rho_e := df(rho_e, t) + df((rho_e + p) * v1, x) + df((rho_e + p) * v2, y); =# + +function euler1d() + quote + ini = c + a * sin(ω * (x - t)) + rho = ini + rho_v1 = ini + rho_e = ini^2 + + v1 = rho_v1 / rho + p = (γ - 1) * (rho_e - 1/2 * rho * v1^2) + + source_rho = df(rho, t) + df(rho_v1, x) + source_rho_v1 = df(rho_v1, t) + df(rho * v1^2 + p, x) #+ df(rho * v1 * v2, y) + source_rho_e = df(rho_e, t) + df((rho_e + p) * v1, x) #+ df((rho_e + p) * v2, y) + end |> rcall +end + + function euler2d() quote ini = c + a * sin(ω * (x + y - t)) @@ -52,7 +69,6 @@ function euler2d() v1 = rho_v1 / rho v2 = rho_v2 / rho p = (γ - 1) * (rho_e - 1/2 * rho * (v1^2 + v2^2)) - source_rho = df(rho, t) + df(rho_v1, x) + df(rho_v2, y) source_rho_v1 = df(rho_v1, t) + df(rho * v1^2 + p, x) + df(rho * v1 * v2, y) source_rho_v2 = df(rho_v2, t) + df(rho * v1 * v2, x) + df(rho * v2^2 + p, y) From 01155ebb5c56c0dab9fd05acd8f29559a4ea5750 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 19 Sep 2020 05:52:24 +0200 Subject: [PATCH 03/55] Re-arrange manufactured solution generator --- utils/euler-manufactured.jl | 43 +++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/utils/euler-manufactured.jl b/utils/euler-manufactured.jl index 763b02c76fc..6b3e04d2fc6 100644 --- a/utils/euler-manufactured.jl +++ b/utils/euler-manufactured.jl @@ -13,6 +13,7 @@ julia> includet("euler-manufactured.jl") # Run methods to generate source terms. The source terms that need to be # implemented can be found in the `source_...` variables of the quote'd output. # If you want, you can also modify, e.g., the ini method to get different manufactured solutions. +julia> euler1d() julia> euler2d() julia> euler3d() =# @@ -20,24 +21,22 @@ julia> euler3d() using Reduce @force using Reduce.Algebra -# Original Reduce code (CompressibleEulerEquations 2D) + +# Original Reduce code (CompressibleEulerEquations 1D) #= clear(γ,f,A,ω,c,ini,rho,rho_v1,rho_v2,rho_v3,rho_e,v1,v2,p,x,y,t,u1,u2,u3,u4); -ini := c + A * sin(ω * (x + y - t)); +ini := c + A * sin(ω * (x - t)); rho := ini; rho_v1 := ini; -rho_v2 := ini; rho_e := ini^2; v1 := rho_v1 / rho; -v2 := rho_v2 / rho; -p := (γ - 1) * (rho_e - 1/2 * rho * (v1^2 + v2^2)); +p := (γ - 1) * (rho_e - 1/2 * rho * v1^2); -source_rho := df(rho, t) + df(rho_v1, x) + df(rho_v2, y); -source_rho_v1 := df(rho_v1, t) + df(rho * v1^2 + p, x) + df(rho * v1 * v2, y); -source_rho_v2 := df(rho_v2, t) + df(rho * v1 * v2, x) + df(rho * v2^2 + p, y); -source_rho_e := df(rho_e, t) + df((rho_e + p) * v1, x) + df((rho_e + p) * v2, y); +source_rho := df(rho, t) + df(rho_v1, x) +source_rho_v1 := df(rho_v1, t) + df(rho * v1^2 + p, x) +source_rho_e := df(rho_e, t) + df((rho_e + p) * v1, x) =# @@ -52,12 +51,33 @@ function euler1d() p = (γ - 1) * (rho_e - 1/2 * rho * v1^2) source_rho = df(rho, t) + df(rho_v1, x) - source_rho_v1 = df(rho_v1, t) + df(rho * v1^2 + p, x) #+ df(rho * v1 * v2, y) - source_rho_e = df(rho_e, t) + df((rho_e + p) * v1, x) #+ df((rho_e + p) * v2, y) + source_rho_v1 = df(rho_v1, t) + df(rho * v1^2 + p, x) + source_rho_e = df(rho_e, t) + df((rho_e + p) * v1, x) end |> rcall end +# Original Reduce code (CompressibleEulerEquations 2D) +#= +clear(γ,f,A,ω,c,ini,rho,rho_v1,rho_v2,rho_v3,rho_e,v1,v2,p,x,y,t,u1,u2,u3,u4); + +ini := c + A * sin(ω * (x + y - t)); +rho := ini; +rho_v1 := ini; +rho_v2 := ini; +rho_e := ini^2; + +v1 := rho_v1 / rho; +v2 := rho_v2 / rho; +p := (γ - 1) * (rho_e - 1/2 * rho * (v1^2 + v2^2)); + +source_rho := df(rho, t) + df(rho_v1, x) + df(rho_v2, y); +source_rho_v1 := df(rho_v1, t) + df(rho * v1^2 + p, x) + df(rho * v1 * v2, y); +source_rho_v2 := df(rho_v2, t) + df(rho * v1 * v2, x) + df(rho * v2^2 + p, y); +source_rho_e := df(rho_e, t) + df((rho_e + p) * v1, x) + df((rho_e + p) * v2, y); +=# + + function euler2d() quote ini = c + a * sin(ω * (x + y - t)) @@ -69,6 +89,7 @@ function euler2d() v1 = rho_v1 / rho v2 = rho_v2 / rho p = (γ - 1) * (rho_e - 1/2 * rho * (v1^2 + v2^2)) + source_rho = df(rho, t) + df(rho_v1, x) + df(rho_v2, y) source_rho_v1 = df(rho_v1, t) + df(rho * v1^2 + p, x) + df(rho * v1 * v2, y) source_rho_v2 = df(rho_v2, t) + df(rho * v1 * v2, x) + df(rho * v2^2 + p, y) From c5d7e4e35d3714f4e989af5442edebf023d15e34 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Sat, 19 Sep 2020 06:06:06 +0200 Subject: [PATCH 04/55] Fix Euler conv test --- src/equations/1d/compressible_euler.jl | 29 +++++++++----------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index bac5bf0d5d0..b7e2ef11714 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -346,27 +346,18 @@ function source_terms_convergence_test(ut, u, x, element_id, t, n_nodes, equatio for i in 1:n_nodes x1 = x[1, i, element_id] - si, co = sincos((x1 - t)*ω) - tmp1 = co * A * ω - tmp2 = si * A - tmp3 = γ - 1 - tmp4 = (2*c - 1)*tmp3 - tmp5 = (2*tmp2*γ - 2*tmp2 + tmp4 + 1)*tmp1 - tmp6 = tmp2 + c - - ut[1, i, element_id] += tmp1 - ut[2, i, element_id] += tmp5 - #ut[3, i, j, element_id] += tmp5 - ut[3, i, element_id] += 2*((tmp6 - 1)*tmp3 + tmp6*γ)*tmp1 #TODO change from 2D to 1D + si, co = sincos((t - x1)*ω) + tmp = (-((4 * si * A - 4c) + 1) * (γ - 1) * co * A * ω) / 2 + + ut[2, i, element_id] += tmp + ut[3, i, element_id] += tmp # Original terms (without performanc enhancements) - # ut[1, i, element_id] += cos((x1 - t)*ω)*A*ω - # ut[2, i, element_id] += (2*sin((x1 - t)*ω)*A*γ - 2*sin((x1 - t)*ω)*A + - # 2*c*γ - 2*c - γ + 2)*cos((x1 - t)*ω)*A*ω - # ut[3, i, element_id] += (2*sin((x1 - t)*ω)*A*γ - 2*sin((x1 - t)*ω)*A + - # 2*c*γ - 2*c - γ + 2)*cos((x1 - t)*ω)*A*ω - # ut[4, i, element_id] += 2*((c - 1 + sin((x1 + x2 - t)*ω)*A)*(γ - 1) + - # (sin((x1 + x2 - t)*ω)*A + c)*γ)*cos((x1 + x2 - t)*ω)*A*ω + # ut[1, i, element_id] += 0 + # ut[2, i, element_id] += (-(((4 * sin((t - x1) * ω) * A - 4c) + 1)) * + # (γ - 1) * cos((t - x1) * ω) * A * ω) / 2 + # ut[3, i, element_id] += (-(((4 * sin((t - x1) * ω) * A - 4c) + 1)) * + # (γ - 1) * cos((t - x1) * ω) * A * ω) / 2 end return nothing From 271903eac94cce604c10607d57e5051f58fb5542 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Mon, 21 Sep 2020 11:15:18 +0200 Subject: [PATCH 05/55] Apply suggestions from code review Co-authored-by: Michael Schlottke-Lakemper --- Project.toml | 2 -- examples/1d/parameters.toml | 2 +- examples/2d/parameters.toml | 6 +++--- src/equations/1d/compressible_euler.jl | 13 +++++-------- src/equations/1d/linear_scalar_advection.jl | 2 +- src/solvers/dg/1d/dg.jl | 5 ++--- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Project.toml b/Project.toml index e8a9cbfd438..f07bee5cfed 100644 --- a/Project.toml +++ b/Project.toml @@ -12,8 +12,6 @@ Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -Reduce = "93e0c654-6965-5f22-aba9-9c1ae6b3c259" -Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" Tullio = "bc48ee85-29a4-5162-ae0b-a64e1601d4bc" diff --git a/examples/1d/parameters.toml b/examples/1d/parameters.toml index 4bbc165a436..5bcfd83e6d2 100644 --- a/examples/1d/parameters.toml +++ b/examples/1d/parameters.toml @@ -20,7 +20,7 @@ t_end = 1.0 #################################################################################################### # Solver solver = "dg" -polydeg = 4 +polydeg = 3 cfl = 0.2 n_steps_max = 10000 analysis_interval = 100 diff --git a/examples/2d/parameters.toml b/examples/2d/parameters.toml index a97ba0eb1ae..252bde76061 100644 --- a/examples/2d/parameters.toml +++ b/examples/2d/parameters.toml @@ -6,8 +6,8 @@ equations = "LinearScalarAdvectionEquation" advectionvelocity = [1.0, 1.0] # Need only for linarscalaradvection initial_conditions = "initial_conditions_convergence_test" -#initial_conditions = "initial_conditions_constant" -#initial_conditions = "initial_conditions_linear_x_y" +# initial_conditions = "initial_conditions_constant" +# initial_conditions = "initial_conditions_linear_x_y" surface_flux = "flux_lax_friedrichs" # source_terms = t_start = 0.0 @@ -21,7 +21,7 @@ t_end = 1.0 # Solver solver = "dg" polydeg = 3 -cfl = 0.2 +cfl = 0.8 n_steps_max = 10000 analysis_interval = 100 extra_analysis_quantities = ["entropy", "energy_total"] diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index b7e2ef11714..09c63ed477c 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -1,7 +1,7 @@ @doc raw""" CompressibleEulerEquations1D -The compressible Euler equations for an ideal gas in two space dimensions. +The compressible Euler equations for an ideal gas in one space dimension. """ struct CompressibleEulerEquations1D <: AbstractCompressibleEulerEquations{1, 3} gamma::Float64 @@ -421,11 +421,10 @@ end rho, rho_v1, rho_e = u v1 = rho_v1/rho p = (equation.gamma - 1) * (rho_e - 1/2 * rho * v1^2) - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = (rho_e + p) * v1 - end + # Ignore orientation since it is always "1" in 1D + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = (rho_e + p) * v1 return SVector(f1, f2, f3) end @@ -638,12 +637,10 @@ function flux_hll(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1 f1 = f_ll[1] f2 = f_ll[2] f3 = f_ll[3] - #f4 = f_ll[4] elseif Ssr <= 0.0 && Ssl < 0.0 f1 = f_rr[1] f2 = f_rr[2] f3 = f_rr[3] - #f4 = f_rr[4] else f1 = (Ssr*f_ll[1] - Ssl*f_rr[1] + Ssl*Ssr*(rho_rr[1] - rho_ll[1]))/(Ssr - Ssl) f2 = (Ssr*f_ll[2] - Ssl*f_rr[2] + Ssl*Ssr*(rho_v1_rr[1] - rho_v1_ll[1]))/(Ssr - Ssl) diff --git a/src/equations/1d/linear_scalar_advection.jl b/src/equations/1d/linear_scalar_advection.jl index cf28aa78519..780d8c5bf92 100644 --- a/src/equations/1d/linear_scalar_advection.jl +++ b/src/equations/1d/linear_scalar_advection.jl @@ -6,7 +6,7 @@ The linear scalar advection equation ```math \partial_t u + a \partial_1 u = 0 ``` -in one space dimensions with constant velocity `a`. +in one space dimension with constant velocity `a`. """ struct LinearScalarAdvectionEquation1D <: AbstractLinearScalarAdvectionEquation{1, 1} sources::String diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 3169111193f..bda7194fcbc 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -78,9 +78,8 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v # Sanity checks - if isperiodic(mesh.tree) #&& n_l2mortars == 0 && n_ecmortars == 0 - @assert n_interfaces == 1*n_elements ("For 1D and periodic domains and conforming elements, " - * "n_surf must be the same as 1*n_elem") + if isperiodic(mesh.tree) + @assert n_interfaces == 1*n_elements ("For 1D and periodic domains, n_surf must be the same as 1*n_elem") end # Initialize interpolation data structures From d37a853a3fa9d95d6bbeadbf4459973a57e39926 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Mon, 21 Sep 2020 16:06:53 +0200 Subject: [PATCH 06/55] Update 1d solver --- examples/1d/parameters_source_terms.toml | 2 +- src/equations/1d/compressible_euler.jl | 381 ++++---------------- src/equations/1d/linear_scalar_advection.jl | 30 ++ src/solvers/dg/1d/amr.jl | 90 ++--- src/solvers/dg/1d/containers.jl | 6 +- src/solvers/dg/1d/dg.jl | 368 ++++++++----------- 6 files changed, 297 insertions(+), 580 deletions(-) diff --git a/examples/1d/parameters_source_terms.toml b/examples/1d/parameters_source_terms.toml index 99b4a74c141..63ac13013c1 100644 --- a/examples/1d/parameters_source_terms.toml +++ b/examples/1d/parameters_source_terms.toml @@ -17,7 +17,7 @@ t_end = 2.0 #################################################################################################### # Solver solver = "dg" -polydeg = 3 +polydeg = 4 cfl = 0.6 n_steps_max = 10000 analysis_interval = 100 diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 09c63ed477c..9054e72e282 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -45,24 +45,6 @@ function initial_conditions_density_wave(x, t, equation::CompressibleEulerEquati rho_e = p / (equation.gamma - 1) + 1/2 * rho * v1^2 return @SVector [rho, rho_v1, rho_e] end -#= -function initial_conditions_pressure_pulse(x, t, equation::CompressibleEulerEquations1D) - rho = 1 - v1 = 1 - rho_v1 = rho * v1 - p = 1 + exp(-(x[1]^2))/2 - rho_e = p/(equation.gamma - 1) + 1/2 * rho * v1^2 - return @SVector [rho, rho_v1, rho_e] -end - -function initial_conditions_density_pressure_pulse(x, t, equation::CompressibleEulerEquations1D) - rho = 1 + exp(-(x[1]^2))/2 - v1 = 1 - rho_v1 = rho * v1 - p = 1 + exp(-(x[1]^2))/2 - rho_e = p/(equation.gamma - 1) + 1/2 * rho * v1^2 - return @SVector [rho, rho_v1, rho_e] -end function initial_conditions_constant(x, t, equation::CompressibleEulerEquations1D) rho = 1.0 @@ -70,7 +52,7 @@ function initial_conditions_constant(x, t, equation::CompressibleEulerEquations1 rho_e = 10.0 return @SVector [rho, rho_v1, rho_e] end -=# + function initial_conditions_convergence_test(x, t, equation::CompressibleEulerEquations1D) c = 2 A = 0.1 @@ -85,254 +67,101 @@ function initial_conditions_convergence_test(x, t, equation::CompressibleEulerEq return @SVector [rho, rho_v1, rho_e] end -#= -function initial_conditions_isentropic_vortex(x, t, equation::CompressibleEulerEquations1D) - # needs appropriate mesh size, e.g. [-10,-10]x[10,10] - # make sure that the inicenter does not exit the domain, e.g. T=10.0 - # initial center of the vortex - inicenter = SVector(0.0, 0.0) - # size and strength of the vortex - iniamplitude = 0.2 - # base flow - rho = 1.0 - v1 = 1.0 - v2 = 1.0 - vel = SVector(v1, v2) - p = 10.0 - vec = SVector(v1, v2) - rt = p / rho # ideal gas equation - cent = inicenter + vel*t # advection of center - cent = x - cent # distance to centerpoint - #cent=cross(iniaxis,cent) # distance to axis, tangent vector, length r - # cross product with iniaxis = [0,0,1] - cent = SVector(-cent[2], cent[1]) - r2 = cent[1]^2 + cent[2]^2 - du = iniamplitude/(2*π)*exp(0.5*(1-r2)) # vel. perturbation - dtemp = -(equation.gamma-1)/(2*equation.gamma*rt)*du^2 # isentrop - rho = rho * (1+dtemp)^(1\(equation.gamma-1)) - vel = vel + du*cent - v1, v2 = vel - p = p * (1+dtemp)^(equation.gamma/(equation.gamma-1)) - prim = SVector(rho, v1, v2, p) - return prim2cons(prim, equation) -end - -function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEquations2D) + + +function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEquations1D) # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) # Set up polar coordinates inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) - phi = atan(y_norm, x_norm) - sin_phi, cos_phi = sincos(phi) + r = abs(x_norm) + phi = atan(x_norm) + cos_phi = cos(phi) # Calculate primitive variables rho = r > 0.5 ? 1.0 : 1.1691 v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi - v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi p = r > 0.5 ? 1.0 : 1.245 - return prim2cons(SVector(rho, v1, v2, p), equation) + return prim2cons(SVector(rho, v1, p), equation) end -function initial_conditions_blast_wave(x, t, equation::CompressibleEulerEquations2D) +function initial_conditions_blast_wave(x, t, equation::CompressibleEulerEquations1D) # Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> "medium blast wave" # Set up polar coordinates inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) - phi = atan(y_norm, x_norm) - sin_phi, cos_phi = sincos(phi) + r = abs(x_norm) + phi = atan(x_norm) + cos_phi = cos(phi) # Calculate primitive variables rho = r > 0.5 ? 1.0 : 1.1691 v1 = r > 0.5 ? 0.0 : 0.1882 * cos_phi - v2 = r > 0.5 ? 0.0 : 0.1882 * sin_phi p = r > 0.5 ? 1.0E-3 : 1.245 - return prim2cons(SVector(rho, v1, v2, p), equation) + return prim2cons(SVector(rho, v1, p), equation) end -function initial_conditions_sedov_blast_wave(x, t, equation::CompressibleEulerEquations2D) +function initial_conditions_sedov_blast_wave(x, t, equation::CompressibleEulerEquations1D) # Set up polar coordinates inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) + r = abs(x_norm) # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) # r0 = 0.5 # = more reasonable setup E = 1.0 - p0_inner = 3 * (equation.gamma - 1) * E / (3 * pi * r0^2) + p0_inner = 6 * (equation.gamma - 1) * E / (3 * pi * r0) p0_outer = 1.0e-5 # = true Sedov setup # p0_outer = 1.0e-3 # = more reasonable setup # Calculate primitive variables rho = 1.0 v1 = 0.0 - v2 = 0.0 p = r > r0 ? p0_outer : p0_inner - return prim2cons(SVector(rho, v1, v2, p), equation) + return prim2cons(SVector(rho, v1, p), equation) end -function initial_conditions_medium_sedov_blast_wave(x, t, equation::CompressibleEulerEquations2D) - # Set up polar coordinates - inicenter = SVector(0.0, 0.0) - x_norm = x[1] - inicenter[1] - y_norm = x[2] - inicenter[2] - r = sqrt(x_norm^2 + y_norm^2) - - # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000 - r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6) - # r0 = 0.5 # = more reasonable setup - E = 1.0 - p0_inner = 3 * (equation.gamma - 1) * E / (3 * pi * r0^2) - # p0_outer = 1.0e-5 # = true Sedov setup - p0_outer = 1.0e-3 # = more reasonable setup - - # Calculate primitive variables - rho = 1.0 - v1 = 0.0 - v2 = 0.0 - p = r > r0 ? p0_outer : p0_inner +# Apply boundary conditions +function boundary_conditions_convergence_test(u_inner, orientation, direction, x, t, + surface_flux_function, + equation::CompressibleEulerEquations1D) + u_boundary = initial_conditions_convergence_test(x, t, equation) - return prim2cons(SVector(rho, v1, v2, p), equation) -end - -function initial_conditions_khi(x, t, equation::CompressibleEulerEquations2D) - # https://rsaa.anu.edu.au/research/established-projects/fyris/2-d-kelvin-helmholtz-test - # change discontinuity to tanh - # typical resolution 128^2, 256^2 - # domain size is [-0.5,0.5]^2 - dens0 = 1.0 # outside density - dens1 = 2.0 # inside density - velx0 = -0.5 # outside velocity - velx1 = 0.5 # inside velocity - slope = 50 # used for tanh instead of discontinuous initial condition - # pressure equilibrium - p = 2.5 - # y velocity v2 is only white noise - v2 = 0.01*(rand(Float64,1)[1]-0.5) - # density - rho = dens0 + (dens1-dens0) * 0.5*(1+(tanh(slope*(x[2]+0.25)) - (tanh(slope*(x[2]-0.25)) + 1))) - # x velocity is also augmented with noise - v1 = velx0 + (velx1-velx0) * 0.5*(1+(tanh(slope*(x[2]+0.25)) - (tanh(slope*(x[2]-0.25)) + 1)))+0.01*(rand(Float64,1)[1]-0.5) - return prim2cons(SVector(rho, v1, v2, p), equation) -end - -function initial_conditions_blob(x, t, equation::CompressibleEulerEquations2D) - # blob test case, see Agertz et al. https://arxiv.org/pdf/astro-ph/0610051.pdf - # other reference: https://arxiv.org/pdf/astro-ph/0610051.pdf - # change discontinuity to tanh - # typical domain is rectangular, we change it to a square, as Trixi can only do squares - # resolution 128^2, 256^2 - # domain size is [-20.0,20.0]^2 - # gamma = 5/3 for this test case - R = 1.0 # radius of the blob - # background density - dens0 = 1.0 - Chi = 10.0 # density contrast - # reference time of characteristic growth of KH instability equal to 1.0 - tau_kh = 1.0 - tau_cr = tau_kh/1.6 # crushing time - # determine background velocity - velx0 = 2*R*sqrt(Chi)/tau_cr - vely0 = 0.0 - Ma0 = 2.7 # background flow Mach number Ma=v/c - c = velx0/Ma0 # sound speed - # use perfect gas assumption to compute background pressure via the sound speed c^2 = gamma * pressure/density - p0 = c*c*dens0/equation.gamma - # initial center of the blob - inicenter = [-15,0] - x_rel = x-inicenter - r = sqrt(x_rel[1]^2 + x_rel[2]^2) - # steepness of the tanh transition zone - slope = 2 - # density blob - dens = dens0 + (Chi-1) * 0.5*(1+(tanh(slope*(r+R)) - (tanh(slope*(r-R)) + 1))) - # velocity blob is zero - velx = velx0 - velx0 * 0.5*(1+(tanh(slope*(r+R)) - (tanh(slope*(r-R)) + 1))) - return prim2cons(SVector(dens, velx, vely0, p0), equation) -end - -function initial_conditions_jeans_instability(x, t, equation::CompressibleEulerEquations2D) - # Jeans gravitational instability test case - # see Derigs et al. https://arxiv.org/abs/1605.03572; Sec. 4.6 - # OBS! this uses cgs (centimeter, gram, second) units - # periodic boundaries - # domain size [0,L]^2 depends on the wave number chosen for the perturbation - # OBS! Be very careful here L must be chosen such that problem is periodic - # typical final time is T = 5 - # gamma = 5/3 - dens0 = 1.5e7 # g/cm^3 - pres0 = 1.5e7 # dyn/cm^2 - delta0 = 1e-3 - # set wave vector values for pertubation (units 1/cm) - # see FLASH manual: https://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel.pdf - kx = 2.0*pi/0.5 # 2π/λ_x, λ_x = 0.5 - ky = 0.0 # 2π/λ_y, λ_y = 1e10 - k_dot_x = kx*x[1] + ky*x[2] - # perturb density and pressure away from reference states ρ_0 and p_0 - dens = dens0*(1.0 + delta0*cos(k_dot_x)) # g/cm^3 - pres = pres0*(1.0 + equation.gamma*delta0*cos(k_dot_x)) # dyn/cm^2 - # flow starts as stationary - velx = 0.0 # cm/s - vely = 0.0 # cm/s - return prim2cons(SVector(dens, velx, vely, pres), equation) -end - -function initial_conditions_eoc_test_coupled_euler_gravity(x, t, equation::CompressibleEulerEquations2D) - # OBS! this assumes that γ = 2 other manufactured source terms are incorrect - if equation.gamma != 2.0 - error("adiabatic constant must be 2 for the coupling convergence test") + # Calculate boundary flux + if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation, equation) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equation) end - c = 2.0 - A = 0.1 - ini = c + A * sin(pi * (x[1] + x[2] - t)) - G = 1.0 # gravitational constant - - rho = ini - v1 = 1.0 - v2 = 1.0 - p = ini^2 * G / pi # * 2 / ndims, but ndims==2 here - return prim2cons(SVector(rho, v1, v2, p), equation) + return flux end -function initial_conditions_sedov_self_gravity(x, t, equation::CompressibleEulerEquations2D) - # Set up polar coordinates - r = sqrt(x[1]^2 + x[2]^2) - - # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000 - r0 = 0.125 # = 4.0 * smallest dx (for domain length=8 and max-ref=8) - E = 1.0 - p_inner = (equation.gamma - 1) * E / (pi * r0^2) - p_ambient = 1e-5 # = true Sedov setup - - # Calculate primitive variables - # use a logistic function to tranfer density value smoothly - L = 1.0 # maximum of function - x0 = 1.0 # center point of function - k = -150.0 # sharpness of transfer - logistic_function_rho = L/(1.0 + exp(-k*(r - x0))) - rho_ambient = 1e-5 - rho = max(logistic_function_rho, rho_ambient) # clip background density to not be so tiny - - # velocities are zero +function boundary_conditions_sedov_self_gravity(u_inner, orientation, direction, x, t, + surface_flux_function, + equation::CompressibleEulerEquations1D) + # velocities are zero, density/pressure are ambient values according to + # initial_conditions_sedov_self_gravity + rho = 1e-5 v1 = 0.0 - v2 = 0.0 + p = 1e-5 - # use a logistic function to tranfer pressure value smoothly - logistic_function_p = p_inner/(1.0 + exp(-k*(r - r0))) - p = max(logistic_function_p, p_ambient) + u_boundary = prim2cons(SVector(rho, v1, p), equation) - return prim2cons(SVector(rho, v1, v2, p), equation) + # Calculate boundary flux + if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation, equation) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equation) + end + + return flux end -=# + # Apply source terms function source_terms_convergence_test(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations1D) # Same settings as in `initial_conditions` @@ -430,7 +259,7 @@ end """ - function flux_shima_etal(u_ll, u_rr, orientation, equation::CompressibleEulerEquations2D) + function flux_shima_etal(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) This flux is is a modification of the original kinetic energy preserving two-point flux by Kuya, Totani and Kawai (2018) @@ -458,20 +287,19 @@ Nao Shima, Yuichi Kuya, Yoshiharu Tamaki, Soshi Kawai (JCP 2020) p_avg = 1/2 * ( p_ll + p_rr) kin_avg = 1/2 * (v1_ll*v1_rr) - # Calculate fluxes depending on orientation - if orientation == 1 - pv1_avg = 1/2 * (p_ll*v1_rr + p_rr*v1_ll) - f1 = rho_avg * v1_avg - f2 = rho_avg * v1_avg * v1_avg + p_avg - f3 = p_avg*v1_avg/(equation.gamma-1) + rho_avg*v1_avg*kin_avg + pv1_avg - end + # Calculate fluxes + # Ignore orientation since it is always "1" in 1D + pv1_avg = 1/2 * (p_ll*v1_rr + p_rr*v1_ll) + f1 = rho_avg * v1_avg + f2 = rho_avg * v1_avg * v1_avg + p_avg + f3 = p_avg*v1_avg/(equation.gamma-1) + rho_avg*v1_avg*kin_avg + pv1_avg return SVector(f1, f2, f3) end """ - flux_kennedy_gruber(u_ll, u_rr, orientation, equation::CompressibleEulerEquations2D) + flux_kennedy_gruber(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1D) Kinetic energy preserving two-point flux by Kennedy and Gruber (2008) Reduced aliasing formulations of the convective terms within the @@ -493,12 +321,10 @@ Kinetic energy preserving two-point flux by Kennedy and Gruber (2008) (equation.gamma - 1) * (rho_e_rr - 1/2 * rho_rr * (v1_rr^2))) e_avg = 1/2 * (rho_e_ll/rho_ll + rho_e_rr/rho_rr) - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_avg * v1_avg - f2 = rho_avg * v1_avg * v1_avg + p_avg - f3 = (rho_avg * e_avg + p_avg) * v1_avg - end + # Ignore orientation since it is always "1" in 1D + f1 = rho_avg * v1_avg + f2 = rho_avg * v1_avg * v1_avg + p_avg + f3 = (rho_avg * e_avg + p_avg) * v1_avg return SVector(f1, f2, f3) end @@ -535,12 +361,11 @@ Entropy conserving two-point flux by Chandrashekar (2013) p_mean = 0.5*rho_avg/beta_avg velocity_square_avg = specific_kin_ll + specific_kin_rr - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_mean - f3 = f1 * 0.5*(1/(equation.gamma-1)/beta_mean - velocity_square_avg)+f2*v1_avg - end + # Calculate fluxes + # Ignore orientation since it is always "1" in 1D + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_mean + f3 = f1 * 0.5*(1/(equation.gamma-1)/beta_mean - velocity_square_avg)+f2*v1_avg return SVector(f1, f2, f3) end @@ -575,12 +400,11 @@ See also Ranocha (2020) p_avg = 0.5 * (p_ll + p_rr) velocity_square_avg = 0.5 * (v1_ll*v1_rr) - # Calculate fluxes depending on orientation - if orientation == 1 - f1 = rho_mean * v1_avg - f2 = f1 * v1_avg + p_avg - f3 = f1 * ( velocity_square_avg + 1 / ((equation.gamma-1) * rho_p_mean) ) + 0.5 * (p_ll*v1_rr + p_rr*v1_ll) - end + # Calculate fluxes + # Ignore orientation since it is always "1" in 1D + f1 = rho_mean * v1_avg + f2 = f1 * v1_avg + p_avg + f3 = f1 * ( velocity_square_avg + 1 / ((equation.gamma-1) * rho_p_mean) ) + 0.5 * (p_ll*v1_rr + p_rr*v1_ll) return SVector(f1, f2, f3) end @@ -628,10 +452,8 @@ function flux_hll(u_ll, u_rr, orientation, equation::CompressibleEulerEquations1 f_ll = calcflux(u_ll, orientation, equation) f_rr = calcflux(u_rr, orientation, equation) - if orientation == 1 # x-direction - Ssl = v1_ll - sqrt(equation.gamma * p_ll / rho_ll) - Ssr = v1_rr + sqrt(equation.gamma * p_rr / rho_rr) - end + Ssl = v1_ll - sqrt(equation.gamma * p_ll / rho_ll) + Ssr = v1_rr + sqrt(equation.gamma * p_rr / rho_rr) if Ssl >= 0.0 && Ssr > 0.0 f1 = f_ll[1] @@ -708,65 +530,22 @@ end end -#@inline function density(u, equation::CompressibleEulerEquations2D) -# rho = u[1] -# return rho -#end - - -#@inline function pressure(u, equation::CompressibleEulerEquations2D) -# rho, rho_v1, rho_v2, rho_e = u -# p = (equation.gamma - 1) * (rho_e - 0.5 * (rho_v1^2 + rho_v2^2) / rho) -# return p -#end - - -#@inline function density_pressure(u, equation::CompressibleEulerEquations2D) -# rho, rho_v1, rho_v2, rho_e = u -# rho_times_p = (equation.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2 + rho_v2^2)) -# return rho_times_p -#end - - -# Convert conservative variables to indicator variable for discontinuities (elementwise version) -@inline function cons2indicator!(indicator, cons, element_id, n_nodes, indicator_variable, - equation::CompressibleEulerEquations1D) - for i in 1:n_nodes - indicator[1, i] = cons2indicator(cons[1, i, element_id], cons[2, i, element_id], - cons[3, i, element_id], - indicator_variable, equation) - end +@inline function density(u, equation::CompressibleEulerEquations1D) + rho = u[1] + return rho end - -# Convert conservative variables to indicator variable for discontinuities (pointwise version) -@inline function cons2indicator(rho, rho_v1, rho_e, ::Val{:density}, - equation::CompressibleEulerEquations1D) - # Indicator variable is rho - return rho +@inline function pressure(u, equation::CompressibleEulerEquations1D) + rho, rho_v1, rho_e = u + p = (equation.gamma - 1) * (rho_e - 0.5 * (rho_v1^2) / rho) + return p end -# Convert conservative variables to indicator variable for discontinuities (pointwise version) -@inline function cons2indicator(rho, rho_v1, rho_e, ::Val{:density_pressure}, - equation::CompressibleEulerEquations1D) - v1 = rho_v1/rho - - # Calculate pressure - p = (equation.gamma - 1) * (rho_e - 1/2 * rho * (v1^2)) - - # Indicator variable is rho * p - return rho * p -end - - -# Convert conservative variables to indicator variable for discontinuities (pointwise version) -@inline function cons2indicator(rho, rho_v1, rho_e, ::Val{:pressure}, - equation::CompressibleEulerEquations1D) - v1 = rho_v1/rho - - # Indicator variable is p - return (equation.gamma - 1) * (rho_e - 1/2 * rho * (v1^2)) +@inline function density_pressure(u, equation::CompressibleEulerEquations1D) + rho, rho_v1, rho_e = u + rho_times_p = (equation.gamma - 1) * (rho * rho_e - 0.5 * (rho_v1^2)) + return rho_times_p end diff --git a/src/equations/1d/linear_scalar_advection.jl b/src/equations/1d/linear_scalar_advection.jl index 780d8c5bf92..108fb824bf0 100644 --- a/src/equations/1d/linear_scalar_advection.jl +++ b/src/equations/1d/linear_scalar_advection.jl @@ -69,6 +69,36 @@ function initial_conditions_linear_x(x, t, equation::LinearScalarAdvectionEquati return @SVector [x_trans[1]] end +# Apply boundary conditions +function boundary_conditions_gauss(u_inner, orientation, direction, x, t, surface_flux_function, + equation::LinearScalarAdvectionEquation1D) + u_boundary = initial_conditions_gauss(x, t, equation) + + # Calculate boundary flux + if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation, equation) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equation) + end + + return flux +end + + +function boundary_conditions_linear_x(u_inner, orientation, direction, x, t, + surface_flux_function, + equation::LinearScalarAdvectionEquation1D) + u_boundary = initial_conditions_linear_x(x, t, equation) + + # Calculate boundary flux + if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation, equation) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equation) + end + + return flux +end # Pre-defined source terms should be implemented as diff --git a/src/solvers/dg/1d/amr.jl b/src/solvers/dg/1d/amr.jl index 40f04b2d98d..44ee9563160 100644 --- a/src/solvers/dg/1d/amr.jl +++ b/src/solvers/dg/1d/amr.jl @@ -46,15 +46,14 @@ function refine!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, n_interfaces = ninterfaces(interfaces) # Initialize boundaries - boundaries = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + boundaries, n_boundaries_per_direction = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) n_boundaries = nboundaries(boundaries) # Sanity check - if isperiodic(mesh.tree) #&& n_l2mortars == 0 && n_ecmortars == 0 - @assert n_interfaces == 1*n_elements ("For 1D and periodic domains and conforming elements, " - * "n_surf must be the same as 1*n_elem") + if isperiodic(mesh.tree) + @assert n_interfaces == 1*n_elements ("For 1D and periodic domains, n_surf must be the same as 1*n_elem") end # Update DG instance with new data @@ -64,62 +63,39 @@ function refine!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, dg.n_interfaces = n_interfaces dg.boundaries = boundaries dg.n_boundaries = n_boundaries + dg.n_boundaries_per_direction = n_boundaries_per_direction end # Refine solution data u for an element, using L2 projection (interpolation) function refine_element!(u, element_id, old_u, old_element_id, dg::Dg1D) # Store new element ids - lower_left_id = element_id - lower_right_id = element_id + 1 - #upper_left_id = element_id + 2 - #upper_right_id = element_id + 3 + left_id = element_id + right_id = element_id + 1 - # Interpolate to lower left element + # Interpolate to left element for i in 1:nnodes(dg) acc = zero(get_node_vars(u, dg, i, element_id)) for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, old_element_id) # * forward_lower[i, k] #TODO + acc += get_node_vars(old_u, dg, k, old_element_id) end - set_node_vars!(u, acc, dg, i, lower_left_id) + set_node_vars!(u, acc, dg, i, left_id) end - # Interpolate to bottom lower left element + # Interpolate to left element #multiply_dimensionwise!( # view(u, :, :, lower_left_id), forward_lower, # view(old_u, :, :, old_element_id)) - # Interpolate to lower right element + # Interpolate to right element for i in 1:nnodes(dg) acc = zero(get_node_vars(u, dg, i, element_id)) for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, old_element_id)# * forward_upper[i, k] + acc += get_node_vars(old_u, dg, k, old_element_id) end - set_node_vars!(u, acc, dg, i, lower_right_id) + set_node_vars!(u, acc, dg, i, right_id) end - # Interpolate to lower right element - #multiply_dimensionwise!( - # view(u, :, :, lower_right_id), forward_upper, - # view(old_u, :, :, old_element_id)) - - # Interpolate to upper left element - #for j in 1:nnodes(dg), i in 1:nnodes(dg) - # acc = zero(get_node_vars(u, dg, i, j, element_id)) - # for l in 1:nnodes(dg), k in 1:nnodes(dg) - # acc += get_node_vars(old_u, dg, k, l, old_element_id) * forward_lower[i, k] * forward_upper[j, l] - # end - # set_node_vars!(u, acc, dg, i, j, upper_left_id) - #end - - # Interpolate to upper right element - #for j in 1:nnodes(dg), i in 1:nnodes(dg) - # acc = zero(get_node_vars(u, dg, i, j, element_id)) - # for l in 1:nnodes(dg), k in 1:nnodes(dg) - # acc += get_node_vars(old_u, dg, k, l, old_element_id) * forward_upper[i, k] * forward_upper[j, l] - # end - # set_node_vars!(u, acc, dg, i, j, upper_right_id) - #end end @@ -181,13 +157,12 @@ function coarsen!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, n_interfaces = ninterfaces(interfaces) # Initialize boundaries - boundaries = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + boundaries, n_boundaries_per_direction = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) n_boundaries = nboundaries(boundaries) # Sanity check - if isperiodic(mesh.tree) #&& n_l2mortars == 0 && n_ecmortars == 0 - @assert n_interfaces == 1*n_elements ("For 1D and periodic domains and conforming elements, " - * "n_surf must be the same as 1*n_elem") + if isperiodic(mesh.tree) + @assert n_interfaces == 1*n_elements ("For 1D and periodic domains, n_surf must be the same as 1*n_elem") end # Update DG instance with new data @@ -197,40 +172,29 @@ function coarsen!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, dg.n_interfaces = n_interfaces dg.boundaries = boundaries dg.n_boundaries = n_boundaries + dg.n_boundaries_per_direction = n_boundaries_per_direction end # Coarsen solution data u for four elements, using L2 projection function coarsen_elements!(u, element_id, old_u, old_element_id, dg::Dg1D) # Store old element ids - lower_left_id = old_element_id - lower_right_id = old_element_id + 1 - #upper_left_id = old_element_id + 2 - #upper_right_id = old_element_id + 3 + left_id = old_element_id + right_id = old_element_id + 1 for i in 1:nnodes(dg) acc = zero(get_node_vars(u, dg, i, element_id)) - # Project from lower left element + # Project from left element for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, lower_left_id) #* reverse_lower[i, k] #* reverse_lower[j, l] #TODO + acc += get_node_vars(old_u, dg, k, left_id) #* reverse_lower[i, k] #* reverse_lower[j, l] end - # Project from lower right element + # Project from right element for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, lower_right_id) #* reverse_upper[i, k] #* reverse_lower[j, l] + acc += get_node_vars(old_u, dg, k, right_id) #* reverse_upper[i, k] #* reverse_lower[j, l] end - # Project from upper left element - #for l in 1:nnodes(dg), k in 1:nnodes(dg) - # acc += get_node_vars(old_u, dg, k, l, upper_left_id) * reverse_lower[i, k] * reverse_upper[j, l] - #end - - # Project from upper right element - #for l in 1:nnodes(dg), k in 1:nnodes(dg) - # acc += get_node_vars(old_u, dg, k, l, upper_right_id) * reverse_upper[i, k] * reverse_upper[j, l] - #end - # Update value set_node_vars!(u, acc, dg, i, element_id) end @@ -342,7 +306,7 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) alpha = dg.element_variables[:amr_indicator_values] alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, false, - Val(:density), dg.thread_cache, dg) + density, dg.thread_cache, dg) # Iterate over all elements for element_id in 1:dg.n_elements @@ -387,7 +351,7 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) alpha = dg.element_variables[:amr_indicator_values] alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, false, - Val(:density), dg.thread_cache, dg) + density, dg.thread_cache, dg) # (Re-)initialize element variable storage for blending factor if (!haskey(dg.element_variables, :blending_factor) || @@ -448,7 +412,7 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) alpha = dg.element_variables[:amr_indicator_values] alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, dg.amr_alpha_smooth, - Val(:density_pressure), dg.thread_cache, dg) + density_pressure, dg.thread_cache, dg) # Iterate over all elements for element_id in 1:dg.n_elements @@ -487,7 +451,7 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) alpha = dg.element_variables[:amr_indicator_values] alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, dg.amr_alpha_smooth, - Val(:density_pressure), dg.thread_cache, dg) + density_pressure, dg.thread_cache, dg) # Iterate over all elements for element_id in 1:dg.n_elements diff --git a/src/solvers/dg/1d/containers.jl b/src/solvers/dg/1d/containers.jl index 7df6c71ba10..5248e456989 100644 --- a/src/solvers/dg/1d/containers.jl +++ b/src/solvers/dg/1d/containers.jl @@ -8,7 +8,7 @@ struct ElementContainer1D{NVARS, POLYDEG} <: AbstractContainer inverse_jacobian::Vector{Float64} # [elements] node_coordinates::Array{Float64, 3} # [orientation, i, elements] surface_ids::Matrix{Int} # [direction, elements] - surface_flux_values::Array{Float64, 3} # [variables, direction, elements] + surface_flux_values::Array{Float64, 3} # [variables, direction, elements] cell_ids::Vector{Int} # [elements] end @@ -40,7 +40,7 @@ nelements(elements::ElementContainer1D) = length(elements.cell_ids) # Container data structure (structure-of-arrays style) for DG interfaces struct InterfaceContainer1D{NVARS, POLYDEG} <: AbstractContainer - u::Array{Float64, 3} # [leftright, variables, i, interfaces] + u::Array{Float64, 3} # [leftright, variables, interfaces] neighbor_ids::Matrix{Int} # [leftright, interfaces] orientations::Vector{Int} # [interfaces] end @@ -65,7 +65,7 @@ ninterfaces(interfaces::InterfaceContainer1D) = length(interfaces.orientations) # Container data structure (structure-of-arrays style) for DG boundaries struct BoundaryContainer1D{NVARS, POLYDEG} <: AbstractContainer - u::Array{Float64, 3} # [leftright, variables,boundaries] + u::Array{Float64, 3} # [leftright, variables, boundaries] neighbor_ids::Vector{Int} # [boundaries] orientations::Vector{Int} # [boundaries] neighbor_sides::Vector{Int} # [boundaries] diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index bda7194fcbc..d30bbfa4ad8 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -1,6 +1,6 @@ # Main DG data structure that contains all relevant data for the DG solver mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, - SurfaceFlux, VolumeFlux, InitialConditions, SourceTerms, + SurfaceFlux, VolumeFlux, InitialConditions, SourceTerms, BoundaryConditions, VolumeIntegralType, ShockIndicatorVariable, VectorNnodes, MatrixNnodes, MatrixNnodes2, InverseVandermondeLegendre, @@ -21,6 +21,8 @@ mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, boundaries::BoundaryContainer1D{NVARS, POLYDEG} n_boundaries::Int + n_boundaries_per_direction::SVector{2, Int} + boundary_conditions::BoundaryConditions nodes::VectorNnodes weights::VectorNnodes @@ -73,7 +75,7 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v n_interfaces = ninterfaces(interfaces) # Initialize boundaries - boundaries = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) + boundaries, n_boundaries_per_direction = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) n_boundaries = nboundaries(boundaries) @@ -82,6 +84,9 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v @assert n_interfaces == 1*n_elements ("For 1D and periodic domains, n_surf must be the same as 1*n_elem") end + # Initialize boundary conditions + boundary_conditions = init_boundary_conditions(n_boundaries_per_direction, mesh) + # Initialize interpolation data structures n_nodes = POLYDEG + 1 nodes, weights = gauss_lobatto_nodes_weights(n_nodes) @@ -151,8 +156,10 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v shock_alpha_smooth = parameter("shock_alpha_smooth", true) # variable used to compute the shock capturing indicator - shock_indicator_variable = Val(Symbol(parameter("shock_indicator_variable", "density_pressure", - valid=["density", "density_pressure", "pressure"]))) + # "eval is evil" + # This is a temporary hack until we have switched to a library based approach + # with pure Julia code instead of parameter files. + shock_indicator_variable = eval(Symbol(parameter("shock_indicator_variable", "density_pressure"))) # maximum and minimum alpha for amr control amr_alpha_max = parameter("amr_alpha_max", 0.5) @@ -178,7 +185,8 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v initial_conditions, source_terms, elements, n_elements, interfaces, n_interfaces, - boundaries, n_boundaries, + boundaries, n_boundaries, n_boundaries_per_direction, + Tuple(boundary_conditions), SVector{POLYDEG+1}(nodes), SVector{POLYDEG+1}(weights), SVector{POLYDEG+1}(inverse_weights), inverse_vandermonde_legendre, SMatrix{POLYDEG+1,2}(lhat), volume_integral_type, @@ -234,7 +242,7 @@ function count_required_interfaces(mesh::TreeMesh{1}, cell_ids) for cell_id in cell_ids for direction in 1:n_directions(mesh.tree) # Only count interfaces in positive direction to avoid double counting - if direction % 2 == 1 + if direction == 1 continue end @@ -314,8 +322,6 @@ function init_elements(cell_ids, mesh::TreeMesh{1}, ::Val{NVARS}, ::Val{POLYDEG} for i in 1:n_nodes elements.node_coordinates[1, i, element_id] = ( mesh.tree.coordinates[1, cell_id] + dx/2 * nodes[i]) - #elements.node_coordinates[2, i, j, element_id] = ( - # mesh.tree.coordinates[2, cell_id] + dx/2 * nodes[j]) end end @@ -349,9 +355,9 @@ function init_boundaries(cell_ids, mesh::TreeMesh{1}, ::Val{NVARS}, ::Val{POLYDE boundaries = BoundaryContainer1D{NVARS, POLYDEG}(n_boundaries) # Connect elements with boundaries - init_boundary_connectivity!(elements, boundaries, mesh) + n_boundaries_per_direction = init_boundary_connectivity!(elements, boundaries, mesh) - return boundaries + return boundaries, n_boundaries_per_direction end @@ -375,7 +381,7 @@ function init_interface_connectivity!(elements, interfaces, mesh::TreeMesh{1}) # Loop over directions for direction in 1:n_directions(mesh.tree) # Only create interfaces in positive direction - if direction % 2 == 1 + if direction == 1 continue end @@ -395,8 +401,8 @@ function init_interface_connectivity!(elements, interfaces, mesh::TreeMesh{1}) interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id] interfaces.neighbor_ids[1, count] = element_id - # Set orientation (x -> 1, y -> 2) - interfaces.orientations[count] = div(direction, 2) + # Set orientation (x -> 1) + interfaces.orientations[count] = 1 end end @@ -410,42 +416,45 @@ function init_boundary_connectivity!(elements, boundaries, mesh::TreeMesh{1}) # Reset boundaries count count = 0 - # Iterate over all elements to find missing neighbors and to connect to boundaries - for element_id in 1:nelements(elements) - # Get cell id - cell_id = elements.cell_ids[element_id] + # Initialize boundary counts + counts_per_direction = MVector(0, 0) + + # OBS! Iterate over directions first, then over elements, and count boundaries in each direction + # Rationale: This way the boundaries are internally sorted by the directions -x, +x, -y etc., + # obviating the need to store the boundary condition to be applied explicitly. + # Loop over directions + for direction in 1:n_directions(mesh.tree) + # Iterate over all elements to find missing neighbors and to connect to boundaries + for element_id in 1:nelements(elements) + # Get cell id + cell_id = elements.cell_ids[element_id] - # Loop over directions - for direction in 1:n_directions(mesh.tree) # If neighbor exists, current cell is not at a boundary if has_neighbor(mesh.tree, cell_id, direction) continue end # If coarse neighbor exists, current cell is not at a boundary - if has_coarse_neighbor(mesh.tree, cell_id, direction) #TODO + if has_coarse_neighbor(mesh.tree, cell_id, direction) continue end # Create boundary count += 1 + counts_per_direction[direction] += 1 # Set neighbor element id boundaries.neighbor_ids[count] = element_id # Set neighbor side, which denotes the direction (1 -> negative, 2 -> positive) of the element - if direction in (2) + if direction == 2 boundaries.neighbor_sides[count] = 1 else boundaries.neighbor_sides[count] = 2 end - # Set orientation (x -> 1, y -> 2) - if direction in (1, 2) - boundaries.orientations[count] = 1 - else - boundaries.orientations[count] = 2 - end + # Set orientation (x -> 1) + boundaries.orientations[count] = 1 # Store node coordinates enc = elements.node_coordinates @@ -453,10 +462,6 @@ function init_boundary_connectivity!(elements, boundaries, mesh::TreeMesh{1}) boundaries.node_coordinates[:, count] .= enc[:, 1, element_id] elseif direction == 2 # +x direction boundaries.node_coordinates[:, count] .= enc[:, end, element_id] - #elseif direction == 3 # -y direction - # boundaries.node_coordinates[:, :, count] .= enc[:, :, 1, element_id] - #elseif direction == 4 # +y direction - # boundaries.node_coordinates[:, :, count] .= enc[:, :, end, element_id] else error("should not happen") end @@ -465,6 +470,42 @@ function init_boundary_connectivity!(elements, boundaries, mesh::TreeMesh{1}) @assert count == nboundaries(boundaries) ("Actual boundaries count ($count) does not match " * "expectations $(nboundaries(boundaries))") + + @assert sum(counts_per_direction) == count + + return SVector(counts_per_direction) +end + +function init_boundary_conditions(n_boundaries_per_direction, mesh::TreeMesh{1}) + # "eval is evil" + # This is a temporary hack until we have switched to a library based approach + # with pure Julia code instead of parameter files. + bcs = parameter("boundary_conditions", ["nothing", "nothing"]) + if bcs isa AbstractArray + boundary_conditions = eval_if_not_function.(bcs) + else + # This adds support for using a scalar boundary condition (like 'periodicity = "false"') + boundary_conditions = eval_if_not_function.([bcs for _ in 1:n_directions(mesh.tree)]) + end + + # Sanity check about specifying boundary conditions + for direction in 1:n_directions(mesh.tree) + bc = boundary_conditions[direction] + count = n_boundaries_per_direction[direction] + if direction == 1 + dir = "-x" + elseif direction == 2 + dir = "+x" + end + + # All directions with boundaries should have a boundary condition + if count > 0 && isnothing(bc) + error("Found $(count) boundaries in the $(dir)-direction, but corresponding boundary " * + "condition is '$(get_name(bc))'") + end + end + + return boundary_conditions end @@ -497,11 +538,9 @@ function integrate(func, dg::Dg1D, args...; normalize=true) # Use quadrature to numerically integrate over entire domain for element_id in 1:dg.n_elements jacobian_volume = inv(dg.elements.inverse_jacobian[element_id])^ndims(dg) - #for j in 1:nnodes(dg) for i in 1:nnodes(dg) integral += jacobian_volume * dg.weights[i] * func(i, element_id, dg, args...) end - #end end # Normalize with total volume @@ -550,12 +589,9 @@ function calc_error_norms(func, dg::Dg1D, t) # pre-allocate buffers u = zeros(eltype(dg.elements.u), nvariables(dg), size(dg.analysis_vandermonde, 1)) - #u_tmp1 = similar(u, - # nvariables(dg), size(dg.analysis_vandermonde, 1), size(dg.analysis_vandermonde, 2)) + x = zeros(eltype(dg.elements.node_coordinates), 1, size(dg.analysis_vandermonde, 1)) - #x_tmp1 = similar(x, - # 2, size(dg.analysis_vandermonde, 1), size(dg.analysis_vandermonde, 2)) # Set up data structures l2_error = zero(func(get_node_vars(dg.elements.u, dg, 1, 1), equation)) @@ -620,7 +656,7 @@ function calc_mhd_solenoid_condition(dg::Dg1D, t::Float64) for i in 1:nnodes(dg) divb = 0.0 for k in 1:nnodes(dg) - divb += d[i,k]*dg.elements.u[6,k,element_id] #+ d[j,k]*dg.elements.u[7,i,k,element_id] + divb += d[i,k]*dg.elements.u[6,k,element_id] end divb *= dg.elements.inverse_jacobian[element_id] linf_divb = max(linf_divb,abs(divb)) @@ -1006,12 +1042,10 @@ function set_initial_conditions!(dg::Dg1D, time) # make sure that the random number generator is reseted and the ICs are reproducible in the julia REPL/interactive mode seed!(0) for element_id in 1:dg.n_elements - #for j in 1:nnodes(dg) for i in 1:nnodes(dg) dg.elements.u[:, i, element_id] .= dg.initial_conditions( dg.elements.node_coordinates[:, i, element_id], time, equation) end - #end end end @@ -1036,12 +1070,6 @@ function rhs!(dg::Dg1D, t_stage) # Calculate boundary fluxes @timeit timer() "boundary flux" calc_boundary_flux!(dg, t_stage) - # Prolong solution to mortars - #@timeit timer() "prolong2mortars" prolong2mortars!(dg) - - # Calculate mortar fluxes - #@timeit timer() "mortar flux" calc_mortar_flux!(dg) - # Calculate surface integrals @timeit timer() "surface integral" calc_surface_integral!(dg) @@ -1061,18 +1089,15 @@ end calc_volume_integral!(dg::Dg1D) = calc_volume_integral!(dg.elements.u_t, dg.volume_integral_type, dg) -# Calculate 2D twopoint flux (element version) +# Calculate 1D twopoint flux (element version) @inline function calcflux_twopoint!(f1, u, element_id, dg::Dg1D) @unpack volume_flux_function = dg - #for j in 1:nnodes(dg) for i in 1:nnodes(dg) # Set diagonal entries (= regular volume fluxes due to consistency) u_node = get_node_vars(u, dg, i, element_id) flux1 = calcflux(u_node, 1, equations(dg)) - #flux2 = calcflux(u_node, 2, equations(dg)) - set_node_vars!(f1, flux1, dg, i, j) - #set_node_vars!(f2, flux2, dg, j, i, j) + set_node_vars!(f1, flux1, dg, i, i) # Flux in x-direction for l in (i+1):nnodes(dg) @@ -1083,18 +1108,7 @@ calc_volume_integral!(dg::Dg1D) = calc_volume_integral!(dg.elements.u_t, dg.volu f1[v, i, l] = f1[v, l, i] = flux[v] end end - - # Flux in y-direction - #for l in (j+1):nnodes(dg) - # u_ll = get_node_vars(u, dg, i, j, element_id) - # u_rr = get_node_vars(u, dg, i, l, element_id) - # flux = volume_flux_function(u_ll, u_rr, 2, equations(dg)) # 2 -> y-direction - # for v in 1:nvariables(dg) - # f2[v, j, i, l] = f2[v, l, i, j] = flux[v] - # end - #end end - #end calcflux_twopoint_nonconservative!(f1, u, element_id, have_nonconservative_terms(equations(dg)), dg) end @@ -1116,7 +1130,6 @@ function calc_volume_integral!(u_t, ::Val{:weak_form}, dg::Dg1D) Threads.@threads for element_id in 1:dg.n_elements # Calculate volume integral - #for j in 1:nnodes(dg) for i in 1:nnodes(dg) u_node = get_node_vars(dg.elements.u, dg, i, element_id) @@ -1125,14 +1138,7 @@ function calc_volume_integral!(u_t, ::Val{:weak_form}, dg::Dg1D) integral_contribution = dhat[l, i] * flux1 add_to_node_vars!(u_t, integral_contribution, dg, l, element_id) end - - #flux2 = calcflux(u_node, 2, equations(dg)) - #for l in 1:nnodes(dg) - # integral_contribution = dhat[l, j] * flux2 - # add_to_node_vars!(u_t, integral_contribution, dg, i, l, element_id) - #end end - #end end end @@ -1173,20 +1179,6 @@ end add_to_node_vars!(u_t, integral_contribution, dg, ii, element_id) end - # y direction - # use consistency of the volume flux to make this evaluation cheaper - #flux = calcflux(u_node, 2, equations(dg)) - #integral_contribution = alpha * dsplit[j, j] * flux - #add_to_node_vars!(u_t, integral_contribution, dg, i, j, element_id) - # use symmetry of the volume flux for the remaining terms - #for jj in (j+1):nnodes(dg) - # u_node_jj = get_node_vars(dg.elements.u, dg, i, jj, element_id) - # flux = volume_flux_function(u_node, u_node_jj, 2, equations(dg)) - # integral_contribution = alpha * dsplit[j, jj] * flux - # add_to_node_vars!(u_t, integral_contribution, dg, i, j, element_id) - # integral_contribution = alpha * dsplit[jj, j] * flux - # add_to_node_vars!(u_t, integral_contribution, dg, i, jj, element_id) - #end end end @@ -1196,7 +1188,6 @@ end # Choose thread-specific pre-allocated container f1 = f1_threaded[Threads.threadid()] - #f2 = f2_threaded[Threads.threadid()] # Calculate volume fluxes (one more dimension than weak form) calcflux_twopoint!(f1, dg.elements.u, element_id, dg) @@ -1207,7 +1198,7 @@ end # Use local accumulator to improve performance acc = zero(eltype(u_t)) for l in 1:nnodes(dg) - acc += dsplit_transposed[l, i] * f1[v, l, i] #+ dsplit_transposed[l, j] * f2[v, l, i, j] + acc += dsplit_transposed[l, i] * f1[v, l, i] end u_t[v, i, element_id] += alpha * acc end @@ -1271,7 +1262,6 @@ function calc_volume_integral!(u_t, ::Val{:shock_capturing}, alpha, alpha_tmp, # Calculate FV two-point fluxes fstar1 = fstar1_threaded[Threads.threadid()] - #fstar2 = fstar2_threaded[Threads.threadid()] calcflux_fv!(fstar1, dg.elements.u, element_id, dg) # Calculate FV volume integral contribution @@ -1293,7 +1283,6 @@ Calculate the finite volume fluxes inside the elements. # Arguments - `fstar1::AbstractArray{T} where T<:Real`: -- `fstar2::AbstractArray{T} where T<:Real` - `dg::Dg1D` - `u::AbstractArray{T} where T<:Real` - `element_id::Integer` @@ -1304,26 +1293,12 @@ Calculate the finite volume fluxes inside the elements. fstar1[:, 1 ] .= zero(eltype(fstar1)) fstar1[:, nnodes(dg)+1] .= zero(eltype(fstar1)) - #for j in 1:nnodes(dg) for i in 2:nnodes(dg) u_ll = get_node_vars(u, dg, i-1, element_id) u_rr = get_node_vars(u, dg, i, element_id) flux = surface_flux_function(u_ll, u_rr, 1, equations(dg)) # orientation 1: x direction set_node_vars!(fstar1, flux, dg, i) end - #end - - #fstar2[:, :, 1 ] .= zero(eltype(fstar2)) - #fstar2[:, :, nnodes(dg)+1] .= zero(eltype(fstar2)) - - #for j in 2:nnodes(dg) - # for i in 1:nnodes(dg) - # u_ll = get_node_vars(u, dg, i, j-1, element_id) - # u_rr = get_node_vars(u, dg, i, j, element_id) - # flux = surface_flux_function(u_ll, u_rr, 2, equations(dg)) # orientation 2: y direction - # set_node_vars!(fstar2, flux, dg, i, j) - # end - #end end @@ -1334,18 +1309,10 @@ function prolong2interfaces!(dg::Dg1D) Threads.@threads for s in 1:dg.n_interfaces left_element_id = dg.interfaces.neighbor_ids[1, s] right_element_id = dg.interfaces.neighbor_ids[2, s] - if dg.interfaces.orientations[s] == 1 - # interface in x-direction - for v in 1:nvariables(dg) - dg.interfaces.u[1, v, s] = dg.elements.u[v, nnodes(dg), left_element_id] - dg.interfaces.u[2, v, s] = dg.elements.u[v, 1, right_element_id] - end - #else - # # interface in y-direction - # for i in 1:nnodes(dg), v in 1:nvariables(dg) - # dg.interfaces.u[1, v, i, s] = dg.elements.u[v, i, nnodes(dg), left_element_id] - # dg.interfaces.u[2, v, i, s] = dg.elements.u[v, i, 1, right_element_id] - # end + # interface in x-direction + for v in 1:nvariables(dg) + dg.interfaces.u[1, v, s] = dg.elements.u[v, nnodes(dg), left_element_id] + dg.interfaces.u[2, v, s] = dg.elements.u[v, 1, right_element_id] end end end @@ -1357,26 +1324,14 @@ function prolong2boundaries!(dg::Dg1D) for b in 1:dg.n_boundaries element_id = dg.boundaries.neighbor_ids[b] - if dg.boundaries.orientations[b] == 1 # Boundary in x-direction - if dg.boundaries.neighbor_sides[b] == 1 # Element in -x direction of boundary - for v in 1:nvariables(dg) - dg.boundaries.u[1, v, b] = dg.elements.u[v, nnodes(dg), element_id] - end - else # Element in +x direction of boundary - for v in 1:nvariables(dg) - dg.boundaries.u[2, v, b] = dg.elements.u[v, 1, element_id] - end + if dg.boundaries.neighbor_sides[b] == 1 # Element in -x direction of boundary + for v in 1:nvariables(dg) + dg.boundaries.u[1, v, b] = dg.elements.u[v, nnodes(dg), element_id] + end + else # Element in +x direction of boundary + for v in 1:nvariables(dg) + dg.boundaries.u[2, v, b] = dg.elements.u[v, 1, element_id] end - # else # Boundary in y-direction - # if dg.boundaries.neighbor_sides[b] == 1 # Element in -y direction of boundary - # for l in 1:nnodes(dg), v in 1:nvariables(dg) - # dg.boundaries.u[1, v, l, b] = dg.elements.u[v, l, nnodes(dg), element_id] - # end - # else # Element in +y direction of boundary - # for l in 1:nnodes(dg), v in 1:nvariables(dg) - # dg.boundaries.u[2, v, l, b] = dg.elements.u[v, l, 1, element_id] - # end - # end end end end @@ -1404,18 +1359,18 @@ function calc_fstar!(destination, u_interfaces_left, u_interfaces_right, interfa # Call pointwise two-point numerical flux function # i -> left, j -> right - for i in 1:nnodes(dg) + for j in 1:nnodes(dg), i in 1:nnodes(dg) u_ll = get_node_vars(u_interfaces_left, dg, i, interface_id) - u_rr = get_node_vars(u_interfaces_right, dg, i, interface_id) + u_rr = get_node_vars(u_interfaces_right, dg, j, interface_id) flux = surface_flux_function(u_ll, u_rr, orientations[interface_id], equations(dg)) # Copy flux back to actual flux array - set_node_vars!(destination, flux, dg, i) + set_node_vars!(destination, flux, dg, i, j) end end """ - calc_fstar!(destination, u_interfaces, interface_id, orientations, dg::Dg2D) + calc_fstar!(destination, u_interfaces, interface_id, orientations, dg::Dg1D) Calculate the surface flux across interface with different states given by `u_interfaces_left, u_interfaces_right` on both sides (interface version). @@ -1459,24 +1414,20 @@ function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{fa # Determine interface direction with respect to elements: # orientation = 1: left -> 2, right -> 1 - # orientation = 2: left -> 4, right -> 3 left_direction = 2 * orientations[s] right_direction = 2 * orientations[s] - 1 - #for i in 1:nnodes(dg) - # Call pointwise Riemann solver - u_ll, u_rr = get_surface_node_vars(u, dg, s) - flux = surface_flux_function(u_ll, u_rr, orientations[s], equations(dg)) + # Call pointwise Riemann solver + u_ll, u_rr = get_surface_node_vars(u, dg, s) + flux = surface_flux_function(u_ll, u_rr, orientations[s], equations(dg)) - # Copy flux to left and right element storage - for v in 1:nvariables(dg) - surface_flux_values[v, left_direction, left_id] = surface_flux_values[v, right_direction, right_id] = flux[v] - end - #end + # Copy flux to left and right element storage + for v in 1:nvariables(dg) + surface_flux_values[v, left_direction, left_id] = surface_flux_values[v, right_direction, right_id] = flux[v] + end end end -#TODO 1D? # Calculate and store Riemann and nonconservative fluxes across interfaces function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{true}, dg::Dg1D) @@ -1505,15 +1456,14 @@ function calc_interface_flux!(surface_flux_values, neighbor_ids, # Done twice because left/right orientation matters så # 1 -> primary element and 2 -> secondary element # See Bohm et al. 2018 for details on the nonconservative diamond "flux" - #for i in 1:nnodes(dg) - # Call pointwise nonconservative term - u_ll, u_rr = get_surface_node_vars(u_interfaces, dg, s) - noncons_primary = noncons_interface_flux(u_ll, u_rr, orientations[s], equations(dg)) - noncons_secondary = noncons_interface_flux(u_rr, u_ll, orientations[s], equations(dg)) - # Save to primary and secondary temporay storage - set_node_vars!(noncons_diamond_primary, noncons_primary, dg) - set_node_vars!(noncons_diamond_secondary, noncons_secondary, dg) - #end + + # Call pointwise nonconservative term + u_ll, u_rr = get_surface_node_vars(u_interfaces, dg, s) + noncons_primary = noncons_interface_flux(u_ll, u_rr, orientations[s], equations(dg)) + noncons_secondary = noncons_interface_flux(u_rr, u_ll, orientations[s], equations(dg)) + # Save to primary and secondary temporay storage + set_node_vars!(noncons_diamond_primary, noncons_primary, dg) + set_node_vars!(noncons_diamond_secondary, noncons_secondary, dg) # Get neighboring elements left_neighbor_id = neighbor_ids[1, s] @@ -1521,69 +1471,63 @@ function calc_interface_flux!(surface_flux_values, neighbor_ids, # Determine interface direction with respect to elements: # orientation = 1: left -> 2, right -> 1 - # orientation = 2: left -> 4, right -> 3 left_neighbor_direction = 2 * orientations[s] right_neighbor_direction = 2 * orientations[s] - 1 # Copy flux to left and right element storage - #for i in 1:nnodes(dg) - for v in 1:nvariables(dg) - surface_flux_values[v, left_neighbor_direction, left_neighbor_id] = (fstar[v] + - noncons_diamond_primary[v]) - surface_flux_values[v, right_neighbor_direction, right_neighbor_id] = (fstar[v] + - noncons_diamond_secondary[v]) - end - #end + for v in 1:nvariables(dg) + surface_flux_values[v, left_neighbor_direction, left_neighbor_id] = (fstar[v] + + noncons_diamond_primary[v]) + surface_flux_values[v, right_neighbor_direction, right_neighbor_id] = (fstar[v] + + noncons_diamond_secondary[v]) + end end end # Calculate and store boundary flux across domain boundaries #NOTE: Do we need to dispatch on have_nonconservative_terms(dg.equations)? -calc_boundary_flux!(dg::Dg1D, time) = calc_boundary_flux!(dg.elements.surface_flux_values, - dg, time) +calc_boundary_flux!(dg::Dg1D, time) = calc_boundary_flux!(dg.elements.surface_flux_values, dg, time) + + function calc_boundary_flux!(surface_flux_values, dg::Dg1D, time) + @unpack n_boundaries_per_direction, boundary_conditions = dg + + # Calculate indices + lasts = accumulate(+, n_boundaries_per_direction) + firsts = lasts - n_boundaries_per_direction .+ 1 + + # Calc boundary fluxes in each direction + calc_boundary_flux_by_direction!(surface_flux_values, dg, time, + boundary_conditions[1], 1, firsts[1], lasts[1]) + calc_boundary_flux_by_direction!(surface_flux_values, dg, time, + boundary_conditions[2], 2, firsts[2], lasts[2]) +end + +function calc_boundary_flux_by_direction!(surface_flux_values, dg::Dg1D, time, boundary_condition, + direction, first_boundary_id, last_boundary_id) @unpack surface_flux_function = dg @unpack u, neighbor_ids, neighbor_sides, node_coordinates, orientations = dg.boundaries - Threads.@threads for b in 1:dg.n_boundaries - # Fill outer boundary state - # FIXME: This should be replaced by a proper boundary condition - #for i in 1:nnodes(dg) - @views u[3 - neighbor_sides[b], :, b] .= dg.initial_conditions( - node_coordinates[:, b], time, equations(dg)) - #end - + Threads.@threads for b in first_boundary_id:last_boundary_id # Get neighboring element neighbor_id = neighbor_ids[b] - # Determine boundary direction with respect to elements: - # orientation = 1: left -> 2, right -> 1 - # orientation = 2: left -> 4, right -> 3 - if orientations[b] == 1 # Boundary in x-direction - if neighbor_sides[b] == 1 # Element is on the left, boundary on the right - direction = 2 - else # Element is on the right, boundary on the left - direction = 1 - end - #else # Boundary in y-direction - # if neighbor_sides[b] == 1 # Element is below, boundary is above - # direction = 4 - # else # Element is above, boundary is below - # direction = 3 - # end + # Get boundary flux + u_ll, u_rr = get_surface_node_vars(u, dg, b) + if neighbor_sides[b] == 1 # Element is on the left, boundary on the right + u_inner = u_ll + else # Element is on the right, boundary on the left + u_inner = u_rr end + x = get_node_coords(node_coordinates, dg, b) + flux = boundary_condition(u_inner, orientations[b], direction, x, time, surface_flux_function, + equations(dg)) - #for i in 1:nnodes(dg) - # Call pointwise Riemann solver - u_ll, u_rr = get_surface_node_vars(u, dg, b) - flux = surface_flux_function(u_ll, u_rr, orientations[b], equations(dg)) - - # Copy flux to left and right element storage - for v in 1:nvariables(dg) - surface_flux_values[v, direction, neighbor_id] = flux[v] - end - #end + # Copy flux to left and right element storage + for v in 1:nvariables(dg) + surface_flux_values[v, direction, neighbor_id] = flux[v] + end end end @@ -1595,18 +1539,12 @@ function calc_surface_integral!(u_t, surface_flux_values, dg::Dg1D) @unpack lhat = dg Threads.@threads for element_id in 1:dg.n_elements - #for l in 1:nnodes(dg) for v in 1:nvariables(dg) # surface at -x u_t[v, 1, element_id] -= surface_flux_values[v, 1, element_id] * lhat[1, 1] # surface at +x u_t[v, nnodes(dg), element_id] += surface_flux_values[v, 2, element_id] * lhat[nnodes(dg), 2] - # surface at -y - #u_t[v, l, 1, element_id] -= surface_flux_values[v, 3, element_id] * lhat[1, 1] - # surface at +y - #u_t[v, l, nnodes(dg), element_id] += surface_flux_values[v, 4, element_id] * lhat[nnodes(dg), 2] end - #end end end @@ -1615,13 +1553,11 @@ end function apply_jacobian!(dg::Dg1D) Threads.@threads for element_id in 1:dg.n_elements factor = -dg.elements.inverse_jacobian[element_id] - #for j in 1:nnodes(dg) for i in 1:nnodes(dg) for v in 1:nvariables(dg) dg.elements.u_t[v, i, element_id] *= factor end end - #end end end @@ -1664,13 +1600,12 @@ function calc_blending_factors!(alpha, alpha_pre_smooth, u, Threads.@threads for element_id in 1:dg.n_elements indicator = indicator_threaded[Threads.threadid()] modal = modal_threaded[Threads.threadid()] - modal_tmp1 = modal_tmp1_threaded[Threads.threadid()] # Calculate indicator variables at Gauss-Lobatto nodes - cons2indicator!(indicator, u, element_id, nnodes(dg), indicator_variable, equations(dg)) + cons2indicator!(indicator, u, element_id, indicator_variable, dg) # Convert to modal representation - multiply_dimensionwise!(modal, dg.inverse_vandermonde_legendre, indicator, modal_tmp1) + multiply_dimensionwise!(modal, dg.inverse_vandermonde_legendre, indicator) # Calculate total energies for all modes, without highest, without two highest total_energy = 0.0 @@ -1724,6 +1659,15 @@ function calc_blending_factors!(alpha, alpha_pre_smooth, u, end end +# Convert conservative variables to indicator variable for discontinuities (elementwise version) +@inline function cons2indicator!(indicator, u, element_id, indicator_variable, dg::Dg1D) + eqs = equations(dg) + + for i in 1:nnodes(dg) + u_node = get_node_vars(u, dg, i, element_id) + indicator[1, i] = indicator_variable(u_node, eqs) + end +end """ pure_and_blended_element_ids!(element_ids_dg, element_ids_dgfv, alpha, dg) From cb5123fe27d14065c28d52cf2d635e42e5a3d879 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 23 Sep 2020 10:15:18 +0200 Subject: [PATCH 07/55] Remove erroneously added `src/Project.toml` --- src/Project.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/Project.toml diff --git a/src/Project.toml b/src/Project.toml deleted file mode 100644 index e69de29bb2d..00000000000 From ebb3c16b2142979ac925dab89380ba992d41d2a9 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 23 Sep 2020 10:38:13 +0200 Subject: [PATCH 08/55] Reuse simulation summary output from 2D/3D for 1D --- src/run.jl | 90 +++++++++++++---------------------------- src/solvers/dg/1d/dg.jl | 5 +++ 2 files changed, 33 insertions(+), 62 deletions(-) diff --git a/src/run.jl b/src/run.jl index f3921bd5aae..2bdb009d663 100644 --- a/src/run.jl +++ b/src/run.jl @@ -195,68 +195,34 @@ function init_simulation() s *= "| | AMR interval: $amr_interval\n" s *= "| | adapt ICs: $(adapt_initial_conditions ? "yes" : "no")\n" end - if ndims_ > 1 - s *= """| n_steps_max: $n_steps_max - | time integration: $(get_name(time_integration_function)) - | restart interval: $restart_interval - | solution interval: $solution_interval - | #parallel threads: $(Threads.nthreads()) - | - | Solver - | | solver: $solver_name - | | polydeg: $polydeg - | | CFL: $cfl - | | volume integral: $(get_name(solver.volume_integral_type)) - | | volume flux: $(get_name(solver.volume_flux_function)) - | | surface flux: $(get_name(solver.surface_flux_function)) - | | #elements: $(solver.n_elements) - | | #interfaces: $(solver.n_interfaces) - | | #boundaries: $(solver.n_boundaries) - | | #l2mortars: $(solver.n_l2mortars) - | | #DOFs: $(ndofs(solver)) - | - | Mesh - | | #cells: $(length(mesh.tree)) - | | #leaf cells: $n_leaf_cells - | | minimum level: $min_level - | | maximum level: $max_level - | | domain center: $(join(domain_center, ", ")) - | | domain length: $domain_length - | | minimum dx: $min_dx - | | maximum dx: $max_dx - """ - else - s *= """| n_steps_max: $n_steps_max - | time integration: $(get_name(time_integration_function)) - | restart interval: $restart_interval - | solution interval: $solution_interval - | #parallel threads: $(Threads.nthreads()) - | - | Solver - | | solver: $solver_name - | | polydeg: $polydeg - | | CFL: $cfl - | | volume integral: $(get_name(solver.volume_integral_type)) - | | volume flux: $(get_name(solver.volume_flux_function)) - | | surface flux: $(get_name(solver.surface_flux_function)) - | | #elements: $(solver.n_elements) - | | #interfaces: $(solver.n_interfaces) - | | #boundaries: $(solver.n_boundaries) - | | #DOFs: $(ndofs(solver)) - | - | Mesh - | | #cells: $(length(mesh.tree)) - | | #leaf cells: $n_leaf_cells - | | minimum level: $min_level - | | maximum level: $max_level - | | domain center: $(join(domain_center, ", ")) - | | domain length: $domain_length - | | minimum dx: $min_dx - | | maximum dx: $max_dx - """ - - end - + s *= """| n_steps_max: $n_steps_max + | time integration: $(get_name(time_integration_function)) + | restart interval: $restart_interval + | solution interval: $solution_interval + | #parallel threads: $(Threads.nthreads()) + | + | Solver + | | solver: $solver_name + | | polydeg: $polydeg + | | CFL: $cfl + | | volume integral: $(get_name(solver.volume_integral_type)) + | | volume flux: $(get_name(solver.volume_flux_function)) + | | surface flux: $(get_name(solver.surface_flux_function)) + | | #elements: $(solver.n_elements) + | | #interfaces: $(solver.n_interfaces) + | | #boundaries: $(solver.n_boundaries) + | | #DOFs: $(ndofs(solver)) + | + | Mesh + | | #cells: $(length(mesh.tree)) + | | #leaf cells: $n_leaf_cells + | | minimum level: $min_level + | | maximum level: $max_level + | | domain center: $(join(domain_center, ", ")) + | | domain length: $domain_length + | | minimum dx: $min_dx + | | maximum dx: $max_dx + """ println() println(s) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index d30bbfa4ad8..41b85645120 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -22,6 +22,9 @@ mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, boundaries::BoundaryContainer1D{NVARS, POLYDEG} n_boundaries::Int n_boundaries_per_direction::SVector{2, Int} + + n_l2mortars::Int # TODO: Only needed for simulation summary output -> fix me when Taal is alive + boundary_conditions::BoundaryConditions nodes::VectorNnodes @@ -78,6 +81,7 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v boundaries, n_boundaries_per_direction = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) n_boundaries = nboundaries(boundaries) + n_l2mortars = -1 # TODO: Only needed for simulation summary output -> fix me when Taal is alive # Sanity checks if isperiodic(mesh.tree) @@ -186,6 +190,7 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v elements, n_elements, interfaces, n_interfaces, boundaries, n_boundaries, n_boundaries_per_direction, + n_l2mortars, Tuple(boundary_conditions), SVector{POLYDEG+1}(nodes), SVector{POLYDEG+1}(weights), SVector{POLYDEG+1}(inverse_weights), inverse_vandermonde_legendre, SMatrix{POLYDEG+1,2}(lhat), From 5c515016fa2568ebdd88337de27a4be24a97962f Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 23 Sep 2020 10:39:43 +0200 Subject: [PATCH 09/55] Add missing L2 mortar output --- src/run.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/run.jl b/src/run.jl index 2bdb009d663..d787ca16bfa 100644 --- a/src/run.jl +++ b/src/run.jl @@ -211,6 +211,7 @@ function init_simulation() | | #elements: $(solver.n_elements) | | #interfaces: $(solver.n_interfaces) | | #boundaries: $(solver.n_boundaries) + | | #l2mortars: $(solver.n_l2mortars) | | #DOFs: $(ndofs(solver)) | | Mesh From aa79731a87564e21ae1502ce310f22944fe98463 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 23 Sep 2020 10:59:01 +0200 Subject: [PATCH 10/55] Remove non-conservative stuff --- src/solvers/dg/1d/dg.jl | 74 +++-------------------------------------- 1 file changed, 4 insertions(+), 70 deletions(-) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 41b85645120..68af69d3e19 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -104,7 +104,7 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v volume_integral_type = Val(Symbol(parameter("volume_integral_type", "weak_form", valid=["weak_form", "split_form", "shock_capturing"]))) # FIXME: This should be removed as soon as it possible to set solver-specific parameters - if equation isa HyperbolicDiffusionEquations2D && globals[:euler_gravity] + if equation isa AbstractHyperbolicDiffusionEquations && globals[:euler_gravity] volume_integral_type = Val(:weak_form) end dhat = calc_dhat(nodes, weights) @@ -215,27 +215,17 @@ function create_thread_cache_1d(n_variables, n_nodes) A2dp1_x = Array{Float64, 2} MA1d = MArray{Tuple{n_variables, n_nodes}, Float64} - A1d = Array{Float64, 1} - # Pre-allocate data structures to speed up computation (thread-safe) #TODO + # Pre-allocate data structures to speed up computation (thread-safe) f1_threaded = A3d[A3d(undef, n_variables, n_nodes, n_nodes) for _ in 1:Threads.nthreads()] - #f2_threaded = A4d[A4d(undef, n_variables, n_nodes, n_nodes, n_nodes) for _ in 1:Threads.nthreads()] fstar1_threaded = A2dp1_x[A2dp1_x(undef, n_variables, n_nodes+1) for _ in 1:Threads.nthreads()] - #fstar2_threaded = A3dp1_y[A3dp1_y(undef, n_variables, n_nodes, n_nodes+1) for _ in 1:Threads.nthreads()] - fstar_upper_threaded = [MA1d(undef) for _ in 1:Threads.nthreads()] - #fstar_lower_threaded = [MA2d(undef) for _ in 1:Threads.nthreads()] - noncons_diamond_upper_threaded = [MA1d(undef) for _ in 1:Threads.nthreads()] -# noncons_diamond_lower_threaded = [MA2d(undef) for _ in 1:Threads.nthreads()] indicator_threaded = [A2d(undef, 1, n_nodes) for _ in 1:Threads.nthreads()] modal_threaded = [A2d(undef, 1, n_nodes) for _ in 1:Threads.nthreads()] - modal_tmp1_threaded = [A2d(undef, 1, n_nodes) for _ in 1:Threads.nthreads()] return (; f1_threaded, fstar1_threaded, - fstar_upper_threaded, #fstar_lower_threaded, - noncons_diamond_upper_threaded, #noncons_diamond_lower_threaded, - indicator_threaded, modal_threaded, modal_tmp1_threaded) + indicator_threaded, modal_threaded) end @@ -1434,62 +1424,6 @@ function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{fa end -# Calculate and store Riemann and nonconservative fluxes across interfaces -function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{true}, dg::Dg1D) - #TODO temporary workaround while implementing the other stuff - calc_interface_flux!(surface_flux_values, dg.interfaces.neighbor_ids, dg.interfaces.u, - nonconservative_terms, dg.interfaces.orientations, dg, dg.thread_cache) -end - -function calc_interface_flux!(surface_flux_values, neighbor_ids, - u_interfaces, nonconservative_terms::Val{true}, - orientations, dg::Dg1D, thread_cache) - fstar_threaded = thread_cache.fstar_upper_threaded - noncons_diamond_primary_threaded = thread_cache.noncons_diamond_upper_threaded - noncons_diamond_secondary_threaded = thread_cache.noncons_diamond_lower_threaded - - Threads.@threads for s in 1:dg.n_interfaces - # Choose thread-specific pre-allocated container - fstar = fstar_threaded[Threads.threadid()] - noncons_diamond_primary = noncons_diamond_primary_threaded[Threads.threadid()] - noncons_diamond_secondary = noncons_diamond_secondary_threaded[Threads.threadid()] - - # Calculate flux - calc_fstar!(fstar, u_interfaces, s, orientations, dg) - - # Compute the nonconservative numerical "flux" along an interface - # Done twice because left/right orientation matters så - # 1 -> primary element and 2 -> secondary element - # See Bohm et al. 2018 for details on the nonconservative diamond "flux" - - # Call pointwise nonconservative term - u_ll, u_rr = get_surface_node_vars(u_interfaces, dg, s) - noncons_primary = noncons_interface_flux(u_ll, u_rr, orientations[s], equations(dg)) - noncons_secondary = noncons_interface_flux(u_rr, u_ll, orientations[s], equations(dg)) - # Save to primary and secondary temporay storage - set_node_vars!(noncons_diamond_primary, noncons_primary, dg) - set_node_vars!(noncons_diamond_secondary, noncons_secondary, dg) - - # Get neighboring elements - left_neighbor_id = neighbor_ids[1, s] - right_neighbor_id = neighbor_ids[2, s] - - # Determine interface direction with respect to elements: - # orientation = 1: left -> 2, right -> 1 - left_neighbor_direction = 2 * orientations[s] - right_neighbor_direction = 2 * orientations[s] - 1 - - # Copy flux to left and right element storage - for v in 1:nvariables(dg) - surface_flux_values[v, left_neighbor_direction, left_neighbor_id] = (fstar[v] + - noncons_diamond_primary[v]) - surface_flux_values[v, right_neighbor_direction, right_neighbor_id] = (fstar[v] + - noncons_diamond_secondary[v]) - end - end -end - - # Calculate and store boundary flux across domain boundaries #NOTE: Do we need to dispatch on have_nonconservative_terms(dg.equations)? calc_boundary_flux!(dg::Dg1D, time) = calc_boundary_flux!(dg.elements.surface_flux_values, dg, time) @@ -1597,7 +1531,7 @@ function calc_blending_factors!(alpha, alpha_pre_smooth, u, alpha_max, alpha_min, do_smoothing, indicator_variable, thread_cache, dg::Dg1D) # temporary buffers - @unpack indicator_threaded, modal_threaded, modal_tmp1_threaded = thread_cache + @unpack indicator_threaded, modal_threaded = thread_cache # magic parameters threshold = 0.5 * 10^(-1.8 * (nnodes(dg))^0.25) parameter_s = log((1 - 0.0001)/0.0001) From 6a4142a420b9a9cdef99d63bf0382955d3ff66e1 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 23 Sep 2020 11:00:49 +0200 Subject: [PATCH 11/55] mRemove more non-cons stuff for 1D --- src/solvers/dg/1d/dg.jl | 63 +++++++++-------------------------------- 1 file changed, 14 insertions(+), 49 deletions(-) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 68af69d3e19..9c74b5f32ee 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -1088,34 +1088,22 @@ calc_volume_integral!(dg::Dg1D) = calc_volume_integral!(dg.elements.u_t, dg.volu @inline function calcflux_twopoint!(f1, u, element_id, dg::Dg1D) @unpack volume_flux_function = dg - for i in 1:nnodes(dg) - # Set diagonal entries (= regular volume fluxes due to consistency) - u_node = get_node_vars(u, dg, i, element_id) - flux1 = calcflux(u_node, 1, equations(dg)) - set_node_vars!(f1, flux1, dg, i, i) - - # Flux in x-direction - for l in (i+1):nnodes(dg) - u_ll = get_node_vars(u, dg, i, element_id) - u_rr = get_node_vars(u, dg, l, element_id) - flux = volume_flux_function(u_ll, u_rr, 1, equations(dg)) # 1-> x-direction - for v in 1:nvariables(dg) - f1[v, i, l] = f1[v, l, i] = flux[v] - end + for i in 1:nnodes(dg) + # Set diagonal entries (= regular volume fluxes due to consistency) + u_node = get_node_vars(u, dg, i, element_id) + flux1 = calcflux(u_node, 1, equations(dg)) + set_node_vars!(f1, flux1, dg, i, i) + + # Flux in x-direction + for l in (i+1):nnodes(dg) + u_ll = get_node_vars(u, dg, i, element_id) + u_rr = get_node_vars(u, dg, l, element_id) + flux = volume_flux_function(u_ll, u_rr, 1, equations(dg)) # 1-> x-direction + for v in 1:nvariables(dg) + f1[v, i, l] = f1[v, l, i] = flux[v] end end - - calcflux_twopoint_nonconservative!(f1, u, element_id, have_nonconservative_terms(equations(dg)), dg) -end - -function calcflux_twopoint_nonconservative!(f1, u, element_id, nonconservative_terms::Val{false}, dg::Dg1D) - return nothing -end - -function calcflux_twopoint_nonconservative!(f1, u, element_id, nonconservative_terms::Val{true}, dg::Dg1D) - #TODO: Create a unified interface, e.g. using non-symmetric two-point (extended) volume fluxes - # For now, just dispatch to an existing function for the IdealMhdEquations - calcflux_twopoint_nonconservative!(f1, u, element_id, equations(dg), dg) + end end @@ -1177,29 +1165,6 @@ end end end -@inline function split_form_kernel!(u_t, element_id, nonconservative_terms::Val{true}, thread_cache, dg::Dg1D, alpha=true) - @unpack volume_flux_function, dsplit_transposed = dg - @unpack f1_threaded = thread_cache - - # Choose thread-specific pre-allocated container - f1 = f1_threaded[Threads.threadid()] - - # Calculate volume fluxes (one more dimension than weak form) - calcflux_twopoint!(f1, dg.elements.u, element_id, dg) - - # Calculate volume integral in one element - for i in 1:nnodes(dg) - for v in 1:nvariables(dg) - # Use local accumulator to improve performance - acc = zero(eltype(u_t)) - for l in 1:nnodes(dg) - acc += dsplit_transposed[l, i] * f1[v, l, i] - end - u_t[v, i, element_id] += alpha * acc - end - end -end - # Calculate volume integral (DGSEM in split form with shock capturing) function calc_volume_integral!(u_t, ::Val{:shock_capturing}, dg::Dg1D) From 86b5754132e44e9a36f47136b60d347a3a8bd8c3 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Wed, 23 Sep 2020 16:50:40 +0200 Subject: [PATCH 12/55] parameters_amr for 1d --- examples/1d/parameters_amr.toml | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/1d/parameters_amr.toml diff --git a/examples/1d/parameters_amr.toml b/examples/1d/parameters_amr.toml new file mode 100644 index 00000000000..5208dac0d38 --- /dev/null +++ b/examples/1d/parameters_amr.toml @@ -0,0 +1,49 @@ +#################################################################################################### +# Simulation +ndims = 1 +#equations = "CompressibleEulerEquations" +equations = "LinearScalarAdvectionEquation" +advectionvelocity = [1.0] # Need only for linarscalaradvection + +#initial_conditions = "initial_conditions_convergence_test" +#initial_conditions = "initial_conditions_constant" +#initial_conditions = "initial_conditions_linear_x" +initial_conditions = "initial_conditions_gauss" +amr_indicator = "gauss" +# surface_flux = "flux_lax_friedrichs" +# source_terms = +t_start = 0.0 +t_end = 10.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.8 +n_steps_max = 10000 +analysis_interval = 100 + + +#################################################################################################### +# Mesh +n_cells_max = 10000 +coordinates_min = [-5] +coordinates_max = [5] +initial_refinement_level = 4 +amr_interval = 5 +# refinement_patches = [ +# {type = "box", coordinates_min = [-0.5, -0.5], coordinates_max = [0.5, 0.5]}, +# {type = "box", coordinates_min = [-0.1, -0.1], coordinates_max = [0.1, 0.1]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 10 From 97d16eeff8f1ad4aed91b19f86a30f946b37cbb5 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Fri, 25 Sep 2020 09:57:20 +0200 Subject: [PATCH 13/55] update refinement_patches in parameters.toml --- examples/1d/parameters.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/1d/parameters.toml b/examples/1d/parameters.toml index 5bcfd83e6d2..2289aa28443 100644 --- a/examples/1d/parameters.toml +++ b/examples/1d/parameters.toml @@ -7,7 +7,7 @@ advectionvelocity = [1.0] # Need only for linarscalaradvection initial_conditions = "initial_conditions_convergence_test" #initial_conditions = "initial_conditions_constant" -#initial_conditions = "initial_conditions_linear_x_y" +#initial_conditions = "initial_conditions_linear_x" surface_flux = "flux_lax_friedrichs" # source_terms = t_start = 0.0 @@ -33,10 +33,10 @@ n_cells_max = 30000 coordinates_min = [-1] coordinates_max = [1] initial_refinement_level = 4 -# refinement_patches = [ -# {type = "box", coordinates_min = [-0.5, -0.5], coordinates_max = [0.5, 0.5]}, -# {type = "box", coordinates_min = [-0.1, -0.1], coordinates_max = [0.1, 0.1]}, -# ] +#refinement_patches = [ +# {type = "box", coordinates_min = [-0.5], coordinates_max = [0.5]}, +# {type = "box", coordinates_min = [-0.1], coordinates_max = [0.1]}, + #] #################################################################################################### From 7d04a5bae263ba7e883fb3c7a6b1dfe3774f7987 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Fri, 25 Sep 2020 10:03:07 +0200 Subject: [PATCH 14/55] Update count_required_interfaces and init_interface_connectivity! for 1D --- src/solvers/dg/1d/dg.jl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 9c74b5f32ee..50ab51f8697 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -241,14 +241,8 @@ function count_required_interfaces(mesh::TreeMesh{1}, cell_ids) continue end - # If no neighbor exists, current cell is small or at boundary and thus we need a mortar - if !has_neighbor(mesh.tree, cell_id, direction) - continue - end - - # Skip if neighbor has children - neighbor_id = mesh.tree.neighbor_ids[direction, cell_id] - if has_children(mesh.tree, neighbor_id) + # Skip if no neighbor exists and current cell is not small + if !has_neighbor(mesh.tree, cell_id, direction) && !has_coarse_neighbor(mesh.tree, cell_id, direction) continue end @@ -380,20 +374,26 @@ function init_interface_connectivity!(elements, interfaces, mesh::TreeMesh{1}) continue end - # If no neighbor exists, current cell is small and thus we need a mortar - if !has_neighbor(mesh.tree, cell_id, direction) - continue + # Skip if no neighbor exists and current cell is not small + if !has_neighbor(mesh.tree, cell_id, direction) && !has_coarse_neighbor(mesh.tree, cell_id, direction) + continue end - # Skip if neighbor has children - neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id] - if has_children(mesh.tree, neighbor_cell_id) - continue + count += 1 + + if has_neighbor(mesh.tree, cell_id, direction) + neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id] + if has_children(mesh.tree, neighbor_cell_id) # Cell has small neighbor + interfaces.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[1, neighbor_cell_id]] + else # Cell has same refinement level neighbor + interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id] + end + else # Cell is small and has large neighbor + parent_id = mesh.tree.parent_ids[cell_id] + neighbor_id = mesh.tree.neighbor_ids[direction, parent_id] + interfaces.neighbor_ids[2, count] = c2e[neighbor_id] end - # Create interface between elements (1 -> "left" of interface, 2 -> "right" of interface) - count += 1 - interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id] interfaces.neighbor_ids[1, count] = element_id # Set orientation (x -> 1) From 5e6401e7f53dd34d9ea669df27f1559b9cdcbc26 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Tue, 29 Sep 2020 17:38:07 +0200 Subject: [PATCH 15/55] Add boundary_conditions_convergence_test --- src/equations/1d/linear_scalar_advection.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/equations/1d/linear_scalar_advection.jl b/src/equations/1d/linear_scalar_advection.jl index 108fb824bf0..20c92ae1b6a 100644 --- a/src/equations/1d/linear_scalar_advection.jl +++ b/src/equations/1d/linear_scalar_advection.jl @@ -100,6 +100,21 @@ function boundary_conditions_linear_x(u_inner, orientation, direction, x, t, return flux end +function boundary_conditions_convergence_test(u_inner, orientation, direction, x, t, + surface_flux_function, + equation::LinearScalarAdvectionEquation1D) + u_boundary = initial_conditions_convergence_test(x, t, equation) + + # Calculate boundary flux + if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation, equation) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equation) + end + + return flux +end + # Pre-defined source terms should be implemented as # function source_terms_WHATEVER(ut, u, x, element_id, t, n_nodes, equation::LinearScalarAdvectionEquation2D) From a42a02afa3307b58591cade0864297b0136c5b86 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Tue, 29 Sep 2020 17:40:36 +0200 Subject: [PATCH 16/55] Remove non-conservative stuff for 1D --- src/solvers/dg/1d/dg.jl | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 50ab51f8697..813de633dc3 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -395,7 +395,6 @@ function init_interface_connectivity!(elements, interfaces, mesh::TreeMesh{1}) end interfaces.neighbor_ids[1, count] = element_id - # Set orientation (x -> 1) interfaces.orientations[count] = 1 end @@ -1128,17 +1127,17 @@ end # Calculate volume integral (DGSEM in split form) @inline function calc_volume_integral!(u_t, volume_integral_type::Val{:split_form}, dg::Dg1D) - calc_volume_integral!(u_t, volume_integral_type, have_nonconservative_terms(equations(dg)), dg.thread_cache, dg) + calc_volume_integral!(u_t, volume_integral_type, dg.thread_cache, dg) end -function calc_volume_integral!(u_t, ::Val{:split_form}, nonconservative_terms, cache, dg::Dg1D) +function calc_volume_integral!(u_t, ::Val{:split_form}, cache, dg::Dg1D) Threads.@threads for element_id in 1:dg.n_elements - split_form_kernel!(u_t, element_id, nonconservative_terms, cache, dg) + split_form_kernel!(u_t, element_id, cache, dg) end end -@inline function split_form_kernel!(u_t, element_id, nonconservative_terms::Val{false}, cache, dg::Dg1D, alpha=true) +@inline function split_form_kernel!(u_t, element_id, cache, dg::Dg1D, alpha=true) # true * [some floating point value] == [exactly the same floating point value] # This can get optimized away due to constant propagation. @unpack volume_flux_function, dsplit = dg @@ -1212,13 +1211,13 @@ function calc_volume_integral!(u_t, ::Val{:shock_capturing}, alpha, alpha_tmp, # Loop over pure DG elements @timeit timer() "pure DG" Threads.@threads for element_id in element_ids_dg - split_form_kernel!(u_t, element_id, have_nonconservative_terms(equations(dg)), thread_cache, dg) + split_form_kernel!(u_t, element_id, thread_cache, dg) end # Loop over blended DG-FV elements @timeit timer() "blended DG-FV" Threads.@threads for element_id in element_ids_dgfv # Calculate DG volume integral contribution - split_form_kernel!(u_t, element_id, have_nonconservative_terms(equations(dg)), thread_cache, dg, 1 - alpha[element_id]) + split_form_kernel!(u_t, element_id, thread_cache, dg, 1 - alpha[element_id]) # Calculate FV two-point fluxes fstar1 = fstar1_threaded[Threads.threadid()] @@ -1227,8 +1226,7 @@ function calc_volume_integral!(u_t, ::Val{:shock_capturing}, alpha, alpha_tmp, # Calculate FV volume integral contribution for i in 1:nnodes(dg) for v in 1:nvariables(dg) - u_t[v, i, element_id] += ( alpha[element_id] * - (inverse_weights[i] * (fstar1[v, i+1] - fstar1[v, i]) ) ) + u_t[v, i, element_id] += ( alpha[element_id] * (inverse_weights[i] * (fstar1[v, i+1] - fstar1[v, i]) ) ) end end @@ -1360,10 +1358,9 @@ end # Calculate and store the surface fluxes (standard Riemann and nonconservative parts) at an interface # OBS! Regarding the nonconservative terms: 1) currently only needed for the MHD equations # 2) not implemented for boundaries -calc_interface_flux!(dg::Dg1D) = calc_interface_flux!(dg.elements.surface_flux_values, - have_nonconservative_terms(dg.equations), dg) +calc_interface_flux!(dg::Dg1D) = calc_interface_flux!(dg.elements.surface_flux_values, dg) -function calc_interface_flux!(surface_flux_values, nonconservative_terms::Val{false}, dg::Dg1D) +function calc_interface_flux!(surface_flux_values, dg::Dg1D) @unpack surface_flux_function = dg @unpack u, neighbor_ids, orientations = dg.interfaces From b857b58e01845651a52543705bafa51201fa8768 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Tue, 29 Sep 2020 17:42:59 +0200 Subject: [PATCH 17/55] parameters_nonperiodic.toml for 1D --- examples/1d/parameters_nonperiodic.toml | 52 +++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 examples/1d/parameters_nonperiodic.toml diff --git a/examples/1d/parameters_nonperiodic.toml b/examples/1d/parameters_nonperiodic.toml new file mode 100644 index 00000000000..5428d48a69e --- /dev/null +++ b/examples/1d/parameters_nonperiodic.toml @@ -0,0 +1,52 @@ +#################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" + +initial_conditions = "initial_conditions_convergence_test" +boundary_conditions = "boundary_conditions_convergence_test" +# initial_conditions = "initial_conditions_constant" +surface_flux = "flux_lax_friedrichs" +source_terms = "source_terms_convergence_test" +t_start = 0.0 +t_end = 2.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.2 +n_steps_max = 10000 +analysis_interval = 100 + + +#################################################################################################### +# Mesh +n_cells_max = 10000 +coordinates_min = [0] +coordinates_max = [2] +initial_refinement_level = 4 +# Fully periodic (default; can be omitted) +# periodicity = true +# No periodic boundaries: on all boundaries the initial conditions are weakly +# enforced as a Dirichlet BCs +periodicity = false +# Periodic in x-direction, BCs in y-direction +# periodicity = [true, false] +# BCs in x-direction, periodic in y-direction +# periodicity = [false, true] +# refinement_patches = [ +# {type = "box", coordinates_min = [0.0], coordinates_max = [1.0]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 100 From c6b4752cce0aad4929616d005c9d893d8eca1861 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Tue, 29 Sep 2020 17:45:24 +0200 Subject: [PATCH 18/55] Correction refinement_patches --- examples/1d/parameters_amr.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/1d/parameters_amr.toml b/examples/1d/parameters_amr.toml index 5208dac0d38..a77844fc948 100644 --- a/examples/1d/parameters_amr.toml +++ b/examples/1d/parameters_amr.toml @@ -36,8 +36,8 @@ coordinates_max = [5] initial_refinement_level = 4 amr_interval = 5 # refinement_patches = [ -# {type = "box", coordinates_min = [-0.5, -0.5], coordinates_max = [0.5, 0.5]}, -# {type = "box", coordinates_min = [-0.1, -0.1], coordinates_max = [0.1, 0.1]}, +# {type = "box", coordinates_min = [-0.5], coordinates_max = [0.5]}, +# {type = "box", coordinates_min = [-0.1], coordinates_max = [0.1]}, # ] From cc411d114668fe86404c661e49edc8a747000bff Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Tue, 29 Sep 2020 17:47:21 +0200 Subject: [PATCH 19/55] parameters_blast_wave_shockcapturing.toml for 1D --- .../parameters_blast_wave_shockcapturing.toml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/1d/parameters_blast_wave_shockcapturing.toml diff --git a/examples/1d/parameters_blast_wave_shockcapturing.toml b/examples/1d/parameters_blast_wave_shockcapturing.toml new file mode 100644 index 00000000000..3e7a4f9789f --- /dev/null +++ b/examples/1d/parameters_blast_wave_shockcapturing.toml @@ -0,0 +1,40 @@ +################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" + +initial_conditions = "initial_conditions_blast_wave" +surface_flux = "flux_lax_friedrichs" +volume_flux = "flux_chandrashekar" +# source_terms = +t_start = 0.0 +t_end = 12.5 + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.5 +n_steps_max = 10000 +analysis_interval = 100 +volume_integral_type = "shock_capturing" + + +#################################################################################################### +# Mesh +n_cells_max = 100000 +coordinates_min = [-2] +coordinates_max = [2] +initial_refinement_level = 6 +# refinement_patches = [ +# {type = "box", coordinates_min = [0.0], coordinates_max = [1]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 10 +solution_variables = "primitive" +restart_interval = 0 From 6c8320448806d538e77eb82f8901272dec1e493e Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 30 Sep 2020 07:01:19 +0200 Subject: [PATCH 20/55] Add trixi2txt script and gnuplot script --- utils/plot.gp | 35 +++++ utils/trixi2txt.jl | 328 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 utils/plot.gp create mode 100644 utils/trixi2txt.jl diff --git a/utils/plot.gp b/utils/plot.gp new file mode 100644 index 00000000000..f58cfcdf608 --- /dev/null +++ b/utils/plot.gp @@ -0,0 +1,35 @@ +# set term pdfcairo color enhanced font ",8" fontscale 1.0 lw 0.5 size 7cm,5cm +set term pdfcairo color enhanced font ",8" fontscale 1.0 lw 0.5 size 14cm,10cm + +# Set defaults +if (!exists("infile")) infile="solution_000000.txt" +if (!exists("eqn")) eqn="linear_scalar_advection" +ext = ".pdf" + +# Legend +set grid + +# Axes and labels +set ylabel "" +set xlabel "x" + +# Line styles +set style line 2 lw 3 lc rgb "black" +set style line 3 lw 3 lc rgb "black" dt 3 pt 4 ps 0.6 +set style line 4 lw 3 lc rgb "black" dt (12,12) pt 2 ps 0.6 +set style line 5 lw 3 lc rgb "black" dt (6,9) pt 6 ps 0.6 +set style line 6 lw 3 lc rgb "black" dt (18,6,3,6) pt 8 ps 0.6 +set style line 7 lw 3 lc rgb "black" dt (9,8,3,8) pt 10 ps 0.6 + +# Skip first line in data file... +set key autotitle columnhead + +set out infile.ext +if (eqn eq "linear_scalar_advection") { + plot infile u 1:2 w l lw 3 +} +if (eqn eq "euler") { + plot infile u 1:2 w l lw 3, \ + '' u 1:3 w l lw 3, \ + '' u 1:4 w l lw 3 +} diff --git a/utils/trixi2txt.jl b/utils/trixi2txt.jl new file mode 100644 index 00000000000..d7dd8ff3e1d --- /dev/null +++ b/utils/trixi2txt.jl @@ -0,0 +1,328 @@ +module Trixi2Txt + +using EllipsisNotation +using Glob: glob +using Printf: @printf +using HDF5: h5open, attrs, exists +using Tullio: @tullio +using LoopVectorization + +include("../src/solvers/dg/interpolation.jl") + + +function trixi2txt(filename::AbstractString...; + variables=[], output_directory=".", nvisnodes=nothing, max_supported_level=11) + # Convert filenames to a single list of strings + if isempty(filename) + error("no input file was provided") + end + filenames = String[] + for pattern in filename + append!(filenames, glob(pattern)) + end + + # Iterate over input files + for (index, filename) in enumerate(filenames) + # Check if data file exists + if !isfile(filename) + error("file '$filename' does not exist") + end + + # Make sure it is a data file + if !is_solution_restart_file(filename) + error("file '$filename' is not a data file") + end + + # Get mesh file name + meshfile = extract_mesh_filename(filename) + + # Check if mesh file exists + if !isfile(meshfile) + error("mesh file '$meshfile' does not exist") + end + + # Read mesh + center_level_0, length_level_0, leaf_cells, coordinates, levels = read_meshfile(meshfile) + + # Read data + labels, data, n_elements, n_nodes, element_variables, time = read_datafile(filename) + + # Check if dimensions match + if length(leaf_cells) != n_elements + error("number of elements in '$(filename)' do not match number of leaf cells in " * + "'$(meshfile)' " * + "(did you forget to clean your 'out/' directory between different runs?)") + end + + # Determine resolution for data interpolation + max_level = maximum(levels) + if max_level > max_supported_level + error("Maximum refinement level in data file $max_level is higher than " * + "maximum supported level $max_supported_level") + end + max_available_nodes_per_finest_element = 2^(max_supported_level - max_level) + if nvisnodes == nothing + max_nvisnodes = 2 * n_nodes + elseif nvisnodes == 0 + max_nvisnodes = n_nodes + else + max_nvisnodes = nvisnodes + end + nvisnodes_at_max_level = min(max_available_nodes_per_finest_element, max_nvisnodes) + resolution = nvisnodes_at_max_level * 2^max_level + nvisnodes_per_level = [2^(max_level - level)*nvisnodes_at_max_level for level in 0:max_level] + + # Interpolate data + structured_data = unstructured2structured(data, levels, resolution, nvisnodes_per_level) + + # Interpolate cell-centered values to node-centered values + node_centered_data = cell2node(structured_data) + + # Determine x coordinates + xs = collect(range(-1, 1, length=resolution+1)) .* length_level_0/2 .+ center_level_0[1] + + # Check that all variables exist in data file + if isempty(variables) + append!(variables, labels) + else + for var in variables + if !(var in labels) + error("variable '$var' does not exist in the data file $filename") + end + end + end + + # Create output directory if it does not exist + mkpath(output_directory) + + # Determine output file name + base, _ = splitext(splitdir(filename)[2]) + output_filename = joinpath(output_directory, "$(base).txt") + + # Write to file + open(output_filename, "w") do io + # Header + print(io, "x ") + for label in variables + @printf(io, " %-14s", label) + end + println(io) + + # Data + for idx in 1:length(xs) + @printf(io, "%+10.8e", xs[idx]) + for variable_id in 1:length(variables) + @printf(io, " %+10.8e", node_centered_data[idx, variable_id]) + end + println(io) + end + end + end +end + + +# Check if file is a data file +function is_solution_restart_file(filename::String) + # Open file for reading + h5open(filename, "r") do file + # If attribute "mesh_file" exists, this must be a data file + return exists(attrs(file), "mesh_file") + end +end + + +# Use data file to extract mesh filename from attributes +function extract_mesh_filename(filename::String) + # Open file for reading + h5open(filename, "r") do file + # Extract filename relative to data file + mesh_file = read(attrs(file)["mesh_file"]) + + return joinpath(dirname(filename), mesh_file) + end +end + + +# Read in mesh file and return relevant data +function read_meshfile(filename::String) + # Open file for reading + h5open(filename, "r") do file + # Check dimension - only 1D supported + if exists(attrs(file), "ndims") + ndims_ = read(attrs(file)["ndims"]) + else + ndims_ = read(attrs(file)["ndim"]) # FIXME once Trixi's 3D branch is merged & released + end + if ndims_ != 1 + error("currently only 1D files can be processed, but '$filename' is $(ndims_)D") + end + + # Extract basic information + n_cells = read(attrs(file)["n_cells"]) + n_leaf_cells = read(attrs(file)["n_leaf_cells"]) + center_level_0 = read(attrs(file)["center_level_0"]) + length_level_0 = read(attrs(file)["length_level_0"]) + + # Extract coordinates, levels, child cells + coordinates = Array{Float64}(undef, ndims_, n_cells) + coordinates .= read(file["coordinates"]) + levels = Array{Int}(undef, n_cells) + levels .= read(file["levels"]) + child_ids = Array{Int}(undef, 2^ndims_, n_cells) + child_ids .= read(file["child_ids"]) + + # Extract leaf cells (= cells to be plotted) and contract all other arrays accordingly + leaf_cells = similar(levels) + n_cells = 0 + for cell_id in 1:length(levels) + if sum(child_ids[:, cell_id]) > 0 + continue + end + + n_cells += 1 + leaf_cells[n_cells] = cell_id + end + leaf_cells = leaf_cells[1:n_cells] + + coordinates = coordinates[:, leaf_cells] + levels = levels[leaf_cells] + + return center_level_0, length_level_0, leaf_cells, coordinates, levels + end +end + + +# Read in data file and return all relevant information +function read_datafile(filename::String) + # Open file for reading + h5open(filename, "r") do file + # Extract basic information + if exists(attrs(file), "ndims") + ndims_ = read(attrs(file)["ndims"]) + else + ndims_ = read(attrs(file)["ndim"]) + end + if exists(attrs(file), "polydeg") + polydeg = read(attrs(file)["polydeg"]) + else + polydeg = read(attrs(file)["N"]) + end + n_elements = read(attrs(file)["n_elements"]) + n_variables = read(attrs(file)["n_vars"]) + time = read(attrs(file)["time"]) + + # Extract labels for legend + labels = Array{String}(undef, 1, n_variables) + for v = 1:n_variables + labels[1, v] = read(attrs(file["variables_$v"])["name"]) + end + + # Extract data arrays + n_nodes = polydeg + 1 + + if ndims_ == 1 + data = Array{Float64}(undef, n_nodes, n_elements, n_variables) + for v = 1:n_variables + vardata = read(file["variables_$v"]) + @views data[:, :, v][:] .= vardata + end + else + error("Unsupported number of spatial dimensions: ", ndims_) + end + + # Extract element variable arrays + element_variables = Dict{String, Union{Vector{Float64}, Vector{Int}}}() + index = 1 + while exists(file, "element_variables_$index") + varname = read(attrs(file["element_variables_$index"])["name"]) + element_variables[varname] = read(file["element_variables_$index"]) + index +=1 + end + + return labels, data, n_elements, n_nodes, element_variables, time + end +end + + +# Interpolate unstructured DG data to structured data (cell-centered) +function unstructured2structured(unstructured_data::AbstractArray{Float64}, + levels::AbstractArray{Int}, resolution::Int, + nvisnodes_per_level::AbstractArray{Int}) + # Extract data shape information + n_nodes_in, n_elements, n_variables = size(unstructured_data) + + # Get node coordinates for DG locations on reference element + nodes_in, _ = gauss_lobatto_nodes_weights(n_nodes_in) + + # Calculate interpolation vandermonde matrices for each level + max_level = length(nvisnodes_per_level) - 1 + vandermonde_per_level = [] + for l in 0:max_level + n_nodes_out = nvisnodes_per_level[l + 1] + dx = 2 / n_nodes_out + nodes_out = collect(range(-1 + dx/2, 1 - dx/2, length=n_nodes_out)) + push!(vandermonde_per_level, polynomial_interpolation_matrix(nodes_in, nodes_out)) + end + + # Create output data structure + structured = Array{Float64}(undef, resolution, n_variables) + + # For each variable, interpolate element data and store to global data structure + for v in 1:n_variables + first = 1 + + # Reshape data array for use in interpolate_nodes function + @views reshaped_data = reshape(unstructured_data[:, :, v], 1, n_nodes_in, n_elements) + + for element_id in 1:n_elements + # Extract level for convenience + level = levels[element_id] + + # Determine target indices + n_nodes_out = nvisnodes_per_level[level + 1] + last = first + (n_nodes_out - 1) + + # Interpolate data + vandermonde = vandermonde_per_level[level + 1] + @views structured[first:last, v] .= ( + reshape(multiply_dimensionwise(reshaped_data[:, :, element_id], vandermonde), + n_nodes_out)) + + # Update first index for next iteration + first += n_nodes_out + end + end + + return structured +end + + +# Convert cell-centered values to node-centered values by averaging over all +# four neighbors and making use of the periodicity of the solution +function cell2node(cell_centered_data::AbstractArray{Float64}) + # Create temporary data structure to make the averaging algorithm as simple + # as possible (by using a ghost layer) + tmp = similar(cell_centered_data, size(cell_centered_data) .+ (2, 0)) + + # Fill center with original data + tmp[2:end-1, :] .= cell_centered_data + + # Fill sides with opposite data (periodic domain) + # x-direction + tmp[1, :] .= cell_centered_data[end, :] + tmp[end, :] .= cell_centered_data[1, :] + + # Create output data structure + resolution_in, n_variables = size(cell_centered_data) + resolution_out = resolution_in + 1 + node_centered_data = Array{Float64}(undef, resolution_out, n_variables) + + # Obtain node-centered value by averaging over neighboring cell-centered values + for i in 1:resolution_out + node_centered_data[i, :] = (tmp[i, :] + tmp[i+1, :]) / 2 + end + + return node_centered_data +end + +end From 2989dfae8e3e62fbb42b7b6c8ad72dcc53fc3030 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Wed, 30 Sep 2020 07:52:00 +0200 Subject: [PATCH 21/55] Minor code simplifications --- src/solvers/dg/1d/dg.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 813de633dc3..13a35b7f3f9 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -241,8 +241,8 @@ function count_required_interfaces(mesh::TreeMesh{1}, cell_ids) continue end - # Skip if no neighbor exists and current cell is not small - if !has_neighbor(mesh.tree, cell_id, direction) && !has_coarse_neighbor(mesh.tree, cell_id, direction) + # Skip if no neighbor exists + if !has_any_neighbor(mesh.tree, cell_id, direction) continue end @@ -375,23 +375,23 @@ function init_interface_connectivity!(elements, interfaces, mesh::TreeMesh{1}) end # Skip if no neighbor exists and current cell is not small - if !has_neighbor(mesh.tree, cell_id, direction) && !has_coarse_neighbor(mesh.tree, cell_id, direction) - continue + if !has_any_neighbor(mesh.tree, cell_id, direction) + continue end count += 1 if has_neighbor(mesh.tree, cell_id, direction) neighbor_cell_id = mesh.tree.neighbor_ids[direction, cell_id] - if has_children(mesh.tree, neighbor_cell_id) # Cell has small neighbor + if has_children(mesh.tree, neighbor_cell_id) # Cell has small neighbor interfaces.neighbor_ids[2, count] = c2e[mesh.tree.child_ids[1, neighbor_cell_id]] else # Cell has same refinement level neighbor interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id] end else # Cell is small and has large neighbor parent_id = mesh.tree.parent_ids[cell_id] - neighbor_id = mesh.tree.neighbor_ids[direction, parent_id] - interfaces.neighbor_ids[2, count] = c2e[neighbor_id] + neighbor_cell_id = mesh.tree.neighbor_ids[direction, parent_id] + interfaces.neighbor_ids[2, count] = c2e[neighbor_cell_id] end interfaces.neighbor_ids[1, count] = element_id From 21114525e2a836de87578da4097f7cf798ffa318 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Wed, 30 Sep 2020 14:33:39 +0200 Subject: [PATCH 22/55] Fix amr for 1D --- src/solvers/dg/1d/amr.jl | 26 +++++++++++++++----------- src/solvers/dg/1d/dg.jl | 14 +++++++++++++- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/solvers/dg/1d/amr.jl b/src/solvers/dg/1d/amr.jl index 44ee9563160..04044be04b8 100644 --- a/src/solvers/dg/1d/amr.jl +++ b/src/solvers/dg/1d/amr.jl @@ -32,7 +32,8 @@ function refine!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, for old_element_id in 1:old_n_elements if needs_refinement[old_element_id] # Refine element and store solution directly in new data structure - refine_element!(elements.u, element_id, old_u, old_element_id, dg) + refine_element!(elements.u, element_id, old_u, old_element_id, + dg.amr_refine_right, dg.amr_refine_left, dg) element_id += 2^ndims(dg) else # Copy old element data to new element container @@ -68,7 +69,8 @@ end # Refine solution data u for an element, using L2 projection (interpolation) -function refine_element!(u, element_id, old_u, old_element_id, dg::Dg1D) +function refine_element!(u, element_id, old_u, old_element_id, + refine_right, refine_left, dg::Dg1D) # Store new element ids left_id = element_id right_id = element_id + 1 @@ -77,21 +79,21 @@ function refine_element!(u, element_id, old_u, old_element_id, dg::Dg1D) for i in 1:nnodes(dg) acc = zero(get_node_vars(u, dg, i, element_id)) for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, old_element_id) + acc += get_node_vars(old_u, dg, k, old_element_id) * refine_left[i, k] end set_node_vars!(u, acc, dg, i, left_id) end # Interpolate to left element #multiply_dimensionwise!( - # view(u, :, :, lower_left_id), forward_lower, - # view(old_u, :, :, old_element_id)) + # view(u, :, :, left_id), refine_left, + # view(old_u, :, :, old_element_id)) # Interpolate to right element for i in 1:nnodes(dg) acc = zero(get_node_vars(u, dg, i, element_id)) for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, old_element_id) + acc += get_node_vars(old_u, dg, k, old_element_id) * refine_right[i, k] end set_node_vars!(u, acc, dg, i, right_id) end @@ -142,7 +144,8 @@ function coarsen!(dg::Dg1D{Eqn, NVARS, POLYDEG}, mesh::TreeMesh, @assert all(to_be_removed[old_element_id:(old_element_id+2^ndims(dg)-1)]) "bad cell/element order" # Coarsen elements and store solution directly in new data structure - coarsen_elements!(elements.u, element_id, old_u, old_element_id, dg) # dg.l2mortar_reverse_upper, dg.l2mortar_reverse_lower, dg + coarsen_elements!(elements.u, element_id, old_u, old_element_id, + dg.amr_coarsen_right, dg.amr_coarsen_left, dg) element_id += 1 skip = 2^ndims(dg) - 1 else @@ -177,7 +180,8 @@ end # Coarsen solution data u for four elements, using L2 projection -function coarsen_elements!(u, element_id, old_u, old_element_id, dg::Dg1D) +function coarsen_elements!(u, element_id, old_u, old_element_id, + coarsen_right, coarsen_left, dg::Dg1D) # Store old element ids left_id = old_element_id right_id = old_element_id + 1 @@ -187,12 +191,12 @@ function coarsen_elements!(u, element_id, old_u, old_element_id, dg::Dg1D) # Project from left element for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, left_id) #* reverse_lower[i, k] #* reverse_lower[j, l] + acc += get_node_vars(old_u, dg, k, left_id) * coarsen_left[i, k] end # Project from right element for k in 1:nnodes(dg) - acc += get_node_vars(old_u, dg, k, right_id) #* reverse_upper[i, k] #* reverse_lower[j, l] + acc += get_node_vars(old_u, dg, k, right_id) * coarsen_right[i, k] end # Update value @@ -264,7 +268,7 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) # Iterate over all elements for element_id in 1:dg.n_elements cell_id = dg.elements.cell_ids[element_id] - r = periodic_distance_1d(mesh.tree.coordinates[:, cell_id], center, domain_length) + r = periodic_distance_1d(mesh.tree.coordinates[cell_id], center, domain_length) if r < radius_high target_level = max_level elseif r < radius_low diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 13a35b7f3f9..1fb03719908 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -3,7 +3,7 @@ mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, SurfaceFlux, VolumeFlux, InitialConditions, SourceTerms, BoundaryConditions, VolumeIntegralType, ShockIndicatorVariable, VectorNnodes, MatrixNnodes, MatrixNnodes2, - InverseVandermondeLegendre, + InverseVandermondeLegendre, MortarMatrix, VectorAnalysisNnodes, AnalysisVandermonde} <: AbstractDg{1, POLYDEG} equations::Eqn @@ -38,6 +38,11 @@ mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, dsplit::MatrixNnodes dsplit_transposed::MatrixNnodes + amr_refine_right::MortarMatrix + amr_refine_left::MortarMatrix + amr_coarsen_right::MortarMatrix + amr_coarsen_left::MortarMatrix + analysis_nodes::VectorAnalysisNnodes analysis_weights::VectorAnalysisNnodes @@ -111,6 +116,11 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v dsplit = calc_dsplit(nodes, weights) dsplit_transposed = transpose(calc_dsplit(nodes, weights)) + # Initialize L2 mortar projection operators + amr_refine_right = calc_forward_upper(n_nodes) + amr_refine_left = calc_forward_lower(n_nodes) + amr_coarsen_right = calc_reverse_upper(n_nodes, Val(:gauss)) + amr_coarsen_left = calc_reverse_lower(n_nodes, Val(:gauss)) # Initialize data structures for error analysis (by default, we use twice the # number of analysis nodes as the normal solution) @@ -196,6 +206,8 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v inverse_vandermonde_legendre, SMatrix{POLYDEG+1,2}(lhat), volume_integral_type, SMatrix{POLYDEG+1,POLYDEG+1}(dhat), SMatrix{POLYDEG+1,POLYDEG+1}(dsplit), SMatrix{POLYDEG+1,POLYDEG+1}(dsplit_transposed), + SMatrix{POLYDEG+1,POLYDEG+1}(amr_refine_right), SMatrix{POLYDEG+1,POLYDEG+1}(amr_refine_left), + SMatrix{POLYDEG+1,POLYDEG+1}(amr_coarsen_right), SMatrix{POLYDEG+1,POLYDEG+1}(amr_coarsen_left), SVector{analysis_polydeg+1}(analysis_nodes), SVector{analysis_polydeg+1}(analysis_weights), SVector{analysis_polydeg+1}(analysis_weights_volume), analysis_vandermonde, analysis_total_volume, analysis_quantities, save_analysis, analysis_filename, From 03dc09e16d502acd541f546b5cdf590c50cea017 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 05:32:49 +0200 Subject: [PATCH 23/55] Ignore solution/restart txt files from 1D --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 32f4e83daee..167972364eb 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ coverage/ coverage_report/ **/*.jl.*.cov **/.ipynb_checkpoints/ +**/solution_*.txt +**/restart_*.txt From 67642041c24bbd9bc3c772c26f436c0188aa88cb Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 05:49:20 +0200 Subject: [PATCH 24/55] Add new 1D parameter files --- examples/1d/parameters_ec.toml | 58 +++++++++++++++++++ ...meters_weak_blast_wave_shockcapturing.toml | 52 +++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 examples/1d/parameters_ec.toml create mode 100644 examples/1d/parameters_weak_blast_wave_shockcapturing.toml diff --git a/examples/1d/parameters_ec.toml b/examples/1d/parameters_ec.toml new file mode 100644 index 00000000000..d218c0e2e62 --- /dev/null +++ b/examples/1d/parameters_ec.toml @@ -0,0 +1,58 @@ +#################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" +# equations = "LinearScalarAdvectionEquation" +# advectionvelocity = [1.0, 1.0] # Need only for linarscalaradvection + +# initial_conditions = "initial_conditions_convergence_test" +# initial_conditions = "initial_conditions_isentropic_vortex" +# initial_conditions = "initial_conditions_linear_x_y" +# initial_conditions = "initial_conditions_linear_x" +# initial_conditions = "initial_conditions_constant" +initial_conditions = "initial_conditions_weak_blast_wave" +#surface_flux = "flux_lax_friedrichs" +surface_flux = "flux_chandrashekar" +volume_flux = "flux_chandrashekar" +#surface_flux = "flux_shima_etal" +#volume_flux = "flux_shima_etal" +# volume_flux = "flux_central" +# source_terms = +t_start = 0.0 +t_end = 0.4 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.5 +n_steps_max = 10000 +analysis_interval = 1000 +alive_interval = 0 +#volume_integral_type = "shock_capturing" +volume_integral_type = "split_form" +# volume_integral_type = "entropy_fix" + + +#################################################################################################### +# Mesh +n_cells_max = 100000 +coordinates_min = [-2] +coordinates_max = [2] +initial_refinement_level = 5 +# refinement_patches = [ +# {type = "box", coordinates_min = [0.0, -10.0], coordinates_max = [10.0, 10.0]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 100 +output_format = "hdf5" diff --git a/examples/1d/parameters_weak_blast_wave_shockcapturing.toml b/examples/1d/parameters_weak_blast_wave_shockcapturing.toml new file mode 100644 index 00000000000..4a4a4241145 --- /dev/null +++ b/examples/1d/parameters_weak_blast_wave_shockcapturing.toml @@ -0,0 +1,52 @@ +#################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" +# equations = "LinearScalarAdvectionEquation" +# advectionvelocity = [1.0, 1.0] # Need only for linarscalaradvection + +# initial_conditions = "initial_conditions_convergence_test" +# initial_conditions = "initial_conditions_isentropic_vortex" +# initial_conditions = "initial_conditions_linear_x_y" +# initial_conditions = "initial_conditions_linear_x" +# initial_conditions = "initial_conditions_constant" +initial_conditions = "initial_conditions_weak_blast_wave" +surface_flux = "flux_lax_friedrichs" +volume_flux = "flux_kennedy_gruber" +# volume_flux = "flux_central" +# source_terms = +t_start = 0.0 +t_end = 1.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.5 +n_steps_max = 10000 +analysis_interval = 1000 +volume_integral_type = "shock_capturing" +# volume_integral_type = "split_form" + + +#################################################################################################### +# Mesh +n_cells_max = 100000 +coordinates_min = [-2] +coordinates_max = [2] +initial_refinement_level = 5 +# refinement_patches = [ +# {type = "box", coordinates_min = [0.0, -10.0], coordinates_max = [10.0, 10.0]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 10 +solution_variables = "primitive" +restart_interval = 0 From 5b984fb081472b961042fe75278ffb0ceee610fd Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 05:50:32 +0200 Subject: [PATCH 25/55] Reduce analysis interval to stay sane during convtests --- examples/1d/parameters.toml | 3 ++- examples/1d/parameters_density_wave.toml | 3 ++- examples/1d/parameters_source_terms.toml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/1d/parameters.toml b/examples/1d/parameters.toml index 2289aa28443..ce300450370 100644 --- a/examples/1d/parameters.toml +++ b/examples/1d/parameters.toml @@ -23,7 +23,8 @@ solver = "dg" polydeg = 3 cfl = 0.2 n_steps_max = 10000 -analysis_interval = 100 +analysis_interval = 2000 +alive_interval = 0 extra_analysis_quantities = ["entropy", "energy_total"] diff --git a/examples/1d/parameters_density_wave.toml b/examples/1d/parameters_density_wave.toml index bc115fdc04d..87d2ec74235 100644 --- a/examples/1d/parameters_density_wave.toml +++ b/examples/1d/parameters_density_wave.toml @@ -18,7 +18,8 @@ solver = "dg" polydeg = 5 cfl = 0.8 n_steps_max = 10000 -analysis_interval = 100 +analysis_interval = 2000 +alive_interval=0 #################################################################################################### diff --git a/examples/1d/parameters_source_terms.toml b/examples/1d/parameters_source_terms.toml index 63ac13013c1..e712c361069 100644 --- a/examples/1d/parameters_source_terms.toml +++ b/examples/1d/parameters_source_terms.toml @@ -20,7 +20,8 @@ solver = "dg" polydeg = 4 cfl = 0.6 n_steps_max = 10000 -analysis_interval = 100 +analysis_interval = 2000 +alive_interval=0 #################################################################################################### From f86be8b31e654f7db2ea0d75125ec8483291bfba Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 06:13:50 +0200 Subject: [PATCH 26/55] Fix ICs for blast waves in 1D --- src/equations/1d/compressible_euler.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 9054e72e282..ccf96e2185d 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -75,7 +75,7 @@ function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEqu inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) - phi = atan(x_norm) + phi = atan(0.0, x_norm) cos_phi = cos(phi) # Calculate primitive variables @@ -92,7 +92,7 @@ function initial_conditions_blast_wave(x, t, equation::CompressibleEulerEquation inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) - phi = atan(x_norm) + phi = atan(0.0, x_norm) cos_phi = cos(phi) # Calculate primitive variables From 07bfc2c00f13d404975ceb9a8566da71df06a275 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 06:27:27 +0200 Subject: [PATCH 27/55] Add first set of tests for 1D --- src/equations/1d/linear_scalar_advection.jl | 15 +++++ test/runtests.jl | 4 ++ test/test_examples_1d.jl | 71 +++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 test/test_examples_1d.jl diff --git a/src/equations/1d/linear_scalar_advection.jl b/src/equations/1d/linear_scalar_advection.jl index 20c92ae1b6a..7b2284b2b87 100644 --- a/src/equations/1d/linear_scalar_advection.jl +++ b/src/equations/1d/linear_scalar_advection.jl @@ -70,6 +70,21 @@ function initial_conditions_linear_x(x, t, equation::LinearScalarAdvectionEquati end # Apply boundary conditions +function boundary_conditions_linear_x(u_inner, orientation, direction, x, t, surface_flux_function, + equation::LinearScalarAdvectionEquation1D) + u_boundary = initial_conditions_linear_x(x, t, equation) + + # Calculate boundary flux + if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary + flux = surface_flux_function(u_inner, u_boundary, orientation, equation) + else # u_boundary is "left" of boundary, u_inner is "right" of boundary + flux = surface_flux_function(u_boundary, u_inner, orientation, equation) + end + + return flux +end + + function boundary_conditions_gauss(u_inner, orientation, direction, x, t, surface_flux_function, equation::LinearScalarAdvectionEquation1D) u_boundary = initial_conditions_gauss(x, t, equation) diff --git a/test/runtests.jl b/test/runtests.jl index 42fdfd3db58..9d9d4c3926e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,6 +5,10 @@ const TRIXI_TEST = get(ENV, "TRIXI_TEST", "all") const ON_APPVEYOR = lowercase(get(ENV, "APPVEYOR", "false")) == "true" @time @testset "Trixi.jl tests" begin + @time if TRIXI_TEST == "all" || TRIXI_TEST == "1D" + include("test_examples_1d.jl") + end + @time if TRIXI_TEST == "all" || TRIXI_TEST == "2D" include("test_examples_2d.jl") end diff --git a/test/test_examples_1d.jl b/test/test_examples_1d.jl new file mode 100644 index 00000000000..277a0e16de1 --- /dev/null +++ b/test/test_examples_1d.jl @@ -0,0 +1,71 @@ +module TestExamples1D + +using Test +using Trixi + +include("test_trixi.jl") + +# Start with a clean environment: remove Trixi output directory if it exists +outdir = "out" +isdir(outdir) && rm(outdir, recursive=true) + +# pathof(Trixi) returns /path/to/Trixi/src/Trixi.jl, dirname gives the parent directory +const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", "1d") + +# Run basic tests +@testset "Examples 1D" begin + @testset "parameters.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters.toml"), + l2 = [5.581321238071356e-6], + linf = [3.270561745361e-5]) + end + @testset "parameters_amr.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_amr.toml"), + l2 = [0.3540209654959832], + linf = [0.9999905446337742]) + end + @testset "parameters_blast_wave_shockcapturing.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_blast_wave_shockcapturing.toml"), + l2 = [0.21530530948120738, 0.2805965425286348, 0.5591770920395336], + linf = [1.508388610723991, 1.5622010377944118, 2.035149673163788], + n_steps_max=30) + end + @testset "parameters_ec.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), + l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], + linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232]) + end +end + +# Coverage test for all initial conditions +@testset "Tests for initial conditions" begin + # Linear scalar advection + @testset "parameters.toml with initial_conditions_sin" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters.toml"), + l2 = [9.506162481381351e-5], + linf = [0.00017492510098227054], + n_steps_max = 1, + initial_conditions = "initial_conditions_sin") + end + @testset "parameters.toml with initial_conditions_constant" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters.toml"), + l2 = [6.120436421866528e-16], + linf = [1.3322676295501878e-15], + n_steps_max = 1, + initial_conditions = "initial_conditions_constant") + end + @testset "parameters.toml with initial_conditions_linear_x" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters.toml"), + l2 = [7.602419413667044e-17], + linf = [2.220446049250313e-16], + n_steps_max = 1, + initial_conditions = "initial_conditions_linear_x", + boundary_conditions = "boundary_conditions_linear_x", + periodicity=false) + end +end + +# Clean up afterwards: delete Trixi output directory +@test_nowarn rm(outdir, recursive=true) + +end #module From 47684d403c3b14a7c7a0ce39ff64dd98e501e729 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 06:38:26 +0200 Subject: [PATCH 28/55] Add 1D tests to Travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 49a3d6d038b..960b5db1f4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ env: global: - COVERALLS_PARALLEL=true jobs: + - TRIXI_TEST=1D - TRIXI_TEST=2D - TRIXI_TEST=3D - TRIXI_TEST=misc From 4afaa040bc628496d3f7dd706430be9e8a3ae7db Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 1 Oct 2020 08:43:22 +0200 Subject: [PATCH 29/55] Remove unused methods for 1D --- src/equations/1d/linear_scalar_advection.jl | 15 -- src/solvers/dg/1d/amr.jl | 189 ------------------- src/solvers/dg/1d/dg.jl | 192 -------------------- 3 files changed, 396 deletions(-) diff --git a/src/equations/1d/linear_scalar_advection.jl b/src/equations/1d/linear_scalar_advection.jl index 7b2284b2b87..1f841d49408 100644 --- a/src/equations/1d/linear_scalar_advection.jl +++ b/src/equations/1d/linear_scalar_advection.jl @@ -100,21 +100,6 @@ function boundary_conditions_gauss(u_inner, orientation, direction, x, t, surfac end -function boundary_conditions_linear_x(u_inner, orientation, direction, x, t, - surface_flux_function, - equation::LinearScalarAdvectionEquation1D) - u_boundary = initial_conditions_linear_x(x, t, equation) - - # Calculate boundary flux - if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary - flux = surface_flux_function(u_inner, u_boundary, orientation, equation) - else # u_boundary is "left" of boundary, u_inner is "right" of boundary - flux = surface_flux_function(u_boundary, u_inner, orientation, equation) - end - - return flux -end - function boundary_conditions_convergence_test(u_inner, orientation, direction, x, t, surface_flux_function, equation::LinearScalarAdvectionEquation1D) diff --git a/src/solvers/dg/1d/amr.jl b/src/solvers/dg/1d/amr.jl index 04044be04b8..729530b9d74 100644 --- a/src/solvers/dg/1d/amr.jl +++ b/src/solvers/dg/1d/amr.jl @@ -248,156 +248,6 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) lambda[element_id] = 0.0 end end - elseif dg.amr_indicator === :isentropic_vortex - base_level = 3 - max_level = 5 - radius_high = 2 - radius_low = 3 - - # Domain size needed to handle periodicity - domain_length = mesh.tree.length_level_0 - - # Get analytical vortex center (based on assumption that center=[0.0] - # at t=0.0 and that we stop after one period) - if time < domain_length/2 - center = Float64[time] - else - center = Float64[time-domain_length] - end - - # Iterate over all elements - for element_id in 1:dg.n_elements - cell_id = dg.elements.cell_ids[element_id] - r = periodic_distance_1d(mesh.tree.coordinates[cell_id], center, domain_length) - if r < radius_high - target_level = max_level - elseif r < radius_low - target_level = max_level - 1 - else - target_level = base_level - end - - # Compare target level with actual level to set indicator - cell_id = dg.elements.cell_ids[element_id] - actual_level = mesh.tree.levels[cell_id] - if actual_level < target_level - lambda[element_id] = 1.0 - elseif actual_level > target_level - lambda[element_id] = -1.0 - else - lambda[element_id] = 0.0 - end - end - elseif dg.amr_indicator === :khi - base_level = 4 - max_level = 6 - # to make the simulation smaller and quicker wall clock time, choose super_max_level = 6 - super_max_level = 7 - blending_factor_threshold0 = 0.3 - blending_factor_threshold1 = 0.003 - blending_factor_threshold2 = 0.0003 - - # (Re-)initialize element variable storage for blending factor - if (!haskey(dg.element_variables, :amr_indicator_values) || - length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) - dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) - end - if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || - length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) - dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) - end - - alpha = dg.element_variables[:amr_indicator_values] - alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] - calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, false, - density, dg.thread_cache, dg) - - # Iterate over all elements - for element_id in 1:dg.n_elements - cell_id = dg.elements.cell_ids[element_id] - actual_level = mesh.tree.levels[cell_id] - target_level = actual_level - if alpha[element_id] >= blending_factor_threshold0 - target_level = super_max_level - elseif alpha[element_id] >= blending_factor_threshold1 - target_level = max_level - elseif alpha[element_id] <= blending_factor_threshold2 - target_level = base_level - end - - # Compare target level with actual level to set indicator - if actual_level < target_level - lambda[element_id] = 1.0 - elseif actual_level > target_level - lambda[element_id] = -1.0 - else - lambda[element_id] = 0.0 - end - end - elseif dg.amr_indicator === :blob - base_level = 4 - max_level = 7 - super_max_level = 7 - blending_factor_threshold0 = 0.3 - blending_factor_threshold1 = 0.003 - blending_factor_threshold2 = 0.0003 - - # (Re-)initialize element variable storage for blending factor - if (!haskey(dg.element_variables, :amr_indicator_values) || - length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) - dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) - end - if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || - length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) - dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) - end - - alpha = dg.element_variables[:amr_indicator_values] - alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] - calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, false, - density, dg.thread_cache, dg) - - # (Re-)initialize element variable storage for blending factor - if (!haskey(dg.element_variables, :blending_factor) || - length(dg.element_variables[:blending_factor]) != dg.n_elements) - dg.element_variables[:blending_factor] = Vector{Float64}(undef, dg.n_elements) - end - if (!haskey(dg.element_variables, :blending_factor_tmp) || - length(dg.element_variables[:blending_factor_tmp]) != dg.n_elements) - dg.element_variables[:blending_factor_tmp] = Vector{Float64}(undef, dg.n_elements) - end - - alpha1 = dg.element_variables[:blending_factor] - alpha1_tmp = dg.element_variables[:blending_factor_tmp] - calc_blending_factors!(alpha1, alpha1_tmp, dg.elements.u, dg.shock_alpha_max, dg.shock_alpha_min, true, - dg.shock_indicator_variable, dg.thread_cache, dg) - - # Iterate over all elements - for element_id in 1:dg.n_elements - cell_id = dg.elements.cell_ids[element_id] - actual_level = mesh.tree.levels[cell_id] - target_level = actual_level - # adapt for the amr indicator - if alpha[element_id] >= blending_factor_threshold0 - target_level = super_max_level - elseif alpha[element_id] >= blending_factor_threshold1 - target_level = max_level - elseif alpha[element_id] <= blending_factor_threshold2 - target_level = base_level - end - # make sure that a highly troubled shock cell is not coarsened - if isapprox.(dg.shock_alpha_max, alpha1[element_id], atol=1e-12) - target_level = max_level - end - # Compare target level with actual level to set indicator - if actual_level < target_level - lambda[element_id] = 1.0 - elseif actual_level > target_level - lambda[element_id] = -1.0 - else - lambda[element_id] = 0.0 - end - end elseif dg.amr_indicator === :blast_wave base_level = 4 max_level = 6 @@ -413,45 +263,6 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) end - alpha = dg.element_variables[:amr_indicator_values] - alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] - calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, dg.amr_alpha_smooth, - density_pressure, dg.thread_cache, dg) - - # Iterate over all elements - for element_id in 1:dg.n_elements - if alpha[element_id] > blending_factor_threshold - target_level = max_level - else - target_level = base_level - end - - # Compare target level with actual level to set indicator - cell_id = dg.elements.cell_ids[element_id] - actual_level = mesh.tree.levels[cell_id] - if actual_level < target_level - lambda[element_id] = 1.0 - elseif actual_level > target_level - lambda[element_id] = -1.0 - else - lambda[element_id] = 0.0 - end - end - elseif dg.amr_indicator === :sedov_self_gravity - base_level = 2 - max_level = 8 - blending_factor_threshold = 0.0003 - - # (Re-)initialize element variable storage for blending factor - if (!haskey(dg.element_variables, :amr_indicator_values) || - length(dg.element_variables[:amr_indicator_values]) != dg.n_elements) - dg.element_variables[:amr_indicator_values] = Vector{Float64}(undef, dg.n_elements) - end - if (!haskey(dg.element_variables, :amr_indicator_values_tmp) || - length(dg.element_variables[:amr_indicator_values_tmp]) != dg.n_elements) - dg.element_variables[:amr_indicator_values_tmp] = Vector{Float64}(undef, dg.n_elements) - end - alpha = dg.element_variables[:amr_indicator_values] alpha_tmp = dg.element_variables[:amr_indicator_values_tmp] calc_blending_factors!(alpha, alpha_tmp, dg.elements.u, dg.amr_alpha_max, dg.amr_alpha_min, dg.amr_alpha_smooth, diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 1fb03719908..4b472981a85 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -643,39 +643,6 @@ function calc_entropy_timederivative(dg::Dg1D, t) end -# Calculate L2/Linf norms of a solenoidal condition ∇ ⋅ B = 0 -# OBS! This works only when the problem setup is designed such that ∂B₁/∂x + ∂B₂/∂y = 0. Cannot -# compute the full 3D divergence from the given data -function calc_mhd_solenoid_condition(dg::Dg1D, t::Float64) - @assert equations(dg) isa IdealGlmMhdEquations2D "Only relevant for MHD" - - # Local copy of standard derivative matrix - d = polynomial_derivative_matrix(dg.nodes) - # Quadrature weights - weights = dg.weights - # integrate over all elements to get the divergence-free condition errors - linf_divb = 0.0 - l2_divb = 0.0 - for element_id in 1:dg.n_elements - jacobian_volume = (1.0/dg.elements.inverse_jacobian[element_id])^ndims(dg) - #for j in 1:nnodes(dg) - for i in 1:nnodes(dg) - divb = 0.0 - for k in 1:nnodes(dg) - divb += d[i,k]*dg.elements.u[6,k,element_id] - end - divb *= dg.elements.inverse_jacobian[element_id] - linf_divb = max(linf_divb,abs(divb)) - l2_divb += jacobian_volume*weights[i]*divb^2 - end - #end - end - l2_divb = sqrt(l2_divb/dg.analysis_total_volume) - - return l2_divb, linf_divb -end - - """ analyze_solution(dg::Dg1D, mesh::TreeMesh, time, dt, step, runtime_absolute, runtime_relative) @@ -886,67 +853,6 @@ function analyze_solution(dg::Dg1D, mesh::TreeMesh, time::Real, dt::Real, step:: println() end - # Magnetic energy - if :energy_magnetic in dg.analysis_quantities - e_magnetic = integrate(dg, dg.elements.u) do i, element_id, dg, u - cons = get_node_vars(u, dg, i, element_id) - return energy_magnetic(cons, equations(dg)) - end - print(" ∑e_magnetic: ") - @printf(" % 10.8e", e_magnetic) - dg.save_analysis && @printf(f, " % 10.8e", e_magnetic) - println() - end - - # Potential energy - if :energy_potential in dg.analysis_quantities - # FIXME: This should be implemented properly for multiple coupled solvers - @assert !isnothing(solver_gravity) "Only works if gravity solver is supplied" - @assert dg.initial_conditions == initial_conditions_jeans_instability "Only works with Jeans instability setup" - - e_potential = integrate(dg, dg.elements.u, solver_gravity.elements.u) do i, element_id, dg, u_euler, u_gravity - cons_euler = get_node_vars(u_euler, dg, i, element_id) - cons_gravity = get_node_vars(u_gravity, solver_gravity, i, element_id) - # OBS! subtraction is specific to Jeans instability test where rho_0 = 1.5e7 - return (cons_euler[1] - 1.5e7) * cons_gravity[1] - end - print(" ∑e_pot: ") - @printf(" % 10.8e", e_potential) - dg.save_analysis && @printf(f, " % 10.8e", e_potential) - println() - end - - # Solenoidal condition ∇ ⋅ B = 0 - if :l2_divb in dg.analysis_quantities || :linf_divb in dg.analysis_quantities - l2_divb, linf_divb = calc_mhd_solenoid_condition(dg, time) - end - # L2 norm of ∇ ⋅ B - if :l2_divb in dg.analysis_quantities - print(" L2 ∇ ⋅B: ") - @printf(" % 10.8e", l2_divb) - dg.save_analysis && @printf(f, " % 10.8e", l2_divb) - println() - end - # Linf norm of ∇ ⋅ B - if :linf_divb in dg.analysis_quantities - print(" Linf ∇ ⋅B: ") - @printf(" % 10.8e", linf_divb) - dg.save_analysis && @printf(f, " % 10.8e", linf_divb) - println() - end - - # Cross helicity - if :cross_helicity in dg.analysis_quantities - h_c = integrate(dg, dg.elements.u) do i, element_id, dg, u - cons = get_node_vars(u, dg, i, element_id) - return cross_helicity(cons, equations(dg)) - end - print(" ∑H_c: ") - @printf(" % 10.8e", h_c) - dg.save_analysis && @printf(f, " % 10.8e", h_c) - println() - end - println("-"^80) println() @@ -1022,21 +928,6 @@ function save_analysis_header(filename, quantities, equation::AbstractEquation{1 if :energy_internal in quantities @printf(f, " %-14s", "e_internal") end - if :energy_magnetic in quantities - @printf(f, " %-14s", "e_magnetic") - end - if :energy_potential in quantities - @printf(f, " %-14s", "e_potential") - end - if :l2_divb in quantities - @printf(f, " %-14s", "l2_divb") - end - if :linf_divb in quantities - @printf(f, " %-14s", "linf_divb") - end - if :cross_helicity in quantities - @printf(f, " %-14s", "cross_helicity") - end println(f) end end @@ -1095,29 +986,6 @@ end calc_volume_integral!(dg::Dg1D) = calc_volume_integral!(dg.elements.u_t, dg.volume_integral_type, dg) -# Calculate 1D twopoint flux (element version) -@inline function calcflux_twopoint!(f1, u, element_id, dg::Dg1D) - @unpack volume_flux_function = dg - - for i in 1:nnodes(dg) - # Set diagonal entries (= regular volume fluxes due to consistency) - u_node = get_node_vars(u, dg, i, element_id) - flux1 = calcflux(u_node, 1, equations(dg)) - set_node_vars!(f1, flux1, dg, i, i) - - # Flux in x-direction - for l in (i+1):nnodes(dg) - u_ll = get_node_vars(u, dg, i, element_id) - u_rr = get_node_vars(u, dg, l, element_id) - flux = volume_flux_function(u_ll, u_rr, 1, equations(dg)) # 1-> x-direction - for v in 1:nvariables(dg) - f1[v, i, l] = f1[v, l, i] = flux[v] - end - end - end -end - - # Calculate volume integral (DGSEM in weak form) function calc_volume_integral!(u_t, ::Val{:weak_form}, dg::Dg1D) @unpack dhat = dg @@ -1307,66 +1175,6 @@ function prolong2boundaries!(dg::Dg1D) end - -""" - calc_fstar!(destination, u_interfaces_left, u_interfaces_right, interface_id, orientations, dg::Dg1D) - -Calculate the surface flux across interface with different states given by -`u_interfaces_left, u_interfaces_right` on both sides (EC mortar version). - -# Arguments -- `destination::AbstractArray{T,3} where T<:Real`: - The array of surface flux values (updated inplace). -- `u_interfaces_left::AbstractArray{T,3} where T<:Real`` -- `u_interfaces_right::AbstractArray{T,3} where T<:Real`` -- `interface_id::Integer` -- `orientations::Vector{T} where T<:Integer` -- `dg::Dg2D` -""" -#TODO -function calc_fstar!(destination, u_interfaces_left, u_interfaces_right, interface_id, orientations, dg::Dg1D) - @unpack surface_flux_function = dg - - # Call pointwise two-point numerical flux function - # i -> left, j -> right - for j in 1:nnodes(dg), i in 1:nnodes(dg) - u_ll = get_node_vars(u_interfaces_left, dg, i, interface_id) - u_rr = get_node_vars(u_interfaces_right, dg, j, interface_id) - flux = surface_flux_function(u_ll, u_rr, orientations[interface_id], equations(dg)) - - # Copy flux back to actual flux array - set_node_vars!(destination, flux, dg, i, j) - end -end - -""" - calc_fstar!(destination, u_interfaces, interface_id, orientations, dg::Dg1D) - -Calculate the surface flux across interface with different states given by -`u_interfaces_left, u_interfaces_right` on both sides (interface version). - -# Arguments -- `destination::AbstractArray{T,2} where T<:Real`: - The array of surface flux values (updated inplace). -- `u_interfaces::AbstractArray{T,4} where T<:Real`` -- `interface_id::Integer` -- `orientations::Vector{T} where T<:Integer` -- `dg::Dg2D` -""" -function calc_fstar!(destination, u_interfaces, interface_id, orientations, dg::Dg1D) - @unpack surface_flux_function = dg - - for i in 1:nnodes(dg) - # Call pointwise two-point numerical flux function - u_ll, u_rr = get_surface_node_vars(u_interfaces, dg, i, interface_id) - flux = surface_flux_function(u_ll, u_rr, orientations[interface_id], equations(dg)) - - # Copy flux to left and right element storage - set_node_vars!(destination, flux, dg, i) - end -end - - # Calculate and store the surface fluxes (standard Riemann and nonconservative parts) at an interface # OBS! Regarding the nonconservative terms: 1) currently only needed for the MHD equations # 2) not implemented for boundaries From 246c7dba9fa5bc8905a62f094c7f50aaf02f4746 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 10:08:07 +0200 Subject: [PATCH 30/55] parameters_amr_nonperiodic for 1D --- examples/1d/parameters_amr_nonperiodic.toml | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/1d/parameters_amr_nonperiodic.toml diff --git a/examples/1d/parameters_amr_nonperiodic.toml b/examples/1d/parameters_amr_nonperiodic.toml new file mode 100644 index 00000000000..c4c5a690a63 --- /dev/null +++ b/examples/1d/parameters_amr_nonperiodic.toml @@ -0,0 +1,51 @@ +#################################################################################################### +# Simulation +ndims = 1 +# equations = "CompressibleEulerEquations" +equations = "LinearScalarAdvectionEquation" +advectionvelocity = [1.0] # Need only for linarscalaradvection + +# initial_conditions = "initial_conditions_convergence_test" +# initial_conditions = "initial_conditions_constant" +# initial_conditions = "initial_conditions_linear_x" +initial_conditions = "initial_conditions_gauss" +boundary_conditions = "boundary_conditions_gauss" +amr_indicator = "gauss" +# surface_flux = "flux_lax_friedrichs" +# source_terms = +t_start = 0.0 +t_end = 5.0 + +# restart = true +# restart_filename = "out/restart_000100.h5" + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.8 +n_steps_max = 10000 +analysis_interval = 100 + + +#################################################################################################### +# Mesh +n_cells_max = 10000 +coordinates_min = [0] +coordinates_max = [ 5] +initial_refinement_level = 4 +amr_interval = 5 +periodicity = false +# refinement_patches = [ +# {type = "box", coordinates_min = [-0.5], coordinates_max = [0.5]}, +# {type = "box", coordinates_min = [-0.1], coordinates_max = [0.1]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 100 +solution_variables = "primitive" +restart_interval = 10 From adb2cf0a0b1502c0600d15777d33cfef137417ee Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 10:42:28 +0200 Subject: [PATCH 31/55] parameters_sedov_blast_wave_shockcapturing --- ...eters_sedov_blast_wave_shockcapturing.toml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 examples/1d/parameters_sedov_blast_wave_shockcapturing.toml diff --git a/examples/1d/parameters_sedov_blast_wave_shockcapturing.toml b/examples/1d/parameters_sedov_blast_wave_shockcapturing.toml new file mode 100644 index 00000000000..ea9cc7c7221 --- /dev/null +++ b/examples/1d/parameters_sedov_blast_wave_shockcapturing.toml @@ -0,0 +1,47 @@ +#################################################################################################### +# Simulation +ndims = 1 +equations = "CompressibleEulerEquations" + +initial_conditions = "initial_conditions_sedov_blast_wave" +surface_flux = "flux_lax_friedrichs" +volume_flux = "flux_chandrashekar" +t_start = 0.0 +t_end = 12.5 + + +#################################################################################################### +# Solver +solver = "dg" +polydeg = 3 +cfl = 0.5 +n_steps_max = 10000 +analysis_interval = 100 +volume_integral_type = "shock_capturing" +shock_indicator_variable = "density_pressure" +shock_alpha_min = 0.001 +shock_alpha_max = 0.5 +shock_alpha_smooth = true + +#################################################################################################### +# Mesh +n_cells_max = 100000 +coordinates_min = [-2] +coordinates_max = [2] +initial_refinement_level = 6 +amr_indicator = "blast_wave" +amr_interval = 5 +amr_alpha_min = 0.001 +amr_alpha_max = 0.5 +amr_alpha_smooth = true +# refinement_patches = [ +# {type = "box", coordinates_min = [0.0], coordinates_max = [10.0]}, +# ] + + +#################################################################################################### +# I/O +# save_initial_solution = false +solution_interval = 10 +solution_variables = "primitive" +restart_interval = 0 From 407bc9dbf88bc79cecdef46bac800621b35954ee Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 10:43:09 +0200 Subject: [PATCH 32/55] Remove unused stuff for 1D --- src/solvers/dg/1d/amr.jl | 10 ---------- src/solvers/dg/1d/dg.jl | 6 ++---- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/solvers/dg/1d/amr.jl b/src/solvers/dg/1d/amr.jl index 729530b9d74..7f184933d14 100644 --- a/src/solvers/dg/1d/amr.jl +++ b/src/solvers/dg/1d/amr.jl @@ -293,13 +293,3 @@ function calc_amr_indicator(dg::Dg1D, mesh::TreeMesh, time) return lambda end - - -# For periodic domains, distance between two points must take into account -# periodic extensions of the domain -function periodic_distance_1d(coordinates, center, domain_length) - dx = abs.(coordinates - center) - dx_periodic = min.(dx, domain_length .- dx) - return abs(dx_periodic) - #sqrt(sum(dx_periodic.^2)) -end diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 4b472981a85..31d37e0b0ea 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -157,13 +157,11 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v # Initialize AMR amr_indicator = Symbol(parameter("amr_indicator", "n/a", - valid=["n/a", "gauss", "isentropic_vortex", "blast_wave", "khi", "blob", "sedov_self_gravity"])) + valid=["n/a", "gauss", "blast_wave"])) # Initialize storage for element variables element_variables = Dict{Symbol, Union{Vector{Float64}, Vector{Int}}}() - if amr_indicator === :khi || amr_indicator === :blob - element_variables[:amr_indicator_values] = zeros(n_elements) - end + # maximum and minimum alpha for shock capturing shock_alpha_max = parameter("shock_alpha_max", 0.5) shock_alpha_min = parameter("shock_alpha_min", 0.001) From 6c4dbc44a54a3c81ff9660ddf50dad290585faf4 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 10:43:45 +0200 Subject: [PATCH 33/55] Add tests for 1D --- test/test_examples_1d.jl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/test_examples_1d.jl b/test/test_examples_1d.jl index 277a0e16de1..43628faad06 100644 --- a/test/test_examples_1d.jl +++ b/test/test_examples_1d.jl @@ -24,6 +24,16 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " l2 = [0.3540209654959832], linf = [0.9999905446337742]) end + @testset "parameters_amr_nonperiodic.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_amr_nonperiodic.toml"), + l2 = [4.323673749626657e-6], + linf = [3.239622040581043e-5]) + end + @testset "parameters_nonperiodic.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_nonperiodic.toml"), + l2 = [3.8103398423084437e-6, 1.6765350427819571e-6, 7.733123446821975e-6], + linf = [1.2975101617795914e-5, 9.274867029507305e-6, 3.093686036947929e-5]) + end @testset "parameters_blast_wave_shockcapturing.toml" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_blast_wave_shockcapturing.toml"), l2 = [0.21530530948120738, 0.2805965425286348, 0.5591770920395336], @@ -35,6 +45,21 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232]) end + @testset "parameters_sedov_blast_wave_shockcapturing.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_sedov_blast_wave_shockcapturing.toml"), + l2 = [1.2500050612446159, 0.06878411345533555, 0.9447942342833009], + linf = [2.9791692123401017, 0.1683336841958163, 2.665578807135144]) + end + @testset "parameters_source_terms.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_source_terms.toml"), + l2 = [2.243591980786875e-8, 1.8007794300157155e-8, 7.701353735993148e-8], + linf = [1.6169171668245497e-7, 1.4838378192827406e-7, 5.407841152660353e-7]) + end + @testset "parameters_density_wave.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_density_wave.toml"), + l2 = [0.0011482554828446659, 0.00011482554838682677, 5.741277410494742e-6], + linf = [0.004090978306810378, 0.0004090978313616156, 2.045489169688608e-5]) + end end # Coverage test for all initial conditions From 2182fd48c496bf8a9e21c7c06e456bf545f954ff Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 11:49:50 +0200 Subject: [PATCH 34/55] Add tests for 1D --- test/test_examples_1d.jl | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/test_examples_1d.jl b/test/test_examples_1d.jl index 43628faad06..2fa604c7bd8 100644 --- a/test/test_examples_1d.jl +++ b/test/test_examples_1d.jl @@ -45,11 +45,40 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232]) end + @testset "parameters_ec.toml with flux_shima_etal" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), + l2 = [0.06423364669980625, 0.08503530800170918, 0.2407844935006154], + linf = [0.3212150514022287, 0.3070502221852398, 1.1446658347785068], + n_steps_max=10, + surface_flux = "flux_shima_etal", + volume_flux = "flux_shima_etal") + end + @testset "parameters_ec.toml with flux_ranocha" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), + l2 = [0.06424564531300972, 0.08500942143178748, 0.2407606831620822], + linf = [0.3215742010701772, 0.30592054370082256, 1.1453122141121064], + n_steps_max=10, + surface_flux = "flux_ranocha", + volume_flux = "flux_ranocha") + end + @testset "parameters_ec.toml with flux_hll" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), + l2 = [0.0575654253650954, 0.0748264642646861, 0.21532027367350406], + linf = [0.17289848639699257, 0.22023865765090028, 0.6349097763679086], + n_steps_max=10, + surface_flux = "flux_hll", + volume_flux = "flux_hll") + end @testset "parameters_sedov_blast_wave_shockcapturing.toml" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_sedov_blast_wave_shockcapturing.toml"), l2 = [1.2500050612446159, 0.06878411345533555, 0.9447942342833009], linf = [2.9791692123401017, 0.1683336841958163, 2.665578807135144]) end + @testset "parameters_weak_blast_wave_shockcapturing.toml" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_weak_blast_wave_shockcapturing.toml"), + l2 = [0.1166063015913971, 0.15097998823740955, 0.4348178492249418], + linf = [0.1872570975062362, 0.245999816865685, 0.7037939282238272]) + end @testset "parameters_source_terms.toml" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_source_terms.toml"), l2 = [2.243591980786875e-8, 1.8007794300157155e-8, 7.701353735993148e-8], @@ -60,6 +89,18 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " l2 = [0.0011482554828446659, 0.00011482554838682677, 5.741277410494742e-6], linf = [0.004090978306810378, 0.0004090978313616156, 2.045489169688608e-5]) end + @testset "parameters_density_wave.toml with initial_conditions_density_pulse" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_density_wave.toml"), + l2 = [0.003724642049410045, 0.0037246420494099837, 0.0018623210247047657], + linf = [0.018538787219922304, 0.018538787219903874, 0.009269393609915078], + initial_conditions = "initial_conditions_density_pulse") + end + @testset "parameters_density_wave.toml with initial_conditions_constant" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_density_wave.toml"), + l2 = [7.058654266604569e-16, 1.9703187362332313e-14, 7.286819681608443e-15], + linf = [3.774758283725532e-15, 6.733502644351574e-14, 2.4868995751603507e-14], + initial_conditions = "initial_conditions_constant") + end end # Coverage test for all initial conditions @@ -88,6 +129,15 @@ end boundary_conditions = "boundary_conditions_linear_x", periodicity=false) end + @testset "parameters.toml with initial_conditions_convergence_test" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters.toml"), + l2 = [2.9989673704826656e-6], + linf = [5.841215237722963e-6], + n_steps_max = 1, + initial_conditions = "initial_conditions_convergence_test", + boundary_conditions = "boundary_conditions_convergence_test", + periodicity=false) + end end # Clean up afterwards: delete Trixi output directory From c3a1d0665c2a65064cd20a5f05c8e4b35f1a38fc Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 12:49:09 +0200 Subject: [PATCH 35/55] Add 1D support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d21bd2003e..c9245c17e47 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ focused on being easy to use for new or inexperienced users, including the installation and postprocessing procedures. Its features include: * Hierarchical quadtree/octree grid with adaptive mesh refinement -* Native support for 2D and 3D simulations +* Native support for 1D, 2D, and 3D simulations * High-order accuracy in space in time * Nodal discontinuous Galerkin spectral element methods * Kinetic energy-preserving and entropy-stable split forms From fe5e6ae9fa00a272dedb3b6a6b29cd5e1e728f40 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 12:50:21 +0200 Subject: [PATCH 36/55] Remove unused stuff for 1D --- src/equations/1d/compressible_euler.jl | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index ccf96e2185d..539a897cd62 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -141,26 +141,6 @@ function boundary_conditions_convergence_test(u_inner, orientation, direction, x return flux end -function boundary_conditions_sedov_self_gravity(u_inner, orientation, direction, x, t, - surface_flux_function, - equation::CompressibleEulerEquations1D) - # velocities are zero, density/pressure are ambient values according to - # initial_conditions_sedov_self_gravity - rho = 1e-5 - v1 = 0.0 - p = 1e-5 - - u_boundary = prim2cons(SVector(rho, v1, p), equation) - - # Calculate boundary flux - if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary - flux = surface_flux_function(u_inner, u_boundary, orientation, equation) - else # u_boundary is "left" of boundary, u_inner is "right" of boundary - flux = surface_flux_function(u_boundary, u_inner, orientation, equation) - end - - return flux -end # Apply source terms function source_terms_convergence_test(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations1D) @@ -238,12 +218,6 @@ function source_terms_eoc_test_euler(ut, u, x, element_id, t, n_nodes, equation: return nothing end =# -# Empty source terms required for coupled Euler-gravity simulations -function source_terms_harmonic(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations1D) - # OBS! used for the Jeans instability as well as self-gravitating Sedov blast - # TODO: make this cleaner and let each solver have a different source term name - return nothing -end # Calculate 1D flux for a single point @inline function calcflux(u, orientation, equation::CompressibleEulerEquations1D) From 529e582e8cb3250da6f98218d15321e0f6313d07 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 12:51:07 +0200 Subject: [PATCH 37/55] Add test for 1D --- test/test_examples_1d.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_examples_1d.jl b/test/test_examples_1d.jl index 2fa604c7bd8..bae48a9b159 100644 --- a/test/test_examples_1d.jl +++ b/test/test_examples_1d.jl @@ -45,6 +45,15 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232]) end + @testset "parameters_ec.toml with extra_analysis_quantities" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), + l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], + linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232] + extra_analysis_quantities = ["l2_error_primitve", "linf_error_primitive", "conservation_error", + "entropy", "energy_total", "energy_kinetic", "energy_internal", "residual"], + save_analysis = true + ) + end @testset "parameters_ec.toml with flux_shima_etal" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), l2 = [0.06423364669980625, 0.08503530800170918, 0.2407844935006154], From bdc126d29dd18c4aaf46244f058185bf299c2ed7 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 13:30:01 +0200 Subject: [PATCH 38/55] Fix test for 1D --- test/test_examples_1d.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_examples_1d.jl b/test/test_examples_1d.jl index bae48a9b159..5c4b67dc150 100644 --- a/test/test_examples_1d.jl +++ b/test/test_examples_1d.jl @@ -48,11 +48,10 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " @testset "parameters_ec.toml with extra_analysis_quantities" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], - linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232] + linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232], extra_analysis_quantities = ["l2_error_primitve", "linf_error_primitive", "conservation_error", "entropy", "energy_total", "energy_kinetic", "energy_internal", "residual"], - save_analysis = true - ) + save_analysis = true) end @testset "parameters_ec.toml with flux_shima_etal" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), From 8c88767afeb5b6422a4ad839bdc42ceb7e320653 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 14:12:40 +0200 Subject: [PATCH 39/55] Fix test --- test/test_examples_1d.jl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/test_examples_1d.jl b/test/test_examples_1d.jl index 5c4b67dc150..a999e0f2bdb 100644 --- a/test/test_examples_1d.jl +++ b/test/test_examples_1d.jl @@ -49,7 +49,7 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_ec.toml"), l2 = [0.11948926375393912, 0.15554606230413676, 0.4466895989733186], linf = [0.2956500342985863, 0.28341906267346123, 1.0655211913235232], - extra_analysis_quantities = ["l2_error_primitve", "linf_error_primitive", "conservation_error", + extra_analysis_quantities = ["l2_error_primitive", "linf_error_primitive", "conservation_error", "entropy", "energy_total", "energy_kinetic", "energy_internal", "residual"], save_analysis = true) end @@ -82,6 +82,18 @@ const EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", " l2 = [1.2500050612446159, 0.06878411345533555, 0.9447942342833009], linf = [2.9791692123401017, 0.1683336841958163, 2.665578807135144]) end + @testset "parameters_sedov_blast_wave_shockcapturing.toml with pressure" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_sedov_blast_wave_shockcapturing.toml"), + l2 = [1.2974912081242604, 0.07965704393481755, 0.9453618260835944], + linf = [3.1823155476320926, 0.21380426507857242, 2.6650734792251995], + shock_indicator_variable = "pressure") + end + @testset "parameters_sedov_blast_wave_shockcapturing.toml with density" begin + test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_sedov_blast_wave_shockcapturing.toml"), + l2 = [1.2797014548135697, 0.07077838776630381, 0.9457917493772532], + linf = [3.117424382044245, 0.17775688760995997, 2.666854886766347], + shock_indicator_variable = "density") + end @testset "parameters_weak_blast_wave_shockcapturing.toml" begin test_trixi_run(joinpath(EXAMPLES_DIR, "parameters_weak_blast_wave_shockcapturing.toml"), l2 = [0.1166063015913971, 0.15097998823740955, 0.4348178492249418], From ef277b75a0a684f33c5a18a95ad4200d652be249 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 14:13:18 +0200 Subject: [PATCH 40/55] Remove unused stuff --- src/solvers/dg/1d/amr.jl | 5 ----- src/solvers/dg/1d/dg.jl | 12 ++++++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/solvers/dg/1d/amr.jl b/src/solvers/dg/1d/amr.jl index 7f184933d14..e2ad5385c9e 100644 --- a/src/solvers/dg/1d/amr.jl +++ b/src/solvers/dg/1d/amr.jl @@ -84,11 +84,6 @@ function refine_element!(u, element_id, old_u, old_element_id, set_node_vars!(u, acc, dg, i, left_id) end - # Interpolate to left element - #multiply_dimensionwise!( - # view(u, :, :, left_id), refine_left, - # view(old_u, :, :, old_element_id)) - # Interpolate to right element for i in 1:nnodes(dg) acc = zero(get_node_vars(u, dg, i, element_id)) diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index 31d37e0b0ea..cba7a72941d 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -109,18 +109,18 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v volume_integral_type = Val(Symbol(parameter("volume_integral_type", "weak_form", valid=["weak_form", "split_form", "shock_capturing"]))) # FIXME: This should be removed as soon as it possible to set solver-specific parameters - if equation isa AbstractHyperbolicDiffusionEquations && globals[:euler_gravity] - volume_integral_type = Val(:weak_form) - end + #if equation isa AbstractHyperbolicDiffusionEquations && globals[:euler_gravity] + # volume_integral_type = Val(:weak_form) + #end dhat = calc_dhat(nodes, weights) dsplit = calc_dsplit(nodes, weights) dsplit_transposed = transpose(calc_dsplit(nodes, weights)) # Initialize L2 mortar projection operators - amr_refine_right = calc_forward_upper(n_nodes) + amr_refine_right = calc_forward_upper(n_nodes) amr_refine_left = calc_forward_lower(n_nodes) amr_coarsen_right = calc_reverse_upper(n_nodes, Val(:gauss)) - amr_coarsen_left = calc_reverse_lower(n_nodes, Val(:gauss)) + amr_coarsen_left = calc_reverse_lower(n_nodes, Val(:gauss)) # Initialize data structures for error analysis (by default, we use twice the # number of analysis nodes as the normal solution) @@ -161,7 +161,7 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v # Initialize storage for element variables element_variables = Dict{Symbol, Union{Vector{Float64}, Vector{Int}}}() - + # maximum and minimum alpha for shock capturing shock_alpha_max = parameter("shock_alpha_max", 0.5) shock_alpha_min = parameter("shock_alpha_min", 0.001) From 1bcb120f370e7167dc31f5ab9824d8ca36dcd238 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:47:58 +0200 Subject: [PATCH 41/55] Update examples/1d/parameters_amr.toml Co-authored-by: Hendrik Ranocha --- examples/1d/parameters_amr.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/1d/parameters_amr.toml b/examples/1d/parameters_amr.toml index a77844fc948..09ccc347220 100644 --- a/examples/1d/parameters_amr.toml +++ b/examples/1d/parameters_amr.toml @@ -1,7 +1,6 @@ #################################################################################################### # Simulation ndims = 1 -#equations = "CompressibleEulerEquations" equations = "LinearScalarAdvectionEquation" advectionvelocity = [1.0] # Need only for linarscalaradvection From c5217fe3bc0d84f7a95487acb9726cdc17c46811 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:49:10 +0200 Subject: [PATCH 42/55] Update examples/1d/parameters_ec.toml Co-authored-by: Hendrik Ranocha --- examples/1d/parameters_ec.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/1d/parameters_ec.toml b/examples/1d/parameters_ec.toml index d218c0e2e62..e8f71a88c9d 100644 --- a/examples/1d/parameters_ec.toml +++ b/examples/1d/parameters_ec.toml @@ -2,8 +2,6 @@ # Simulation ndims = 1 equations = "CompressibleEulerEquations" -# equations = "LinearScalarAdvectionEquation" -# advectionvelocity = [1.0, 1.0] # Need only for linarscalaradvection # initial_conditions = "initial_conditions_convergence_test" # initial_conditions = "initial_conditions_isentropic_vortex" From eb514b25951a37a6a62858d4425835ec863029aa Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:50:33 +0200 Subject: [PATCH 43/55] Remove comments in examples/1d/parameters_ec.toml Co-authored-by: Hendrik Ranocha --- examples/1d/parameters_ec.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/1d/parameters_ec.toml b/examples/1d/parameters_ec.toml index e8f71a88c9d..753ef494737 100644 --- a/examples/1d/parameters_ec.toml +++ b/examples/1d/parameters_ec.toml @@ -5,8 +5,6 @@ equations = "CompressibleEulerEquations" # initial_conditions = "initial_conditions_convergence_test" # initial_conditions = "initial_conditions_isentropic_vortex" -# initial_conditions = "initial_conditions_linear_x_y" -# initial_conditions = "initial_conditions_linear_x" # initial_conditions = "initial_conditions_constant" initial_conditions = "initial_conditions_weak_blast_wave" #surface_flux = "flux_lax_friedrichs" From cad60ef5bc1b6b343636ba9f96c0f88b0cbbdb29 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:50:55 +0200 Subject: [PATCH 44/55] Remove comments in examples/1d/parameters_amr_nonperiodic.toml Co-authored-by: Hendrik Ranocha --- examples/1d/parameters_amr_nonperiodic.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/1d/parameters_amr_nonperiodic.toml b/examples/1d/parameters_amr_nonperiodic.toml index c4c5a690a63..e13abc211d8 100644 --- a/examples/1d/parameters_amr_nonperiodic.toml +++ b/examples/1d/parameters_amr_nonperiodic.toml @@ -1,7 +1,6 @@ #################################################################################################### # Simulation ndims = 1 -# equations = "CompressibleEulerEquations" equations = "LinearScalarAdvectionEquation" advectionvelocity = [1.0] # Need only for linarscalaradvection From 9daa68b35201fd5ef55a3bea148cac144fc7cac4 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:51:17 +0200 Subject: [PATCH 45/55] Remove comments in examples/1d/parameters.toml Co-authored-by: Hendrik Ranocha --- examples/1d/parameters.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/1d/parameters.toml b/examples/1d/parameters.toml index ce300450370..b006518fe13 100644 --- a/examples/1d/parameters.toml +++ b/examples/1d/parameters.toml @@ -1,7 +1,6 @@ #################################################################################################### # Simulation ndims = 1 -# equations = "CompressibleEulerEquations" equations = "LinearScalarAdvectionEquation" advectionvelocity = [1.0] # Need only for linarscalaradvection From b2fd286f8c521c2724b4511d062b70e37a30aa04 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:52:04 +0200 Subject: [PATCH 46/55] Remove comments in examples/1d/parameters_ec.toml Co-authored-by: Hendrik Ranocha --- examples/1d/parameters_ec.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/1d/parameters_ec.toml b/examples/1d/parameters_ec.toml index 753ef494737..1ccd476afb5 100644 --- a/examples/1d/parameters_ec.toml +++ b/examples/1d/parameters_ec.toml @@ -31,7 +31,6 @@ analysis_interval = 1000 alive_interval = 0 #volume_integral_type = "shock_capturing" volume_integral_type = "split_form" -# volume_integral_type = "entropy_fix" #################################################################################################### From b194c873d0f1c40bd7948c8414acb7dbf93d687a Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:55:10 +0200 Subject: [PATCH 47/55] Apply suggestions from code review Remove comments Co-authored-by: Hendrik Ranocha --- examples/1d/parameters_nonperiodic.toml | 5 ----- examples/1d/parameters_source_terms.toml | 1 - .../1d/parameters_weak_blast_wave_shockcapturing.toml | 10 +--------- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/examples/1d/parameters_nonperiodic.toml b/examples/1d/parameters_nonperiodic.toml index 5428d48a69e..e750b0d4251 100644 --- a/examples/1d/parameters_nonperiodic.toml +++ b/examples/1d/parameters_nonperiodic.toml @@ -5,7 +5,6 @@ equations = "CompressibleEulerEquations" initial_conditions = "initial_conditions_convergence_test" boundary_conditions = "boundary_conditions_convergence_test" -# initial_conditions = "initial_conditions_constant" surface_flux = "flux_lax_friedrichs" source_terms = "source_terms_convergence_test" t_start = 0.0 @@ -35,10 +34,6 @@ initial_refinement_level = 4 # No periodic boundaries: on all boundaries the initial conditions are weakly # enforced as a Dirichlet BCs periodicity = false -# Periodic in x-direction, BCs in y-direction -# periodicity = [true, false] -# BCs in x-direction, periodic in y-direction -# periodicity = [false, true] # refinement_patches = [ # {type = "box", coordinates_min = [0.0], coordinates_max = [1.0]}, # ] diff --git a/examples/1d/parameters_source_terms.toml b/examples/1d/parameters_source_terms.toml index e712c361069..8d59a62741b 100644 --- a/examples/1d/parameters_source_terms.toml +++ b/examples/1d/parameters_source_terms.toml @@ -4,7 +4,6 @@ ndims = 1 equations = "CompressibleEulerEquations" initial_conditions = "initial_conditions_convergence_test" -# initial_conditions = "initial_conditions_constant" surface_flux = "flux_lax_friedrichs" source_terms = "source_terms_convergence_test" t_start = 0.0 diff --git a/examples/1d/parameters_weak_blast_wave_shockcapturing.toml b/examples/1d/parameters_weak_blast_wave_shockcapturing.toml index 4a4a4241145..e073e29328f 100644 --- a/examples/1d/parameters_weak_blast_wave_shockcapturing.toml +++ b/examples/1d/parameters_weak_blast_wave_shockcapturing.toml @@ -2,18 +2,10 @@ # Simulation ndims = 1 equations = "CompressibleEulerEquations" -# equations = "LinearScalarAdvectionEquation" -# advectionvelocity = [1.0, 1.0] # Need only for linarscalaradvection - -# initial_conditions = "initial_conditions_convergence_test" -# initial_conditions = "initial_conditions_isentropic_vortex" -# initial_conditions = "initial_conditions_linear_x_y" -# initial_conditions = "initial_conditions_linear_x" -# initial_conditions = "initial_conditions_constant" + initial_conditions = "initial_conditions_weak_blast_wave" surface_flux = "flux_lax_friedrichs" volume_flux = "flux_kennedy_gruber" -# volume_flux = "flux_central" # source_terms = t_start = 0.0 t_end = 1.0 From 5ae9471925662b878e328374a1b15518722e27be Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:57:57 +0200 Subject: [PATCH 48/55] Update src/equations/1d/compressible_euler.jl Co-authored-by: Hendrik Ranocha --- src/equations/1d/compressible_euler.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 539a897cd62..26d085ceff8 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -75,8 +75,11 @@ function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEqu inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) - phi = atan(0.0, x_norm) - cos_phi = cos(phi) + # The following code is equivalent to + # phi = atan(0.0, x_norm) + # cos_phi = cos(phi) + # in 1D but faster + cos_phi = x_norm > 0 ? one(x_norm) : -one(x_norm) # Calculate primitive variables rho = r > 0.5 ? 1.0 : 1.1691 From f6a09f86aaf81790dddccb16e48331f4156f6b14 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:00:21 +0200 Subject: [PATCH 49/55] Remove comments in src/equations/1d/compressible_euler.jl Co-authored-by: Michael Schlottke-Lakemper --- src/equations/1d/compressible_euler.jl | 47 -------------------------- 1 file changed, 47 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 26d085ceff8..6a1e4ccdbb0 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -174,53 +174,6 @@ function source_terms_convergence_test(ut, u, x, element_id, t, n_nodes, equatio return nothing end -#= -function source_terms_eoc_test_coupled_euler_gravity(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations2D) - # Same settings as in `initial_conditions_eoc_test_coupled_euler_gravity` - c = 2.0 - A = 0.1 - G = 1.0 # gravitational constant, must match coupling solver - C_grav = -2.0 * G / pi # 2 == 4 / ndims - - for j in 1:n_nodes, i in 1:n_nodes - x1 = x[1, i, j, element_id] - x2 = x[2, i, j, element_id] - si, co = sincos(pi * (x1 + x2 - t)) - rhox = A * pi * co - rho = c + A * si - - ut[1, i, j, element_id] += rhox - ut[2, i, j, element_id] += rhox - ut[3, i, j, element_id] += rhox - ut[4, i, j, element_id] += (1.0 - C_grav*rho)*rhox - end - - return nothing -end - -function source_terms_eoc_test_euler(ut, u, x, element_id, t, n_nodes, equation::CompressibleEulerEquations2D) - # Same settings as in `initial_conditions_eoc_test_coupled_euler_gravity` - c = 2.0 - A = 0.1 - G = 1.0 - C_grav = -2 * G / pi # 2 == 4 / ndims - - for j in 1:n_nodes, i in 1:n_nodes - x1 = x[1, i, j, element_id] - x2 = x[2, i, j, element_id] - si, co = sincos(pi * (x1 + x2 - t)) - rhox = A * pi * co - rho = c + A * si - - ut[1, i, j, element_id] += rhox - ut[2, i, j, element_id] += rhox * (1 - C_grav * rho) - ut[3, i, j, element_id] += rhox * (1 - C_grav * rho) - ut[4, i, j, element_id] += rhox * (1 - 3 * C_grav * rho) - end - - return nothing -end -=# # Calculate 1D flux for a single point @inline function calcflux(u, orientation, equation::CompressibleEulerEquations1D) From cbcacdbe551af236ae661d1f12b8045e6014a789 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:04:07 +0200 Subject: [PATCH 50/55] Apply suggestions from code review Co-authored-by: Michael Schlottke-Lakemper Co-authored-by: Hendrik Ranocha --- src/equations/1d/compressible_euler.jl | 24 ------------------------ src/solvers/dg/1d/dg.jl | 10 +++------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 6a1e4ccdbb0..54c39b5a1bc 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -479,30 +479,6 @@ end end -# Calculates the entropy flux in direction "orientation" and the entropy variables for a state cons -# NOTE: This method seems to work currently (b82534e) but is never used anywhere. Thus it is -# commented here until someone uses it or writes a test for it. -# @inline function cons2entropyvars_and_flux(gamma::Float64, cons, orientation::Int) -# entropy = MVector{4, Float64}(undef) -# v = (cons[2] / cons[1] , cons[3] / cons[1]) -# v_square= v[1]*v[1]+v[2]*v[2] -# p = (gamma - 1) * (cons[4] - 1/2 * (cons[2] * v[1] + cons[3] * v[2])) -# rho_p = cons[1] / p -# # thermodynamic entropy -# s = log(p) - gamma*log(cons[1]) -# # mathematical entropy -# S = - s*cons[1]/(gamma-1) -# # entropy variables -# entropy[1] = (gamma - s)/(gamma-1) - 0.5*rho_p*v_square -# entropy[2] = rho_p*v[1] -# entropy[3] = rho_p*v[2] -# entropy[4] = -rho_p -# # entropy flux -# entropy_flux = S*v[orientation] -# return entropy, entropy_flux -# end - - # Calculate thermodynamic entropy for a conservative state `cons` @inline function entropy_thermodynamic(cons, equation::CompressibleEulerEquations1D) # Pressure diff --git a/src/solvers/dg/1d/dg.jl b/src/solvers/dg/1d/dg.jl index cba7a72941d..da3b13a0d31 100644 --- a/src/solvers/dg/1d/dg.jl +++ b/src/solvers/dg/1d/dg.jl @@ -23,7 +23,7 @@ mutable struct Dg1D{Eqn<:AbstractEquation, NVARS, POLYDEG, n_boundaries::Int n_boundaries_per_direction::SVector{2, Int} - n_l2mortars::Int # TODO: Only needed for simulation summary output -> fix me when Taal is alive + n_l2mortars::Int # TODO: Taal. Only needed for simulation summary output -> fix me when Taal is alive boundary_conditions::BoundaryConditions @@ -86,7 +86,7 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v boundaries, n_boundaries_per_direction = init_boundaries(leaf_cell_ids, mesh, Val(NVARS), Val(POLYDEG), elements) n_boundaries = nboundaries(boundaries) - n_l2mortars = -1 # TODO: Only needed for simulation summary output -> fix me when Taal is alive + n_l2mortars = -1 # TODO: Taal. Only needed for simulation summary output -> fix me when Taal is alive # Sanity checks if isperiodic(mesh.tree) @@ -108,10 +108,6 @@ function Dg1D(equation::AbstractEquation{NDIMS, NVARS}, surface_flux_function, v # Initialize differentiation operator volume_integral_type = Val(Symbol(parameter("volume_integral_type", "weak_form", valid=["weak_form", "split_form", "shock_capturing"]))) - # FIXME: This should be removed as soon as it possible to set solver-specific parameters - #if equation isa AbstractHyperbolicDiffusionEquations && globals[:euler_gravity] - # volume_integral_type = Val(:weak_form) - #end dhat = calc_dhat(nodes, weights) dsplit = calc_dsplit(nodes, weights) dsplit_transposed = transpose(calc_dsplit(nodes, weights)) @@ -975,7 +971,7 @@ function rhs!(dg::Dg1D, t_stage) @timeit timer() "source terms" calc_sources!(dg, dg.source_terms, t_stage) end -# TODO: implement 2D!!! +# TODO: implement 1D!!! # Apply positivity limiter of Zhang and Shu to nodal values elements.u function apply_positivity_preserving_limiter!(dg::Dg1D) end From 0bb4e766f0468be0ba8bbb06095594bb84e5bbac Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 17:06:34 +0200 Subject: [PATCH 51/55] Update cos_phi --- src/equations/1d/compressible_euler.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 54c39b5a1bc..abd655913c3 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -75,7 +75,7 @@ function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEqu inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) - # The following code is equivalent to + # The following code is equivalent to # phi = atan(0.0, x_norm) # cos_phi = cos(phi) # in 1D but faster @@ -95,8 +95,11 @@ function initial_conditions_blast_wave(x, t, equation::CompressibleEulerEquation inicenter = SVector(0.0, 0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) - phi = atan(0.0, x_norm) - cos_phi = cos(phi) + # The following code is equivalent to + # phi = atan(0.0, x_norm) + # cos_phi = cos(phi) + # in 1D but faster + cos_phi = x_norm > 0 ? one(x_norm) : -one(x_norm) # Calculate primitive variables rho = r > 0.5 ? 1.0 : 1.1691 From 5ba0477b57c212bff753af1960577009ca7de93b Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 17:16:07 +0200 Subject: [PATCH 52/55] Remove surface_ids --- src/solvers/dg/1d/containers.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/solvers/dg/1d/containers.jl b/src/solvers/dg/1d/containers.jl index 5248e456989..f41fae19971 100644 --- a/src/solvers/dg/1d/containers.jl +++ b/src/solvers/dg/1d/containers.jl @@ -7,7 +7,6 @@ struct ElementContainer1D{NVARS, POLYDEG} <: AbstractContainer u_tmp3::Array{Float64, 3} # [variables, i, elements] inverse_jacobian::Vector{Float64} # [elements] node_coordinates::Array{Float64, 3} # [orientation, i, elements] - surface_ids::Matrix{Int} # [direction, elements] surface_flux_values::Array{Float64, 3} # [variables, direction, elements] cell_ids::Vector{Int} # [elements] end @@ -23,12 +22,11 @@ function ElementContainer1D{NVARS, POLYDEG}(capacity::Integer) where {NVARS, POL u_tmp3 = fill(0.0, NVARS, n_nodes, capacity) inverse_jacobian = fill(NaN, capacity) node_coordinates = fill(NaN, 1, n_nodes, capacity) - surface_ids = fill(typemin(Int), 2 * 1, capacity) surface_flux_values = fill(NaN, NVARS, 2 * 1, capacity) cell_ids = fill(typemin(Int), capacity) elements = ElementContainer1D{NVARS, POLYDEG}(u, u_t, u_tmp2, u_tmp3, inverse_jacobian, node_coordinates, - surface_ids, surface_flux_values, cell_ids) + surface_flux_values, cell_ids) return elements end From 9deb913746d8cb4c5312a094cac5f0b5aa21abf0 Mon Sep 17 00:00:00 2001 From: JuliaOd <71124291+JuliaOd@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:20:03 +0200 Subject: [PATCH 53/55] Update src/equations/1d/compressible_euler.jl Co-authored-by: Hendrik Ranocha --- src/equations/1d/compressible_euler.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index abd655913c3..48d48048c5f 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -72,7 +72,7 @@ end function initial_conditions_weak_blast_wave(x, t, equation::CompressibleEulerEquations1D) # From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) # Set up polar coordinates - inicenter = SVector(0.0, 0.0) + inicenter = SVector(0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) # The following code is equivalent to From 0d7311cc6e2463cfb3c8734c9b0c185fd2033ba7 Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 17:34:42 +0200 Subject: [PATCH 54/55] Add Julia Odenthal to Authors --- AUTHORS.md | 1 + docs/src/authors.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 31119f68d34..49b902813b1 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -22,6 +22,7 @@ are listed in alphabetical order: * Erik Faulhaber * Gregor Gassner +* Julia Odenthal * Hendrik Ranocha * Michael Schlottke-Lakemper * Andrew Winters diff --git a/docs/src/authors.md b/docs/src/authors.md index 52a7606b68e..075965598bd 100644 --- a/docs/src/authors.md +++ b/docs/src/authors.md @@ -22,6 +22,7 @@ are listed in alphabetical order: * Erik Faulhaber * Gregor Gassner +* Julia Odenthal * Hendrik Ranocha * Michael Schlottke-Lakemper * Andrew Winters From 67aabd4b3f62d2bc3f6136a10aad3034041318ad Mon Sep 17 00:00:00 2001 From: JuliaOd Date: Thu, 1 Oct 2020 17:36:42 +0200 Subject: [PATCH 55/55] Update inicenters in ICs --- src/equations/1d/compressible_euler.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equations/1d/compressible_euler.jl b/src/equations/1d/compressible_euler.jl index 48d48048c5f..d486290fcce 100644 --- a/src/equations/1d/compressible_euler.jl +++ b/src/equations/1d/compressible_euler.jl @@ -92,7 +92,7 @@ end function initial_conditions_blast_wave(x, t, equation::CompressibleEulerEquations1D) # Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> "medium blast wave" # Set up polar coordinates - inicenter = SVector(0.0, 0.0) + inicenter = SVector(0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm) # The following code is equivalent to @@ -111,7 +111,7 @@ end function initial_conditions_sedov_blast_wave(x, t, equation::CompressibleEulerEquations1D) # Set up polar coordinates - inicenter = SVector(0.0, 0.0) + inicenter = SVector(0.0) x_norm = x[1] - inicenter[1] r = abs(x_norm)