From 95518c5670774dfccfd66db2fc0df15ec91b251f Mon Sep 17 00:00:00 2001
From: Simon Candelaresi <10759273+SimonCan@users.noreply.github.com>
Date: Fri, 16 Jun 2023 20:35:24 +0100
Subject: [PATCH 001/263] Initial support for surface coupling of two systems
(#1452)
* Corrected bugs from coupled to main merger.
* Added polytropic equation.
* Added further polytropic equations and examples.
* Added coupling equations.
* Added coupling equation between Euler.
* Commented debugging bits, like infiltrator.
* Add missing `using`
* Fix `destats` deprecation warning
* Added coupled elixir.
* Removed commented testing code.
* Added other_list to the coupled semi discretisation elixir.
* Removed flux coupling equation.
* Removed surface coupling equation for polytropic Euler.
* Removed polytropic Euler equation.
* Removed any code related to BoundaryConditionCoupledAB.
* Removed flux coupling code.
* Removed numerical fluxes for BoundaryConditionCoupledAB.
* Removed surface fluxes for BoundaryConditionCoupledAB.
* Removed coupled elixir.
* Remove Coupled StructuredMesh from visualization test.
* Remove duplicate function definitions
* make advection elixir go further
* Removed initial_condition_peak.
* Removed src/equations/hyperbolic_diffusion_2d.jl.
* Removed 3d coupling.
* Remopved 3d capability.
* Removed 3d capability.
* Removed 3d plotting of coupled data.
* Remove extra dependencies
* Remove whitespace changes
* Fix type instability
* Some temporary fixes.
* Fix type in semidiscretization
* Removed analysis_callback for simple coupled elixir.
* Removed analysis callbacks for the coupled case.
* Removed AnalysysCallback for coupled elixir.
* Removed polytropic coupling elixir.
* Update summary output
* Update src/solvers/dgsem_structured/dg_2d.jl
* Format summary
* Fix save solution callback
* Remove unused code
* Move timeit call before dispatch on semi
* Avoid copy on calculcate_dt
* Avoid copy on save_solution_file
* Remove unnnecessary override of wrap_array
* Undo changes to analysis callback
* Remove equations_list
* Remove unused functions
* nmeshes -> nsystems
* Further cleanup
* Move BoundaryConditionCoupled to the correct location
* Visualize setup
* Formatting improvmenets
* Change 1:nsystems(semi) to eachsystem(semi)
* Remove redundant ndofs(...) function
* copy_to_coupled_boundary --> copy_to_coupled_boundary!
* Move all SemidiscretizationCoupled-specific code to semi/semi_coupled.jl
* Use uEltype for BCCoupled
* Add comment
* I --> Indices
* Add comment
* Remove Base.summary for SemiCoupled since it appears to be unused
* Add parens
* Int64 -> Int
* Add xref for Documenter.jl
* Fixup comment
* Remove unused `total_volume`
* Remove obsolete comment
* summary_semi --> print_summary_semi for clarity
* Make SemiCoupled ctor more convenient
* Fix docstring
* Add description to elixir
* Rename elixir
* Remove unused kwarg
* Fix argument order and simplify interface for IO functions
* Explicitly return nothing in functions that should do - nothing
* Update comment
* Add AnalysisCallback to coupled semidiscretization (#1505)
* Add AnalysisCallback to coupled semidiscretization
* First non-crashing version of the AnalysisCallbackCoupled
* Added comment to offending line in the analysis callback.
* Fix stupid bug
* Rename variable for easier testing
* Clean up code
* Remove type instability
* Prettify output
* Add test
* Enable `convergence_test` for SemidiscretizationCoupled
* Increased the frequency of the solution write out for a more usable animation.
* Reverted analysis intervals.
* Updated the l2 and linf errors for the elixir_advection_basic_coupled.jl test
to reflect the increased simulation time.
* Corrected bracket typo in structured_2d test.
* Renamed plural variable names to lists.
* Further renaiming plural variable names.
* Added convergence_test for elixir_advection_basic_coupled.
* Fix coverage for convergence_test
* Add test for analysis_callback(sol)
* Split timers between systems
* fully specialize on rhs and parameters when constructing an ODEProblem
* switch example back to OrdinaryDiffEq.jl
* Reverted coupled example to use Trixi.solve instead of OrdinaryDiffEq solve.
This should fix issues with LoadError: Failed to precompile OrdinaryDiffEq
in the thread_legacy test.
* Changed Julia version in project toml to 1.9
to fix OrdinaryDiffEq issues on the github test.
* Change 1:nsystems(semi) to eachsystem(semi)
* Use `get_system_u_ode`
* Move all SemidiscretizationCoupled-specific code to semi/semi_coupled.jl
* Changed file name name of elixir_advection_basic_coupled.jl to elixir_advection_coupled.jl.
* Reverted Julia version to 1.8 in Project toml file.
* Apply suggestions from code review
* -use type SciMLBase.FullSpecialize instead of instance
* Use get_system_u_ode instead of manual view
* Reorder elixir ingredients
* Make comment reflect code again
* Use solve from OrdinaryDiffEq
* Use more precise type for array
* Test EOCs for each system separately
* Allow test to run for the full duration
---------
Co-authored-by: SimonCan
Co-authored-by: Hendrik Ranocha
* Remove unused `total_volume(...)`
* Make `save_solution_file` work for SemiEulerGravity again (and make it multi-system aware)
* Update src/semidiscretization/semidiscretization_euler_gravity.jl
Co-authored-by: Hendrik Ranocha
* Apply formatting
---------
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
Co-authored-by: Hendrik Ranocha
Co-authored-by: Benjamin Bolm <74359358+bennibolm@users.noreply.github.com>
Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com>
Co-authored-by: Lucas Gemein <74359570+NichtLucas@users.noreply.github.com>
---
.../elixir_advection_coupled.jl | 117 ++++
src/Trixi.jl | 8 +-
src/auxiliary/special_elixirs.jl | 16 +-
src/callbacks_step/analysis.jl | 30 +-
src/callbacks_step/save_solution.jl | 91 +--
src/callbacks_step/stepsize.jl | 19 +-
src/callbacks_step/summary.jl | 22 +-
src/meshes/mesh_io.jl | 8 +-
src/semidiscretization/semidiscretization.jl | 6 +-
.../semidiscretization_coupled.jl | 610 ++++++++++++++++++
.../semidiscretization_euler_gravity.jl | 17 +-
test/test_special_elixirs.jl | 7 +
test/test_structured_2d.jl | 13 +
13 files changed, 890 insertions(+), 74 deletions(-)
create mode 100644 examples/structured_2d_dgsem/elixir_advection_coupled.jl
create mode 100644 src/semidiscretization/semidiscretization_coupled.jl
diff --git a/examples/structured_2d_dgsem/elixir_advection_coupled.jl b/examples/structured_2d_dgsem/elixir_advection_coupled.jl
new file mode 100644
index 00000000000..1e54e411db6
--- /dev/null
+++ b/examples/structured_2d_dgsem/elixir_advection_coupled.jl
@@ -0,0 +1,117 @@
+using OrdinaryDiffEq
+using Trixi
+
+
+###############################################################################
+# Coupled semidiscretization of two linear advection systems, which are connected periodically
+#
+# In this elixir, we have a square domain that is divided into a left half and a right half. On each
+# half of the domain, a completely independent SemidiscretizationHyperbolic is created for the
+# linear advection equations. The two systems are coupled in the x-direction and have periodic
+# boundaries in the y-direction. For a high-level overview, see also the figure below:
+#
+# (-1, 1) ( 1, 1)
+# ┌────────────────────┬────────────────────┐
+# │ ↑ periodic ↑ │ ↑ periodic ↑ │
+# │ │ │
+# │ │ │
+# │ ========= │ ========= │
+# │ system #1 │ system #2 │
+# │ ========= │ ========= │
+# │ │ │
+# │ │ │
+# │ │ │
+# │ │ │
+# │ coupled -->│<-- coupled │
+# │ │ │
+# │<-- coupled │ coupled -->│
+# │ │ │
+# │ │ │
+# │ ↓ periodic ↓ │ ↓ periodic ↓ │
+# └────────────────────┴────────────────────┘
+# (-1, -1) ( 1, -1)
+
+advection_velocity = (0.2, -0.7)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)
+
+# First mesh is the left half of a [-1,1]^2 square
+coordinates_min1 = (-1.0, -1.0) # minimum coordinates (min(x), min(y))
+coordinates_max1 = ( 0.0, 1.0) # maximum coordinates (max(x), max(y))
+
+# Define identical resolution as a variable such that it is easier to change from `trixi_include`
+cells_per_dimension = (8, 16)
+
+cells_per_dimension1 = cells_per_dimension
+
+mesh1 = StructuredMesh(cells_per_dimension1, coordinates_min1, coordinates_max1)
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi1 = SemidiscretizationHyperbolic(mesh1, equations, initial_condition_convergence_test, solver,
+ boundary_conditions=(
+ # Connect left boundary with right boundary of right mesh
+ x_neg=BoundaryConditionCoupled(2, (:end, :i_forward), Float64),
+ # Connect right boundary with left boundary of right mesh
+ x_pos=BoundaryConditionCoupled(2, (:begin, :i_forward), Float64),
+ y_neg=boundary_condition_periodic,
+ y_pos=boundary_condition_periodic))
+
+
+# Second mesh is the right half of a [-1,1]^2 square
+coordinates_min2 = (0.0, -1.0) # minimum coordinates (min(x), min(y))
+coordinates_max2 = (1.0, 1.0) # maximum coordinates (max(x), max(y))
+
+cells_per_dimension2 = cells_per_dimension
+
+mesh2 = StructuredMesh(cells_per_dimension2, coordinates_min2, coordinates_max2)
+
+semi2 = SemidiscretizationHyperbolic(mesh2, equations, initial_condition_convergence_test, solver,
+ boundary_conditions=(
+ # Connect left boundary with right boundary of left mesh
+ x_neg=BoundaryConditionCoupled(1, (:end, :i_forward), Float64),
+ # Connect right boundary with left boundary of left mesh
+ x_pos=BoundaryConditionCoupled(1, (:begin, :i_forward), Float64),
+ y_neg=boundary_condition_periodic,
+ y_pos=boundary_condition_periodic))
+
+# Create a semidiscretization that bundles semi1 and semi2
+semi = SemidiscretizationCoupled(semi1, semi2)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 2.0
+ode = semidiscretize(semi, (0.0, 2.0));
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
+# and resets the timers
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
+analysis_callback1 = AnalysisCallback(semi1, interval=100)
+analysis_callback2 = AnalysisCallback(semi2, interval=100)
+analysis_callback = AnalysisCallbackCoupled(semi, analysis_callback1, analysis_callback2)
+
+# The SaveSolutionCallback allows to save the solution to a file in regular intervals
+save_solution = SaveSolutionCallback(interval=100,
+ solution_variables=cons2prim)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
+stepsize_callback = StepsizeCallback(cfl=1.6)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback)
+
+
+###############################################################################
+# run the simulation
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Print the timer summary
+summary_callback()
diff --git a/src/Trixi.jl b/src/Trixi.jl
index d5579aeea33..86e349c7dad 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -117,6 +117,7 @@ include("semidiscretization/semidiscretization.jl")
include("semidiscretization/semidiscretization_hyperbolic.jl")
include("semidiscretization/semidiscretization_hyperbolic_parabolic.jl")
include("semidiscretization/semidiscretization_euler_acoustics.jl")
+include("semidiscretization/semidiscretization_coupled.jl")
include("callbacks_step/callbacks_step.jl")
include("callbacks_stage/callbacks_stage.jl")
include("semidiscretization/semidiscretization_euler_gravity.jl")
@@ -184,7 +185,8 @@ export boundary_condition_do_nothing,
boundary_condition_noslip_wall,
boundary_condition_slip_wall,
boundary_condition_wall,
- BoundaryConditionNavierStokesWall, NoSlip, Adiabatic, Isothermal
+ BoundaryConditionNavierStokesWall, NoSlip, Adiabatic, Isothermal,
+ BoundaryConditionCoupled
export initial_condition_convergence_test, source_terms_convergence_test
export source_terms_harmonic
@@ -229,12 +231,14 @@ export SemidiscretizationEulerAcoustics
export SemidiscretizationEulerGravity, ParametersEulerGravity,
timestep_gravity_erk52_3Sstar!, timestep_gravity_carpenter_kennedy_erk54_2N!
+export SemidiscretizationCoupled
+
export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback,
SaveRestartCallback, SaveSolutionCallback, TimeSeriesCallback, VisualizationCallback,
AveragingCallback,
AMRCallback, StepsizeCallback,
GlmSpeedCallback, LBMCollisionCallback, EulerAcousticsCouplingCallback,
- TrivialCallback
+ TrivialCallback, AnalysisCallbackCoupled
export load_mesh, load_time
diff --git a/src/auxiliary/special_elixirs.jl b/src/auxiliary/special_elixirs.jl
index da73b42e572..25bca8939ce 100644
--- a/src/auxiliary/special_elixirs.jl
+++ b/src/auxiliary/special_elixirs.jl
@@ -85,9 +85,21 @@ function convergence_test(mod::Module, elixir::AbstractString, iterations; kwarg
println("#"^100)
end
- # number of variables
- _, equations, _, _ = mesh_equations_solver_cache(mod.semi)
+ # Use raw error values to compute EOC
+ analyze_convergence(errors, iterations, mod.semi)
+end
+
+# Analyze convergence for any semidiscretization
+# Note: this intermediate method is to allow dispatching on the semidiscretization
+function analyze_convergence(errors, iterations, semi::AbstractSemidiscretization)
+ _, equations, _, _ = mesh_equations_solver_cache(semi)
variablenames = varnames(cons2cons, equations)
+ analyze_convergence(errors, iterations, variablenames)
+end
+
+# This method is called with the collected error values to actually compute and print the EOC
+function analyze_convergence(errors, iterations,
+ variablenames::Union{Tuple, AbstractArray})
nvariables = length(variablenames)
# Reshape errors to get a matrix where the i-th row represents the i-th iteration
diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl
index 2e038401df7..7fa2e21a244 100644
--- a/src/callbacks_step/analysis.jl
+++ b/src/callbacks_step/analysis.jl
@@ -84,11 +84,13 @@ function Base.show(io::IO, ::MIME"text/plain",
end
end
+# This is the convenience constructor that gets called from the elixirs
function AnalysisCallback(semi::AbstractSemidiscretization; kwargs...)
mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
AnalysisCallback(mesh, equations, solver, cache; kwargs...)
end
+# This is the actual constructor
function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache;
interval = 0,
save_analysis = false,
@@ -132,9 +134,18 @@ function AnalysisCallback(mesh, equations::AbstractEquations, solver, cache;
initialize = initialize!)
end
+# This method gets called from OrdinaryDiffEq's `solve(...)`
function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t,
integrator) where {Condition, Affect! <: AnalysisCallback}
semi = integrator.p
+ du_ode = first(get_tmp_cache(integrator))
+ initialize!(cb, u_ode, du_ode, t, integrator, semi)
+end
+
+# This is the actual initialization method
+# Note: we have this indirection to allow initializing a callback from the AnalysisCallbackCoupled
+function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, du_ode, t,
+ integrator, semi) where {Condition, Affect! <: AnalysisCallback}
initial_state_integrals = integrate(u_ode, semi)
_, equations, _, _ = mesh_equations_solver_cache(semi)
@@ -202,13 +213,21 @@ function initialize!(cb::DiscreteCallback{Condition, Affect!}, u_ode, t,
# Note: For details see the actual callback function below
analysis_callback.start_gc_time = Base.gc_time_ns()
- analysis_callback(integrator)
+ analysis_callback(u_ode, du_ode, integrator, semi)
return nothing
end
-# TODO: Taal refactor, allow passing an IO object (which could be devnull to avoid cluttering the console)
+# This method gets called from OrdinaryDiffEq's `solve(...)`
function (analysis_callback::AnalysisCallback)(integrator)
semi = integrator.p
+ du_ode = first(get_tmp_cache(integrator))
+ u_ode = integrator.u
+ analysis_callback(u_ode, du_ode, integrator, semi)
+end
+
+# This method gets called internally as the main entry point to the AnalysiCallback
+# TODO: Taal refactor, allow passing an IO object (which could be devnull to avoid cluttering the console)
+function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi)
mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
@unpack dt, t = integrator
iter = integrator.stats.naccept
@@ -300,15 +319,14 @@ function (analysis_callback::AnalysisCallback)(integrator)
end
# Calculate current time derivative (needed for semidiscrete entropy time derivative, residual, etc.)
- du_ode = first(get_tmp_cache(integrator))
# `integrator.f` is usually just a call to `rhs!`
# However, we want to allow users to modify the ODE RHS outside of Trixi.jl
# and allow us to pass a combined ODE RHS to OrdinaryDiffEq, e.g., for
# hyperbolic-parabolic systems.
- @notimeit timer() integrator.f(du_ode, integrator.u, semi, t)
- u = wrap_array(integrator.u, mesh, equations, solver, cache)
+ @notimeit timer() integrator.f(du_ode, u_ode, semi, t)
+ u = wrap_array(u_ode, mesh, equations, solver, cache)
du = wrap_array(du_ode, mesh, equations, solver, cache)
- l2_error, linf_error = analysis_callback(io, du, u, integrator.u, t, semi)
+ l2_error, linf_error = analysis_callback(io, du, u, u_ode, t, semi)
mpi_println("─"^100)
mpi_println()
diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl
index 55f17bbc1c7..1fe0d6b1e15 100644
--- a/src/callbacks_step/save_solution.jl
+++ b/src/callbacks_step/save_solution.jl
@@ -141,14 +141,7 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte
mpi_isroot() && mkpath(solution_callback.output_directory)
semi = integrator.p
- mesh, _, _, _ = mesh_equations_solver_cache(semi)
- @trixi_timeit timer() "I/O" begin
- if mesh.unsaved_changes
- mesh.current_filename = save_mesh_file(mesh,
- solution_callback.output_directory)
- mesh.unsaved_changes = false
- end
- end
+ @trixi_timeit timer() "I/O" save_mesh(semi, solution_callback.output_directory)
if solution_callback.save_initial_solution
solution_callback(integrator)
@@ -157,6 +150,16 @@ function initialize_save_cb!(solution_callback::SaveSolutionCallback, u, t, inte
return nothing
end
+# Save mesh for a general semidiscretization (default)
+function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep = 0)
+ mesh, _, _, _ = mesh_equations_solver_cache(semi)
+
+ if mesh.unsaved_changes
+ mesh.current_filename = save_mesh_file(mesh, output_directory)
+ mesh.unsaved_changes = false
+ end
+end
+
# this method is called to determine whether the callback should be activated
function (solution_callback::SaveSolutionCallback)(u, t, integrator)
@unpack interval_or_dt, save_final_solution = solution_callback
@@ -174,41 +177,15 @@ end
# this method is called when the callback is activated
function (solution_callback::SaveSolutionCallback)(integrator)
u_ode = integrator.u
- @unpack t, dt = integrator
- iter = integrator.stats.naccept
semi = integrator.p
- mesh, _, _, _ = mesh_equations_solver_cache(semi)
+ iter = integrator.stats.naccept
@trixi_timeit timer() "I/O" begin
- @trixi_timeit timer() "save mesh" if mesh.unsaved_changes
- mesh.current_filename = save_mesh_file(mesh,
- solution_callback.output_directory,
- iter)
- mesh.unsaved_changes = false
- end
-
- element_variables = Dict{Symbol, Any}()
- @trixi_timeit timer() "get element variables" begin
- get_element_variables!(element_variables, u_ode, semi)
- callbacks = integrator.opts.callback
- if callbacks isa CallbackSet
- for cb in callbacks.continuous_callbacks
- get_element_variables!(element_variables, u_ode, semi, cb;
- t = integrator.t,
- iter = integrator.stats.naccept)
- end
- for cb in callbacks.discrete_callbacks
- get_element_variables!(element_variables, u_ode, semi, cb;
- t = integrator.t,
- iter = integrator.stats.naccept)
- end
- end
- end
-
- @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter,
- semi,
- solution_callback,
- element_variables)
+ # Call high-level functions that dispatch on semidiscretization type
+ @trixi_timeit timer() "save mesh" save_mesh(semi,
+ solution_callback.output_directory,
+ iter)
+ save_solution_file(semi, u_ode, solution_callback, integrator)
end
# avoid re-evaluating possible FSAL stages
@@ -216,13 +193,43 @@ function (solution_callback::SaveSolutionCallback)(integrator)
return nothing
end
+@inline function save_solution_file(semi::AbstractSemidiscretization, u_ode,
+ solution_callback,
+ integrator; system = "")
+ @unpack t, dt = integrator
+ iter = integrator.stats.naccept
+
+ element_variables = Dict{Symbol, Any}()
+ @trixi_timeit timer() "get element variables" begin
+ get_element_variables!(element_variables, u_ode, semi)
+ callbacks = integrator.opts.callback
+ if callbacks isa CallbackSet
+ for cb in callbacks.continuous_callbacks
+ get_element_variables!(element_variables, u_ode, semi, cb;
+ t = integrator.t, iter = iter)
+ end
+ for cb in callbacks.discrete_callbacks
+ get_element_variables!(element_variables, u_ode, semi, cb;
+ t = integrator.t, iter = iter)
+ end
+ end
+ end
+
+ @trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi,
+ solution_callback,
+ element_variables,
+ system = system)
+end
+
@inline function save_solution_file(u_ode, t, dt, iter,
semi::AbstractSemidiscretization, solution_callback,
- element_variables = Dict{Symbol, Any}())
+ element_variables = Dict{Symbol, Any}();
+ system = "")
mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
u = wrap_array_native(u_ode, mesh, equations, solver, cache)
save_solution_file(u, t, dt, iter, mesh, equations, solver, cache,
- solution_callback, element_variables)
+ solution_callback,
+ element_variables; system = system)
end
# TODO: Taal refactor, move save_mesh_file?
diff --git a/src/callbacks_step/stepsize.jl b/src/callbacks_step/stepsize.jl
index 9e9f2d4885b..8b5cb958318 100644
--- a/src/callbacks_step/stepsize.jl
+++ b/src/callbacks_step/stepsize.jl
@@ -64,14 +64,11 @@ end
t = integrator.t
u_ode = integrator.u
semi = integrator.p
- mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
@unpack cfl_number = stepsize_callback
- u = wrap_array(u_ode, mesh, equations, solver, cache)
- dt = @trixi_timeit timer() "calculate dt" begin
- cfl_number * max_dt(u, t, mesh, have_constant_speed(equations), equations,
- solver, cache)
- end
+ # Dispatch based on semidiscretization
+ dt = @trixi_timeit timer() "calculate dt" calculate_dt(u_ode, t, cfl_number,
+ semi)
set_proposed_dt!(integrator, dt)
integrator.opts.dtmax = dt
@@ -83,6 +80,16 @@ end
return nothing
end
+# General case for a single semidiscretization
+function calculate_dt(u_ode, t, cfl_number, semi::AbstractSemidiscretization)
+ mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
+ u = wrap_array(u_ode, mesh, equations, solver, cache)
+
+ dt = cfl_number * max_dt(u, t, mesh,
+ have_constant_speed(equations), equations,
+ solver, cache)
+end
+
# Time integration methods from the DiffEq ecosystem without adaptive time stepping on their own
# such as `CarpenterKennedy2N54` require passing `dt=...` in `solve(ode, ...)`. Since we don't have
# an integrator at this stage but only the ODE, this method will be used there. It's called in
diff --git a/src/callbacks_step/summary.jl b/src/callbacks_step/summary.jl
index a73b2a1913b..08e13d0b98d 100644
--- a/src/callbacks_step/summary.jl
+++ b/src/callbacks_step/summary.jl
@@ -152,15 +152,7 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator)
:indentation_level => 0)
semi = integrator.p
- show(io_context, MIME"text/plain"(), semi)
- println(io, "\n")
- mesh, equations, solver, _ = mesh_equations_solver_cache(semi)
- show(io_context, MIME"text/plain"(), mesh)
- println(io, "\n")
- show(io_context, MIME"text/plain"(), equations)
- println(io, "\n")
- show(io_context, MIME"text/plain"(), solver)
- println(io, "\n")
+ print_summary_semidiscretization(io_context, semi)
callbacks = integrator.opts.callback
if callbacks isa CallbackSet
@@ -208,6 +200,18 @@ function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator)
return nothing
end
+function print_summary_semidiscretization(io::IO, semi::AbstractSemidiscretization)
+ show(io, MIME"text/plain"(), semi)
+ println(io, "\n")
+ mesh, equations, solver, _ = mesh_equations_solver_cache(semi)
+ show(io, MIME"text/plain"(), mesh)
+ println(io, "\n")
+ show(io, MIME"text/plain"(), equations)
+ println(io, "\n")
+ show(io, MIME"text/plain"(), solver)
+ println(io, "\n")
+end
+
function (cb::DiscreteCallback{Condition, Affect!})(io::IO = stdout) where {Condition,
Affect! <:
typeof(summary_callback)
diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl
index b9c462fa15a..ede85d80106 100644
--- a/src/meshes/mesh_io.jl
+++ b/src/meshes/mesh_io.jl
@@ -95,11 +95,15 @@ end
# of the mesh, like its size and the type of boundary mapping function.
# Then, within Trixi2Vtk, the StructuredMesh and its node coordinates are reconstructured from
# these attributes for plotting purposes
-function save_mesh_file(mesh::StructuredMesh, output_directory)
+function save_mesh_file(mesh::StructuredMesh, output_directory; system = "")
# Create output directory (if it does not exist)
mkpath(output_directory)
- filename = joinpath(output_directory, "mesh.h5")
+ if isempty(system)
+ filename = joinpath(output_directory, "mesh.h5")
+ else
+ filename = joinpath(output_directory, @sprintf("mesh_%s.h5", system))
+ end
# Open file (clobber existing content)
h5open(filename, "w") do file
diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl
index 8fef66d261e..ac312c57c89 100644
--- a/src/semidiscretization/semidiscretization.jl
+++ b/src/semidiscretization/semidiscretization.jl
@@ -76,7 +76,8 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan)
# mpi_isparallel() && MPI.Barrier(mpi_comm())
# See https://github.com/trixi-framework/Trixi.jl/issues/328
iip = true # is-inplace, i.e., we modify a vector when calling rhs!
- return ODEProblem{iip}(rhs!, u0_ode, tspan, semi)
+ specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi)
+ return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi)
end
"""
@@ -93,7 +94,8 @@ function semidiscretize(semi::AbstractSemidiscretization, tspan,
# mpi_isparallel() && MPI.Barrier(mpi_comm())
# See https://github.com/trixi-framework/Trixi.jl/issues/328
iip = true # is-inplace, i.e., we modify a vector when calling rhs!
- return ODEProblem{iip}(rhs!, u0_ode, tspan, semi)
+ specialize = SciMLBase.FullSpecialize # specialize on rhs! and parameters (semi)
+ return ODEProblem{iip, specialize}(rhs!, u0_ode, tspan, semi)
end
"""
diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl
new file mode 100644
index 00000000000..b7adff78425
--- /dev/null
+++ b/src/semidiscretization/semidiscretization_coupled.jl
@@ -0,0 +1,610 @@
+"""
+ SemidiscretizationCoupled
+
+A struct used to bundle multiple semidiscretizations.
+[`semidiscretize`](@ref) will return an `ODEProblem` that synchronizes time steps between the semidiscretizations.
+Each call of `rhs!` will call `rhs!` for each semidiscretization individually.
+The semidiscretizations can be coupled by gluing meshes together using [`BoundaryConditionCoupled`](@ref).
+
+!!! warning "Experimental code"
+ This is an experimental feature and can change any time.
+"""
+struct SemidiscretizationCoupled{S, Indices, EquationList} <: AbstractSemidiscretization
+ semis::S
+ u_indices::Indices # u_ode[u_indices[i]] is the part of u_ode corresponding to semis[i]
+ performance_counter::PerformanceCounter
+end
+
+"""
+ SemidiscretizationCoupled(semis...)
+
+Create a coupled semidiscretization that consists of the semidiscretizations passed as arguments.
+"""
+function SemidiscretizationCoupled(semis...)
+ @assert all(semi -> ndims(semi) == ndims(semis[1]), semis) "All semidiscretizations must have the same dimension!"
+
+ # Number of coefficients for each semidiscretization
+ n_coefficients = zeros(Int, length(semis))
+ for i in 1:length(semis)
+ _, equations, _, _ = mesh_equations_solver_cache(semis[i])
+ n_coefficients[i] = ndofs(semis[i]) * nvariables(equations)
+ end
+
+ # Compute range of coefficients associated with each semidiscretization and allocate coupled BCs
+ u_indices = Vector{UnitRange{Int}}(undef, length(semis))
+ for i in 1:length(semis)
+ offset = sum(n_coefficients[1:(i - 1)]) + 1
+ u_indices[i] = range(offset, length = n_coefficients[i])
+
+ allocate_coupled_boundary_conditions(semis[i])
+ end
+
+ performance_counter = PerformanceCounter()
+
+ SemidiscretizationCoupled{typeof(semis), typeof(u_indices), typeof(performance_counter)
+ }(semis, u_indices, performance_counter)
+end
+
+function Base.show(io::IO, semi::SemidiscretizationCoupled)
+ @nospecialize semi # reduce precompilation time
+
+ print(io, "SemidiscretizationCoupled($(semi.semis))")
+end
+
+function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled)
+ @nospecialize semi # reduce precompilation time
+
+ if get(io, :compact, false)
+ show(io, semi)
+ else
+ summary_header(io, "SemidiscretizationCoupled")
+ summary_line(io, "#spatial dimensions", ndims(semi.semis[1]))
+ summary_line(io, "#systems", nsystems(semi))
+ for i in eachsystem(semi)
+ summary_line(io, "system", i)
+ mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i])
+ summary_line(increment_indent(io), "mesh", mesh |> typeof |> nameof)
+ summary_line(increment_indent(io), "equations", equations |> typeof |> nameof)
+ summary_line(increment_indent(io), "initial condition",
+ semi.semis[i].initial_condition)
+ # no boundary conditions since that could be too much
+ summary_line(increment_indent(io), "source terms", semi.semis[i].source_terms)
+ summary_line(increment_indent(io), "solver", solver |> typeof |> nameof)
+ end
+ summary_line(io, "total #DOFs", ndofs(semi))
+ summary_footer(io)
+ end
+end
+
+function print_summary_semidiscretization(io::IO, semi::SemidiscretizationCoupled)
+ show(io, MIME"text/plain"(), semi)
+ println(io, "\n")
+ for i in eachsystem(semi)
+ mesh, equations, solver, _ = mesh_equations_solver_cache(semi.semis[i])
+ summary_header(io, "System #$i")
+
+ summary_line(io, "mesh", mesh |> typeof |> nameof)
+ show(increment_indent(io), MIME"text/plain"(), mesh)
+
+ summary_line(io, "equations", equations |> typeof |> nameof)
+ show(increment_indent(io), MIME"text/plain"(), equations)
+
+ summary_line(io, "solver", solver |> typeof |> nameof)
+ show(increment_indent(io), MIME"text/plain"(), solver)
+
+ summary_footer(io)
+ println(io, "\n")
+ end
+end
+
+@inline Base.ndims(semi::SemidiscretizationCoupled) = ndims(semi.semis[1])
+
+@inline nsystems(semi::SemidiscretizationCoupled) = length(semi.semis)
+
+@inline eachsystem(semi::SemidiscretizationCoupled) = Base.OneTo(nsystems(semi))
+
+@inline Base.real(semi::SemidiscretizationCoupled) = promote_type(real.(semi.semis)...)
+
+@inline Base.eltype(semi::SemidiscretizationCoupled) = promote_type(eltype.(semi.semis)...)
+
+@inline function ndofs(semi::SemidiscretizationCoupled)
+ sum(ndofs, semi.semis)
+end
+
+@inline function nelements(semi::SemidiscretizationCoupled)
+ return sum(semi.semis) do semi_
+ mesh, equations, solver, cache = mesh_equations_solver_cache(semi_)
+
+ nelements(mesh, solver, cache)
+ end
+end
+
+function compute_coefficients(t, semi::SemidiscretizationCoupled)
+ @unpack u_indices = semi
+
+ u_ode = Vector{real(semi)}(undef, u_indices[end][end])
+
+ for i in eachsystem(semi)
+ # Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl`
+ u_ode[u_indices[i]] .= compute_coefficients(t, semi.semis[i])
+ end
+
+ return u_ode
+end
+
+@inline function get_system_u_ode(u_ode, index, semi::SemidiscretizationCoupled)
+ @view u_ode[semi.u_indices[index]]
+end
+
+function rhs!(du_ode, u_ode, semi::SemidiscretizationCoupled, t)
+ @unpack u_indices = semi
+
+ time_start = time_ns()
+
+ @trixi_timeit timer() "copy to coupled boundaries" begin
+ for semi_ in semi.semis
+ copy_to_coupled_boundary!(semi_.boundary_conditions, u_ode, semi)
+ end
+ end
+
+ # Call rhs! for each semidiscretization
+ for i in eachsystem(semi)
+ u_loc = get_system_u_ode(u_ode, i, semi)
+ du_loc = get_system_u_ode(du_ode, i, semi)
+
+ @trixi_timeit timer() "system #$i" rhs!(du_loc, u_loc, semi.semis[i], t)
+ end
+
+ runtime = time_ns() - time_start
+ put!(semi.performance_counter, runtime)
+
+ return nothing
+end
+
+################################################################################
+### AnalysisCallback
+################################################################################
+
+"""
+ AnalysisCallbackCoupled(semi, callbacks...)
+
+Combine multiple analysis callbacks for coupled simulations with a
+[`SemidiscretizationCoupled`](@ref). For each coupled system, an indididual
+[`AnalysisCallback`](@ref) **must** be created and passed to the `AnalysisCallbackCoupled` **in
+order**, i.e., in the same sequence as the indidvidual semidiscretizations are stored in the
+`SemidiscretizationCoupled`.
+
+!!! warning "Experimental code"
+ This is an experimental feature and can change any time.
+"""
+struct AnalysisCallbackCoupled{CB}
+ callbacks::CB
+end
+
+function Base.show(io::IO, ::MIME"text/plain",
+ cb_coupled::DiscreteCallback{<:Any, <:AnalysisCallbackCoupled})
+ @nospecialize cb_coupled # reduce precompilation time
+
+ if get(io, :compact, false)
+ show(io, cb_coupled)
+ else
+ analysis_callback_coupled = cb_coupled.affect!
+
+ summary_header(io, "AnalysisCallbackCoupled")
+ for (i, cb) in enumerate(analysis_callback_coupled.callbacks)
+ summary_line(io, "Callback #$i", "")
+ show(increment_indent(io), MIME"text/plain"(), cb)
+ end
+ summary_footer(io)
+ end
+end
+
+# Convenience constructor for the coupled callback that gets called directly from the elixirs
+function AnalysisCallbackCoupled(semi_coupled, callbacks...)
+ if length(callbacks) != nsystems(semi_coupled)
+ error("an AnalysisCallbackCoupled requires one AnalysisCallback for each semidiscretization")
+ end
+
+ analysis_callback_coupled = AnalysisCallbackCoupled{typeof(callbacks)}(callbacks)
+
+ # This callback is triggered if any of its subsidiary callbacks' condition is triggered
+ condition = (u, t, integrator) -> any(callbacks) do callback
+ callback.condition(u, t, integrator)
+ end
+
+ DiscreteCallback(condition, analysis_callback_coupled,
+ save_positions = (false, false),
+ initialize = initialize!)
+end
+
+# This method gets called during initialization from OrdinaryDiffEq's `solve(...)`
+function initialize!(cb_coupled::DiscreteCallback{Condition, Affect!}, u_ode_coupled, t,
+ integrator) where {Condition, Affect! <: AnalysisCallbackCoupled}
+ analysis_callback_coupled = cb_coupled.affect!
+ semi_coupled = integrator.p
+ du_ode_coupled = first(get_tmp_cache(integrator))
+
+ # Loop over coupled systems' callbacks and initialize them individually
+ for i in eachsystem(semi_coupled)
+ cb = analysis_callback_coupled.callbacks[i]
+ semi = semi_coupled.semis[i]
+ u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled)
+ du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled)
+ initialize!(cb, u_ode, du_ode, t, integrator, semi)
+ end
+end
+
+# This method gets called from OrdinaryDiffEq's `solve(...)`
+function (analysis_callback_coupled::AnalysisCallbackCoupled)(integrator)
+ semi_coupled = integrator.p
+ u_ode_coupled = integrator.u
+ du_ode_coupled = first(get_tmp_cache(integrator))
+
+ # Loop over coupled systems' callbacks and call them individually
+ for i in eachsystem(semi_coupled)
+ @unpack condition = analysis_callback_coupled.callbacks[i]
+ analysis_callback = analysis_callback_coupled.callbacks[i].affect!
+ u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled)
+
+ # Check condition and skip callback if it is not yet its turn
+ if !condition(u_ode, integrator.t, integrator)
+ continue
+ end
+
+ semi = semi_coupled.semis[i]
+ du_ode = get_system_u_ode(du_ode_coupled, i, semi_coupled)
+ analysis_callback(u_ode, du_ode, integrator, semi)
+ end
+end
+
+# used for error checks and EOC analysis
+function (cb::DiscreteCallback{Condition, Affect!})(sol) where {Condition,
+ Affect! <:
+ AnalysisCallbackCoupled}
+ semi_coupled = sol.prob.p
+ u_ode_coupled = sol.u[end]
+ @unpack callbacks = cb.affect!
+
+ uEltype = real(semi_coupled)
+ l2_error_collection = uEltype[]
+ linf_error_collection = uEltype[]
+ for i in eachsystem(semi_coupled)
+ analysis_callback = callbacks[i].affect!
+ @unpack analyzer = analysis_callback
+ cache_analysis = analysis_callback.cache
+
+ semi = semi_coupled.semis[i]
+ u_ode = get_system_u_ode(u_ode_coupled, i, semi_coupled)
+
+ l2_error, linf_error = calc_error_norms(u_ode, sol.t[end], analyzer, semi,
+ cache_analysis)
+ append!(l2_error_collection, l2_error)
+ append!(linf_error_collection, linf_error)
+ end
+
+ (; l2 = l2_error_collection, linf = linf_error_collection)
+end
+
+################################################################################
+### SaveSolutionCallback
+################################################################################
+
+# Save mesh for a coupled semidiscretization, which contains multiple meshes internally
+function save_mesh(semi::SemidiscretizationCoupled, output_directory, timestep = 0)
+ for i in eachsystem(semi)
+ mesh, _, _, _ = mesh_equations_solver_cache(semi.semis[i])
+
+ if mesh.unsaved_changes
+ mesh.current_filename = save_mesh_file(mesh, output_directory, system = i)
+ mesh.unsaved_changes = false
+ end
+ end
+end
+
+@inline function save_solution_file(semi::SemidiscretizationCoupled, u_ode,
+ solution_callback,
+ integrator)
+ @unpack semis = semi
+
+ for i in eachsystem(semi)
+ u_ode_slice = get_system_u_ode(u_ode, i, semi)
+ save_solution_file(semis[i], u_ode_slice, solution_callback, integrator, system = i)
+ end
+end
+
+################################################################################
+### StepsizeCallback
+################################################################################
+
+# In case of coupled system, use minimum timestep over all systems
+function calculate_dt(u_ode, t, cfl_number, semi::SemidiscretizationCoupled)
+ dt = minimum(eachsystem(semi)) do i
+ u_ode_slice = get_system_u_ode(u_ode, i, semi)
+ calculate_dt(u_ode_slice, t, cfl_number, semi.semis[i])
+ end
+
+ return dt
+end
+
+################################################################################
+### Equations
+################################################################################
+
+"""
+ BoundaryConditionCoupled(other_semi_index, indices, uEltype)
+
+Boundary condition to glue two meshes together. Solution values at the boundary
+of another mesh will be used as boundary values. This requires the use
+of [`SemidiscretizationCoupled`](@ref). The other mesh is specified by `other_semi_index`,
+which is the index of the mesh in the tuple of semidiscretizations.
+
+Note that the elements and nodes of the two meshes at the coupled boundary must coincide.
+This is currently only implemented for [`StructuredMesh`](@ref).
+
+# Arguments
+- `other_semi_index`: the index in `SemidiscretizationCoupled` of the semidiscretization
+ from which the values are copied
+- `indices::Tuple`: node/cell indices at the boundary of the mesh in the other
+ semidiscretization. See examples below.
+- `uEltype::Type`: element type of solution
+
+# Examples
+```julia
+# Connect the left boundary of mesh 2 to our boundary such that our positive
+# boundary direction will match the positive y direction of the other boundary
+BoundaryConditionCoupled(2, (:begin, :i), Float64)
+
+# Connect the same two boundaries oppositely oriented
+BoundaryConditionCoupled(2, (:begin, :i_backwards), Float64)
+
+# Using this as y_neg boundary will connect `our_cells[i, 1, j]` to `other_cells[j, end-i, end]`
+BoundaryConditionCoupled(2, (:j, :i_backwards, :end), Float64)
+```
+
+!!! warning "Experimental code"
+ This is an experimental feature and can change any time.
+"""
+mutable struct BoundaryConditionCoupled{NDIMS, NDIMST2M1, uEltype <: Real, Indices}
+ # NDIMST2M1 == NDIMS * 2 - 1
+ # Buffer for boundary values: [variable, nodes_i, nodes_j, cell_i, cell_j]
+ u_boundary :: Array{uEltype, NDIMST2M1} # NDIMS * 2 - 1
+ other_semi_index :: Int
+ other_orientation :: Int
+ indices :: Indices
+
+ function BoundaryConditionCoupled(other_semi_index, indices, uEltype)
+ NDIMS = length(indices)
+ u_boundary = Array{uEltype, NDIMS * 2 - 1}(undef, ntuple(_ -> 0, NDIMS * 2 - 1))
+
+ if indices[1] in (:begin, :end)
+ other_orientation = 1
+ elseif indices[2] in (:begin, :end)
+ other_orientation = 2
+ else # indices[3] in (:begin, :end)
+ other_orientation = 3
+ end
+
+ new{NDIMS, NDIMS * 2 - 1, uEltype, typeof(indices)}(u_boundary, other_semi_index,
+ other_orientation, indices)
+ end
+end
+
+function Base.eltype(boundary_condition::BoundaryConditionCoupled)
+ eltype(boundary_condition.u_boundary)
+end
+
+function (boundary_condition::BoundaryConditionCoupled)(u_inner, orientation, direction,
+ cell_indices, surface_node_indices,
+ surface_flux_function, equations)
+ # get_node_vars(boundary_condition.u_boundary, equations, solver, surface_node_indices..., cell_indices...),
+ # but we don't have a solver here
+ u_boundary = SVector(ntuple(v -> boundary_condition.u_boundary[v,
+ surface_node_indices...,
+ cell_indices...],
+ Val(nvariables(equations))))
+
+ # Calculate boundary flux
+ if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary
+ flux = surface_flux_function(u_inner, u_boundary, orientation, equations)
+ else # u_boundary is "left" of boundary, u_inner is "right" of boundary
+ flux = surface_flux_function(u_boundary, u_inner, orientation, equations)
+ end
+
+ return flux
+end
+
+function allocate_coupled_boundary_conditions(semi::AbstractSemidiscretization)
+ n_boundaries = 2 * ndims(semi)
+ mesh, equations, solver, _ = mesh_equations_solver_cache(semi)
+
+ for direction in 1:n_boundaries
+ boundary_condition = semi.boundary_conditions[direction]
+
+ allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations,
+ solver)
+ end
+end
+
+# Don't do anything for other BCs than BoundaryConditionCoupled
+function allocate_coupled_boundary_condition(boundary_condition, direction, mesh, equations,
+ solver)
+ return nothing
+end
+
+# In 2D
+function allocate_coupled_boundary_condition(boundary_condition::BoundaryConditionCoupled{2
+ },
+ direction, mesh, equations, dg::DGSEM)
+ if direction in (1, 2)
+ cell_size = size(mesh, 2)
+ else
+ cell_size = size(mesh, 1)
+ end
+
+ uEltype = eltype(boundary_condition)
+ boundary_condition.u_boundary = Array{uEltype, 3}(undef, nvariables(equations),
+ nnodes(dg),
+ cell_size)
+end
+
+# Don't do anything for other BCs than BoundaryConditionCoupled
+function copy_to_coupled_boundary!(boundary_condition, u_ode, semi)
+ return nothing
+end
+
+function copy_to_coupled_boundary!(boundary_conditions::Union{Tuple, NamedTuple}, u_ode,
+ semi)
+ for boundary_condition in boundary_conditions
+ copy_to_coupled_boundary!(boundary_condition, u_ode, semi)
+ end
+end
+
+# In 2D
+function copy_to_coupled_boundary!(boundary_condition::BoundaryConditionCoupled{2}, u_ode,
+ semi)
+ @unpack u_indices = semi
+ @unpack other_semi_index, other_orientation, indices = boundary_condition
+
+ mesh, equations, solver, cache = mesh_equations_solver_cache(semi.semis[other_semi_index])
+ u = wrap_array(get_system_u_ode(u_ode, other_semi_index, semi), mesh, equations, solver,
+ cache)
+
+ linear_indices = LinearIndices(size(mesh))
+
+ if other_orientation == 1
+ cells = axes(mesh, 2)
+ else # other_orientation == 2
+ cells = axes(mesh, 1)
+ end
+
+ # Copy solution data to the coupled boundary using "delayed indexing" with
+ # a start value and a step size to get the correct face and orientation.
+ node_index_range = eachnode(solver)
+ i_node_start, i_node_step = index_to_start_step_2d(indices[1], node_index_range)
+ j_node_start, j_node_step = index_to_start_step_2d(indices[2], node_index_range)
+
+ i_cell_start, i_cell_step = index_to_start_step_2d(indices[1], axes(mesh, 1))
+ j_cell_start, j_cell_step = index_to_start_step_2d(indices[2], axes(mesh, 2))
+
+ i_cell = i_cell_start
+ j_cell = j_cell_start
+
+ for cell in cells
+ i_node = i_node_start
+ j_node = j_node_start
+
+ for i in eachnode(solver)
+ for v in 1:size(u, 1)
+ boundary_condition.u_boundary[v, i, cell] = u[v, i_node, j_node,
+ linear_indices[i_cell,
+ j_cell]]
+ end
+ i_node += i_node_step
+ j_node += j_node_step
+ end
+ i_cell += i_cell_step
+ j_cell += j_cell_step
+ end
+end
+
+################################################################################
+### DGSEM/structured
+################################################################################
+
+@inline function calc_boundary_flux_by_direction!(surface_flux_values, u, t, orientation,
+ boundary_condition::BoundaryConditionCoupled,
+ mesh::StructuredMesh, equations,
+ surface_integral, dg::DG, cache,
+ direction, node_indices,
+ surface_node_indices, element)
+ @unpack node_coordinates, contravariant_vectors, inverse_jacobian = cache.elements
+ @unpack surface_flux = surface_integral
+
+ cell_indices = get_boundary_indices(element, orientation, mesh)
+
+ u_inner = get_node_vars(u, equations, dg, node_indices..., element)
+
+ # If the mapping is orientation-reversing, the contravariant vectors' orientation
+ # is reversed as well. The normal vector must be oriented in the direction
+ # from `left_element` to `right_element`, or the numerical flux will be computed
+ # incorrectly (downwind direction).
+ sign_jacobian = sign(inverse_jacobian[node_indices..., element])
+
+ # Contravariant vector Ja^i is the normal vector
+ normal = sign_jacobian * get_contravariant_vector(orientation, contravariant_vectors,
+ node_indices..., element)
+
+ # If the mapping is orientation-reversing, the normal vector will be reversed (see above).
+ # However, the flux now has the wrong sign, since we need the physical flux in normal direction.
+ flux = sign_jacobian * boundary_condition(u_inner, normal, direction, cell_indices,
+ surface_node_indices, surface_flux, equations)
+
+ for v in eachvariable(equations)
+ surface_flux_values[v, surface_node_indices..., direction, element] = flux[v]
+ end
+end
+
+function get_boundary_indices(element, orientation, mesh::StructuredMesh{2})
+ cartesian_indices = CartesianIndices(size(mesh))
+ if orientation == 1
+ # Get index of element in y-direction
+ cell_indices = (cartesian_indices[element][2],)
+ else # orientation == 2
+ # Get index of element in x-direction
+ cell_indices = (cartesian_indices[element][1],)
+ end
+
+ return cell_indices
+end
+
+################################################################################
+### Special elixirs
+################################################################################
+
+# Analyze convergence for SemidiscretizationCoupled
+function analyze_convergence(errors_coupled, iterations,
+ semi_coupled::SemidiscretizationCoupled)
+ # Extract errors: the errors are currently stored as
+ # | iter 1 sys 1 var 1...n | iter 1 sys 2 var 1...n | ... | iter 2 sys 1 var 1...n | ...
+ # but for calling `analyze_convergence` below, we need the following layout
+ # sys n: | iter 1 var 1...n | iter 1 var 1...n | ... | iter 2 var 1...n | ...
+ # That is, we need to extract and join the data for a single system
+ errors = Dict{Symbol, Vector{Float64}}[]
+ for i in eachsystem(semi_coupled)
+ push!(errors, Dict(:l2 => Float64[], :linf => Float64[]))
+ end
+ offset = 0
+ for iter in 1:iterations, i in eachsystem(semi_coupled)
+ # Extract information on current semi
+ semi = semi_coupled.semis[i]
+ _, equations, _, _ = mesh_equations_solver_cache(semi)
+ variablenames = varnames(cons2cons, equations)
+
+ # Compute offset
+ first = offset + 1
+ last = offset + length(variablenames)
+ offset += length(variablenames)
+
+ # Append errors to appropriate storage
+ append!(errors[i][:l2], errors_coupled[:l2][first:last])
+ append!(errors[i][:linf], errors_coupled[:linf][first:last])
+ end
+
+ eoc_mean_values = Vector{Dict{Symbol, Any}}(undef, nsystems(semi_coupled))
+ for i in eachsystem(semi_coupled)
+ # Use visual cues to separate output from multiple systems
+ println()
+ println("="^100)
+ println("# System $i")
+ println("="^100)
+
+ # Extract information on current semi
+ semi = semi_coupled.semis[i]
+ _, equations, _, _ = mesh_equations_solver_cache(semi)
+ variablenames = varnames(cons2cons, equations)
+
+ eoc_mean_values[i] = analyze_convergence(errors[i], iterations, variablenames)
+ end
+
+ return eoc_mean_values
+end
diff --git a/src/semidiscretization/semidiscretization_euler_gravity.jl b/src/semidiscretization/semidiscretization_euler_gravity.jl
index 665f2be9bfa..8fe9de1d2b2 100644
--- a/src/semidiscretization/semidiscretization_euler_gravity.jl
+++ b/src/semidiscretization/semidiscretization_euler_gravity.jl
@@ -477,18 +477,29 @@ end
@inline function save_solution_file(u_ode, t, dt, iter,
semi::SemidiscretizationEulerGravity,
solution_callback,
- element_variables = Dict{Symbol, Any}())
+ element_variables = Dict{Symbol, Any}();
+ system = "")
+ # If this is called already as part of a multi-system setup (i.e., system is non-empty),
+ # we build a combined system name
+ if !isempty(system)
+ system_euler = system * "_euler"
+ system_gravity = system * "_gravity"
+ else
+ system_euler = "euler"
+ system_gravity = "gravity"
+ end
+
u_euler = wrap_array_native(u_ode, semi.semi_euler)
filename_euler = save_solution_file(u_euler, t, dt, iter,
mesh_equations_solver_cache(semi.semi_euler)...,
solution_callback, element_variables,
- system = "euler")
+ system = system_euler)
u_gravity = wrap_array_native(semi.cache.u_ode, semi.semi_gravity)
filename_gravity = save_solution_file(u_gravity, t, dt, iter,
mesh_equations_solver_cache(semi.semi_gravity)...,
solution_callback, element_variables,
- system = "gravity")
+ system = system_gravity)
return filename_euler, filename_gravity
end
diff --git a/test/test_special_elixirs.jl b/test/test_special_elixirs.jl
index 742a3abc376..23017059eaa 100644
--- a/test/test_special_elixirs.jl
+++ b/test/test_special_elixirs.jl
@@ -30,6 +30,12 @@ coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none",
@test isapprox(mean_convergence[:l2], [4.0], rtol=0.05)
end
+ @timed_testset "structured_2d_dgsem coupled" begin
+ mean_convergence = convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_coupled.jl"), 3)
+ @test isapprox(mean_convergence[1][:l2], [4.0], rtol=0.05)
+ @test isapprox(mean_convergence[2][:l2], [4.0], rtol=0.05)
+ end
+
@timed_testset "p4est_2d_dgsem" begin
# Run convergence test on unrefined mesh
no_refine = @cfunction((p4est, which_tree, quadrant) -> Cint(0), Cint, (Ptr{Trixi.p4est_t}, Ptr{Trixi.p4est_topidx_t}, Ptr{Trixi.p4est_quadrant_t}))
@@ -57,6 +63,7 @@ coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none",
@test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_basic.jl"), 2, tspan=(0.0, 0.01))
@test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_extended.jl"), 2, initial_refinement_level=0, tspan=(0.0, 0.1))
@test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_basic.jl"), 2, tspan=(0.0, 0.01))
+ @test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_coupled.jl"), 2, tspan=(0.0, 0.01))
@test_nowarn_mod convergence_test(@__MODULE__, joinpath(EXAMPLES_DIR, "structured_2d_dgsem", "elixir_advection_extended.jl"), 2, cells_per_dimension=(1, 1), tspan=(0.0, 0.1))
end
end
diff --git a/test/test_structured_2d.jl b/test/test_structured_2d.jl
index feaf66c4a7f..16fc72f0a46 100644
--- a/test/test_structured_2d.jl
+++ b/test/test_structured_2d.jl
@@ -19,6 +19,19 @@ isdir(outdir) && rm(outdir, recursive=true)
linf = [6.627000273229378e-5])
end
+ @trixi_testset "elixir_advection_coupled.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_coupled.jl"),
+ l2 = [7.816742843181738e-6, 7.816742843196112e-6],
+ linf = [6.314906965543265e-5, 6.314906965410039e-5],
+ coverage_override = (maxiters=10^5,))
+
+ @testset "analysis_callback(sol) for AnalysisCallbackCoupled" begin
+ errors = analysis_callback(sol)
+ @test errors.l2 ≈ [7.816742843181738e-6, 7.816742843196112e-6] rtol=1.0e-4
+ @test errors.linf ≈ [6.314906965543265e-5, 6.314906965410039e-5] rtol=1.0e-4
+ end
+ end
+
@trixi_testset "elixir_advection_extended.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
l2 = [4.220397559713772e-6],
From deb027adefdd88fccf6cec5ce4ca5c76106a0439 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 16 Jun 2023 22:53:10 +0200
Subject: [PATCH 002/263] Bump crate-ci/typos from 1.14.12 to 1.15.0 (#1524)
* Bump crate-ci/typos from 1.14.12 to 1.15.0
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.14.12 to 1.15.0.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.14.12...v1.15.0)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
* Fix typos
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Schlottke-Lakemper
---
.github/workflows/SpellCheck.yml | 2 +-
docs/src/visualization.md | 2 +-
examples/p4est_2d_dgsem/elixir_euler_supersonic_cylinder.jl | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index c4ab3a98557..bc324c689bc 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.14.12
+ uses: crate-ci/typos@v1.15.0
diff --git a/docs/src/visualization.md b/docs/src/visualization.md
index e29313cc080..8f72bb4b1c6 100644
--- a/docs/src/visualization.md
+++ b/docs/src/visualization.md
@@ -339,7 +339,7 @@ create a [`PlotData1D`](@ref) with the keyword argument `curve` set to your list
Let's give an example of this with the basic advection equation from above by creating
a plot along the circle marked in green:
-![2d-plot-along-cirlce](https://user-images.githubusercontent.com/72009492/130951042-e1849447-8e55-4798-9361-c8badb9f3a49.png)
+![2d-plot-along-circle](https://user-images.githubusercontent.com/72009492/130951042-e1849447-8e55-4798-9361-c8badb9f3a49.png)
We can write a function like this, that outputs a list of points on a circle:
```julia
diff --git a/examples/p4est_2d_dgsem/elixir_euler_supersonic_cylinder.jl b/examples/p4est_2d_dgsem/elixir_euler_supersonic_cylinder.jl
index 42370e861ce..366be700f9f 100644
--- a/examples/p4est_2d_dgsem/elixir_euler_supersonic_cylinder.jl
+++ b/examples/p4est_2d_dgsem/elixir_euler_supersonic_cylinder.jl
@@ -3,7 +3,7 @@
# Boundary conditions are supersonic Mach 3 inflow at the left portion of the domain
# and supersonic outflow at the right portion of the domain. The top and bottom of the
# channel as well as the cylinder are treated as Euler slip wall boundaries.
-# This flow results in strong shock refletions / interactions as well as Kelvin-Helmholtz
+# This flow results in strong shock reflections / interactions as well as Kelvin-Helmholtz
# instabilities at later times as two Mach stems form above and below the cylinder.
#
# For complete details on the problem setup see Section 5.7 of the paper:
From 642da1af9f9cc390f1d3d2a47a5fd07628a632f0 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Mon, 19 Jun 2023 09:07:15 +0200
Subject: [PATCH 003/263] Use `Pointerwrapper`s in `P4estMesh` (#1434)
* use PointerWrappers
* fix typo
* fix typo
* fixes
* fixes
* fixes
* unsafe_load_sc returns PointerWrapper
* bug fixes
* bug fixes
* bug fixes
* add comment about
* rename unsafe_load_* to load_pointerwrapper_*
* format
* fix merge conflicts
* fix bad format again
* fix
* add unsafe_wrap_sc again
* fix
* fix
* Update src/auxiliary/p4est.jl
Co-authored-by: Michael Schlottke-Lakemper
* introduce generic type PointerOrWrapper{T}
* format
---------
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
---
src/auxiliary/p4est.jl | 94 +++++++-----
src/callbacks_step/amr.jl | 30 ++--
src/callbacks_step/amr_dg.jl | 3 +-
src/callbacks_step/analysis.jl | 2 +-
src/meshes/p4est_mesh.jl | 117 +++++++-------
src/solvers/dgsem_p4est/containers.jl | 122 +++++++--------
src/solvers/dgsem_p4est/containers_2d.jl | 2 +-
src/solvers/dgsem_p4est/containers_3d.jl | 2 +-
.../dgsem_p4est/containers_parallel.jl | 145 +++++++++---------
src/solvers/dgsem_p4est/dg_parallel.jl | 80 +++++-----
10 files changed, 311 insertions(+), 286 deletions(-)
diff --git a/src/auxiliary/p4est.jl b/src/auxiliary/p4est.jl
index 93b5166cd81..968af339cbd 100644
--- a/src/auxiliary/p4est.jl
+++ b/src/auxiliary/p4est.jl
@@ -24,35 +24,38 @@ function init_p4est()
return nothing
end
+# for convenience to either pass a Ptr or a PointerWrapper
+const PointerOrWrapper = Union{Ptr{T}, PointerWrapper{T}} where {T}
+
# Convert sc_array of type T to Julia array
-function unsafe_wrap_sc(::Type{T}, sc_array::Ptr{sc_array}) where {T}
- sc_array_obj = unsafe_load(sc_array)
+function unsafe_wrap_sc(::Type{T}, sc_array_ptr::Ptr{sc_array}) where {T}
+ sc_array_obj = unsafe_load(sc_array_ptr)
return unsafe_wrap_sc(T, sc_array_obj)
end
function unsafe_wrap_sc(::Type{T}, sc_array_obj::sc_array) where {T}
elem_count = sc_array_obj.elem_count
array = sc_array_obj.array
-
return unsafe_wrap(Array, Ptr{T}(array), elem_count)
end
-# Load the ith element (1-indexed) of an sc array of type T
-function unsafe_load_sc(::Type{T}, sc_array::Ptr{sc_array}, i = 1) where {T}
- sc_array_obj = unsafe_load(sc_array)
- return unsafe_load_sc(T, sc_array_obj, i)
-end
+function unsafe_wrap_sc(::Type{T}, sc_array_pw::PointerWrapper{sc_array}) where {T}
+ elem_count = sc_array_pw.elem_count[]
+ array = sc_array_pw.array
-function unsafe_load_sc(::Type{T}, sc_array_obj::sc_array, i = 1) where {T}
- element_size = sc_array_obj.elem_size
- @assert element_size == sizeof(T)
+ return unsafe_wrap(Array, Ptr{T}(pointer(array)), elem_count)
+end
- return unsafe_load(Ptr{T}(sc_array_obj.array), i)
+# Load the ith element (1-indexed) of an sc array of type T as PointerWrapper
+function load_pointerwrapper_sc(::Type{T}, sc_array::PointerWrapper{sc_array},
+ i::Integer = 1) where {T}
+ return PointerWrapper(T, pointer(sc_array.array) + (i - 1) * sizeof(T))
end
# Create new `p4est` from a p4est_connectivity
# 2D
-function new_p4est(connectivity::Ptr{p4est_connectivity_t}, initial_refinement_level)
+function new_p4est(connectivity::PointerOrWrapper{p4est_connectivity_t},
+ initial_refinement_level)
comm = P4est.uses_mpi() ? mpi_comm() : 0 # Use Trixi.jl's MPI communicator if p4est supports MPI
p4est_new_ext(comm,
connectivity,
@@ -65,7 +68,8 @@ function new_p4est(connectivity::Ptr{p4est_connectivity_t}, initial_refinement_l
end
# 3D
-function new_p4est(connectivity::Ptr{p8est_connectivity_t}, initial_refinement_level)
+function new_p4est(connectivity::PointerOrWrapper{p8est_connectivity_t},
+ initial_refinement_level)
comm = P4est.uses_mpi() ? mpi_comm() : 0 # Use Trixi.jl's MPI communicator if p4est supports MPI
p8est_new_ext(comm, connectivity, 0, initial_refinement_level, true,
2 * sizeof(Int), C_NULL, C_NULL)
@@ -73,13 +77,13 @@ end
# Save `p4est` data to file
# 2D
-function save_p4est!(file, p4est::Ptr{p4est_t})
+function save_p4est!(file, p4est::PointerOrWrapper{p4est_t})
# Don't save user data of the quads
p4est_save(file, p4est, false)
end
# 3D
-function save_p4est!(file, p8est::Ptr{p8est_t})
+function save_p4est!(file, p8est::PointerOrWrapper{p8est_t})
# Don't save user data of the quads
p8est_save(file, p8est, false)
end
@@ -107,27 +111,33 @@ read_inp_p4est(meshfile, ::Val{3}) = p8est_connectivity_read_inp(meshfile)
# Refine `p4est` if refine_fn_c returns 1
# 2D
-function refine_p4est!(p4est::Ptr{p4est_t}, recursive, refine_fn_c, init_fn_c)
+function refine_p4est!(p4est::PointerOrWrapper{p4est_t}, recursive, refine_fn_c,
+ init_fn_c)
p4est_refine(p4est, recursive, refine_fn_c, init_fn_c)
end
# 3D
-function refine_p4est!(p8est::Ptr{p8est_t}, recursive, refine_fn_c, init_fn_c)
+function refine_p4est!(p8est::PointerOrWrapper{p8est_t}, recursive, refine_fn_c,
+ init_fn_c)
p8est_refine(p8est, recursive, refine_fn_c, init_fn_c)
end
# Refine `p4est` if coarsen_fn_c returns 1
# 2D
-function coarsen_p4est!(p4est::Ptr{p4est_t}, recursive, coarsen_fn_c, init_fn_c)
+function coarsen_p4est!(p4est::PointerOrWrapper{p4est_t}, recursive, coarsen_fn_c,
+ init_fn_c)
p4est_coarsen(p4est, recursive, coarsen_fn_c, init_fn_c)
end
# 3D
-function coarsen_p4est!(p8est::Ptr{p8est_t}, recursive, coarsen_fn_c, init_fn_c)
+function coarsen_p4est!(p8est::PointerOrWrapper{p8est_t}, recursive, coarsen_fn_c,
+ init_fn_c)
p8est_coarsen(p8est, recursive, coarsen_fn_c, init_fn_c)
end
# Create new ghost layer from p4est, only connections via faces are relevant
# 2D
-ghost_new_p4est(p4est::Ptr{p4est_t}) = p4est_ghost_new(p4est, P4est.P4EST_CONNECT_FACE)
+function ghost_new_p4est(p4est::PointerOrWrapper{p4est_t})
+ p4est_ghost_new(p4est, P4est.P4EST_CONNECT_FACE)
+end
# 3D
# In 3D it is not sufficient to use `P8EST_CONNECT_FACE`. Consider the neighbor elements of a mortar
# in 3D. We have to determine which MPI ranks are involved in this mortar.
@@ -147,28 +157,37 @@ ghost_new_p4est(p4est::Ptr{p4est_t}) = p4est_ghost_new(p4est, P4est.P4EST_CONNEC
# `P8EST_CONNECT_FACE`. But if it is not in the ghost layer, it will not be available in
# `iterate_p4est` and thus we cannot determine its MPI rank
# (see https://github.com/cburstedde/p4est/blob/439bc9aae849555256ddfe4b03d1f9fe8d18ff0e/src/p8est_iterate.h#L66-L72).
-ghost_new_p4est(p8est::Ptr{p8est_t}) = p8est_ghost_new(p8est, P4est.P8EST_CONNECT_FULL)
+function ghost_new_p4est(p8est::PointerOrWrapper{p8est_t})
+ p8est_ghost_new(p8est, P4est.P8EST_CONNECT_FULL)
+end
# Check if ghost layer is valid
# 2D
-function ghost_is_valid_p4est(p4est::Ptr{p4est_t}, ghost_layer::Ptr{p4est_ghost_t})
+function ghost_is_valid_p4est(p4est::PointerOrWrapper{p4est_t},
+ ghost_layer::Ptr{p4est_ghost_t})
return p4est_ghost_is_valid(p4est, ghost_layer)
end
# 3D
-function ghost_is_valid_p4est(p4est::Ptr{p8est_t}, ghost_layer::Ptr{p8est_ghost_t})
+function ghost_is_valid_p4est(p4est::PointerOrWrapper{p8est_t},
+ ghost_layer::Ptr{p8est_ghost_t})
return p8est_ghost_is_valid(p4est, ghost_layer)
end
# Destroy ghost layer
# 2D
-ghost_destroy_p4est(ghost_layer::Ptr{p4est_ghost_t}) = p4est_ghost_destroy(ghost_layer)
+function ghost_destroy_p4est(ghost_layer::PointerOrWrapper{p4est_ghost_t})
+ p4est_ghost_destroy(ghost_layer)
+end
# 3D
-ghost_destroy_p4est(ghost_layer::Ptr{p8est_ghost_t}) = p8est_ghost_destroy(ghost_layer)
+function ghost_destroy_p4est(ghost_layer::PointerOrWrapper{p8est_ghost_t})
+ p8est_ghost_destroy(ghost_layer)
+end
# Let `p4est` iterate over each cell volume and cell face.
# Call iter_volume_c for each cell and iter_face_c for each face.
# 2D
-function iterate_p4est(p4est::Ptr{p4est_t}, user_data; ghost_layer = C_NULL,
+function iterate_p4est(p4est::PointerOrWrapper{p4est_t}, user_data;
+ ghost_layer = C_NULL,
iter_volume_c = C_NULL, iter_face_c = C_NULL)
if user_data === C_NULL
user_data_ptr = user_data
@@ -191,7 +210,8 @@ function iterate_p4est(p4est::Ptr{p4est_t}, user_data; ghost_layer = C_NULL,
end
# 3D
-function iterate_p4est(p8est::Ptr{p8est_t}, user_data; ghost_layer = C_NULL,
+function iterate_p4est(p8est::PointerOrWrapper{p8est_t}, user_data;
+ ghost_layer = C_NULL,
iter_volume_c = C_NULL, iter_face_c = C_NULL)
if user_data === C_NULL
user_data_ptr = user_data
@@ -216,23 +236,25 @@ end
# Load i-th element of the sc_array info.sides of the type p[48]est_iter_face_side_t
# 2D version
-function unsafe_load_side(info::Ptr{p4est_iter_face_info_t}, i = 1)
- return unsafe_load_sc(p4est_iter_face_side_t, unsafe_load(info).sides, i)
+function load_pointerwrapper_side(info::PointerWrapper{p4est_iter_face_info_t},
+ i::Integer = 1)
+ return load_pointerwrapper_sc(p4est_iter_face_side_t, info.sides, i)
end
# 3D version
-function unsafe_load_side(info::Ptr{p8est_iter_face_info_t}, i = 1)
- return unsafe_load_sc(p8est_iter_face_side_t, unsafe_load(info).sides, i)
+function load_pointerwrapper_side(info::PointerWrapper{p8est_iter_face_info_t},
+ i::Integer = 1)
+ return load_pointerwrapper_sc(p8est_iter_face_side_t, info.sides, i)
end
# Load i-th element of the sc_array p4est.trees of the type p[48]est_tree_t
# 2D version
-function unsafe_load_tree(p4est::Ptr{p4est_t}, i = 1)
- return unsafe_load_sc(p4est_tree_t, unsafe_load(p4est).trees, i)
+function load_pointerwrapper_tree(p4est::PointerWrapper{p4est_t}, i::Integer = 1)
+ return load_pointerwrapper_sc(p4est_tree_t, p4est.trees, i)
end
# 3D version
-function unsafe_load_tree(p8est::Ptr{p8est_t}, i = 1)
- return unsafe_load_sc(p8est_tree_t, unsafe_load(p8est).trees, i)
+function load_pointerwrapper_tree(p8est::PointerWrapper{p8est_t}, i::Integer = 1)
+ return load_pointerwrapper_sc(p8est_tree_t, p8est.trees, i)
end
end # @muladd
diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl
index d6e19b79886..bef49b4c482 100644
--- a/src/callbacks_step/amr.jl
+++ b/src/callbacks_step/amr.jl
@@ -348,24 +348,24 @@ end
# Copy controller values to quad user data storage, will be called below
function copy_to_quad_iter_volume(info, user_data)
- info_obj = unsafe_load(info)
+ info_pw = PointerWrapper(info)
# Load tree from global trees array, one-based indexing
- tree = unsafe_load_tree(info_obj.p4est, info_obj.treeid + 1)
+ tree_pw = load_pointerwrapper_tree(info_pw.p4est, info_pw.treeid[] + 1)
# Quadrant numbering offset of this quadrant
- offset = tree.quadrants_offset
+ offset = tree_pw.quadrants_offset[]
# Global quad ID
- quad_id = offset + info_obj.quadid
+ quad_id = offset + info_pw.quadid[]
# Access user_data = lambda
- user_data_ptr = Ptr{Int}(user_data)
+ user_data_pw = PointerWrapper(Int, user_data)
# Load controller_value = lambda[quad_id + 1]
- controller_value = unsafe_load(user_data_ptr, quad_id + 1)
+ controller_value = user_data_pw[quad_id + 1]
# Access quadrant's user data ([global quad ID, controller_value])
- quad_data_ptr = Ptr{Int}(unsafe_load(info_obj.quad.p.user_data))
+ quad_data_pw = PointerWrapper(Int, info_pw.quad.p.user_data[])
# Save controller value to quadrant's user data.
- unsafe_store!(quad_data_ptr, controller_value, 2)
+ quad_data_pw[2] = controller_value
return nothing
end
@@ -599,22 +599,22 @@ function current_element_levels(mesh::TreeMesh, solver, cache)
end
function extract_levels_iter_volume(info, user_data)
- info_obj = unsafe_load(info)
+ info_pw = PointerWrapper(info)
# Load tree from global trees array, one-based indexing
- tree = unsafe_load_tree(info_obj.p4est, info_obj.treeid + 1)
+ tree_pw = load_pointerwrapper_tree(info_pw.p4est, info_pw.treeid[] + 1)
# Quadrant numbering offset of this quadrant
- offset = tree.quadrants_offset
+ offset = tree_pw.quadrants_offset[]
# Global quad ID
- quad_id = offset + info_obj.quadid
+ quad_id = offset + info_pw.quadid[]
# Julia element ID
element_id = quad_id + 1
- current_level = unsafe_load(info_obj.quad.level)
+ current_level = info_pw.quad.level[]
# Unpack user_data = current_levels and save current element level
- ptr = Ptr{Int}(user_data)
- unsafe_store!(ptr, current_level, element_id)
+ pw = PointerWrapper(Int, user_data)
+ pw[element_id] = current_level
return nothing
end
diff --git a/src/callbacks_step/amr_dg.jl b/src/callbacks_step/amr_dg.jl
index 19bbebd9254..1dcfdccdea8 100644
--- a/src/callbacks_step/amr_dg.jl
+++ b/src/callbacks_step/amr_dg.jl
@@ -9,8 +9,7 @@
function rebalance_solver!(u_ode::AbstractVector, mesh::ParallelP4estMesh, equations,
dg::DGSEM, cache, old_global_first_quadrant)
# mpi ranks are 0-based, this array uses 1-based indices
- global_first_quadrant = unsafe_wrap(Array,
- unsafe_load(mesh.p4est).global_first_quadrant,
+ global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant,
mpi_nranks() + 1)
if global_first_quadrant[mpi_rank() + 1] ==
old_global_first_quadrant[mpi_rank() + 1] &&
diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl
index 7fa2e21a244..8cf43a1d15e 100644
--- a/src/callbacks_step/analysis.jl
+++ b/src/callbacks_step/analysis.jl
@@ -508,7 +508,7 @@ function print_amr_information(callbacks, mesh::P4estMesh, solver, cache)
elements_per_level = zeros(P4EST_MAXLEVEL + 1)
- for tree in unsafe_wrap_sc(p4est_tree_t, unsafe_load(mesh.p4est).trees)
+ for tree in unsafe_wrap_sc(p4est_tree_t, mesh.p4est.trees)
elements_per_level .+= tree.quadrants_per_level
end
diff --git a/src/meshes/p4est_mesh.jl b/src/meshes/p4est_mesh.jl
index ddd6cf473e4..60db285e04f 100644
--- a/src/meshes/p4est_mesh.jl
+++ b/src/meshes/p4est_mesh.jl
@@ -13,9 +13,9 @@ to manage trees and mesh refinement.
"""
mutable struct P4estMesh{NDIMS, RealT <: Real, IsParallel, P, Ghost, NDIMSP2, NNODES} <:
AbstractMesh{NDIMS}
- p4est::P # Either Ptr{p4est_t} or Ptr{p8est_t}
- is_parallel::IsParallel
- ghost::Ghost # Either Ptr{p4est_ghost_t} or Ptr{p8est_ghost_t}
+ p4est :: P # Either PointerWrapper{p4est_t} or PointerWrapper{p8est_t}
+ is_parallel :: IsParallel
+ ghost :: Ghost # Either PointerWrapper{p4est_ghost_t} or PointerWrapper{p8est_ghost_t}
# Coordinates at the nodes specified by the tensor product of `nodes` (NDIMS times).
# This specifies the geometry interpolation for each tree.
tree_node_coordinates::Array{RealT, NDIMSP2} # [dimension, i, j, k, tree]
@@ -43,18 +43,21 @@ mutable struct P4estMesh{NDIMS, RealT <: Real, IsParallel, P, Ghost, NDIMSP2, NN
is_parallel = False()
end
+ p4est_pw = PointerWrapper(p4est)
+
ghost = ghost_new_p4est(p4est)
+ ghost_pw = PointerWrapper(ghost)
mesh = new{NDIMS, eltype(tree_node_coordinates), typeof(is_parallel),
- typeof(p4est), typeof(ghost), NDIMS + 2, length(nodes)}(p4est,
- is_parallel,
- ghost,
- tree_node_coordinates,
- nodes,
- boundary_names,
- current_filename,
- unsaved_changes,
- p4est_partition_allow_for_coarsening)
+ typeof(p4est_pw), typeof(ghost_pw), NDIMS + 2, length(nodes)}(p4est_pw,
+ is_parallel,
+ ghost_pw,
+ tree_node_coordinates,
+ nodes,
+ boundary_names,
+ current_filename,
+ unsaved_changes,
+ p4est_partition_allow_for_coarsening)
# Destroy `p4est` structs when the mesh is garbage collected
finalizer(destroy_mesh, mesh)
@@ -70,14 +73,14 @@ const ParallelP4estMesh{NDIMS} = P4estMesh{NDIMS, <:Real, <:True}
@inline mpi_parallel(mesh::ParallelP4estMesh) = True()
function destroy_mesh(mesh::P4estMesh{2})
- connectivity = unsafe_load(mesh.p4est).connectivity
+ connectivity = mesh.p4est.connectivity
p4est_ghost_destroy(mesh.ghost)
p4est_destroy(mesh.p4est)
p4est_connectivity_destroy(connectivity)
end
function destroy_mesh(mesh::P4estMesh{3})
- connectivity = unsafe_load(mesh.p4est).connectivity
+ connectivity = mesh.p4est.connectivity
p8est_ghost_destroy(mesh.ghost)
p8est_destroy(mesh.p4est)
p8est_connectivity_destroy(connectivity)
@@ -87,11 +90,10 @@ end
@inline Base.real(::P4estMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT
@inline function ntrees(mesh::P4estMesh)
- trees = unsafe_load(mesh.p4est).trees
- return unsafe_load(trees).elem_count
+ return mesh.p4est.trees.elem_count[]
end
# returns Int32 by default which causes a weird method error when creating the cache
-@inline ncells(mesh::P4estMesh) = Int(unsafe_load(mesh.p4est).local_num_quadrants)
+@inline ncells(mesh::P4estMesh) = Int(mesh.p4est.local_num_quadrants[])
function Base.show(io::IO, mesh::P4estMesh)
print(io, "P4estMesh{", ndims(mesh), ", ", real(mesh), "}")
@@ -387,14 +389,14 @@ function p4est_mesh_from_hohqmesh_abaqus(meshfile, initial_refinement_level,
n_dimensions, RealT)
# Create the mesh connectivity using `p4est`
connectivity = read_inp_p4est(meshfile, Val(n_dimensions))
- connectivity_obj = unsafe_load(connectivity)
+ connectivity_pw = PointerWrapper(connectivity)
# These need to be of the type Int for unsafe_wrap below to work
- n_trees::Int = connectivity_obj.num_trees
- n_vertices::Int = connectivity_obj.num_vertices
+ n_trees::Int = connectivity_pw.num_trees[]
+ n_vertices::Int = connectivity_pw.num_vertices[]
# Extract a copy of the element vertices to compute the tree node coordinates
- vertices = unsafe_wrap(Array, connectivity_obj.vertices, (3, n_vertices))
+ vertices = unsafe_wrap(Array, connectivity_pw.vertices, (3, n_vertices))
# Readin all the information from the mesh file into a string array
file_lines = readlines(open(meshfile))
@@ -445,14 +447,14 @@ function p4est_mesh_from_standard_abaqus(meshfile, mapping, polydeg,
initial_refinement_level, n_dimensions, RealT)
# Create the mesh connectivity using `p4est`
connectivity = read_inp_p4est(meshfile, Val(n_dimensions))
- connectivity_obj = unsafe_load(connectivity)
+ connectivity_pw = PointerWrapper(connectivity)
# These need to be of the type Int for unsafe_wrap below to work
- n_trees::Int = connectivity_obj.num_trees
- n_vertices::Int = connectivity_obj.num_vertices
+ n_trees::Int = connectivity_pw.num_trees[]
+ n_vertices::Int = connectivity_pw.num_vertices[]
- vertices = unsafe_wrap(Array, connectivity_obj.vertices, (3, n_vertices))
- tree_to_vertex = unsafe_wrap(Array, connectivity_obj.tree_to_vertex,
+ vertices = unsafe_wrap(Array, connectivity_pw.vertices, (3, n_vertices))
+ tree_to_vertex = unsafe_wrap(Array, connectivity_pw.tree_to_vertex,
(2^n_dimensions, n_trees))
basis = LobattoLegendreBasis(RealT, polydeg)
@@ -1511,17 +1513,18 @@ end
function update_ghost_layer!(mesh::P4estMesh)
ghost_destroy_p4est(mesh.ghost)
- mesh.ghost = ghost_new_p4est(mesh.p4est)
+ mesh.ghost = PointerWrapper(ghost_new_p4est(mesh.p4est))
end
function init_fn(p4est, which_tree, quadrant)
# Unpack quadrant's user data ([global quad ID, controller_value])
- ptr = Ptr{Int}(unsafe_load(quadrant.p.user_data))
+ # Use `unsafe_load` here since `quadrant.p.user_data isa Ptr{Ptr{Nothing}}`
+ # and we only need the first (only!) entry
+ pw = PointerWrapper(Int, unsafe_load(quadrant.p.user_data))
# Initialize quad ID as -1 and controller_value as 0 (don't refine or coarsen)
- unsafe_store!(ptr, -1, 1)
- unsafe_store!(ptr, 0, 2)
-
+ pw[1] = -1
+ pw[2] = 0
return nothing
end
@@ -1539,8 +1542,10 @@ end
function refine_fn(p4est, which_tree, quadrant)
# Controller value has been copied to the quadrant's user data storage before.
# Unpack quadrant's user data ([global quad ID, controller_value]).
- ptr = Ptr{Int}(unsafe_load(quadrant.p.user_data))
- controller_value = unsafe_load(ptr, 2)
+ # Use `unsafe_load` here since `quadrant.p.user_data isa Ptr{Ptr{Nothing}}`
+ # and we only need the first (only!) entry
+ pw = PointerWrapper(Int, unsafe_load(quadrant.p.user_data))
+ controller_value = pw[2]
if controller_value > 0
# return true (refine)
@@ -1586,9 +1591,9 @@ function coarsen_fn(p4est, which_tree, quadrants_ptr)
# Controller value has been copied to the quadrant's user data storage before.
# Load controller value from quadrant's user data ([global quad ID, controller_value]).
- function controller_value(i)
- unsafe_load(Ptr{Int}(unsafe_load(quadrants[i].p.user_data)), 2)
- end
+ # Use `unsafe_load` here since `quadrant.p.user_data isa Ptr{Ptr{Nothing}}`
+ # and we only need the first (only!) entry
+ controller_value(i) = PointerWrapper(Int, unsafe_load(quadrants[i].p.user_data))[2]
# `p4est` calls this function for each 2^ndims quads that could be coarsened to a single one.
# Only coarsen if all these 2^ndims quads have been marked for coarsening.
@@ -1671,20 +1676,19 @@ end
# Copy global quad ID to quad's user data storage, will be called below
function save_original_id_iter_volume(info, user_data)
- info_obj = unsafe_load(info)
+ info_pw = PointerWrapper(info)
# Load tree from global trees array, one-based indexing
- tree = unsafe_load_tree(info_obj.p4est, info_obj.treeid + 1)
+ tree_pw = load_pointerwrapper_tree(info_pw.p4est, info_pw.treeid[] + 1)
# Quadrant numbering offset of this quadrant
- offset = tree.quadrants_offset
+ offset = tree_pw.quadrants_offset[]
# Global quad ID
- quad_id = offset + info_obj.quadid
+ quad_id = offset + info_pw.quadid[]
# Unpack quadrant's user data ([global quad ID, controller_value])
- ptr = Ptr{Int}(unsafe_load(info_obj.quad.p.user_data))
+ pw = PointerWrapper(Int, info_pw.quad.p.user_data[])
# Save global quad ID
- unsafe_store!(ptr, quad_id, 1)
-
+ pw[1] = quad_id
return nothing
end
@@ -1708,24 +1712,23 @@ end
# Extract information about which cells have been changed
function collect_changed_iter_volume(info, user_data)
- info_obj = unsafe_load(info)
+ info_pw = PointerWrapper(info)
# The original element ID has been saved to user_data before.
# Load original quad ID from quad's user data ([global quad ID, controller_value]).
- quad_data_ptr = Ptr{Int}(unsafe_load(info_obj.quad.p.user_data))
- original_id = unsafe_load(quad_data_ptr, 1)
+ quad_data_pw = PointerWrapper(Int, info_pw.quad.p.user_data[])
+ original_id = quad_data_pw[1]
# original_id of cells that have been newly created is -1
if original_id >= 0
# Unpack user_data = original_cells
- user_data_ptr = Ptr{Int}(user_data)
+ user_data_pw = PointerWrapper(Int, user_data)
# If quad has an original_id, it existed before refinement/coarsening,
# and therefore wasn't changed.
# Mark original_id as "not changed during refinement/coarsening" in original_cells
- unsafe_store!(user_data_ptr, 0, original_id + 1)
+ user_data_pw[original_id + 1] = 0
end
-
return nothing
end
@@ -1756,29 +1759,27 @@ end
# Extract newly created cells
function collect_new_iter_volume(info, user_data)
- info_obj = unsafe_load(info)
+ info_pw = PointerWrapper(info)
# The original element ID has been saved to user_data before.
# Unpack quadrant's user data ([global quad ID, controller_value]).
- quad_data_ptr = Ptr{Int}(unsafe_load(info_obj.quad.p.user_data))
- original_id = unsafe_load(quad_data_ptr, 1)
+ original_id = PointerWrapper(Int, info_pw.quad.p.user_data[])[1]
# original_id of cells that have been newly created is -1
if original_id < 0
# Load tree from global trees array, one-based indexing
- tree = unsafe_load_tree(info_obj.p4est, info_obj.treeid + 1)
+ tree_pw = load_pointerwrapper_tree(info_pw.p4est, info_pw.treeid[] + 1)
# Quadrant numbering offset of this quadrant
- offset = tree.quadrants_offset
+ offset = tree_pw.quadrants_offset[]
# Global quad ID
- quad_id = offset + info_obj.quadid
+ quad_id = offset + info_pw.quadid[]
# Unpack user_data = original_cells
- user_data_ptr = Ptr{Int}(user_data)
+ user_data_pw = PointerWrapper(Int, user_data)
# Mark cell as "newly created during refinement/coarsening/balancing"
- unsafe_store!(user_data_ptr, 1, quad_id + 1)
+ user_data_pw[quad_id + 1] = 1
end
-
return nothing
end
diff --git a/src/solvers/dgsem_p4est/containers.jl b/src/solvers/dgsem_p4est/containers.jl
index 9b87de777a6..2b9c6987d24 100644
--- a/src/solvers/dgsem_p4est/containers.jl
+++ b/src/solvers/dgsem_p4est/containers.jl
@@ -276,18 +276,18 @@ function init_boundaries!(boundaries, mesh::P4estMesh)
end
# Function barrier for type stability
-function init_boundaries_iter_face_inner(info, boundaries, boundary_id, mesh)
+function init_boundaries_iter_face_inner(info_pw, boundaries, boundary_id, mesh)
# Extract boundary data
- side = unsafe_load_side(info)
+ side_pw = load_pointerwrapper_side(info_pw)
# Get local tree, one-based indexing
- tree = unsafe_load_tree(mesh.p4est, side.treeid + 1)
+ tree_pw = load_pointerwrapper_tree(mesh.p4est, side_pw.treeid[] + 1)
# Quadrant numbering offset of this quadrant
- offset = tree.quadrants_offset
+ offset = tree_pw.quadrants_offset[]
# Verify before accessing is.full, but this should never happen
- @assert side.is_hanging == false
+ @assert side_pw.is_hanging[] == false
- local_quad_id = side.is.full.quadid
+ local_quad_id = side_pw.is.full.quadid[]
# Global ID of this quad
quad_id = offset + local_quad_id
@@ -296,13 +296,13 @@ function init_boundaries_iter_face_inner(info, boundaries, boundary_id, mesh)
boundaries.neighbor_ids[boundary_id] = quad_id + 1
# Face at which the boundary lies
- face = side.face
+ face = side_pw.face[]
# Save boundaries.node_indices dimension specific in containers_[23]d.jl
init_boundary_node_indices!(boundaries, face, boundary_id)
# One-based indexing
- boundaries.name[boundary_id] = mesh.boundary_names[face + 1, side.treeid + 1]
+ boundaries.name[boundary_id] = mesh.boundary_names[face + 1, side_pw.treeid[] + 1]
return nothing
end
@@ -479,32 +479,33 @@ end
# Function barrier for type stability
function init_surfaces_iter_face_inner(info, user_data)
@unpack interfaces, mortars, boundaries = user_data
- elem_count = unsafe_load(info).sides.elem_count
+ info_pw = PointerWrapper(info)
+ elem_count = info_pw.sides.elem_count[]
if elem_count == 2
# Two neighboring elements => Interface or mortar
# Extract surface data
- sides = (unsafe_load_side(info, 1), unsafe_load_side(info, 2))
+ sides_pw = (load_pointerwrapper_side(info_pw, 1),
+ load_pointerwrapper_side(info_pw, 2))
- if sides[1].is_hanging == false && sides[2].is_hanging == false
+ if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false
# No hanging nodes => normal interface
if interfaces !== nothing
- init_interfaces_iter_face_inner(info, sides, user_data)
+ init_interfaces_iter_face_inner(info_pw, sides_pw, user_data)
end
else
# Hanging nodes => mortar
if mortars !== nothing
- init_mortars_iter_face_inner(info, sides, user_data)
+ init_mortars_iter_face_inner(info_pw, sides_pw, user_data)
end
end
elseif elem_count == 1
# One neighboring elements => boundary
if boundaries !== nothing
- init_boundaries_iter_face_inner(info, user_data)
+ init_boundaries_iter_face_inner(info_pw, user_data)
end
end
-
return nothing
end
@@ -519,18 +520,18 @@ function init_surfaces!(interfaces, mortars, boundaries, mesh::P4estMesh)
end
# Initialization of interfaces after the function barrier
-function init_interfaces_iter_face_inner(info, sides, user_data)
+function init_interfaces_iter_face_inner(info_pw, sides_pw, user_data)
@unpack interfaces, interface_id, mesh = user_data
user_data.interface_id += 1
# Get Tuple of local trees, one-based indexing
- trees = (unsafe_load_tree(mesh.p4est, sides[1].treeid + 1),
- unsafe_load_tree(mesh.p4est, sides[2].treeid + 1))
+ trees_pw = (load_pointerwrapper_tree(mesh.p4est, sides_pw[1].treeid[] + 1),
+ load_pointerwrapper_tree(mesh.p4est, sides_pw[2].treeid[] + 1))
# Quadrant numbering offsets of the quadrants at this interface
- offsets = SVector(trees[1].quadrants_offset,
- trees[2].quadrants_offset)
+ offsets = SVector(trees_pw[1].quadrants_offset[],
+ trees_pw[2].quadrants_offset[])
- local_quad_ids = SVector(sides[1].is.full.quadid, sides[2].is.full.quadid)
+ local_quad_ids = SVector(sides_pw[1].is.full.quadid[], sides_pw[2].is.full.quadid[])
# Global IDs of the neighboring quads
quad_ids = offsets + local_quad_ids
@@ -540,31 +541,30 @@ function init_interfaces_iter_face_inner(info, sides, user_data)
interfaces.neighbor_ids[2, interface_id] = quad_ids[2] + 1
# Face at which the interface lies
- faces = (sides[1].face, sides[2].face)
+ faces = (sides_pw[1].face[], sides_pw[2].face[])
# Save interfaces.node_indices dimension specific in containers_[23]d.jl
- init_interface_node_indices!(interfaces, faces,
- unsafe_load(info).orientation, interface_id)
+ init_interface_node_indices!(interfaces, faces, info_pw.orientation[], interface_id)
return nothing
end
# Initialization of boundaries after the function barrier
-function init_boundaries_iter_face_inner(info, user_data)
+function init_boundaries_iter_face_inner(info_pw, user_data)
@unpack boundaries, boundary_id, mesh = user_data
user_data.boundary_id += 1
# Extract boundary data
- side = unsafe_load_side(info)
+ side_pw = load_pointerwrapper_side(info_pw)
# Get local tree, one-based indexing
- tree = unsafe_load_tree(mesh.p4est, side.treeid + 1)
+ tree_pw = load_pointerwrapper_tree(mesh.p4est, side_pw.treeid[] + 1)
# Quadrant numbering offset of this quadrant
- offset = tree.quadrants_offset
+ offset = tree_pw.quadrants_offset[]
# Verify before accessing is.full, but this should never happen
- @assert side.is_hanging == false
+ @assert side_pw.is_hanging[] == false
- local_quad_id = side.is.full.quadid
+ local_quad_id = side_pw.is.full.quadid[]
# Global ID of this quad
quad_id = offset + local_quad_id
@@ -573,52 +573,52 @@ function init_boundaries_iter_face_inner(info, user_data)
boundaries.neighbor_ids[boundary_id] = quad_id + 1
# Face at which the boundary lies
- face = side.face
+ face = side_pw.face[]
# Save boundaries.node_indices dimension specific in containers_[23]d.jl
init_boundary_node_indices!(boundaries, face, boundary_id)
# One-based indexing
- boundaries.name[boundary_id] = mesh.boundary_names[face + 1, side.treeid + 1]
+ boundaries.name[boundary_id] = mesh.boundary_names[face + 1, side_pw.treeid[] + 1]
return nothing
end
# Initialization of mortars after the function barrier
-function init_mortars_iter_face_inner(info, sides, user_data)
+function init_mortars_iter_face_inner(info_pw, sides_pw, user_data)
@unpack mortars, mortar_id, mesh = user_data
user_data.mortar_id += 1
# Get Tuple of local trees, one-based indexing
- trees = (unsafe_load_tree(mesh.p4est, sides[1].treeid + 1),
- unsafe_load_tree(mesh.p4est, sides[2].treeid + 1))
+ trees_pw = (load_pointerwrapper_tree(mesh.p4est, sides_pw[1].treeid[] + 1),
+ load_pointerwrapper_tree(mesh.p4est, sides_pw[2].treeid[] + 1))
# Quadrant numbering offsets of the quadrants at this interface
- offsets = SVector(trees[1].quadrants_offset,
- trees[2].quadrants_offset)
+ offsets = SVector(trees_pw[1].quadrants_offset[],
+ trees_pw[2].quadrants_offset[])
- if sides[1].is_hanging == true
+ if sides_pw[1].is_hanging[] == true
# Left is small, right is large
- faces = (sides[1].face, sides[2].face)
+ faces = (sides_pw[1].face[], sides_pw[2].face[])
- local_small_quad_ids = sides[1].is.hanging.quadid
+ local_small_quad_ids = sides_pw[1].is.hanging.quadid[]
# Global IDs of the two small quads
small_quad_ids = offsets[1] .+ local_small_quad_ids
# Just be sure before accessing is.full
- @assert sides[2].is_hanging == false
- large_quad_id = offsets[2] + sides[2].is.full.quadid
- else # sides[2].is_hanging == true
+ @assert sides_pw[2].is_hanging[] == false
+ large_quad_id = offsets[2] + sides_pw[2].is.full.quadid[]
+ else # sides_pw[2].is_hanging[] == true
# Right is small, left is large.
# init_mortar_node_indices! below expects side 1 to contain the small elements.
- faces = (sides[2].face, sides[1].face)
+ faces = (sides_pw[2].face[], sides_pw[1].face[])
- local_small_quad_ids = sides[2].is.hanging.quadid
+ local_small_quad_ids = sides_pw[2].is.hanging.quadid[]
# Global IDs of the two small quads
small_quad_ids = offsets[2] .+ local_small_quad_ids
# Just be sure before accessing is.full
- @assert sides[1].is_hanging == false
- large_quad_id = offsets[1] + sides[1].is.full.quadid
+ @assert sides_pw[1].is_hanging[] == false
+ large_quad_id = offsets[1] + sides_pw[1].is.full.quadid[]
end
# Write data to mortar container, 1 and 2 are the small elements
@@ -627,7 +627,7 @@ function init_mortars_iter_face_inner(info, sides, user_data)
# Last entry is the large element
mortars.neighbor_ids[end, mortar_id] = large_quad_id + 1
- init_mortar_node_indices!(mortars, faces, unsafe_load(info).orientation, mortar_id)
+ init_mortar_node_indices!(mortars, faces, info_pw.orientation[], mortar_id)
return nothing
end
@@ -638,34 +638,36 @@ end
# - boundaries
# and collect the numbers in `user_data` in this order.
function count_surfaces_iter_face(info, user_data)
- elem_count = unsafe_load(info).sides.elem_count
+ info_pw = PointerWrapper(info)
+ elem_count = info_pw.sides.elem_count[]
if elem_count == 2
# Two neighboring elements => Interface or mortar
# Extract surface data
- sides = (unsafe_load_side(info, 1), unsafe_load_side(info, 2))
+ sides_pw = (load_pointerwrapper_side(info_pw, 1),
+ load_pointerwrapper_side(info_pw, 2))
- if sides[1].is_hanging == false && sides[2].is_hanging == false
+ if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false
# No hanging nodes => normal interface
# Unpack user_data = [interface_count] and increment interface_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 1)
- unsafe_store!(ptr, id + 1, 1)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[1]
+ pw[1] = id + 1
else
# Hanging nodes => mortar
# Unpack user_data = [mortar_count] and increment mortar_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 2)
- unsafe_store!(ptr, id + 1, 2)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[2]
+ pw[2] = id + 1
end
elseif elem_count == 1
# One neighboring elements => boundary
# Unpack user_data = [boundary_count] and increment boundary_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 3)
- unsafe_store!(ptr, id + 1, 3)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[3]
+ pw[3] = id + 1
end
return nothing
diff --git a/src/solvers/dgsem_p4est/containers_2d.jl b/src/solvers/dgsem_p4est/containers_2d.jl
index 4f7d903897a..11747f1f175 100644
--- a/src/solvers/dgsem_p4est/containers_2d.jl
+++ b/src/solvers/dgsem_p4est/containers_2d.jl
@@ -52,7 +52,7 @@ function calc_node_coordinates!(node_coordinates,
p4est_root_len = 1 << P4EST_MAXLEVEL
p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)
- trees = unsafe_wrap_sc(p4est_tree_t, unsafe_load(mesh.p4est).trees)
+ trees = unsafe_wrap_sc(p4est_tree_t, mesh.p4est.trees)
for tree in eachindex(trees)
offset = trees[tree].quadrants_offset
diff --git a/src/solvers/dgsem_p4est/containers_3d.jl b/src/solvers/dgsem_p4est/containers_3d.jl
index 6cdc2cf9611..e9994fe4569 100644
--- a/src/solvers/dgsem_p4est/containers_3d.jl
+++ b/src/solvers/dgsem_p4est/containers_3d.jl
@@ -43,7 +43,7 @@ function calc_node_coordinates!(node_coordinates,
p4est_root_len = 1 << P4EST_MAXLEVEL
p4est_quadrant_len(l) = 1 << (P4EST_MAXLEVEL - l)
- trees = unsafe_wrap_sc(p8est_tree_t, unsafe_load(mesh.p4est).trees)
+ trees = unsafe_wrap_sc(p8est_tree_t, mesh.p4est.trees)
for tree in eachindex(trees)
offset = trees[tree].quadrants_offset
diff --git a/src/solvers/dgsem_p4est/containers_parallel.jl b/src/solvers/dgsem_p4est/containers_parallel.jl
index 42d6ea44c5e..e7ee1f81478 100644
--- a/src/solvers/dgsem_p4est/containers_parallel.jl
+++ b/src/solvers/dgsem_p4est/containers_parallel.jl
@@ -311,21 +311,24 @@ function init_surfaces_iter_face_inner(info,
# surfaces at once or any subset of them, some of the unpacked values above may be `nothing` if
# they're not supposed to be initialized during this call. That is why we need additional
# `!== nothing` checks below before initializing individual faces.
- if unsafe_load(info).sides.elem_count == 2
+ info_pw = PointerWrapper(info)
+ if info_pw.sides.elem_count[] == 2
# Two neighboring elements => Interface or mortar
# Extract surface data
- sides = (unsafe_load_side(info, 1), unsafe_load_side(info, 2))
+ sides_pw = (load_pointerwrapper_side(info_pw, 1),
+ load_pointerwrapper_side(info_pw, 2))
- if sides[1].is_hanging == false && sides[2].is_hanging == false
+ if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false
# No hanging nodes => normal interface or MPI interface
- if sides[1].is.full.is_ghost == true || sides[2].is.full.is_ghost == true # remote side => MPI interface
+ if sides_pw[1].is.full.is_ghost[] == true ||
+ sides_pw[2].is.full.is_ghost[] == true # remote side => MPI interface
if mpi_interfaces !== nothing
- init_mpi_interfaces_iter_face_inner(info, sides, user_data)
+ init_mpi_interfaces_iter_face_inner(info_pw, sides_pw, user_data)
end
else
if interfaces !== nothing
- init_interfaces_iter_face_inner(info, sides, user_data)
+ init_interfaces_iter_face_inner(info_pw, sides_pw, user_data)
end
end
else
@@ -333,18 +336,18 @@ function init_surfaces_iter_face_inner(info,
# First, we check which side is hanging, i.e., on which side we have the refined cells.
# Then we check if any of the refined cells or the coarse cell are "ghost" cells, i.e., they
# belong to another rank. That way we can determine if this is a regular mortar or MPI mortar
- if sides[1].is_hanging == true
- @assert sides[2].is_hanging == false
- if any(sides[1].is.hanging.is_ghost .== true) ||
- sides[2].is.full.is_ghost == true
+ if sides_pw[1].is_hanging[] == true
+ @assert sides_pw[2].is_hanging[] == false
+ if any(sides_pw[1].is.hanging.is_ghost[] .== true) ||
+ sides_pw[2].is.full.is_ghost[] == true
face_has_ghost_side = true
else
face_has_ghost_side = false
end
- else # sides[2].is_hanging == true
- @assert sides[1].is_hanging == false
- if sides[1].is.full.is_ghost == true ||
- any(sides[2].is.hanging.is_ghost .== true)
+ else # sides_pw[2].is_hanging[] == true
+ @assert sides_pw[1].is_hanging[] == false
+ if sides_pw[1].is.full.is_ghost[] == true ||
+ any(sides_pw[2].is.hanging.is_ghost[] .== true)
face_has_ghost_side = true
else
face_has_ghost_side = false
@@ -352,15 +355,15 @@ function init_surfaces_iter_face_inner(info,
end
# Initialize mortar or MPI mortar
if face_has_ghost_side && mpi_mortars !== nothing
- init_mpi_mortars_iter_face_inner(info, sides, user_data)
+ init_mpi_mortars_iter_face_inner(info_pw, sides_pw, user_data)
elseif !face_has_ghost_side && mortars !== nothing
- init_mortars_iter_face_inner(info, sides, user_data)
+ init_mortars_iter_face_inner(info_pw, sides_pw, user_data)
end
end
- elseif unsafe_load(info).sides.elem_count == 1
+ elseif info_pw.sides.elem_count[] == 1
# One neighboring elements => boundary
if boundaries !== nothing
- init_boundaries_iter_face_inner(info, user_data)
+ init_boundaries_iter_face_inner(info_pw, user_data)
end
end
@@ -381,23 +384,23 @@ function init_surfaces!(interfaces, mortars, boundaries, mpi_interfaces, mpi_mor
end
# Initialization of MPI interfaces after the function barrier
-function init_mpi_interfaces_iter_face_inner(info, sides, user_data)
+function init_mpi_interfaces_iter_face_inner(info_pw, sides_pw, user_data)
@unpack mpi_interfaces, mpi_interface_id, mesh = user_data
user_data.mpi_interface_id += 1
- if sides[1].is.full.is_ghost == true
+ if sides_pw[1].is.full.is_ghost[] == true
local_side = 2
- elseif sides[2].is.full.is_ghost == true
+ elseif sides_pw[2].is.full.is_ghost[] == true
local_side = 1
else
error("should not happen")
end
# Get local tree, one-based indexing
- tree = unsafe_load_tree(mesh.p4est, sides[local_side].treeid + 1)
+ tree_pw = load_pointerwrapper_tree(mesh.p4est, sides_pw[local_side].treeid[] + 1)
# Quadrant numbering offset of the local quadrant at this interface
- offset = tree.quadrants_offset
- tree_quad_id = sides[local_side].is.full.quadid # quadid in the local tree
+ offset = tree_pw.quadrants_offset[]
+ tree_quad_id = sides_pw[local_side].is.full.quadid[] # quadid in the local tree
# ID of the local neighboring quad, cumulative over local trees
local_quad_id = offset + tree_quad_id
@@ -406,52 +409,52 @@ function init_mpi_interfaces_iter_face_inner(info, sides, user_data)
mpi_interfaces.local_sides[mpi_interface_id] = local_side
# Face at which the interface lies
- faces = (sides[1].face, sides[2].face)
+ faces = (sides_pw[1].face[], sides_pw[2].face[])
# Save mpi_interfaces.node_indices dimension specific in containers_[23]d_parallel.jl
init_mpi_interface_node_indices!(mpi_interfaces, faces, local_side,
- unsafe_load(info).orientation,
+ info_pw.orientation[],
mpi_interface_id)
return nothing
end
# Initialization of MPI mortars after the function barrier
-function init_mpi_mortars_iter_face_inner(info, sides, user_data)
+function init_mpi_mortars_iter_face_inner(info_pw, sides_pw, user_data)
@unpack mpi_mortars, mpi_mortar_id, mesh = user_data
user_data.mpi_mortar_id += 1
# Get Tuple of adjacent trees, one-based indexing
- trees = (unsafe_load_tree(mesh.p4est, sides[1].treeid + 1),
- unsafe_load_tree(mesh.p4est, sides[2].treeid + 1))
+ trees_pw = (load_pointerwrapper_tree(mesh.p4est, sides_pw[1].treeid[] + 1),
+ load_pointerwrapper_tree(mesh.p4est, sides_pw[2].treeid[] + 1))
# Quadrant numbering offsets of the quadrants at this mortar
- offsets = SVector(trees[1].quadrants_offset,
- trees[2].quadrants_offset)
+ offsets = SVector(trees_pw[1].quadrants_offset[],
+ trees_pw[2].quadrants_offset[])
- if sides[1].is_hanging == true
+ if sides_pw[1].is_hanging[] == true
hanging_side = 1
full_side = 2
- else # sides[2].is_hanging == true
+ else # sides_pw[2].is_hanging[] == true
hanging_side = 2
full_side = 1
end
# Just be sure before accessing is.full or is.hanging later
- @assert sides[full_side].is_hanging == false
- @assert sides[hanging_side].is_hanging == true
+ @assert sides_pw[full_side].is_hanging[] == false
+ @assert sides_pw[hanging_side].is_hanging[] == true
# Find small quads that are locally available
- local_small_quad_positions = findall(sides[hanging_side].is.hanging.is_ghost .==
+ local_small_quad_positions = findall(sides_pw[hanging_side].is.hanging.is_ghost[] .==
false)
# Get id of local small quadrants within their tree
# Indexing CBinding.Caccessor via a Vector does not work here -> use map instead
- tree_small_quad_ids = map(p -> sides[hanging_side].is.hanging.quadid[p],
+ tree_small_quad_ids = map(p -> sides_pw[hanging_side].is.hanging.quadid[][p],
local_small_quad_positions)
local_small_quad_ids = offsets[hanging_side] .+ tree_small_quad_ids # ids cumulative over local trees
# Determine if large quadrant is available and if yes, determine its id
- if sides[full_side].is.full.is_ghost == false
- local_large_quad_id = offsets[full_side] + sides[full_side].is.full.quadid
+ if sides_pw[full_side].is.full.is_ghost[] == false
+ local_large_quad_id = offsets[full_side] + sides_pw[full_side].is.full.quadid[]
else
local_large_quad_id = -1 # large quad is ghost
end
@@ -470,9 +473,8 @@ function init_mpi_mortars_iter_face_inner(info, sides, user_data)
mpi_mortars.local_neighbor_positions[mpi_mortar_id] = local_neighbor_positions
# init_mortar_node_indices! expects side 1 to contain small elements
- faces = (sides[hanging_side].face, sides[full_side].face)
- init_mortar_node_indices!(mpi_mortars, faces, unsafe_load(info).orientation,
- mpi_mortar_id)
+ faces = (sides_pw[hanging_side].face[], sides_pw[full_side].face[])
+ init_mortar_node_indices!(mpi_mortars, faces, info_pw.orientation[], mpi_mortar_id)
return nothing
end
@@ -485,42 +487,45 @@ end
# - (MPI) mortars at subdomain boundaries
# and collect the numbers in `user_data` in this order.
function count_surfaces_iter_face_parallel(info, user_data)
- if unsafe_load(info).sides.elem_count == 2
+ info_pw = PointerWrapper(info)
+ if info_pw.sides.elem_count[] == 2
# Two neighboring elements => Interface or mortar
# Extract surface data
- sides = (unsafe_load_side(info, 1), unsafe_load_side(info, 2))
+ sides_pw = (load_pointerwrapper_side(info_pw, 1),
+ load_pointerwrapper_side(info_pw, 2))
- if sides[1].is_hanging == false && sides[2].is_hanging == false
+ if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false
# No hanging nodes => normal interface or MPI interface
- if sides[1].is.full.is_ghost == true || sides[2].is.full.is_ghost == true # remote side => MPI interface
+ if sides_pw[1].is.full.is_ghost[] == true ||
+ sides_pw[2].is.full.is_ghost[] == true # remote side => MPI interface
# Unpack user_data = [mpi_interface_count] and increment mpi_interface_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 4)
- unsafe_store!(ptr, id + 1, 4)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[4]
+ pw[4] = id + 1
else
# Unpack user_data = [interface_count] and increment interface_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 1)
- unsafe_store!(ptr, id + 1, 1)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[1]
+ pw[1] = id + 1
end
else
# Hanging nodes => mortar or MPI mortar
# First, we check which side is hanging, i.e., on which side we have the refined cells.
# Then we check if any of the refined cells or the coarse cell are "ghost" cells, i.e., they
# belong to another rank. That way we can determine if this is a regular mortar or MPI mortar
- if sides[1].is_hanging == true
- @assert sides[2].is_hanging == false
- if any(sides[1].is.hanging.is_ghost .== true) ||
- sides[2].is.full.is_ghost == true
+ if sides_pw[1].is_hanging[] == true
+ @assert sides_pw[2].is_hanging[] == false
+ if any(sides_pw[1].is.hanging.is_ghost[] .== true) ||
+ sides_pw[2].is.full.is_ghost[] == true
face_has_ghost_side = true
else
face_has_ghost_side = false
end
- else # sides[2].is_hanging == true
- @assert sides[1].is_hanging == false
- if sides[1].is.full.is_ghost == true ||
- any(sides[2].is.hanging.is_ghost .== true)
+ else # sides_pw[2].is_hanging[] == true
+ @assert sides_pw[1].is_hanging[] == false
+ if sides_pw[1].is.full.is_ghost[] == true ||
+ any(sides_pw[2].is.hanging.is_ghost[] .== true)
face_has_ghost_side = true
else
face_has_ghost_side = false
@@ -528,23 +533,23 @@ function count_surfaces_iter_face_parallel(info, user_data)
end
if face_has_ghost_side
# Unpack user_data = [mpi_mortar_count] and increment mpi_mortar_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 5)
- unsafe_store!(ptr, id + 1, 5)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[5]
+ pw[5] = id + 1
else
# Unpack user_data = [mortar_count] and increment mortar_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 2)
- unsafe_store!(ptr, id + 1, 2)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[2]
+ pw[2] = id + 1
end
end
- elseif unsafe_load(info).sides.elem_count == 1
+ elseif info_pw.sides.elem_count[] == 1
# One neighboring elements => boundary
# Unpack user_data = [boundary_count] and increment boundary_count
- ptr = Ptr{Int}(user_data)
- id = unsafe_load(ptr, 3)
- unsafe_store!(ptr, id + 1, 3)
+ pw = PointerWrapper(Int, user_data)
+ id = pw[3]
+ pw[3] = id + 1
end
return nothing
diff --git a/src/solvers/dgsem_p4est/dg_parallel.jl b/src/solvers/dgsem_p4est/dg_parallel.jl
index ac122d048c1..324bc7f3cd6 100644
--- a/src/solvers/dgsem_p4est/dg_parallel.jl
+++ b/src/solvers/dgsem_p4est/dg_parallel.jl
@@ -263,15 +263,13 @@ function init_mpi_cache!(mpi_cache::P4estMPICache, mesh::ParallelP4estMesh,
uEltype)
# Determine local and total number of elements
- n_elements_global = Int(unsafe_load(mesh.p4est).global_num_quadrants)
- n_elements_by_rank = vcat(Int.(unsafe_wrap(Array,
- unsafe_load(mesh.p4est).global_first_quadrant,
+ n_elements_global = Int(mesh.p4est.global_num_quadrants[])
+ n_elements_by_rank = vcat(Int.(unsafe_wrap(Array, mesh.p4est.global_first_quadrant,
mpi_nranks())),
n_elements_global) |> diff # diff sufficient due to 0-based quad indices
n_elements_by_rank = OffsetArray(n_elements_by_rank, 0:(mpi_nranks() - 1))
# Account for 1-based indexing in Julia
- first_element_global_id = Int(unsafe_load(unsafe_load(mesh.p4est).global_first_quadrant,
- mpi_rank() + 1)) + 1
+ first_element_global_id = Int(mesh.p4est.global_first_quadrant[mpi_rank() + 1]) + 1
@assert n_elements_global==sum(n_elements_by_rank) "error in total number of elements"
# TODO reuse existing structures
@@ -379,17 +377,19 @@ function init_neighbor_rank_connectivity_iter_face_inner(info, user_data)
@unpack interfaces, interface_id, global_interface_ids, neighbor_ranks_interface,
mortars, mortar_id, global_mortar_ids, neighbor_ranks_mortar, mesh = user_data
+ info_pw = PointerWrapper(info)
# Get the global interface/mortar ids and neighbor rank if current face belongs to an MPI
# interface/mortar
- if unsafe_load(info).sides.elem_count == 2 # MPI interfaces/mortars have two neighboring elements
+ if info_pw.sides.elem_count[] == 2 # MPI interfaces/mortars have two neighboring elements
# Extract surface data
- sides = (unsafe_load_side(info, 1), unsafe_load_side(info, 2))
+ sides_pw = (load_pointerwrapper_side(info_pw, 1),
+ load_pointerwrapper_side(info_pw, 2))
- if sides[1].is_hanging == false && sides[2].is_hanging == false # No hanging nodes for MPI interfaces
- if sides[1].is.full.is_ghost == true
+ if sides_pw[1].is_hanging[] == false && sides_pw[2].is_hanging[] == false # No hanging nodes for MPI interfaces
+ if sides_pw[1].is.full.is_ghost[] == true
remote_side = 1
local_side = 2
- elseif sides[2].is.full.is_ghost == true
+ elseif sides_pw[2].is.full.is_ghost[] == true
remote_side = 2
local_side = 1
else # both sides are on this rank -> skip since it's a regular interface
@@ -397,16 +397,17 @@ function init_neighbor_rank_connectivity_iter_face_inner(info, user_data)
end
# Sanity check, current face should belong to current MPI interface
- local_tree = unsafe_load_tree(mesh.p4est, sides[local_side].treeid + 1) # one-based indexing
- local_quad_id = local_tree.quadrants_offset +
- sides[local_side].is.full.quadid
+ local_tree_pw = load_pointerwrapper_tree(mesh.p4est,
+ sides_pw[local_side].treeid[] + 1) # one-based indexing
+ local_quad_id = local_tree_pw.quadrants_offset[] +
+ sides_pw[local_side].is.full.quadid[]
@assert interfaces.local_neighbor_ids[interface_id] == local_quad_id + 1 # one-based indexing
# Get neighbor ID from ghost layer
proc_offsets = unsafe_wrap(Array,
- unsafe_load(unsafe_load(info).ghost_layer).proc_offsets,
+ info_pw.ghost_layer.proc_offsets,
mpi_nranks() + 1)
- ghost_id = sides[remote_side].is.full.quadid # indexes the ghost layer, 0-based
+ ghost_id = sides_pw[remote_side].is.full.quadid[] # indexes the ghost layer, 0-based
neighbor_rank = findfirst(r -> proc_offsets[r] <= ghost_id <
proc_offsets[r + 1],
1:mpi_nranks()) - 1 # MPI ranks are 0-based
@@ -415,21 +416,18 @@ function init_neighbor_rank_connectivity_iter_face_inner(info, user_data)
# Global interface id is the globally unique quadrant id of the quadrant on the primary
# side (1) multiplied by the number of faces per quadrant plus face
if local_side == 1
- offset = unsafe_load(unsafe_load(mesh.p4est).global_first_quadrant,
- mpi_rank() + 1) # one-based indexing
+ offset = mesh.p4est.global_first_quadrant[mpi_rank() + 1] # one-based indexing
primary_quad_id = offset + local_quad_id
else
- offset = unsafe_load(unsafe_load(mesh.p4est).global_first_quadrant,
- neighbor_rank + 1) # one-based indexing
- primary_quad_id = offset +
- unsafe_load(sides[1].is.full.quad.p.piggy3.local_num)
+ offset = mesh.p4est.global_first_quadrant[neighbor_rank + 1] # one-based indexing
+ primary_quad_id = offset + sides_pw[1].is.full.quad.p.piggy3.local_num[]
end
- global_interface_id = 2 * ndims(mesh) * primary_quad_id + sides[1].face
+ global_interface_id = 2 * ndims(mesh) * primary_quad_id + sides_pw[1].face[]
global_interface_ids[interface_id] = global_interface_id
user_data.interface_id += 1
else # hanging node
- if sides[1].is_hanging == true
+ if sides_pw[1].is_hanging[] == true
hanging_side = 1
full_side = 2
else
@@ -437,26 +435,26 @@ function init_neighbor_rank_connectivity_iter_face_inner(info, user_data)
full_side = 1
end
# Verify before accessing is.full / is.hanging
- @assert sides[hanging_side].is_hanging == true &&
- sides[full_side].is_hanging == false
+ @assert sides_pw[hanging_side].is_hanging[] == true &&
+ sides_pw[full_side].is_hanging[] == false
# If all quadrants are locally available, this is a regular mortar -> skip
- if sides[full_side].is.full.is_ghost == false &&
- all(sides[hanging_side].is.hanging.is_ghost .== false)
+ if sides_pw[full_side].is.full.is_ghost[] == false &&
+ all(sides_pw[hanging_side].is.hanging.is_ghost[] .== false)
return nothing
end
- trees = (unsafe_load_tree(mesh.p4est, sides[1].treeid + 1),
- unsafe_load_tree(mesh.p4est, sides[2].treeid + 1))
+ trees_pw = (load_pointerwrapper_tree(mesh.p4est, sides_pw[1].treeid[] + 1),
+ load_pointerwrapper_tree(mesh.p4est, sides_pw[2].treeid[] + 1))
# Find small quads that are remote and determine which rank owns them
- remote_small_quad_positions = findall(sides[hanging_side].is.hanging.is_ghost .==
+ remote_small_quad_positions = findall(sides_pw[hanging_side].is.hanging.is_ghost[] .==
true)
proc_offsets = unsafe_wrap(Array,
- unsafe_load(unsafe_load(info).ghost_layer).proc_offsets,
+ info_pw.ghost_layer.proc_offsets,
mpi_nranks() + 1)
# indices of small remote quads inside the ghost layer, 0-based
- ghost_ids = map(pos -> sides[hanging_side].is.hanging.quadid[pos],
+ ghost_ids = map(pos -> sides_pw[hanging_side].is.hanging.quadid[][pos],
remote_small_quad_positions)
neighbor_ranks = map(ghost_ids) do ghost_id
return findfirst(r -> proc_offsets[r] <= ghost_id < proc_offsets[r + 1],
@@ -464,28 +462,26 @@ function init_neighbor_rank_connectivity_iter_face_inner(info, user_data)
end
# Determine global quad id of large element to determine global MPI mortar id
# Furthermore, if large element is ghost, add its owner rank to neighbor_ranks
- if sides[full_side].is.full.is_ghost == true
- ghost_id = sides[full_side].is.full.quadid
+ if sides_pw[full_side].is.full.is_ghost[] == true
+ ghost_id = sides_pw[full_side].is.full.quadid[]
large_quad_owner_rank = findfirst(r -> proc_offsets[r] <= ghost_id <
proc_offsets[r + 1],
1:mpi_nranks()) - 1 # MPI ranks are 0-based
push!(neighbor_ranks, large_quad_owner_rank)
- offset = unsafe_load(unsafe_load(mesh.p4est).global_first_quadrant,
- large_quad_owner_rank + 1) # one-based indexing
+ offset = mesh.p4est.global_first_quadrant[large_quad_owner_rank + 1] # one-based indexing
large_quad_id = offset +
- unsafe_load(sides[full_side].is.full.quad.p.piggy3.local_num)
+ sides_pw[full_side].is.full.quad.p.piggy3.local_num[]
else
- offset = unsafe_load(unsafe_load(mesh.p4est).global_first_quadrant,
- mpi_rank() + 1) # one-based indexing
- large_quad_id = offset + trees[full_side].quadrants_offset +
- sides[full_side].is.full.quadid
+ offset = mesh.p4est.global_first_quadrant[mpi_rank() + 1] # one-based indexing
+ large_quad_id = offset + trees_pw[full_side].quadrants_offset[] +
+ sides_pw[full_side].is.full.quadid[]
end
neighbor_ranks_mortar[mortar_id] = neighbor_ranks
# Global mortar id is the globally unique quadrant id of the large quadrant multiplied by the
# number of faces per quadrant plus face
global_mortar_ids[mortar_id] = 2 * ndims(mesh) * large_quad_id +
- sides[full_side].face
+ sides_pw[full_side].face[]
user_data.mortar_id += 1
end
From eb4c91a6ba3c1fe1c1f0bbb9c332ab0068171113 Mon Sep 17 00:00:00 2001
From: David Knapp
Date: Mon, 19 Jun 2023 10:48:56 +0200
Subject: [PATCH 004/263] Added a dispatchable constructur for DG with
TensorProductWedges (#1389)
* Added a dispatchable constructur for DG with TensorProductWedges
Adapted the timestepping for DGMulti to use the minimal polynomial degree of the tensorproduct
* Use the maximal poly-degree
* Update src/solvers/dgmulti/types.jl
Co-authored-by: Hendrik Ranocha
* Change DGMulti-call for Tensorwedges
* Adding comment about the polydeg-choice
* Adapt constructor
* Add an example for TensorWedges
* Update example
* Update examples/dgmulti_3d/elixir_euler_tensorWedge.jl
Co-authored-by: Hendrik Ranocha
* Update examples/dgmulti_3d/elixir_euler_tensorWedge.jl
Co-authored-by: Hendrik Ranocha
* Update examples/dgmulti_3d/elixir_euler_tensorWedge.jl
Co-authored-by: Hendrik Ranocha
* Readd Gauss
Accidentaly deleted during Merge-Conflict resolvement online
* Remove explicit load of StartUpDG in elixir
* Update max_dt
* Apply suggestions from code review
Co-authored-by: Hendrik Ranocha
* Update comments
* Add tensorWedge-test
* Update src/solvers/dgmulti/dg.jl
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
* Renamed tensorwedge elixir
* Update testset title
* Remove typos
* temporal push
* fix max_dt dispatch
* Update TensorProductWedges-test
* Update test
* Address CI-Warning
* Update dt_polydeg_scaling
* Correct dt_polydeg_scaling
A previous commit changed it from (polydeg + 1) to polydeg
* Update DGMulti-call
* Rename file
* Change Path in test-file
* Apply suggestions from code review
Co-authored-by: Hendrik Ranocha
* Update examples/dgmulti_3d/elixir_advection_tensor_wedge.jl
Co-authored-by: Hendrik Ranocha
* Reformat src/Trixi.jl
* Format solvers/dgmulti/dg.jl
* Formatting
---------
Co-authored-by: Knapp
Co-authored-by: Hendrik Ranocha
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Co-authored-by: Jesse Chan
---
.../elixir_advection_tensor_wedge.jl | 56 +++++++++++++++++++
src/Trixi.jl | 3 +-
src/solvers/dgmulti/dg.jl | 11 ++--
src/solvers/dgmulti/types.jl | 15 +++++
test/test_dgmulti_3d.jl | 6 ++
5 files changed, 86 insertions(+), 5 deletions(-)
create mode 100644 examples/dgmulti_3d/elixir_advection_tensor_wedge.jl
diff --git a/examples/dgmulti_3d/elixir_advection_tensor_wedge.jl b/examples/dgmulti_3d/elixir_advection_tensor_wedge.jl
new file mode 100644
index 00000000000..4f43f2571a3
--- /dev/null
+++ b/examples/dgmulti_3d/elixir_advection_tensor_wedge.jl
@@ -0,0 +1,56 @@
+using OrdinaryDiffEq
+using Trixi
+using LinearAlgebra
+
+###############################################################################
+equations = LinearScalarAdvectionEquation3D(1.0, 1.0, 1.0)
+
+initial_condition = initial_condition_convergence_test
+
+# Define the polynomial degrees for the polynoms of the triangular base and the line
+# of the tensor-prism
+tensor_polydeg = (3, 4)
+
+dg = DGMulti(element_type = Wedge(),
+ approximation_type = Polynomial(),
+ surface_flux = flux_lax_friedrichs,
+ polydeg = tensor_polydeg)
+
+
+cells_per_dimension = (8, 8, 8)
+mesh = DGMultiMesh(dg,
+ cells_per_dimension,
+ coordinates_min = (-1.0, -1.0, -1.0),
+ coordinates_max = (1.0, 1.0, 1.0),
+ periodicity = true)
+
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, dg,
+ boundary_conditions=boundary_condition_periodic)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 5.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, uEltype=real(dg))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
+stepsize_callback = StepsizeCallback(cfl=1.0)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, stepsize_callback)
+
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), dt = 1.0,
+ save_everystep=false, callback=callbacks);
+
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/src/Trixi.jl b/src/Trixi.jl
index 86e349c7dad..66878f4b459 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -83,7 +83,8 @@ import SummationByPartsOperators: integrate, semidiscretize,
upwind_operators
# DGMulti solvers
-@reexport using StartUpDG: StartUpDG, Polynomial, Gauss, SBP, Line, Tri, Quad, Hex, Tet
+@reexport using StartUpDG: StartUpDG, Polynomial, Gauss, TensorProductWedge, SBP, Line, Tri,
+ Quad, Hex, Tet, Wedge
using StartUpDG: RefElemData, MeshData, AbstractElemShape
# TODO: include_optimized
diff --git a/src/solvers/dgmulti/dg.jl b/src/solvers/dgmulti/dg.jl
index d51c7cabf9d..bc76aa1a9d2 100644
--- a/src/solvers/dgmulti/dg.jl
+++ b/src/solvers/dgmulti/dg.jl
@@ -226,6 +226,11 @@ function estimate_dt(mesh::DGMultiMesh, dg::DGMulti)
return StartUpDG.estimate_h(rd, mesh.md) / StartUpDG.inverse_trace_constant(rd)
end
+dt_polydeg_scaling(dg::DGMulti) = inv(dg.basis.N + 1)
+function dt_polydeg_scaling(dg::DGMulti{3, <:Wedge, <:TensorProductWedge})
+ inv(maximum(dg.basis.N) + 1)
+end
+
# for the stepsize callback
function max_dt(u, t, mesh::DGMultiMesh,
constant_speed::False, equations, dg::DGMulti{NDIMS},
@@ -247,8 +252,7 @@ function max_dt(u, t, mesh::DGMultiMesh,
# `polydeg+1`. This is because `nnodes(dg)` returns the total number of
# multi-dimensional nodes for DGMulti solver types, while `nnodes(dg)` returns
# the number of 1D nodes for `DGSEM` solvers.
- polydeg = rd.N
- return 2 * dt_min / (polydeg + 1)
+ return 2 * dt_min * dt_polydeg_scaling(dg)
end
function max_dt(u, t, mesh::DGMultiMesh,
@@ -270,8 +274,7 @@ function max_dt(u, t, mesh::DGMultiMesh,
# `polydeg+1`. This is because `nnodes(dg)` returns the total number of
# multi-dimensional nodes for DGMulti solver types, while `nnodes(dg)` returns
# the number of 1D nodes for `DGSEM` solvers.
- polydeg = rd.N
- return 2 * dt_min / (polydeg + 1)
+ return 2 * dt_min * dt_polydeg_scaling(dg)
end
# interpolates from solution coefficients to face quadrature points
diff --git a/src/solvers/dgmulti/types.jl b/src/solvers/dgmulti/types.jl
index c225e334e8e..f1f7b158dec 100644
--- a/src/solvers/dgmulti/types.jl
+++ b/src/solvers/dgmulti/types.jl
@@ -96,6 +96,21 @@ function DGMulti(; polydeg = nothing,
polydeg = polydeg, kwargs...)
end
+# dispatchable constructor for DGMulti using a TensorProductWedge
+function DGMulti(element_type::Wedge,
+ approximation_type,
+ volume_integral,
+ surface_integral;
+ polydeg::Tuple,
+ kwargs...)
+ factor_a = RefElemData(Tri(), approximation_type, polydeg[1]; kwargs...)
+ factor_b = RefElemData(Line(), approximation_type, polydeg[2]; kwargs...)
+
+ tensor = TensorProductWedge(factor_a, factor_b)
+ rd = RefElemData(element_type, tensor; kwargs...)
+ return DG(rd, nothing, surface_integral, volume_integral)
+end
+
# dispatchable constructor for DGMulti to allow for specialization
function DGMulti(element_type::AbstractElemShape,
approximation_type,
diff --git a/test/test_dgmulti_3d.jl b/test/test_dgmulti_3d.jl
index 22c0a0fd3ba..68fa1d13304 100644
--- a/test/test_dgmulti_3d.jl
+++ b/test/test_dgmulti_3d.jl
@@ -135,6 +135,12 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "elixir_advection_tensor_wedge.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_tensor_wedge.jl"),
+ l2 = [2.30487910e-04] ,
+ linf = [6.31795281e-04] )
+ end
+
end
# Clean up afterwards: delete Trixi.jl output directory
From a837dd7726f25a88c7bab8bed2dc6955f20534bf Mon Sep 17 00:00:00 2001
From: Andrew Winters
Date: Mon, 19 Jun 2023 14:50:27 +0200
Subject: [PATCH 005/263] Update test values for 1D discontinuous examples
(#1511)
* set true discontinuous initial conditions for 1D
* update Burgers tests
* update MHD tests in 1D
* update structured_1d_dgsem/elixir_advection_shockcapturing elixir and test
* remove workaround in elixir_shallowwater_ec.jl and update test
* accidentally removed adjusted tolerance in structured mesh test
* remove workaround in elixir_shallowwater_shockcapturing.jl and update test
* remove workaround in elixir_shallowwater_well_balanced.jl and update tests
* bug fix in energy_total computation for ShallowWaterTwoLayerEquations1D
* remove workaround in elixir_shallowwater_twolayer_dam_break.jl and update test
* update docs test for adding a nonconservative equation
* update Nonconservative terms in 1D (linear advection) test
* update all necessary IdealGlmMhdMulticomponentEquations1D tests
* update all necessary CompressibleEulerMulticomponentEquations1D tests
* update necessary CompressibleEuler1D tests except for neural network shock capturing
* Update test values for NN-based SC
* add short description to the NEWS.md
* add extra test to avoid coverage drop
* Update src/solvers/dg.jl
Co-authored-by: Hendrik Ranocha
---------
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
---
NEWS.md | 1 +
.../files/adding_nonconservative_equation.jl | 2 +-
.../elixir_advection_shockcapturing.jl | 6 +-
.../tree_1d_dgsem/elixir_burgers_shock.jl | 6 +-
..._eulermulti_two_interacting_blast_waves.jl | 2 +-
.../elixir_mhd_briowu_shock_tube.jl | 2 +-
examples/tree_1d_dgsem/elixir_mhdmulti_ec.jl | 4 +-
examples/tree_1d_dgsem/elixir_mhdmulti_es.jl | 4 +-
.../tree_1d_dgsem/elixir_shallowwater_ec.jl | 71 ++++++---------
.../elixir_shallowwater_shock_capturing.jl | 81 +++++++----------
.../elixir_shallowwater_twolayer_dam_break.jl | 77 +++++++----------
.../elixir_shallowwater_well_balanced.jl | 56 +++---------
src/equations/shallow_water_two_layer_1d.jl | 86 +++++++++----------
src/solvers/dg.jl | 9 ++
test/test_structured_1d.jl | 4 +-
test/test_tree_1d.jl | 2 +-
test/test_tree_1d_burgers.jl | 8 +-
test/test_tree_1d_euler.jl | 44 +++++-----
test/test_tree_1d_eulermulti.jl | 32 ++++---
test/test_tree_1d_mhd.jl | 12 +--
test/test_tree_1d_mhdmulti.jl | 48 +++++------
test/test_tree_1d_shallowwater.jl | 28 +++---
test/test_tree_1d_shallowwater_twolayer.jl | 6 +-
23 files changed, 267 insertions(+), 324 deletions(-)
diff --git a/NEWS.md b/NEWS.md
index 9b46ba565fe..35c7039b2ef 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -9,6 +9,7 @@ for human readability.
#### Added
- Experimental support for 3D parabolic diffusion terms has been added.
+- Capability to set truly discontinuous initial conditions in 1D.
#### Changed
diff --git a/docs/literate/src/files/adding_nonconservative_equation.jl b/docs/literate/src/files/adding_nonconservative_equation.jl
index 08dd631058e..110fa486070 100644
--- a/docs/literate/src/files/adding_nonconservative_equation.jl
+++ b/docs/literate/src/files/adding_nonconservative_equation.jl
@@ -147,7 +147,7 @@ plot(sol)
# above.
error_1 = analysis_callback(sol).l2 |> first
-@test isapprox(error_1, 0.0002961027497) #src
+@test isapprox(error_1, 0.00029609575838969394) #src
# Next, we increase the grid resolution by one refinement level and run the
# simulation again.
diff --git a/examples/structured_1d_dgsem/elixir_advection_shockcapturing.jl b/examples/structured_1d_dgsem/elixir_advection_shockcapturing.jl
index 9a81acfe51c..313812fe08d 100644
--- a/examples/structured_1d_dgsem/elixir_advection_shockcapturing.jl
+++ b/examples/structured_1d_dgsem/elixir_advection_shockcapturing.jl
@@ -9,7 +9,9 @@ advection_velocity = 1.0
"""
initial_condition_composite(x, t, equations::LinearScalarAdvectionEquation1D)
-Slight simplification of
+Wave form that is a combination of a Gaussian pulse, a square wave, a triangle wave,
+and half an ellipse with periodic boundary conditions.
+Slight simplification from
- Jiang, Shu (1996)
Efficient Implementation of Weighted ENO Schemes
[DOI: 10.1006/jcph.1996.0130](https://doi.org/10.1006/jcph.1996.0130)
@@ -60,7 +62,7 @@ volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
solver = DGSEM(basis, surface_flux, volume_integral)
# Create curved mesh
-cells_per_dimension = (125,)
+cells_per_dimension = (120,)
coordinates_min = (-1.0,) # minimum coordinate
coordinates_max = (1.0,) # maximum coordinate
mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max,
diff --git a/examples/tree_1d_dgsem/elixir_burgers_shock.jl b/examples/tree_1d_dgsem/elixir_burgers_shock.jl
index 987fb320ad6..00b5314e19f 100644
--- a/examples/tree_1d_dgsem/elixir_burgers_shock.jl
+++ b/examples/tree_1d_dgsem/elixir_burgers_shock.jl
@@ -21,7 +21,7 @@ surface_flux = flux_lax_friedrichs
volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=surface_flux,
volume_flux_fv=surface_flux)
-
+
solver = DGSEM(basis, surface_flux, volume_integral)
coordinate_min = 0.0
@@ -59,7 +59,7 @@ end
boundary_conditions = (x_neg=boundary_condition_inflow,
x_pos=boundary_condition_outflow)
-
+
initial_condition = initial_condition_shock
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
@@ -79,7 +79,7 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
alive_callback = AliveCallback(analysis_interval=analysis_interval)
-stepsize_callback = StepsizeCallback(cfl=0.9)
+stepsize_callback = StepsizeCallback(cfl=0.85)
callbacks = CallbackSet(summary_callback,
diff --git a/examples/tree_1d_dgsem/elixir_eulermulti_two_interacting_blast_waves.jl b/examples/tree_1d_dgsem/elixir_eulermulti_two_interacting_blast_waves.jl
index 353093e5f70..81966194180 100644
--- a/examples/tree_1d_dgsem/elixir_eulermulti_two_interacting_blast_waves.jl
+++ b/examples/tree_1d_dgsem/elixir_eulermulti_two_interacting_blast_waves.jl
@@ -88,7 +88,7 @@ ode = semidiscretize(semi, tspan)
summary_callback = SummaryCallback()
-analysis_interval = 100
+analysis_interval = 1000
analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
diff --git a/examples/tree_1d_dgsem/elixir_mhd_briowu_shock_tube.jl b/examples/tree_1d_dgsem/elixir_mhd_briowu_shock_tube.jl
index 1c07fc4fdde..c5727109d92 100644
--- a/examples/tree_1d_dgsem/elixir_mhd_briowu_shock_tube.jl
+++ b/examples/tree_1d_dgsem/elixir_mhd_briowu_shock_tube.jl
@@ -94,7 +94,7 @@ amr_callback = AMRCallback(semi, amr_controller,
adapt_initial_condition=true,
adapt_initial_condition_only_refine=true)
-stepsize_callback = StepsizeCallback(cfl=0.8)
+stepsize_callback = StepsizeCallback(cfl=0.65)
callbacks = CallbackSet(summary_callback,
analysis_callback, alive_callback,
diff --git a/examples/tree_1d_dgsem/elixir_mhdmulti_ec.jl b/examples/tree_1d_dgsem/elixir_mhdmulti_ec.jl
index 34fdce6634e..69ea0551bed 100644
--- a/examples/tree_1d_dgsem/elixir_mhdmulti_ec.jl
+++ b/examples/tree_1d_dgsem/elixir_mhdmulti_ec.jl
@@ -4,8 +4,8 @@ using Trixi
###############################################################################
# semidiscretization of the ideal MHD equations
-equations = IdealGlmMhdMulticomponentEquations1D(gammas = (2.0, 2.0, 2.0),
- gas_constants = (2.0, 2.0, 2.0))
+equations = IdealGlmMhdMulticomponentEquations1D(gammas = (2.0, 2.0, 2.0),
+ gas_constants = (2.0, 2.0, 2.0))
initial_condition = initial_condition_weak_blast_wave
diff --git a/examples/tree_1d_dgsem/elixir_mhdmulti_es.jl b/examples/tree_1d_dgsem/elixir_mhdmulti_es.jl
index 8ca32194b9e..93cf3e0fdb2 100644
--- a/examples/tree_1d_dgsem/elixir_mhdmulti_es.jl
+++ b/examples/tree_1d_dgsem/elixir_mhdmulti_es.jl
@@ -4,8 +4,8 @@ using Trixi
###############################################################################
# semidiscretization of the ideal MHD equations
-equations = IdealGlmMhdMulticomponentEquations1D(gammas = (2.0, 2.0, 2.0),
- gas_constants = (2.0, 2.0, 2.0))
+equations = IdealGlmMhdMulticomponentEquations1D(gammas = (2.0, 2.0, 2.0),
+ gas_constants = (2.0, 2.0, 2.0))
initial_condition = initial_condition_weak_blast_wave
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_ec.jl b/examples/tree_1d_dgsem/elixir_shallowwater_ec.jl
index be6a2cb166c..1469afec1ca 100644
--- a/examples/tree_1d_dgsem/elixir_shallowwater_ec.jl
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_ec.jl
@@ -8,9 +8,34 @@ using Trixi
equations = ShallowWaterEquations1D(gravity_constant=9.81)
-# Note, this initial condition is used to compute errors in the analysis callback but the initialization is
-# overwritten by `initial_condition_ec_discontinuous_bottom` below.
-initial_condition = initial_condition_weak_blast_wave
+# Initial condition with a truly discontinuous water height, velocity, and bottom
+# topography function as an academic testcase for entropy conservation.
+# The errors from the analysis callback are not important but `∑∂S/∂U ⋅ Uₜ` should
+# be around machine roundoff.
+# Works as intended for TreeMesh1D with `initial_refinement_level=4`. If the mesh
+# refinement level is changed the initial condition below may need changed as well to
+# ensure that the discontinuities lie on an element interface.
+function initial_condition_ec_discontinuous_bottom(x, t, equations::ShallowWaterEquations1D)
+ # Set the background values
+ H = 4.25
+ v = 0.0
+ b = sin(x[1]) # arbitrary continuous function
+
+ # Setup the discontinuous water height and velocity
+ if x[1] >= 0.125 && x[1] <= 0.25
+ H = 5.0
+ v = 0.1882
+ end
+
+ # Setup a discontinuous bottom topography
+ if x[1] >= -0.25 && x[1] <= -0.125
+ b = 2.0 + 0.5 * sin(2.0 * pi * x[1])
+ end
+
+ return prim2cons(SVector(H, v, b), equations)
+end
+
+initial_condition = initial_condition_ec_discontinuous_bottom
###############################################################################
# Get the DG approximation space
@@ -37,46 +62,6 @@ semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
tspan = (0.0, 2.0)
ode = semidiscretize(semi, tspan)
-###############################################################################
-# Workaround to set a discontinuous bottom topography and initial condition for debugging and testing.
-
-# alternative version of the initial conditinon used to setup a truly discontinuous
-# bottom topography function and initial condition for this academic testcase of entropy conservation.
-# The errors from the analysis callback are not important but `∑∂S/∂U ⋅ Uₜ` should be around machine roundoff
-# In contrast to the usual signature of initial conditions, this one get passed the
-# `element_id` explicitly. In particular, this initial conditions works as intended
-# only for the TreeMesh1D with `initial_refinement_level=4`.
-function initial_condition_ec_discontinuous_bottom(x, t, element_id, equations::ShallowWaterEquations1D)
- # Set the background values
- H = 4.25
- v = 0.0
- b = sin(x[1]) # arbitrary continuous function
-
- # setup the discontinuous water height and velocity
- if element_id == 10
- H = 5.0
- v = 0.1882
- end
-
- # Setup a discontinuous bottom topography using the element id number
- if element_id == 7
- b = 2.0 + 0.5 * sin(2.0 * pi * x[1])
- end
-
- return prim2cons(SVector(H, v, b), equations)
-end
-
-# point to the data we want to augment
-u = Trixi.wrap_array(ode.u0, semi)
-# reset the initial condition
-for element in eachelement(semi.solver, semi.cache)
- for i in eachnode(semi.solver)
- x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, semi.solver, i, element)
- u_node = initial_condition_ec_discontinuous_bottom(x_node, first(tspan), element, equations)
- Trixi.set_node_vars!(u, u_node, equations, semi.solver, i, element)
- end
-end
-
###############################################################################
# Callbacks
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_shock_capturing.jl b/examples/tree_1d_dgsem/elixir_shallowwater_shock_capturing.jl
index 50241126a28..62346d7b5ab 100644
--- a/examples/tree_1d_dgsem/elixir_shallowwater_shock_capturing.jl
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_shock_capturing.jl
@@ -7,24 +7,37 @@ using Trixi
equations = ShallowWaterEquations1D(gravity_constant=9.812, H0=1.75)
-function initial_condition_stone_throw(x, t, equations::ShallowWaterEquations1D)
- # Set up polar coordinates
- inicenter = 0.15
- x_norm = x[1] - inicenter[1]
- r = abs(x_norm)
+# Initial condition with a truly discontinuous velocity and bottom topography.
+# Works as intended for TreeMesh1D with `initial_refinement_level=3`. If the mesh
+# refinement level is changed the initial condition below may need changed as well to
+# ensure that the discontinuities lie on an element interface.
+function initial_condition_stone_throw_discontinuous_bottom(x, t, equations::ShallowWaterEquations1D)
+
+ # Calculate primitive variables
+
+ # flat lake
+ H = equations.H0
+
+ # Discontinuous velocity
+ v = 0.0
+ if x[1] >= -0.75 && x[1] <= 0.0
+ v = -1.0
+ elseif x[1] >= 0.0 && x[1] <= 0.75
+ v = 1.0
+ end
- # Calculate primitive variables
- H = equations.H0
- # v = 0.0 # for well-balanced test
- v = r < 0.6 ? 1.75 : 0.0 # for stone throw
+ b = ( 1.5 / exp( 0.5 * ((x[1] - 1.0)^2 ) )
+ + 0.75 / exp( 0.5 * ((x[1] + 1.0)^2 ) ) )
- b = ( 1.5 / exp( 0.5 * ((x[1] - 1.0)^2 ) )
- + 0.75 / exp( 0.5 * ((x[1] + 1.0)^2 ) ) )
+ # Force a discontinuous bottom topography
+ if x[1] >= -1.5 && x[1] <= 0.0
+ b = 0.5
+ end
- return prim2cons(SVector(H, v, b), equations)
+ return prim2cons(SVector(H, v, b), equations)
end
-initial_condition = initial_condition_stone_throw
+initial_condition = initial_condition_stone_throw_discontinuous_bottom
boundary_condition = boundary_condition_slip_wall
@@ -62,49 +75,13 @@ semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
boundary_conditions = boundary_condition)
###############################################################################
-# ODE solvers, callbacks etc.
+# ODE solver
tspan = (0.0, 3.0)
ode = semidiscretize(semi, tspan)
-# Hack in a discontinuous bottom for a more interesting test
-function initial_condition_stone_throw_discontinuous_bottom(x, t, element_id, equations::ShallowWaterEquations1D)
-
- inicenter = 0.15
- x_norm = x[1] - inicenter[1]
- r = abs(x_norm)
-
- # Calculate primitive variables
- H = equations.H0 # flat lake
- # Discontinuous velocity set via element id number
- v = 0.0
- if element_id == 4
- v = -1.0
- elseif element_id == 5
- v = 1.0
- end
-
- b = ( 1.5 / exp( 0.5 * ((x[1] - 1.0)^2 ) )
- + 0.75 / exp( 0.5 * ((x[1] + 1.0)^2 ) ) )
-
- # Setup a discontinuous bottom topography using the element id number
- if element_id == 3 || element_id == 4
- b = 0.5
- end
-
- return prim2cons(SVector(H, v, b), equations)
-end
-
-# point to the data we want to augment
-u = Trixi.wrap_array(ode.u0, semi)
-# reset the initial condition
-for element in eachelement(semi.solver, semi.cache)
- for i in eachnode(semi.solver)
- x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, semi.solver, i, element)
- u_node = initial_condition_stone_throw_discontinuous_bottom(x_node, first(tspan), element, equations)
- Trixi.set_node_vars!(u, u_node, equations, semi.solver, i, element)
- end
-end
+###############################################################################
+# Callbacks
summary_callback = SummaryCallback()
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_twolayer_dam_break.jl b/examples/tree_1d_dgsem/elixir_shallowwater_twolayer_dam_break.jl
index 67c1098b178..60770d158fa 100644
--- a/examples/tree_1d_dgsem/elixir_shallowwater_twolayer_dam_break.jl
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_twolayer_dam_break.jl
@@ -3,20 +3,34 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# Semidiscretization of the two-layer shallow water equations for a dam break test with a
-# discontinuous bottom topography function to test entropy conservation
+# Semidiscretization of the two-layer shallow water equations for a dam break
+# test with a discontinuous bottom topography function to test entropy conservation
equations = ShallowWaterTwoLayerEquations1D(gravity_constant=9.81, H0=2.0, rho_upper=0.9, rho_lower=1.0)
-###############################################################################
-# Workaround to set a discontinuous bottom topography and initial condition.
+# Initial condition of a dam break with a discontinuous water heights and bottom topography.
+# Works as intended for TreeMesh1D with `initial_refinement_level=5`. If the mesh
+# refinement level is changed the initial condition below may need changed as well to
+# ensure that the discontinuities lie on an element interface.
+function initial_condition_dam_break(x, t, equations::ShallowWaterTwoLayerEquations1D)
+ v1_upper = 0.0
+ v1_lower = 0.0
+
+ # Set the discontinuity
+ if x[1] <= 10.0
+ H_lower = 2.0
+ H_upper = 4.0
+ b = 0.0
+ else
+ H_lower = 1.5
+ H_upper = 3.0
+ b = 0.5
+ end
+
+ return prim2cons(SVector(H_upper, v1_upper, H_lower, v1_lower, b), equations)
+end
-# This test case uses a special work around to setup a truly discontinuous bottom topography
-# function and initial condition for this academic testcase of entropy conservation. First, a
-# dummy initial condition is introduced to create the semidiscretization. Then the initial condition
-# is reset with the true discontinuous values from initial_condition_dam_break. Note, that this
-# initial condition only works for TreeMesh1D with `initial_refinement_level=5`.
-initial_condition = initial_condition_convergence_test
+initial_condition = initial_condition_dam_break
###############################################################################
# Get the DG approximation space
@@ -25,7 +39,6 @@ volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
solver = DGSEM(polydeg=3, surface_flux=(flux_fjordholm_etal, flux_nonconservative_fjordholm_etal),
volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
-
###############################################################################
# Get the TreeMesh and setup a non-periodic mesh
@@ -39,54 +52,22 @@ mesh = TreeMesh(coordinates_min, coordinates_max,
boundary_condition = boundary_condition_slip_wall
# create the semidiscretization object
-semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
boundary_conditions=boundary_condition)
###############################################################################
-# ODE solvers, callbacks etc.
+# ODE solvers
tspan = (0.0,0.4)
ode = semidiscretize(semi, tspan)
-# Initial conditions dam break test case
-function initial_condition_dam_break(x, t, element_id, equations::ShallowWaterTwoLayerEquations1D)
- v1_upper = 0.0
- v1_lower = 0.0
-
- # Set the discontinuity
- if element_id <= 16
- H_lower = 2.0
- H_upper = 4.0
- b = 0.0
- else
- H_lower = 1.5
- H_upper = 3.0
- b = 0.5
- end
-
- return prim2cons(SVector(H_upper, v1_upper, H_lower, v1_lower, b), equations)
-end
-
-
-# point to the data we want to augment
-u = Trixi.wrap_array(ode.u0, semi)
-# reset the initial condition
-for element in eachelement(semi.solver, semi.cache)
- for i in eachnode(semi.solver)
- x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates,
- equations, semi.solver, i, element)
- u_node = initial_condition_dam_break(x_node, first(tspan), element, equations)
- Trixi.set_node_vars!(u, u_node, equations, semi.solver, i, element)
- end
-end
-
-
-
+###############################################################################
+# Callbacks
summary_callback = SummaryCallback()
analysis_interval = 500
-analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
extra_analysis_integrals=(energy_total, energy_kinetic, energy_internal,))
stepsize_callback = StepsizeCallback(cfl=1.0)
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced.jl b/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced.jl
index 83835656839..e07bc04d76a 100644
--- a/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced.jl
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced.jl
@@ -8,21 +8,28 @@ using Trixi
equations = ShallowWaterEquations1D(gravity_constant=9.81, H0=3.25)
-# An initial condition with constant total water height and zero velocities to test well-balancedness.
-# Note, this routine is used to compute errors in the analysis callback but the initialization is
-# overwritten by `initial_condition_discontinuous_well_balancedness` below.
-function initial_condition_well_balancedness(x, t, equations::ShallowWaterEquations1D)
+# Setup a truly discontinuous bottom topography function for this academic
+# testcase of well-balancedness. The errors from the analysis callback are
+# not important but the error for this lake-at-rest test case
+# `∑|H0-(h+b)|` should be around machine roundoff.
+# Works as intended for TreeMesh1D with `initial_refinement_level=3`. If the mesh
+# refinement level is changed the initial condition below may need changed as well to
+# ensure that the discontinuities lie on an element interface.
+function initial_condition_discontinuous_well_balancedness(x, t, equations::ShallowWaterEquations1D)
# Set the background values
H = equations.H0
v = 0.0
+ b = 0.0
- # bottom topography inspired by from Pond.control in [HOHQMesh](https://github.com/trixi-framework/HOHQMesh)
- b = (1.5 / exp( 0.5 * ((x[1] - 1.0)^2) )+ 0.75 / exp(0.5 * ((x[1] + 1.0)^2)))
+ # Setup a discontinuous bottom topography
+ if x[1] >= 0.5 && x[1] <= 0.75
+ b = 2.0 + 0.5 * sin(2.0 * pi * x[1])
+ end
return prim2cons(SVector(H, v, b), equations)
end
-initial_condition = initial_condition_well_balancedness
+initial_condition = initial_condition_discontinuous_well_balancedness
###############################################################################
# Get the DG approximation space
@@ -50,41 +57,6 @@ semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
tspan = (0.0, 100.0)
ode = semidiscretize(semi, tspan)
-###############################################################################
-# Workaround to set a discontinuous bottom topography and initial condition for debugging and testing.
-
-# alternative version of the initial conditinon used to setup a truly discontinuous
-# bottom topography function for this academic testcase of well-balancedness.
-# The errors from the analysis callback are not important but the error for this lake at rest test case
-# `∑|H0-(h+b)|` should be around machine roundoff.
-# In contrast to the usual signature of initial conditions, this one get passed the
-# `element_id` explicitly. In particular, this initial conditions works as intended
-# only for the TreeMesh1D with `initial_refinement_level=3`.
-function initial_condition_discontinuous_well_balancedness(x, t, element_id, equations::ShallowWaterEquations1D)
- # Set the background values
- H = equations.H0
- v = 0.0
- b = 0.0
-
- # Setup a discontinuous bottom topography using the element id number
- if element_id == 7
- b = 2.0 + 0.5 * sin(2.0 * pi * x[1])
- end
-
- return prim2cons(SVector(H, v, b), equations)
-end
-
-# point to the data we want to augment
-u = Trixi.wrap_array(ode.u0, semi)
-# reset the initial condition
-for element in eachelement(semi.solver, semi.cache)
- for i in eachnode(semi.solver)
- x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, semi.solver, i, element)
- u_node = initial_condition_discontinuous_well_balancedness(x_node, first(tspan), element, equations)
- Trixi.set_node_vars!(u, u_node, equations, semi.solver, i, element)
- end
-end
-
###############################################################################
# Callbacks
diff --git a/src/equations/shallow_water_two_layer_1d.jl b/src/equations/shallow_water_two_layer_1d.jl
index edf7d5e32ff..02899171509 100644
--- a/src/equations/shallow_water_two_layer_1d.jl
+++ b/src/equations/shallow_water_two_layer_1d.jl
@@ -11,28 +11,28 @@
Two-Layer Shallow Water equations (2LSWE) in one space dimension. The equations are given by
```math
\begin{alignat*}{4}
-&\frac{\partial}{\partial t}h_{upper}
-&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper}\right)
+&\frac{\partial}{\partial t}h_{upper}
+&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper}\right)
&&= 0 \\
-&\frac{\partial}{\partial t}\left(h_{upper}v_{1,upper}\right)
-&&+ \frac{\partial}{\partial x}\left(h_{upper}v_{1,upper}^2 + \dfrac{gh_{upper}^2}{2}\right)
+&\frac{\partial}{\partial t}\left(h_{upper}v_{1,upper}\right)
+&&+ \frac{\partial}{\partial x}\left(h_{upper}v_{1,upper}^2 + \dfrac{gh_{upper}^2}{2}\right)
&&= -gh_{upper}\frac{\partial}{\partial x}\left(b+h_{lower}\right)\\
-&\frac{\partial}{\partial t}h_{lower}
-&&+ \frac{\partial}{\partial x}\left(h_{lower}v_{1,lower}\right)
+&\frac{\partial}{\partial t}h_{lower}
+&&+ \frac{\partial}{\partial x}\left(h_{lower}v_{1,lower}\right)
&&= 0 \\
-&\frac{\partial}{\partial t}\left(h_{lower}v_{1,lower}\right)
-&&+ \frac{\partial}{\partial x}\left(h_{lower}v_{1,lower}^2 + \dfrac{gh_{lower}^2}{2}\right)
+&\frac{\partial}{\partial t}\left(h_{lower}v_{1,lower}\right)
+&&+ \frac{\partial}{\partial x}\left(h_{lower}v_{1,lower}^2 + \dfrac{gh_{lower}^2}{2}\right)
&&= -gh_{lower}\frac{\partial}{\partial x}\left(b+\dfrac{\rho_{upper}}{\rho_{lower}}h_{upper}\right).
\end{alignat*}
```
-The unknown quantities of the 2LSWE are the water heights of the {lower} layer ``h_{lower}`` and the
-{upper} layer ``h_{upper}`` with respective velocities ``v_{1,upper}`` and ``v_{1,lower}``. The gravitational constant is
-denoted by `g`, the layer densitites by ``\rho_{upper}``and ``\rho_{lower}`` and the (possibly) variable
-bottom topography function ``b(x)``. The conservative variable water height ``h_{lower}`` is measured
-from the bottom topography ``b`` and ``h_{upper}`` relative to ``h_{lower}``, therefore one also defines the
+The unknown quantities of the 2LSWE are the water heights of the {lower} layer ``h_{lower}`` and the
+{upper} layer ``h_{upper}`` with respective velocities ``v_{1,upper}`` and ``v_{1,lower}``. The gravitational constant is
+denoted by `g`, the layer densitites by ``\rho_{upper}``and ``\rho_{lower}`` and the (possibly) variable
+bottom topography function ``b(x)``. The conservative variable water height ``h_{lower}`` is measured
+from the bottom topography ``b`` and ``h_{upper}`` relative to ``h_{lower}``, therefore one also defines the
total water heights as ``H_{upper} = h_{upper} + h_{upper} + b`` and ``H_{lower} = h_{lower} + b``.
-The densities must be chosen such that ``\rho_{upper} < \rho_{lower}``, to make sure that the heavier fluid
+The densities must be chosen such that ``\rho_{upper} < \rho_{lower}``, to make sure that the heavier fluid
``\rho_{lower}`` is in the bottom layer and the lighter fluid ``\rho_{upper}`` in the {upper} layer.
The additional quantity ``H_0`` is also available to store a reference value for the total water
@@ -41,13 +41,13 @@ height that is useful to set initial conditions or test the "lake-at-rest" well-
The bottom topography function ``b(x)`` is set inside the initial condition routine
for a particular problem setup.
-In addition to the unknowns, Trixi currently stores the bottom topography values at the
-approximation points despite being fixed in time. This is done for convenience of computing the
-bottom topography gradients on the fly during the approximation as well as computing auxiliary
+In addition to the unknowns, Trixi currently stores the bottom topography values at the
+approximation points despite being fixed in time. This is done for convenience of computing the
+bottom topography gradients on the fly during the approximation as well as computing auxiliary
quantities like the total water height ``H`` or the entropy variables.
This affects the implementation and use of these equations in various ways:
* The flux values corresponding to the bottom topography must be zero.
-* The bottom topography values must be included when defining initial conditions, boundary
+* The bottom topography values must be included when defining initial conditions, boundary
conditions or source terms.
* [`AnalysisCallback`](@ref) analyzes this variable.
* Trixi's visualization tools will visualize the bottom topography by default.
@@ -101,7 +101,7 @@ end
initial_condition_convergence_test(x, t, equations::ShallowWaterTwoLayerEquations1D)
A smooth initial condition used for convergence tests in combination with
-[`source_terms_convergence_test`](@ref) (and
+[`source_terms_convergence_test`](@ref) (and
[`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).
"""
function initial_condition_convergence_test(x, t,
@@ -121,9 +121,9 @@ end
"""
source_terms_convergence_test(u, x, t, equations::ShallowWaterTwoLayerEquations1D)
-Source terms used for convergence tests in combination with
-[`initial_condition_convergence_test`](@ref)
-(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref)
+Source terms used for convergence tests in combination with
+[`initial_condition_convergence_test`](@ref)
+(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref)
in non-periodic domains).
"""
@inline function source_terms_convergence_test(u, x, t,
@@ -167,8 +167,8 @@ the internal value.
For details see Section 9.2.5 of the book:
- Eleuterio F. Toro (2001)
- Shock-Capturing Methods for Free-Surface Shallow Flows
- 1st edition
+ Shock-Capturing Methods for Free-Surface Shallow Flows
+ 1st edition
ISBN 0471987662
"""
@inline function boundary_condition_slip_wall(u_inner, orientation_or_normal, direction,
@@ -219,7 +219,7 @@ end
Non-symmetric two-point volume flux discretizing the nonconservative (source) term
that contains the gradient of the bottom topography [`ShallowWaterTwoLayerEquations2D`](@ref) and an
-additional term that couples the momentum of both layers. This is a slightly modified version
+additional term that couples the momentum of both layers. This is a slightly modified version
to account for the additional source term compared to the standard SWE described in the paper.
Further details are available in the paper:
@@ -238,7 +238,7 @@ Further details are available in the paper:
z = zero(eltype(u_ll))
- # Bottom gradient nonconservative term: (0, g*h_upper*(b+h_lower)_x,
+ # Bottom gradient nonconservative term: (0, g*h_upper*(b+h_lower)_x,
# 0, g*h_lower*(b+r*h_upper)_x, 0)
f = SVector(z,
equations.gravity * h_upper_ll * (b_rr + h_lower_rr),
@@ -254,9 +254,9 @@ end
!!! warning "Experimental code"
This numerical flux is experimental and may change in any future release.
-
-Non-symmetric two-point surface flux discretizing the nonconservative (source) term that contains
-the gradients of the bottom topography and an additional term that couples the momentum of both
+
+Non-symmetric two-point surface flux discretizing the nonconservative (source) term that contains
+the gradients of the bottom topography and an additional term that couples the momentum of both
layers [`ShallowWaterTwoLayerEquations2D`](@ref).
Further details are available in the paper:
@@ -286,7 +286,7 @@ formulation.
z = zero(eltype(u_ll))
- # Bottom gradient nonconservative term: (0, g*h_upper*(b+h_lower)_x,
+ # Bottom gradient nonconservative term: (0, g*h_upper*(b+h_lower)_x,
# 0, g*h_lower*(b+r*h_upper)_x, 0)
f = SVector(z,
g * h_upper_ll * (b_ll + h_lower_ll) +
@@ -303,13 +303,13 @@ end
flux_fjordholm_etal(u_ll, u_rr, orientation,
equations::ShallowWaterTwoLayerEquations1D)
-Total energy conservative (mathematical entropy for shallow water equations). When the bottom
-topography is nonzero this should only be used as a surface flux otherwise the scheme will not be
+Total energy conservative (mathematical entropy for shallow water equations). When the bottom
+topography is nonzero this should only be used as a surface flux otherwise the scheme will not be
well-balanced. For well-balancedness in the volume flux use [`flux_wintermeyer_etal`](@ref).
Details are available in Eq. (4.1) in the paper:
- Ulrik S. Fjordholm, Siddhartha Mishra and Eitan Tadmor (2011)
- Well-balanced and energy stable schemes for the shallow water equations with discontinuous
+ Well-balanced and energy stable schemes for the shallow water equations with discontinuous
topography [DOI: 10.1016/j.jcp.2011.03.042](https://doi.org/10.1016/j.jcp.2011.03.042)
and the application to two layers is shown in the paper:
- Ulrik Skre Fjordholm (2012)
@@ -348,11 +348,11 @@ end
"""
flux_wintermeyer_etal(u_ll, u_rr, orientation,
equations::ShallowWaterTwoLayerEquations1D)
-
+
Total energy conservative (mathematical entropy for two-layer shallow water equations) split form.
When the bottom topography is nonzero this scheme will be well-balanced when used as a `volume_flux`.
The `surface_flux` should still use, e.g., [`flux_fjordholm_etal`](@ref). To obtain the flux for the
-two-layer shallow water equations the flux that is described in the paper for the normal shallow
+two-layer shallow water equations the flux that is described in the paper for the normal shallow
water equations is used within each layer.
Further details are available in Theorem 1 of the paper:
@@ -391,8 +391,8 @@ end
flux_es_fjordholm_etal(u_ll, u_rr, orientation,
equations::ShallowWaterTwoLayerEquations1D)
-Entropy stable surface flux for the two-layer shallow water equations. Uses the entropy
-conservative flux_fjordholm_etal and adds a Lax-Friedrichs type dissipation dependent on the jump
+Entropy stable surface flux for the two-layer shallow water equations. Uses the entropy
+conservative flux_fjordholm_etal and adds a Lax-Friedrichs type dissipation dependent on the jump
of entropy variables.
Further details are available in the paper:
@@ -460,10 +460,10 @@ formulation.
end
# Calculate approximation for maximum wave speed for local Lax-Friedrichs-type dissipation as the
-# maximum velocity magnitude plus the maximum speed of sound. This function uses approximate
-# eigenvalues using the speed of the barotropic mode as there is no simple way to calculate them
+# maximum velocity magnitude plus the maximum speed of sound. This function uses approximate
+# eigenvalues using the speed of the barotropic mode as there is no simple way to calculate them
# analytically.
-#
+#
# A good overview of the derivation is given in:
# - Jonas Nycander, Andrew McC. Hogg, Leela M. Frankcombe (2008)
# Open boundary conditions for nonlinear channel Flows
@@ -488,7 +488,7 @@ end
return (max(abs(v_m_ll) + c_ll, abs(v_m_rr) + c_rr))
end
-# Specialized `DissipationLocalLaxFriedrichs` to avoid spurious dissipation in the bottom
+# Specialized `DissipationLocalLaxFriedrichs` to avoid spurious dissipation in the bottom
# topography
@inline function (dissipation::DissipationLocalLaxFriedrichs)(u_ll, u_rr,
orientation_or_normal_direction,
@@ -530,7 +530,7 @@ end
end
# Convert conservative variables to entropy variables
-# Note, only the first four are the entropy variables, the fifth entry still just carries the
+# Note, only the first four are the entropy variables, the fifth entry still just carries the
# bottom topography values for convenience
@inline function cons2entropy(u, equations::ShallowWaterTwoLayerEquations1D)
h_upper, _, h_lower, _, b = u
@@ -567,7 +567,7 @@ end
# Calculate total energy for a conservative state `cons`
@inline function energy_total(cons, equations::ShallowWaterTwoLayerEquations1D)
- h_upper, h_lower, h_v1_upper, h_v2_lower, b = cons
+ h_upper, h_v1_upper, h_lower, h_v2_lower, b = cons
# Set new variables for better readability
g = equations.gravity
rho_upper = equations.rho_upper
diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl
index 838fa2d5819..2536cfe0bf2 100644
--- a/src/solvers/dg.jl
+++ b/src/solvers/dg.jl
@@ -628,6 +628,15 @@ function compute_coefficients!(u, func, t, mesh::AbstractMesh{1}, equations, dg:
for i in eachnode(dg)
x_node = get_node_coords(cache.elements.node_coordinates, equations, dg, i,
element)
+ # Changing the node positions passed to the initial condition by the minimum
+ # amount possible with the current type of floating point numbers allows setting
+ # discontinuous initial data in a simple way. In particular, a check like `if x < x_jump`
+ # works if the jump location `x_jump` is at the position of an interface.
+ if i == 1
+ x_node = SVector(nextfloat(x_node[1]))
+ elseif i == nnodes(dg)
+ x_node = SVector(prevfloat(x_node[1]))
+ end
u_node = func(x_node, t, equations)
set_node_vars!(u, u_node, equations, dg, i, element)
end
diff --git a/test/test_structured_1d.jl b/test/test_structured_1d.jl
index a27d3c219e1..ec8c7a138d5 100644
--- a/test/test_structured_1d.jl
+++ b/test/test_structured_1d.jl
@@ -27,8 +27,8 @@ isdir(outdir) && rm(outdir, recursive=true)
@trixi_testset "elixir_advection_shockcapturing.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_shockcapturing.jl"),
- l2 = [0.08004076716881656],
- linf = [0.6342577638501385],
+ l2 = [0.08015029105233593],
+ linf = [0.610709468736576],
atol = 1.0e-5)
end
diff --git a/test/test_tree_1d.jl b/test/test_tree_1d.jl
index e37e1efc3e6..7737a93a15a 100644
--- a/test/test_tree_1d.jl
+++ b/test/test_tree_1d.jl
@@ -271,7 +271,7 @@ end
sol = solve(ode, Tsit5(), abstol=1.0e-6, reltol=1.0e-6,
save_everystep=false, callback=callbacks);
- @test analysis_callback(sol).l2 ≈ [0.00029610274971929974, 5.573684084938363e-6]
+ @test analysis_callback(sol).l2 ≈ [0.00029609575838969394, 5.5681704039507985e-6]
end
diff --git a/test/test_tree_1d_burgers.jl b/test/test_tree_1d_burgers.jl
index 788c7ab4199..8c4cfaa406d 100644
--- a/test/test_tree_1d_burgers.jl
+++ b/test/test_tree_1d_burgers.jl
@@ -22,14 +22,14 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_burgers_shock.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_burgers_shock.jl"),
- l2 = [0.4407585104869119],
- linf = [1.000000000000001])
+ l2 = [0.4422505602587537],
+ linf = [1.0000000000000009])
end
@trixi_testset "elixir_burgers_rarefaction.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_burgers_rarefaction.jl"),
- l2 = [0.40287062735307044],
- linf = [1.0042992585765542])
+ l2 = [0.4038224690923722],
+ linf = [1.0049201454652736])
end
end
diff --git a/test/test_tree_1d_euler.jl b/test/test_tree_1d_euler.jl
index 40f2a38b0e1..5fb74b80bce 100644
--- a/test/test_tree_1d_euler.jl
+++ b/test/test_tree_1d_euler.jl
@@ -22,8 +22,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_density_wave.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_density_wave.jl"),
- l2 = [0.0011482554820185795, 0.00011482554830363504, 5.741277417754598e-6],
- linf = [0.004090978306820037, 0.00040909783134346345, 2.0454891732413216e-5])
+ l2 = [0.0011482554820217855, 0.00011482554830323462, 5.741277429325267e-6],
+ linf = [0.004090978306812376, 0.0004090978313582294, 2.045489210189544e-5])
end
@trixi_testset "elixir_euler_density_wave.jl with initial_condition_constant" begin
@@ -41,14 +41,14 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
- l2 = [0.11915540925414216, 0.15489191247295198, 0.44543052524765375],
- linf = [0.2751485868543495, 0.2712764982000735, 0.9951407418216425])
+ l2 = [0.11821957357197649, 0.15330089521538678, 0.4417674632047301],
+ linf = [0.24280567569982958, 0.29130548795961936, 0.8847009003152442])
end
@trixi_testset "elixir_euler_ec.jl with flux_kennedy_gruber" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
- l2 = [0.07905582221868049, 0.10180958900546237, 0.29596551476711125],
- linf = [0.23515297345769826, 0.2958208108392532, 0.8694224308790321],
+ l2 = [0.07803455838661963, 0.10032577312032283, 0.29228156303827935],
+ linf = [0.2549869853794955, 0.3376472164661263, 0.9650477546553962],
maxiters = 10,
surface_flux = flux_kennedy_gruber,
volume_flux = flux_kennedy_gruber)
@@ -56,8 +56,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_ec.jl with flux_shima_etal" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
- l2 = [0.07909267609417114, 0.1018246500951966, 0.2959649187481973],
- linf = [0.23631829743146504, 0.2977756307879202, 0.8642794698697331],
+ l2 = [0.07800654460172655, 0.10030365573277883, 0.2921481199111959],
+ linf = [0.25408579350400395, 0.3388657679031271, 0.9776486386921928],
maxiters = 10,
surface_flux = flux_shima_etal,
volume_flux = flux_shima_etal)
@@ -65,8 +65,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_ec.jl with flux_chandrashekar" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
- l2 = [0.07905306555214126, 0.10181180378499956, 0.2959171937479504],
- linf = [0.24057642004451651, 0.29691454643616433, 0.886425723870524],
+ l2 = [0.07801923089205756, 0.10039557434912669, 0.2922210399923278],
+ linf = [0.2576521982607225, 0.3409717926625057, 0.9772961936567048],
maxiters = 10,
surface_flux = flux_chandrashekar,
volume_flux = flux_chandrashekar)
@@ -74,8 +74,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_ec.jl with flux_hll" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_ec.jl"),
- l2 = [0.07959780803600519, 0.10342491934977621, 0.2978851659149904],
- linf = [0.19228754121840885, 0.2524152253292552, 0.725604944702432],
+ l2 = [0.07852272782240548, 0.10209790867523805, 0.293873048809011],
+ linf = [0.19244768908604093, 0.2515941686151897, 0.7258000837553769],
maxiters = 10,
surface_flux = flux_hll,
volume_flux = flux_ranocha)
@@ -83,8 +83,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_shockcapturing.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing.jl"),
- l2 = [0.11665968950973675, 0.15105507394693413, 0.43503082674771115],
- linf = [0.1867400345208743, 0.24621854448555328, 0.703826406555577])
+ l2 = [0.11606096465319675, 0.15028768943458806, 0.4328230323046703],
+ linf = [0.18031710091067965, 0.2351582421501841, 0.6776805692092567])
end
@trixi_testset "elixir_euler_sedov_blast_wave.jl" begin
@@ -96,8 +96,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_sedov_blast_wave_pure_fv.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave_pure_fv.jl"),
- l2 = [1.075075094036344, 0.06766902169711514, 0.9221426570128292],
- linf = [3.3941512671408542, 0.16862631133303882, 2.6572394126490315],
+ l2 = [1.0735456065491455, 0.07131078703089379, 0.9205739468590453],
+ linf = [3.4296365168219216, 0.17635583964559245, 2.6574584326179505],
# Let this test run longer to cover some lines in flux_hllc
coverage_override = (maxiters=10^5, tspan=(0.0, 0.1)))
end
@@ -129,22 +129,22 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_euler_blast_wave.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave.jl"),
- l2 = [0.21651329948737183, 0.28091709900008616, 0.5580778880050432],
- linf = [1.513525457073142, 1.5328754303137992, 2.0467706106669556],
+ l2 = [0.21934822867340323, 0.28131919126002686, 0.554361702716662],
+ linf = [1.5180897390290355, 1.3967085956620369, 2.0663825294019595],
maxiters = 30)
end
@trixi_testset "elixir_euler_blast_wave_neuralnetwork_perssonperaire.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_neuralnetwork_perssonperaire.jl"),
- l2 = [2.13605618e-01, 2.79953055e-01, 5.54424459e-01],
- linf = [1.55151701e+00, 1.55696782e+00, 2.05525953e+00],
+ l2 = [0.21814833203212694, 0.2818328665444332, 0.5528379124720818],
+ linf = [1.5548653877320868, 1.4474018998129738, 2.071919577393772],
maxiters = 30)
end
@trixi_testset "elixir_euler_blast_wave_neuralnetwork_rayhesthaven.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave_neuralnetwork_rayhesthaven.jl"),
- l2 = [2.18148857e-01, 2.83182959e-01, 5.59096194e-01],
- linf = [1.62706876e+00, 1.61680275e+00, 2.05876517e+00],
+ l2 = [0.22054468879127423, 0.2828269190680846, 0.5542369885642424],
+ linf = [1.5623359741479623, 1.4290121654488288, 2.1040405133123072],
maxiters = 30)
end
end
diff --git a/test/test_tree_1d_eulermulti.jl b/test/test_tree_1d_eulermulti.jl
index eac54a9372c..e880f98e2d0 100644
--- a/test/test_tree_1d_eulermulti.jl
+++ b/test/test_tree_1d_eulermulti.jl
@@ -11,39 +11,47 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_eulermulti_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_ec.jl"),
- l2 = [1.54891912e-01, 4.45430525e-01, 1.70222013e-02, 3.40444026e-02, 6.80888053e-02],
- linf = [2.71276498e-01, 9.95140742e-01, 3.93069410e-02, 7.86138820e-02, 1.57227764e-01])
+ l2 = [0.15330089521538684, 0.4417674632047301, 0.016888510510282385, 0.03377702102056477,
+ 0.06755404204112954],
+ linf = [0.29130548795961864, 0.8847009003152357, 0.034686525099975274, 0.06937305019995055,
+ 0.1387461003999011])
end
@trixi_testset "elixir_eulermulti_es.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_es.jl"),
- l2 = [1.53387916e-01, 4.41585576e-01, 3.93605635e-02, 7.87211270e-02],
- linf = [2.49632117e-01, 7.21088064e-01, 6.38328770e-02, 1.27665754e-01])
+ l2 = [0.1522380497572071, 0.43830846465313206, 0.03907262116499431, 0.07814524232998862],
+ linf = [0.24939193075537294, 0.7139395740052739, 0.06324208768391237, 0.12648417536782475])
end
@trixi_testset "elixir_eulermulti_convergence_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_convergence_ec.jl"),
- l2 = [8.57523604e-05, 1.63878043e-04, 1.94126993e-05, 3.88253986e-05],
- linf = [3.05932773e-04, 6.24480393e-04, 7.25312144e-05, 1.45062429e-04])
+ l2 = [8.575236038539227e-5, 0.00016387804318585358, 1.9412699303977585e-5, 3.882539860795517e-5],
+ linf = [0.00030593277277124464, 0.0006244803933350696, 7.253121435135679e-5, 0.00014506242870271358])
end
@trixi_testset "elixir_eulermulti_convergence_es.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_convergence_es.jl"),
- l2 = [1.8983933794407234e-5, 6.207744299844731e-5, 1.5466205761868047e-6, 3.0932411523736094e-6, 6.186482304747219e-6, 1.2372964609494437e-5],
- linf = [0.00012014372605895218, 0.0003313207215800418, 6.50836791016296e-6, 1.301673582032592e-5, 2.603347164065184e-5, 5.206694328130368e-5])
+ l2 = [1.8983933794407234e-5, 6.207744299844731e-5, 1.5466205761868047e-6, 3.0932411523736094e-6,
+ 6.186482304747219e-6, 1.2372964609494437e-5],
+ linf = [0.00012014372605895218, 0.0003313207215800418, 6.50836791016296e-6, 1.301673582032592e-5,
+ 2.603347164065184e-5, 5.206694328130368e-5])
end
@trixi_testset "elixir_eulermulti_convergence_es.jl with flux_chandrashekar" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_convergence_es.jl"),
- l2 = [1.88845048e-05, 5.49106005e-05, 9.42673716e-07, 1.88534743e-06, 3.77069486e-06, 7.54138973e-06],
- linf = [1.16223512e-04, 3.07922197e-04, 3.21774233e-06, 6.43548465e-06, 1.28709693e-05, 2.57419386e-05],
+ l2 = [1.888450477353845e-5, 5.4910600482795386e-5, 9.426737161533622e-7, 1.8853474323067245e-6,
+ 3.770694864613449e-6, 7.541389729226898e-6],
+ linf = [0.00011622351152063004, 0.0003079221967086099, 3.2177423254231563e-6, 6.435484650846313e-6,
+ 1.2870969301692625e-5, 2.574193860338525e-5],
volume_flux = flux_chandrashekar)
end
@trixi_testset "elixir_eulermulti_two_interacting_blast_waves.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_two_interacting_blast_waves.jl"),
- l2 = [1.28886761e+00, 8.27133526e+01, 3.50680272e-03, 1.36987844e-02, 1.91795185e-02],
- linf = [2.96413045e+01, 1.32258448e+03, 9.19191937e-02, 3.10929710e-01, 4.41798976e-01],
+ l2 = [1.288867611915533, 82.71335258388848, 0.00350680272313187, 0.013698784353152794,
+ 0.019179518517518084],
+ linf = [29.6413044707026, 1322.5844802186496, 0.09191919374782143, 0.31092970966717925,
+ 0.4417989757182038],
tspan = (0.0, 0.0001))
end
diff --git a/test/test_tree_1d_mhd.jl b/test/test_tree_1d_mhd.jl
index 938959831c1..e3a0cda3250 100644
--- a/test/test_tree_1d_mhd.jl
+++ b/test/test_tree_1d_mhd.jl
@@ -32,14 +32,14 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_mhd_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_ec.jl"),
- l2 = [5.86009540e-02, 8.16048158e-02, 5.46791194e-02, 5.46791194e-02, 1.54509265e-01, 4.13046273e-17, 5.47637521e-02, 5.47637521e-02],
- linf = [1.10014999e-01, 1.81982581e-01, 9.13611439e-02, 9.13611439e-02, 4.23831370e-01, 1.11022302e-16, 9.93731761e-02, 9.93731761e-02])
+ l2 = [0.05815183849746399, 0.08166807325621023, 0.054659228513541165, 0.054659228513541165, 0.15578125987042743, 4.130462730494e-17, 0.05465258887150046, 0.05465258887150046],
+ linf = [0.12165312668363826, 0.1901920742264952, 0.10059813883022554, 0.10059813883022554, 0.44079257431070706, 1.1102230246251565e-16, 0.10528911365809579, 0.10528911365809579])
end
@trixi_testset "elixir_mhd_briowu_shock_tube.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_briowu_shock_tube.jl"),
- l2 = [0.17764301067932906, 0.19693621875378622, 0.3635136528288642, 0.0, 0.3757321708837591, 8.593007507325741e-16, 0.36473438378159656, 0.0],
- linf = [0.5601530250396535, 0.43867368105486537, 1.0960903616351099, 0.0, 1.0551794137886303, 4.107825191113079e-15, 1.5374410890043144, 0.0],
+ l2 = [0.17477712356961989, 0.19489623595086944, 0.3596546157640463, 0.0, 0.3723215736814466, 1.2060075775846403e-15, 0.36276754492568164, 0.0],
+ linf = [0.5797109945880677, 0.4372991899547103, 1.0906536287185835, 0.0, 1.0526758874956808, 5.995204332975845e-15, 1.5122922036932964, 0.0],
coverage_override = (maxiters=6,))
end
@@ -51,8 +51,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_mhd_ryujones_shock_tube.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_ryujones_shock_tube.jl"),
- l2 = [2.34809441e-01, 3.92255943e-01, 8.23575546e-02, 1.75599624e-01, 9.61613519e-01, 6.60825891e-17, 2.15346454e-01, 1.07006529e-01],
- linf = [6.40732148e-01, 9.44889516e-01, 3.54932707e-01, 8.54060243e-01, 2.07757711e+00, 1.11022302e-16, 4.92584725e-01, 2.49526561e-01],
+ l2 = [0.23469781891518154, 0.3916675299696121, 0.08245195301016353, 0.1745346945706147, 0.9606363432904367, 6.608258910237605e-17, 0.21542929107153735, 0.10705457908737925],
+ linf = [0.6447951791685409, 0.9461857095377463, 0.35074627554617605, 0.8515177411529542, 2.0770652030507053, 1.1102230246251565e-16, 0.49670855513788204, 0.24830199967863564],
tspan = (0.0, 0.1))
end
diff --git a/test/test_tree_1d_mhdmulti.jl b/test/test_tree_1d_mhdmulti.jl
index 2985e6d5663..5214ed26d38 100644
--- a/test/test_tree_1d_mhdmulti.jl
+++ b/test/test_tree_1d_mhdmulti.jl
@@ -11,33 +11,33 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_mhdmulti_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhdmulti_ec.jl"),
- l2 = [0.08160481582829862, 0.05467911944326103, 0.05467911944326103, 0.15450926504459692,
- 4.130462730494e-17, 0.054763752050210085, 0.054763752050210085, 0.008371564857135208,
- 0.016743129714270416, 0.03348625942854083],
- linf = [0.18198258075330706, 0.09136114386311774, 0.09136114386311774, 0.423831369951313,
- 1.1102230246251565e-16, 0.09937317613143604, 0.09937317613143604, 0.0157164284712992,
- 0.0314328569425984, 0.0628657138851968])
+ l2 = [0.08166807325620999, 0.054659228513541616, 0.054659228513541616, 0.15578125987042812,
+ 4.130462730494e-17, 0.054652588871500665, 0.054652588871500665, 0.008307405499637766,
+ 0.01661481099927553, 0.03322962199855106],
+ linf = [0.19019207422649645, 0.10059813883022888, 0.10059813883022888, 0.4407925743107146,
+ 1.1102230246251565e-16, 0.10528911365809623, 0.10528911365809623, 0.01737901809766182,
+ 0.03475803619532364, 0.06951607239064728])
end
@trixi_testset "elixir_mhdmulti_ec.jl with flux_derigs_etal" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhdmulti_ec.jl"),
- l2 = [0.08153372259925547, 0.05464109003345891, 0.05464109003345891, 0.1540576724164453,
- 4.130462730494e-17, 0.054734930802131036, 0.054734930802131036, 0.008391254781284321,
- 0.016782509562568642, 0.033565019125137284],
- linf = [0.17492544007323832, 0.09029632168248182, 0.09029632168248182, 0.40798609353896564,
- 1.1102230246251565e-16, 0.09872923637833075, 0.09872923637833075, 0.01609818847160674,
- 0.03219637694321348, 0.06439275388642696],
+ l2 = [0.08151404166186461, 0.054640238302693274, 0.054640238302693274, 0.15536125426328573,
+ 4.130462730494e-17, 0.054665489963920275, 0.054665489963920275, 0.008308349501359825,
+ 0.01661669900271965, 0.0332333980054393],
+ linf = [0.1824424257860952, 0.09734687137001484, 0.09734687137001484, 0.4243089502087325,
+ 1.1102230246251565e-16, 0.09558639591092555, 0.09558639591092555, 0.017364773041550624,
+ 0.03472954608310125, 0.0694590921662025],
volume_flux = flux_derigs_etal)
end
@trixi_testset "elixir_mhdmulti_es.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhdmulti_es.jl"),
- l2 = [0.07968782477167513, 0.05398115008116676, 0.05398115008116676, 0.15015281822439228,
- 4.130462730494e-17, 0.053629890024921495, 0.053629890024921495, 0.008279068245579706,
- 0.016558136491159413, 0.033116272982318826],
- linf = [0.14118014632124837, 0.07820697032983395, 0.07820697032983395, 0.3390558674728652,
- 1.1102230246251565e-16, 0.06998787893467828, 0.06998787893467828, 0.014943825414763745,
- 0.02988765082952749, 0.05977530165905498])
+ l2 = [0.07994082660130175, 0.053940174914031976, 0.053940174914031976, 0.15165513559250643,
+ 4.130462730494e-17, 0.05363207135290325, 0.05363207135290325, 0.008258265884659555,
+ 0.01651653176931911, 0.03303306353863822],
+ linf = [0.14101014428198477, 0.07762441749521025, 0.07762441749521025, 0.3381334453289866,
+ 1.1102230246251565e-16, 0.07003646400675223, 0.07003646400675223, 0.014962483760600165,
+ 0.02992496752120033, 0.05984993504240066])
end
@trixi_testset "elixir_mhdmulti_convergence.jl" begin
@@ -52,12 +52,12 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_mhdmulti_briowu_shock_tube.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhdmulti_briowu_shock_tube.jl"),
- l2 = [0.1946577804333822, 0.3591196215528672, 0.0, 0.36875476066849383,
- 4.7644020131827105e-16, 0.36668249926193885, 0.0, 0.05775369214541893,
- 0.11550738429083786],
- linf = [0.4345551123140612, 1.0874941615375844, 0.0, 1.0493729052116585,
- 3.219646771412954e-15, 1.5160434573973656, 0.0, 0.18616213071936066,
- 0.3723242614387213],
+ l2 = [0.1877830835572639, 0.3455841730726793, 0.0, 0.35413123388836687,
+ 8.745556626531982e-16, 0.3629920109231055, 0.0, 0.05329005553971236,
+ 0.10658011107942472],
+ linf = [0.4288187627971754, 1.0386547815614993, 0.0, 0.9541678878162702,
+ 5.773159728050814e-15, 1.4595119339458051, 0.0, 0.18201910908829552,
+ 0.36403821817659104],
coverage_override = (maxiters=6,))
end
diff --git a/test/test_tree_1d_shallowwater.jl b/test/test_tree_1d_shallowwater.jl
index f8901a3dcb6..1c3bac1fab6 100644
--- a/test/test_tree_1d_shallowwater.jl
+++ b/test/test_tree_1d_shallowwater.jl
@@ -10,22 +10,30 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@testset "Shallow Water" begin
@trixi_testset "elixir_shallowwater_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_ec.jl"),
- l2 = [0.8122354510732459, 1.01586214815876, 0.43404255061704217],
- linf = [1.4883285368551107, 3.8717508164234276, 1.7711213427919539],
+ l2 = [0.244729018751225, 0.8583565222389505, 0.07330427577586297],
+ linf = [2.1635021283528504, 3.8717508164234453, 1.7711213427919539],
+ tspan = (0.0, 0.25))
+ end
+
+ @trixi_testset "elixir_shallowwater_ec.jl with initial_condition_weak_blast_wave" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_ec.jl"),
+ l2 = [0.39464782107209717, 2.03880864210846, 4.1623084150546725e-10],
+ linf = [0.778905801278281, 3.2409883402608273, 7.419800190922032e-10],
+ initial_condition=initial_condition_weak_blast_wave,
tspan = (0.0, 0.25))
end
@trixi_testset "elixir_shallowwater_well_balanced.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced.jl"),
- l2 = [1.2427984842961743, 1.0332499675061871e-14, 1.2427984842961741],
- linf = [1.619041478244762, 1.266865149831811e-14, 1.6190414782447629],
+ l2 = [0.10416666834254829, 1.4352935256803184e-14, 0.10416666834254838],
+ linf = [1.9999999999999996, 3.248036646353028e-14, 2.0],
tspan = (0.0, 0.25))
end
@trixi_testset "elixir_shallowwater_well_balanced.jl with FluxHydrostaticReconstruction" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced.jl"),
- l2 = [1.2427984842961743, 1.2663646513352053e-14, 1.2427984842961741],
- linf = [1.619041478244762, 2.4566658711604395e-14, 1.6190414782447629],
+ l2 = [0.10416666834254835, 1.1891029971551825e-14, 0.10416666834254838],
+ linf = [2.0000000000000018, 2.4019608337954543e-14, 2.0],
surface_flux=(FluxHydrostaticReconstruction(flux_lax_friedrichs, hydrostatic_reconstruction_audusse_etal), flux_nonconservative_audusse_etal),
tspan = (0.0, 0.25))
end
@@ -59,14 +67,14 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
tspan = (0.0, 0.025))
end
- @trixi_testset "elixir_shallowwater_well_balanced_nonperiodic.jl with dirichlet boundary" begin
+ @trixi_testset "elixir_shallowwater_well_balanced_nonperiodic.jl with Dirichlet boundary" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced_nonperiodic.jl"),
l2 = [1.725964362045055e-8, 5.0427180314307505e-16, 1.7259643530442137e-8],
linf = [3.844551077492042e-8, 3.469453422316143e-15, 3.844551077492042e-8],
tspan = (0.0, 0.25))
end
- @trixi_testset "elixir_shallowwater_well_nonperiodic.jl with wall boundary" begin
+ @trixi_testset "elixir_shallowwater_well_balanced_nonperiodic.jl with wall boundary" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced_nonperiodic.jl"),
l2 = [1.7259643614361866e-8, 3.5519018243195145e-16, 1.7259643530442137e-8],
linf = [3.844551010878661e-8, 9.846474508971374e-16, 3.844551077492042e-8],
@@ -76,8 +84,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_shallowwater_shock_capturing.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_shock_capturing.jl"),
- l2 = [0.2884024818919076, 0.5252262013521178, 0.2890348477852955],
- linf = [0.7565706154863958, 2.076621603471687, 0.8646939843534258],
+ l2 = [0.07424140641160326, 0.2148642632748155, 0.0372579849000542],
+ linf = [1.1209754279344226, 1.3230788645853582, 0.8646939843534251],
tspan = (0.0, 0.05))
end
end
diff --git a/test/test_tree_1d_shallowwater_twolayer.jl b/test/test_tree_1d_shallowwater_twolayer.jl
index 6c0ad2941cc..0d8a83806f9 100644
--- a/test/test_tree_1d_shallowwater_twolayer.jl
+++ b/test/test_tree_1d_shallowwater_twolayer.jl
@@ -38,9 +38,9 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@trixi_testset "elixir_shallowwater_twolayer_dam_break.jl with flux_lax_friedrichs" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_twolayer_dam_break.jl"),
- l2 = [0.35490827242437256, 1.6715402155795918, 0.6960264969949427,
- 0.9351481433409805, 0.7938172946965545],
- linf = [0.6417127471419837, 1.9742107034120873, 1.135774587483082, 1.236125279347084, 1.1],
+ l2 = [0.10010269243463918, 0.5668733957648654, 0.08759617327649398,
+ 0.4538443183566172, 0.013638618139749523],
+ linf = [0.5854202777756559, 2.1278930820498934, 0.5193686074348809, 1.8071213168086229, 0.5],
surface_flux = (flux_lax_friedrichs, flux_nonconservative_fjordholm_etal),
tspan = (0.0, 0.25))
end
From 9ea37cce8f510dcc135ad568280fd770a693ba10 Mon Sep 17 00:00:00 2001
From: David Knapp
Date: Mon, 19 Jun 2023 16:16:48 +0200
Subject: [PATCH 006/263] Add a pre-commit script and an adapted formatting
file (#1534)
* Add a pre-commit script and an adapted formatting file
* Formatting
I am aware of the irony
* Fix typo
* Added documentation
* Fix Typo
---
docs/src/styleguide.md | 11 ++++++++++
utils/pre-commit | 40 ++++++++++++++++++++++++++++++++++++
utils/trixi-format-file.jl | 42 ++++++++++++++++++++++++++++++++++++++
3 files changed, 93 insertions(+)
create mode 100755 utils/pre-commit
create mode 100755 utils/trixi-format-file.jl
diff --git a/docs/src/styleguide.md b/docs/src/styleguide.md
index de367c086cc..9e6b6a8c265 100644
--- a/docs/src/styleguide.md
+++ b/docs/src/styleguide.md
@@ -65,3 +65,14 @@ utils/trixi-format.jl
```
You can get more information about using the convenience script by running it with the
`--help`/`-h` flag.
+
+### Checking formatting before committing
+It can be convenient to check the formatting of source code automatically before each commit.
+We use git-hooks for it and provide a `pre-commit` script in the `utils` folder. The script uses
+[JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl) just like formatting script that
+runs over the whole Trixi.jl directory.
+You can copy the `pre-commit`-script into `.git/hooks/pre-commit` and it will check your formatting
+before each commit. If errors are found the commit is aborted and you can add the corrections via
+```shell
+git add -p
+```
\ No newline at end of file
diff --git a/utils/pre-commit b/utils/pre-commit
new file mode 100755
index 00000000000..2977b9a200b
--- /dev/null
+++ b/utils/pre-commit
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Copy this file into .git/hooks/pre-commit to execute before each commit.
+# It checks and corrects the format for each file.
+# If incorrect formatting is found you can add the correction via git add -p
+
+echo "Checking format before committing"
+
+if git ref-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=280fc57fade28e35046c3e884e587ffef05d3867
+fi
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Create a list of files to format.
+files=()
+
+for file in `git diff --cached --name-only`
+do
+ # only indent existing files, this is necessary since if we rename or delete
+ # a file it is added to the committed files and we thus would try to indent a
+ # nonexisting file.
+ if [ ! -e $file ]
+ then
+ continue
+ fi
+ # We only indent .jl files
+ FILE_ENDING="${file##*.}"
+ if [ $FILE_ENDING = "jl" ]
+ then
+ files+=($file)
+ fi
+done
+
+julia utils/trixi-format-file.jl "${files[@]}"
diff --git a/utils/trixi-format-file.jl b/utils/trixi-format-file.jl
new file mode 100755
index 00000000000..c4d8e7c9032
--- /dev/null
+++ b/utils/trixi-format-file.jl
@@ -0,0 +1,42 @@
+#!/usr/bin/env julia
+
+using Pkg
+Pkg.activate(; temp = true, io = devnull)
+Pkg.add("JuliaFormatter"; preserve = PRESERVE_ALL, io = devnull)
+
+using JuliaFormatter: format_file
+
+function main()
+ # Show help
+ if "-h" in ARGS || "--help" in ARGS
+ println("usage: trixi-format.jl PATH [PATH...]")
+ println()
+ println("positional arguments:")
+ println()
+ println(" PATH One or more paths (directories or files) to format. Default: '.'")
+ return nothing
+ end
+
+ file_list = ARGS
+ if isempty(ARGS)
+ exit(0)
+ end
+ non_formatted_files = Vector{String}()
+ for file in file_list
+ println("Checking file " * file)
+ if !format_file(file)
+ push!(non_formatted_files, file)
+ end
+ end
+ if isempty(non_formatted_files)
+ exit(0)
+ else
+ @error "Some files have not been formatted! Formatting has been applied, run 'git add -p' to update changes."
+ for file in non_formatted_files
+ println(file)
+ end
+ exit(1)
+ end
+end
+
+main()
From 11f6fa786340534c10f6356886e19d6291cf618a Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Mon, 19 Jun 2023 16:57:26 +0200
Subject: [PATCH 007/263] set version to v0.5.29
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 9d51e4dcffc..303e97d6324 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.29-pre"
+version = "0.5.29"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From e84946146c022588ef2d4260e4fa4c34b1ab2d9d Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Mon, 19 Jun 2023 16:57:48 +0200
Subject: [PATCH 008/263] set development version to v0.5.30-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 303e97d6324..d3983262591 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.29"
+version = "0.5.30-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 9519c26b2fb88398ab12ddc4f3c6acc62c02a426 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 19 Jun 2023 21:24:37 +0200
Subject: [PATCH 009/263] Bump crate-ci/typos from 1.15.0 to 1.15.1 (#1537)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.15.0 to 1.15.1.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.15.0...v1.15.1)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index bc324c689bc..75886465f85 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.15.0
+ uses: crate-ci/typos@v1.15.1
From 830d1d7a5fcc76d9ee205c72abff15f328372d02 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Tue, 20 Jun 2023 09:02:05 +0200
Subject: [PATCH 010/263] Fix pre-commit (#1536)
* fix pre-commit
* consistent indent
---
utils/pre-commit | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/utils/pre-commit b/utils/pre-commit
index 2977b9a200b..73ad061baef 100755
--- a/utils/pre-commit
+++ b/utils/pre-commit
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
# Copy this file into .git/hooks/pre-commit to execute before each commit.
# It checks and corrects the format for each file.
@@ -8,10 +8,10 @@ echo "Checking format before committing"
if git ref-parse --verify HEAD >/dev/null 2>&1
then
- against=HEAD
+ against=HEAD
else
- # Initial commit: diff against an empty tree object
- against=280fc57fade28e35046c3e884e587ffef05d3867
+ # Initial commit: diff against an empty tree object
+ against=280fc57fade28e35046c3e884e587ffef05d3867
fi
# Redirect output to stderr.
@@ -22,19 +22,19 @@ files=()
for file in `git diff --cached --name-only`
do
- # only indent existing files, this is necessary since if we rename or delete
- # a file it is added to the committed files and we thus would try to indent a
- # nonexisting file.
- if [ ! -e $file ]
- then
- continue
- fi
- # We only indent .jl files
- FILE_ENDING="${file##*.}"
- if [ $FILE_ENDING = "jl" ]
- then
- files+=($file)
- fi
+ # only indent existing files, this is necessary since if we rename or delete
+ # a file it is added to the committed files and we thus would try to indent a
+ # nonexisting file.
+ if [ ! -e $file ]
+ then
+ continue
+ fi
+ # We only indent .jl files
+ FILE_ENDING="${file##*.}"
+ if [ $FILE_ENDING = "jl" ]
+ then
+ files+=($file)
+ fi
done
julia utils/trixi-format-file.jl "${files[@]}"
From 196f139069370bd1d450b1d3865170c447c2b74f Mon Sep 17 00:00:00 2001
From: Johannes Markert <10619309+jmark@users.noreply.github.com>
Date: Tue, 20 Jun 2023 09:12:02 +0200
Subject: [PATCH 011/263] Update styleguide.md (#1538)
Added missing apostrophe.
---
docs/src/styleguide.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/src/styleguide.md b/docs/src/styleguide.md
index 9e6b6a8c265..c2562a0c651 100644
--- a/docs/src/styleguide.md
+++ b/docs/src/styleguide.md
@@ -55,7 +55,7 @@ julia -e 'using Pkg; Pkg.add("JuliaFormatter")'
```
You can then recursively format all Julia files in the Trixi.jl repo by executing
```shell
-julia -e 'using JuliaFormatter; format(".")
+julia -e 'using JuliaFormatter; format(".")'
```
from inside the Trixi.jl repository. For convenience, there is also a script you can
directly run from your terminal shell, which will automatically install JuliaFormatter in a
@@ -75,4 +75,4 @@ You can copy the `pre-commit`-script into `.git/hooks/pre-commit` and it will ch
before each commit. If errors are found the commit is aborted and you can add the corrections via
```shell
git add -p
-```
\ No newline at end of file
+```
From 3303ed8c0b8af262a16e96f2ad6dcd84034bbe7b Mon Sep 17 00:00:00 2001
From: Johannes Markert <10619309+jmark@users.noreply.github.com>
Date: Tue, 20 Jun 2023 11:45:41 +0200
Subject: [PATCH 012/263] Update styleguide.md (#1540)
* Update styleguide.md
Updated the formatting command line fixing the issue https://github.com/trixi-framework/Trixi.jl/issues/1539
* Update styleguide.md
Removed superfluous whitespace.
---
docs/src/styleguide.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/src/styleguide.md b/docs/src/styleguide.md
index c2562a0c651..60e227204ca 100644
--- a/docs/src/styleguide.md
+++ b/docs/src/styleguide.md
@@ -53,9 +53,9 @@ of your PR), you need to install JuliaFormatter.jl first by running
```shell
julia -e 'using Pkg; Pkg.add("JuliaFormatter")'
```
-You can then recursively format all Julia files in the Trixi.jl repo by executing
+You can then recursively format the core Julia files in the Trixi.jl repo by executing
```shell
-julia -e 'using JuliaFormatter; format(".")'
+julia -e 'using JuliaFormatter; format(["benchmark", "ext", "src", "utils"])'
```
from inside the Trixi.jl repository. For convenience, there is also a script you can
directly run from your terminal shell, which will automatically install JuliaFormatter in a
From 414dafa310738414d43d16d0393ca13588b2c101 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Wed, 21 Jun 2023 11:31:35 +0200
Subject: [PATCH 013/263] Fix some typos in docs (#1541)
* fix some typos in docs
* fix copy mistake
* update link
---
docs/src/github-git.md | 4 ++--
src/auxiliary/auxiliary.jl | 10 +++++-----
src/auxiliary/math.jl | 6 +++++-
src/auxiliary/mpi.jl | 4 ++--
src/equations/acoustic_perturbation_2d.jl | 2 +-
src/equations/compressible_euler_2d.jl | 2 +-
src/equations/compressible_euler_3d.jl | 2 +-
src/equations/compressible_euler_multicomponent_1d.jl | 8 ++++----
src/equations/compressible_euler_multicomponent_2d.jl | 8 ++++----
src/equations/compressible_navier_stokes_2d.jl | 8 ++++----
src/equations/compressible_navier_stokes_3d.jl | 4 ++--
src/equations/hyperbolic_diffusion_2d.jl | 2 +-
src/equations/hyperbolic_diffusion_3d.jl | 2 +-
src/equations/ideal_glm_mhd_3d.jl | 2 +-
src/equations/shallow_water_two_layer_1d.jl | 2 +-
src/equations/shallow_water_two_layer_2d.jl | 3 ++-
src/meshes/structured_mesh.jl | 4 ++--
src/solvers/dgmulti/types.jl | 6 +++---
src/solvers/dgsem_tree/indicators.jl | 2 +-
19 files changed, 43 insertions(+), 38 deletions(-)
diff --git a/docs/src/github-git.md b/docs/src/github-git.md
index ad5991d87af..57b63073e79 100644
--- a/docs/src/github-git.md
+++ b/docs/src/github-git.md
@@ -112,7 +112,7 @@ branch, and the corresponding pull request will be updated automatically.
Please note that a review has nothing to do with the lack of experience of the
person developing changes: We try to review all code before it gets added to
`main`, even from the most experienced developers. This is good practice and
-helps to keep the error rate low while ensuring the the code is developed in a
+helps to keep the error rate low while ensuring that the code is developed in a
consistent fashion. Furthermore, do not take criticism of your code personally -
we just try to keep Trixi.jl as accessible and easy to use for everyone.
@@ -121,7 +121,7 @@ Once your branch is reviewed and declared ready for merging by the reviewer,
make sure that all the latest changes have been pushed. Then, one of the
developers will merge your PR. If you are one of the developers, you can also go
to the pull request page on GitHub and and click on **Merge pull request**.
-Voilá, you are done! Your branch will have been merged to
+Voilà, you are done! Your branch will have been merged to
`main` and the source branch will have been deleted in the GitHub repository
(if you are not working in your own fork).
diff --git a/src/auxiliary/auxiliary.jl b/src/auxiliary/auxiliary.jl
index 115d055c0ca..1f7d30d6aa8 100644
--- a/src/auxiliary/auxiliary.jl
+++ b/src/auxiliary/auxiliary.jl
@@ -132,7 +132,7 @@ end
default_example()
Return the path to an example elixir that can be used to quickly see Trixi.jl in action on a
-[`TreeMesh`]@(ref). See also [`examples_dir`](@ref) and [`get_examples`](@ref).
+[`TreeMesh`](@ref). See also [`examples_dir`](@ref) and [`get_examples`](@ref).
"""
function default_example()
joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_basic.jl")
@@ -142,7 +142,7 @@ end
default_example_unstructured()
Return the path to an example elixir that can be used to quickly see Trixi.jl in action on an
-[`UnstructuredMesh2D`]@(ref). This simulation is run on the example curved, unstructured mesh
+[`UnstructuredMesh2D`](@ref). This simulation is run on the example curved, unstructured mesh
given in the Trixi.jl documentation regarding unstructured meshes.
"""
function default_example_unstructured()
@@ -155,7 +155,7 @@ end
Return the default options for OrdinaryDiffEq's `solve`. Pass `ode_default_options()...` to `solve`
to only return the solution at the final time and enable **MPI aware** error-based step size control,
whenever MPI is used.
-For example, use `solve(ode, alg; ode_default_options()...)`
+For example, use `solve(ode, alg; ode_default_options()...)`.
"""
function ode_default_options()
if mpi_isparallel()
@@ -213,8 +213,8 @@ might be provided by other packages such as [Polyester.jl](https://github.com/Ju
This macro does not necessarily work for general `for` loops. For example,
it does not necessarily support general iterables such as `eachline(filename)`.
-Some discussion can be found at https://discourse.julialang.org/t/overhead-of-threads-threads/53964
-and https://discourse.julialang.org/t/threads-threads-with-one-thread-how-to-remove-the-overhead/58435.
+Some discussion can be found at [https://discourse.julialang.org/t/overhead-of-threads-threads/53964](https://discourse.julialang.org/t/overhead-of-threads-threads/53964)
+and [https://discourse.julialang.org/t/threads-threads-with-one-thread-how-to-remove-the-overhead/58435](https://discourse.julialang.org/t/threads-threads-with-one-thread-how-to-remove-the-overhead/58435).
"""
macro threaded(expr)
# Use `esc(quote ... end)` for nested macro calls as suggested in
diff --git a/src/auxiliary/math.jl b/src/auxiliary/math.jl
index 27c1bed5ca4..4ecf7dd3fcc 100644
--- a/src/auxiliary/math.jl
+++ b/src/auxiliary/math.jl
@@ -51,7 +51,7 @@ Given ε = 1.0e-4, we use the following algorithm.
- Agner Fog.
Lists of instruction latencies, throughputs and micro-operation breakdowns
for Intel, AMD, and VIA CPUs.
- https://www.agner.org/optimize/instruction_tables.pdf
+ [https://www.agner.org/optimize/instruction_tables.pdf](https://www.agner.org/optimize/instruction_tables.pdf)
"""
@inline function ln_mean(x, y)
epsilon_f2 = 1.0e-4
@@ -166,8 +166,10 @@ checks necessary in the presence of `NaN`s (or signed zeros).
# Examples
+```jldoctest
julia> max(2, 5, 1)
5
+```
"""
@inline max(args...) = @fastmath max(args...)
@@ -183,8 +185,10 @@ checks necessary in the presence of `NaN`s (or signed zeros).
# Examples
+```jldoctest
julia> min(2, 5, 1)
1
+```
"""
@inline min(args...) = @fastmath min(args...)
diff --git a/src/auxiliary/mpi.jl b/src/auxiliary/mpi.jl
index 2c485b4832c..c85c23670b0 100644
--- a/src/auxiliary/mpi.jl
+++ b/src/auxiliary/mpi.jl
@@ -72,7 +72,7 @@ You must pass this function as a keyword argument
to OrdinaryDiffEq.jl's `solve` when using error-based step size control with MPI
parallel execution of Trixi.jl.
-See the "Advanced Adaptive Stepsize Control" section of the [documentation](https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/)
+See the "Advanced Adaptive Stepsize Control" section of the [documentation](https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/).
"""
ode_norm(u::Number, t) = @fastmath abs(u)
function ode_norm(u::AbstractArray, t)
@@ -125,6 +125,6 @@ You should pass this function as a keyword argument
to OrdinaryDiffEq.jl's `solve` when using error-based step size control with MPI
parallel execution of Trixi.jl.
-See the "Miscellaneous" section of the [documentation](https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/)
+See the "Miscellaneous" section of the [documentation](https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/).
"""
ode_unstable_check(dt, u, semi, t) = isnan(dt)
diff --git a/src/equations/acoustic_perturbation_2d.jl b/src/equations/acoustic_perturbation_2d.jl
index 786630a14c7..f4ce770e1e9 100644
--- a/src/equations/acoustic_perturbation_2d.jl
+++ b/src/equations/acoustic_perturbation_2d.jl
@@ -145,7 +145,7 @@ function initial_condition_convergence_test(x, t,
end
"""
- source_terms_convergence_test(u, x, t, equations::AcousticPerturbationEquations2D)
+ source_terms_convergence_test(u, x, t, equations::AcousticPerturbationEquations2D)
Source terms used for convergence tests in combination with
[`initial_condition_convergence_test`](@ref).
diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl
index 66e3c7bff84..89f04ef1e05 100644
--- a/src/equations/compressible_euler_2d.jl
+++ b/src/equations/compressible_euler_2d.jl
@@ -31,7 +31,7 @@ The compressible Euler equations
```
for an ideal gas with ratio of specific heats `gamma`
in two space dimensions.
-Here, ``\rho`` is the density, ``v_1``,`v_2` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and
+Here, ``\rho`` is the density, ``v_1``, ``v_2`` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and
```math
p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2) \right)
```
diff --git a/src/equations/compressible_euler_3d.jl b/src/equations/compressible_euler_3d.jl
index c16a454b176..cd081cfc42a 100644
--- a/src/equations/compressible_euler_3d.jl
+++ b/src/equations/compressible_euler_3d.jl
@@ -36,7 +36,7 @@ The compressible Euler equations
```
for an ideal gas with ratio of specific heats `gamma`
in three space dimensions.
-Here, ``\rho`` is the density, ``v_1``,`v_2`, `v_3` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and
+Here, ``\rho`` is the density, ``v_1``, ``v_2``, ``v_3`` the velocities, ``e`` the specific total energy **rather than** specific internal energy, and
```math
p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2+v_3^2) \right)
```
diff --git a/src/equations/compressible_euler_multicomponent_1d.jl b/src/equations/compressible_euler_multicomponent_1d.jl
index 4a50d60471a..23ac222b976 100644
--- a/src/equations/compressible_euler_multicomponent_1d.jl
+++ b/src/equations/compressible_euler_multicomponent_1d.jl
@@ -44,8 +44,8 @@ specific heat capacity at constant volume of component ``i``.
In case of more than one component, the specific heat ratios `gammas` and the gas constants
`gas_constants` should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.
-The remaining variables like the specific heats at constant volume 'cv' or the specific heats at
-constant pressure 'cp' are then calculated considering a calorically perfect gas.
+The remaining variables like the specific heats at constant volume `cv` or the specific heats at
+constant pressure `cp` are then calculated considering a calorically perfect gas.
"""
struct CompressibleEulerMulticomponentEquations1D{NVARS, NCOMP, RealT <: Real} <:
AbstractCompressibleEulerMulticomponentEquations{1, NVARS, NCOMP}
@@ -247,8 +247,8 @@ end
Entropy conserving two-point flux by
- Ayoub Gouasmi, Karthik Duraisamy (2020)
- "Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations""
- arXiv:1904.00972v3 [math.NA] 4 Feb 2020
+ "Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations"
+ [arXiv:1904.00972v3](https://arxiv.org/abs/1904.00972) [math.NA] 4 Feb 2020
"""
@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerMulticomponentEquations1D)
diff --git a/src/equations/compressible_euler_multicomponent_2d.jl b/src/equations/compressible_euler_multicomponent_2d.jl
index 5a015777cb1..7b437f4a1b4 100644
--- a/src/equations/compressible_euler_multicomponent_2d.jl
+++ b/src/equations/compressible_euler_multicomponent_2d.jl
@@ -48,8 +48,8 @@ specific heat capacity at constant volume of component ``i``.
In case of more than one component, the specific heat ratios `gammas` and the gas constants
`gas_constants` in [kJ/(kg*K)] should be passed as tuples, e.g., `gammas=(1.4, 1.667)`.
-The remaining variables like the specific heats at constant volume 'cv' or the specific heats at
-constant pressure 'cp' are then calculated considering a calorically perfect gas.
+The remaining variables like the specific heats at constant volume `cv` or the specific heats at
+constant pressure `cp` are then calculated considering a calorically perfect gas.
"""
struct CompressibleEulerMulticomponentEquations2D{NVARS, NCOMP, RealT <: Real} <:
AbstractCompressibleEulerMulticomponentEquations{2, NVARS, NCOMP}
@@ -275,8 +275,8 @@ end
Adaption of the entropy conserving two-point flux by
- Ayoub Gouasmi, Karthik Duraisamy (2020)
- "Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations""
- arXiv:1904.00972v3 [math.NA] 4 Feb 2020
+ "Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations"
+ [arXiv:1904.00972v3](https://arxiv.org/abs/1904.00972) [math.NA] 4 Feb 2020
"""
@inline function flux_chandrashekar(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerMulticomponentEquations2D)
diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl
index 33badba15d9..9b06e0b5abf 100644
--- a/src/equations/compressible_navier_stokes_2d.jl
+++ b/src/equations/compressible_navier_stokes_2d.jl
@@ -73,8 +73,8 @@ where
w_2 = \frac{\rho v_1}{p},\, w_3 = \frac{\rho v_2}{p},\, w_4 = -\frac{\rho}{p}
```
-#!!! warning "Experimental code"
-# This code is experimental and may be changed or removed in any future release.
+!!! warning "Experimental code"
+ This code is experimental and may be changed or removed in any future release.
"""
struct CompressibleNavierStokesDiffusion2D{GradientVariables, RealT <: Real,
E <: AbstractCompressibleEulerEquations{2}} <:
@@ -94,8 +94,8 @@ struct CompressibleNavierStokesDiffusion2D{GradientVariables, RealT <: Real,
end
"""
-#!!! warning "Experimental code"
-# This code is experimental and may be changed or removed in any future release.
+!!! warning "Experimental code"
+ This code is experimental and may be changed or removed in any future release.
`GradientVariablesPrimitive` and `GradientVariablesEntropy` are gradient variable type parameters
for `CompressibleNavierStokesDiffusion2D`. By default, the gradient variables are set to be
diff --git a/src/equations/compressible_navier_stokes_3d.jl b/src/equations/compressible_navier_stokes_3d.jl
index 8930489295d..0b770dff1ca 100644
--- a/src/equations/compressible_navier_stokes_3d.jl
+++ b/src/equations/compressible_navier_stokes_3d.jl
@@ -73,8 +73,8 @@ where
w_2 = \frac{\rho v_1}{p},\, w_3 = \frac{\rho v_2}{p},\, w_4 = \frac{\rho v_3}{p},\, w_5 = -\frac{\rho}{p}
```
-#!!! warning "Experimental code"
-# This code is experimental and may be changed or removed in any future release.
+!!! warning "Experimental code"
+ This code is experimental and may be changed or removed in any future release.
"""
struct CompressibleNavierStokesDiffusion3D{GradientVariables, RealT <: Real,
E <: AbstractCompressibleEulerEquations{3}} <:
diff --git a/src/equations/hyperbolic_diffusion_2d.jl b/src/equations/hyperbolic_diffusion_2d.jl
index 25536a060f8..511d1b8935d 100644
--- a/src/equations/hyperbolic_diffusion_2d.jl
+++ b/src/equations/hyperbolic_diffusion_2d.jl
@@ -10,7 +10,7 @@
The linear hyperbolic diffusion equations in two space dimensions.
A description of this system can be found in Sec. 2.5 of the book "I Do Like CFD, Too: Vol 1".
-The book is freely available at http://www.cfdbooks.com/ and further analysis can be found in
+The book is freely available at [http://www.cfdbooks.com/](http://www.cfdbooks.com/) and further analysis can be found in
the paper by Nishikawa [DOI: 10.1016/j.jcp.2007.07.029](https://doi.org/10.1016/j.jcp.2007.07.029)
"""
struct HyperbolicDiffusionEquations2D{RealT <: Real} <:
diff --git a/src/equations/hyperbolic_diffusion_3d.jl b/src/equations/hyperbolic_diffusion_3d.jl
index bf6a00140d4..ed807511b67 100644
--- a/src/equations/hyperbolic_diffusion_3d.jl
+++ b/src/equations/hyperbolic_diffusion_3d.jl
@@ -10,7 +10,7 @@
The linear hyperbolic diffusion equations in three space dimensions.
A description of this system can be found in Sec. 2.5 of the book "I Do Like CFD, Too: Vol 1".
-The book is freely available at http://www.cfdbooks.com/ and further analysis can be found in
+The book is freely available at [http://www.cfdbooks.com/](http://www.cfdbooks.com/) and further analysis can be found in
the paper by Nishikawa [DOI: 10.1016/j.jcp.2007.07.029](https://doi.org/10.1016/j.jcp.2007.07.029)
"""
struct HyperbolicDiffusionEquations3D{RealT <: Real} <:
diff --git a/src/equations/ideal_glm_mhd_3d.jl b/src/equations/ideal_glm_mhd_3d.jl
index 401fcd2daf1..2e149d2849f 100644
--- a/src/equations/ideal_glm_mhd_3d.jl
+++ b/src/equations/ideal_glm_mhd_3d.jl
@@ -41,7 +41,7 @@ end
# Set initial conditions at physical location `x` for time `t`
"""
-initial_condition_constant(x, t, equations::IdealGlmMhdEquations3D)
+ initial_condition_constant(x, t, equations::IdealGlmMhdEquations3D)
A constant initial condition to test free-stream preservation.
"""
diff --git a/src/equations/shallow_water_two_layer_1d.jl b/src/equations/shallow_water_two_layer_1d.jl
index 02899171509..e126eec7c25 100644
--- a/src/equations/shallow_water_two_layer_1d.jl
+++ b/src/equations/shallow_water_two_layer_1d.jl
@@ -392,7 +392,7 @@ end
equations::ShallowWaterTwoLayerEquations1D)
Entropy stable surface flux for the two-layer shallow water equations. Uses the entropy
-conservative flux_fjordholm_etal and adds a Lax-Friedrichs type dissipation dependent on the jump
+conservative [`flux_fjordholm_etal`](@ref) and adds a Lax-Friedrichs type dissipation dependent on the jump
of entropy variables.
Further details are available in the paper:
diff --git a/src/equations/shallow_water_two_layer_2d.jl b/src/equations/shallow_water_two_layer_2d.jl
index b5e52d636e4..a54831c711f 100644
--- a/src/equations/shallow_water_two_layer_2d.jl
+++ b/src/equations/shallow_water_two_layer_2d.jl
@@ -695,8 +695,9 @@ end
"""
flux_es_fjordholm_etal(u_ll, u_rr, orientation_or_normal_direction,
equations::ShallowWaterTwoLayerEquations1D)
+
Entropy stable surface flux for the two-layer shallow water equations. Uses the entropy conservative
-flux_fjordholm_etal and adds a Lax-Friedrichs type dissipation dependent on the jump of entropy
+[`flux_fjordholm_etal`](@ref) and adds a Lax-Friedrichs type dissipation dependent on the jump of entropy
variables.
Further details are available in the paper:
diff --git a/src/meshes/structured_mesh.jl b/src/meshes/structured_mesh.jl
index 5872681933a..df067db833d 100644
--- a/src/meshes/structured_mesh.jl
+++ b/src/meshes/structured_mesh.jl
@@ -33,7 +33,7 @@ Create a StructuredMesh of the given size and shape that uses `RealT` as coordin
the reference mesh to the physical domain.
If no `mapping_as_string` is defined, this function must be defined with the name `mapping`
to allow for restarts.
- This will be changed in the future, see https://github.com/trixi-framework/Trixi.jl/issues/541.
+ This will be changed in the future, see [https://github.com/trixi-framework/Trixi.jl/issues/541](https://github.com/trixi-framework/Trixi.jl/issues/541).
- `RealT::Type`: the type that should be used for coordinates.
- `periodicity`: either a `Bool` deciding if all of the boundaries are periodic or an `NTuple{NDIMS, Bool}`
deciding for each dimension if the boundaries in this dimension are periodic.
@@ -41,7 +41,7 @@ Create a StructuredMesh of the given size and shape that uses `RealT` as coordin
- `mapping_as_string::String`: the code that defines the `mapping`.
If `CodeTracking` can't find the function definition, it can be passed directly here.
The code string must define the mapping function with the name `mapping`.
- This will be changed in the future, see https://github.com/trixi-framework/Trixi.jl/issues/541.
+ This will be changed in the future, see [https://github.com/trixi-framework/Trixi.jl/issues/541](https://github.com/trixi-framework/Trixi.jl/issues/541).
"""
function StructuredMesh(cells_per_dimension, mapping; RealT = Float64,
periodicity = true, unsaved_changes = true,
diff --git a/src/solvers/dgmulti/types.jl b/src/solvers/dgmulti/types.jl
index f1f7b158dec..fe6510856b0 100644
--- a/src/solvers/dgmulti/types.jl
+++ b/src/solvers/dgmulti/types.jl
@@ -180,9 +180,9 @@ GeometricTermsType(mesh_type::Curved, element_type::AbstractElemShape) = NonAffi
# other potential mesh types to add later: Polynomial{polydeg_geo}?
"""
- DGMultiMesh(dg::DGMulti{NDIMS}, vertex_coordinates, EToV;
- is_on_boundary=nothing,
- periodicity=ntuple(_->false, NDIMS)) where {NDIMS}
+ DGMultiMesh(dg::DGMulti{NDIMS}, vertex_coordinates, EToV;
+ is_on_boundary=nothing,
+ periodicity=ntuple(_->false, NDIMS)) where {NDIMS}
- `dg::DGMulti` contains information associated with to the reference element (e.g., quadrature,
basis evaluation, differentiation, etc).
diff --git a/src/solvers/dgsem_tree/indicators.jl b/src/solvers/dgsem_tree/indicators.jl
index 2eb0af87148..b8f8a796f2b 100644
--- a/src/solvers/dgsem_tree/indicators.jl
+++ b/src/solvers/dgsem_tree/indicators.jl
@@ -159,7 +159,7 @@ and `basis` if this indicator should be used for shock capturing.
- Löhner (1987)
"An adaptive finite element scheme for transient problems in CFD"
[doi: 10.1016/0045-7825(87)90098-3](https://doi.org/10.1016/0045-7825(87)90098-3)
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node59.html#SECTION05163100000000000000
+- [https://flash.rochester.edu/site/flashcode/user_support/flash4_ug_4p62/node59.html#SECTION05163100000000000000](https://flash.rochester.edu/site/flashcode/user_support/flash4_ug_4p62/node59.html#SECTION05163100000000000000)
"""
struct IndicatorLöhner{RealT <: Real, Variable, Cache} <: AbstractIndicator
f_wave::RealT # TODO: Taal documentation
From 054a917a09127570dabc458f1350550f2ddb6a09 Mon Sep 17 00:00:00 2001
From: Andrew Winters
Date: Wed, 21 Jun 2023 17:56:07 +0200
Subject: [PATCH 014/263] update links to Flash manual (#1544)
Co-authored-by: Hendrik Ranocha
---
examples/p4est_2d_dgsem/elixir_euler_sedov.jl | 4 ++--
examples/p4est_3d_dgsem/elixir_euler_sedov.jl | 4 ++--
.../elixir_eulergravity_jeans_instability.jl | 6 +++---
.../elixir_eulergravity_sedov_blast_wave.jl | 10 +++++-----
examples/structured_1d_dgsem/elixir_euler_sedov.jl | 8 ++++----
examples/structured_2d_dgsem/elixir_euler_sedov.jl | 10 +++++-----
examples/structured_3d_dgsem/elixir_euler_sedov.jl | 10 +++++-----
examples/tree_1d_dgsem/elixir_euler_positivity.jl | 4 ++--
.../tree_1d_dgsem/elixir_euler_sedov_blast_wave.jl | 4 ++--
.../elixir_euler_sedov_blast_wave_pure_fv.jl | 4 ++--
examples/tree_2d_dgsem/elixir_euler_positivity.jl | 4 ++--
.../tree_2d_dgsem/elixir_euler_sedov_blast_wave.jl | 4 ++--
...er_sedov_blast_wave_neuralnetwork_perssonperaire.jl | 4 ++--
.../tree_3d_dgsem/elixir_euler_sedov_blast_wave.jl | 6 +++---
examples/unstructured_2d_dgsem/elixir_euler_sedov.jl | 4 ++--
15 files changed, 43 insertions(+), 43 deletions(-)
diff --git a/examples/p4est_2d_dgsem/elixir_euler_sedov.jl b/examples/p4est_2d_dgsem/elixir_euler_sedov.jl
index 9f5247e8c4d..d5d8e0c78bf 100644
--- a/examples/p4est_2d_dgsem/elixir_euler_sedov.jl
+++ b/examples/p4est_2d_dgsem/elixir_euler_sedov.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations2D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
# Set up polar coordinates
@@ -20,7 +20,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
E = 1.0
p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2)
diff --git a/examples/p4est_3d_dgsem/elixir_euler_sedov.jl b/examples/p4est_3d_dgsem/elixir_euler_sedov.jl
index 00da4132851..6fa285b5565 100644
--- a/examples/p4est_3d_dgsem/elixir_euler_sedov.jl
+++ b/examples/p4est_3d_dgsem/elixir_euler_sedov.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations3D(1.4)
initial_condition_medium_sedov_blast_wave(x, t, equations::CompressibleEulerEquations3D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
with smaller strength of the initial discontinuity.
"""
function initial_condition_medium_sedov_blast_wave(x, t, equations::CompressibleEulerEquations3D)
@@ -22,7 +22,7 @@ function initial_condition_medium_sedov_blast_wave(x, t, equations::Compressible
z_norm = x[3] - inicenter[3]
r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
E = 1.0
p0_inner = 3 * (equations.gamma - 1) * E / (4 * pi * r0^2)
diff --git a/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_jeans_instability.jl b/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_jeans_instability.jl
index 1774e39513d..fb445616cd4 100644
--- a/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_jeans_instability.jl
+++ b/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_jeans_instability.jl
@@ -15,7 +15,7 @@ The classical Jeans instability taken from
- Dominik Derigs, Andrew R. Winters, Gregor J. Gassner, Stefanie Walch (2016)
A Novel High-Order, Entropy Stable, 3D AMR MHD Solver with Guaranteed Positive Pressure
[arXiv: 1605.03572](https://arxiv.org/abs/1605.03572)
-- Flash manual https://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel.pdf
+- Flash manual https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node189.html#SECTION010131000000000000000
in CGS (centimeter, gram, second) units.
"""
function initial_condition_jeans_instability(x, t,
@@ -32,7 +32,7 @@ function initial_condition_jeans_instability(x, t,
pres0 = 1.5e7 # dyn/cm^2
delta0 = 1e-3
# set wave vector values for perturbation (units 1/cm)
- # see FLASH manual: https://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel.pdf
+ # see FLASH manual: https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node189.html#SECTION010131000000000000000
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]
@@ -49,7 +49,7 @@ function initial_condition_jeans_instability(x, t,
equations::HyperbolicDiffusionEquations2D)
# gravity equation: -Δϕ = -4πGρ
# Constants taken from the FLASH manual
- # https://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel.pdf
+ # https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node189.html#SECTION010131000000000000000
rho0 = 1.5e7
delta0 = 1e-3
diff --git a/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_sedov_blast_wave.jl b/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_sedov_blast_wave.jl
index f7bb5bbb01c..8933224a2c7 100644
--- a/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_sedov_blast_wave.jl
+++ b/examples/paper_self_gravitating_gas_dynamics/elixir_eulergravity_sedov_blast_wave.jl
@@ -15,14 +15,14 @@ Adaptation of the Sedov blast wave with self-gravity taken from
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
based on
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114100000000000000
Should be used together with [`boundary_condition_sedov_self_gravity`](@ref).
"""
function initial_condition_sedov_self_gravity(x, t, equations::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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114100000000000000
r0 = 0.125 # = 4.0 * smallest dx (for domain length=8 and max-ref=8)
E = 1.0
p_inner = (equations.gamma - 1) * E / (pi * r0^2)
@@ -59,7 +59,7 @@ Adaptation of the Sedov blast wave with self-gravity taken from
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
based on
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114100000000000000
Should be used together with [`initial_condition_sedov_self_gravity`](@ref).
"""
function boundary_condition_sedov_self_gravity(u_inner, orientation, direction, x, t,
@@ -122,7 +122,7 @@ Adaptation of the Sedov blast wave with self-gravity taken from
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
based on
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114100000000000000
Should be used together with [`boundary_condition_sedov_self_gravity`](@ref).
"""
function initial_condition_sedov_self_gravity(x, t, equations::HyperbolicDiffusionEquations2D)
@@ -143,7 +143,7 @@ Adaptation of the Sedov blast wave with self-gravity taken from
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
based on
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114100000000000000
Should be used together with [`initial_condition_sedov_self_gravity`](@ref).
"""
function boundary_condition_sedov_self_gravity(u_inner, orientation, direction, x, t,
diff --git a/examples/structured_1d_dgsem/elixir_euler_sedov.jl b/examples/structured_1d_dgsem/elixir_euler_sedov.jl
index ee950b3aaaa..9d7be21a5c1 100644
--- a/examples/structured_1d_dgsem/elixir_euler_sedov.jl
+++ b/examples/structured_1d_dgsem/elixir_euler_sedov.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations1D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
# Set up polar coordinates
@@ -19,7 +19,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
x_norm = x[1] - inicenter[1]
r = abs(x_norm)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
@@ -78,8 +78,8 @@ save_solution = SaveSolutionCallback(interval=100,
stepsize_callback = StepsizeCallback(cfl=0.5)
-callbacks = CallbackSet(summary_callback,
- analysis_callback,
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
alive_callback,
save_solution,
stepsize_callback)
diff --git a/examples/structured_2d_dgsem/elixir_euler_sedov.jl b/examples/structured_2d_dgsem/elixir_euler_sedov.jl
index ed1bfab3be2..efc3b6627c0 100644
--- a/examples/structured_2d_dgsem/elixir_euler_sedov.jl
+++ b/examples/structured_2d_dgsem/elixir_euler_sedov.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations2D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
# Set up polar coordinates
@@ -20,7 +20,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
E = 1.0
p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2)
@@ -59,12 +59,12 @@ function mapping(xi, eta)
y = eta + 0.125 * (cos(1.5 * pi * xi) * cos(0.5 * pi * eta))
x = xi + 0.125 * (cos(0.5 * pi * xi) * cos(2 * pi * y))
-
+
return SVector(x, y)
end
-
+
cells_per_dimension = (16, 16)
-
+
mesh = StructuredMesh(cells_per_dimension, mapping, periodicity=true)
# create the semidiscretization
diff --git a/examples/structured_3d_dgsem/elixir_euler_sedov.jl b/examples/structured_3d_dgsem/elixir_euler_sedov.jl
index 8f428495b4f..e0595437c99 100644
--- a/examples/structured_3d_dgsem/elixir_euler_sedov.jl
+++ b/examples/structured_3d_dgsem/elixir_euler_sedov.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations3D(1.4)
initial_condition_medium_sedov_blast_wave(x, t, equations::CompressibleEulerEquations3D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
with smaller strength of the initial discontinuity.
"""
function initial_condition_medium_sedov_blast_wave(x, t, equations::CompressibleEulerEquations3D)
@@ -22,11 +22,11 @@ function initial_condition_medium_sedov_blast_wave(x, t, equations::Compressible
z_norm = x[3] - inicenter[3]
r = sqrt(x_norm^2 + y_norm^2 + z_norm^2)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
E = 1.0
p0_inner = 3 * (equations.gamma - 1) * E / (4 * pi * r0^2)
- p0_outer = 1.0e-3
+ p0_outer = 1.0e-3
# Calculate primitive variables
rho = 1.0
@@ -52,8 +52,8 @@ indicator_sc = IndicatorHennemannGassner(equations, basis,
volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
volume_flux_dg=volume_flux,
volume_flux_fv=surface_flux)
-
-solver = DGSEM(polydeg=polydeg, surface_flux=surface_flux, volume_integral=volume_integral)
+
+solver = DGSEM(polydeg=polydeg, surface_flux=surface_flux, volume_integral=volume_integral)
# Mapping as described in https://arxiv.org/abs/2012.12040
function mapping(xi, eta, zeta)
diff --git a/examples/tree_1d_dgsem/elixir_euler_positivity.jl b/examples/tree_1d_dgsem/elixir_euler_positivity.jl
index 7942937151a..966661e8894 100644
--- a/examples/tree_1d_dgsem/elixir_euler_positivity.jl
+++ b/examples/tree_1d_dgsem/elixir_euler_positivity.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations1D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
# Set up polar coordinates
@@ -19,7 +19,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
x_norm = x[1] - inicenter[1]
r = abs(x_norm)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
diff --git a/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave.jl b/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave.jl
index 746a7cf1bac..106ccacf4f5 100644
--- a/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave.jl
+++ b/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations1D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
# Set up polar coordinates
@@ -19,7 +19,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
x_norm = x[1] - inicenter[1]
r = abs(x_norm)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
diff --git a/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave_pure_fv.jl b/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave_pure_fv.jl
index 00b80dbae92..ebe8fa7cebf 100644
--- a/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave_pure_fv.jl
+++ b/examples/tree_1d_dgsem/elixir_euler_sedov_blast_wave_pure_fv.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations1D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations1D)
# Set up polar coordinates
@@ -19,7 +19,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
x_norm = x[1] - inicenter[1]
r = abs(x_norm)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
diff --git a/examples/tree_2d_dgsem/elixir_euler_positivity.jl b/examples/tree_2d_dgsem/elixir_euler_positivity.jl
index e40dc3b47af..4c7dd7eb6cf 100644
--- a/examples/tree_2d_dgsem/elixir_euler_positivity.jl
+++ b/examples/tree_2d_dgsem/elixir_euler_positivity.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations2D(gamma)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
# Set up polar coordinates
@@ -20,7 +20,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
diff --git a/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave.jl b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave.jl
index da7e1d55c91..512e5822374 100644
--- a/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave.jl
+++ b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations2D(gamma)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
# Set up polar coordinates
@@ -20,7 +20,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
diff --git a/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl
index 56715789377..5fd32da2e5c 100644
--- a/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl
+++ b/examples/tree_2d_dgsem/elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl
@@ -23,7 +23,7 @@ equations = CompressibleEulerEquations2D(gamma)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
# Set up polar coordinates
@@ -32,7 +32,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.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
diff --git a/examples/tree_3d_dgsem/elixir_euler_sedov_blast_wave.jl b/examples/tree_3d_dgsem/elixir_euler_sedov_blast_wave.jl
index 336c09e9212..3641878149a 100644
--- a/examples/tree_3d_dgsem/elixir_euler_sedov_blast_wave.jl
+++ b/examples/tree_3d_dgsem/elixir_euler_sedov_blast_wave.jl
@@ -15,14 +15,14 @@ Adaptation of the Sedov blast wave with self-gravity taken from
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
based on
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
Should be used together with [`boundary_condition_sedov_self_gravity`](@ref).
"""
function initial_condition_sedov_self_gravity(x, t, equations::CompressibleEulerEquations3D)
# Calculate radius as distance from origin
r = sqrt(x[1]^2 + x[2]^2 + x[3]^2)
- # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.25 # = 4.0 * smallest dx (for domain length=8 and max-ref=7)
E = 1.0
p_inner = (equations.gamma - 1) * E / (4/3 * pi * r0^3)
@@ -60,7 +60,7 @@ Adaptation of the Sedov blast wave with self-gravity taken from
A purely hyperbolic discontinuous Galerkin approach for self-gravitating gas dynamics
[arXiv: 2008.10593](https://arxiv.org/abs/2008.10593)
based on
-- http://flash.uchicago.edu/site/flashcode/user_support/flash4_ug_4p62/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
Should be used together with [`initial_condition_sedov_self_gravity`](@ref).
"""
function boundary_condition_sedov_self_gravity(u_inner, orientation, direction, x, t,
diff --git a/examples/unstructured_2d_dgsem/elixir_euler_sedov.jl b/examples/unstructured_2d_dgsem/elixir_euler_sedov.jl
index 3d5a391bd90..570a2084691 100644
--- a/examples/unstructured_2d_dgsem/elixir_euler_sedov.jl
+++ b/examples/unstructured_2d_dgsem/elixir_euler_sedov.jl
@@ -11,7 +11,7 @@ equations = CompressibleEulerEquations2D(1.4)
initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
The Sedov blast wave setup based on Flash
-- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+- https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
"""
function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
# Set up polar coordinates
@@ -20,7 +20,7 @@ function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEq
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
+ # Setup based on https://flash.rochester.edu/site/flashcode/user_support/flash_ug_devel/node187.html#SECTION010114000000000000000
r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
E = 1.0
p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2)
From bea4bfed27ce896edcfa3dbe79861a4faef4dec7 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Thu, 22 Jun 2023 17:06:19 +0200
Subject: [PATCH 015/263] `splitting_lax_friedrichs` for
`LinearScalarAdvection1D` (#1546)
* splitting_lax_friedrichs for LinearScalarAdvection1D
* Update src/equations/linear_scalar_advection_1d.jl
Co-authored-by: Andrew Winters
---------
Co-authored-by: Andrew Winters
---
.../tree_1d_fdsbp/elixir_advection_upwind.jl | 57 +++++++++++++++++++
src/equations/inviscid_burgers_1d.jl | 2 +-
src/equations/linear_scalar_advection_1d.jl | 38 +++++++++++++
test/test_tree_1d_fdsbp.jl | 18 ++++++
4 files changed, 114 insertions(+), 1 deletion(-)
create mode 100644 examples/tree_1d_fdsbp/elixir_advection_upwind.jl
diff --git a/examples/tree_1d_fdsbp/elixir_advection_upwind.jl b/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
new file mode 100644
index 00000000000..5c50e1a6c64
--- /dev/null
+++ b/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
@@ -0,0 +1,57 @@
+# !!! warning "Experimental implementation (upwind SBP)"
+# This is an experimental feature and may change in future releases.
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the linear scalar advection equation equation
+
+equations = LinearScalarAdvectionEquation1D(1.0)
+
+function initial_condition_sin(x, t, equation::LinearScalarAdvectionEquation1D)
+ return SVector(sinpi(x[1] - equations.advection_velocity[1] * t))
+end
+
+D_upw = upwind_operators(SummationByPartsOperators.Mattsson2017,
+ derivative_order = 1,
+ accuracy_order = 4,
+ xmin = -1.0, xmax = 1.0,
+ N = 16)
+flux_splitting = splitting_lax_friedrichs
+solver = FDSBP(D_upw,
+ surface_integral = SurfaceIntegralUpwind(flux_splitting),
+ volume_integral = VolumeIntegralUpwind(flux_splitting))
+
+coordinates_min = -1.0
+coordinates_max = 1.0
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level = 4,
+ n_cells_max = 10_000)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_sin, solver)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 2.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback)
+
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, RDPK3SpFSAL49(); abstol=1.0e-6, reltol=1.0e-6,
+ ode_default_options()..., callback=callbacks);
+summary_callback() # print the timer summary
diff --git a/src/equations/inviscid_burgers_1d.jl b/src/equations/inviscid_burgers_1d.jl
index 8d4410b6ffe..6a2cfb6aa8e 100644
--- a/src/equations/inviscid_burgers_1d.jl
+++ b/src/equations/inviscid_burgers_1d.jl
@@ -132,7 +132,7 @@ end
equations::InviscidBurgersEquation1D)
Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)`
-and `f⁻ = 0.5 (f - λ u)` where λ = abs(u).
+and `f⁻ = 0.5 (f - λ u)` where `λ = abs(u)`.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
diff --git a/src/equations/linear_scalar_advection_1d.jl b/src/equations/linear_scalar_advection_1d.jl
index 7769cb61fbf..6c6b9dd3721 100644
--- a/src/equations/linear_scalar_advection_1d.jl
+++ b/src/equations/linear_scalar_advection_1d.jl
@@ -172,6 +172,44 @@ end
return abs.(equation.advection_velocity)
end
+"""
+ splitting_lax_friedrichs(u, orientation::Integer,
+ equations::LinearScalarAdvectionEquation1D)
+ splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}}
+ orientation::Integer,
+ equations::LinearScalarAdvectionEquation1D)
+
+Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)`
+and `f⁻ = 0.5 (f - λ u)` where `λ` is the absolute value of the advection
+velocity.
+
+Returns a tuple of the fluxes "minus" (associated with waves going into the
+negative axis direction) and "plus" (associated with waves going into the
+positive axis direction). If only one of the fluxes is required, use the
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
+
+!!! warning "Experimental implementation (upwind SBP)"
+ This is an experimental feature and may change in future releases.
+"""
+@inline function splitting_lax_friedrichs(u, orientation::Integer,
+ equations::LinearScalarAdvectionEquation1D)
+ fm = splitting_lax_friedrichs(u, Val{:minus}(), orientation, equations)
+ fp = splitting_lax_friedrichs(u, Val{:plus}(), orientation, equations)
+ return fm, fp
+end
+
+@inline function splitting_lax_friedrichs(u, ::Val{:plus}, orientation::Integer,
+ equations::LinearScalarAdvectionEquation1D)
+ a = equations.advection_velocity[1]
+ return a > 0 ? flux(u, orientation, equations) : zero(u)
+end
+
+@inline function splitting_lax_friedrichs(u, ::Val{:minus}, orientation::Integer,
+ equations::LinearScalarAdvectionEquation1D)
+ a = equations.advection_velocity[1]
+ return a < 0 ? flux(u, orientation, equations) : zero(u)
+end
+
# Convert conservative variables to primitive
@inline cons2prim(u, equation::LinearScalarAdvectionEquation1D) = u
diff --git a/test/test_tree_1d_fdsbp.jl b/test/test_tree_1d_fdsbp.jl
index a966b3836f3..118385c34b3 100644
--- a/test/test_tree_1d_fdsbp.jl
+++ b/test/test_tree_1d_fdsbp.jl
@@ -7,6 +7,24 @@ include("test_trixi.jl")
EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_fdsbp")
+@testset "Linear scalar advection" begin
+ @trixi_testset "elixir_advection_upwind.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_upwind.jl"),
+ l2 = [1.7735637157305526e-6],
+ linf = [1.0418854521951328e-5],
+ tspan = (0.0, 0.5))
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
+ end
+ end
+end
+
@testset "Inviscid Burgers" begin
@trixi_testset "elixir_burgers_basic.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_burgers_basic.jl"),
From 6160fe952bd1d6f619fb77627329520f0b586956 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 23 Jun 2023 07:50:05 +0200
Subject: [PATCH 016/263] fix typos in FDSBP elixir comments (#1548)
---
examples/tree_2d_fdsbp/elixir_euler_convergence.jl | 3 ++-
.../tree_2d_fdsbp/elixir_euler_kelvin_helmholtz_instability.jl | 3 ++-
examples/tree_2d_fdsbp/elixir_euler_vortex.jl | 3 ++-
examples/tree_3d_fdsbp/elixir_euler_convergence.jl | 1 -
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/examples/tree_2d_fdsbp/elixir_euler_convergence.jl b/examples/tree_2d_fdsbp/elixir_euler_convergence.jl
index 0843cece67e..2a6c291f0bf 100644
--- a/examples/tree_2d_fdsbp/elixir_euler_convergence.jl
+++ b/examples/tree_2d_fdsbp/elixir_euler_convergence.jl
@@ -5,7 +5,8 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# semidiscretization of the linear advection equation
+# semidiscretization of the compressible Euler equations
+
equations = CompressibleEulerEquations2D(1.4)
initial_condition = initial_condition_convergence_test
diff --git a/examples/tree_2d_fdsbp/elixir_euler_kelvin_helmholtz_instability.jl b/examples/tree_2d_fdsbp/elixir_euler_kelvin_helmholtz_instability.jl
index 1e58badf47a..e63343852ab 100644
--- a/examples/tree_2d_fdsbp/elixir_euler_kelvin_helmholtz_instability.jl
+++ b/examples/tree_2d_fdsbp/elixir_euler_kelvin_helmholtz_instability.jl
@@ -5,7 +5,8 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# semidiscretization of the linear advection equation
+# semidiscretization of the compressible Euler equations
+
equations = CompressibleEulerEquations2D(1.4)
function initial_condition_kelvin_helmholtz_instability(x, t, equations::CompressibleEulerEquations2D)
diff --git a/examples/tree_2d_fdsbp/elixir_euler_vortex.jl b/examples/tree_2d_fdsbp/elixir_euler_vortex.jl
index abaf3d494d4..c1bee8f9c4d 100644
--- a/examples/tree_2d_fdsbp/elixir_euler_vortex.jl
+++ b/examples/tree_2d_fdsbp/elixir_euler_vortex.jl
@@ -5,7 +5,8 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# semidiscretization of the linear advection equation
+# semidiscretization of the compressible Euler equations
+
equations = CompressibleEulerEquations2D(1.4)
"""
diff --git a/examples/tree_3d_fdsbp/elixir_euler_convergence.jl b/examples/tree_3d_fdsbp/elixir_euler_convergence.jl
index 576a07e6aba..6aafa1b5cc1 100644
--- a/examples/tree_3d_fdsbp/elixir_euler_convergence.jl
+++ b/examples/tree_3d_fdsbp/elixir_euler_convergence.jl
@@ -6,7 +6,6 @@ using Trixi
###############################################################################
# semidiscretization of the compressible Euler equations
-
equations = CompressibleEulerEquations3D(1.4)
initial_condition = initial_condition_convergence_test
From d4c556960d6307d6279c698203a9e741c2479c2e Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 23 Jun 2023 07:51:00 +0200
Subject: [PATCH 017/263] set version to v0.5.30
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index d3983262591..e015c90310f 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.30-pre"
+version = "0.5.30"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 1b69182dc06bddbcb4dd693d7aedb576d68fabc0 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 23 Jun 2023 07:51:14 +0200
Subject: [PATCH 018/263] set development version to v0.5.31-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index e015c90310f..0edba6b681c 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.30"
+version = "0.5.31-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From b7be5856eba029a3d91257166be4ee62514ae0dd Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Fri, 23 Jun 2023 03:34:22 -0500
Subject: [PATCH 019/263] Add parabolic BCs for `P4estMesh{2}` (#1493)
* generalize function signatures to P4estMesh
* add specializations for P4estMesh
d
* add normals
* add surface integrals
* fix type ambiguity
* generalizing `apply_jacobian!` to P4estMesh
* resolving type ambiguity with apply_jacobian!
d
* `apply_jacobian!` -> `apply_jacobian_parabolic!`
* `apply_jacobian!` -> `apply_jacobian_parabolic!`
* switch to `apply_jacobian_parabolic!`
* Update src/solvers/dgsem_tree/dg_1d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* missed one
* draft of prolong2interfaces and calc_interface_flux
* cache -> cache_parabolic
* adding prolong2boundaries! and calc_boundary_flux_gradients! back
* remove todo
* variable renaming
* extending TreeMesh parabolic functions to P4estMesh
* adding elixir
* comments
* add prolong2boundaries! (untested)
* update test
* initial commit
* fix CI
f
* Update src/solvers/dgsem_p4est/dg_2d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Update src/solvers/dgsem_p4est/dg_2d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* add "no mortars" check
* add curved elixir
* fix gradient bug
* add curved test
* Apply suggestions from code review
Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com>
Co-authored-by: Michael Schlottke-Lakemper
* add comment on mapping
* reuse P4estMesh{2} code
* fix += for muladd
* Update examples/p4est_2d_dgsem/elixir_advection_diffusion_periodic_curved.jl
Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com>
* comment
* comments + remove cruft
* add BCs for parabolic P43st
* add tests
* Update examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
* formatting
* fix CNS convergence elixir and add to tests
* update test values
---------
Co-authored-by: Hendrik Ranocha
Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com>
Co-authored-by: Michael Schlottke-Lakemper
---
..._advection_diffusion_nonperiodic_curved.jl | 96 ++++++++
.../elixir_navierstokes_convergence.jl | 209 ++++++++++++++++++
.../elixir_navierstokes_lid_driven_cavity.jl | 82 +++++++
src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 133 ++++++++++-
test/test_parabolic_2d.jl | 32 +++
5 files changed, 544 insertions(+), 8 deletions(-)
create mode 100644 examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_curved.jl
create mode 100644 examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
create mode 100644 examples/p4est_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl
diff --git a/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_curved.jl b/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_curved.jl
new file mode 100644
index 00000000000..55682f73fce
--- /dev/null
+++ b/examples/p4est_2d_dgsem/elixir_advection_diffusion_nonperiodic_curved.jl
@@ -0,0 +1,96 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the linear advection-diffusion equation
+
+diffusivity() = 5.0e-2
+advection_velocity = (1.0, 0.0)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+equations_parabolic = LaplaceDiffusion2D(diffusivity(), equations)
+
+# Example setup taken from
+# - Truman Ellis, Jesse Chan, and Leszek Demkowicz (2016).
+# Robust DPG methods for transient convection-diffusion.
+# In: Building bridges: connections and challenges in modern approaches
+# to numerical partial differential equations.
+# [DOI](https://doi.org/10.1007/978-3-319-41640-3_6).
+function initial_condition_eriksson_johnson(x, t, equations)
+ l = 4
+ epsilon = diffusivity() # TODO: this requires epsilon < .6 due to sqrt
+ lambda_1 = (-1 + sqrt(1 - 4 * epsilon * l)) / (-2 * epsilon)
+ lambda_2 = (-1 - sqrt(1 - 4 * epsilon * l)) / (-2 * epsilon)
+ r1 = (1 + sqrt(1 + 4 * pi^2 * epsilon^2)) / (2 * epsilon)
+ s1 = (1 - sqrt(1 + 4 * pi^2 * epsilon^2)) / (2 * epsilon)
+ u = exp(-l * t) * (exp(lambda_1 * x[1]) - exp(lambda_2 * x[1])) +
+ cos(pi * x[2]) * (exp(s1 * x[1]) - exp(r1 * x[1])) / (exp(-s1) - exp(-r1))
+ return SVector{1}(u)
+end
+initial_condition = initial_condition_eriksson_johnson
+
+boundary_conditions = Dict(:x_neg => BoundaryConditionDirichlet(initial_condition),
+ :y_neg => BoundaryConditionDirichlet(initial_condition),
+ :y_pos => BoundaryConditionDirichlet(initial_condition),
+ :x_pos => boundary_condition_do_nothing)
+
+boundary_conditions_parabolic = Dict(:x_neg => BoundaryConditionDirichlet(initial_condition),
+ :x_pos => BoundaryConditionDirichlet(initial_condition),
+ :y_neg => BoundaryConditionDirichlet(initial_condition),
+ :y_pos => BoundaryConditionDirichlet(initial_condition))
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)
+
+coordinates_min = (-1.0, -0.5)
+coordinates_max = ( 0.0, 0.5)
+
+# This maps the domain [-1, 1]^2 to [-1, 0] x [-0.5, 0.5] while also
+# introducing a curved warping to interior nodes.
+function mapping(xi, eta)
+ x = xi + 0.1 * sin(pi * xi) * sin(pi * eta)
+ y = eta + 0.1 * sin(pi * xi) * sin(pi * eta)
+ return SVector(0.5 * (1 + x) - 1, 0.5 * y)
+end
+
+trees_per_dimension = (4, 4)
+mesh = P4estMesh(trees_per_dimension,
+ polydeg=3, initial_refinement_level=2,
+ mapping=mapping, periodicity=(false, false))
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver,
+ boundary_conditions = (boundary_conditions, boundary_conditions_parabolic))
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan);
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
+# and resets the timers
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+# The AliveCallback prints short status information in regular intervals
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback)
+
+
+###############################################################################
+# run the simulation
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+time_int_tol = 1.0e-11
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+
+# Print the timer summary
+summary_callback()
diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
new file mode 100644
index 00000000000..8111df8251a
--- /dev/null
+++ b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
@@ -0,0 +1,209 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the ideal compressible Navier-Stokes equations
+
+prandtl_number() = 0.72
+mu() = 0.01
+
+equations = CompressibleEulerEquations2D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(), Prandtl=prandtl_number(),
+ gradient_variables=GradientVariablesPrimitive())
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs,
+ volume_integral=VolumeIntegralWeakForm())
+
+coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y))
+coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y))
+
+trees_per_dimension = (4, 4)
+mesh = P4estMesh(trees_per_dimension,
+ polydeg=3, initial_refinement_level=2,
+ coordinates_min=coordinates_min, coordinates_max=coordinates_max,
+ periodicity=(true, false))
+
+# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D`
+# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`)
+# and by the initial condition (which passes in `CompressibleEulerEquations2D`).
+# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
+function initial_condition_navier_stokes_convergence_test(x, t, equations)
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_y = pi * x[2]
+ pi_t = pi * t
+
+ rho = c + A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+ v1 = sin(pi_x) * log(x[2] + 2.0) * (1.0 - exp(-A * (x[2] - 1.0)) ) * cos(pi_t)
+ v2 = v1
+ p = rho^2
+
+ return prim2cons(SVector(rho, v1, v2, p), equations)
+end
+
+@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
+ y = x[2]
+
+ # TODO: parabolic
+ # we currently need to hardcode these parameters until we fix the "combined equation" issue
+ # see also https://github.com/trixi-framework/Trixi.jl/pull/1160
+ inv_gamma_minus_one = inv(equations.gamma - 1)
+ Pr = prandtl_number()
+ mu_ = mu()
+
+ # Same settings as in `initial_condition`
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_y = pi * x[2]
+ pi_t = pi * t
+
+ # compute the manufactured solution and all necessary derivatives
+ rho = c + A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+ rho_t = -pi * A * sin(pi_x) * cos(pi_y) * sin(pi_t)
+ rho_x = pi * A * cos(pi_x) * cos(pi_y) * cos(pi_t)
+ rho_y = -pi * A * sin(pi_x) * sin(pi_y) * cos(pi_t)
+ rho_xx = -pi * pi * A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+ rho_yy = -pi * pi * A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+
+ v1 = sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
+ v1_t = -pi * sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * sin(pi_t)
+ v1_x = pi * cos(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
+ v1_y = sin(pi_x) * (A * log(y + 2.0) * exp(-A * (y - 1.0)) + (1.0 - exp(-A * (y - 1.0))) / (y + 2.0)) * cos(pi_t)
+ v1_xx = -pi * pi * sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
+ v1_xy = pi * cos(pi_x) * (A * log(y + 2.0) * exp(-A * (y - 1.0)) + (1.0 - exp(-A * (y - 1.0))) / (y + 2.0)) * cos(pi_t)
+ v1_yy = (sin(pi_x) * ( 2.0 * A * exp(-A * (y - 1.0)) / (y + 2.0)
+ - A * A * log(y + 2.0) * exp(-A * (y - 1.0))
+ - (1.0 - exp(-A * (y - 1.0))) / ((y + 2.0) * (y + 2.0))) * cos(pi_t))
+ v2 = v1
+ v2_t = v1_t
+ v2_x = v1_x
+ v2_y = v1_y
+ v2_xx = v1_xx
+ v2_xy = v1_xy
+ v2_yy = v1_yy
+
+ p = rho * rho
+ p_t = 2.0 * rho * rho_t
+ p_x = 2.0 * rho * rho_x
+ p_y = 2.0 * rho * rho_y
+ p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x
+ p_yy = 2.0 * rho * rho_yy + 2.0 * rho_y * rho_y
+
+ # Note this simplifies slightly because the ansatz assumes that v1 = v2
+ E = p * inv_gamma_minus_one + 0.5 * rho * (v1^2 + v2^2)
+ E_t = p_t * inv_gamma_minus_one + rho_t * v1^2 + 2.0 * rho * v1 * v1_t
+ E_x = p_x * inv_gamma_minus_one + rho_x * v1^2 + 2.0 * rho * v1 * v1_x
+ E_y = p_y * inv_gamma_minus_one + rho_y * v1^2 + 2.0 * rho * v1 * v1_y
+
+ # Some convenience constants
+ T_const = equations.gamma * inv_gamma_minus_one / Pr
+ inv_rho_cubed = 1.0 / (rho^3)
+
+ # compute the source terms
+ # density equation
+ du1 = rho_t + rho_x * v1 + rho * v1_x + rho_y * v2 + rho * v2_y
+
+ # x-momentum equation
+ du2 = ( rho_t * v1 + rho * v1_t + p_x + rho_x * v1^2
+ + 2.0 * rho * v1 * v1_x
+ + rho_y * v1 * v2
+ + rho * v1_y * v2
+ + rho * v1 * v2_y
+ # stress tensor from x-direction
+ - 4.0 / 3.0 * v1_xx * mu_
+ + 2.0 / 3.0 * v2_xy * mu_
+ - v1_yy * mu_
+ - v2_xy * mu_ )
+ # y-momentum equation
+ du3 = ( rho_t * v2 + rho * v2_t + p_y + rho_x * v1 * v2
+ + rho * v1_x * v2
+ + rho * v1 * v2_x
+ + rho_y * v2^2
+ + 2.0 * rho * v2 * v2_y
+ # stress tensor from y-direction
+ - v1_xy * mu_
+ - v2_xx * mu_
+ - 4.0 / 3.0 * v2_yy * mu_
+ + 2.0 / 3.0 * v1_xy * mu_ )
+ # total energy equation
+ du4 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x)
+ + v2_y * (E + p) + v2 * (E_y + p_y)
+ # stress tensor and temperature gradient terms from x-direction
+ - 4.0 / 3.0 * v1_xx * v1 * mu_
+ + 2.0 / 3.0 * v2_xy * v1 * mu_
+ - 4.0 / 3.0 * v1_x * v1_x * mu_
+ + 2.0 / 3.0 * v2_y * v1_x * mu_
+ - v1_xy * v2 * mu_
+ - v2_xx * v2 * mu_
+ - v1_y * v2_x * mu_
+ - v2_x * v2_x * mu_
+ - T_const * inv_rho_cubed * ( p_xx * rho * rho
+ - 2.0 * p_x * rho * rho_x
+ + 2.0 * p * rho_x * rho_x
+ - p * rho * rho_xx ) * mu_
+ # stress tensor and temperature gradient terms from y-direction
+ - v1_yy * v1 * mu_
+ - v2_xy * v1 * mu_
+ - v1_y * v1_y * mu_
+ - v2_x * v1_y * mu_
+ - 4.0 / 3.0 * v2_yy * v2 * mu_
+ + 2.0 / 3.0 * v1_xy * v2 * mu_
+ - 4.0 / 3.0 * v2_y * v2_y * mu_
+ + 2.0 / 3.0 * v1_x * v2_y * mu_
+ - T_const * inv_rho_cubed * ( p_yy * rho * rho
+ - 2.0 * p_y * rho * rho_y
+ + 2.0 * p * rho_y * rho_y
+ - p * rho * rho_yy ) * mu_ )
+
+ return SVector(du1, du2, du3, du4)
+end
+
+initial_condition = initial_condition_navier_stokes_convergence_test
+
+# BC types
+velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3])
+heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
+boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
+
+# define inviscid boundary conditions
+boundary_conditions = Dict(:y_neg => boundary_condition_slip_wall,
+ :y_pos => boundary_condition_slip_wall)
+
+# define viscous boundary conditions
+boundary_conditions_parabolic = Dict(:y_neg => boundary_condition_top_bottom,
+ :y_pos => boundary_condition_top_bottom)
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+
+# ###############################################################################
+# # ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 0.5)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+alive_callback = AliveCallback(alive_interval=10)
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
+
diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl
new file mode 100644
index 00000000000..051f4defe54
--- /dev/null
+++ b/examples/p4est_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl
@@ -0,0 +1,82 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the ideal compressible Navier-Stokes equations
+
+# TODO: parabolic; unify names of these accessor functions
+prandtl_number() = 0.72
+mu() = 0.001
+
+equations = CompressibleEulerEquations2D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(),
+ Prandtl=prandtl_number())
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)
+
+coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y))
+coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y))
+
+# Create a uniformly refined mesh
+trees_per_dimension = (4, 4)
+mesh = P4estMesh(trees_per_dimension,
+ polydeg=3, initial_refinement_level=2,
+ coordinates_min=coordinates_min, coordinates_max=coordinates_max,
+ periodicity=(false, false))
+
+function initial_condition_cavity(x, t, equations::CompressibleEulerEquations2D)
+ Ma = 0.1
+ rho = 1.0
+ u, v = 0.0, 0.0
+ p = 1.0 / (Ma^2 * equations.gamma)
+ return prim2cons(SVector(rho, u, v, p), equations)
+end
+initial_condition = initial_condition_cavity
+
+# BC types
+velocity_bc_lid = NoSlip((x, t, equations) -> SVector(1.0, 0.0))
+velocity_bc_cavity = NoSlip((x, t, equations) -> SVector(0.0, 0.0))
+heat_bc = Adiabatic((x, t, equations) -> 0.0)
+boundary_condition_lid = BoundaryConditionNavierStokesWall(velocity_bc_lid, heat_bc)
+boundary_condition_cavity = BoundaryConditionNavierStokesWall(velocity_bc_cavity, heat_bc)
+
+# define periodic boundary conditions everywhere
+boundary_conditions = Dict( :x_neg => boundary_condition_slip_wall,
+ :y_neg => boundary_condition_slip_wall,
+ :y_pos => boundary_condition_slip_wall,
+ :x_pos => boundary_condition_slip_wall)
+
+boundary_conditions_parabolic = Dict( :x_neg => boundary_condition_cavity,
+ :y_neg => boundary_condition_cavity,
+ :y_pos => boundary_condition_lid,
+ :x_pos => boundary_condition_cavity)
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
+ initial_condition, solver;
+ boundary_conditions=(boundary_conditions,
+ boundary_conditions_parabolic))
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 25.0)
+ode = semidiscretize(semi, tspan);
+
+summary_callback = SummaryCallback()
+alive_callback = AliveCallback(alive_interval=100)
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+callbacks = CallbackSet(summary_callback, alive_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
+
+
diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
index e73a8cda9b8..73ac47ed1e3 100644
--- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
+++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
@@ -365,6 +365,7 @@ function prolong2interfaces!(cache_parabolic, flux_viscous,
return nothing
end
+# This version is used for divergence flux computations
function calc_interface_flux!(surface_flux_values,
mesh::P4estMesh{2}, equations_parabolic,
dg::DG, cache_parabolic)
@@ -405,7 +406,7 @@ function calc_interface_flux!(surface_flux_values,
end
for node in eachnode(dg)
- # We prolong the viscous flux dotted with respect the outward normal on the
+ # We prolong the viscous flux dotted with respect the outward normal on the
# primary element. We assume a BR-1 type of flux.
viscous_flux_normal_ll, viscous_flux_normal_rr = get_surface_node_vars(cache_parabolic.interfaces.u,
equations_parabolic,
@@ -446,6 +447,7 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
# a start value and a step size to get the correct face and orientation.
element = boundaries.neighbor_ids[boundary]
node_indices = boundaries.node_indices[boundary]
+ direction = indices2direction(node_indices)
i_node_start, i_node_step = index_to_start_step_2d(node_indices[1], index_range)
j_node_start, j_node_step = index_to_start_step_2d(node_indices[2], index_range)
@@ -454,15 +456,12 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
j_node = j_node_start
for i in eachnode(dg)
# this is the outward normal direction on the primary element
- normal_direction = get_normal_direction(primary_direction,
- contravariant_vectors,
- i_node, j_node, primary_element)
+ normal_direction = get_normal_direction(direction, contravariant_vectors,
+ i_node, j_node, element)
for v in eachvariable(equations_parabolic)
- flux_viscous = SVector(flux_viscous_x[v, i_primary, j_primary,
- primary_element],
- flux_viscous_y[v, i_primary, j_primary,
- primary_element])
+ flux_viscous = SVector(flux_viscous_x[v, i_node, j_node, element],
+ flux_viscous_y[v, i_node, j_node, element])
boundaries.u[v, i, boundary] = dot(flux_viscous, normal_direction)
end
@@ -470,6 +469,124 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
j_node += j_node_step
end
end
+ return nothing
+end
+
+function calc_boundary_flux_gradients!(cache, t,
+ boundary_condition::Union{BoundaryConditionPeriodic,
+ BoundaryConditionDoNothing
+ },
+ mesh::P4estMesh, equations, surface_integral, dg::DG)
+ @assert isempty(eachboundary(dg, cache))
+end
+
+# Function barrier for type stability
+function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh,
+ equations, surface_integral, dg::DG)
+ (; boundary_condition_types, boundary_indices) = boundary_conditions
+ calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
+ Gradient(), mesh, equations, surface_integral, dg)
return nothing
end
+
+function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh,
+ equations, surface_integral, dg::DG)
+ (; boundary_condition_types, boundary_indices) = boundary_conditions
+
+ calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
+ Divergence(), mesh, equations, surface_integral, dg)
+ return nothing
+end
+
+# Iterate over tuples of boundary condition types and associated indices
+# in a type-stable way using "lispy tuple programming".
+function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any},
+ BC_indices::NTuple{N, Vector{Int}},
+ operator_type,
+ mesh::P4estMesh,
+ equations, surface_integral, dg::DG) where {N}
+ # Extract the boundary condition type and index vector
+ boundary_condition = first(BCs)
+ boundary_condition_indices = first(BC_indices)
+ # Extract the remaining types and indices to be processed later
+ remaining_boundary_conditions = Base.tail(BCs)
+ remaining_boundary_condition_indices = Base.tail(BC_indices)
+
+ # process the first boundary condition type
+ calc_boundary_flux!(cache, t, boundary_condition, boundary_condition_indices,
+ operator_type, mesh, equations, surface_integral, dg)
+
+ # recursively call this method with the unprocessed boundary types
+ calc_boundary_flux_by_type!(cache, t, remaining_boundary_conditions,
+ remaining_boundary_condition_indices,
+ operator_type,
+ mesh, equations, surface_integral, dg)
+
+ return nothing
+end
+
+# terminate the type-stable iteration over tuples
+function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{},
+ operator_type, mesh::P4estMesh, equations,
+ surface_integral, dg::DG)
+ nothing
+end
+
+function calc_boundary_flux!(cache, t,
+ boundary_condition_parabolic, # works with Dict types
+ boundary_condition_indices,
+ operator_type, mesh::P4estMesh{2},
+ equations_parabolic::AbstractEquationsParabolic,
+ surface_integral, dg::DG)
+ (; boundaries) = cache
+ (; node_coordinates, surface_flux_values) = cache.elements
+ (; contravariant_vectors) = cache.elements
+ index_range = eachnode(dg)
+
+ @threaded for local_index in eachindex(boundary_condition_indices)
+ # Use the local index to get the global boundary index from the pre-sorted list
+ boundary_index = boundary_condition_indices[local_index]
+
+ # Get information on the adjacent element, compute the surface fluxes,
+ # and store them
+ element = boundaries.neighbor_ids[boundary_index]
+ node_indices = boundaries.node_indices[boundary_index]
+ direction_index = indices2direction(node_indices)
+
+ i_node_start, i_node_step = index_to_start_step_2d(node_indices[1], index_range)
+ j_node_start, j_node_step = index_to_start_step_2d(node_indices[2], index_range)
+
+ i_node = i_node_start
+ j_node = j_node_start
+ for node_index in eachnode(dg)
+ # Extract solution data from boundary container
+ u_inner = get_node_vars(boundaries.u, equations_parabolic, dg, node_index,
+ boundary_index)
+
+ # Outward-pointing normal direction (not normalized)
+ normal_direction = get_normal_direction(direction_index, contravariant_vectors,
+ i_node, j_node, element)
+
+ # TODO: revisit if we want more general boundary treatments.
+ # This assumes the gradient numerical flux at the boundary is the gradient variable,
+ # which is consistent with BR1, LDG.
+ flux_inner = u_inner
+
+ # Coordinates at boundary node
+ x = get_node_coords(node_coordinates, equations_parabolic, dg, i_node, j_node,
+ element)
+
+ flux_ = boundary_condition_parabolic(flux_inner, u_inner, normal_direction,
+ x, t, operator_type, equations_parabolic)
+
+ # Copy flux to element storage in the correct orientation
+ for v in eachvariable(equations_parabolic)
+ surface_flux_values[v, node_index, direction_index, element] = flux_[v]
+ end
+
+ i_node += i_node_step
+ j_node += j_node_step
+ end
+ end
+end
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index b0ac63d4ce9..471b976e990 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -200,6 +200,38 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "P4estMesh2D: elixir_advection_diffusion_periodic_curved.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_advection_diffusion_periodic_curved.jl"),
+ trees_per_dimension = (1, 1), initial_refinement_level = 2, tspan=(0.0, 0.5),
+ l2 = [0.012380458938507371],
+ linf = [0.10860506906472567]
+ )
+ end
+
+ @trixi_testset "P4estMesh2D: elixir_advection_diffusion_nonperiodic_curved.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_advection_diffusion_nonperiodic_curved.jl"),
+ trees_per_dimension = (1, 1), initial_refinement_level = 2, tspan=(0.0, 0.5),
+ l2 = [0.04933902988507035],
+ linf = [0.2550261714590271]
+ )
+ end
+
+ @trixi_testset "P4estMesh2D: elixir_navierstokes_convergence.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_navierstokes_convergence.jl"),
+ initial_refinement_level = 1, tspan=(0.0, 0.2),
+ l2 = [0.0003811978985836709, 0.0005874314969169538, 0.0009142898787923481, 0.0011613918899727263],
+ linf = [0.0021633623982135752, 0.009484348274135372, 0.004231572066492217, 0.011661660275365193]
+ )
+ end
+
+ @trixi_testset "P4estMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_navierstokes_lid_driven_cavity.jl"),
+ initial_refinement_level = 2, tspan=(0.0, 0.5),
+ l2 = [0.00028716166408816073, 0.08101204560401647, 0.02099595625377768, 0.05008149754143295],
+ linf = [0.014804500261322406, 0.9513271652357098, 0.7223919625994717, 1.4846907331004786]
+ )
+ end
+
end
# Clean up afterwards: delete Trixi.jl output directory
From 87a16931fdcafd488b05d4009d476250f0b98a4f Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Mon, 26 Jun 2023 12:08:58 +0200
Subject: [PATCH 020/263] fix some typos in docstrings of flux splittings
(#1550)
---
src/equations/compressible_euler_1d.jl | 6 +++---
src/equations/compressible_euler_2d.jl | 6 +++---
src/equations/compressible_euler_3d.jl | 2 +-
src/equations/inviscid_burgers_1d.jl | 2 +-
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/equations/compressible_euler_1d.jl b/src/equations/compressible_euler_1d.jl
index f484f26a588..15f7a2cb4c4 100644
--- a/src/equations/compressible_euler_1d.jl
+++ b/src/equations/compressible_euler_1d.jl
@@ -374,7 +374,7 @@ Splitting of the compressible Euler flux of Steger and Warming.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
@@ -462,7 +462,7 @@ it proved the most robust in practice.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
@@ -555,7 +555,7 @@ are to handle flows at the low Mach number limit.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl
index 89f04ef1e05..05987c510b8 100644
--- a/src/equations/compressible_euler_2d.jl
+++ b/src/equations/compressible_euler_2d.jl
@@ -694,7 +694,7 @@ Splitting of the compressible Euler flux of Steger and Warming.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
@@ -826,7 +826,7 @@ it proved the most robust in practice.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
@@ -924,7 +924,7 @@ to Burgers' equation.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
diff --git a/src/equations/compressible_euler_3d.jl b/src/equations/compressible_euler_3d.jl
index cd081cfc42a..2085811f832 100644
--- a/src/equations/compressible_euler_3d.jl
+++ b/src/equations/compressible_euler_3d.jl
@@ -770,7 +770,7 @@ Splitting of the compressible Euler flux of Steger and Warming.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
diff --git a/src/equations/inviscid_burgers_1d.jl b/src/equations/inviscid_burgers_1d.jl
index 6a2cfb6aa8e..f2387f26ba7 100644
--- a/src/equations/inviscid_burgers_1d.jl
+++ b/src/equations/inviscid_burgers_1d.jl
@@ -137,7 +137,7 @@ and `f⁻ = 0.5 (f - λ u)` where `λ = abs(u)`.
Returns a tuple of the fluxes "minus" (associated with waves going into the
negative axis direction) and "plus" (associated with waves going into the
positive axis direction). If only one of the fluxes is required, use the
-function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}`.
+function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
!!! warning "Experimental implementation (upwind SBP)"
This is an experimental feature and may change in future releases.
From fdccbb17aaddd31a935ee7560fd4ea506a6d93ad Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 27 Jun 2023 07:43:59 +0200
Subject: [PATCH 021/263] Bump crate-ci/typos from 1.15.1 to 1.15.6 (#1552)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.15.1 to 1.15.6.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.15.1...v1.15.6)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index 75886465f85..93bee1ce4fc 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.15.1
+ uses: crate-ci/typos@v1.15.6
From 13b26a3d5c27c6c8040b56a56cb35b66f0e3bb42 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Jul 2023 03:25:15 +0200
Subject: [PATCH 022/263] Bump crate-ci/typos from 1.15.6 to 1.15.10 (#1559)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.15.6 to 1.15.10.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.15.6...v1.15.10)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index 93bee1ce4fc..90bd9366b50 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.15.6
+ uses: crate-ci/typos@v1.15.10
From 58fbab4df7bdfaf161fff16214f629a10426532b Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 5 Jul 2023 16:06:48 +0200
Subject: [PATCH 023/263] fix typo in docs (#1560)
---
docs/literate/src/files/shock_capturing.jl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/literate/src/files/shock_capturing.jl b/docs/literate/src/files/shock_capturing.jl
index b165f7ec8bd..afa34cbf06a 100644
--- a/docs/literate/src/files/shock_capturing.jl
+++ b/docs/literate/src/files/shock_capturing.jl
@@ -48,7 +48,7 @@
# with the total energy $\mathbb{E}=\max\big(\frac{m_N^2}{\sum_{j=0}^N m_j^2}, \frac{m_{N-1}^2}{\sum_{j=0}^{N-1} m_j^2}\big)$,
# threshold $\mathbb{T}= 0.5 * 10^{-1.8*(N+1)^{1/4}}$ and parameter $s=ln\big(\frac{1-0.0001}{0.0001}\big)\approx 9.21024$.
-# For computational efficiency, $\alpha_{min}$ is introduced und used for
+# For computational efficiency, $\alpha_{min}$ is introduced and used for
# ```math
# \tilde{\alpha} = \begin{cases}
# 0, & \text{if } \alpha<\alpha_{min}\\
From 3bd55515a03dac926446cbb8ee41edd21d9baec0 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sat, 8 Jul 2023 06:16:24 +0200
Subject: [PATCH 024/263] set version to v0.5.31
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 0edba6b681c..81657e868db 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.31-pre"
+version = "0.5.31"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From dc364ebc665c7f0ab74601ba0041618dddbabc18 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sat, 8 Jul 2023 06:16:45 +0200
Subject: [PATCH 025/263] set development version to v0.5.32-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 81657e868db..828f4778f74 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.31"
+version = "0.5.32-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 766e3f94465f48608c92d1fe91cd46db4c31c362 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 11 Jul 2023 07:14:30 +0200
Subject: [PATCH 026/263] Bump crate-ci/typos from 1.15.10 to 1.16.0 (#1563)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.15.10 to 1.16.0.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.15.10...v1.16.0)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index 90bd9366b50..bb5a32f72ee 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.15.10
+ uses: crate-ci/typos@v1.16.0
From 5ff677c1d246e7a500ab845201bc328d1d3bde92 Mon Sep 17 00:00:00 2001
From: Lars Christmann
Date: Tue, 11 Jul 2023 18:53:25 +0200
Subject: [PATCH 027/263] Implement upwind flux for linearized Euler equations
(#1557)
* Enable input checks for LEE keyword constructor
* Extend LEE implementation to curved meshes
* Implement upwind flux for linearized Euler equations
* Add upwind flux examples and tests
* Fix comments in linearized Euler elixirs
* Clarify LEE Gaussian source elixir
* Rename `flux_upwind` to `flux_godunov`
* Add parentheses around multiline expressions
* Add consistency checks for LEE Godunov flux
* Explain odd mean values in more detail
* Use normalized normal vector to simplify flux
* Add docstring for LEE upwind flux
* Update examples/p4est_2d_dgsem/elixir_linearizedeuler_gaussian_source.jl
Co-authored-by: Michael Schlottke-Lakemper
---------
Co-authored-by: Michael Schlottke-Lakemper
---
.../elixir_linearizedeuler_gaussian_source.jl | 89 ++++++++
.../elixir_linearizedeuler_gauss_wall.jl | 68 ++++++
src/equations/linearized_euler_2d.jl | 212 +++++++++++++++++-
test/test_p4est_2d.jl | 5 +
test/test_tree_2d_linearizedeuler.jl | 6 +
test/test_unit.jl | 20 ++
6 files changed, 399 insertions(+), 1 deletion(-)
create mode 100644 examples/p4est_2d_dgsem/elixir_linearizedeuler_gaussian_source.jl
create mode 100644 examples/tree_2d_dgsem/elixir_linearizedeuler_gauss_wall.jl
diff --git a/examples/p4est_2d_dgsem/elixir_linearizedeuler_gaussian_source.jl b/examples/p4est_2d_dgsem/elixir_linearizedeuler_gaussian_source.jl
new file mode 100644
index 00000000000..ba2ec827778
--- /dev/null
+++ b/examples/p4est_2d_dgsem/elixir_linearizedeuler_gaussian_source.jl
@@ -0,0 +1,89 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+# Based on the TreeMesh example `elixir_acoustics_gaussian_source.jl`.
+# The acoustic perturbation equations have been replaced with the linearized Euler
+# equations and instead of the Cartesian `TreeMesh` a rotated `P4estMesh` is used
+
+# Oscillating Gaussian-shaped source terms
+function source_terms_gauss(u, x, t, equations::LinearizedEulerEquations2D)
+ r = 0.1
+ A = 1.0
+ f = 2.0
+
+ # Velocity sources
+ s2 = 0.0
+ s3 = 0.0
+ # Density and pressure source
+ s1 = s4 = exp(-(x[1]^2 + x[2]^2) / (2 * r^2)) * A * sin(2 * pi * f * t)
+
+ return SVector(s1, s2, s3, s4)
+end
+
+initial_condition_zero(x, t, equations::LinearizedEulerEquations2D) = SVector(0.0, 0.0, 0.0, 0.0)
+
+###############################################################################
+# semidiscretization of the linearized Euler equations
+
+# Create a domain that is a 30° rotated version of [-3, 3]^2
+c = cospi(2 * 30.0 / 360.0)
+s = sinpi(2 * 30.0 / 360.0)
+rot_mat = Trixi.SMatrix{2, 2}([c -s; s c])
+mapping(xi, eta) = rot_mat * SVector(3.0*xi, 3.0*eta)
+
+# Mean density and speed of sound are slightly off from 1.0 to allow proper verification of
+# curved LEE implementation using this elixir (some things in the LEE cancel if both are 1.0)
+equations = LinearizedEulerEquations2D(v_mean_global=Tuple(rot_mat * SVector(-0.5, 0.25)),
+ c_mean_global=1.02, rho_mean_global=1.01)
+
+initial_condition = initial_condition_zero
+
+# Create DG solver with polynomial degree = 3 and upwind flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_godunov)
+
+# Create a uniformly refined mesh with periodic boundaries
+trees_per_dimension = (4, 4)
+mesh = P4estMesh(trees_per_dimension, polydeg=1,
+ mapping=mapping,
+ periodicity=true, initial_refinement_level=2)
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ source_terms=source_terms_gauss)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 2.0
+tspan = (0.0, 2.0)
+ode = semidiscretize(semi, tspan)
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
+# and resets the timers
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
+analysis_callback = AnalysisCallback(semi, interval=100)
+
+# The SaveSolutionCallback allows to save the solution to a file in regular intervals
+save_solution = SaveSolutionCallback(interval=100)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
+stepsize_callback = StepsizeCallback(cfl=0.5)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback)
+
+
+###############################################################################
+# run the simulation
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Print the timer summary
+summary_callback()
diff --git a/examples/tree_2d_dgsem/elixir_linearizedeuler_gauss_wall.jl b/examples/tree_2d_dgsem/elixir_linearizedeuler_gauss_wall.jl
new file mode 100644
index 00000000000..14fe201a291
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_linearizedeuler_gauss_wall.jl
@@ -0,0 +1,68 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the linearized Euler equations
+
+equations = LinearizedEulerEquations2D(v_mean_global=(0.5, 0.0), c_mean_global=1.0,
+ rho_mean_global=1.0)
+
+# Create DG solver with polynomial degree = 5 and upwind flux as surface flux
+solver = DGSEM(polydeg=5, surface_flux=flux_godunov)
+
+coordinates_min = (-100.0, 0.0) # minimum coordinates (min(x), min(y))
+coordinates_max = (100.0, 200.0) # maximum coordinates (max(x), max(y))
+
+# Create a uniformly refined mesh with periodic boundaries
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=4,
+ n_cells_max=100_000,
+ periodicity=false)
+
+function initial_condition_gauss_wall(x, t, equations::LinearizedEulerEquations2D)
+ v1_prime = 0.0
+ v2_prime = 0.0
+ rho_prime = p_prime = exp(-log(2) * (x[1]^2 + (x[2] - 25)^2) / 25)
+ return SVector(rho_prime, v1_prime, v2_prime, p_prime)
+end
+initial_condition = initial_condition_gauss_wall
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions=boundary_condition_wall)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 30.0
+tspan = (0.0, 30.0)
+ode = semidiscretize(semi, tspan)
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
+# and resets the timers
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
+analysis_callback = AnalysisCallback(semi, interval=100)
+
+# The SaveSolutionCallback allows to save the solution to a file in regular intervals
+save_solution = SaveSolutionCallback(interval=100)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
+stepsize_callback = StepsizeCallback(cfl=0.7)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks)
+
+# Print the timer summary
+summary_callback()
diff --git a/src/equations/linearized_euler_2d.jl b/src/equations/linearized_euler_2d.jl
index cd681365cae..e478c32bd29 100644
--- a/src/equations/linearized_euler_2d.jl
+++ b/src/equations/linearized_euler_2d.jl
@@ -53,7 +53,7 @@ end
function LinearizedEulerEquations2D(; v_mean_global::NTuple{2, <:Real},
c_mean_global::Real, rho_mean_global::Real)
- return LinearizedEulerEquations2D(SVector(v_mean_global), c_mean_global,
+ return LinearizedEulerEquations2D(v_mean_global, c_mean_global,
rho_mean_global)
end
@@ -126,6 +126,24 @@ end
return SVector(f1, f2, f3, f4)
end
+# Calculate 1D flux for a single point
+@inline function flux(u, normal_direction::AbstractVector,
+ equations::LinearizedEulerEquations2D)
+ @unpack v_mean_global, c_mean_global, rho_mean_global = equations
+ rho_prime, v1_prime, v2_prime, p_prime = u
+
+ v_mean_normal = v_mean_global[1] * normal_direction[1] +
+ v_mean_global[2] * normal_direction[2]
+ v_prime_normal = v1_prime * normal_direction[1] + v2_prime * normal_direction[2]
+
+ f1 = v_mean_normal * rho_prime + rho_mean_global * v_prime_normal
+ f2 = v_mean_normal * v1_prime + normal_direction[1] * p_prime / rho_mean_global
+ f3 = v_mean_normal * v2_prime + normal_direction[2] * p_prime / rho_mean_global
+ f4 = v_mean_normal * p_prime + c_mean_global^2 * rho_mean_global * v_prime_normal
+
+ return SVector(f1, f2, f3, f4)
+end
+
@inline have_constant_speed(::LinearizedEulerEquations2D) = True()
@inline function max_abs_speeds(equations::LinearizedEulerEquations2D)
@@ -143,6 +161,198 @@ end
end
end
+@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::LinearizedEulerEquations2D)
+ @unpack v_mean_global, c_mean_global = equations
+ v_mean_normal = normal_direction[1] * v_mean_global[1] +
+ normal_direction[2] * v_mean_global[2]
+ return abs(v_mean_normal) + c_mean_global * norm(normal_direction)
+end
+
+@doc raw"""
+ flux_godunov(u_ll, u_rr, orientation_or_normal_direction,
+ equations::LinearizedEulerEquations2D)
+
+An upwind flux for the linearized Euler equations based on diagonalization of the physical
+flux matrix. Given the physical flux ``Au``, ``A=T \Lambda T^{-1}`` with
+``\Lambda`` being a diagonal matrix that holds the eigenvalues of ``A``, decompose
+``\Lambda = \Lambda^+ + \Lambda^-`` where ``\Lambda^+`` and ``\Lambda^-`` are diagonal
+matrices holding the positive and negative eigenvalues of ``A``, respectively. Then for
+left and right states ``u_L, u_R``, the numerical flux calculated by this function is given
+by ``A^+ u_L + A^- u_R`` where ``A^{\pm} = T \Lambda^{\pm} T^{-1}``.
+
+The diagonalization of the flux matrix can be found in
+- R. F. Warming, Richard M. Beam and B. J. Hyett (1975)
+ Diagonalization and simultaneous symmetrization of the gas-dynamic matrices
+ [DOI: 10.1090/S0025-5718-1975-0388967-5](https://doi.org/10.1090/S0025-5718-1975-0388967-5)
+"""
+@inline function flux_godunov(u_ll, u_rr, orientation::Integer,
+ equations::LinearizedEulerEquations2D)
+ @unpack v_mean_global, rho_mean_global, c_mean_global = equations
+ v1_mean = v_mean_global[1]
+ v2_mean = v_mean_global[2]
+
+ rho_prime_ll, v1_prime_ll, v2_prime_ll, p_prime_ll = u_ll
+ rho_prime_rr, v1_prime_rr, v2_prime_rr, p_prime_rr = u_rr
+
+ if orientation == 1
+ # Eigenvalues of the flux matrix
+ lambda1 = v1_mean
+ lambda2 = v1_mean - c_mean_global
+ lambda3 = v1_mean + c_mean_global
+
+ lambda1_p = positive_part(lambda1)
+ lambda2_p = positive_part(lambda2)
+ lambda3_p = positive_part(lambda3)
+ lambda2p3_half_p = 0.5 * (lambda2_p + lambda3_p)
+ lambda3m2_half_p = 0.5 * (lambda3_p - lambda2_p)
+
+ lambda1_m = negative_part(lambda1)
+ lambda2_m = negative_part(lambda2)
+ lambda3_m = negative_part(lambda3)
+ lambda2p3_half_m = 0.5 * (lambda2_m + lambda3_m)
+ lambda3m2_half_m = 0.5 * (lambda3_m - lambda2_m)
+
+ f1p = (lambda1_p * rho_prime_ll +
+ lambda3m2_half_p / c_mean_global * rho_mean_global * v1_prime_ll +
+ (lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)
+ f2p = (lambda2p3_half_p * v1_prime_ll +
+ lambda3m2_half_p / c_mean_global * p_prime_ll / rho_mean_global)
+ f3p = lambda1_p * v2_prime_ll
+ f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v1_prime_ll +
+ lambda2p3_half_p * p_prime_ll)
+
+ f1m = (lambda1_m * rho_prime_rr +
+ lambda3m2_half_m / c_mean_global * rho_mean_global * v1_prime_rr +
+ (lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)
+ f2m = (lambda2p3_half_m * v1_prime_rr +
+ lambda3m2_half_m / c_mean_global * p_prime_rr / rho_mean_global)
+ f3m = lambda1_m * v2_prime_rr
+ f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v1_prime_rr +
+ lambda2p3_half_m * p_prime_rr)
+
+ f1 = f1p + f1m
+ f2 = f2p + f2m
+ f3 = f3p + f3m
+ f4 = f4p + f4m
+ else # orientation == 2
+ # Eigenvalues of the flux matrix
+ lambda1 = v2_mean
+ lambda2 = v2_mean - c_mean_global
+ lambda3 = v2_mean + c_mean_global
+
+ lambda1_p = positive_part(lambda1)
+ lambda2_p = positive_part(lambda2)
+ lambda3_p = positive_part(lambda3)
+ lambda2p3_half_p = 0.5 * (lambda2_p + lambda3_p)
+ lambda3m2_half_p = 0.5 * (lambda3_p - lambda2_p)
+
+ lambda1_m = negative_part(lambda1)
+ lambda2_m = negative_part(lambda2)
+ lambda3_m = negative_part(lambda3)
+ lambda2p3_half_m = 0.5 * (lambda2_m + lambda3_m)
+ lambda3m2_half_m = 0.5 * (lambda3_m - lambda2_m)
+
+ f1p = (lambda1_p * rho_prime_ll +
+ lambda3m2_half_p / c_mean_global * rho_mean_global * v2_prime_ll +
+ (lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)
+ f2p = lambda1_p * v1_prime_ll
+ f3p = (lambda2p3_half_p * v2_prime_ll +
+ lambda3m2_half_p / c_mean_global * p_prime_ll / rho_mean_global)
+ f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v2_prime_ll +
+ lambda2p3_half_p * p_prime_ll)
+
+ f1m = (lambda1_m * rho_prime_rr +
+ lambda3m2_half_m / c_mean_global * rho_mean_global * v2_prime_rr +
+ (lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)
+ f2m = lambda1_m * v1_prime_rr
+ f3m = (lambda2p3_half_m * v2_prime_rr +
+ lambda3m2_half_m / c_mean_global * p_prime_rr / rho_mean_global)
+ f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v2_prime_rr +
+ lambda2p3_half_m * p_prime_rr)
+
+ f1 = f1p + f1m
+ f2 = f2p + f2m
+ f3 = f3p + f3m
+ f4 = f4p + f4m
+ end
+
+ return SVector(f1, f2, f3, f4)
+end
+
+@inline function flux_godunov(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::LinearizedEulerEquations2D)
+ @unpack v_mean_global, rho_mean_global, c_mean_global = equations
+ rho_prime_ll, v1_prime_ll, v2_prime_ll, p_prime_ll = u_ll
+ rho_prime_rr, v1_prime_rr, v2_prime_rr, p_prime_rr = u_rr
+
+ # Do not use `normalize` since we use `norm_` later to scale the eigenvalues
+ norm_ = norm(normal_direction)
+ normal_vector = normal_direction / norm_
+
+ # Use normalized vector here, scaling is applied via eigenvalues of the flux matrix
+ v_mean_normal = v_mean_global[1] * normal_vector[1] +
+ v_mean_global[2] * normal_vector[2]
+ v_prime_normal_ll = v1_prime_ll * normal_vector[1] + v2_prime_ll * normal_vector[2]
+ v_prime_normal_rr = v1_prime_rr * normal_vector[1] + v2_prime_rr * normal_vector[2]
+
+ # Eigenvalues of the flux matrix
+ lambda1 = v_mean_normal * norm_
+ lambda2 = (v_mean_normal - c_mean_global) * norm_
+ lambda3 = (v_mean_normal + c_mean_global) * norm_
+
+ lambda1_p = positive_part(lambda1)
+ lambda2_p = positive_part(lambda2)
+ lambda3_p = positive_part(lambda3)
+ lambda2p3_half_p = 0.5 * (lambda2_p + lambda3_p)
+ lambda3m2_half_p = 0.5 * (lambda3_p - lambda2_p)
+
+ lambda1_m = negative_part(lambda1)
+ lambda2_m = negative_part(lambda2)
+ lambda3_m = negative_part(lambda3)
+ lambda2p3_half_m = 0.5 * (lambda2_m + lambda3_m)
+ lambda3m2_half_m = 0.5 * (lambda3_m - lambda2_m)
+
+ f1p = (lambda1_p * rho_prime_ll +
+ lambda3m2_half_p / c_mean_global * rho_mean_global * v_prime_normal_ll +
+ (lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)
+ f2p = (((lambda1_p * normal_vector[2]^2 +
+ lambda2p3_half_p * normal_vector[1]^2) * v1_prime_ll +
+ (lambda2p3_half_p - lambda1_p) * prod(normal_vector) * v2_prime_ll) +
+ lambda3m2_half_p / c_mean_global * normal_vector[1] * p_prime_ll /
+ rho_mean_global)
+ f3p = (((lambda1_p * normal_vector[1]^2 +
+ lambda2p3_half_p * normal_vector[2]^2) * v2_prime_ll +
+ (lambda2p3_half_p - lambda1_p) * prod(normal_vector) * v1_prime_ll) +
+ lambda3m2_half_p / c_mean_global * normal_vector[2] * p_prime_ll /
+ rho_mean_global)
+ f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v_prime_normal_ll +
+ lambda2p3_half_p * p_prime_ll)
+
+ f1m = (lambda1_m * rho_prime_rr +
+ lambda3m2_half_m / c_mean_global * rho_mean_global * v_prime_normal_rr +
+ (lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)
+ f2m = (((lambda1_m * normal_vector[2]^2 +
+ lambda2p3_half_m * normal_vector[1]^2) * v1_prime_rr +
+ (lambda2p3_half_m - lambda1_m) * prod(normal_vector) * v2_prime_rr) +
+ lambda3m2_half_m / c_mean_global * normal_vector[1] * p_prime_rr /
+ rho_mean_global)
+ f3m = (((lambda1_m * normal_vector[1]^2 +
+ lambda2p3_half_m * normal_vector[2]^2) * v2_prime_rr +
+ (lambda2p3_half_m - lambda1_m) * prod(normal_vector) * v1_prime_rr) +
+ lambda3m2_half_m / c_mean_global * normal_vector[2] * p_prime_rr /
+ rho_mean_global)
+ f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v_prime_normal_rr +
+ lambda2p3_half_m * p_prime_rr)
+
+ f1 = f1p + f1m
+ f2 = f2p + f2m
+ f3 = f3p + f3m
+ f4 = f4p + f4m
+
+ return SVector(f1, f2, f3, f4)
+end
+
# Convert conservative variables to primitive
@inline cons2prim(u, equations::LinearizedEulerEquations2D) = u
@inline cons2entropy(u, ::LinearizedEulerEquations2D) = u
diff --git a/test/test_p4est_2d.jl b/test/test_p4est_2d.jl
index f66664c7a89..c4ce2619e15 100644
--- a/test/test_p4est_2d.jl
+++ b/test/test_p4est_2d.jl
@@ -164,6 +164,11 @@ isdir(outdir) && rm(outdir, recursive=true)
tspan = (0.0, 0.02))
end
+ @trixi_testset "elixir_linearizedeuler_gaussian_source.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_linearizedeuler_gaussian_source.jl"),
+ l2 = [0.006047938590548741, 0.0040953286019907035, 0.004222698522497298, 0.006269492499336128],
+ linf = [0.06386175207349379, 0.0378926444850457, 0.041759728067967065, 0.06430136016259067])
+ end
end
# Clean up afterwards: delete Trixi.jl output directory
diff --git a/test/test_tree_2d_linearizedeuler.jl b/test/test_tree_2d_linearizedeuler.jl
index 540b3951212..2c5f6dc2cd1 100644
--- a/test/test_tree_2d_linearizedeuler.jl
+++ b/test/test_tree_2d_linearizedeuler.jl
@@ -13,4 +13,10 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_dgsem")
linf = [0.0011006084408365924, 0.0005788678074691855, 0.0005788678074701847, 0.0011006084408365924]
)
end
+
+ @trixi_testset "elixir_linearizedeuler_gauss_wall.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_linearizedeuler_gauss_wall.jl"),
+ l2 = [0.048185623945503485, 0.01941899333212175, 0.019510224816991825, 0.048185623945503485],
+ linf = [1.0392165942153189, 0.18188777290819994, 0.1877028372108587, 1.0392165942153189])
+ end
end
diff --git a/test/test_unit.jl b/test/test_unit.jl
index 2156e9bac32..b0c3e4205e5 100644
--- a/test/test_unit.jl
+++ b/test/test_unit.jl
@@ -670,6 +670,26 @@ isdir(outdir) && rm(outdir, recursive=true)
for normal_direction in normal_directions
@test flux_godunov(u, u, normal_direction, equation) ≈ flux(u, normal_direction, equation)
end
+
+ # Linearized Euler 2D
+ equation = LinearizedEulerEquations2D(v_mean_global=(0.5, -0.7), c_mean_global=1.1,
+ rho_mean_global=1.2)
+ u_values = [SVector(1.0, 0.5, -0.7, 1.0),
+ SVector(1.5, -0.2, 0.1, 5.0),]
+
+ orientations = [1, 2]
+ for orientation in orientations, u in u_values
+ @test flux_godunov(u, u, orientation, equation) ≈ flux(u, orientation, equation)
+ end
+
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+
+ for normal_direction in normal_directions, u in u_values
+ @test flux_godunov(u, u, normal_direction, equation) ≈ flux(u, normal_direction, equation)
+ end
end
@timed_testset "Consistency check for Engquist-Osher flux" begin
From 42732dbd09b21c2e0237ba3f004469b94f3d5600 Mon Sep 17 00:00:00 2001
From: Simon Candelaresi <10759273+SimonCan@users.noreply.github.com>
Date: Tue, 11 Jul 2023 22:44:39 +0100
Subject: [PATCH 028/263] Added load_timestep function. (#1528)
* Added load_timestep function.
Corrected time index in restart simulations.
* Changed doc string for load_timestep to clarify that we read the iteration number.
* Added reading function for dt.
Changed restart example elixir such that they use dt from previous simulation.
* Get attribute 'current_filename' when loading an existing mesh.
This fixes issues with converting from hdf5 into vtk
when rerunning a simulation.
* format
* Update make.jl to include restart simulation documentation.
* Create restart.md.
* Added unformatted docs on how to restart a simulation from an old snapshot.
* Completed restart tutorial.
* Fixed a few typos in the docs for restarting a simulation.
* Minor typo.
* Added myself to the contributor list.
* Update docs/src/restart.md
Co-authored-by: Hendrik Ranocha
* Update docs/src/restart.md
Co-authored-by: Hendrik Ranocha
* Update docs/src/restart.md
Co-authored-by: Hendrik Ranocha
* Update docs/src/restart.md
Co-authored-by: Hendrik Ranocha
* Update docs/src/restart.md
Co-authored-by: Michael Schlottke-Lakemper
* Update docs/src/restart.md
Co-authored-by: Michael Schlottke-Lakemper
* Update restart.md
Added a few links to the restart documentation.
* Update docs/src/restart.md
Co-authored-by: Hendrik Ranocha
* Corrected reference file name.
* Added reference to save solution callback.
---------
Co-authored-by: Hendrik Ranocha
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
---
AUTHORS.md | 1 +
docs/make.jl | 1 +
docs/src/restart.md | 89 +++++++++++++++++++
.../elixir_advection_restart.jl | 16 +++-
.../elixir_advection_restart.jl | 16 +++-
.../elixir_advection_restart.jl | 15 +++-
.../elixir_advection_restart.jl | 16 +++-
.../tree_2d_dgsem/elixir_advection_restart.jl | 16 +++-
.../tree_3d_dgsem/elixir_advection_restart.jl | 16 +++-
.../elixir_euler_restart.jl | 16 +++-
src/Trixi.jl | 2 +-
src/callbacks_step/save_restart.jl | 22 +++++
src/meshes/mesh_io.jl | 1 +
13 files changed, 205 insertions(+), 22 deletions(-)
create mode 100644 docs/src/restart.md
diff --git a/AUTHORS.md b/AUTHORS.md
index 973e311920b..abaa3e7e037 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -24,6 +24,7 @@ are listed in alphabetical order:
* Maximilian D. Bertrand
* Benjamin Bolm
+* Simon Candelaresi
* Jesse Chan
* Lars Christmann
* Christof Czernik
diff --git a/docs/make.jl b/docs/make.jl
index 5069e4dc49a..57629577ddb 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -92,6 +92,7 @@ makedocs(
"Getting started" => [
"Overview" => "overview.md",
"Visualization" => "visualization.md",
+ "Restart simulation" => "restart.md",
],
"Tutorials" => tutorials,
"Basic building blocks" => [
diff --git a/docs/src/restart.md b/docs/src/restart.md
new file mode 100644
index 00000000000..d24d93cb297
--- /dev/null
+++ b/docs/src/restart.md
@@ -0,0 +1,89 @@
+# [Restart simulation](@id restart)
+
+You can continue running an already finished simulation by first
+preparing the simulation for the restart and then performing the restart.
+Here we suppose that in the first run your simulation stops at time 1.0
+and then you want it to run further to time 2.0.
+
+## [Prepare the simulation for a restart](@id restart_preparation)
+In you original elixir you need to specify to write out restart files.
+Those will later be read for the restart of your simulation.
+This is done almost the same way as writing the snapshots using the
+[`SaveSolutionCallback`](@ref) callback.
+For the restart files it is called [`SaveRestartCallback`](@ref):
+```julia
+save_restart = SaveRestartCallback(interval=100,
+ save_final_restart=true)
+```
+Make this part of your `CallbackSet`.
+
+An example is
+[```examples/examples/structured_2d_dgsem/elixir_advection_extended.jl```](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_extended.jl).
+
+
+## [Perform the simulation restart](@id restart_perform)
+Since all of the information about the simulation can be obtained from the
+last snapshot, the restart can be done with relatively few lines
+in an extra elixir file.
+However, some might prefer to keep everything in one elixir and
+conditionals like ```if restart``` with a boolean variable ```restart``` that is user defined.
+
+First we need to define from which file we want to restart, e.g.
+```julia
+restart_file = "restart_000021.h5"
+restart_filename = joinpath("out", restart_file)
+```
+
+Then we load the mesh file:
+```julia
+mesh = load_mesh(restart_filename)
+```
+
+This is then needed for the semidiscretization:
+```julia
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+```
+
+We then define a new time span for the simulation that takes as starting
+time the one form the snapshot:
+```julia
+tspan = (load_time(restart_filename), 2.0)
+```
+
+We now also take the last ```dt```, so that our solver does not need to first find
+one to fulfill the CFL condition:
+```julia
+dt = load_dt(restart_filename)
+```
+
+The ODE that we will pass to the solver is now:
+```julia
+ode = semidiscretize(semi, tspan, restart_filename)
+```
+
+You should now define a [`SaveSolutionCallback`](@ref) similar to the
+[original simulation](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_extended.jl),
+but with ```save_initial_solution=false```, otherwise our initial snapshot will be overwritten.
+If you are using one file for the original simulation and the restart
+you can reuse your [`SaveSolutionCallback`](@ref), but need to set
+```julia
+save_solution.condition.save_initial_solution = false
+```
+
+Before we compute the solution using
+[OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl)
+we need to set the integrator
+and its time step number, e.g.:
+```julia
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, save_everystep=false, callback=callbacks);
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
+```
+
+Now we can compute the solution:
+```julia
+sol = solve!(integrator)
+```
+
+An example is in `[``examples/structured_2d_dgsem/elixir_advection_restart.jl```](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_restart.jl).
diff --git a/examples/p4est_2d_dgsem/elixir_advection_restart.jl b/examples/p4est_2d_dgsem/elixir_advection_restart.jl
index 1906fb2896e..79a35199b83 100644
--- a/examples/p4est_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/p4est_2d_dgsem/elixir_advection_restart.jl
@@ -24,13 +24,23 @@ semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
boundary_conditions=boundary_conditions)
tspan = (load_time(restart_filename), 2.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
+
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
summary_callback() # print the timer summary
diff --git a/examples/p4est_3d_dgsem/elixir_advection_restart.jl b/examples/p4est_3d_dgsem/elixir_advection_restart.jl
index 71b37e9f39b..b27eaab62e2 100644
--- a/examples/p4est_3d_dgsem/elixir_advection_restart.jl
+++ b/examples/p4est_3d_dgsem/elixir_advection_restart.jl
@@ -21,13 +21,23 @@ mesh = load_mesh(restart_filename)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver)
tspan = (load_time(restart_filename), 2.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
+
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
summary_callback() # print the timer summary
diff --git a/examples/structured_2d_dgsem/elixir_advection_restart.jl b/examples/structured_2d_dgsem/elixir_advection_restart.jl
index 2c2a0ef8f51..98c44fac71a 100644
--- a/examples/structured_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/structured_2d_dgsem/elixir_advection_restart.jl
@@ -23,13 +23,22 @@ mesh = load_mesh(restart_filename)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
tspan = (load_time(restart_filename), 2.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
summary_callback() # print the timer summary
diff --git a/examples/structured_3d_dgsem/elixir_advection_restart.jl b/examples/structured_3d_dgsem/elixir_advection_restart.jl
index 39e1a675167..39d28848c77 100644
--- a/examples/structured_3d_dgsem/elixir_advection_restart.jl
+++ b/examples/structured_3d_dgsem/elixir_advection_restart.jl
@@ -21,13 +21,23 @@ mesh = load_mesh(restart_filename)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test, solver)
tspan = (load_time(restart_filename), 2.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
+
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
summary_callback() # print the timer summary
diff --git a/examples/tree_2d_dgsem/elixir_advection_restart.jl b/examples/tree_2d_dgsem/elixir_advection_restart.jl
index 2cb45c0b47e..4ceb5932573 100644
--- a/examples/tree_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/tree_2d_dgsem/elixir_advection_restart.jl
@@ -20,13 +20,23 @@ mesh = load_mesh(restart_filename)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
tspan = (load_time(restart_filename), 2.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks)
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
+
summary_callback() # print the timer summary
diff --git a/examples/tree_3d_dgsem/elixir_advection_restart.jl b/examples/tree_3d_dgsem/elixir_advection_restart.jl
index 83bf4418b98..3061f165874 100644
--- a/examples/tree_3d_dgsem/elixir_advection_restart.jl
+++ b/examples/tree_3d_dgsem/elixir_advection_restart.jl
@@ -20,13 +20,23 @@ mesh = load_mesh(restart_filename)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
tspan = (load_time(restart_filename), 2.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
+
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
summary_callback() # print the timer summary
diff --git a/examples/unstructured_2d_dgsem/elixir_euler_restart.jl b/examples/unstructured_2d_dgsem/elixir_euler_restart.jl
index 2ac67652023..b85cc2c6d70 100644
--- a/examples/unstructured_2d_dgsem/elixir_euler_restart.jl
+++ b/examples/unstructured_2d_dgsem/elixir_euler_restart.jl
@@ -22,14 +22,24 @@ semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
boundary_conditions=boundary_conditions)
tspan = (load_time(restart_filename), 1.0)
+dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
+# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
+save_solution.condition.save_initial_solution = false
+
+integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+
+# Get the last time index and work with that.
+integrator.iter = load_timestep(restart_filename)
+integrator.stats.naccept = integrator.iter
+
###############################################################################
# run the simulation
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
- dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+sol = solve!(integrator)
summary_callback() # print the timer summary
diff --git a/src/Trixi.jl b/src/Trixi.jl
index 66878f4b459..6fc62f50520 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -241,7 +241,7 @@ export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback,
GlmSpeedCallback, LBMCollisionCallback, EulerAcousticsCouplingCallback,
TrivialCallback, AnalysisCallbackCoupled
-export load_mesh, load_time
+export load_mesh, load_time, load_timestep, load_dt
export ControllerThreeLevel, ControllerThreeLevelCombined,
IndicatorLöhner, IndicatorLoehner, IndicatorMax,
diff --git a/src/callbacks_step/save_restart.jl b/src/callbacks_step/save_restart.jl
index e23f58f26ea..f567a5c7fda 100644
--- a/src/callbacks_step/save_restart.jl
+++ b/src/callbacks_step/save_restart.jl
@@ -130,6 +130,28 @@ function load_time(restart_file::AbstractString)
end
end
+"""
+ load_timestep(restart_file::AbstractString)
+
+Load the time step number (`iter` in OrdinaryDiffEq.jl) saved in a `restart_file`.
+"""
+function load_timestep(restart_file::AbstractString)
+ h5open(restart_file, "r") do file
+ read(attributes(file)["timestep"])
+ end
+end
+
+"""
+ load_dt(restart_file::AbstractString)
+
+Load the time step size (`dt` in OrdinaryDiffEq.jl) saved in a `restart_file`.
+"""
+function load_dt(restart_file::AbstractString)
+ h5open(restart_file, "r") do file
+ read(attributes(file)["dt"])
+ end
+end
+
function load_restart_file(semi::AbstractSemidiscretization, restart_file)
load_restart_file(mesh_equations_solver_cache(semi)..., restart_file)
end
diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl
index ede85d80106..da67fe23e0e 100644
--- a/src/meshes/mesh_io.jl
+++ b/src/meshes/mesh_io.jl
@@ -286,6 +286,7 @@ function load_mesh_serial(mesh_file::AbstractString; n_cells_max, RealT)
mesh = StructuredMesh(size, mapping; RealT = RealT, unsaved_changes = false,
mapping_as_string = mapping_as_string)
+ mesh.current_filename = mesh_file
elseif mesh_type == "UnstructuredMesh2D"
mesh_filename, periodicity_ = h5open(mesh_file, "r") do file
return read(attributes(file)["mesh_filename"]),
From a191e39b0d6bc75616aa2113b446b68e3b65ee41 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Thu, 13 Jul 2023 20:28:55 +0200
Subject: [PATCH 029/263] Hll 2 wave improvements non breaking (#1561)
* Add Classical and Naive HLL 2 Wave solver to classic Hyperbolic PDEs
* Format Code
* HLLE wave speeds for SWE
* Fix typos
* Update tests for HLL
* Unit test 1D MHD HLL, HLLE
* Add example for classical HLL 2 wave
* remove plots
* Use lowercase for flux
* Use einfeldt for mhd
* Use hlle for mhd tets
* Missing comma causes failing tests
* Correct bug in SWE 2D Roe eigval comp, unit tests
* format
* Revert "format"
This reverts commit 047a5e75b4a5ee4a0f58a7979d58b26f15f24334.
* format equations
* Add unit tests for HLL naive
* Revert default hll flux
* Rename min_max_speed to min_max_speed_davis and reduce documentation
* Update src/equations/shallow_water_1d.jl: Comments
Co-authored-by: Hendrik Ranocha
* Add published resource for Roe averages for SWE
* Add tests for rotation
* Remove breaking portionv from PR
* fix copy paste error
* Lowercase davis
* Update src/equations/numerical_fluxes.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/numerical_fluxes.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/numerical_fluxes.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/numerical_fluxes.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/numerical_fluxes.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/numerical_fluxes.jl
Co-authored-by: Hendrik Ranocha
* Update test/test_tree_2d_mhd.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/ideal_glm_mhd_1d.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/ideal_glm_mhd_2d.jl
Co-authored-by: Hendrik Ranocha
* Update src/equations/ideal_glm_mhd_3d.jl
Co-authored-by: Hendrik Ranocha
* Update test/test_tree_3d_mhd.jl
Co-authored-by: Hendrik Ranocha
* Remove hll_davis test
* Split consistency checks
* Try to resolve conflict with 5ff677c
* Add tests
* More tests
---------
Co-authored-by: Hendrik Ranocha
---
examples/dgmulti_2d/elixir_euler_bilinear.jl | 2 +-
examples/dgmulti_2d/elixir_euler_curved.jl | 2 +-
.../elixir_euler_triangulate_pkg_mesh.jl | 2 +-
examples/dgmulti_2d/elixir_euler_weakform.jl | 2 +-
.../elixir_euler_weakform_periodic.jl | 2 +-
examples/dgmulti_3d/elixir_euler_curved.jl | 2 +-
examples/dgmulti_3d/elixir_euler_weakform.jl | 2 +-
.../elixir_euler_weakform_periodic.jl | 2 +-
src/Trixi.jl | 2 +-
src/equations/compressible_euler_1d.jl | 19 +-
src/equations/compressible_euler_2d.jl | 43 +-
src/equations/compressible_euler_3d.jl | 50 ++-
src/equations/ideal_glm_mhd_1d.jl | 24 +-
src/equations/ideal_glm_mhd_2d.jl | 67 ++-
src/equations/ideal_glm_mhd_3d.jl | 71 ++++
src/equations/linearized_euler_2d.jl | 38 ++
src/equations/numerical_fluxes.jl | 45 ++-
src/equations/shallow_water_1d.jl | 66 ++-
src/equations/shallow_water_2d.jl | 147 ++++++-
test/test_structured_1d.jl | 8 +
test/test_unit.jl | 381 +++++++++++++++++-
21 files changed, 946 insertions(+), 31 deletions(-)
diff --git a/examples/dgmulti_2d/elixir_euler_bilinear.jl b/examples/dgmulti_2d/elixir_euler_bilinear.jl
index beb5c863971..bdd582610ea 100644
--- a/examples/dgmulti_2d/elixir_euler_bilinear.jl
+++ b/examples/dgmulti_2d/elixir_euler_bilinear.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Quad(), approximation_type = SBP(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralFluxDifferencing(flux_ranocha))
equations = CompressibleEulerEquations2D(1.4)
diff --git a/examples/dgmulti_2d/elixir_euler_curved.jl b/examples/dgmulti_2d/elixir_euler_curved.jl
index 4f1d613b247..a3ba62f1cfb 100644
--- a/examples/dgmulti_2d/elixir_euler_curved.jl
+++ b/examples/dgmulti_2d/elixir_euler_curved.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Quad(), approximation_type = SBP(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralFluxDifferencing(flux_ranocha))
equations = CompressibleEulerEquations2D(1.4)
diff --git a/examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl b/examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl
index 1f35a11bf8e..c10b5e46a14 100644
--- a/examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl
+++ b/examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl
@@ -1,7 +1,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Tri(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralWeakForm())
equations = CompressibleEulerEquations2D(1.4)
diff --git a/examples/dgmulti_2d/elixir_euler_weakform.jl b/examples/dgmulti_2d/elixir_euler_weakform.jl
index 1ecc666c8db..486a30b37f1 100644
--- a/examples/dgmulti_2d/elixir_euler_weakform.jl
+++ b/examples/dgmulti_2d/elixir_euler_weakform.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Tri(), approximation_type = Polynomial(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralWeakForm())
equations = CompressibleEulerEquations2D(1.4)
diff --git a/examples/dgmulti_2d/elixir_euler_weakform_periodic.jl b/examples/dgmulti_2d/elixir_euler_weakform_periodic.jl
index 48cc8070857..c4c83fff642 100644
--- a/examples/dgmulti_2d/elixir_euler_weakform_periodic.jl
+++ b/examples/dgmulti_2d/elixir_euler_weakform_periodic.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Tri(), approximation_type = Polynomial(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralWeakForm())
equations = CompressibleEulerEquations2D(1.4)
diff --git a/examples/dgmulti_3d/elixir_euler_curved.jl b/examples/dgmulti_3d/elixir_euler_curved.jl
index 339d6ce0186..d8c4df5dd64 100644
--- a/examples/dgmulti_3d/elixir_euler_curved.jl
+++ b/examples/dgmulti_3d/elixir_euler_curved.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Hex(), approximation_type=SBP(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralFluxDifferencing(flux_ranocha))
equations = CompressibleEulerEquations3D(1.4)
diff --git a/examples/dgmulti_3d/elixir_euler_weakform.jl b/examples/dgmulti_3d/elixir_euler_weakform.jl
index 4ad9f045eb6..b167377af51 100644
--- a/examples/dgmulti_3d/elixir_euler_weakform.jl
+++ b/examples/dgmulti_3d/elixir_euler_weakform.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Tet(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralWeakForm())
equations = CompressibleEulerEquations3D(1.4)
diff --git a/examples/dgmulti_3d/elixir_euler_weakform_periodic.jl b/examples/dgmulti_3d/elixir_euler_weakform_periodic.jl
index f554167df90..6b17d4bba65 100644
--- a/examples/dgmulti_3d/elixir_euler_weakform_periodic.jl
+++ b/examples/dgmulti_3d/elixir_euler_weakform_periodic.jl
@@ -2,7 +2,7 @@
using Trixi, OrdinaryDiffEq
dg = DGMulti(polydeg = 3, element_type = Tet(), approximation_type = Polynomial(),
- surface_integral = SurfaceIntegralWeakForm(FluxHLL()),
+ surface_integral = SurfaceIntegralWeakForm(flux_hll),
volume_integral = VolumeIntegralWeakForm())
equations = CompressibleEulerEquations3D(1.4)
diff --git a/src/Trixi.jl b/src/Trixi.jl
index 6fc62f50520..34a1977d4f5 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -164,7 +164,7 @@ export flux, flux_central, flux_lax_friedrichs, flux_hll, flux_hllc, flux_hlle,
hydrostatic_reconstruction_audusse_etal, flux_nonconservative_audusse_etal,
FluxPlusDissipation, DissipationGlobalLaxFriedrichs, DissipationLocalLaxFriedrichs,
FluxLaxFriedrichs, max_abs_speed_naive,
- FluxHLL, min_max_speed_naive,
+ FluxHLL, min_max_speed_naive, min_max_speed_davis, min_max_speed_einfeldt,
FluxLMARS,
FluxRotated,
flux_shima_etal_turbo, flux_ranocha_turbo,
diff --git a/src/equations/compressible_euler_1d.jl b/src/equations/compressible_euler_1d.jl
index 15f7a2cb4c4..e4fd0997eae 100644
--- a/src/equations/compressible_euler_1d.jl
+++ b/src/equations/compressible_euler_1d.jl
@@ -628,7 +628,7 @@ end
return SVector(f1m, f2m, f3m)
end
-# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the
+# Calculate estimates for maximum wave speed for local Lax-Friedrichs-type dissipation as the
# maximum velocity magnitude plus the maximum speed of sound
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerEquations1D)
@@ -648,7 +648,7 @@ end
λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr)
end
-# Calculate minimum and maximum wave speeds for HLL-type fluxes
+# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerEquations1D)
rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)
@@ -660,6 +660,21 @@ end
return λ_min, λ_max
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::CompressibleEulerEquations1D)
+ rho_ll, v1_ll, p_ll = cons2prim(u_ll, equations)
+ rho_rr, v1_rr, p_rr = cons2prim(u_rr, equations)
+
+ c_ll = sqrt(equations.gamma * p_ll / rho_ll)
+ c_rr = sqrt(equations.gamma * p_rr / rho_rr)
+
+ λ_min = min(v1_ll - c_ll, v1_rr - c_rr)
+ λ_max = max(v1_ll + c_ll, v1_rr + c_rr)
+
+ return λ_min, λ_max
+end
+
"""
flux_hllc(u_ll, u_rr, orientation, equations::CompressibleEulerEquations1D)
diff --git a/src/equations/compressible_euler_2d.jl b/src/equations/compressible_euler_2d.jl
index 05987c510b8..27b92f41953 100644
--- a/src/equations/compressible_euler_2d.jl
+++ b/src/equations/compressible_euler_2d.jl
@@ -1032,7 +1032,7 @@ end
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction)
end
-# Calculate minimum and maximum wave speeds for HLL-type fluxes
+# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerEquations2D)
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
@@ -1065,6 +1065,47 @@ end
return λ_min, λ_max
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::CompressibleEulerEquations2D)
+ rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
+ rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)
+
+ c_ll = sqrt(equations.gamma * p_ll / rho_ll)
+ c_rr = sqrt(equations.gamma * p_rr / rho_rr)
+
+ if orientation == 1 # x-direction
+ λ_min = min(v1_ll - c_ll, v1_rr - c_rr)
+ λ_max = max(v1_ll + c_ll, v1_rr + c_rr)
+ else # y-direction
+ λ_min = min(v2_ll - c_ll, v2_rr - c_rr)
+ λ_max = max(v2_ll + c_ll, v2_rr + c_rr)
+ end
+
+ return λ_min, λ_max
+end
+
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::CompressibleEulerEquations2D)
+ rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
+ rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)
+
+ norm_ = norm(normal_direction)
+
+ c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_
+ c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_
+
+ v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
+ v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
+
+ # The v_normals are already scaled by the norm
+ λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)
+ λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)
+
+ return λ_min, λ_max
+end
+
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the direction
# has been normalized prior to this rotation of the state vector
@inline function rotate_to_x(u, normal_vector, equations::CompressibleEulerEquations2D)
diff --git a/src/equations/compressible_euler_3d.jl b/src/equations/compressible_euler_3d.jl
index 2085811f832..7f25bde31fd 100644
--- a/src/equations/compressible_euler_3d.jl
+++ b/src/equations/compressible_euler_3d.jl
@@ -1070,7 +1070,7 @@ end
return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction)
end
-# Calculate minimum and maximum wave speeds for HLL-type fluxes
+# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerEquations3D)
rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
@@ -1108,6 +1108,54 @@ end
return λ_min, λ_max
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::CompressibleEulerEquations3D)
+ rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
+ rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
+
+ c_ll = sqrt(equations.gamma * p_ll / rho_ll)
+ c_rr = sqrt(equations.gamma * p_rr / rho_rr)
+
+ if orientation == 1 # x-direction
+ λ_min = min(v1_ll - c_ll, v1_rr - c_rr)
+ λ_max = max(v1_ll + c_ll, v1_rr + c_rr)
+ elseif orientation == 2 # y-direction
+ λ_min = min(v2_ll - c_ll, v2_rr - c_rr)
+ λ_max = max(v2_ll + c_ll, v2_rr + c_rr)
+ else # z-direction
+ λ_min = min(v3_ll - c_ll, v3_rr - c_rr)
+ λ_max = max(v3_ll + c_ll, v3_rr + c_rr)
+ end
+
+ return λ_min, λ_max
+end
+
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::CompressibleEulerEquations3D)
+ rho_ll, v1_ll, v2_ll, v3_ll, p_ll = cons2prim(u_ll, equations)
+ rho_rr, v1_rr, v2_rr, v3_rr, p_rr = cons2prim(u_rr, equations)
+
+ norm_ = norm(normal_direction)
+
+ c_ll = sqrt(equations.gamma * p_ll / rho_ll) * norm_
+ c_rr = sqrt(equations.gamma * p_rr / rho_rr) * norm_
+
+ v_normal_ll = v1_ll * normal_direction[1] +
+ v2_ll * normal_direction[2] +
+ v3_ll * normal_direction[3]
+ v_normal_rr = v1_rr * normal_direction[1] +
+ v2_rr * normal_direction[2] +
+ v3_rr * normal_direction[3]
+
+ # The v_normals are already scaled by the norm
+ λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)
+ λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)
+
+ return λ_min, λ_max
+end
+
# Rotate normal vector to x-axis; normal, tangent1 and tangent2 need to be orthonormal
# Called inside `FluxRotated` in `numerical_fluxes.jl` so the directions
# has been normalized prior to this rotation of the state vector
diff --git a/src/equations/ideal_glm_mhd_1d.jl b/src/equations/ideal_glm_mhd_1d.jl
index 4ef593cda53..7e5c94c7bc3 100644
--- a/src/equations/ideal_glm_mhd_1d.jl
+++ b/src/equations/ideal_glm_mhd_1d.jl
@@ -277,13 +277,33 @@ end
λ_max = max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::IdealGlmMhdEquations1D)
+ rho_ll, rho_v1_ll, _ = u_ll
+ rho_rr, rho_v1_rr, _ = u_rr
+
+ # Calculate primitive variables
+ v1_ll = rho_v1_ll / rho_ll
+ v1_rr = rho_v1_rr / rho_rr
+
+ # Approximate the left-most and right-most eigenvalues in the Riemann fan
+ c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
+
+ λ_min = min(v1_ll - c_f_ll, v1_rr - c_f_rr)
+ λ_max = max(v1_ll + c_f_ll, v1_rr + c_f_rr)
+
+ return λ_min, λ_max
+end
+
"""
- min_max_speed_naive(u_ll, u_rr, orientation, equations::IdealGlmMhdEquations1D)
+ min_max_speed_naive(u_ll, u_rr, orientation::Integer, equations::IdealGlmMhdEquations1D)
Calculate minimum and maximum wave speeds for HLL-type fluxes as in
- Li (2005)
An HLLC Riemann solver for magneto-hydrodynamics
- [DOI: 10.1016/j.jcp.2004.08.020](https://doi.org/10.1016/j.jcp.2004.08.020)
+ [DOI: 10.1016/j.jcp.2004.08.020](https://doi.org/10.1016/j.jcp.2004.08.020).
"""
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::IdealGlmMhdEquations1D)
diff --git a/src/equations/ideal_glm_mhd_2d.jl b/src/equations/ideal_glm_mhd_2d.jl
index fb3048fe883..8fef1ee22c9 100644
--- a/src/equations/ideal_glm_mhd_2d.jl
+++ b/src/equations/ideal_glm_mhd_2d.jl
@@ -585,13 +585,70 @@ end
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::IdealGlmMhdEquations2D)
+ rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll
+ rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr
+
+ # Calculate primitive velocity variables
+ v1_ll = rho_v1_ll / rho_ll
+ v2_ll = rho_v2_ll / rho_ll
+
+ v1_rr = rho_v1_rr / rho_rr
+ v2_rr = rho_v2_rr / rho_rr
+
+ # Approximate the left-most and right-most eigenvalues in the Riemann fan
+ if orientation == 1 # x-direction
+ c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
+
+ λ_min = min(v1_ll - c_f_ll, v1_rr - c_f_rr)
+ λ_max = max(v1_ll + c_f_ll, v1_rr + c_f_rr)
+ else # y-direction
+ c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
+
+ λ_min = min(v2_ll - c_f_ll, v2_rr - c_f_rr)
+ λ_max = max(v2_ll + c_f_ll, v1_rr + c_f_rr)
+ end
+
+ return λ_min, λ_max
+end
+
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::IdealGlmMhdEquations2D)
+ rho_ll, rho_v1_ll, rho_v2_ll, _ = u_ll
+ rho_rr, rho_v1_rr, rho_v2_rr, _ = u_rr
+
+ # Calculate primitive velocity variables
+ v1_ll = rho_v1_ll / rho_ll
+ v2_ll = rho_v2_ll / rho_ll
+
+ v1_rr = rho_v1_rr / rho_rr
+ v2_rr = rho_v2_rr / rho_rr
+
+ v_normal_ll = (v1_ll * normal_direction[1] + v2_ll * normal_direction[2])
+ v_normal_rr = (v1_rr * normal_direction[1] + v2_rr * normal_direction[2])
+
+ c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
+
+ # Estimate the min/max eigenvalues in the normal direction
+ λ_min = min(v_normal_ll - c_f_ll, v_normal_rr - c_f_rr)
+ λ_max = max(v_normal_ll + c_f_ll, v_normal_rr + c_f_rr)
+
+ return λ_min, λ_max
+end
+
"""
- min_max_speed_naive(u_ll, u_rr, orientation, equations::IdealGlmMhdEquations2D)
+ min_max_speed_naive(u_ll, u_rr, orientation::Integer, equations::IdealGlmMhdEquations2D)
Calculate minimum and maximum wave speeds for HLL-type fluxes as in
- Li (2005)
An HLLC Riemann solver for magneto-hydrodynamics
- [DOI: 10.1016/j.jcp.2004.08.020](https://doi.org/10.1016/j.jcp.2004.08.020)
+ [DOI: 10.1016/j.jcp.2004.08.020](https://doi.org/10.1016/j.jcp.2004.08.020).
"""
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::IdealGlmMhdEquations2D)
@@ -635,10 +692,8 @@ end
v1_rr = rho_v1_rr / rho_rr
v2_rr = rho_v2_rr / rho_rr
- v_normal_ll = (v1_ll * normal_direction[1] +
- v2_ll * normal_direction[2])
- v_normal_rr = (v1_rr * normal_direction[1] +
- v2_rr * normal_direction[2])
+ v_normal_ll = (v1_ll * normal_direction[1] + v2_ll * normal_direction[2])
+ v_normal_rr = (v1_rr * normal_direction[1] + v2_rr * normal_direction[2])
c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
diff --git a/src/equations/ideal_glm_mhd_3d.jl b/src/equations/ideal_glm_mhd_3d.jl
index 2e149d2849f..09990837706 100644
--- a/src/equations/ideal_glm_mhd_3d.jl
+++ b/src/equations/ideal_glm_mhd_3d.jl
@@ -670,6 +670,77 @@ end
return max(abs(v_ll), abs(v_rr)) + max(cf_ll, cf_rr)
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::IdealGlmMhdEquations3D)
+ rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
+ rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
+
+ # Calculate primitive variables and speed of sound
+ v1_ll = rho_v1_ll / rho_ll
+ v2_ll = rho_v2_ll / rho_ll
+ v3_ll = rho_v3_ll / rho_ll
+
+ v1_rr = rho_v1_rr / rho_rr
+ v2_rr = rho_v2_rr / rho_rr
+ v3_rr = rho_v3_rr / rho_rr
+
+ # Approximate the left-most and right-most eigenvalues in the Riemann fan
+ if orientation == 1 # x-direction
+ c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
+
+ λ_min = min(v1_ll - c_f_ll, v1_rr - c_f_rr)
+ λ_max = max(v1_ll + c_f_ll, v1_rr + c_f_rr)
+ elseif orientation == 2 # y-direction
+ c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
+
+ λ_min = min(v2_ll - c_f_ll, v2_rr - c_f_rr)
+ λ_max = max(v2_ll + c_f_ll, v2_rr + c_f_rr)
+ else # z-direction
+ c_f_ll = calc_fast_wavespeed(u_ll, orientation, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, orientation, equations)
+
+ λ_min = min(v3_ll - c_f_ll, v3_rr - c_f_rr)
+ λ_max = max(v3_ll + c_f_ll, v3_rr + c_f_rr)
+ end
+
+ return λ_min, λ_max
+end
+
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::IdealGlmMhdEquations3D)
+ rho_ll, rho_v1_ll, rho_v2_ll, rho_v3_ll, _ = u_ll
+ rho_rr, rho_v1_rr, rho_v2_rr, rho_v3_rr, _ = u_rr
+
+ # Calculate primitive velocity variables
+ v1_ll = rho_v1_ll / rho_ll
+ v2_ll = rho_v2_ll / rho_ll
+ v3_ll = rho_v3_ll / rho_ll
+
+ v1_rr = rho_v1_rr / rho_rr
+ v2_rr = rho_v2_rr / rho_rr
+ v3_rr = rho_v3_rr / rho_rr
+
+ v_normal_ll = (v1_ll * normal_direction[1] +
+ v2_ll * normal_direction[2] +
+ v3_ll * normal_direction[3])
+ v_normal_rr = (v1_rr * normal_direction[1] +
+ v2_rr * normal_direction[2] +
+ v3_rr * normal_direction[3])
+
+ c_f_ll = calc_fast_wavespeed(u_ll, normal_direction, equations)
+ c_f_rr = calc_fast_wavespeed(u_rr, normal_direction, equations)
+
+ # Estimate the min/max eigenvalues in the normal direction
+ λ_min = min(v_normal_ll - c_f_ll, v_normal_rr - c_f_rr)
+ λ_max = max(v_normal_ll + c_f_ll, v_normal_rr + c_f_rr)
+
+ return λ_min, λ_max
+end
+
"""
min_max_speed_naive(u_ll, u_rr, orientation_or_normal_direction, equations::IdealGlmMhdEquations3D)
diff --git a/src/equations/linearized_euler_2d.jl b/src/equations/linearized_euler_2d.jl
index e478c32bd29..d497762bf62 100644
--- a/src/equations/linearized_euler_2d.jl
+++ b/src/equations/linearized_euler_2d.jl
@@ -353,6 +353,44 @@ end
return SVector(f1, f2, f3, f4)
end
+# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
+ equations::LinearizedEulerEquations2D)
+ min_max_speed_davis(u_ll, u_rr, orientation, equations)
+end
+
+@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::LinearizedEulerEquations2D)
+ min_max_speed_davis(u_ll, u_rr, normal_direction, equations)
+end
+
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::LinearizedEulerEquations2D)
+ @unpack v_mean_global, c_mean_global = equations
+
+ λ_min = v_mean_global[orientation] - c_mean_global
+ λ_max = v_mean_global[orientation] + c_mean_global
+
+ return λ_min, λ_max
+end
+
+@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::LinearizedEulerEquations2D)
+ @unpack v_mean_global, c_mean_global = equations
+
+ norm_ = norm(normal_direction)
+
+ v_normal = v_mean_global[1] * normal_direction[1] +
+ v_mean_global[2] * normal_direction[2]
+
+ # The v_normals are already scaled by the norm
+ λ_min = v_normal - c_mean_global * norm_
+ λ_max = v_normal + c_mean_global * norm_
+
+ return λ_min, λ_max
+end
+
# Convert conservative variables to primitive
@inline cons2prim(u, equations::LinearizedEulerEquations2D) = u
@inline cons2entropy(u, ::LinearizedEulerEquations2D) = u
diff --git a/src/equations/numerical_fluxes.jl b/src/equations/numerical_fluxes.jl
index 16a83124d14..abd9d66c490 100644
--- a/src/equations/numerical_fluxes.jl
+++ b/src/equations/numerical_fluxes.jl
@@ -214,6 +214,10 @@ Create an HLL (Harten, Lax, van Leer) numerical flux where the minimum and maxim
wave speeds are estimated as
`λ_min, λ_max = min_max_speed(u_ll, u_rr, orientation_or_normal_direction, equations)`,
defaulting to [`min_max_speed_naive`](@ref).
+Original paper:
+- Amiram Harten, Peter D. Lax, Bram van Leer (1983)
+ On Upstream Differencing and Godunov-Type Schemes for Hyperbolic Conservation Laws
+ [DOI: 10.1137/1025002](https://doi.org/10.1137/1025002)
"""
struct FluxHLL{MinMaxSpeed}
min_max_speed::MinMaxSpeed
@@ -222,18 +226,55 @@ end
FluxHLL() = FluxHLL(min_max_speed_naive)
"""
- min_max_speed_naive(u_ll, u_rr, orientation::Integer, equations)
+ min_max_speed_naive(u_ll, u_rr, orientation::Integer, equations)
min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations)
-Simple and fast estimate of the minimal and maximal wave speed of the Riemann problem with
+Simple and fast estimate(!) of the minimal and maximal wave speed of the Riemann problem with
left and right states `u_ll, u_rr`, usually based only on the local wave speeds associated to
`u_ll` and `u_rr`.
- Amiram Harten, Peter D. Lax, Bram van Leer (1983)
On Upstream Differencing and Godunov-Type Schemes for Hyperbolic Conservation Laws
[DOI: 10.1137/1025002](https://doi.org/10.1137/1025002)
+
+See also [`FluxHLL`](@ref), [`min_max_speed_davis`](@ref), [`min_max_speed_einfeldt`](@ref).
"""
function min_max_speed_naive end
+"""
+ min_max_speed_davis(u_ll, u_rr, orientation::Integer, equations)
+ min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector, equations)
+
+Simple and fast estimates of the minimal and maximal wave speed of the Riemann problem with
+left and right states `u_ll, u_rr`, usually based only on the local wave speeds associated to
+`u_ll` and `u_rr`.
+
+- S.F. Davis (1988)
+ Simplified Second-Order Godunov-Type Methods
+ [DOI: 10.1137/0909030](https://doi.org/10.1137/0909030)
+
+See also [`FluxHLL`](@ref), [`min_max_speed_naive`](@ref), [`min_max_speed_einfeldt`](@ref).
+"""
+function min_max_speed_davis end
+
+"""
+ min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer, equations)
+ min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector, equations)
+
+More advanced mininmal and maximal wave speed computation based on
+- Bernd Einfeldt (1988)
+ On Godunov-type methods for gas dynamics.
+ [DOI: 10.1137/0725021](https://doi.org/10.1137/0725021)
+- Bernd Einfeldt, Claus-Dieter Munz, Philip L. Roe and Björn Sjögreen (1991)
+ On Godunov-type methods near low densities.
+ [DOI: 10.1016/0021-9991(91)90211-3](https://doi.org/10.1016/0021-9991(91)90211-3)
+
+originally developed for the compressible Euler equations.
+A compact representation can be found in [this lecture notes, eq. (9.28)](https://metaphor.ethz.ch/x/2019/hs/401-4671-00L/literature/mishra_hyperbolic_pdes.pdf).
+
+See also [`FluxHLL`](@ref), [`min_max_speed_naive`](@ref), [`min_max_speed_davis`](@ref).
+"""
+function min_max_speed_einfeldt end
+
@inline function (numflux::FluxHLL)(u_ll, u_rr, orientation_or_normal_direction,
equations)
λ_min, λ_max = numflux.min_max_speed(u_ll, u_rr, orientation_or_normal_direction,
diff --git a/src/equations/shallow_water_1d.jl b/src/equations/shallow_water_1d.jl
index 851cbacdd57..c33b31fca81 100644
--- a/src/equations/shallow_water_1d.jl
+++ b/src/equations/shallow_water_1d.jl
@@ -460,7 +460,7 @@ end
end
end
-# Calculate minimum and maximum wave speeds for HLL-type fluxes
+# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::ShallowWaterEquations1D)
h_ll = waterheight(u_ll, equations)
@@ -474,6 +474,41 @@ end
return λ_min, λ_max
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations1D)
+ h_ll = waterheight(u_ll, equations)
+ v_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v_rr = velocity(u_rr, equations)
+
+ c_ll = sqrt(equations.gravity * h_ll)
+ c_rr = sqrt(equations.gravity * h_rr)
+
+ λ_min = min(v_ll - c_ll, v_rr - c_rr)
+ λ_max = max(v_rr + c_rr, v_rr + c_rr)
+
+ return λ_min, λ_max
+end
+
+@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations1D)
+ h_ll = waterheight(u_ll, equations)
+ v_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v_rr = velocity(u_rr, equations)
+
+ c_ll = sqrt(equations.gravity * h_ll)
+ c_rr = sqrt(equations.gravity * h_rr)
+
+ v_roe, c_roe = calc_wavespeed_roe(u_ll, u_rr, orientation, equations)
+
+ λ_min = min(v_ll - c_ll, v_roe - c_roe)
+ λ_max = max(v_rr + c_rr, v_roe + c_roe)
+
+ return λ_min, λ_max
+end
+
@inline function max_abs_speeds(u, equations::ShallowWaterEquations1D)
h = waterheight(u, equations)
v = velocity(u, equations)
@@ -547,6 +582,35 @@ end
return waterheight(u, equations) * pressure(u, equations)
end
+"""
+ calc_wavespeed_roe(u_ll, u_rr, direction::Integer,
+ equations::ShallowWaterEquations1D)
+
+Calculate Roe-averaged velocity `v_roe` and wavespeed `c_roe = sqrt{g * h_roe}`
+See for instance equation (62) in
+- Paul A. Ullrich, Christiane Jablonowski, and Bram van Leer (2010)
+ High-order finite-volume methods for the shallow-water equations on the sphere
+ [DOI: 10.1016/j.jcp.2010.04.044](https://doi.org/10.1016/j.jcp.2010.04.044)
+Or equation (9.17) in [this lecture notes](https://metaphor.ethz.ch/x/2019/hs/401-4671-00L/literature/mishra_hyperbolic_pdes.pdf).
+"""
+@inline function calc_wavespeed_roe(u_ll, u_rr, direction::Integer,
+ equations::ShallowWaterEquations1D)
+ h_ll = waterheight(u_ll, equations)
+ v_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v_rr = velocity(u_rr, equations)
+
+ h_roe = 0.5 * (h_ll + h_rr)
+ c_roe = sqrt(equations.gravity * h_roe)
+
+ h_ll_sqrt = sqrt(h_ll)
+ h_rr_sqrt = sqrt(h_rr)
+
+ v_roe = (h_ll_sqrt * v_ll + h_rr_sqrt * v_rr) / (h_ll_sqrt + h_rr_sqrt)
+
+ return v_roe, c_roe
+end
+
# Entropy function for the shallow water equations is the total energy
@inline function entropy(cons, equations::ShallowWaterEquations1D)
energy_total(cons, equations)
diff --git a/src/equations/shallow_water_2d.jl b/src/equations/shallow_water_2d.jl
index f9ebbd597f9..9e227cd4a77 100644
--- a/src/equations/shallow_water_2d.jl
+++ b/src/equations/shallow_water_2d.jl
@@ -725,7 +725,7 @@ end
end
end
-# Calculate minimum and maximum wave speeds for HLL-type fluxes
+# Calculate estimates for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,
equations::ShallowWaterEquations2D)
h_ll = waterheight(u_ll, equations)
@@ -762,6 +762,94 @@ end
return λ_min, λ_max
end
+# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
+@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ c_ll = sqrt(equations.gravity * h_ll)
+ c_rr = sqrt(equations.gravity * h_rr)
+
+ if orientation == 1 # x-direction
+ λ_min = min(v1_ll - c_ll, v1_rr - c_rr)
+ λ_max = max(v1_ll + c_ll, v1_rr + c_rr)
+ else # y-direction
+ λ_min = min(v2_ll - c_ll, v2_rr - c_rr)
+ λ_max = max(v2_ll + c_ll, v2_rr + c_rr)
+ end
+
+ return λ_min, λ_max
+end
+
+@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ norm_ = norm(normal_direction)
+ c_ll = sqrt(equations.gravity * h_ll) * norm_
+ c_rr = sqrt(equations.gravity * h_rr) * norm_
+
+ v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
+ v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
+
+ # The v_normals are already scaled by the norm
+ λ_min = min(v_normal_ll - c_ll, v_normal_rr - c_rr)
+ λ_max = max(v_normal_ll + c_ll, v_normal_rr + c_rr)
+
+ return λ_min, λ_max
+end
+
+@inline function min_max_speed_einfeldt(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ c_ll = sqrt(equations.gravity * h_ll)
+ c_rr = sqrt(equations.gravity * h_rr)
+
+ if orientation == 1 # x-direction
+ v_roe, c_roe = calc_wavespeed_roe(u_ll, u_rr, orientation, equations)
+ λ_min = min(v1_ll - c_ll, v_roe - c_roe)
+ λ_max = max(v1_rr + c_rr, v_roe + c_roe)
+ else # y-direction
+ v_roe, c_roe = calc_wavespeed_roe(u_ll, u_rr, orientation, equations)
+ λ_min = min(v2_ll - c_ll, v_roe - c_roe)
+ λ_max = max(v2_rr + c_rr, v_roe + c_roe)
+ end
+
+ return λ_min, λ_max
+end
+
+@inline function min_max_speed_einfeldt(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ norm_ = norm(normal_direction)
+
+ c_ll = sqrt(equations.gravity * h_ll) * norm_
+ c_rr = sqrt(equations.gravity * h_rr) * norm_
+
+ v_normal_ll = (v1_ll * normal_direction[1] + v2_ll * normal_direction[2])
+ v_normal_rr = (v1_rr * normal_direction[1] + v2_rr * normal_direction[2])
+
+ v_roe, c_roe = calc_wavespeed_roe(u_ll, u_rr, normal_direction, equations)
+ λ_min = min(v_normal_ll - c_ll, v_roe - c_roe)
+ λ_max = max(v_normal_rr + c_rr, v_roe + c_roe)
+
+ return λ_min, λ_max
+end
+
@inline function max_abs_speeds(u, equations::ShallowWaterEquations2D)
h = waterheight(u, equations)
v1, v2 = velocity(u, equations)
@@ -837,6 +925,63 @@ end
return waterheight(u, equations) * pressure(u, equations)
end
+"""
+ calc_wavespeed_roe(u_ll, u_rr, direction::Integer,
+ equations::ShallowWaterEquations2D)
+
+Calculate Roe-averaged velocity `v_roe` and wavespeed `c_roe = sqrt{g * h_roe}` depending on direction.
+See for instance equation (62) in
+- Paul A. Ullrich, Christiane Jablonowski, and Bram van Leer (2010)
+ High-order finite-volume methods for the shallow-water equations on the sphere
+ [DOI: 10.1016/j.jcp.2010.04.044](https://doi.org/10.1016/j.jcp.2010.04.044)
+Or [this slides](https://faculty.washington.edu/rjl/classes/am574w2011/slides/am574lecture20nup3.pdf),
+slides 8 and 9.
+"""
+@inline function calc_wavespeed_roe(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ h_roe = 0.5 * (h_ll + h_rr)
+ c_roe = sqrt(equations.gravity * h_roe)
+
+ h_ll_sqrt = sqrt(h_ll)
+ h_rr_sqrt = sqrt(h_rr)
+
+ if orientation == 1 # x-direction
+ v_roe = (h_ll_sqrt * v1_ll + h_rr_sqrt * v1_rr) / (h_ll_sqrt + h_rr_sqrt)
+ else # y-direction
+ v_roe = (h_ll_sqrt * v2_ll + h_rr_sqrt * v2_rr) / (h_ll_sqrt + h_rr_sqrt)
+ end
+
+ return v_roe, c_roe
+end
+
+@inline function calc_wavespeed_roe(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ norm_ = norm(normal_direction)
+
+ h_roe = 0.5 * (h_ll + h_rr)
+ c_roe = sqrt(equations.gravity * h_roe) * norm_
+
+ h_ll_sqrt = sqrt(h_ll)
+ h_rr_sqrt = sqrt(h_rr)
+
+ v1_roe = (h_ll_sqrt * v1_ll + h_rr_sqrt * v1_rr) / (h_ll_sqrt + h_rr_sqrt)
+ v2_roe = (h_ll_sqrt * v2_ll + h_rr_sqrt * v2_rr) / (h_ll_sqrt + h_rr_sqrt)
+
+ v_roe = (v1_roe * normal_direction[1] + v2_roe * normal_direction[2])
+
+ return v_roe, c_roe
+end
+
# Entropy function for the shallow water equations is the total energy
@inline function entropy(cons, equations::ShallowWaterEquations2D)
energy_total(cons, equations)
diff --git a/test/test_structured_1d.jl b/test/test_structured_1d.jl
index ec8c7a138d5..d280e2a5e01 100644
--- a/test/test_structured_1d.jl
+++ b/test/test_structured_1d.jl
@@ -39,6 +39,14 @@ isdir(outdir) && rm(outdir, recursive=true)
tspan = (0.0, 0.3))
end
+ @trixi_testset "elixir_euler_sedov_hll_davis.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"),
+ l2 = [1.278661029299215, 0.0663853410742763, 0.9585741943783386],
+ linf = [3.1661064228547255, 0.16256363944708607, 2.667676158812806],
+ tspan = (0.0, 12.5),
+ surface_flux = FluxHLL(min_max_speed_davis))
+ end
+
@trixi_testset "elixir_euler_source_terms.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_source_terms.jl"),
# Expected errors are exactly the same as with TreeMesh!
diff --git a/test/test_unit.jl b/test/test_unit.jl
index b0c3e4205e5..2ce111b2bf4 100644
--- a/test/test_unit.jl
+++ b/test/test_unit.jl
@@ -382,7 +382,7 @@ isdir(outdir) && rm(outdir, recursive=true)
@timed_testset "HLL flux with vanishing wave speed estimates (#502)" begin
equations = CompressibleEulerEquations1D(1.4)
u = SVector(1.0, 0.0, 0.0)
- @test !any(isnan, FluxHLL()(u, u, 1, equations))
+ @test !any(isnan, flux_hll(u, u, 1, equations))
end
@timed_testset "DG L2 mortar container debug output" begin
@@ -586,7 +586,265 @@ isdir(outdir) && rm(outdir, recursive=true)
@test_throws ArgumentError TimeSeriesCallback(semi, [1.0 1.0 1.0; 2.0 2.0 2.0])
end
- @timed_testset "Consistency check for HLLE flux" begin
+ @timed_testset "Consistency check for HLL flux (naive): CEE" begin
+ flux_hll = FluxHLL(min_max_speed_naive)
+
+ # Set up equations and dummy conservative variables state
+ equations = CompressibleEulerEquations1D(1.4)
+ u = SVector(1.1, 2.34, 5.5)
+
+ orientations = [1]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ equations = CompressibleEulerEquations2D(1.4)
+ u = SVector(1.1, -0.5, 2.34, 5.5)
+
+ orientations = [1, 2]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ equations = CompressibleEulerEquations3D(1.4)
+ u = SVector(1.1, -0.5, 2.34, 2.4, 5.5)
+
+ orientations = [1, 2, 3]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux (naive): LEE" begin
+ flux_hll = FluxHLL(min_max_speed_naive)
+
+ equations = LinearizedEulerEquations2D(SVector(1.0, 1.0), 1.0, 1.0)
+ u = SVector(1.1, -0.5, 2.34, 5.5)
+
+ orientations = [1, 2]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux (naive): SWE" begin
+ flux_hll = FluxHLL(min_max_speed_naive)
+
+ equations = ShallowWaterEquations1D(gravity_constant=9.81)
+ u = SVector(1, 0.5, 0.0)
+ @test flux_hll(u, u, 1, equations) ≈ flux(u, 1, equations)
+
+ equations = ShallowWaterEquations2D(gravity_constant=9.81)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+ u = SVector(1, 0.5, 0.5, 0.0)
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux (naive): MHD" begin
+ flux_hll = FluxHLL(min_max_speed_naive)
+
+ equations = IdealGlmMhdEquations1D(1.4)
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2),]
+
+ for u in u_values
+ @test flux_hll(u, u, 1, equations) ≈ flux(u, 1, equations)
+ end
+
+ equations = IdealGlmMhdEquations2D(1.4, 5.0 #= c_h =#)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+ orientations = [1, 2]
+
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
+
+ for u in u_values, orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for u in u_values, normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+
+ equations = IdealGlmMhdEquations3D(1.4, 5.0 #= c_h =#)
+ normal_directions = [SVector(1.0, 0.0, 0.0),
+ SVector(0.0, 1.0, 0.0),
+ SVector(0.0, 0.0, 1.0),
+ SVector(0.5, -0.5, 0.2),
+ SVector(-1.2, 0.3, 1.4)]
+ orientations = [1, 2, 3]
+
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
+
+ for u in u_values, orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for u in u_values, normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux with Davis wave speed estimates: CEE" begin
+ flux_hll = FluxHLL(min_max_speed_davis)
+
+ # Set up equations and dummy conservative variables state
+ equations = CompressibleEulerEquations1D(1.4)
+ u = SVector(1.1, 2.34, 5.5)
+
+ orientations = [1]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ equations = CompressibleEulerEquations2D(1.4)
+ u = SVector(1.1, -0.5, 2.34, 5.5)
+
+ orientations = [1, 2]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+
+ equations = CompressibleEulerEquations3D(1.4)
+ u = SVector(1.1, -0.5, 2.34, 2.4, 5.5)
+
+ orientations = [1, 2, 3]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ normal_directions = [SVector(1.0, 0.0, 0.0),
+ SVector(0.0, 1.0, 0.0),
+ SVector(0.0, 0.0, 1.0),
+ SVector(0.5, -0.5, 0.2),
+ SVector(-1.2, 0.3, 1.4)]
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux with Davis wave speed estimates: LEE" begin
+ flux_hll = FluxHLL(min_max_speed_davis)
+
+ equations = LinearizedEulerEquations2D(SVector(1.0, 1.0), 1.0, 1.0)
+ u = SVector(1.1, -0.5, 2.34, 5.5)
+
+ orientations = [1, 2]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux with Davis wave speed estimates: SWE" begin
+ flux_hll = FluxHLL(min_max_speed_davis)
+
+ equations = ShallowWaterEquations1D(gravity_constant=9.81)
+ u = SVector(1, 0.5, 0.0)
+ @test flux_hll(u, u, 1, equations) ≈ flux(u, 1, equations)
+
+ equations = ShallowWaterEquations2D(gravity_constant=9.81)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+ u = SVector(1, 0.5, 0.5, 0.0)
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+
+ orientations = [1, 2]
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLL flux with Davis wave speed estimates: MHD" begin
+ flux_hll = FluxHLL(min_max_speed_davis)
+
+ equations = IdealGlmMhdEquations1D(1.4)
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2),]
+
+ for u in u_values
+ @test flux_hll(u, u, 1, equations) ≈ flux(u, 1, equations)
+ end
+
+ equations = IdealGlmMhdEquations2D(1.4, 5.0 #= c_h =#)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+ orientations = [1, 2]
+
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
+
+ for u in u_values, orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for u in u_values, normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+
+ equations = IdealGlmMhdEquations3D(1.4, 5.0 #= c_h =#)
+ normal_directions = [SVector(1.0, 0.0, 0.0),
+ SVector(0.0, 1.0, 0.0),
+ SVector(0.0, 0.0, 1.0),
+ SVector(0.5, -0.5, 0.2),
+ SVector(-1.2, 0.3, 1.4)]
+ orientations = [1, 2, 3]
+
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
+
+ for u in u_values, orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for u in u_values, normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLLE flux: CEE" begin
# Set up equations and dummy conservative variables state
equations = CompressibleEulerEquations1D(1.4)
u = SVector(1.1, 2.34, 5.5)
@@ -604,6 +862,15 @@ isdir(outdir) && rm(outdir, recursive=true)
@test flux_hlle(u, u, orientation, equations) ≈ flux(u, orientation, equations)
end
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+
equations = CompressibleEulerEquations3D(1.4)
u = SVector(1.1, -0.5, 2.34, 2.4, 5.5)
@@ -611,6 +878,92 @@ isdir(outdir) && rm(outdir, recursive=true)
for orientation in orientations
@test flux_hlle(u, u, orientation, equations) ≈ flux(u, orientation, equations)
end
+
+ normal_directions = [SVector(1.0, 0.0, 0.0),
+ SVector(0.0, 1.0, 0.0),
+ SVector(0.0, 0.0, 1.0),
+ SVector(0.5, -0.5, 0.2),
+ SVector(-1.2, 0.3, 1.4)]
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLLE flux: SWE" begin
+ # Test HLL flux with min_max_speed_einfeldt
+ flux_hll = FluxHLL(min_max_speed_einfeldt)
+
+ equations = ShallowWaterEquations1D(gravity_constant=9.81)
+ u = SVector(1, 0.5, 0.0)
+ @test flux_hll(u, u, 1, equations) ≈ flux(u, 1, equations)
+
+ equations = ShallowWaterEquations2D(gravity_constant=9.81)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+ orientations = [1, 2]
+
+ u = SVector(1, 0.5, 0.5, 0.0)
+
+ for orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+ end
+
+ @timed_testset "Consistency check for HLLE flux: MHD" begin
+ # Test HLL flux with min_max_speed_einfeldt
+ flux_hll = FluxHLL(min_max_speed_naive)
+
+ equations = IdealGlmMhdEquations1D(1.4)
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2),]
+
+ for u in u_values
+ @test flux_hll(u, u, 1, equations) ≈ flux(u, 1, equations)
+ end
+
+ equations = IdealGlmMhdEquations2D(1.4, 5.0 #= c_h =#)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+ orientations = [1, 2]
+
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
+
+ for u in u_values, orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for u in u_values, normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
+
+ equations = IdealGlmMhdEquations3D(1.4, 5.0 #= c_h =#)
+ normal_directions = [SVector(1.0, 0.0, 0.0),
+ SVector(0.0, 1.0, 0.0),
+ SVector(0.0, 0.0, 1.0),
+ SVector(0.5, -0.5, 0.2),
+ SVector(-1.2, 0.3, 1.4)]
+ orientations = [1, 2, 3]
+
+ u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
+ SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
+
+ for u in u_values, orientation in orientations
+ @test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
+ end
+
+ for u in u_values, normal_direction in normal_directions
+ @test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
+ end
end
@timed_testset "Consistency check for Godunov flux" begin
@@ -780,7 +1133,8 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(-1.2, 0.3)]
u_values = [SVector(1.0, 0.5, -0.7, 1.0),
SVector(1.5, -0.2, 0.1, 5.0),]
- fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber]
+ fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber,
+ flux_hll, FluxHLL(min_max_speed_davis)]
for f_std in fluxes
f_rot = FluxRotated(f_std)
@@ -799,7 +1153,8 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(-1.2, 0.3, 1.4)]
u_values = [SVector(1.0, 0.5, -0.7, 0.1, 1.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0),]
- fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber, FluxLMARS(340)]
+ fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber, FluxLMARS(340),
+ flux_hll, FluxHLL(min_max_speed_davis)]
for f_std in fluxes
f_rot = FluxRotated(f_std)
@@ -809,6 +1164,20 @@ isdir(outdir) && rm(outdir, recursive=true)
end
end
+ @timed_testset "ShallowWaterEquations2D" begin
+ equations = ShallowWaterEquations2D(gravity_constant=9.81)
+ normal_directions = [SVector(1.0, 0.0),
+ SVector(0.0, 1.0),
+ SVector(0.5, -0.5),
+ SVector(-1.2, 0.3)]
+
+ u = SVector(1, 0.5, 0.5, 0.0)
+
+ fluxes = [flux_central, flux_fjordholm_etal, flux_wintermeyer_etal,
+ flux_hll, FluxHLL(min_max_speed_davis), FluxHLL(min_max_speed_einfeldt)]
+
+ end
+
@timed_testset "IdealGlmMhdEquations2D" begin
equations = IdealGlmMhdEquations2D(1.4, 5.0 #= c_h =#)
normal_directions = [SVector(1.0, 0.0),
@@ -817,7 +1186,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(-1.2, 0.3)]
u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
- fluxes = [flux_central, flux_hindenlang_gassner]
+ fluxes = [flux_central, flux_hindenlang_gassner, flux_hll, FluxHLL(min_max_speed_davis)]
for f_std in fluxes
f_rot = FluxRotated(f_std)
@@ -836,7 +1205,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(-1.2, 0.3, 1.4)]
u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
- fluxes = [flux_central, flux_hindenlang_gassner]
+ fluxes = [flux_central, flux_hindenlang_gassner, flux_hll, FluxHLL(min_max_speed_davis)]
for f_std in fluxes
f_rot = FluxRotated(f_std)
From dd91d7ed7fe99f437d8d0261cf7f9c43eb32c95b Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 14 Jul 2023 07:33:12 +0200
Subject: [PATCH 030/263] set version to v0.5.32
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 828f4778f74..f3ede1c74b4 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.32-pre"
+version = "0.5.32"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 0816ed0b62679bcd656dc38bad68034843632ba1 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 14 Jul 2023 07:33:26 +0200
Subject: [PATCH 031/263] set development version to v0.5.33-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index f3ede1c74b4..4a289380850 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.32"
+version = "0.5.33-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 905c8e29ef30bdb2648fa3073166ad0887cd1278 Mon Sep 17 00:00:00 2001
From: Andrew Winters
Date: Fri, 14 Jul 2023 17:47:20 +0200
Subject: [PATCH 032/263] Merge wet/dry capability to `main` (#1501)
* add dummy commit in order to open a dev to main PR
* [WIP] Wet/dry capabilities for 2D shallow water equations (#1340)
* HR of Chen and Noelle (1D) and edit SWE struct
* Overload limiter (SWE 1D) to cut off waterheight
* New indicatorHG (SWE 1D) to apply FV on dry cells
* Threshold in rhs! before calculation (SWE 1D)
* New lake_at_rest_error for SWE 1D
* New wet/dry elixirs for testing scheme for SWE 1D
* HR of Chen and Noelle (2D) and edit SWE struct
* Overload limiter (SWE 2D) to cut off waterheight
* New indicatorHG (SWE 2D) to apply FV on dry cells
* Threshold in rhs! before calculation (SWE 2D)
* New lake_at_rest_error for SWE 2D
* New wet/dry elixirs for testing scheme for SWE 2D
* Elixir SWE 2D: 3 mounds, problem with boundaries
* Fixed MethodError; apply_thresholds! too strict
* Fixed MethodError; apply_thresholds! too strict
* Move threshold on volume integral in stage_limiter
* Indentation, spacing and comments adjustment
* Renaming numerical HLL type flux (SWE 1D)
* Move threshold on volume integral in stage_limiter
* Renaming numerical HLL type flux (SWE 2D)
* Indentation, spacing and comments adjustment
* Describing docs for Chen and Noelle HR (SWE 1D)
* Edit SWE 1D elixirs, error-based solver and docs
* Including tests on new SWE 1D elixirs
* Describing docs for Chen and Noelle HR (SWE 2D)
* Edit SWE 2D elixirs, error-based solver and docs
* Including tests on new SWE 2D elixirs
* New/reorganize positivity limiter (SWE 2D)
* New/reorganize positivity limiter (SWE 1D)
* Editing docs SWE 1D
* Editing docs SWE 2D
* Rearrange cut off at interfaces, edit tests SWE 1D
* Edit docs, add Ref
* Edit docs and indenting (SWE 2D)
* Rearrange cut off at interfaces, edit tests SWE 2D
* Remove tree/structured mesh elixir from repo SWE2D
* Create unstructured mesh elixir SWE 2D
* Add 1D lake-at-rest-error logic to pass 1D tests
* Add 2D lake-at-rest-error logic to pass 2D tests
* Fixed typo. Confusing name, but correct math
* Correction of comments and docstrings
* Correction of comments and docstrings
* Rename mesh file in elixir for UnstructuredMesh
* Update test_unstructured_2d.jl
forgot an end statement for the new test
* Fixing typos
* fix dispatching error on new lake-at-rest error calculation. See if this fixes broken tests
* Editing initial condition in parabolic bowl elixir
* Delete unnecessary variable in elixir
* adjust lake-at-rest error computation strategy. move specialized version of error into the wet-dry elixir as the new functionality was only needed in this speacial case. update corresponding test values as the bottom is now truly discontinuous
* update structured mesh version of the wet-dry well-balancedness test
* fix typos
* update values in parabolic bowl test on StructuredMesh
* update parabolic bowl test on TreeMesh
* revert the 1D computation of the lake-at-rest error to the standard way. This will change once the 1D wet/dry merges
* Reset lake-at-rest error computation strategy.
New version of error only in wet-dry elixir (special case)
Update test values as the bottom is now truly discontinuous
* Fix typo
* Shorten test run for parabolic bowl 1D
* Choose lower resolution for parabolic bowl
and update test values
* Further reduce resolution for parabolic bowl
and update test values
* adjust special initial conditions and well-balancedness error routines to avoid the need of element IDs
* Remove MPI from well-balanced test
* simplify workaround to set discontinuous initial data
* Simplify workaround to set discontinuity
* Change structure of Chen&Noelle flux
* Fix typos and indenting
* Adjust call of solve and use ode_default_options
* Edit docstring
* Replace boolean with if, remove set_node_vars
Shorten test runs on TreeMesh and UnstructuredMesh
* Change structure of Chen&Noelle flux
* Fix typos and indenting
* Adjust call of solve and use ode_default_options
* Edit docstring
* Replace boolean with if, remove set_node_vars
Shorten test runs on TreeMesh and UnstructuredMesh
* Update comment regarding H0 for lake-at-rest error
* Add the original source to the parabolic bowl test
* Update comment regarding H0 for lake-at-rest error
* Add the original source to the parabolic bowl test
* New sc indicator especially for SWE
* Remove threshold parameter from SWE limiter call
* update some docstrings
* remove type instability in positivty limiter
* typo fix
* move safety check for dry state in the new positivity limiter into the same element loop
* more docstring updates
* remove dummy comment added in the dev initial commit
* adjust default threshold values to be precision agnostic
* update comment on the default threshold value in the new TreeMesh elixirs
* update comments for the three new TreeMesh examples
* update IC comment for three mound test
* update IC comments for new StructuredMesh2D tests
* update comment on shallow water constructor
* adjust comments in the shallow_water_2d file
* adjust comment regarding threshold_limiter in the new elixirs
* fix typos found by SpellCheck
* Edit docs
* Import Printf macros for printing wb error
* Remove type instability in Chen & Noelle HR
* Change logic for setting SC indicator to one
* Change logic for default values of SWE struct
* Outsource HG shock capturing indicator for SWE
Create different function to compute indicator
Edit comments
Change wet/dry clipping to if-else logic
* Move limiterthreshold into function & edit docs
Threshold was a passed variable in elixir before.
Now, it is taken right from the SWE struct in the limiter
Edit docs
* Move new limiter safety check in same element loop
* Adjust default threshold values
* Remove type instability
* Import Printf package for terminal output
* Edit docs
* Add Printf package to the test/Project.toml
Used for printing lake-at-rest error in well-balancedness test
* Add Printf package to the test/Project.toml
Used for printing lake-at-rest error in well-balancedness test
* Typo fix in elixir_shallowwater_well_balanced_wet_dry.jl
* Typo fix in elixir_shallowwater_well_balanced_wet_dry.jl
* unify new code with required formatting
* fix weird formatting and add 'format: noindent' where missing. fix crashing structured mesh run
* add unit test for new show routine
* apply JuliaFormatter
* simplify elixir as we can set discontinuous ICs in 1D. Also update beach test values
* dummy commit to check push access
* remove dummy comment
* typo fix
---------
Co-authored-by: Andrew Winters
Co-authored-by: Michael Schlottke-Lakemper
* adjust comments and remove duplicate code
* add TODOs for code pieces that should move to TrixiShallowWater package
* remove accidentally added file
* apply formatter to avoid errors with new comments
* move TODO comments to avoid errors in Documentation build
* Apply suggestions from code review
Co-authored-by: Hendrik Ranocha
* remove unnecessary analysis quantities from several new elixirs
* rename local threshold variable in new indicator to avoid confusion
* update NEWS.md with wetting and drying feature
* fix fomartting issue from conflict resolution
---------
Co-authored-by: svengoldberg <102215246+svengoldberg@users.noreply.github.com>
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
---
NEWS.md | 1 +
.../elixir_shallowwater_conical_island.jl | 113 ++++++++
.../elixir_shallowwater_parabolic_bowl.jl | 119 ++++++++
...ixir_shallowwater_well_balanced_wet_dry.jl | 200 +++++++++++++
.../elixir_shallowwater_beach.jl | 121 ++++++++
.../elixir_shallowwater_parabolic_bowl.jl | 117 ++++++++
...ixir_shallowwater_well_balanced_wet_dry.jl | 165 +++++++++++
.../elixir_shallowwater_conical_island.jl | 116 ++++++++
.../elixir_shallowwater_parabolic_bowl.jl | 120 ++++++++
...ixir_shallowwater_well_balanced_wet_dry.jl | 198 +++++++++++++
...ixir_shallowwater_three_mound_dam_break.jl | 139 +++++++++
src/Trixi.jl | 9 +-
src/callbacks_stage/callbacks_stage.jl | 2 +
.../positivity_shallow_water.jl | 89 ++++++
.../positivity_shallow_water_dg1d.jl | 89 ++++++
.../positivity_shallow_water_dg2d.jl | 90 ++++++
src/equations/numerical_fluxes.jl | 23 ++
src/equations/shallow_water_1d.jl | 192 ++++++++++++-
src/equations/shallow_water_2d.jl | 270 +++++++++++++++++-
src/equations/shallow_water_two_layer_1d.jl | 2 +
src/equations/shallow_water_two_layer_2d.jl | 96 ++++---
src/solvers/dgsem_tree/indicators.jl | 73 ++++-
src/solvers/dgsem_tree/indicators_1d.jl | 109 +++++++
src/solvers/dgsem_tree/indicators_2d.jl | 110 +++++++
test/Project.toml | 1 +
test/test_structured_2d.jl | 25 +-
test/test_tree_1d_shallowwater.jl | 23 ++
test/test_tree_1d_shallowwater_twolayer.jl | 2 +
test/test_tree_2d_shallowwater.jl | 24 ++
test/test_tree_2d_shallowwater_twolayer.jl | 20 +-
test/test_unit.jl | 4 +
test/test_unstructured_2d.jl | 18 ++
32 files changed, 2608 insertions(+), 72 deletions(-)
create mode 100644 examples/structured_2d_dgsem/elixir_shallowwater_conical_island.jl
create mode 100644 examples/structured_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl
create mode 100644 examples/structured_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
create mode 100644 examples/tree_1d_dgsem/elixir_shallowwater_beach.jl
create mode 100644 examples/tree_1d_dgsem/elixir_shallowwater_parabolic_bowl.jl
create mode 100644 examples/tree_1d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
create mode 100644 examples/tree_2d_dgsem/elixir_shallowwater_conical_island.jl
create mode 100644 examples/tree_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl
create mode 100644 examples/tree_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
create mode 100644 examples/unstructured_2d_dgsem/elixir_shallowwater_three_mound_dam_break.jl
create mode 100644 src/callbacks_stage/positivity_shallow_water.jl
create mode 100644 src/callbacks_stage/positivity_shallow_water_dg1d.jl
create mode 100644 src/callbacks_stage/positivity_shallow_water_dg2d.jl
diff --git a/NEWS.md b/NEWS.md
index 35c7039b2ef..8e374d9ce99 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -10,6 +10,7 @@ for human readability.
- Experimental support for 3D parabolic diffusion terms has been added.
- Capability to set truly discontinuous initial conditions in 1D.
+- Wetting and drying feature and examples for 1D and 2D shallow water equations
#### Changed
diff --git a/examples/structured_2d_dgsem/elixir_shallowwater_conical_island.jl b/examples/structured_2d_dgsem/elixir_shallowwater_conical_island.jl
new file mode 100644
index 00000000000..44bc7a12b35
--- /dev/null
+++ b/examples/structured_2d_dgsem/elixir_shallowwater_conical_island.jl
@@ -0,0 +1,113 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+ ###############################################################################
+ # Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations2D(gravity_constant=9.81, H0=1.4)
+
+"""
+ initial_condition_conical_island(x, t, equations::ShallowWaterEquations2D)
+
+Initial condition for the [`ShallowWaterEquations2D`](@ref) to test the [`hydrostatic_reconstruction_chen_noelle`](@ref)
+and its handling of discontinuous water heights at the start in combination with wetting and
+drying. The bottom topography is given by a conical island in the middle of the domain. Around that
+island, there is a cylindrical water column at t=0 and the rest of the domain is dry. This
+discontinuous water height is smoothed by a logistic function. This simulation uses periodic
+boundary conditions.
+"""
+function initial_condition_conical_island(x, t, equations::ShallowWaterEquations2D)
+ # Set the background values
+
+ v1 = 0.0
+ v2 = 0.0
+
+ x1, x2 = x
+ b = max(0.1, 1.0 - 4.0 * sqrt(x1^2 + x2^2))
+
+ # use a logistic function to transfer water height value smoothly
+ L = equations.H0 # maximum of function
+ x0 = 0.3 # center point of function
+ k = -25.0 # sharpness of transfer
+
+ H = max(b, L/(1.0 + exp(-k*(sqrt(x1^2+x2^2) - x0))))
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v1) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_conical_island
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(4)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Get the StructuredMesh and setup a periodic mesh
+
+coordinates_min = (-1.0, -1.0)
+coordinates_max = (1.0, 1.0)
+
+cells_per_dimension = (16, 16)
+
+mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max)
+
+# Create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solver
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+###############################################################################
+# Callbacks
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+###############################################################################
+# run the simulation
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/examples/structured_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl b/examples/structured_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl
new file mode 100644
index 00000000000..15cfe6698fc
--- /dev/null
+++ b/examples/structured_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl
@@ -0,0 +1,119 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations2D(gravity_constant=9.81)
+
+"""
+ initial_condition_parabolic_bowl(x, t, equations:: ShallowWaterEquations2D)
+
+Well-known initial condition to test the [`hydrostatic_reconstruction_chen_noelle`](@ref) and its
+wet-dry mechanics. This test has an analytical solution. The initial condition is defined by the
+analytical solution at time t=0. The bottom topography defines a bowl and the water level is given
+by an oscillating lake.
+
+The original test and its analytical solution were first presented in
+- William C. Thacker (1981)
+ Some exact solutions to the nonlinear shallow-water wave equations
+ [DOI: 10.1017/S0022112081001882](https://doi.org/10.1017/S0022112081001882).
+
+The particular setup below is taken from Section 6.2 of
+- Niklas Wintermeyer, Andrew R. Winters, Gregor J. Gassner and Timothy Warburton (2018)
+ An entropy stable discontinuous Galerkin method for the shallow water equations on
+ curvilinear meshes with wet/dry fronts accelerated by GPUs
+ [DOI: 10.1016/j.jcp.2018.08.038](https://doi.org/10.1016/j.jcp.2018.08.038).
+"""
+function initial_condition_parabolic_bowl(x, t, equations:: ShallowWaterEquations2D)
+ a = 1.0
+ h_0 = 0.1
+ sigma = 0.5
+ ω = sqrt(2 * equations.gravity * h_0) / a
+
+ v1 = -sigma * ω * sin(ω * t)
+ v2 = sigma * ω * cos(ω * t)
+
+ b = h_0 * ((x[1])^2 + (x[2])^2) / a^2
+
+ H = sigma * h_0 / a^2 * (2 * x[1] * cos(ω * t) + 2 * x[2] * sin(ω * t) - sigma) + h_0
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v1) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_parabolic_bowl
+
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(4)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.6,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+
+###############################################################################
+
+coordinates_min = (-2.0, -2.0)
+coordinates_max = (2.0, 2.0)
+
+cells_per_dimension = (150, 150)
+
+mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+
+summary_callback() # print the timer summary
diff --git a/examples/structured_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl b/examples/structured_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
new file mode 100644
index 00000000000..b18b02e0b4c
--- /dev/null
+++ b/examples/structured_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
@@ -0,0 +1,200 @@
+
+using OrdinaryDiffEq
+using Trixi
+using Printf: @printf, @sprintf
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+
+equations = ShallowWaterEquations2D(gravity_constant=9.812)
+
+"""
+ initial_condition_well_balanced_chen_noelle(x, t, equations:: ShallowWaterEquations2D)
+
+Initial condition with a complex (discontinuous) bottom topography to test the well-balanced
+property for the [`hydrostatic_reconstruction_chen_noelle`](@ref) including dry areas within the
+domain. The errors from the analysis callback are not important but the error for this
+lake-at-rest test case `∑|H0-(h+b)|` should be around machine roundoff.
+
+The initial condition is taken from Section 5.2 of the paper:
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+function initial_condition_complex_bottom_well_balanced(x, t, equations:: ShallowWaterEquations2D)
+ v1 = 0
+ v2 = 0
+ b = sin(4 * pi * x[1]) + 3
+
+ if x[1] >= 0.5
+ b = sin(4 * pi * x[1]) + 1
+ end
+
+ H = max(b, 2.5)
+
+ if x[1] >= 0.5
+ H = max(b, 1.5)
+ end
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v1) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_complex_bottom_well_balanced
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(3)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+
+###############################################################################
+# Create the StructuredMesh for the domain [0, 1]^2
+
+coordinates_min = (0.0, 0.0)
+coordinates_max = (1.0, 1.0)
+
+cells_per_dimension = (16, 16)
+
+mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max)
+
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+###############################################################################
+# Workaround to set a discontinuous water and bottom topography for
+# debugging and testing. Essentially, this is a slight augmentation of the
+# `compute_coefficients` where the `x` node value passed here is slightly
+# perturbed to the left / right in order to set a true discontinuity that avoids
+# the doubled value of the LGL nodes at a particular element interface.
+#
+# Note! The errors from the analysis callback are not important but the error
+# for this lake at rest test case `∑|H0-(h+b)|` should be near machine roundoff.
+
+# point to the data we want to augment
+u = Trixi.wrap_array(ode.u0, semi)
+# reset the initial condition
+for element in eachelement(semi.solver, semi.cache)
+ for j in eachnode(semi.solver), i in eachnode(semi.solver)
+ x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, semi.solver, i, j, element)
+ # We know that the discontinuity is a vertical line. Slightly augment the x value by a factor
+ # of unit roundoff to avoid the repeted value from the LGL nodes at at interface.
+ if i == 1
+ x_node = SVector(nextfloat(x_node[1]) , x_node[2])
+ elseif i == nnodes(semi.solver)
+ x_node = SVector(prevfloat(x_node[1]) , x_node[2])
+ end
+ u_node = initial_condition_complex_bottom_well_balanced(x_node, first(tspan), equations)
+ Trixi.set_node_vars!(u, u_node, equations, semi.solver, i, j, element)
+ end
+end
+
+###############################################################################
+# Callbacks
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=1000,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+stepsize_callback = StepsizeCallback(cfl=1.0)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution, stepsize_callback)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, SSPRK43(stage_limiter!); dt=1.0,
+ ode_default_options()..., callback=callbacks, adaptive=false);
+
+summary_callback() # print the timer summary
+
+###############################################################################
+# Workaround to compute the well-balancedness error for this particular problem
+# that has two reference water heights. One for a lake to the left of the
+# discontinuous bottom topography `H0_upper = 2.5` and another for a lake to the
+# right of the discontinuous bottom topography `H0_lower = 1.5`.
+
+# Declare a special version of the function to compute the lake-at-rest error
+# OBS! The reference water height values are hardcoded for convenience.
+function lake_at_rest_error_two_level(u, x, equations::ShallowWaterEquations2D)
+ h, _, _, b = u
+
+ # For well-balancedness testing with possible wet/dry regions the reference
+ # water height `H0` accounts for the possibility that the bottom topography
+ # can emerge out of the water as well as for the threshold offset to avoid
+ # division by a "hard" zero water heights as well.
+ if x[1] < 0.5
+ H0_wet_dry = max( 2.5 , b + equations.threshold_limiter )
+ else
+ H0_wet_dry = max( 1.5 , b + equations.threshold_limiter )
+ end
+
+ return abs(H0_wet_dry - (h + b))
+end
+
+# point to the data we want to analyze
+u = Trixi.wrap_array(sol[end], semi)
+# Perform the actual integration of the well-balancedness error over the domain
+l1_well_balance_error = Trixi.integrate_via_indices(u, mesh, equations, semi.solver, semi.cache; normalize=true) do u, i, j, element, equations, solver
+ x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, solver, i, j, element)
+ # We know that the discontinuity is a vertical line. Slightly augment the x value by a factor
+ # of unit roundoff to avoid the repeted value from the LGL nodes at at interface.
+ if i == 1
+ x_node = SVector(nextfloat(x_node[1]) , x_node[2])
+ elseif i == nnodes(semi.solver)
+ x_node = SVector(prevfloat(x_node[1]) , x_node[2])
+ end
+ u_local = Trixi.get_node_vars(u, equations, solver, i, j, element)
+ return lake_at_rest_error_two_level(u_local, x_node, equations)
+end
+
+# report the well-balancedness lake-at-rest error to the screen
+println("─"^100)
+println(" Lake-at-rest error for '", Trixi.get_name(equations), "' with ", summary(solver),
+ " at final time " * @sprintf("%10.8e", tspan[end]))
+
+@printf(" %-12s:", Trixi.pretty_form_utf(lake_at_rest_error))
+@printf(" % 10.8e", l1_well_balance_error)
+println()
+println("─"^100)
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_beach.jl b/examples/tree_1d_dgsem/elixir_shallowwater_beach.jl
new file mode 100644
index 00000000000..1288bc5e66a
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_beach.jl
@@ -0,0 +1,121 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations1D(gravity_constant=9.812)
+
+"""
+ initial_condition_beach(x, t, equations:: ShallowWaterEquations1D)
+Initial condition to simulate a wave running towards a beach and crashing. Difficult test
+including both wetting and drying in the domain using slip wall boundary conditions.
+The bottom topography is altered to be differentiable on the domain [0,8] and
+differs from the reference below.
+
+The water height and speed functions used here, are adapted from the initial condition
+found in section 5.2 of the paper:
+ - Andreas Bollermann, Sebastian Noelle, Maria Lukáčová-Medvid’ová (2011)
+ Finite volume evolution Galerkin methods for the shallow water equations with dry beds\n
+ [DOI: 10.4208/cicp.220210.020710a](https://dx.doi.org/10.4208/cicp.220210.020710a)
+"""
+function initial_condition_beach(x, t, equations:: ShallowWaterEquations1D)
+ D = 1
+ delta = 0.02
+ gamma = sqrt((3 * delta) / (4 * D))
+ x_a = sqrt((4 * D) / (3 * delta)) * acosh(sqrt(20))
+
+ f = D + 40 * delta * sech(gamma * (8 * x[1] - x_a))^2
+
+ # steep curved beach
+ b = 0.01 + 99 / 409600 * 4^x[1]
+
+ if x[1] >= 6
+ H = b
+ v = 0.0
+ else
+ H = f
+ v = sqrt(equations.gravity / D) * H
+ end
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v, b), equations)
+end
+
+initial_condition = initial_condition_beach
+boundary_condition = boundary_condition_slip_wall
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(3)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Create the TreeMesh for the domain [0, 8]
+
+coordinates_min = 0.0
+coordinates_max = 8.0
+
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=7,
+ n_cells_max=10_000,
+ periodicity=false)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions=boundary_condition)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(dt=0.5,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_parabolic_bowl.jl b/examples/tree_1d_dgsem/elixir_shallowwater_parabolic_bowl.jl
new file mode 100644
index 00000000000..916bba76ece
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_parabolic_bowl.jl
@@ -0,0 +1,117 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations1D(gravity_constant=9.81)
+
+"""
+ initial_condition_parabolic_bowl(x, t, equations:: ShallowWaterEquations1D)
+
+Well-known initial condition to test the [`hydrostatic_reconstruction_chen_noelle`](@ref) and its
+wet-dry mechanics. This test has analytical solutions. The initial condition is defined by the
+analytical solution at time t=0. The bottom topography defines a bowl and the water level is given
+by an oscillating lake.
+
+The original test and its analytical solution in two dimensions were first presented in
+- William C. Thacker (1981)
+ Some exact solutions to the nonlinear shallow-water wave equations
+ [DOI: 10.1017/S0022112081001882](https://doi.org/10.1017/S0022112081001882).
+
+The particular setup below is taken from Section 6.2 of
+- Niklas Wintermeyer, Andrew R. Winters, Gregor J. Gassner and Timothy Warburton (2018)
+ An entropy stable discontinuous Galerkin method for the shallow water equations on
+ curvilinear meshes with wet/dry fronts accelerated by GPUs
+ [DOI: 10.1016/j.jcp.2018.08.038](https://doi.org/10.1016/j.jcp.2018.08.038).
+"""
+function initial_condition_parabolic_bowl(x, t, equations:: ShallowWaterEquations1D)
+ a = 1
+ h_0 = 0.1
+ sigma = 0.5
+ ω = sqrt(2 * equations.gravity * h_0) / a
+
+ v = -sigma * ω * sin(ω * t)
+
+ b = h_0 * x[1]^2 / a^2
+
+ H = sigma * h_0 / a^2 * (2 * x[1] * cos(ω * t) - sigma) + h_0
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v, b), equations)
+end
+
+initial_condition = initial_condition_parabolic_bowl
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(5)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Create the TreeMesh for the domain [-2, 2]
+
+coordinates_min = -2.0
+coordinates_max = 2.0
+
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=6,
+ n_cells_max=10_000)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=1000,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl b/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
new file mode 100644
index 00000000000..8de46c61794
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
@@ -0,0 +1,165 @@
+
+using OrdinaryDiffEq
+using Trixi
+using Printf: @printf, @sprintf
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations1D(gravity_constant=9.812)
+
+"""
+ initial_condition_complex_bottom_well_balanced(x, t, equations:: ShallowWaterEquations1D)
+
+Initial condition with a complex (discontinuous) bottom topography to test the well-balanced
+property for the [`hydrostatic_reconstruction_chen_noelle`](@ref) including dry areas within the
+domain. The errors from the analysis callback are not important but the error for this
+lake-at-rest test case `∑|H0-(h+b)|` should be around machine roundoff.
+
+The initial condition is taken from Section 5.2 of the paper:
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+function initial_condition_complex_bottom_well_balanced(x, t, equations:: ShallowWaterEquations1D)
+ v = 0.0
+ b = sin(4 * pi * x[1]) + 3
+
+ if x[1] >= 0.5
+ b = sin(4 * pi * x[1]) + 1
+ end
+
+ H = max(b, 2.5)
+
+ if x[1] >= 0.5
+ H = max(b, 1.5)
+ end
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v, b), equations)
+end
+
+initial_condition = initial_condition_complex_bottom_well_balanced
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(3)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Create the TreeMesh for the domain [0, 1]
+
+coordinates_min = 0.0
+coordinates_max = 1.0
+
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=6,
+ n_cells_max=10_000)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 25.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 5000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=5000,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+stepsize_callback = StepsizeCallback(cfl=1.5)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution,
+ stepsize_callback)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, SSPRK43(stage_limiter!); dt=1.0,
+ ode_default_options()..., callback=callbacks, adaptive=false);
+
+summary_callback() # print the timer summary
+
+###############################################################################
+# Workaround to compute the well-balancedness error for this particular problem
+# that has two reference water heights. One for a lake to the left of the
+# discontinuous bottom topography `H0_upper = 2.5` and another for a lake to the
+# right of the discontinuous bottom topography `H0_lower = 1.5`.
+
+# Declare a special version of the function to compute the lake-at-rest error
+# OBS! The reference water height values are hardcoded for convenience.
+function lake_at_rest_error_two_level(u, x, equations::ShallowWaterEquations1D)
+ h, _, b = u
+
+ # For well-balancedness testing with possible wet/dry regions the reference
+ # water height `H0` accounts for the possibility that the bottom topography
+ # can emerge out of the water as well as for the threshold offset to avoid
+ # division by a "hard" zero water heights as well.
+ if x[1] < 0.5
+ H0_wet_dry = max( 2.5 , b + equations.threshold_limiter )
+ else
+ H0_wet_dry = max( 1.5 , b + equations.threshold_limiter )
+ end
+
+ return abs(H0_wet_dry - (h + b))
+ end
+
+# point to the data we want to analyze
+u = Trixi.wrap_array(sol[end], semi)
+# Perform the actual integration of the well-balancedness error over the domain
+l1_well_balance_error = Trixi.integrate_via_indices(u, mesh, equations, semi.solver, semi.cache; normalize=true) do u, i, element, equations, solver
+ x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, solver, i, element)
+ # We know that the discontinuity is a vertical line. Slightly augment the x value by a factor
+ # of unit roundoff to avoid the repeted value from the LGL nodes at at interface.
+ if i == 1
+ x_node = SVector(nextfloat(x_node[1]))
+ elseif i == nnodes(semi.solver)
+ x_node = SVector(prevfloat(x_node[1]))
+ end
+ u_local = Trixi.get_node_vars(u, equations, solver, i, element)
+ return lake_at_rest_error_two_level(u_local, x_node, equations)
+end
+
+# report the well-balancedness lake-at-rest error to the screen
+println("─"^100)
+println(" Lake-at-rest error for '", Trixi.get_name(equations), "' with ", summary(solver),
+ " at final time " * @sprintf("%10.8e", tspan[end]))
+
+@printf(" %-12s:", Trixi.pretty_form_utf(lake_at_rest_error))
+@printf(" % 10.8e", l1_well_balance_error)
+println()
+println("─"^100)
\ No newline at end of file
diff --git a/examples/tree_2d_dgsem/elixir_shallowwater_conical_island.jl b/examples/tree_2d_dgsem/elixir_shallowwater_conical_island.jl
new file mode 100644
index 00000000000..7c60e35b03e
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_shallowwater_conical_island.jl
@@ -0,0 +1,116 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations2D(gravity_constant=9.81, H0=1.4)
+
+"""
+ initial_condition_conical_island(x, t, equations::ShallowWaterEquations2D)
+
+Initial condition for the [`ShallowWaterEquations2D`](@ref) to test the [`hydrostatic_reconstruction_chen_noelle`](@ref)
+and its handling of discontinuous water heights at the start in combination with wetting and
+drying. The bottom topography is given by a conical island in the middle of the domain. Around that
+island, there is a cylindrical water column at t=0 and the rest of the domain is dry. This
+discontinuous water height is smoothed by a logistic function. This simulation uses a Dirichlet
+boundary condition with the initial values. Due to the dry cells at the boundary, this has the
+effect of an outflow which can be seen in the simulation.
+"""
+function initial_condition_conical_island(x, t, equations::ShallowWaterEquations2D)
+ # Set the background values
+
+ v1 = 0.0
+ v2 = 0.0
+
+ x1, x2 = x
+ b = max(0.1, 1.0 - 4.0 * sqrt(x1^2 + x2^2))
+
+ # use a logistic function to transfer water height value smoothly
+ L = equations.H0 # maximum of function
+ x0 = 0.3 # center point of function
+ k = -25.0 # sharpness of transfer
+
+ H = max(b, L/(1.0 + exp(-k*(sqrt(x1^2+x2^2) - x0))))
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v1) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_conical_island
+boundary_conditions = BoundaryConditionDirichlet(initial_condition)
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(4)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Get the TreeMesh and setup a mesh
+
+coordinates_min = (-1.0, -1.0)
+coordinates_max = (1.0, 1.0)
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=4,
+ n_cells_max=10_000,
+ periodicity=false)
+
+# Create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions=boundary_conditions)
+
+###############################################################################
+# ODE solver
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+###############################################################################
+# Callbacks
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+###############################################################################
+# run the simulation
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+
+summary_callback() # print the timer summary
diff --git a/examples/tree_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl b/examples/tree_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl
new file mode 100644
index 00000000000..03dcf017266
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_shallowwater_parabolic_bowl.jl
@@ -0,0 +1,120 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations2D(gravity_constant=9.81)
+
+"""
+ initial_condition_parabolic_bowl(x, t, equations:: ShallowWaterEquations2D)
+
+Well-known initial condition to test the [`hydrostatic_reconstruction_chen_noelle`](@ref) and its
+wet-dry mechanics. This test has an analytical solution. The initial condition is defined by the
+analytical solution at time t=0. The bottom topography defines a bowl and the water level is given
+by an oscillating lake.
+
+The original test and its analytical solution were first presented in
+- William C. Thacker (1981)
+ Some exact solutions to the nonlinear shallow-water wave equations
+ [DOI: 10.1017/S0022112081001882](https://doi.org/10.1017/S0022112081001882).
+
+The particular setup below is taken from Section 6.2 of
+- Niklas Wintermeyer, Andrew R. Winters, Gregor J. Gassner and Timothy Warburton (2018)
+ An entropy stable discontinuous Galerkin method for the shallow water equations on
+ curvilinear meshes with wet/dry fronts accelerated by GPUs
+ [DOI: 10.1016/j.jcp.2018.08.038](https://doi.org/10.1016/j.jcp.2018.08.038).
+"""
+function initial_condition_parabolic_bowl(x, t, equations:: ShallowWaterEquations2D)
+ a = 1.0
+ h_0 = 0.1
+ sigma = 0.5
+ ω = sqrt(2 * equations.gravity * h_0) / a
+
+ v1 = -sigma * ω * sin(ω * t)
+ v2 = sigma * ω * cos(ω * t)
+
+ b = h_0 * ((x[1])^2 + (x[2])^2) / a^2
+
+ H = sigma * h_0 / a^2 * (2 * x[1] * cos(ω * t) + 2 * x[2] * sin(ω * t) - sigma) + h_0
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v1) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_parabolic_bowl
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(7)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.6,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+
+###############################################################################
+# Create the TreeMesh for the domain [-2, 2]^2
+
+coordinates_min = (-2.0, -2.0)
+coordinates_max = (2.0, 2.0)
+
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=5,
+ n_cells_max=10_000)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+
+summary_callback() # print the timer summary
diff --git a/examples/tree_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl b/examples/tree_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
new file mode 100644
index 00000000000..6fede2fa4ea
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_shallowwater_well_balanced_wet_dry.jl
@@ -0,0 +1,198 @@
+
+using OrdinaryDiffEq
+using Trixi
+using Printf: @printf, @sprintf
+
+###############################################################################
+# Semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+equations = ShallowWaterEquations2D(gravity_constant=9.812)
+
+"""
+ initial_condition_well_balanced_chen_noelle(x, t, equations:: ShallowWaterEquations2D)
+
+Initial condition with a complex (discontinuous) bottom topography to test the well-balanced
+property for the [`hydrostatic_reconstruction_chen_noelle`](@ref) including dry areas within the
+domain. The errors from the analysis callback are not important but the error for this
+lake-at-rest test case `∑|H0-(h+b)|` should be around machine roundoff.
+
+The initial condition is taken from Section 5.2 of the paper:
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+function initial_condition_complex_bottom_well_balanced(x, t, equations::ShallowWaterEquations2D)
+ v1 = 0
+ v2 = 0
+ b = sin(4 * pi * x[1]) + 3
+
+ if x[1] >= 0.5
+ b = sin(4 * pi * x[1]) + 1
+ end
+
+ H = max(b, 2.5)
+ if x[1] >= 0.5
+ H = max(b, 1.5)
+ end
+
+ # It is mandatory to shift the water level at dry areas to make sure the water height h
+ # stays positive. The system would not be stable for h set to a hard 0 due to division by h in
+ # the computation of velocity, e.g., (h v1) / h. Therefore, a small dry state threshold
+ # with a default value of 500*eps() ≈ 1e-13 in double precision, is set in the constructor above
+ # for the ShallowWaterEquations and added to the initial condition if h = 0.
+ # This default value can be changed within the constructor call depending on the simulation setup.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_complex_bottom_well_balanced
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(3)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Create the TreeMesh for the domain [0, 1]^2
+
+coordinates_min = (0.0, 0.0)
+coordinates_max = (1.0, 1.0)
+
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=3,
+ n_cells_max=10_000)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 50.0)
+ode = semidiscretize(semi, tspan)
+
+###############################################################################
+# Workaround to set a discontinuous water and bottom topography for
+# debugging and testing. Essentially, this is a slight augmentation of the
+# `compute_coefficients` where the `x` node value passed here is slightly
+# perturbed to the left / right in order to set a true discontinuity that avoids
+# the doubled value of the LGL nodes at a particular element interface.
+#
+# Note! The errors from the analysis callback are not important but the error
+# for this lake at rest test case `∑|H0-(h+b)|` should be near machine roundoff.
+
+# point to the data we want to augment
+u = Trixi.wrap_array(ode.u0, semi)
+# reset the initial condition
+for element in eachelement(semi.solver, semi.cache)
+ for j in eachnode(semi.solver), i in eachnode(semi.solver)
+ x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, semi.solver, i, j, element)
+ # We know that the discontinuity is a vertical line. Slightly augment the x value by a factor
+ # of unit roundoff to avoid the repeted value from the LGL nodes at at interface.
+ if i == 1
+ x_node = SVector(nextfloat(x_node[1]) , x_node[2])
+ elseif i == nnodes(semi.solver)
+ x_node = SVector(prevfloat(x_node[1]) , x_node[2])
+ end
+ u_node = initial_condition_complex_bottom_well_balanced(x_node, first(tspan), equations)
+ Trixi.set_node_vars!(u, u_node, equations, semi.solver, i, j, element)
+ end
+end
+
+###############################################################################
+# Callbacks
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=false)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=1000,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+stepsize_callback = StepsizeCallback(cfl=2.0)
+
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution,
+ stepsize_callback)
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, SSPRK43(stage_limiter!); dt=1.0,
+ ode_default_options()..., callback=callbacks, adaptive=false);
+
+summary_callback() # print the timer summary
+
+###############################################################################
+# Workaround to compute the well-balancedness error for this particular problem
+# that has two reference water heights. One for a lake to the left of the
+# discontinuous bottom topography `H0_upper = 2.5` and another for a lake to the
+# right of the discontinuous bottom topography `H0_lower = 1.5`.
+
+# Declare a special version of the function to compute the lake-at-rest error
+# OBS! The reference water height values are hardcoded for convenience.
+function lake_at_rest_error_two_level(u, x, equations::ShallowWaterEquations2D)
+ h, _, _, b = u
+
+ # For well-balancedness testing with possible wet/dry regions the reference
+ # water height `H0` accounts for the possibility that the bottom topography
+ # can emerge out of the water as well as for the threshold offset to avoid
+ # division by a "hard" zero water heights as well.
+
+ if x[1] < 0.5
+ H0_wet_dry = max( 2.5 , b + equations.threshold_limiter )
+ else
+ H0_wet_dry = max( 1.5 , b + equations.threshold_limiter )
+ end
+
+ return abs(H0_wet_dry - (h + b))
+end
+
+# point to the data we want to analyze
+u = Trixi.wrap_array(sol[end], semi)
+# Perform the actual integration of the well-balancedness error over the domain
+l1_well_balance_error = Trixi.integrate_via_indices(u, mesh, equations, semi.solver, semi.cache; normalize=true) do u, i, j, element, equations, solver
+ x_node = Trixi.get_node_coords(semi.cache.elements.node_coordinates, equations, solver, i, j, element)
+ # We know that the discontinuity is a vertical line. Slightly augment the x value by a factor
+ # of unit roundoff to avoid the repeted value from the LGL nodes at at interface.
+ if i == 1
+ x_node = SVector(nextfloat(x_node[1]) , x_node[2])
+ elseif i == nnodes(semi.solver)
+ x_node = SVector(prevfloat(x_node[1]) , x_node[2])
+ end
+ u_local = Trixi.get_node_vars(u, equations, solver, i, j, element)
+ return lake_at_rest_error_two_level(u_local, x_node, equations)
+end
+
+# report the well-balancedness lake-at-rest error to the screen
+println("─"^100)
+println(" Lake-at-rest error for '", Trixi.get_name(equations), "' with ", summary(solver),
+ " at final time " * @sprintf("%10.8e", tspan[end]))
+
+@printf(" %-12s:", Trixi.pretty_form_utf(lake_at_rest_error))
+@printf(" % 10.8e", l1_well_balance_error)
+println()
+println("─"^100)
diff --git a/examples/unstructured_2d_dgsem/elixir_shallowwater_three_mound_dam_break.jl b/examples/unstructured_2d_dgsem/elixir_shallowwater_three_mound_dam_break.jl
new file mode 100644
index 00000000000..65b0fcae462
--- /dev/null
+++ b/examples/unstructured_2d_dgsem/elixir_shallowwater_three_mound_dam_break.jl
@@ -0,0 +1,139 @@
+
+using Downloads: download
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the shallow water equations
+#
+# TODO: TrixiShallowWater: wet/dry example elixir
+
+
+equations = ShallowWaterEquations2D(gravity_constant=9.81, H0=1.875,
+ threshold_limiter=1e-12, threshold_wet=1e-14)
+
+
+"""
+ initial_condition_three_mounds(x, t, equations::ShallowWaterEquations2D)
+
+Initial condition simulating a dam break. The bottom topography is given by one large and two smaller
+mounds. The mounds are flooded by the water for t > 0. To smooth the discontinuity, a logistic function
+is applied.
+
+The initial conditions is taken from Section 6.3 of the paper:
+- Niklas Wintermeyer, Andrew R. Winters, Gregor J. Gassner and Timothy Warburton (2018)
+ An entropy stable discontinuous Galerkin method for the shallow water equations on
+ curvilinear meshes with wet/dry fronts accelerated by GPUs\n
+ [DOI: 10.1016/j.jcp.2018.08.038](https://doi.org/10.1016/j.jcp.2018.08.038)
+"""
+function initial_condition_three_mounds(x, t, equations::ShallowWaterEquations2D)
+
+ # Set the background values
+ v1 = 0.0
+ v2 = 0.0
+
+ x1, x2 = x
+ M_1 = 1 - 0.1 * sqrt( (x1 - 30.0)^2 + (x2 - 22.5)^2 )
+ M_2 = 1 - 0.1 * sqrt( (x1 - 30.0)^2 + (x2 - 7.5)^2 )
+ M_3 = 2.8 - 0.28 * sqrt( (x1 - 47.5)^2 + (x2 - 15.0)^2 )
+
+ b = max(0.0, M_1, M_2, M_3)
+
+ # use a logistic function to transfer water height value smoothly
+ L = equations.H0 # maximum of function
+ x0 = 8 # center point of function
+ k = -75.0 # sharpness of transfer
+
+ H = max(b, L / (1.0 + exp(-k * (x1 - x0))))
+
+ # Avoid division by zero by adjusting the initial condition with a small dry state threshold
+ # that defaults to 500*eps() ≈ 1e-13 in double precision and is set in the constructor above
+ # for the ShallowWaterEquations struct.
+ H = max(H, b + equations.threshold_limiter)
+ return prim2cons(SVector(H, v1, v2, b), equations)
+end
+
+initial_condition = initial_condition_three_mounds
+
+function boundary_condition_outflow(u_inner, normal_direction::AbstractVector, x, t,
+ surface_flux_function, equations::ShallowWaterEquations2D)
+ # Impulse and bottom from inside, height from external state
+ u_outer = SVector(equations.threshold_wet, u_inner[2], u_inner[3], u_inner[4])
+
+ # calculate the boundary flux
+ flux = surface_flux_function(u_inner, u_outer, normal_direction, equations)
+
+ return flux
+end
+
+boundary_conditions = Dict( :Bottom => boundary_condition_slip_wall,
+ :Top => boundary_condition_slip_wall,
+ :Right => boundary_condition_outflow,
+ :Left => boundary_condition_slip_wall )
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+surface_flux = (FluxHydrostaticReconstruction(flux_hll_chen_noelle, hydrostatic_reconstruction_chen_noelle),
+ flux_nonconservative_chen_noelle)
+
+basis = LobattoLegendreBasis(4)
+
+indicator_sc = IndicatorHennemannGassnerShallowWater(equations, basis,
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable=waterheight_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+###############################################################################
+# Get the unstructured quad mesh from a file (downloads the file if not available locally)
+
+default_meshfile = joinpath(@__DIR__, "mesh_three_mound.mesh")
+
+isfile(default_meshfile) || download("https://gist.githubusercontent.com/svengoldberg/c3c87fecb3fc6e46be7f0d1c7cb35f83/raw/e817ecd9e6c4686581d63c46128f9b6468d396d3/mesh_three_mound.mesh",
+ default_meshfile)
+
+meshfile = default_meshfile
+
+mesh = UnstructuredMesh2D(meshfile)
+
+# Create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;
+ boundary_conditions=boundary_conditions)
+
+###############################################################################
+# ODE solver
+
+tspan = (0.0, 20.0)
+ode = semidiscretize(semi, tspan)
+
+###############################################################################
+# Callbacks
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+###############################################################################
+# run the simulation
+
+stage_limiter! = PositivityPreservingLimiterShallowWater(variables=(Trixi.waterheight,))
+
+sol = solve(ode, SSPRK43(stage_limiter!);
+ ode_default_options()..., callback=callbacks);
+summary_callback() # print the timer summary
diff --git a/src/Trixi.jl b/src/Trixi.jl
index 34a1977d4f5..cf6158e29eb 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -162,9 +162,13 @@ export flux, flux_central, flux_lax_friedrichs, flux_hll, flux_hllc, flux_hlle,
flux_fjordholm_etal, flux_nonconservative_fjordholm_etal, flux_es_fjordholm_etal,
flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal,
hydrostatic_reconstruction_audusse_etal, flux_nonconservative_audusse_etal,
+# TODO: TrixiShallowWater: move anything with "chen_noelle" to new file
+ hydrostatic_reconstruction_chen_noelle, flux_nonconservative_chen_noelle,
+ flux_hll_chen_noelle,
FluxPlusDissipation, DissipationGlobalLaxFriedrichs, DissipationLocalLaxFriedrichs,
FluxLaxFriedrichs, max_abs_speed_naive,
FluxHLL, min_max_speed_naive, min_max_speed_davis, min_max_speed_einfeldt,
+ min_max_speed_chen_noelle,
FluxLMARS,
FluxRotated,
flux_shima_etal_turbo, flux_ranocha_turbo,
@@ -215,6 +219,8 @@ export DG,
VolumeIntegralFluxDifferencing,
VolumeIntegralPureLGLFiniteVolume,
VolumeIntegralShockCapturingHG, IndicatorHennemannGassner,
+# TODO: TrixiShallowWater: move new indicator
+ IndicatorHennemannGassnerShallowWater,
VolumeIntegralUpwind,
SurfaceIntegralWeakForm, SurfaceIntegralStrongForm,
SurfaceIntegralUpwind,
@@ -248,7 +254,8 @@ export ControllerThreeLevel, ControllerThreeLevelCombined,
IndicatorNeuralNetwork, NeuralNetworkPerssonPeraire, NeuralNetworkRayHesthaven,
NeuralNetworkCNN
-export PositivityPreservingLimiterZhangShu
+# TODO: TrixiShallowWater: move new limiter
+export PositivityPreservingLimiterZhangShu, PositivityPreservingLimiterShallowWater
export trixi_include, examples_dir, get_examples, default_example,
default_example_unstructured, ode_default_options
diff --git a/src/callbacks_stage/callbacks_stage.jl b/src/callbacks_stage/callbacks_stage.jl
index 7609f9b341d..ab0f34efb78 100644
--- a/src/callbacks_stage/callbacks_stage.jl
+++ b/src/callbacks_stage/callbacks_stage.jl
@@ -6,4 +6,6 @@
#! format: noindent
include("positivity_zhang_shu.jl")
+# TODO: TrixiShallowWater: move specific limiter file
+include("positivity_shallow_water.jl")
end # @muladd
diff --git a/src/callbacks_stage/positivity_shallow_water.jl b/src/callbacks_stage/positivity_shallow_water.jl
new file mode 100644
index 00000000000..36276026fe9
--- /dev/null
+++ b/src/callbacks_stage/positivity_shallow_water.jl
@@ -0,0 +1,89 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+# TODO: TrixiShallowWater: generic wet/dry limiter
+
+"""
+ PositivityPreservingLimiterShallowWater(; variables)
+
+The limiter is specifically designed for the shallow water equations.
+It is applied to all scalar `variables` in their given order
+using the defined `threshold_limiter` from the [`ShallowWaterEquations1D`](@ref) struct
+or the [`ShallowWaterEquations2D`](@ref) struct to determine the minimal acceptable values.
+The order of the `variables` is important and might have a strong influence
+on the robustness.
+
+As opposed to the standard version of the [`PositivityPreservingLimiterZhangShu`](@ref),
+nodes with a water height below the `threshold_limiter` are treated in a special way.
+To avoid numerical problems caused by velocities close to zero,
+the velocity is cut off, such that the node can be identified as "dry". The special feature of the
+`ShallowWaterEquations` used here is that the bottom topography is stored as an additional
+quantity in the solution vector `u`. However, the value of the bottom topography
+should not be changed. That is why, it is not limited.
+
+After the limiting process is applied to all degrees of freedom, for safety reasons,
+the `threshold_limiter` is applied again on all the DG nodes in order to avoid water height below.
+In the case where the cell mean value is below the threshold before applying the limiter,
+there could still be dry nodes afterwards due to the logic of the limiter.
+
+This fully-discrete positivity-preserving limiter is based on the work of
+- Zhang, Shu (2011)
+ Maximum-principle-satisfying and positivity-preserving high-order schemes
+ for conservation laws: survey and new developments
+ [doi: 10.1098/rspa.2011.0153](https://doi.org/10.1098/rspa.2011.0153)
+"""
+struct PositivityPreservingLimiterShallowWater{N, Variables <: NTuple{N, Any}}
+ variables::Variables
+end
+
+function PositivityPreservingLimiterShallowWater(; variables)
+ PositivityPreservingLimiterShallowWater(variables)
+end
+
+function (limiter!::PositivityPreservingLimiterShallowWater)(u_ode, integrator,
+ semi::AbstractSemidiscretization,
+ t)
+ u = wrap_array(u_ode, semi)
+ @trixi_timeit timer() "positivity-preserving limiter" limiter_shallow_water!(u,
+ limiter!.variables,
+ mesh_equations_solver_cache(semi)...)
+end
+
+# Iterate over tuples in a type-stable way using "lispy tuple programming",
+# similar to https://stackoverflow.com/a/55849398:
+# Iterating over tuples of different functions isn't type-stable in general
+# but accessing the first element of a tuple is type-stable. Hence, it's good
+# to process one element at a time and replace iteration by recursion here.
+# Note that you shouldn't use this with too many elements per tuple since the
+# compile times can increase otherwise - but a handful of elements per tuple
+# is definitely fine.
+function limiter_shallow_water!(u, variables::NTuple{N, Any},
+ mesh,
+ equations::Union{ShallowWaterEquations1D,
+ ShallowWaterEquations2D},
+ solver, cache) where {N}
+ variable = first(variables)
+ remaining_variables = Base.tail(variables)
+
+ limiter_shallow_water!(u, equations.threshold_limiter, variable, mesh, equations,
+ solver, cache)
+ limiter_shallow_water!(u, remaining_variables, mesh, equations, solver, cache)
+ return nothing
+end
+
+# terminate the type-stable iteration over tuples
+function limiter_shallow_water!(u, variables::Tuple{},
+ mesh,
+ equations::Union{ShallowWaterEquations1D,
+ ShallowWaterEquations2D},
+ solver, cache)
+ nothing
+end
+
+include("positivity_shallow_water_dg1d.jl")
+include("positivity_shallow_water_dg2d.jl")
+end # @muladd
diff --git a/src/callbacks_stage/positivity_shallow_water_dg1d.jl b/src/callbacks_stage/positivity_shallow_water_dg1d.jl
new file mode 100644
index 00000000000..13c6866e895
--- /dev/null
+++ b/src/callbacks_stage/positivity_shallow_water_dg1d.jl
@@ -0,0 +1,89 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+# TODO: TrixiShallowWater: 1D wet/dry limiter should move
+
+function limiter_shallow_water!(u, threshold::Real, variable,
+ mesh::AbstractMesh{1},
+ equations::ShallowWaterEquations1D,
+ dg::DGSEM, cache)
+ @unpack weights = dg.basis
+
+ @threaded for element in eachelement(dg, cache)
+ # determine minimum value
+ value_min = typemax(eltype(u))
+ for i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, element)
+ value_min = min(value_min, variable(u_node, equations))
+ end
+
+ # detect if limiting is necessary
+ value_min < threshold || continue
+
+ # compute mean value
+ u_mean = zero(get_node_vars(u, equations, dg, 1, element))
+ for i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, element)
+ u_mean += u_node * weights[i]
+ end
+ # note that the reference element is [-1,1]^ndims(dg), thus the weights sum to 2
+ u_mean = u_mean / 2^ndims(mesh)
+
+ # We compute the value directly with the mean values, as we assume that
+ # Jensen's inequality holds (e.g. pressure for compressible Euler equations).
+ value_mean = variable(u_mean, equations)
+ theta = (value_mean - threshold) / (value_mean - value_min)
+ for i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, element)
+
+ # Cut off velocity in case that the waterheight is smaller than the threshold
+
+ h_node, h_v_node, b_node = u_node
+ h_mean, h_v_mean, _ = u_mean # b_mean is not used as b_node must not be overwritten
+
+ # Set them both to zero to apply linear combination correctly
+ if h_node <= threshold
+ h_v_node = zero(eltype(u))
+ h_v_mean = zero(eltype(u))
+ end
+
+ u_node = SVector(h_node, h_v_node, b_node)
+ u_mean = SVector(h_mean, h_v_mean, b_node)
+
+ # When velocity is cut off, the only averaged value is the waterheight,
+ # because the velocity is set to zero and this value is passed.
+ # Otherwise, the velocity is averaged, as well.
+ # Note that the auxiliary bottom topography variable `b` is never limited.
+ set_node_vars!(u, theta * u_node + (1 - theta) * u_mean,
+ equations, dg, i, element)
+ end
+ end
+
+ # "Safety" application of the wet/dry thresholds over all the DG nodes
+ # on the current `element` after the limiting above in order to avoid dry nodes.
+ # If the value_mean < threshold before applying limiter, there
+ # could still be dry nodes afterwards due to logic of the limiting
+ @threaded for element in eachelement(dg, cache)
+ for i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, element)
+
+ h, hv, b = u_node
+
+ if h <= threshold
+ h = threshold
+ hv = zero(eltype(u))
+ end
+
+ u_node = SVector(h, hv, b)
+
+ set_node_vars!(u, u_node, equations, dg, i, element)
+ end
+ end
+
+ return nothing
+end
+end # @muladd
diff --git a/src/callbacks_stage/positivity_shallow_water_dg2d.jl b/src/callbacks_stage/positivity_shallow_water_dg2d.jl
new file mode 100644
index 00000000000..da3a25fdcf4
--- /dev/null
+++ b/src/callbacks_stage/positivity_shallow_water_dg2d.jl
@@ -0,0 +1,90 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+# TODO: TrixiShallowWater: 2D wet/dry limiter should move
+
+function limiter_shallow_water!(u, threshold::Real, variable,
+ mesh::AbstractMesh{2},
+ equations::ShallowWaterEquations2D, dg::DGSEM, cache)
+ @unpack weights = dg.basis
+
+ @threaded for element in eachelement(dg, cache)
+ # determine minimum value
+ value_min = typemax(eltype(u))
+ for j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, j, element)
+ value_min = min(value_min, variable(u_node, equations))
+ end
+
+ # detect if limiting is necessary
+ value_min < threshold || continue
+
+ # compute mean value
+ u_mean = zero(get_node_vars(u, equations, dg, 1, 1, element))
+ for j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, j, element)
+ u_mean += u_node * weights[i] * weights[j]
+ end
+ # note that the reference element is [-1,1]^ndims(dg), thus the weights sum to 2
+ u_mean = u_mean / 2^ndims(mesh)
+
+ # We compute the value directly with the mean values, as we assume that
+ # Jensen's inequality holds (e.g. pressure for compressible Euler equations).
+ value_mean = variable(u_mean, equations)
+ theta = (value_mean - threshold) / (value_mean - value_min)
+ for j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, j, element)
+
+ # Cut off velocity in case that the water height is smaller than the threshold
+
+ h_node, h_v1_node, h_v2_node, b_node = u_node
+ h_mean, h_v1_mean, h_v2_mean, _ = u_mean # b_mean is not used as it must not be overwritten
+
+ if h_node <= threshold
+ h_v1_node = zero(eltype(u))
+ h_v2_node = zero(eltype(u))
+ h_v1_mean = zero(eltype(u))
+ h_v2_mean = zero(eltype(u))
+ end
+
+ u_node = SVector(h_node, h_v1_node, h_v2_node, b_node)
+ u_mean = SVector(h_mean, h_v1_mean, h_v2_mean, b_node)
+
+ # When velocities are cut off, the only averaged value is the water height,
+ # because the velocities are set to zero and this value is passed.
+ # Otherwise, the velocities are averaged, as well.
+ # Note that the auxiliary bottom topography variable `b` is never limited.
+ set_node_vars!(u, theta * u_node + (1 - theta) * u_mean,
+ equations, dg, i, j, element)
+ end
+ end
+
+ # "Safety" application of the wet/dry thresholds over all the DG nodes
+ # on the current `element` after the limiting above in order to avoid dry nodes.
+ # If the value_mean < threshold before applying limiter, there
+ # could still be dry nodes afterwards due to logic of the limiting
+ @threaded for element in eachelement(dg, cache)
+ for j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, j, element)
+
+ h, h_v1, h_v2, b = u_node
+
+ if h <= threshold
+ h = threshold
+ h_v1 = zero(eltype(u))
+ h_v2 = zero(eltype(u))
+ end
+
+ u_node = SVector(h, h_v1, h_v2, b)
+
+ set_node_vars!(u, u_node, equations, dg, i, j, element)
+ end
+ end
+
+ return nothing
+end
+end # @muladd
diff --git a/src/equations/numerical_fluxes.jl b/src/equations/numerical_fluxes.jl
index abd9d66c490..87010275f2c 100644
--- a/src/equations/numerical_fluxes.jl
+++ b/src/equations/numerical_fluxes.jl
@@ -304,6 +304,29 @@ See [`FluxHLL`](@ref).
"""
const flux_hll = FluxHLL()
+# TODO: TrixiShallowWater: move the chen_noelle flux structure to the new package
+
+# An empty version of the `min_max_speed_chen_noelle` function is declared here
+# in order to create a dimension agnostic version of `flux_hll_chen_noelle`.
+# The full description of this wave speed estimate can be found in the docstrings
+# for `min_max_speed_chen_noelle` in `shallow_water_1d.jl` or `shallow_water_2d.jl`.
+function min_max_speed_chen_noelle end
+
+"""
+ flux_hll_chen_noelle = FluxHLL(min_max_speed_chen_noelle)
+
+An instance of [`FluxHLL`](@ref) specific to the shallow water equations that
+uses the wave speed estimates from [`min_max_speed_chen_noelle`](@ref).
+This HLL flux is guaranteed to have zero numerical mass flux out of a "dry" element,
+maintain positivity of the water height, and satisfy an entropy inequality.
+
+For complete details see Section 2.4 of the following reference
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI: 10.1137/15M1053074](https://doi.org/10.1137/15M1053074)
+"""
+const flux_hll_chen_noelle = FluxHLL(min_max_speed_chen_noelle)
+
"""
flux_shima_etal_turbo(u_ll, u_rr, orientation_or_normal_direction, equations)
diff --git a/src/equations/shallow_water_1d.jl b/src/equations/shallow_water_1d.jl
index c33b31fca81..57bcb1212e1 100644
--- a/src/equations/shallow_water_1d.jl
+++ b/src/equations/shallow_water_1d.jl
@@ -6,7 +6,7 @@
#! format: noindent
@doc raw"""
- ShallowWaterEquations1D(gravity, H0)
+ ShallowWaterEquations1D(; gravity, H0 = 0, threshold_limiter = nothing threshold_wet = nothing)
Shallow water equations (SWE) in one space dimension. The equations are given by
```math
@@ -24,6 +24,12 @@ also defines the total water height as ``H = h + b``.
The additional quantity ``H_0`` is also available to store a reference value for the total water height that
is useful to set initial conditions or test the "lake-at-rest" well-balancedness.
+Also, there are two thresholds which prevent numerical problems as well as instabilities. Both of them do not
+have to be passed, as default values are defined within the struct. The first one, `threshold_limiter`, is
+used in [`PositivityPreservingLimiterShallowWater`](@ref) on the water height, as a (small) shift on the initial
+condition and cutoff before the next time step. The second one, `threshold_wet`, is applied on the water height to
+define when the flow is "wet" before calculating the numerical flux.
+
The bottom topography function ``b(x)`` is set inside the initial condition routine
for a particular problem setup. To test the conservative form of the SWE one can set the bottom topography
variable `b` to zero.
@@ -45,16 +51,35 @@ References for the SWE are many but a good introduction is available in Chapter
[DOI: 10.1017/CBO9780511791253](https://doi.org/10.1017/CBO9780511791253)
"""
struct ShallowWaterEquations1D{RealT <: Real} <: AbstractShallowWaterEquations{1, 3}
+ # TODO: TrixiShallowWater: where should the `threshold_limiter` and `threshold_wet` live?
+ # how to "properly" export these constants across the two packages?
gravity::RealT # gravitational constant
H0::RealT # constant "lake-at-rest" total water height
+ # `threshold_limiter` used in `PositivityPreservingLimiterShallowWater` on water height,
+ # as a (small) shift on the initial condition and cutoff before the next time step.
+ # Default is 500*eps() which in double precision is ≈1e-13.
+ threshold_limiter::RealT
+ # `threshold_wet` applied on water height to define when the flow is "wet"
+ # before calculating the numerical flux.
+ # Default is 5*eps() which in double precision is ≈1e-15.
+ threshold_wet::RealT
end
# Allow for flexibility to set the gravitational constant within an elixir depending on the
# application where `gravity_constant=1.0` or `gravity_constant=9.81` are common values.
# The reference total water height H0 defaults to 0.0 but is used for the "lake-at-rest"
-# well-balancedness test cases
-function ShallowWaterEquations1D(; gravity_constant, H0 = 0.0)
- ShallowWaterEquations1D(gravity_constant, H0)
+# well-balancedness test cases.
+# Strict default values for thresholds that performed well in many numerical experiments
+function ShallowWaterEquations1D(; gravity_constant, H0 = zero(gravity_constant),
+ threshold_limiter = nothing, threshold_wet = nothing)
+ T = promote_type(typeof(gravity_constant), typeof(H0))
+ if threshold_limiter === nothing
+ threshold_limiter = 500 * eps(T)
+ end
+ if threshold_wet === nothing
+ threshold_wet = 5 * eps(T)
+ end
+ ShallowWaterEquations1D(gravity_constant, H0, threshold_limiter, threshold_wet)
end
have_nonconservative_terms(::ShallowWaterEquations1D) = True()
@@ -307,6 +332,54 @@ Further details on the hydrostatic reconstruction and its motivation can be foun
z)
end
+# TODO: TrixiShallowWater: move wet/dry specific routine
+"""
+ flux_nonconservative_chen_noelle(u_ll, u_rr,
+ orientation::Integer,
+ equations::ShallowWaterEquations1D)
+
+Non-symmetric two-point surface flux that discretizes the nonconservative (source) term.
+The discretization uses the `hydrostatic_reconstruction_chen_noelle` on the conservative
+variables.
+
+Should be used together with [`FluxHydrostaticReconstruction`](@ref) and
+[`hydrostatic_reconstruction_chen_noelle`](@ref) in the surface flux to ensure consistency.
+
+Further details on the hydrostatic reconstruction and its motivation can be found in
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+@inline function flux_nonconservative_chen_noelle(u_ll, u_rr,
+ orientation::Integer,
+ equations::ShallowWaterEquations1D)
+
+ # Pull the water height and bottom topography on the left
+ h_ll, _, b_ll = u_ll
+ h_rr, _, b_rr = u_rr
+
+ H_ll = h_ll + b_ll
+ H_rr = h_rr + b_rr
+
+ b_star = min(max(b_ll, b_rr), min(H_ll, H_rr))
+
+ # Create the hydrostatic reconstruction for the left solution state
+ u_ll_star, _ = hydrostatic_reconstruction_chen_noelle(u_ll, u_rr, equations)
+
+ # Copy the reconstructed water height for easier to read code
+ h_ll_star = u_ll_star[1]
+
+ z = zero(eltype(u_ll))
+ # Includes two parts:
+ # (i) Diagonal (consistent) term from the volume flux that uses `b_ll` to avoid
+ # cross-averaging across a discontinuous bottom topography
+ # (ii) True surface part that uses `h_ll` and `h_ll_star` to handle discontinuous bathymetry
+ return SVector(z,
+ equations.gravity * h_ll * b_ll -
+ equations.gravity * (h_ll_star + h_ll) * (b_ll - b_star),
+ z)
+end
+
"""
flux_fjordholm_etal(u_ll, u_rr, orientation,
equations::ShallowWaterEquations1D)
@@ -381,7 +454,7 @@ end
A particular type of hydrostatic reconstruction on the water height to guarantee well-balancedness
for a general bottom topography [`ShallowWaterEquations1D`](@ref). The reconstructed solution states
-`u_ll_star` and `u_rr_star` variables are used to evaluate the surface numerical flux at the interface.
+`u_ll_star` and `u_rr_star` variables are then used to evaluate the surface numerical flux at the interface.
Use in combination with the generic numerical flux routine [`FluxHydrostaticReconstruction`](@ref).
Further details on this hydrostatic reconstruction and its motivation can be found in
@@ -410,6 +483,67 @@ Further details on this hydrostatic reconstruction and its motivation can be fou
return u_ll_star, u_rr_star
end
+# TODO: TrixiShallowWater: move wet/dry specific routine
+"""
+ hydrostatic_reconstruction_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations1D)
+
+A particular type of hydrostatic reconstruction of the water height to guarantee well-balancedness
+for a general bottom topography of the [`ShallowWaterEquations1D`](@ref). The reconstructed solution states
+`u_ll_star` and `u_rr_star` variables are used to evaluate the surface numerical flux at the interface.
+The key idea is a linear reconstruction of the bottom and water height at the interfaces using subcells.
+Use in combination with the generic numerical flux routine [`FluxHydrostaticReconstruction`](@ref).
+
+Further details on this hydrostatic reconstruction and its motivation can be found in
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+@inline function hydrostatic_reconstruction_chen_noelle(u_ll, u_rr,
+ equations::ShallowWaterEquations1D)
+ # Unpack left and right water heights and bottom topographies
+ h_ll, _, b_ll = u_ll
+ h_rr, _, b_rr = u_rr
+
+ # Get the velocities on either side
+ v_ll = velocity(u_ll, equations)
+ v_rr = velocity(u_rr, equations)
+
+ H_ll = b_ll + h_ll
+ H_rr = b_rr + h_rr
+
+ b_star = min(max(b_ll, b_rr), min(H_ll, H_rr))
+
+ # Compute the reconstructed water heights
+ h_ll_star = min(H_ll - b_star, h_ll)
+ h_rr_star = min(H_rr - b_star, h_rr)
+
+ # Set the water height to be at least the value stored in the variable threshold after
+ # the hydrostatic reconstruction is applied and before the numerical flux is calculated
+ # to avoid numerical problem with arbitrary small values. Interfaces with a water height
+ # lower or equal to the threshold can be declared as dry.
+ # The default value for `threshold_wet` is ≈ 5*eps(), or 1e-15 in double precision, is set
+ # in the `ShallowWaterEquations1D` struct. This threshold value can be changed in the constructor
+ # call of this equation struct in an elixir.
+ threshold = equations.threshold_wet
+
+ if (h_ll_star <= threshold)
+ h_ll_star = threshold
+ v_ll = zero(v_ll)
+ end
+
+ if (h_rr_star <= threshold)
+ h_rr_star = threshold
+ v_rr = zero(v_rr)
+ end
+
+ # Create the conservative variables using the reconstruted water heights
+ u_ll_star = SVector(h_ll_star, h_ll_star * v_ll, b_ll)
+ u_rr_star = SVector(h_rr_star, h_rr_star * v_rr, b_rr)
+
+ return u_ll_star, u_rr_star
+end
+
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the
# maximum velocity magnitude plus the maximum speed of sound
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
@@ -474,6 +608,39 @@ end
return λ_min, λ_max
end
+# TODO: TrixiShallowWater: move wet/dry specific routine
+"""
+ min_max_speed_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations1D)
+
+The approximated speeds for the HLL type numerical flux used by Chen and Noelle for their
+hydrostatic reconstruction. As they state in the paper, these speeds are chosen for the numerical
+flux to ensure positivity and to satisfy an entropy inequality.
+
+Further details on this hydrostatic reconstruction and its motivation can be found in
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+@inline function min_max_speed_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations1D)
+ # Get the velocity quantities
+ v_ll = velocity(u_ll, equations)
+ v_rr = velocity(u_rr, equations)
+
+ # Calculate the wave celerity on the left and right
+ h_ll = waterheight(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+
+ a_ll = sqrt(equations.gravity * h_ll)
+ a_rr = sqrt(equations.gravity * h_rr)
+
+ λ_min = min(v_ll - a_ll, v_rr - a_rr, zero(eltype(u_ll)))
+ λ_max = max(v_ll + a_ll, v_rr + a_rr, zero(eltype(u_ll)))
+
+ return λ_min, λ_max
+end
+
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
equations::ShallowWaterEquations1D)
@@ -636,9 +803,20 @@ end
end
# Calculate the error for the "lake-at-rest" test case where H = h+b should
-# be a constant value over time
+# be a constant value over time. Note, assumes there is a single reference
+# water height `H0` with which to compare.
+#
+# TODO: TrixiShallowWater: where should `threshold_limiter` live? May need
+# to modify or have different versions of the `lake_at_rest_error` function
@inline function lake_at_rest_error(u, equations::ShallowWaterEquations1D)
h, _, b = u
- return abs(equations.H0 - (h + b))
+
+ # For well-balancedness testing with possible wet/dry regions the reference
+ # water height `H0` accounts for the possibility that the bottom topography
+ # can emerge out of the water as well as for the threshold offset to avoid
+ # division by a "hard" zero water heights as well.
+ H0_wet_dry = max(equations.H0, b + equations.threshold_limiter)
+
+ return abs(H0_wet_dry - (h + b))
end
end # @muladd
diff --git a/src/equations/shallow_water_2d.jl b/src/equations/shallow_water_2d.jl
index 9e227cd4a77..a81fddeed49 100644
--- a/src/equations/shallow_water_2d.jl
+++ b/src/equations/shallow_water_2d.jl
@@ -6,7 +6,7 @@
#! format: noindent
@doc raw"""
- ShallowWaterEquations2D(gravity, H0)
+ ShallowWaterEquations2D(; gravity, H0 = 0, threshold_limiter = nothing, threshold_wet = nothing)
Shallow water equations (SWE) in two space dimensions. The equations are given by
```math
@@ -27,6 +27,12 @@ also defines the total water height as ``H = h + b``.
The additional quantity ``H_0`` is also available to store a reference value for the total water height that
is useful to set initial conditions or test the "lake-at-rest" well-balancedness.
+Also, there are two thresholds which prevent numerical problems as well as instabilities. Both of them do not
+have to be passed, as default values are defined within the struct. The first one, `threshold_limiter`, is
+used in [`PositivityPreservingLimiterShallowWater`](@ref) on the water height, as a (small) shift on the initial
+condition and cutoff before the next time step. The second one, `threshold_wet`, is applied on the water height to
+define when the flow is "wet" before calculating the numerical flux.
+
The bottom topography function ``b(x,y)`` is set inside the initial condition routine
for a particular problem setup. To test the conservative form of the SWE one can set the bottom topography
variable `b` to zero.
@@ -48,16 +54,35 @@ References for the SWE are many but a good introduction is available in Chapter
[DOI: 10.1017/CBO9780511791253](https://doi.org/10.1017/CBO9780511791253)
"""
struct ShallowWaterEquations2D{RealT <: Real} <: AbstractShallowWaterEquations{2, 4}
+ # TODO: TrixiShallowWater: where should the `threshold_limiter` and `threshold_wet` live?
+ # how to "properly" export these constants across the two packages?
gravity::RealT # gravitational constant
H0::RealT # constant "lake-at-rest" total water height
+ # `threshold_limiter` used in `PositivityPreservingLimiterShallowWater` on water height,
+ # as a (small) shift on the initial condition and cutoff before the next time step.
+ # Default is 500*eps() which in double precision is ≈1e-13.
+ threshold_limiter::RealT
+ # `threshold_wet` applied on water height to define when the flow is "wet"
+ # before calculating the numerical flux.
+ # Default is 5*eps() which in double precision is ≈1e-15.
+ threshold_wet::RealT
end
# Allow for flexibility to set the gravitational constant within an elixir depending on the
# application where `gravity_constant=1.0` or `gravity_constant=9.81` are common values.
# The reference total water height H0 defaults to 0.0 but is used for the "lake-at-rest"
-# well-balancedness test cases
-function ShallowWaterEquations2D(; gravity_constant, H0 = 0.0)
- ShallowWaterEquations2D(gravity_constant, H0)
+# well-balancedness test cases.
+# Strict default values for thresholds that performed well in many numerical experiments
+function ShallowWaterEquations2D(; gravity_constant, H0 = zero(gravity_constant),
+ threshold_limiter = nothing, threshold_wet = nothing)
+ T = promote_type(typeof(gravity_constant), typeof(H0))
+ if threshold_limiter === nothing
+ threshold_limiter = 500 * eps(T)
+ end
+ if threshold_wet === nothing
+ threshold_wet = 5 * eps(T)
+ end
+ ShallowWaterEquations2D(gravity_constant, H0, threshold_limiter, threshold_wet)
end
have_nonconservative_terms(::ShallowWaterEquations2D) = True()
@@ -431,6 +456,69 @@ Further details for the hydrostatic reconstruction and its motivation can be fou
return u_ll_star, u_rr_star
end
+# TODO: TrixiShallowWater: move wet/dry specific routine
+"""
+ hydrostatic_reconstruction_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+
+A particular type of hydrostatic reconstruction of the water height to guarantee well-balancedness
+for a general bottom topography of the [`ShallowWaterEquations2D`](@ref). The reconstructed solution states
+`u_ll_star` and `u_rr_star` variables are then used to evaluate the surface numerical flux at the interface.
+The key idea is a linear reconstruction of the bottom and water height at the interfaces using subcells.
+Use in combination with the generic numerical flux routine [`FluxHydrostaticReconstruction`](@ref).
+
+Further details on this hydrostatic reconstruction and its motivation can be found in
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+@inline function hydrostatic_reconstruction_chen_noelle(u_ll, u_rr,
+ equations::ShallowWaterEquations2D)
+ # Unpack left and right water heights and bottom topographies
+ h_ll, _, _, b_ll = u_ll
+ h_rr, _, _, b_rr = u_rr
+
+ # Get the velocities on either side
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ H_ll = b_ll + h_ll
+ H_rr = b_rr + h_rr
+
+ b_star = min(max(b_ll, b_rr), min(H_ll, H_rr))
+
+ # Compute the reconstructed water heights
+ h_ll_star = min(H_ll - b_star, h_ll)
+ h_rr_star = min(H_rr - b_star, h_rr)
+
+ # Set the water height to be at least the value stored in the variable threshold after
+ # the hydrostatic reconstruction is applied and before the numerical flux is calculated
+ # to avoid numerical problem with arbitrary small values. Interfaces with a water height
+ # lower or equal to the threshold can be declared as dry.
+ # The default value for `threshold_wet` is ≈5*eps(), or 1e-15 in double precision, is set
+ # in the `ShallowWaterEquations2D` struct. This threshold value can be changed in the constructor
+ # call of this equation struct in an elixir.
+ threshold = equations.threshold_wet
+
+ if (h_ll_star <= threshold)
+ h_ll_star = threshold
+ v1_ll = zero(v1_ll)
+ v2_ll = zero(v2_ll)
+ end
+
+ if (h_rr_star <= threshold)
+ h_rr_star = threshold
+ v1_rr = zero(v1_rr)
+ v2_rr = zero(v2_rr)
+ end
+
+ # Create the conservative variables using the reconstruted water heights
+ u_ll_star = SVector(h_ll_star, h_ll_star * v1_ll, h_ll_star * v2_ll, b_ll)
+ u_rr_star = SVector(h_rr_star, h_rr_star * v1_rr, h_rr_star * v2_rr, b_rr)
+
+ return u_ll_star, u_rr_star
+end
+
"""
flux_nonconservative_audusse_etal(u_ll, u_rr, orientation::Integer,
equations::ShallowWaterEquations2D)
@@ -516,6 +604,104 @@ end
return SVector(f1, f2, f3, f4)
end
+# TODO: TrixiShallowWater: move wet/dry specific routine
+"""
+ flux_nonconservative_chen_noelle(u_ll, u_rr,
+ orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ flux_nonconservative_chen_noelle(u_ll, u_rr,
+ normal_direction_ll ::AbstractVector,
+ normal_direction_average ::AbstractVector,
+ equations::ShallowWaterEquations2D)
+
+Non-symmetric two-point surface flux that discretizes the nonconservative (source) term.
+The discretization uses the [`hydrostatic_reconstruction_chen_noelle`](@ref) on the conservative
+variables.
+
+Should be used together with [`FluxHydrostaticReconstruction`](@ref) and
+[`hydrostatic_reconstruction_chen_noelle`](@ref) in the surface flux to ensure consistency.
+
+Further details on the hydrostatic reconstruction and its motivation can be found in
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+@inline function flux_nonconservative_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ # Pull the water height and bottom topography on the left
+ h_ll, _, _, b_ll = u_ll
+ h_rr, _, _, b_rr = u_rr
+
+ H_ll = h_ll + b_ll
+ H_rr = h_rr + b_rr
+
+ b_star = min(max(b_ll, b_rr), min(H_ll, H_rr))
+
+ # Create the hydrostatic reconstruction for the left solution state
+ u_ll_star, _ = hydrostatic_reconstruction_chen_noelle(u_ll, u_rr, equations)
+
+ # Copy the reconstructed water height for easier to read code
+ h_ll_star = u_ll_star[1]
+
+ z = zero(eltype(u_ll))
+ # Includes two parts:
+ # (i) Diagonal (consistent) term from the volume flux that uses `b_ll` to avoid
+ # cross-averaging across a discontinuous bottom topography
+ # (ii) True surface part that uses `h_ll` and `h_ll_star` to handle discontinuous bathymetry
+ g = equations.gravity
+ if orientation == 1
+ f = SVector(z,
+ g * h_ll * b_ll - g * (h_ll_star + h_ll) * (b_ll - b_star),
+ z, z)
+ else # orientation == 2
+ f = SVector(z, z,
+ g * h_ll * b_ll - g * (h_ll_star + h_ll) * (b_ll - b_star),
+ z)
+ end
+
+ return f
+end
+
+@inline function flux_nonconservative_chen_noelle(u_ll, u_rr,
+ normal_direction_ll::AbstractVector,
+ normal_direction_average::AbstractVector,
+ equations::ShallowWaterEquations2D)
+ # Pull the water height and bottom topography on the left
+ h_ll, _, _, b_ll = u_ll
+ h_rr, _, _, b_rr = u_rr
+
+ H_ll = h_ll + b_ll
+ H_rr = h_rr + b_rr
+
+ b_star = min(max(b_ll, b_rr), min(H_ll, H_rr))
+
+ # Create the hydrostatic reconstruction for the left solution state
+ u_ll_star, _ = hydrostatic_reconstruction_chen_noelle(u_ll, u_rr, equations)
+
+ # Copy the reconstructed water height for easier to read code
+ h_ll_star = u_ll_star[1]
+
+ # Comes in two parts:
+ # (i) Diagonal (consistent) term from the volume flux that uses `normal_direction_average`
+ # but we use `b_ll` to avoid cross-averaging across a discontinuous bottom topography
+
+ f2 = normal_direction_average[1] * equations.gravity * h_ll * b_ll
+ f3 = normal_direction_average[2] * equations.gravity * h_ll * b_ll
+
+ # (ii) True surface part that uses `normal_direction_ll`, `h_ll` and `h_ll_star`
+ # to handle discontinuous bathymetry
+
+ f2 -= normal_direction_ll[1] * equations.gravity * (h_ll_star + h_ll) *
+ (b_ll - b_star)
+ f3 -= normal_direction_ll[2] * equations.gravity * (h_ll_star + h_ll) *
+ (b_ll - b_star)
+
+ # First and last equations do not have a nonconservative flux
+ f1 = f4 = zero(eltype(u_ll))
+
+ return SVector(f1, f2, f3, f4)
+end
+
"""
flux_fjordholm_etal(u_ll, u_rr, orientation_or_normal_direction,
equations::ShallowWaterEquations2D)
@@ -762,6 +948,67 @@ end
return λ_min, λ_max
end
+# TODO: TrixiShallowWater: move wet/dry specific routine
+"""
+ min_max_speed_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ min_max_speed_chen_noelle(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::ShallowWaterEquations2D)
+
+Special estimate of the minimal and maximal wave speed of the shallow water equations for
+the left and right states `u_ll, u_rr`. These approximate speeds are used for the HLL-type
+numerical flux [`flux_hll_chen_noelle`](@ref). These wave speed estimates
+together with a particular hydrostatic reconstruction technique guarantee
+that the numerical flux is positive and satisfies an entropy inequality.
+
+Further details on this hydrostatic reconstruction and its motivation can be found in
+the reference below. The definition of the wave speeds are given in Equation (2.20).
+- Guoxian Chen and Sebastian Noelle (2017)
+ A new hydrostatic reconstruction scheme based on subcell reconstructions
+ [DOI:10.1137/15M1053074](https://dx.doi.org/10.1137/15M1053074)
+"""
+@inline function min_max_speed_chen_noelle(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ a_ll = sqrt(equations.gravity * h_ll)
+ a_rr = sqrt(equations.gravity * h_rr)
+
+ if orientation == 1 # x-direction
+ λ_min = min(v1_ll - a_ll, v1_rr - a_rr, zero(eltype(u_ll)))
+ λ_max = max(v1_ll + a_ll, v1_rr + a_rr, zero(eltype(u_ll)))
+ else # y-direction
+ λ_min = min(v2_ll - a_ll, v2_rr - a_rr, zero(eltype(u_ll)))
+ λ_max = max(v2_ll + a_ll, v2_rr + a_rr, zero(eltype(u_ll)))
+ end
+
+ return λ_min, λ_max
+end
+
+@inline function min_max_speed_chen_noelle(u_ll, u_rr, normal_direction::AbstractVector,
+ equations::ShallowWaterEquations2D)
+ h_ll = waterheight(u_ll, equations)
+ v1_ll, v2_ll = velocity(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ v1_rr, v2_rr = velocity(u_rr, equations)
+
+ v_normal_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
+ v_normal_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]
+
+ norm_ = norm(normal_direction)
+
+ a_ll = sqrt(equations.gravity * h_ll) * norm_
+ a_rr = sqrt(equations.gravity * h_rr) * norm_
+
+ λ_min = min(v_normal_ll - a_ll, v_normal_rr - a_rr, zero(eltype(u_ll)))
+ λ_max = max(v_normal_ll + a_ll, v_normal_rr + a_rr, zero(eltype(u_ll)))
+
+ return λ_min, λ_max
+end
+
# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes
@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,
equations::ShallowWaterEquations2D)
@@ -1008,9 +1255,20 @@ end
end
# Calculate the error for the "lake-at-rest" test case where H = h+b should
-# be a constant value over time
+# be a constant value over time. Note, assumes there is a single reference
+# water height `H0` with which to compare.
+#
+# TODO: TrixiShallowWater: where should `threshold_limiter` live? May need
+# to modify or have different versions of the `lake_at_rest_error` function
@inline function lake_at_rest_error(u, equations::ShallowWaterEquations2D)
h, _, _, b = u
- return abs(equations.H0 - (h + b))
+
+ # For well-balancedness testing with possible wet/dry regions the reference
+ # water height `H0` accounts for the possibility that the bottom topography
+ # can emerge out of the water as well as for the threshold offset to avoid
+ # division by a "hard" zero water heights as well.
+ H0_wet_dry = max(equations.H0, b + equations.threshold_limiter)
+
+ return abs(H0_wet_dry - (h + b))
end
end # @muladd
diff --git a/src/equations/shallow_water_two_layer_1d.jl b/src/equations/shallow_water_two_layer_1d.jl
index e126eec7c25..4b64481cca3 100644
--- a/src/equations/shallow_water_two_layer_1d.jl
+++ b/src/equations/shallow_water_two_layer_1d.jl
@@ -5,6 +5,8 @@
@muladd begin
#! format: noindent
+# TODO: TrixiShallowWater: 1D two layer equations should move to new package
+
@doc raw"""
ShallowWaterTwoLayerEquations1D(gravity, H0, rho_upper, rho_lower)
diff --git a/src/equations/shallow_water_two_layer_2d.jl b/src/equations/shallow_water_two_layer_2d.jl
index a54831c711f..87249e91948 100644
--- a/src/equations/shallow_water_two_layer_2d.jl
+++ b/src/equations/shallow_water_two_layer_2d.jl
@@ -5,48 +5,50 @@
@muladd begin
#! format: noindent
+# TODO: TrixiShallowWater: 2D two layer equations should move to new package
+
@doc raw"""
ShallowWaterTwoLayerEquations2D(gravity, H0, rho_upper, rho_lower)
Two-Layer Shallow water equations (2LSWE) in two space dimension. The equations are given by
```math
\begin{alignat*}{8}
-&\frac{\partial}{\partial t}h_{upper}
+&\frac{\partial}{\partial t}h_{upper}
&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper}\right)
-&&+ \frac{\partial}{\partial y}\left(h_{upper} v_{2,upper}\right) \quad
+&&+ \frac{\partial}{\partial y}\left(h_{upper} v_{2,upper}\right) \quad
&&= \quad 0 \\
-&\frac{\partial}{\partial t}\left(h_{upper} v_{1,upper}\right)
-&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper}^2 + \frac{gh_{upper}^2}{2}\right)
-&&+ \frac{\partial}{\partial y}\left(h_{upper} v_{1,upper} v_{2,upper}\right) \quad
+&\frac{\partial}{\partial t}\left(h_{upper} v_{1,upper}\right)
+&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper}^2 + \frac{gh_{upper}^2}{2}\right)
+&&+ \frac{\partial}{\partial y}\left(h_{upper} v_{1,upper} v_{2,upper}\right) \quad
&&= -gh_{upper}\frac{\partial}{\partial x}\left(b+h_{lower}\right) \\
-&\frac{\partial}{\partial t}\left(h_{upper} v_{2,upper}\right)
-&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper} v_{2,upper}\right)
-&&+ \frac{\partial}{\partial y}\left(h_{upper} v_{2,upper}^2 + \frac{gh_{upper}^2}{2}\right)
+&\frac{\partial}{\partial t}\left(h_{upper} v_{2,upper}\right)
+&&+ \frac{\partial}{\partial x}\left(h_{upper} v_{1,upper} v_{2,upper}\right)
+&&+ \frac{\partial}{\partial y}\left(h_{upper} v_{2,upper}^2 + \frac{gh_{upper}^2}{2}\right)
&&= -gh_{upper}\frac{\partial}{\partial y}\left(b+h_{lower}\right)\\
-&\frac{\partial}{\partial t}h_{lower}
-&&+ \frac{\partial}{\partial x}\left(h_{lower} v_{1,lower}\right)
-&&+ \frac{\partial}{\partial y}\left(h_{lower} v_{2,lower}\right)
+&\frac{\partial}{\partial t}h_{lower}
+&&+ \frac{\partial}{\partial x}\left(h_{lower} v_{1,lower}\right)
+&&+ \frac{\partial}{\partial y}\left(h_{lower} v_{2,lower}\right)
&&= \quad 0 \\
-&\frac{\partial}{\partial t}\left(h_{lower} v_{1,lower}\right)
-&&+ \frac{\partial}{\partial x}\left(h_{lower} v_{1,lower}^2 + \frac{gh_{lower}^2}{2}\right)
-&&+ \frac{\partial}{\partial y}\left(h_{lower} v_{1,lower} v_{2,lower}\right)
+&\frac{\partial}{\partial t}\left(h_{lower} v_{1,lower}\right)
+&&+ \frac{\partial}{\partial x}\left(h_{lower} v_{1,lower}^2 + \frac{gh_{lower}^2}{2}\right)
+&&+ \frac{\partial}{\partial y}\left(h_{lower} v_{1,lower} v_{2,lower}\right)
&&= -gh_{lower}\frac{\partial}{\partial x}\left(b+\frac{\rho_{upper}}{\rho_{lower}} h_{upper}\right)\\
-&\frac{\partial}{\partial t}\left(h_{lower} v_{2,lower}\right)
-&&+ \frac{\partial}{\partial x}\left(h_{lower} v_{1,lower} v_{2,lower}\right)
-&&+ \frac{\partial}{\partial y}\left(h_{lower} v_{2,lower}^2 + \frac{gh_{lower}^2}{2}\right)
+&\frac{\partial}{\partial t}\left(h_{lower} v_{2,lower}\right)
+&&+ \frac{\partial}{\partial x}\left(h_{lower} v_{1,lower} v_{2,lower}\right)
+&&+ \frac{\partial}{\partial y}\left(h_{lower} v_{2,lower}^2 + \frac{gh_{lower}^2}{2}\right)
&&= -gh_{lower}\frac{\partial}{\partial y}\left(b+\frac{\rho_{upper}}{\rho_{lower}} h_{upper}\right)
\end{alignat*}
```
-The unknown quantities of the 2LSWE are the water heights of the lower layer ``h_{lower}`` and the
-upper
+The unknown quantities of the 2LSWE are the water heights of the lower layer ``h_{lower}`` and the
+upper
layer ``h_{upper}`` and the respective velocities in x-direction ``v_{1,lower}`` and ``v_{1,upper}`` and in y-direction
-``v_{2,lower}`` and ``v_{2,upper}``. The gravitational constant is denoted by `g`, the layer densitites by
-``\rho_{upper}``and ``\rho_{lower}`` and the (possibly) variable bottom topography function by ``b(x)``.
-Conservative variable water height ``h_{lower}`` is measured from the bottom topography ``b`` and ``h_{upper}``
-relative to ``h_{lower}``, therefore one also defines the total water heights as ``H_{lower} = h_{lower} + b`` and
+``v_{2,lower}`` and ``v_{2,upper}``. The gravitational constant is denoted by `g`, the layer densitites by
+``\rho_{upper}``and ``\rho_{lower}`` and the (possibly) variable bottom topography function by ``b(x)``.
+Conservative variable water height ``h_{lower}`` is measured from the bottom topography ``b`` and ``h_{upper}``
+relative to ``h_{lower}``, therefore one also defines the total water heights as ``H_{lower} = h_{lower} + b`` and
``H_{upper} = h_{upper} + h_{lower} + b``.
-The densities must be chosen such that ``\rho_{upper} < \rho_{lower}``, to make sure that the heavier fluid
+The densities must be chosen such that ``\rho_{upper} < \rho_{lower}``, to make sure that the heavier fluid
``\rho_{lower}`` is in the bottom layer and the lighter fluid ``\rho_{upper}`` in the upper layer.
The additional quantity ``H_0`` is also available to store a reference value for the total water
@@ -55,13 +57,13 @@ height that is useful to set initial conditions or test the "lake-at-rest" well-
The bottom topography function ``b(x)`` is set inside the initial condition routine
for a particular problem setup.
-In addition to the unknowns, Trixi currently stores the bottom topography values at the
-approximation points despite being fixed in time. This is done for convenience of computing the
-bottom topography gradients on the fly during the approximation as well as computing auxiliary
+In addition to the unknowns, Trixi currently stores the bottom topography values at the
+approximation points despite being fixed in time. This is done for convenience of computing the
+bottom topography gradients on the fly during the approximation as well as computing auxiliary
quantities like the total water height ``H`` or the entropy variables.
This affects the implementation and use of these equations in various ways:
* The flux values corresponding to the bottom topography must be zero.
-* The bottom topography values must be included when defining initial conditions, boundary
+* The bottom topography values must be included when defining initial conditions, boundary
conditions or source terms.
* [`AnalysisCallback`](@ref) analyzes this variable.
* Trixi's visualization tools will visualize the bottom topography by default.
@@ -113,7 +115,7 @@ end
initial_condition_convergence_test(x, t, equations::ShallowWaterTwoLayerEquations2D)
A smooth initial condition used for convergence tests in combination with
-[`source_terms_convergence_test`](@ref). Constants must be set to ``rho_{upper} = 0.9``,
+[`source_terms_convergence_test`](@ref). Constants must be set to ``rho_{upper} = 0.9``,
``rho_{lower} = 1.0``, ``g = 10.0``.
"""
function initial_condition_convergence_test(x, t,
@@ -141,7 +143,7 @@ Source terms used for convergence tests in combination with
"""
@inline function source_terms_convergence_test(u, x, t,
equations::ShallowWaterTwoLayerEquations2D)
- # Same settings as in `initial_condition_convergence_test`.
+ # Same settings as in `initial_condition_convergence_test`.
# some constants are chosen such that the function is periodic on the domain [0,sqrt(2)]^2]
ω = 2.0 * pi * sqrt(2.0)
@@ -325,7 +327,7 @@ end
Non-symmetric two-point volume flux discretizing the nonconservative (source) term
that contains the gradient of the bottom topography [`ShallowWaterTwoLayerEquations2D`](@ref) and an
-additional term that couples the momentum of both layers. This is a slightly modified version
+additional term that couples the momentum of both layers. This is a slightly modified version
to account for the additional source term compared to the standard SWE described in the paper.
Further details are available in the paper:
@@ -345,7 +347,7 @@ Further details are available in the paper:
z = zero(eltype(u_ll))
# Bottom gradient nonconservative term: (0, g*h_upper*(b + h_lower)_x, g*h_upper*(b + h_lower)_y ,
- # 0, g*h_lower*(b + r*h_upper)_x,
+ # 0, g*h_lower*(b + r*h_upper)_x,
# g*h_lower*(b + r*h_upper)_y, 0)
if orientation == 1
f = SVector(z,
@@ -397,8 +399,8 @@ end
!!! warning "Experimental code"
This numerical flux is experimental and may change in any future release.
-Non-symmetric two-point surface flux discretizing the nonconservative (source) term that contains
-the gradients of the bottom topography and an additional term that couples the momentum of both
+Non-symmetric two-point surface flux discretizing the nonconservative (source) term that contains
+the gradients of the bottom topography and an additional term that couples the momentum of both
layers [`ShallowWaterTwoLayerEquations2D`](@ref).
Further details are available in the paper:
@@ -506,13 +508,13 @@ end
flux_fjordholm_etal(u_ll, u_rr, orientation,
equations::ShallowWaterTwoLayerEquations2D)
-Total energy conservative (mathematical entropy for two-layer shallow water equations). When the
-bottom topography is nonzero this should only be used as a surface flux otherwise the scheme will
+Total energy conservative (mathematical entropy for two-layer shallow water equations). When the
+bottom topography is nonzero this should only be used as a surface flux otherwise the scheme will
not be well-balanced. For well-balancedness in the volume flux use [`flux_wintermeyer_etal`](@ref).
Details are available in Eq. (4.1) in the paper:
- Ulrik S. Fjordholm, Siddhartha Mishra and Eitan Tadmor (2011)
- Well-balanced and energy stable schemes for the shallow water equations with discontinuous
+ Well-balanced and energy stable schemes for the shallow water equations with discontinuous
topography [DOI: 10.1016/j.jcp.2011.03.042](https://doi.org/10.1016/j.jcp.2011.03.042)
and the application to two layers is shown in the paper:
- Ulrik Skre Fjordholm (2012)
@@ -606,11 +608,11 @@ end
"""
flux_wintermeyer_etal(u_ll, u_rr, orientation,
equations::ShallowWaterTwoLayerEquations2D)
-
+
Total energy conservative (mathematical entropy for two-layer shallow water equations) split form.
When the bottom topography is nonzero this scheme will be well-balanced when used as a `volume_flux`.
The `surface_flux` should still use, e.g., [`flux_fjordholm_etal`](@ref). To obtain the flux for the
-two-layer shallow water equations the flux that is described in the paper for the normal shallow
+two-layer shallow water equations the flux that is described in the paper for the normal shallow
water equations is used within each layer.
Further details are available in Theorem 1 of the paper:
@@ -696,9 +698,9 @@ end
flux_es_fjordholm_etal(u_ll, u_rr, orientation_or_normal_direction,
equations::ShallowWaterTwoLayerEquations1D)
-Entropy stable surface flux for the two-layer shallow water equations. Uses the entropy conservative
+Entropy stable surface flux for the two-layer shallow water equations. Uses the entropy conservative
[`flux_fjordholm_etal`](@ref) and adds a Lax-Friedrichs type dissipation dependent on the jump of entropy
-variables.
+variables.
Further details are available in the paper:
- Ulrik Skre Fjordholm (2012)
@@ -723,7 +725,7 @@ formulation.
q_rr = cons2entropy(u_rr, equations)
q_ll = cons2entropy(u_ll, equations)
- # Average values from left and right
+ # Average values from left and right
u_avg = (u_ll + u_rr) / 2
# Introduce variables for better readability
@@ -791,10 +793,10 @@ formulation.
end
# Calculate approximation for maximum wave speed for local Lax-Friedrichs-type dissipation as the
-# maximum velocity magnitude plus the maximum speed of sound. This function uses approximate
-# eigenvalues using the speed of the barotropic mode as there is no simple way to calculate them
-# analytically.
-#
+# maximum velocity magnitude plus the maximum speed of sound. This function uses approximate
+# eigenvalues using the speed of the barotropic mode as there is no simple way to calculate them
+# analytically.
+#
# A good overview of the derivation is given in:
# - Jonas Nycander, Andrew McC. Hogg, Leela M. Frankcombe (2008)
# Open boundary conditions for nonlinear channel Flows
@@ -914,7 +916,7 @@ end
# Convert conservative variables to entropy variables
# Note, only the first four are the entropy variables, the fifth entry still just carries the bottom
-# topography values for convenience.
+# topography values for convenience.
# In contrast to general usage the entropy variables are denoted with q instead of w, because w is
# already used for velocity in y-Direction
@inline function cons2entropy(u, equations::ShallowWaterTwoLayerEquations2D)
diff --git a/src/solvers/dgsem_tree/indicators.jl b/src/solvers/dgsem_tree/indicators.jl
index b8f8a796f2b..4b83e9c1a9e 100644
--- a/src/solvers/dgsem_tree/indicators.jl
+++ b/src/solvers/dgsem_tree/indicators.jl
@@ -92,6 +92,77 @@ end
function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorHennemannGassner)
@nospecialize indicator # reduce precompilation time
+ setup = [
+ "indicator variable" => indicator.variable,
+ "max. α" => indicator.alpha_max,
+ "min. α" => indicator.alpha_min,
+ "smooth α" => (indicator.alpha_smooth ? "yes" : "no"),
+ ]
+ summary_box(io, "IndicatorHennemannGassner", setup)
+end
+
+# TODO: TrixiShallowWater: move the new indicator and all associated routines to the new package
+"""
+ IndicatorHennemannGassnerShallowWater(equations::AbstractEquations, basis;
+ alpha_max=0.5,
+ alpha_min=0.001,
+ alpha_smooth=true,
+ variable)
+
+Modified version of the [`IndicatorHennemannGassner`](@ref)
+indicator used for shock-capturing for shallow water equations. After
+the element-wise values for the blending factors are computed an additional check
+is made to see if the element is partially wet. In this case, partially wet elements
+are set to use the pure finite volume scheme that is guaranteed to be well-balanced
+for this wet/dry transition state of the flow regime.
+
+See also [`VolumeIntegralShockCapturingHG`](@ref).
+
+## References
+
+- Hennemann, Gassner (2020)
+ "A provably entropy stable subcell shock capturing approach for high order split form DG"
+ [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
+"""
+struct IndicatorHennemannGassnerShallowWater{RealT <: Real, Variable, Cache} <:
+ AbstractIndicator
+ alpha_max::RealT
+ alpha_min::RealT
+ alpha_smooth::Bool
+ variable::Variable
+ cache::Cache
+end
+
+# this method is used when the indicator is constructed as for shock-capturing volume integrals
+# of the shallow water equations
+# It modifies the shock-capturing indicator to use full FV method in dry cells
+function IndicatorHennemannGassnerShallowWater(equations::AbstractShallowWaterEquations,
+ basis;
+ alpha_max = 0.5,
+ alpha_min = 0.001,
+ alpha_smooth = true,
+ variable)
+ alpha_max, alpha_min = promote(alpha_max, alpha_min)
+ cache = create_cache(IndicatorHennemannGassner, equations, basis)
+ IndicatorHennemannGassnerShallowWater{typeof(alpha_max), typeof(variable),
+ typeof(cache)}(alpha_max, alpha_min,
+ alpha_smooth, variable, cache)
+end
+
+function Base.show(io::IO, indicator::IndicatorHennemannGassnerShallowWater)
+ @nospecialize indicator # reduce precompilation time
+
+ print(io, "IndicatorHennemannGassnerShallowWater(")
+ print(io, indicator.variable)
+ print(io, ", alpha_max=", indicator.alpha_max)
+ print(io, ", alpha_min=", indicator.alpha_min)
+ print(io, ", alpha_smooth=", indicator.alpha_smooth)
+ print(io, ")")
+end
+
+function Base.show(io::IO, ::MIME"text/plain",
+ indicator::IndicatorHennemannGassnerShallowWater)
+ @nospecialize indicator # reduce precompilation time
if get(io, :compact, false)
show(io, indicator)
@@ -102,7 +173,7 @@ function Base.show(io::IO, ::MIME"text/plain", indicator::IndicatorHennemannGass
"min. α" => indicator.alpha_min,
"smooth α" => (indicator.alpha_smooth ? "yes" : "no"),
]
- summary_box(io, "IndicatorHennemannGassner", setup)
+ summary_box(io, "IndicatorHennemannGassnerShallowWater", setup)
end
end
diff --git a/src/solvers/dgsem_tree/indicators_1d.jl b/src/solvers/dgsem_tree/indicators_1d.jl
index e722584bb2e..8b57348861c 100644
--- a/src/solvers/dgsem_tree/indicators_1d.jl
+++ b/src/solvers/dgsem_tree/indicators_1d.jl
@@ -24,6 +24,115 @@ function create_cache(typ::Type{IndicatorHennemannGassner}, mesh,
create_cache(typ, equations, dg.basis)
end
+# Modified indicator for ShallowWaterEquations1D to apply full FV method on cells
+# containing some "dry" LGL nodes. That is, if an element is partially "wet" then it becomes a
+# full FV element.
+#
+# TODO: TrixiShallowWater: move new indicator type
+function (indicator_hg::IndicatorHennemannGassnerShallowWater)(u::AbstractArray{<:Any, 3
+ },
+ mesh,
+ equations::ShallowWaterEquations1D,
+ dg::DGSEM, cache;
+ kwargs...)
+ @unpack alpha_max, alpha_min, alpha_smooth, variable = indicator_hg
+ @unpack alpha, alpha_tmp, indicator_threaded, modal_threaded = indicator_hg.cache
+ # TODO: Taal refactor, when to `resize!` stuff changed possibly by AMR?
+ # Shall we implement `resize!(semi::AbstractSemidiscretization, new_size)`
+ # or just `resize!` whenever we call the relevant methods as we do now?
+ resize!(alpha, nelements(dg, cache))
+ if alpha_smooth
+ resize!(alpha_tmp, nelements(dg, cache))
+ end
+
+ # magic parameters
+ threshold = 0.5 * 10^(-1.8 * (nnodes(dg))^0.25)
+ parameter_s = log((1 - 0.0001) / 0.0001)
+
+ # If the water height `h` at one LGL node is lower than `threshold_partially_wet`
+ # the indicator sets the element-wise blending factor alpha[element] = 1
+ # via the local variable `indicator_wet`. In turn, this ensures that a pure
+ # FV method is used in partially wet cells and guarantees the well-balanced property.
+ #
+ # Hard-coded cut-off value of `threshold_partially_wet = 1e-4` was determined through many numerical experiments.
+ # Overall idea is to increase robustness when computing the velocity on (nearly) dry cells which
+ # could be "dangerous" due to division of conservative variables, e.g., v = hv / h.
+ # Here, the impact of the threshold on the number of cells being updated with FV is not that
+ # significant. However, its impact on the robustness is very significant.
+ # The value can be seen as a trade-off between accuracy and stability.
+ # Well-balancedness of the scheme on partially wet cells with hydrostatic reconstruction
+ # can only be proven for the FV method (see Chen and Noelle).
+ # Therefore we set alpha to one regardless of its given maximum value.
+ threshold_partially_wet = 1e-4
+
+ @threaded for element in eachelement(dg, cache)
+ indicator = indicator_threaded[Threads.threadid()]
+ modal = modal_threaded[Threads.threadid()]
+
+ # (Re-)set dummy variable for alpha_dry
+ indicator_wet = 1
+
+ # Calculate indicator variables at Gauss-Lobatto nodes
+ for i in eachnode(dg)
+ u_local = get_node_vars(u, equations, dg, i, element)
+ h, _, _ = u_local
+
+ if h <= threshold_partially_wet
+ indicator_wet = 0
+ end
+
+ indicator[i] = indicator_hg.variable(u_local, equations)
+ end
+
+ # Convert to modal representation
+ multiply_scalar_dimensionwise!(modal, dg.basis.inverse_vandermonde_legendre,
+ indicator)
+
+ # Calculate total energies for all modes, without highest, without two highest
+ total_energy = zero(eltype(modal))
+ for i in 1:nnodes(dg)
+ total_energy += modal[i]^2
+ end
+ total_energy_clip1 = zero(eltype(modal))
+ for i in 1:(nnodes(dg) - 1)
+ total_energy_clip1 += modal[i]^2
+ end
+ total_energy_clip2 = zero(eltype(modal))
+ for i in 1:(nnodes(dg) - 2)
+ total_energy_clip2 += modal[i]^2
+ end
+
+ # Calculate energy in higher modes
+ energy = max((total_energy - total_energy_clip1) / total_energy,
+ (total_energy_clip1 - total_energy_clip2) / total_energy_clip1)
+
+ alpha_element = 1 / (1 + exp(-parameter_s / threshold * (energy - threshold)))
+
+ # Take care of the case close to pure DG
+ if alpha_element < alpha_min
+ alpha_element = zero(alpha_element)
+ end
+
+ # Take care of the case close to pure FV
+ if alpha_element > 1 - alpha_min
+ alpha_element = one(alpha_element)
+ end
+
+ # Clip the maximum amount of FV allowed or set to one depending on indicator_wet
+ if indicator_wet == 0
+ alpha[element] = 1
+ else # Element is not defined as dry but wet
+ alpha[element] = min(alpha_max, alpha_element)
+ end
+ end
+
+ if alpha_smooth
+ apply_smoothing!(mesh, alpha, alpha_tmp, dg, cache)
+ end
+
+ return alpha
+end
+
# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl
# with @batch (@threaded).
# Otherwise, @threaded does not work here with Julia ARM on macOS.
diff --git a/src/solvers/dgsem_tree/indicators_2d.jl b/src/solvers/dgsem_tree/indicators_2d.jl
index 085cb71ad0c..f7c78547174 100644
--- a/src/solvers/dgsem_tree/indicators_2d.jl
+++ b/src/solvers/dgsem_tree/indicators_2d.jl
@@ -28,6 +28,116 @@ function create_cache(typ::Type{IndicatorHennemannGassner}, mesh,
create_cache(typ, equations, dg.basis)
end
+# Modified indicator for ShallowWaterEquations2D to apply full FV method on cells
+# containing some "dry" LGL nodes. That is, if an element is partially "wet" then it becomes a
+# full FV element.
+#
+# TODO: TrixiShallowWater: move new indicator type
+function (indicator_hg::IndicatorHennemannGassnerShallowWater)(u::AbstractArray{<:Any, 4
+ },
+ mesh,
+ equations::ShallowWaterEquations2D,
+ dg::DGSEM, cache;
+ kwargs...)
+ @unpack alpha_max, alpha_min, alpha_smooth, variable = indicator_hg
+ @unpack alpha, alpha_tmp, indicator_threaded, modal_threaded, modal_tmp1_threaded = indicator_hg.cache
+ # TODO: Taal refactor, when to `resize!` stuff changed possibly by AMR?
+ # Shall we implement `resize!(semi::AbstractSemidiscretization, new_size)`
+ # or just `resize!` whenever we call the relevant methods as we do now?
+ resize!(alpha, nelements(dg, cache))
+ if alpha_smooth
+ resize!(alpha_tmp, nelements(dg, cache))
+ end
+
+ # magic parameters
+ threshold = 0.5 * 10^(-1.8 * (nnodes(dg))^0.25)
+ parameter_s = log((1 - 0.0001) / 0.0001)
+
+ # If the water height `h` at one LGL node is lower than `threshold_partially_wet`
+ # the indicator sets the element-wise blending factor alpha[element] = 1
+ # via the local variable `indicator_wet`. In turn, this ensures that a pure
+ # FV method is used in partially wet cells and guarantees the well-balanced property.
+ #
+ # Hard-coded cut-off value of `threshold_partially_wet = 1e-4` was determined through many numerical experiments.
+ # Overall idea is to increase robustness when computing the velocity on (nearly) dry cells which
+ # could be "dangerous" due to division of conservative variables, e.g., v1 = hv1 / h.
+ # Here, the impact of the threshold on the number of cells being updated with FV is not that
+ # significant. However, its impact on the robustness is very significant.
+ # The value can be seen as a trade-off between accuracy and stability.
+ # Well-balancedness of the scheme on partially wet cells with hydrostatic reconstruction
+ # can only be proven for the FV method (see Chen and Noelle).
+ # Therefore we set alpha to be one regardless of its given value from the modal indicator.
+ threshold_partially_wet = 1e-4
+
+ @threaded for element in eachelement(dg, cache)
+ indicator = indicator_threaded[Threads.threadid()]
+ modal = modal_threaded[Threads.threadid()]
+ modal_tmp1 = modal_tmp1_threaded[Threads.threadid()]
+
+ # (Re-)set dummy variable for alpha_dry
+ indicator_wet = 1
+
+ # Calculate indicator variables at Gauss-Lobatto nodes
+ for j in eachnode(dg), i in eachnode(dg)
+ u_local = get_node_vars(u, equations, dg, i, j, element)
+ h, _, _, _ = u_local
+
+ if h <= threshold_partially_wet
+ indicator_wet = 0
+ end
+
+ indicator[i, j] = indicator_hg.variable(u_local, equations)
+ end
+
+ # Convert to modal representation
+ multiply_scalar_dimensionwise!(modal, dg.basis.inverse_vandermonde_legendre,
+ indicator, modal_tmp1)
+
+ # Calculate total energies for all modes, without highest, without two highest
+ total_energy = zero(eltype(modal))
+ for j in 1:nnodes(dg), i in 1:nnodes(dg)
+ total_energy += modal[i, j]^2
+ end
+ total_energy_clip1 = zero(eltype(modal))
+ for j in 1:(nnodes(dg) - 1), i in 1:(nnodes(dg) - 1)
+ total_energy_clip1 += modal[i, j]^2
+ end
+ total_energy_clip2 = zero(eltype(modal))
+ for j in 1:(nnodes(dg) - 2), i in 1:(nnodes(dg) - 2)
+ total_energy_clip2 += modal[i, j]^2
+ end
+
+ # Calculate energy in higher modes
+ energy = max((total_energy - total_energy_clip1) / total_energy,
+ (total_energy_clip1 - total_energy_clip2) / total_energy_clip1)
+
+ alpha_element = 1 / (1 + exp(-parameter_s / threshold * (energy - threshold)))
+
+ # Take care of the case close to pure DG
+ if alpha_element < alpha_min
+ alpha_element = zero(alpha_element)
+ end
+
+ # Take care of the case close to pure FV
+ if alpha_element > 1 - alpha_min
+ alpha_element = one(alpha_element)
+ end
+
+ # Clip the maximum amount of FV allowed or set to 1 depending on indicator_wet
+ if indicator_wet == 0
+ alpha[element] = 1
+ else # Element is not defined as dry but wet
+ alpha[element] = min(alpha_max, alpha_element)
+ end
+ end
+
+ if alpha_smooth
+ apply_smoothing!(mesh, alpha, alpha_tmp, dg, cache)
+ end
+
+ return alpha
+end
+
# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl
# with @batch (@threaded).
# Otherwise, @threaded does not work here with Julia ARM on macOS.
diff --git a/test/Project.toml b/test/Project.toml
index 7d386415227..cae1d4ff396 100644
--- a/test/Project.toml
+++ b/test/Project.toml
@@ -17,6 +17,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
+Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
diff --git a/test/test_structured_2d.jl b/test/test_structured_2d.jl
index 16fc72f0a46..75937ba82ad 100644
--- a/test/test_structured_2d.jl
+++ b/test/test_structured_2d.jl
@@ -1,5 +1,7 @@
module TestExamplesStructuredMesh2D
+# TODO: TrixiShallowWater: move any wet/dry tests to new package
+
using Test
using Trixi
@@ -20,7 +22,7 @@ isdir(outdir) && rm(outdir, recursive=true)
end
@trixi_testset "elixir_advection_coupled.jl" begin
- @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_coupled.jl"),
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_coupled.jl"),
l2 = [7.816742843181738e-6, 7.816742843196112e-6],
linf = [6.314906965543265e-5, 6.314906965410039e-5],
coverage_override = (maxiters=10^5,))
@@ -270,6 +272,27 @@ isdir(outdir) && rm(outdir, recursive=true)
tspan = (0.0, 0.25))
end
+ @trixi_testset "elixir_shallowwater_well_balanced_wet_dry.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced_wet_dry.jl"),
+ l2 = [0.019731646454942086, 1.0694532773278277e-14, 1.1969913383405568e-14, 0.0771517260037954],
+ linf = [0.4999999999998892, 6.067153702623552e-14, 4.4849667259339357e-14, 1.9999999999999993],
+ tspan = (0.0, 0.25))
+ end
+
+ @trixi_testset "elixir_shallowwater_conical_island.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_conical_island.jl"),
+ l2 = [0.04593154164306353, 0.1644534881916908, 0.16445348819169076, 0.0011537702354532122],
+ linf = [0.21100717610846442, 0.9501592344310412, 0.950159234431041, 0.021790250683516296],
+ tspan = (0.0, 0.025))
+ end
+
+ @trixi_testset "elixir_shallowwater_parabolic_bowl.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_parabolic_bowl.jl"),
+ l2 = [0.00015285369980313484, 1.9536806395943226e-5, 9.936906607758672e-5, 5.0686313334616055e-15],
+ linf = [0.003316119030459211, 0.0005075409427972817, 0.001986721761060583, 4.701794509287538e-14],
+ tspan = (0.0, 0.025), cells_per_dimension = (40, 40))
+ end
+
@trixi_testset "elixir_mhd_ec_shockcapturing.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_ec_shockcapturing.jl"),
l2 = [0.0364192725149364, 0.0426667193422069, 0.04261673001449095, 0.025884071405646924,
diff --git a/test/test_tree_1d_shallowwater.jl b/test/test_tree_1d_shallowwater.jl
index 1c3bac1fab6..cafa17edd4c 100644
--- a/test/test_tree_1d_shallowwater.jl
+++ b/test/test_tree_1d_shallowwater.jl
@@ -1,5 +1,7 @@
module TestExamples1DShallowWater
+# TODO: TrixiShallowWater: move any wet/dry tests to new package
+
using Test
using Trixi
@@ -38,6 +40,13 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
tspan = (0.0, 0.25))
end
+ @trixi_testset "elixir_shallowwater_well_balanced_wet_dry.jl with FluxHydrostaticReconstruction" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced_wet_dry.jl"),
+ l2 = [0.00965787167169024, 5.345454081916856e-14, 0.03857583749209928],
+ linf = [0.4999999999998892, 2.2447689894899726e-13, 1.9999999999999714],
+ tspan = (0.0, 0.25))
+ end
+
@trixi_testset "elixir_shallowwater_source_terms.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"),
l2 = [0.0022363707373868713, 0.01576799981934617, 4.436491725585346e-5],
@@ -88,6 +97,20 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
linf = [1.1209754279344226, 1.3230788645853582, 0.8646939843534251],
tspan = (0.0, 0.05))
end
+
+ @trixi_testset "elixir_shallowwater_beach.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_beach.jl"),
+ l2 = [0.17979210479598923, 1.2377495706611434, 6.289818963361573e-8],
+ linf = [0.845938394800688, 3.3740800777086575, 4.4541473087633676e-7],
+ tspan = (0.0, 0.05))
+ end
+
+ @trixi_testset "elixir_shallowwater_parabolic_bowl.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_parabolic_bowl.jl"),
+ l2 = [8.965981683033589e-5, 1.8565707397810857e-5, 4.1043039226164336e-17],
+ linf = [0.00041080213807871235, 0.00014823261488938177, 2.220446049250313e-16],
+ tspan = (0.0, 0.05))
+ end
end
end # module
diff --git a/test/test_tree_1d_shallowwater_twolayer.jl b/test/test_tree_1d_shallowwater_twolayer.jl
index 0d8a83806f9..8372d0d4676 100644
--- a/test/test_tree_1d_shallowwater_twolayer.jl
+++ b/test/test_tree_1d_shallowwater_twolayer.jl
@@ -1,5 +1,7 @@
module TestExamples1DShallowWaterTwoLayer
+# TODO: TrixiShallowWater: move two layer tests to new package
+
using Test
using Trixi
diff --git a/test/test_tree_2d_shallowwater.jl b/test/test_tree_2d_shallowwater.jl
index f465a177a67..7670d28f43a 100644
--- a/test/test_tree_2d_shallowwater.jl
+++ b/test/test_tree_2d_shallowwater.jl
@@ -1,5 +1,7 @@
module TestExamples2DShallowWater
+# TODO: TrixiShallowWater: move any wet/dry tests to new package
+
using Test
using Trixi
@@ -37,6 +39,13 @@ EXAMPLES_DIR = joinpath(examples_dir(), "tree_2d_dgsem")
tspan = (0.0, 0.25))
end
+ @trixi_testset "elixir_shallowwater_well_balanced_wet_dry.jl with FluxHydrostaticReconstruction" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_well_balanced_wet_dry.jl"),
+ l2 = [0.030186039395610056, 2.513287752536758e-14, 1.3631397744897607e-16, 0.10911781485920438],
+ linf = [0.49999999999993505, 5.5278950497971455e-14, 7.462550826772548e-16, 2.0],
+ tspan = (0.0, 0.25))
+ end
+
@trixi_testset "elixir_shallowwater_source_terms.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"),
l2 = [0.001868474306068482, 0.01731687445878443, 0.017649083171490863, 6.274146767717023e-5],
@@ -57,6 +66,21 @@ EXAMPLES_DIR = joinpath(examples_dir(), "tree_2d_dgsem")
linf = [0.015156105797771602, 0.07964811135780492, 0.0839787097210376, 0.0001819675955490041],
tspan = (0.0, 0.025), surface_flux=(flux_hll, flux_nonconservative_fjordholm_etal))
end
+
+ @trixi_testset "elixir_shallowwater_conical_island.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_conical_island.jl"),
+ l2 = [0.0459315416430658, 0.1644534881916991, 0.16445348819169914, 0.0011537702354532694],
+ linf = [0.21100717610846464, 0.9501592344310412, 0.9501592344310417, 0.021790250683516282],
+ tspan = (0.0, 0.025))
+ end
+
+ @trixi_testset "elixir_shallowwater_parabolic_bowl.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_parabolic_bowl.jl"),
+ l2 = [0.00025345501281482687, 4.4525120338817177e-5, 0.00015991819160294247, 7.750412064917294e-15],
+ linf = [0.004664246019836723, 0.0004972780116736669, 0.0028735707270457628, 6.866729407306593e-14],
+ tspan = (0.0, 0.025),
+ basis = LobattoLegendreBasis(3))
+ end
end
end # module
diff --git a/test/test_tree_2d_shallowwater_twolayer.jl b/test/test_tree_2d_shallowwater_twolayer.jl
index 4bb45064714..7ad5b0f7316 100644
--- a/test/test_tree_2d_shallowwater_twolayer.jl
+++ b/test/test_tree_2d_shallowwater_twolayer.jl
@@ -1,5 +1,7 @@
module TestExamples2DShallowWaterTwoLayer
+# TODO: TrixiShallowWater: move two layer tests to new package
+
using Test
using Trixi
@@ -19,10 +21,10 @@ EXAMPLES_DIR = joinpath(examples_dir(), "tree_2d_dgsem")
@trixi_testset "elixir_shallowwater_twolayer_convergence.jl with flux_es_fjordholm_etal" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_twolayer_convergence.jl"),
- l2 = [0.00024709443131137236, 0.0019215286339769443, 0.0023833298173254447,
+ l2 = [0.00024709443131137236, 0.0019215286339769443, 0.0023833298173254447,
0.00021258247976270914, 0.0011299428031136195, 0.0009191313765262401,
- 8.873630921431545e-6],
- linf = [0.0016099763244645793, 0.007659242165565017, 0.009123320235427057,
+ 8.873630921431545e-6],
+ linf = [0.0016099763244645793, 0.007659242165565017, 0.009123320235427057,
0.0013496983982568267, 0.0035573687287770994, 0.00296823235874899,
3.361991620143279e-5],
surface_flux = (flux_es_fjordholm_etal, flux_nonconservative_fjordholm_etal),
@@ -31,19 +33,19 @@ EXAMPLES_DIR = joinpath(examples_dir(), "tree_2d_dgsem")
@trixi_testset "elixir_shallowwater_twolayer_well_balanced.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_twolayer_well_balanced.jl"),
- l2 = [3.2935164267930016e-16, 4.6800825611195103e-17, 4.843057532147818e-17,
- 0.0030769233188015013, 1.4809161150389857e-16, 1.509071695038043e-16,
+ l2 = [3.2935164267930016e-16, 4.6800825611195103e-17, 4.843057532147818e-17,
+ 0.0030769233188015013, 1.4809161150389857e-16, 1.509071695038043e-16,
0.0030769233188014935],
- linf = [2.248201624865942e-15, 2.346382070278936e-16, 2.208565017494899e-16,
- 0.026474051138910493, 9.237568031609006e-16, 7.520758026187046e-16,
+ linf = [2.248201624865942e-15, 2.346382070278936e-16, 2.208565017494899e-16,
+ 0.026474051138910493, 9.237568031609006e-16, 7.520758026187046e-16,
0.026474051138910267],
tspan = (0.0, 0.25))
end
@trixi_testset "elixir_shallowwater_twolayer_well_balanced with flux_lax_friedrichs.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_twolayer_well_balanced.jl"),
- l2 = [2.0525741072929735e-16, 6.000589392730905e-17, 6.102759428478984e-17,
- 0.0030769233188014905, 1.8421386173122792e-16, 1.8473184927121752e-16,
+ l2 = [2.0525741072929735e-16, 6.000589392730905e-17, 6.102759428478984e-17,
+ 0.0030769233188014905, 1.8421386173122792e-16, 1.8473184927121752e-16,
0.0030769233188014935],
linf = [7.355227538141662e-16, 2.960836949170518e-16, 4.2726562436938764e-16,
0.02647405113891016, 1.038795478061861e-15, 1.0401789378532516e-15,
diff --git a/test/test_unit.jl b/test/test_unit.jl
index 2ce111b2bf4..e70a9be6a4a 100644
--- a/test/test_unit.jl
+++ b/test/test_unit.jl
@@ -402,6 +402,10 @@ isdir(outdir) && rm(outdir, recursive=true)
indicator_hg = IndicatorHennemannGassner(1.0, 0.0, true, "variable", "cache")
@test_nowarn show(stdout, indicator_hg)
+ # TODO: TrixiShallowWater: move unit test
+ indicator_hg_swe = IndicatorHennemannGassnerShallowWater(1.0, 0.0, true, "variable", "cache")
+ @test_nowarn show(stdout, indicator_hg_swe)
+
indicator_loehner = IndicatorLöhner(1.0, "variable", (; cache=nothing))
@test_nowarn show(stdout, indicator_loehner)
diff --git a/test/test_unstructured_2d.jl b/test/test_unstructured_2d.jl
index d4b0d150ca1..fbe88a2a0a3 100644
--- a/test/test_unstructured_2d.jl
+++ b/test/test_unstructured_2d.jl
@@ -1,5 +1,7 @@
module TestExamplesUnstructuredMesh2D
+# TODO: TrixiShallowWater: move any wet/dry and two layer tests
+
using Test
using Trixi
@@ -134,6 +136,14 @@ isdir(outdir) && rm(outdir, recursive=true)
tspan = (0.0, 0.025))
end
+ @trixi_testset "elixir_shallowwater_source_terms.jl with flux_hll" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"),
+ l2 = [0.0011197139793938727, 0.015430259691311309, 0.017081031802719554, 5.089218476759981e-6],
+ linf = [0.014300809338967824, 0.12783372461224918, 0.17625472321993918, 2.6407324614341476e-5],
+ surface_flux=(flux_hll, flux_nonconservative_fjordholm_etal),
+ tspan = (0.0, 0.025))
+ end
+
@trixi_testset "elixir_shallowwater_dirichlet.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_dirichlet.jl"),
l2 = [1.1577518608940115e-5, 4.867189932537344e-13, 4.647273240470541e-13, 1.1577518608933468e-5],
@@ -155,6 +165,14 @@ isdir(outdir) && rm(outdir, recursive=true)
tspan = (0.0, 0.25))
end
+ @trixi_testset "elixir_shallowwater_three_mound_dam_break.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_three_mound_dam_break.jl"),
+ l2 = [0.0892957892027502, 0.30648836484407915, 2.28712547616214e-15, 0.0008778654298684622],
+ linf = [0.850329472915091, 2.330631694956507, 5.783660020252348e-14, 0.04326237921249021],
+ basis = LobattoLegendreBasis(3),
+ tspan = (0.0, 0.25))
+ end
+
@trixi_testset "elixir_shallowwater_twolayer_convergence.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_twolayer_convergence.jl"),
l2 = [0.0007953969898161991, 0.00882074628714633, 0.0024322572528892934,
From c97eb8c47a0a092ab73889c8ce7c838ba7864127 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sat, 15 Jul 2023 06:51:23 +0200
Subject: [PATCH 033/263] set version to v0.5.33
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 4a289380850..9dd7ecd023f 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.33-pre"
+version = "0.5.33"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 10e0fa93320cd8722d482a89a0e944955b0c23ee Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sat, 15 Jul 2023 06:51:38 +0200
Subject: [PATCH 034/263] set development version to v0.5.34-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 9dd7ecd023f..6c3c7fa0208 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.33"
+version = "0.5.34-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 07981c3e50943a9bdb1a845f918a4e8831714338 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sat, 15 Jul 2023 12:52:38 +0200
Subject: [PATCH 035/263] throw better error message with MPI for TreeMesh1D
(#1569)
---
src/meshes/tree_mesh.jl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/meshes/tree_mesh.jl b/src/meshes/tree_mesh.jl
index 34794ded852..93ba982bce9 100644
--- a/src/meshes/tree_mesh.jl
+++ b/src/meshes/tree_mesh.jl
@@ -125,9 +125,9 @@ function TreeMesh(coordinates_min::NTuple{NDIMS, Real},
# TODO: MPI, create nice interface for a parallel tree/mesh
if mpi_isparallel()
- if mpi_isroot() && NDIMS == 3
+ if mpi_isroot() && NDIMS != 2
println(stderr,
- "ERROR: TreeMesh3D does not support parallel execution with MPI")
+ "ERROR: The TreeMesh supports parallel execution with MPI only in 2 dimensions")
MPI.Abort(mpi_comm(), 1)
end
TreeType = ParallelTree{NDIMS}
From 932f43358acba90e3de7909c2bd18c8b630c66b9 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sat, 15 Jul 2023 16:36:48 +0200
Subject: [PATCH 036/263] allow periodic FDSBP operators (#1570)
* enable fully periodic upwind SBP oeprators
* 2D and 3D tests
* comment on PeriodicFDSBP
---
Project.toml | 2 +-
.../tree_1d_fdsbp/elixir_advection_upwind.jl | 3 +-
.../elixir_advection_upwind_periodic.jl | 57 +++++++++++++++++++
src/solvers/fdsbp_tree/fdsbp.jl | 3 +
src/solvers/fdsbp_tree/fdsbp_1d.jl | 28 ++++++++-
src/solvers/fdsbp_tree/fdsbp_2d.jl | 28 ++++++++-
src/solvers/fdsbp_tree/fdsbp_3d.jl | 28 ++++++++-
test/test_tree_1d_fdsbp.jl | 15 +++++
test/test_tree_2d_fdsbp.jl | 18 ++++++
test/test_tree_3d_fdsbp.jl | 23 +++++++-
10 files changed, 196 insertions(+), 9 deletions(-)
create mode 100644 examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl
diff --git a/Project.toml b/Project.toml
index 6c3c7fa0208..a49cfb2e254 100644
--- a/Project.toml
+++ b/Project.toml
@@ -79,7 +79,7 @@ StaticArrayInterface = "1.4"
StaticArrays = "1"
StrideArrays = "0.1.18"
StructArrays = "0.6"
-SummationByPartsOperators = "0.5.25"
+SummationByPartsOperators = "0.5.41"
TimerOutputs = "0.5"
Triangulate = "2.0"
TriplotBase = "0.1"
diff --git a/examples/tree_1d_fdsbp/elixir_advection_upwind.jl b/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
index 5c50e1a6c64..18dd818e3ca 100644
--- a/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
+++ b/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
@@ -27,7 +27,8 @@ coordinates_min = -1.0
coordinates_max = 1.0
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 4,
- n_cells_max = 10_000)
+ n_cells_max = 10_000,
+ periodicity = true)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_sin, solver)
diff --git a/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl b/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl
new file mode 100644
index 00000000000..3eb805095f4
--- /dev/null
+++ b/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl
@@ -0,0 +1,57 @@
+# !!! warning "Experimental implementation (upwind SBP)"
+# This is an experimental feature and may change in future releases.
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the linear scalar advection equation equation
+
+equations = LinearScalarAdvectionEquation1D(1.0)
+
+function initial_condition_sin(x, t, equation::LinearScalarAdvectionEquation1D)
+ return SVector(sinpi(x[1] - equations.advection_velocity[1] * t))
+end
+
+D_upw = upwind_operators(SummationByPartsOperators.periodic_derivative_operator,
+ accuracy_order = 4,
+ xmin = -1.0, xmax = 1.0,
+ N = 64)
+flux_splitting = splitting_lax_friedrichs
+solver = FDSBP(D_upw,
+ surface_integral = SurfaceIntegralUpwind(flux_splitting),
+ volume_integral = VolumeIntegralUpwind(flux_splitting))
+
+coordinates_min = -1.0
+coordinates_max = 1.0
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level = 0,
+ n_cells_max = 10_000,
+ periodicity = true)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_sin, solver)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 2.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback)
+
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, RDPK3SpFSAL49(); abstol=1.0e-6, reltol=1.0e-6,
+ ode_default_options()..., callback=callbacks);
+summary_callback() # print the timer summary
diff --git a/src/solvers/fdsbp_tree/fdsbp.jl b/src/solvers/fdsbp_tree/fdsbp.jl
index cbb6fd16243..11b09c6df9c 100644
--- a/src/solvers/fdsbp_tree/fdsbp.jl
+++ b/src/solvers/fdsbp_tree/fdsbp.jl
@@ -27,6 +27,9 @@ The other arguments have the same meaning as in [`DG`](@ref) or [`DGSEM`](@ref).
"""
const FDSBP = DG{Basis} where {Basis <: AbstractDerivativeOperator}
+# Internal abbreviation for easier-to-read dispatch (not exported)
+const PeriodicFDSBP = FDSBP{Basis} where {Basis <: AbstractPeriodicDerivativeOperator}
+
function FDSBP(D_SBP::AbstractDerivativeOperator; surface_integral, volume_integral)
# `nothing` is passed as `mortar`
return DG(D_SBP, nothing, surface_integral, volume_integral)
diff --git a/src/solvers/fdsbp_tree/fdsbp_1d.jl b/src/solvers/fdsbp_tree/fdsbp_1d.jl
index c7712074940..0de0cff4851 100644
--- a/src/solvers/fdsbp_tree/fdsbp_1d.jl
+++ b/src/solvers/fdsbp_tree/fdsbp_1d.jl
@@ -165,6 +165,14 @@ function calc_surface_integral!(du, u, mesh::TreeMesh{1},
return nothing
end
+# Periodic FDSBP operators need to use a single element without boundaries
+function calc_surface_integral!(du, u, mesh::TreeMesh1D,
+ equations, surface_integral::SurfaceIntegralStrongForm,
+ dg::PeriodicFDSBP, cache)
+ @assert nelements(dg, cache) == 1
+ return nothing
+end
+
# Specialized interface flux computation because the upwind solver does
# not require a standard numerical flux (Riemann solver). The flux splitting
# already separates the solution information into right-traveling and
@@ -239,13 +247,25 @@ function calc_surface_integral!(du, u, mesh::TreeMesh{1},
return nothing
end
+# Periodic FDSBP operators need to use a single element without boundaries
+function calc_surface_integral!(du, u, mesh::TreeMesh1D,
+ equations, surface_integral::SurfaceIntegralUpwind,
+ dg::PeriodicFDSBP, cache)
+ @assert nelements(dg, cache) == 1
+ return nothing
+end
+
# AnalysisCallback
function integrate_via_indices(func::Func, u,
mesh::TreeMesh{1}, equations,
dg::FDSBP, cache, args...; normalize = true) where {Func}
# TODO: FD. This is rather inefficient right now and allocates...
- weights = diag(SummationByPartsOperators.mass_matrix(dg.basis))
+ M = SummationByPartsOperators.mass_matrix(dg.basis)
+ if M isa UniformScaling
+ M = M(nnodes(dg))
+ end
+ weights = diag(M)
# Initialize integral with zeros of the right shape
integral = zero(func(u, 1, 1, equations, dg, args...))
@@ -271,7 +291,11 @@ function calc_error_norms(func, u, t, analyzer,
mesh::TreeMesh{1}, equations, initial_condition,
dg::FDSBP, cache, cache_analysis)
# TODO: FD. This is rather inefficient right now and allocates...
- weights = diag(SummationByPartsOperators.mass_matrix(dg.basis))
+ M = SummationByPartsOperators.mass_matrix(dg.basis)
+ if M isa UniformScaling
+ M = M(nnodes(dg))
+ end
+ weights = diag(M)
@unpack node_coordinates = cache.elements
# Set up data structures
diff --git a/src/solvers/fdsbp_tree/fdsbp_2d.jl b/src/solvers/fdsbp_tree/fdsbp_2d.jl
index 241e0d95342..beff605629a 100644
--- a/src/solvers/fdsbp_tree/fdsbp_2d.jl
+++ b/src/solvers/fdsbp_tree/fdsbp_2d.jl
@@ -201,6 +201,14 @@ function calc_surface_integral!(du, u, mesh::TreeMesh{2},
return nothing
end
+# Periodic FDSBP operators need to use a single element without boundaries
+function calc_surface_integral!(du, u, mesh::TreeMesh2D,
+ equations, surface_integral::SurfaceIntegralStrongForm,
+ dg::PeriodicFDSBP, cache)
+ @assert nelements(dg, cache) == 1
+ return nothing
+end
+
# Specialized interface flux computation because the upwind solver does
# not require a standard numerical flux (Riemann solver). The flux splitting
# already separates the solution information into right-traveling and
@@ -295,12 +303,24 @@ function calc_surface_integral!(du, u, mesh::TreeMesh{2},
return nothing
end
+# Periodic FDSBP operators need to use a single element without boundaries
+function calc_surface_integral!(du, u, mesh::TreeMesh2D,
+ equations, surface_integral::SurfaceIntegralUpwind,
+ dg::PeriodicFDSBP, cache)
+ @assert nelements(dg, cache) == 1
+ return nothing
+end
+
# AnalysisCallback
function integrate_via_indices(func::Func, u,
mesh::TreeMesh{2}, equations,
dg::FDSBP, cache, args...; normalize = true) where {Func}
# TODO: FD. This is rather inefficient right now and allocates...
- weights = diag(SummationByPartsOperators.mass_matrix(dg.basis))
+ M = SummationByPartsOperators.mass_matrix(dg.basis)
+ if M isa UniformScaling
+ M = M(nnodes(dg))
+ end
+ weights = diag(M)
# Initialize integral with zeros of the right shape
integral = zero(func(u, 1, 1, 1, equations, dg, args...))
@@ -326,7 +346,11 @@ function calc_error_norms(func, u, t, analyzer,
mesh::TreeMesh{2}, equations, initial_condition,
dg::FDSBP, cache, cache_analysis)
# TODO: FD. This is rather inefficient right now and allocates...
- weights = diag(SummationByPartsOperators.mass_matrix(dg.basis))
+ M = SummationByPartsOperators.mass_matrix(dg.basis)
+ if M isa UniformScaling
+ M = M(nnodes(dg))
+ end
+ weights = diag(M)
@unpack node_coordinates = cache.elements
# Set up data structures
diff --git a/src/solvers/fdsbp_tree/fdsbp_3d.jl b/src/solvers/fdsbp_tree/fdsbp_3d.jl
index a4f69d3d481..0c3f18b6d6e 100644
--- a/src/solvers/fdsbp_tree/fdsbp_3d.jl
+++ b/src/solvers/fdsbp_tree/fdsbp_3d.jl
@@ -237,6 +237,14 @@ function calc_surface_integral!(du, u, mesh::TreeMesh{3},
return nothing
end
+# Periodic FDSBP operators need to use a single element without boundaries
+function calc_surface_integral!(du, u, mesh::TreeMesh3D,
+ equations, surface_integral::SurfaceIntegralStrongForm,
+ dg::PeriodicFDSBP, cache)
+ @assert nelements(dg, cache) == 1
+ return nothing
+end
+
# Specialized interface flux computation because the upwind solver does
# not require a standard numerical flux (Riemann solver). The flux splitting
# already separates the solution information into right-traveling and
@@ -346,13 +354,25 @@ function calc_surface_integral!(du, u, mesh::TreeMesh{3},
return nothing
end
+# Periodic FDSBP operators need to use a single element without boundaries
+function calc_surface_integral!(du, u, mesh::TreeMesh3D,
+ equations, surface_integral::SurfaceIntegralUpwind,
+ dg::PeriodicFDSBP, cache)
+ @assert nelements(dg, cache) == 1
+ return nothing
+end
+
# AnalysisCallback
function integrate_via_indices(func::Func, u,
mesh::TreeMesh{3}, equations,
dg::FDSBP, cache, args...; normalize = true) where {Func}
# TODO: FD. This is rather inefficient right now and allocates...
- weights = diag(SummationByPartsOperators.mass_matrix(dg.basis))
+ M = SummationByPartsOperators.mass_matrix(dg.basis)
+ if M isa UniformScaling
+ M = M(nnodes(dg))
+ end
+ weights = diag(M)
# Initialize integral with zeros of the right shape
integral = zero(func(u, 1, 1, 1, 1, equations, dg, args...))
@@ -378,7 +398,11 @@ function calc_error_norms(func, u, t, analyzer,
mesh::TreeMesh{3}, equations, initial_condition,
dg::FDSBP, cache, cache_analysis)
# TODO: FD. This is rather inefficient right now and allocates...
- weights = diag(SummationByPartsOperators.mass_matrix(dg.basis))
+ M = SummationByPartsOperators.mass_matrix(dg.basis)
+ if M isa UniformScaling
+ M = M(nnodes(dg))
+ end
+ weights = diag(M)
@unpack node_coordinates = cache.elements
# Set up data structures
diff --git a/test/test_tree_1d_fdsbp.jl b/test/test_tree_1d_fdsbp.jl
index 118385c34b3..ce0ca660d35 100644
--- a/test/test_tree_1d_fdsbp.jl
+++ b/test/test_tree_1d_fdsbp.jl
@@ -23,6 +23,21 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_fdsbp")
@test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
end
end
+
+ @trixi_testset "elixir_advection_upwind_periodic.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_upwind_periodic.jl"),
+ l2 = [1.1672962783692568e-5],
+ linf = [1.650514414558435e-5])
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
+ end
+ end
end
@testset "Inviscid Burgers" begin
diff --git a/test/test_tree_2d_fdsbp.jl b/test/test_tree_2d_fdsbp.jl
index 7c58ef89a6c..e81c82f3f34 100644
--- a/test/test_tree_2d_fdsbp.jl
+++ b/test/test_tree_2d_fdsbp.jl
@@ -23,6 +23,24 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_fdsbp")
@test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
end
end
+
+ @trixi_testset "elixir_advection_extended.jl with periodic operators" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
+ l2 = [1.1239649404463432e-5],
+ linf = [1.5895264629195438e-5],
+ D_SBP = SummationByPartsOperators.periodic_derivative_operator(
+ derivative_order = 1, accuracy_order = 4, xmin = 0.0, xmax = 1.0, N = 40),
+ initial_refinement_level = 0)
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
+ end
+ end
end
@testset "Compressible Euler" begin
diff --git a/test/test_tree_3d_fdsbp.jl b/test/test_tree_3d_fdsbp.jl
index 9dceab38031..106dd007b09 100644
--- a/test/test_tree_3d_fdsbp.jl
+++ b/test/test_tree_3d_fdsbp.jl
@@ -7,7 +7,7 @@ include("test_trixi.jl")
EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_3d_fdsbp")
-@testset "Compressible Euler" begin
+@testset "Linear scalar advection" begin
@trixi_testset "elixir_advection_extended.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
l2 = [0.005355755365412444],
@@ -23,6 +23,27 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_3d_fdsbp")
end
end
+ @trixi_testset "elixir_advection_extended.jl with periodic operators" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
+ l2 = [1.3819894522373702e-8],
+ linf = [3.381866298113323e-8],
+ D_SBP = SummationByPartsOperators.periodic_derivative_operator(
+ derivative_order = 1, accuracy_order = 4, xmin = 0.0, xmax = 1.0, N = 10),
+ initial_refinement_level = 0,
+ tspan = (0.0, 5.0))
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
+ end
+ end
+end
+
+@testset "Compressible Euler" begin
@trixi_testset "elixir_euler_convergence.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_convergence.jl"),
l2 = [2.247522803543667e-5, 2.2499169224681058e-5, 2.24991692246826e-5, 2.2499169224684707e-5, 5.814121361417382e-5],
From fd239da5af1ba619fa2457c6318a5f3ab3be59b3 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 16 Jul 2023 06:11:40 +0200
Subject: [PATCH 037/263] set version to v0.5.34
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index a49cfb2e254..7f2a52b0aaf 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.34-pre"
+version = "0.5.34"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From d96514f6d58e48b33816f58e02959d167954fdea Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 16 Jul 2023 06:11:55 +0200
Subject: [PATCH 038/263] set development version to v0.5.35-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 7f2a52b0aaf..4c187ed38ff 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.34"
+version = "0.5.35-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 75d70fdf5706ccdc5290303675bcd5ad1cf7d462 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 17 Jul 2023 19:15:37 +0200
Subject: [PATCH 039/263] Bump crate-ci/typos from 1.16.0 to 1.16.1 (#1573)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.0 to 1.16.1.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.16.0...v1.16.1)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index bb5a32f72ee..f72c3b0947b 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.16.0
+ uses: crate-ci/typos@v1.16.1
From a12f82da43a16d59db16063557fef245c87b6c0e Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 18 Jul 2023 05:52:17 +0200
Subject: [PATCH 040/263] fix typos in comments (#1572)
---
examples/tree_1d_fdsbp/elixir_advection_upwind.jl | 2 +-
examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/examples/tree_1d_fdsbp/elixir_advection_upwind.jl b/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
index 18dd818e3ca..1f2498e0866 100644
--- a/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
+++ b/examples/tree_1d_fdsbp/elixir_advection_upwind.jl
@@ -5,7 +5,7 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# semidiscretization of the linear scalar advection equation equation
+# semidiscretization of the linear scalar advection equation
equations = LinearScalarAdvectionEquation1D(1.0)
diff --git a/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl b/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl
index 3eb805095f4..035d3568a80 100644
--- a/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl
+++ b/examples/tree_1d_fdsbp/elixir_advection_upwind_periodic.jl
@@ -5,7 +5,7 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# semidiscretization of the linear scalar advection equation equation
+# semidiscretization of the linear scalar advection equation
equations = LinearScalarAdvectionEquation1D(1.0)
From 375384659cb57a80e253c5e685db8ec298e30d8c Mon Sep 17 00:00:00 2001
From: Michael Schlottke-Lakemper
Date: Wed, 19 Jul 2023 07:39:37 +0200
Subject: [PATCH 041/263] Add talk announcement for JuliaCon 2023 (#1575)
---
README.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/README.md b/README.md
index ccd70b6daf8..7eaee8750dd 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,16 @@
+***
+**Trixi.jl at JuliaCon 2023**
+At this year's JuliaCon, we will be present with an online contribution that involves Trixi.jl:
+
+* [Scaling Trixi.jl to more than 10,000 cores using MPI](https://pretalx.com/juliacon2023/talk/PC8PZ8/),
+ 27th July 2023, 10:30–11:30 (US/Eastern), 32-G449 (Kiva)
+
+We are looking forward to seeing you there ♥️
+***
+
**Trixi.jl** is a numerical simulation framework for hyperbolic conservation
laws written in [Julia](https://julialang.org). A key objective for the
framework is to be useful to both scientists and students. Therefore, next to
From b0ec66ea004c84d8487c4318a54933da8c827c92 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 19 Jul 2023 12:16:53 +0200
Subject: [PATCH 042/263] fix GC time percentage output (#1576)
---
src/callbacks_step/analysis.jl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl
index 8cf43a1d15e..7c453aab633 100644
--- a/src/callbacks_step/analysis.jl
+++ b/src/callbacks_step/analysis.jl
@@ -267,7 +267,7 @@ function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi)
gc_time_absolute = 1.0e-9 * (Base.gc_time_ns() - analysis_callback.start_gc_time)
# Compute the percentage of total time that was spent in garbage collection
- gc_time_percentage = gc_time_absolute / runtime_absolute
+ gc_time_percentage = gc_time_absolute / runtime_absolute * 100
# Obtain the current memory usage of the Julia garbage collector, in MiB, i.e., the total size of
# objects in memory that have been allocated by the JIT compiler or the user code.
From e0aad3cad1a2581eb79cf4fd1d06e7d7fb2c6379 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 19 Jul 2023 14:11:35 +0200
Subject: [PATCH 043/263] set version to v0.5.35
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 4c187ed38ff..1818d1c56c9 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.35-pre"
+version = "0.5.35"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 67d137d6712f28bbe99ffef3d003afe96c47aade Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 19 Jul 2023 14:11:46 +0200
Subject: [PATCH 044/263] set development version to v0.5.36-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 1818d1c56c9..07c4fe55ad4 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.35"
+version = "0.5.36-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 1aec5fa7e17a8011baf77bfa1822491693e1986d Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 21 Jul 2023 11:56:38 +0200
Subject: [PATCH 045/263] test threaded time integration (#1581)
* test threaded time integration
* link to upstream issue in comment
---
examples/dgmulti_2d/elixir_euler_curved.jl | 3 +-
.../elixir_advection_diffusion.jl | 3 +-
.../tree_2d_dgsem/elixir_advection_restart.jl | 3 +-
test/Project.toml | 6 ++--
test/test_threaded.jl | 28 +++++++++++++++++++
5 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/examples/dgmulti_2d/elixir_euler_curved.jl b/examples/dgmulti_2d/elixir_euler_curved.jl
index a3ba62f1cfb..39e3a0a0360 100644
--- a/examples/dgmulti_2d/elixir_euler_curved.jl
+++ b/examples/dgmulti_2d/elixir_euler_curved.jl
@@ -42,7 +42,8 @@ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
###############################################################################
# run the simulation
-sol = solve(ode, RDPK3SpFSAL49(); abstol=1.0e-6, reltol=1.0e-6,
+alg = RDPK3SpFSAL49()
+sol = solve(ode, alg; abstol=1.0e-6, reltol=1.0e-6,
ode_default_options()..., callback=callbacks);
summary_callback() # print the timer summary
diff --git a/examples/tree_2d_dgsem/elixir_advection_diffusion.jl b/examples/tree_2d_dgsem/elixir_advection_diffusion.jl
index e96e1b5a171..a716bd278b8 100644
--- a/examples/tree_2d_dgsem/elixir_advection_diffusion.jl
+++ b/examples/tree_2d_dgsem/elixir_advection_diffusion.jl
@@ -75,8 +75,9 @@ callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback)
# run the simulation
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+alg = RDPK3SpFSAL49()
time_int_tol = 1.0e-11
-sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+sol = solve(ode, alg; abstol=time_int_tol, reltol=time_int_tol,
ode_default_options()..., callback=callbacks)
# Print the timer summary
diff --git a/examples/tree_2d_dgsem/elixir_advection_restart.jl b/examples/tree_2d_dgsem/elixir_advection_restart.jl
index 4ceb5932573..72efb7d0c84 100644
--- a/examples/tree_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/tree_2d_dgsem/elixir_advection_restart.jl
@@ -26,7 +26,8 @@ ode = semidiscretize(semi, tspan, restart_filename);
# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
save_solution.condition.save_initial_solution = false
-integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
+alg = CarpenterKennedy2N54(williamson_condition=false)
+integrator = init(ode, alg,
dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep=false, callback=callbacks)
diff --git a/test/Project.toml b/test/Project.toml
index cae1d4ff396..7115a19b441 100644
--- a/test/Project.toml
+++ b/test/Project.toml
@@ -24,9 +24,9 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[preferences.OrdinaryDiffEq]
PrecompileAutoSpecialize = false
PrecompileAutoSwitch = false
-PrecompileDefaultSpecialize = true
+PrecompileDefaultSpecialize = false
PrecompileFunctionWrapperSpecialize = false
-PrecompileLowStorage = true
+PrecompileLowStorage = false
PrecompileNoSpecialize = false
-PrecompileNonStiff = true
+PrecompileNonStiff = false
PrecompileStiff = false
diff --git a/test/test_threaded.jl b/test/test_threaded.jl
index 1e750707981..323d12d7091 100644
--- a/test/test_threaded.jl
+++ b/test/test_threaded.jl
@@ -18,6 +18,14 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
linf = [6.314906965243505e-5])
end
+ @trixi_testset "elixir_advection_restart.jl with threaded time integration" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_restart.jl"),
+ alg = CarpenterKennedy2N54(williamson_condition = false, thread = OrdinaryDiffEq.True()),
+ # Expected errors are exactly the same as in the serial test!
+ l2 = [7.81674284320524e-6],
+ linf = [6.314906965243505e-5])
+ end
+
@trixi_testset "elixir_advection_amr_refine_twice.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_amr_refine_twice.jl"),
l2 = [0.00020547512522578292],
@@ -42,6 +50,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
l2 = [0.061751715597716854, 0.05018223615408711, 0.05018989446443463, 0.225871559730513],
linf = [0.29347582879608825, 0.31081249232844693, 0.3107380389947736, 1.0540358049885143])
end
+
+ @trixi_testset "elixir_advection_diffusion.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_diffusion.jl"),
+ initial_refinement_level = 2, tspan = (0.0, 0.4), polydeg = 5,
+ alg = RDPK3SpFSAL49(thread = OrdinaryDiffEq.True()),
+ l2 = [4.0915532997994255e-6],
+ linf = [2.3040850347877395e-5]
+ )
+ end
end
@@ -108,6 +125,17 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "elixir_euler_curved.jl with threaded time integration" begin
+ @test_broken false
+ # TODO: This is currently broken and needs to be fixed upstream
+ # See https://github.com/JuliaSIMD/StrideArrays.jl/issues/77
+ # @test_trixi_include(joinpath(examples_dir(), "dgmulti_2d", "elixir_euler_curved.jl"),
+ # alg = RDPK3SpFSAL49(thread = OrdinaryDiffEq.True()),
+ # l2 = [1.720476068165337e-5, 1.592168205710526e-5, 1.592168205812963e-5, 4.894094865697305e-5],
+ # linf = [0.00010525416930584619, 0.00010003778091061122, 0.00010003778085621029, 0.00036426282101720275]
+ # )
+ end
+
@trixi_testset "elixir_euler_triangulate_pkg_mesh.jl" begin
@test_trixi_include(joinpath(examples_dir(), "dgmulti_2d", "elixir_euler_triangulate_pkg_mesh.jl"),
l2 = [2.344080455438114e-6, 1.8610038753097983e-6, 2.4095165666095305e-6, 6.373308158814308e-6],
From 036eaed82b92be9376c5b610d8d40eddf45ca1fa Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Mon, 24 Jul 2023 13:26:16 +0200
Subject: [PATCH 046/263] reset threads in semidiscretize (#1584)
I added the option to reset the threads from Polyester.jl in semidiscretize.
However, I did not document it in the docstring since we have not documented
that we use Polyester.jl threads in general - and the resetting is specific
to Polyester.jl. I was not sure whether we still would like to keep the option
to change the threading backend any time - although I do not see a good reason
why we should do so.
---
Project.toml | 2 +-
src/Trixi.jl | 2 +-
src/semidiscretization/semidiscretization.jl | 20 +++++++++++++++++--
...semidiscretization_hyperbolic_parabolic.jl | 10 +++++++++-
4 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/Project.toml b/Project.toml
index 07c4fe55ad4..00bf2718d8b 100644
--- a/Project.toml
+++ b/Project.toml
@@ -65,7 +65,7 @@ MuladdMacro = "0.2.2"
Octavian = "0.3.5"
OffsetArrays = "1.3"
P4est = "0.4"
-Polyester = "0.3.4, 0.5, 0.6, 0.7"
+Polyester = "0.7.5"
PrecompileTools = "1.1"
RecipesBase = "1.1"
Reexport = "1.0"
diff --git a/src/Trixi.jl b/src/Trixi.jl
index cf6158e29eb..b0c872b1904 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -51,7 +51,7 @@ using LoopVectorization: LoopVectorization, @turbo, indices
using StaticArrayInterface: static_length # used by LoopVectorization
using MuladdMacro: @muladd
using Octavian: Octavian, matmul!
-using Polyester: @batch # You know, the cheapest threads you can find...
+using Polyester: Polyester, @batch # You know, the cheapest threads you can find...
using OffsetArrays: OffsetArray, OffsetVector
using P4est
using Setfield: @set
diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl
index ac312c57c89..fbdcd73e2a8 100644
--- a/src/semidiscretization/semidiscretization.jl
+++ b/src/semidiscretization/semidiscretization.jl
@@ -70,7 +70,15 @@ end
Wrap the semidiscretization `semi` as an ODE problem in the time interval `tspan`
that can be passed to `solve` from the [SciML ecosystem](https://diffeq.sciml.ai/latest/).
"""
-function semidiscretize(semi::AbstractSemidiscretization, tspan)
+function semidiscretize(semi::AbstractSemidiscretization, tspan;
+ reset_threads = true)
+ # Optionally reset Polyester.jl threads. See
+ # https://github.com/trixi-framework/Trixi.jl/issues/1583
+ # https://github.com/JuliaSIMD/Polyester.jl/issues/30
+ if reset_threads
+ Polyester.reset_threads!()
+ end
+
u0_ode = compute_coefficients(first(tspan), semi)
# TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using
# mpi_isparallel() && MPI.Barrier(mpi_comm())
@@ -88,7 +96,15 @@ that can be passed to `solve` from the [SciML ecosystem](https://diffeq.sciml.ai
The initial condition etc. is taken from the `restart_file`.
"""
function semidiscretize(semi::AbstractSemidiscretization, tspan,
- restart_file::AbstractString)
+ restart_file::AbstractString;
+ reset_threads = true)
+ # Optionally reset Polyester.jl threads. See
+ # https://github.com/trixi-framework/Trixi.jl/issues/1583
+ # https://github.com/JuliaSIMD/Polyester.jl/issues/30
+ if reset_threads
+ Polyester.reset_threads!()
+ end
+
u0_ode = load_restart_file(semi, restart_file)
# TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using
# mpi_isparallel() && MPI.Barrier(mpi_comm())
diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
index f54bc744164..8f1e38c891b 100644
--- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
+++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
@@ -274,7 +274,15 @@ The parabolic right-hand side is the first function of the split ODE problem and
will be used by default by the implicit part of IMEX methods from the
SciML ecosystem.
"""
-function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan)
+function semidiscretize(semi::SemidiscretizationHyperbolicParabolic, tspan;
+ reset_threads = true)
+ # Optionally reset Polyester.jl threads. See
+ # https://github.com/trixi-framework/Trixi.jl/issues/1583
+ # https://github.com/JuliaSIMD/Polyester.jl/issues/30
+ if reset_threads
+ Polyester.reset_threads!()
+ end
+
u0_ode = compute_coefficients(first(tspan), semi)
# TODO: MPI, do we want to synchronize loading and print debug statements, e.g. using
# mpi_isparallel() && MPI.Barrier(mpi_comm())
From 253f63ef042ef3f10ca15c5d21327a3b6ce4bcdc Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Mon, 24 Jul 2023 22:46:27 -0500
Subject: [PATCH 047/263] Fix CI failures related to Makie (#1586)
* unrelated cleanup
* fix CI issues?
---
ext/TrixiMakieExt.jl | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/ext/TrixiMakieExt.jl b/ext/TrixiMakieExt.jl
index 1eb11f6a422..8cd7576a6e5 100644
--- a/ext/TrixiMakieExt.jl
+++ b/ext/TrixiMakieExt.jl
@@ -335,7 +335,7 @@ end
# ================== new Makie plot recipes ====================
# This initializes a Makie recipe, which creates a new type definition which Makie uses to create
-# custom `trixiheatmap` plots. See also https://makie.juliaplots.org/stable/recipes.html
+# custom `trixiheatmap` plots. See also https://docs.makie.org/stable/documentation/recipes/
Makie.@recipe(TrixiHeatmap, plot_data_series) do scene
Makie.Theme(colormap = default_Makie_colormap())
end
@@ -346,9 +346,8 @@ function Makie.plot!(myplot::TrixiHeatmap)
plotting_mesh = global_plotting_triangulation_makie(pds;
set_z_coordinate_zero = true)
- @unpack variable_id = pds
pd = pds.plot_data
- solution_z = vec(StructArrays.component(pd.data, variable_id))
+ solution_z = vec(StructArrays.component(pd.data, pds.variable_id))
Makie.mesh!(myplot, plotting_mesh, color = solution_z, shading = false,
colormap = myplot[:colormap])
myplot.colorrange = extrema(solution_z)
@@ -411,7 +410,7 @@ function Makie.plot!(fig, pd::PlotData2DTriangulated;
row = row_list[variable_to_plot]
col = col_list[variable_to_plot]
- Makie.Colorbar(fig[row, col][1, 2], plt)
+ Makie.Colorbar(fig[row, col][1, 2], colormap = colormap)
ax.aspect = Makie.DataAspect() # equal aspect ratio
ax.title = variable_name
From 6e7e3b5bfb4e4a232f04a9b0d3c711ad414a56c2 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 25 Jul 2023 07:08:05 +0200
Subject: [PATCH 048/263] activate previously broken test and add allocation
tests (#1582)
* activate previously broken test and add allocation tests
* fix allocations in prolong2interfaces!
* fix allocations in calc_sources!
* fix allocations in apply_jacobian!
* fixed FDSBP, elixir_euler_convergence.jl
* elixir_euler_triangulate_pkg_mesh.jl is only broken with multithreading
* Update test_threaded.jl
* Update test_threaded.jl
---
src/solvers/dgsem_tree/dg_1d.jl | 20 ++--
src/solvers/dgsem_tree/dg_2d.jl | 25 +++--
src/solvers/dgsem_tree/dg_3d.jl | 29 +++---
test/test_threaded.jl | 175 ++++++++++++++++++++++++++++++--
4 files changed, 212 insertions(+), 37 deletions(-)
diff --git a/src/solvers/dgsem_tree/dg_1d.jl b/src/solvers/dgsem_tree/dg_1d.jl
index c66f427cce3..b5bb076f3b7 100644
--- a/src/solvers/dgsem_tree/dg_1d.jl
+++ b/src/solvers/dgsem_tree/dg_1d.jl
@@ -385,15 +385,17 @@ end
function prolong2interfaces!(cache, u,
mesh::TreeMesh{1}, equations, surface_integral, dg::DG)
@unpack interfaces = cache
+ @unpack neighbor_ids = interfaces
+ interfaces_u = interfaces.u
@threaded for interface in eachinterface(dg, cache)
- left_element = interfaces.neighbor_ids[1, interface]
- right_element = interfaces.neighbor_ids[2, interface]
+ left_element = neighbor_ids[1, interface]
+ right_element = neighbor_ids[2, interface]
# interface in x-direction
for v in eachvariable(equations)
- interfaces.u[1, v, interface] = u[v, nnodes(dg), left_element]
- interfaces.u[2, v, interface] = u[v, 1, right_element]
+ interfaces_u[1, v, interface] = u[v, nnodes(dg), left_element]
+ interfaces_u[2, v, interface] = u[v, 1, right_element]
end
end
@@ -621,8 +623,10 @@ end
function apply_jacobian!(du, mesh::Union{TreeMesh{1}, StructuredMesh{1}},
equations, dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
@threaded for element in eachelement(dg, cache)
- factor = -cache.elements.inverse_jacobian[element]
+ factor = -inverse_jacobian[element]
for i in eachnode(dg)
for v in eachvariable(equations)
@@ -642,11 +646,13 @@ end
function calc_sources!(du, u, t, source_terms,
equations::AbstractEquations{1}, dg::DG, cache)
+ @unpack node_coordinates = cache.elements
+
@threaded for element in eachelement(dg, cache)
for i in eachnode(dg)
u_local = get_node_vars(u, equations, dg, i, element)
- x_local = get_node_coords(cache.elements.node_coordinates, equations, dg, i,
- element)
+ x_local = get_node_coords(node_coordinates, equations, dg,
+ i, element)
du_local = source_terms(u_local, x_local, t, equations)
add_to_node_vars!(du, du_local, equations, dg, i, element)
end
diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl
index d3227710686..6c5e0cee0cf 100644
--- a/src/solvers/dgsem_tree/dg_2d.jl
+++ b/src/solvers/dgsem_tree/dg_2d.jl
@@ -529,23 +529,24 @@ end
function prolong2interfaces!(cache, u,
mesh::TreeMesh{2}, equations, surface_integral, dg::DG)
@unpack interfaces = cache
- @unpack orientations = interfaces
+ @unpack orientations, neighbor_ids = interfaces
+ interfaces_u = interfaces.u
@threaded for interface in eachinterface(dg, cache)
- left_element = interfaces.neighbor_ids[1, interface]
- right_element = interfaces.neighbor_ids[2, interface]
+ left_element = neighbor_ids[1, interface]
+ right_element = neighbor_ids[2, interface]
if orientations[interface] == 1
# interface in x-direction
for j in eachnode(dg), v in eachvariable(equations)
- interfaces.u[1, v, j, interface] = u[v, nnodes(dg), j, left_element]
- interfaces.u[2, v, j, interface] = u[v, 1, j, right_element]
+ interfaces_u[1, v, j, interface] = u[v, nnodes(dg), j, left_element]
+ interfaces_u[2, v, j, interface] = u[v, 1, j, right_element]
end
else # if orientations[interface] == 2
# interface in y-direction
for i in eachnode(dg), v in eachvariable(equations)
- interfaces.u[1, v, i, interface] = u[v, i, nnodes(dg), left_element]
- interfaces.u[2, v, i, interface] = u[v, i, 1, right_element]
+ interfaces_u[1, v, i, interface] = u[v, i, nnodes(dg), left_element]
+ interfaces_u[2, v, i, interface] = u[v, i, 1, right_element]
end
end
end
@@ -1116,8 +1117,10 @@ end
function apply_jacobian!(du, mesh::TreeMesh{2},
equations, dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
@threaded for element in eachelement(dg, cache)
- factor = -cache.elements.inverse_jacobian[element]
+ factor = -inverse_jacobian[element]
for j in eachnode(dg), i in eachnode(dg)
for v in eachvariable(equations)
@@ -1137,11 +1140,13 @@ end
function calc_sources!(du, u, t, source_terms,
equations::AbstractEquations{2}, dg::DG, cache)
+ @unpack node_coordinates = cache.elements
+
@threaded for element in eachelement(dg, cache)
for j in eachnode(dg), i in eachnode(dg)
u_local = get_node_vars(u, equations, dg, i, j, element)
- x_local = get_node_coords(cache.elements.node_coordinates, equations, dg, i,
- j, element)
+ x_local = get_node_coords(node_coordinates, equations, dg,
+ i, j, element)
du_local = source_terms(u_local, x_local, t, equations)
add_to_node_vars!(du, du_local, equations, dg, i, j, element)
end
diff --git a/src/solvers/dgsem_tree/dg_3d.jl b/src/solvers/dgsem_tree/dg_3d.jl
index 95abb2595e5..acdab900cd1 100644
--- a/src/solvers/dgsem_tree/dg_3d.jl
+++ b/src/solvers/dgsem_tree/dg_3d.jl
@@ -598,32 +598,33 @@ end
function prolong2interfaces!(cache, u,
mesh::TreeMesh{3}, equations, surface_integral, dg::DG)
@unpack interfaces = cache
- @unpack orientations = interfaces
+ @unpack orientations, neighbor_ids = interfaces
+ interfaces_u = interfaces.u
@threaded for interface in eachinterface(dg, cache)
- left_element = interfaces.neighbor_ids[1, interface]
- right_element = interfaces.neighbor_ids[2, interface]
+ left_element = neighbor_ids[1, interface]
+ right_element = neighbor_ids[2, interface]
if orientations[interface] == 1
# interface in x-direction
for k in eachnode(dg), j in eachnode(dg), v in eachvariable(equations)
- interfaces.u[1, v, j, k, interface] = u[v, nnodes(dg), j, k,
+ interfaces_u[1, v, j, k, interface] = u[v, nnodes(dg), j, k,
left_element]
- interfaces.u[2, v, j, k, interface] = u[v, 1, j, k, right_element]
+ interfaces_u[2, v, j, k, interface] = u[v, 1, j, k, right_element]
end
elseif orientations[interface] == 2
# interface in y-direction
for k in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
- interfaces.u[1, v, i, k, interface] = u[v, i, nnodes(dg), k,
+ interfaces_u[1, v, i, k, interface] = u[v, i, nnodes(dg), k,
left_element]
- interfaces.u[2, v, i, k, interface] = u[v, i, 1, k, right_element]
+ interfaces_u[2, v, i, k, interface] = u[v, i, 1, k, right_element]
end
else # if orientations[interface] == 3
# interface in z-direction
for j in eachnode(dg), i in eachnode(dg), v in eachvariable(equations)
- interfaces.u[1, v, i, j, interface] = u[v, i, j, nnodes(dg),
+ interfaces_u[1, v, i, j, interface] = u[v, i, j, nnodes(dg),
left_element]
- interfaces.u[2, v, i, j, interface] = u[v, i, j, 1, right_element]
+ interfaces_u[2, v, i, j, interface] = u[v, i, j, 1, right_element]
end
end
end
@@ -1350,8 +1351,10 @@ end
function apply_jacobian!(du, mesh::TreeMesh{3},
equations, dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
@threaded for element in eachelement(dg, cache)
- factor = -cache.elements.inverse_jacobian[element]
+ factor = -inverse_jacobian[element]
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
for v in eachvariable(equations)
@@ -1371,11 +1374,13 @@ end
function calc_sources!(du, u, t, source_terms,
equations::AbstractEquations{3}, dg::DG, cache)
+ @unpack node_coordinates = cache.elements
+
@threaded for element in eachelement(dg, cache)
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
u_local = get_node_vars(u, equations, dg, i, j, k, element)
- x_local = get_node_coords(cache.elements.node_coordinates, equations, dg, i,
- j, k, element)
+ x_local = get_node_coords(node_coordinates, equations, dg,
+ i, j, k, element)
du_local = source_terms(u_local, x_local, t, equations)
add_to_node_vars!(du, du_local, equations, dg, i, j, k, element)
end
diff --git a/test/test_threaded.jl b/test/test_threaded.jl
index 323d12d7091..77fa16ad33e 100644
--- a/test/test_threaded.jl
+++ b/test/test_threaded.jl
@@ -16,6 +16,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
# Expected errors are exactly the same as in the serial test!
l2 = [7.81674284320524e-6],
linf = [6.314906965243505e-5])
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_advection_restart.jl with threaded time integration" begin
@@ -30,12 +39,30 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_amr_refine_twice.jl"),
l2 = [0.00020547512522578292],
linf = [0.007831753383083506])
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_advection_amr_coarsen_twice.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_amr_coarsen_twice.jl"),
l2 = [0.0014321062757891826],
linf = [0.0253454486893413])
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_euler_source_terms_nonperiodic.jl" begin
@@ -43,12 +70,30 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
l2 = [2.259440511766445e-6, 2.318888155713922e-6, 2.3188881557894307e-6, 6.3327863238858925e-6],
linf = [1.498738264560373e-5, 1.9182011928187137e-5, 1.918201192685487e-5, 6.0526717141407005e-5],
rtol = 0.001)
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_euler_ec.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_euler_ec.jl"),
l2 = [0.061751715597716854, 0.05018223615408711, 0.05018989446443463, 0.225871559730513],
linf = [0.29347582879608825, 0.31081249232844693, 0.3107380389947736, 1.0540358049885143])
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_advection_diffusion.jl" begin
@@ -58,6 +103,47 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
l2 = [4.0915532997994255e-6],
linf = [2.3040850347877395e-5]
)
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
+ end
+
+ @trixi_testset "FDSBP, elixir_advection_extended.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_fdsbp", "elixir_advection_extended.jl"),
+ l2 = [2.898644263922225e-6],
+ linf = [8.491517930142578e-6],
+ rtol = 1.0e-7) # These results change a little bit and depend on the CI system
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
+ end
+
+ @trixi_testset "FDSBP, elixir_euler_convergence.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_fdsbp", "elixir_euler_convergence.jl"),
+ l2 = [1.7088389997042244e-6, 1.7437997855125774e-6, 1.7437997855350776e-6, 5.457223460127621e-6],
+ linf = [9.796504903736292e-6, 9.614745892783105e-6, 9.614745892783105e-6, 4.026107182575345e-5],
+ tspan = (0.0, 0.1))
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
end
@@ -70,6 +156,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
rtol = 5.0e-5, # Higher tolerance to make tests pass in CI (in particular with macOS)
elixir_file="elixir_advection_waving_flag.jl",
restart_file="restart_000021.h5")
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_mhd_ec.jl" begin
@@ -81,6 +176,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
0.9757376320946505, 0.12123736788315098, 0.12837436699267113, 0.17793825293524734,
0.03460761690059514],
tspan = (0.0, 0.3))
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
end
@@ -93,6 +197,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
linf = [0.36236334472179443, 0.3690785638275256, 0.8475748723784078, 0.0,
8.881784197001252e-16, 1.7763568394002505e-15, 1.7763568394002505e-15],
tspan = (0.0, 5.0))
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
end
@@ -102,6 +215,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
@test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"),
l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893],
linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657])
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_eulergravity_convergence.jl" begin
@@ -123,17 +245,32 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
l2 = [0.006400337855843578, 0.005303799804137764, 0.005303799804119745, 0.013204169007030144],
linf = [0.03798302318566282, 0.05321027922532284, 0.05321027922605448, 0.13392025411839015],
)
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_euler_curved.jl with threaded time integration" begin
- @test_broken false
- # TODO: This is currently broken and needs to be fixed upstream
- # See https://github.com/JuliaSIMD/StrideArrays.jl/issues/77
- # @test_trixi_include(joinpath(examples_dir(), "dgmulti_2d", "elixir_euler_curved.jl"),
- # alg = RDPK3SpFSAL49(thread = OrdinaryDiffEq.True()),
- # l2 = [1.720476068165337e-5, 1.592168205710526e-5, 1.592168205812963e-5, 4.894094865697305e-5],
- # linf = [0.00010525416930584619, 0.00010003778091061122, 0.00010003778085621029, 0.00036426282101720275]
- # )
+ @test_trixi_include(joinpath(examples_dir(), "dgmulti_2d", "elixir_euler_curved.jl"),
+ alg = RDPK3SpFSAL49(thread = OrdinaryDiffEq.True()),
+ l2 = [1.720476068165337e-5, 1.592168205710526e-5, 1.592168205812963e-5, 4.894094865697305e-5],
+ linf = [0.00010525416930584619, 0.00010003778091061122, 0.00010003778085621029, 0.00036426282101720275]
+ )
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_euler_triangulate_pkg_mesh.jl" begin
@@ -141,6 +278,19 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
l2 = [2.344080455438114e-6, 1.8610038753097983e-6, 2.4095165666095305e-6, 6.373308158814308e-6],
linf = [2.5099852761334418e-5, 2.2683684021362893e-5, 2.6180448559287584e-5, 5.5752932611508044e-5]
)
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ if (Threads.nthreads() < 2) || (VERSION < v"1.9")
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ else
+ @test_broken (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
+ end
end
@trixi_testset "elixir_euler_fdsbp_periodic.jl" begin
@@ -148,6 +298,15 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
l2 = [1.3333320340010056e-6, 2.044834627970641e-6, 2.044834627855601e-6, 5.282189803559564e-6],
linf = [2.7000151718858945e-6, 3.988595028259212e-6, 3.9885950273710336e-6, 8.848583042286862e-6]
)
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
end
end
From 3dd2cb60d0798a5a9a327c73e6150382636c7845 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 25 Jul 2023 08:59:37 +0200
Subject: [PATCH 049/263] set version to v0.5.36
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 00bf2718d8b..2017290c785 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.36-pre"
+version = "0.5.36"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 41b56ef71c535321fca8c99fe9d7b2098b70025d Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 25 Jul 2023 08:59:47 +0200
Subject: [PATCH 050/263] set development version to v0.5.37-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 2017290c785..94c47a35ac1 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.36"
+version = "0.5.37-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From fe6a818a8459d6beef3969c1fd2d5cc7ddf596df Mon Sep 17 00:00:00 2001
From: Ahmad Peyvan <115842305+apey236@users.noreply.github.com>
Date: Tue, 25 Jul 2023 04:17:42 -0400
Subject: [PATCH 051/263] Adding parabolic terms for `P4estMesh{3}` (#1555)
* Adding parabolic terms for 3D P4est mesh
* Adding parabolic terms for 3D P4estMesh
* Adding working parabolic terms for `P4estMesh{3}`
* Formatting
* Addin TGV example and test to `P4estMesh{3}`
* Update src/solvers/dgsem_tree/dg_3d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Update src/solvers/dgsem_tree/dg_3d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Update src/solvers/dgsem_tree/dg_3d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Update src/solvers/dgsem_tree/dg_3d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Removing comments
* Removed comments
* Adding TGV test for `P4estMesh{3}`
* Correcting the format
* Format correction
* Remove .toml file
* Format correction
* Optimized loop for speed
---------
Co-authored-by: Hendrik Ranocha
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
---
.../elixir_navierstokes_convergence.jl | 263 +++++++
...elixir_navierstokes_taylor_green_vortex.jl | 82 +++
src/callbacks_step/analysis_dg3d.jl | 2 +-
src/solvers/dgsem_p4est/dg.jl | 1 +
src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 2 +-
src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 691 ++++++++++++++++++
src/solvers/dgsem_tree/dg_3d_parabolic.jl | 13 +-
test/test_parabolic_3d.jl | 19 +-
8 files changed, 1063 insertions(+), 10 deletions(-)
create mode 100644 examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl
create mode 100644 examples/p4est_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
create mode 100644 src/solvers/dgsem_p4est/dg_3d_parabolic.jl
diff --git a/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl
new file mode 100644
index 00000000000..c426fe95f5b
--- /dev/null
+++ b/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl
@@ -0,0 +1,263 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the ideal compressible Navier-Stokes equations
+
+prandtl_number() = 0.72
+mu() = 0.01
+
+equations = CompressibleEulerEquations3D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion3D(equations, mu=mu(), Prandtl=prandtl_number(),
+ gradient_variables=GradientVariablesPrimitive())
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs,
+ volume_integral=VolumeIntegralWeakForm())
+
+coordinates_min = (-1.0, -1.0, -1.0) # minimum coordinates (min(x), min(y), min(z))
+coordinates_max = ( 1.0, 1.0, 1.0) # maximum coordinates (max(x), max(y), max(z))
+
+trees_per_dimension = (2, 2, 2)
+
+mesh = P4estMesh(trees_per_dimension, polydeg=3,
+ coordinates_min=coordinates_min, coordinates_max=coordinates_max,
+ periodicity=(true, false, true), initial_refinement_level=2)
+
+# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion3D`
+# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion3D`)
+# and by the initial condition (which passes in `CompressibleEulerEquations3D`).
+# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
+function initial_condition_navier_stokes_convergence_test(x, t, equations)
+ # Constants. OBS! Must match those in `source_terms_navier_stokes_convergence_test`
+ c = 2.0
+ A1 = 0.5
+ A2 = 1.0
+ A3 = 0.5
+
+ # Convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_y = pi * x[2]
+ pi_z = pi * x[3]
+ pi_t = pi * t
+
+ rho = c + A1 * sin(pi_x) * cos(pi_y) * sin(pi_z) * cos(pi_t)
+ v1 = A2 * sin(pi_x) * log(x[2] + 2.0) * (1.0 - exp(-A3 * (x[2] - 1.0))) * sin(pi_z) * cos(pi_t)
+ v2 = v1
+ v3 = v1
+ p = rho^2
+
+ return prim2cons(SVector(rho, v1, v2, v3, p), equations)
+end
+
+@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
+ # TODO: parabolic
+ # we currently need to hardcode these parameters until we fix the "combined equation" issue
+ # see also https://github.com/trixi-framework/Trixi.jl/pull/1160
+ inv_gamma_minus_one = inv(equations.gamma - 1)
+ Pr = prandtl_number()
+ mu_ = mu()
+
+ # Constants. OBS! Must match those in `initial_condition_navier_stokes_convergence_test`
+ c = 2.0
+ A1 = 0.5
+ A2 = 1.0
+ A3 = 0.5
+
+ # Convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_y = pi * x[2]
+ pi_z = pi * x[3]
+ pi_t = pi * t
+
+ # Define auxiliary functions for the strange function of the y variable
+ # to make expressions easier to read
+ g = log(x[2] + 2.0) * (1.0 - exp(-A3 * (x[2] - 1.0)))
+ g_y = ( A3 * log(x[2] + 2.0) * exp(-A3 * (x[2] - 1.0))
+ + (1.0 - exp(-A3 * (x[2] - 1.0))) / (x[2] + 2.0) )
+ g_yy = ( 2.0 * A3 * exp(-A3 * (x[2] - 1.0)) / (x[2] + 2.0)
+ - (1.0 - exp(-A3 * (x[2] - 1.0))) / ((x[2] + 2.0)^2)
+ - A3^2 * log(x[2] + 2.0) * exp(-A3 * (x[2] - 1.0)) )
+
+ # Density and its derivatives
+ rho = c + A1 * sin(pi_x) * cos(pi_y) * sin(pi_z) * cos(pi_t)
+ rho_t = -pi * A1 * sin(pi_x) * cos(pi_y) * sin(pi_z) * sin(pi_t)
+ rho_x = pi * A1 * cos(pi_x) * cos(pi_y) * sin(pi_z) * cos(pi_t)
+ rho_y = -pi * A1 * sin(pi_x) * sin(pi_y) * sin(pi_z) * cos(pi_t)
+ rho_z = pi * A1 * sin(pi_x) * cos(pi_y) * cos(pi_z) * cos(pi_t)
+ rho_xx = -pi^2 * (rho - c)
+ rho_yy = -pi^2 * (rho - c)
+ rho_zz = -pi^2 * (rho - c)
+
+ # Velocities and their derivatives
+ # v1 terms
+ v1 = A2 * sin(pi_x) * g * sin(pi_z) * cos(pi_t)
+ v1_t = -pi * A2 * sin(pi_x) * g * sin(pi_z) * sin(pi_t)
+ v1_x = pi * A2 * cos(pi_x) * g * sin(pi_z) * cos(pi_t)
+ v1_y = A2 * sin(pi_x) * g_y * sin(pi_z) * cos(pi_t)
+ v1_z = pi * A2 * sin(pi_x) * g * cos(pi_z) * cos(pi_t)
+ v1_xx = -pi^2 * v1
+ v1_yy = A2 * sin(pi_x) * g_yy * sin(pi_z) * cos(pi_t)
+ v1_zz = -pi^2 * v1
+ v1_xy = pi * A2 * cos(pi_x) * g_y * sin(pi_z) * cos(pi_t)
+ v1_xz = pi^2 * A2 * cos(pi_x) * g * cos(pi_z) * cos(pi_t)
+ v1_yz = pi * A2 * sin(pi_x) * g_y * cos(pi_z) * cos(pi_t)
+ # v2 terms (simplifies from ansatz)
+ v2 = v1
+ v2_t = v1_t
+ v2_x = v1_x
+ v2_y = v1_y
+ v2_z = v1_z
+ v2_xx = v1_xx
+ v2_yy = v1_yy
+ v2_zz = v1_zz
+ v2_xy = v1_xy
+ v2_yz = v1_yz
+ # v3 terms (simplifies from ansatz)
+ v3 = v1
+ v3_t = v1_t
+ v3_x = v1_x
+ v3_y = v1_y
+ v3_z = v1_z
+ v3_xx = v1_xx
+ v3_yy = v1_yy
+ v3_zz = v1_zz
+ v3_xz = v1_xz
+ v3_yz = v1_yz
+
+ # Pressure and its derivatives
+ p = rho^2
+ p_t = 2.0 * rho * rho_t
+ p_x = 2.0 * rho * rho_x
+ p_y = 2.0 * rho * rho_y
+ p_z = 2.0 * rho * rho_z
+
+ # Total energy and its derivatives; simiplifies from ansatz that v2 = v1 and v3 = v1
+ E = p * inv_gamma_minus_one + 1.5 * rho * v1^2
+ E_t = p_t * inv_gamma_minus_one + 1.5 * rho_t * v1^2 + 3.0 * rho * v1 * v1_t
+ E_x = p_x * inv_gamma_minus_one + 1.5 * rho_x * v1^2 + 3.0 * rho * v1 * v1_x
+ E_y = p_y * inv_gamma_minus_one + 1.5 * rho_y * v1^2 + 3.0 * rho * v1 * v1_y
+ E_z = p_z * inv_gamma_minus_one + 1.5 * rho_z * v1^2 + 3.0 * rho * v1 * v1_z
+
+ # Divergence of Fick's law ∇⋅∇q = kappa ∇⋅∇T; simplifies because p = rho², so T = p/rho = rho
+ kappa = equations.gamma * inv_gamma_minus_one / Pr
+ q_xx = kappa * rho_xx # kappa T_xx
+ q_yy = kappa * rho_yy # kappa T_yy
+ q_zz = kappa * rho_zz # kappa T_zz
+
+ # Stress tensor and its derivatives (exploit symmetry)
+ tau11 = 4.0 / 3.0 * v1_x - 2.0 / 3.0 * (v2_y + v3_z)
+ tau12 = v1_y + v2_x
+ tau13 = v1_z + v3_x
+ tau22 = 4.0 / 3.0 * v2_y - 2.0 / 3.0 * (v1_x + v3_z)
+ tau23 = v2_z + v3_y
+ tau33 = 4.0 / 3.0 * v3_z - 2.0 / 3.0 * (v1_x + v2_y)
+
+ tau11_x = 4.0 / 3.0 * v1_xx - 2.0 / 3.0 * (v2_xy + v3_xz)
+ tau12_x = v1_xy + v2_xx
+ tau13_x = v1_xz + v3_xx
+
+ tau12_y = v1_yy + v2_xy
+ tau22_y = 4.0 / 3.0 * v2_yy - 2.0 / 3.0 * (v1_xy + v3_yz)
+ tau23_y = v2_yz + v3_yy
+
+ tau13_z = v1_zz + v3_xz
+ tau23_z = v2_zz + v3_yz
+ tau33_z = 4.0 / 3.0 * v3_zz - 2.0 / 3.0 * (v1_xz + v2_yz)
+
+ # Compute the source terms
+ # Density equation
+ du1 = ( rho_t + rho_x * v1 + rho * v1_x
+ + rho_y * v2 + rho * v2_y
+ + rho_z * v3 + rho * v3_z )
+ # x-momentum equation
+ du2 = ( rho_t * v1 + rho * v1_t + p_x + rho_x * v1^2
+ + 2.0 * rho * v1 * v1_x
+ + rho_y * v1 * v2
+ + rho * v1_y * v2
+ + rho * v1 * v2_y
+ + rho_z * v1 * v3
+ + rho * v1_z * v3
+ + rho * v1 * v3_z
+ - mu_ * (tau11_x + tau12_y + tau13_z) )
+ # y-momentum equation
+ du3 = ( rho_t * v2 + rho * v2_t + p_y + rho_x * v1 * v2
+ + rho * v1_x * v2
+ + rho * v1 * v2_x
+ + rho_y * v2^2
+ + 2.0 * rho * v2 * v2_y
+ + rho_z * v2 * v3
+ + rho * v2_z * v3
+ + rho * v2 * v3_z
+ - mu_ * (tau12_x + tau22_y + tau23_z) )
+ # z-momentum equation
+ du4 = ( rho_t * v3 + rho * v3_t + p_z + rho_x * v1 * v3
+ + rho * v1_x * v3
+ + rho * v1 * v3_x
+ + rho_y * v2 * v3
+ + rho * v2_y * v3
+ + rho * v2 * v3_y
+ + rho_z * v3^2
+ + 2.0 * rho * v3 * v3_z
+ - mu_ * (tau13_x + tau23_y + tau33_z) )
+ # Total energy equation
+ du5 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x)
+ + v2_y * (E + p) + v2 * (E_y + p_y)
+ + v3_z * (E + p) + v3 * (E_z + p_z)
+ # stress tensor and temperature gradient from x-direction
+ - mu_ * ( q_xx + v1_x * tau11 + v2_x * tau12 + v3_x * tau13
+ + v1 * tau11_x + v2 * tau12_x + v3 * tau13_x)
+ # stress tensor and temperature gradient terms from y-direction
+ - mu_ * ( q_yy + v1_y * tau12 + v2_y * tau22 + v3_y * tau23
+ + v1 * tau12_y + v2 * tau22_y + v3 * tau23_y)
+ # stress tensor and temperature gradient terms from z-direction
+ - mu_ * ( q_zz + v1_z * tau13 + v2_z * tau23 + v3_z * tau33
+ + v1 * tau13_z + v2 * tau23_z + v3 * tau33_z) )
+
+ return SVector(du1, du2, du3, du4, du5)
+end
+
+initial_condition = initial_condition_navier_stokes_convergence_test
+
+# BC types
+velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:4])
+heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
+boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
+
+# define inviscid boundary conditions
+boundary_conditions = Dict(
+ :y_neg => boundary_condition_slip_wall,
+ :y_pos => boundary_condition_slip_wall
+ )
+
+# define viscous boundary conditions
+boundary_conditions_parabolic = Dict(
+ :y_neg => boundary_condition_top_bottom,
+ :y_pos => boundary_condition_top_bottom
+ )
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 0.2)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+alive_callback = AliveCallback(alive_interval=10)
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
+
diff --git a/examples/p4est_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl b/examples/p4est_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
new file mode 100644
index 00000000000..c5b9ccf2e38
--- /dev/null
+++ b/examples/p4est_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
@@ -0,0 +1,82 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Navier-Stokes equations
+
+# TODO: parabolic; unify names of these accessor functions
+prandtl_number() = 0.72
+mu() = 6.25e-4 # equivalent to Re = 1600
+
+equations = CompressibleEulerEquations3D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion3D(equations, mu=mu(),
+ Prandtl=prandtl_number())
+
+"""
+ initial_condition_taylor_green_vortex(x, t, equations::CompressibleEulerEquations3D)
+
+The classical inviscid Taylor-Green vortex.
+"""
+function initial_condition_taylor_green_vortex(x, t, equations::CompressibleEulerEquations3D)
+ A = 1.0 # magnitude of speed
+ Ms = 0.1 # maximum Mach number
+
+ rho = 1.0
+ v1 = A * sin(x[1]) * cos(x[2]) * cos(x[3])
+ v2 = -A * cos(x[1]) * sin(x[2]) * cos(x[3])
+ v3 = 0.0
+ p = (A / Ms)^2 * rho / equations.gamma # scaling to get Ms
+ p = p + 1.0/16.0 * A^2 * rho * (cos(2*x[1])*cos(2*x[3]) + 2*cos(2*x[2]) + 2*cos(2*x[1]) + cos(2*x[2])*cos(2*x[3]))
+
+ return prim2cons(SVector(rho, v1, v2, v3, p), equations)
+end
+initial_condition = initial_condition_taylor_green_vortex
+
+volume_flux = flux_ranocha
+solver = DGSEM(polydeg=3, surface_flux=flux_hll,
+ volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
+
+coordinates_min = (-1.0, -1.0, -1.0) .* pi
+coordinates_max = ( 1.0, 1.0, 1.0) .* pi
+
+trees_per_dimension = (2, 2, 2)
+
+mesh = P4estMesh(trees_per_dimension, polydeg=3,
+ coordinates_min=coordinates_min, coordinates_max=coordinates_max,
+ periodicity=(true, true, true), initial_refinement_level=2)
+
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
+ initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 20.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 50
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal,
+ enstrophy))
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true,
+ solution_variables=cons2prim)
+alive_callback = AliveCallback(analysis_interval=analysis_interval,)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback,save_solution)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
diff --git a/src/callbacks_step/analysis_dg3d.jl b/src/callbacks_step/analysis_dg3d.jl
index 76aba813fab..3d9b38fd2a5 100644
--- a/src/callbacks_step/analysis_dg3d.jl
+++ b/src/callbacks_step/analysis_dg3d.jl
@@ -228,7 +228,7 @@ function integrate(func::Func, u,
end
function integrate(func::Func, u,
- mesh::TreeMesh{3},
+ mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations, equations_parabolic,
dg::DGSEM,
cache, cache_parabolic; normalize = true) where {Func}
diff --git a/src/solvers/dgsem_p4est/dg.jl b/src/solvers/dgsem_p4est/dg.jl
index a7cc1eee04d..ec50627d3ef 100644
--- a/src/solvers/dgsem_p4est/dg.jl
+++ b/src/solvers/dgsem_p4est/dg.jl
@@ -50,5 +50,6 @@ include("dg_2d.jl")
include("dg_2d_parabolic.jl")
include("dg_3d.jl")
+include("dg_3d_parabolic.jl")
include("dg_parallel.jl")
end # @muladd
diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
index 73ac47ed1e3..7e90a83a9ca 100644
--- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
+++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
@@ -1,7 +1,7 @@
# This method is called when a SemidiscretizationHyperbolicParabolic is constructed.
# It constructs the basic `cache` used throughout the simulation to compute
# the RHS etc.
-function create_cache_parabolic(mesh::P4estMesh, equations_hyperbolic::AbstractEquations,
+function create_cache_parabolic(mesh::P4estMesh{2}, equations_hyperbolic::AbstractEquations,
equations_parabolic::AbstractEquationsParabolic,
dg::DG, parabolic_scheme, RealT, uEltype)
balance!(mesh)
diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
new file mode 100644
index 00000000000..5370c927e05
--- /dev/null
+++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
@@ -0,0 +1,691 @@
+# This method is called when a SemidiscretizationHyperbolicParabolic is constructed.
+# It constructs the basic `cache` used throughout the simulation to compute
+# the RHS etc.
+function create_cache_parabolic(mesh::P4estMesh{3}, equations_hyperbolic::AbstractEquations,
+ equations_parabolic::AbstractEquationsParabolic,
+ dg::DG, parabolic_scheme, RealT, uEltype)
+ balance!(mesh)
+
+ elements = init_elements(mesh, equations_hyperbolic, dg.basis, uEltype)
+ interfaces = init_interfaces(mesh, equations_hyperbolic, dg.basis, elements)
+ boundaries = init_boundaries(mesh, equations_hyperbolic, dg.basis, elements)
+
+ n_vars = nvariables(equations_hyperbolic)
+ n_elements = nelements(elements)
+ n_nodes = nnodes(dg.basis) # nodes in one direction
+ u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_nodes, n_nodes, n_elements)
+ gradients = ntuple(_ -> similar(u_transformed), ndims(mesh))
+ flux_viscous = ntuple(_ -> similar(u_transformed), ndims(mesh))
+
+ cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed)
+
+ return cache
+end
+
+function calc_gradient!(gradients, u_transformed, t,
+ mesh::P4estMesh{3}, equations_parabolic,
+ boundary_conditions_parabolic, dg::DG,
+ cache, cache_parabolic)
+ gradients_x, gradients_y, gradients_z = gradients
+
+ # Reset du
+ @trixi_timeit timer() "reset gradients" begin
+ reset_du!(gradients_x, dg, cache)
+ reset_du!(gradients_y, dg, cache)
+ reset_du!(gradients_z, dg, cache)
+ end
+
+ # Calculate volume integral
+ @trixi_timeit timer() "volume integral" begin
+ (; derivative_dhat) = dg.basis
+ (; contravariant_vectors) = cache.elements
+
+ @threaded for element in eachelement(dg, cache)
+
+ # Calculate gradients with respect to reference coordinates in one element
+ for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u_transformed, equations_parabolic, dg, i, j, k,
+ element)
+
+ for ii in eachnode(dg)
+ multiply_add_to_node_vars!(gradients_x, derivative_dhat[ii, i],
+ u_node, equations_parabolic, dg, ii, j,
+ k, element)
+ end
+
+ for jj in eachnode(dg)
+ multiply_add_to_node_vars!(gradients_y, derivative_dhat[jj, j],
+ u_node, equations_parabolic, dg, i, jj,
+ k, element)
+ end
+
+ for kk in eachnode(dg)
+ multiply_add_to_node_vars!(gradients_z, derivative_dhat[kk, k],
+ u_node, equations_parabolic, dg, i, j,
+ kk, element)
+ end
+ end
+
+ # now that the reference coordinate gradients are computed, transform them node-by-node to physical gradients
+ # using the contravariant vectors
+ for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
+ Ja11, Ja12, Ja13 = get_contravariant_vector(1, contravariant_vectors,
+ i, j, k, element)
+ Ja21, Ja22, Ja23 = get_contravariant_vector(2, contravariant_vectors,
+ i, j, k, element)
+ Ja31, Ja32, Ja33 = get_contravariant_vector(3, contravariant_vectors,
+ i, j, k, element)
+
+ gradients_reference_1 = get_node_vars(gradients_x, equations_parabolic, dg,
+ i, j, k, element)
+ gradients_reference_2 = get_node_vars(gradients_y, equations_parabolic, dg,
+ i, j, k, element)
+ gradients_reference_3 = get_node_vars(gradients_z, equations_parabolic, dg,
+ i, j, k, element)
+
+ # note that the contravariant vectors are transposed compared with computations of flux
+ # divergences in `calc_volume_integral!`. See
+ # https://github.com/trixi-framework/Trixi.jl/pull/1490#discussion_r1213345190
+ # for a more detailed discussion.
+ gradient_x_node = Ja11 * gradients_reference_1 +
+ Ja21 * gradients_reference_2 +
+ Ja31 * gradients_reference_3
+ gradient_y_node = Ja12 * gradients_reference_1 +
+ Ja22 * gradients_reference_2 +
+ Ja32 * gradients_reference_3
+ gradient_z_node = Ja13 * gradients_reference_1 +
+ Ja23 * gradients_reference_2 +
+ Ja33 * gradients_reference_3
+
+ set_node_vars!(gradients_x, gradient_x_node, equations_parabolic, dg,
+ i, j, k, element)
+ set_node_vars!(gradients_y, gradient_y_node, equations_parabolic, dg,
+ i, j, k, element)
+ set_node_vars!(gradients_z, gradient_z_node, equations_parabolic, dg,
+ i, j, k, element)
+ end
+ end
+ end
+
+ # Prolong solution to interfaces
+ @trixi_timeit timer() "prolong2interfaces" begin
+ prolong2interfaces!(cache_parabolic, u_transformed, mesh,
+ equations_parabolic, dg.surface_integral, dg)
+ end
+
+ # Calculate interface fluxes for the gradient. This reuses P4est `calc_interface_flux!` along with a
+ # specialization for AbstractEquationsParabolic.
+ @trixi_timeit timer() "interface flux" begin
+ calc_interface_flux!(cache_parabolic.elements.surface_flux_values,
+ mesh, False(), # False() = no nonconservative terms
+ equations_parabolic, dg.surface_integral, dg, cache_parabolic)
+ end
+
+ # Prolong solution to boundaries
+ @trixi_timeit timer() "prolong2boundaries" begin
+ prolong2boundaries!(cache_parabolic, u_transformed, mesh,
+ equations_parabolic, dg.surface_integral, dg)
+ end
+
+ # Calculate boundary fluxes
+ @trixi_timeit timer() "boundary flux" begin
+ calc_boundary_flux_gradients!(cache_parabolic, t, boundary_conditions_parabolic,
+ mesh, equations_parabolic, dg.surface_integral, dg)
+ end
+
+ # TODO: parabolic; mortars
+ @assert nmortars(dg, cache) == 0
+
+ # Calculate surface integrals
+ @trixi_timeit timer() "surface integral" begin
+ (; boundary_interpolation) = dg.basis
+ (; surface_flux_values) = cache_parabolic.elements
+ (; contravariant_vectors) = cache.elements
+
+ # Access the factors only once before beginning the loop to increase performance.
+ # We also use explicit assignments instead of `+=` to let `@muladd` turn these
+ # into FMAs (see comment at the top of the file).
+ factor_1 = boundary_interpolation[1, 1]
+ factor_2 = boundary_interpolation[nnodes(dg), 2]
+ @threaded for element in eachelement(dg, cache)
+ for l in eachnode(dg), m in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ for dim in 1:3
+ grad = gradients[dim]
+ # surface at -x
+ normal_direction = get_normal_direction(1, contravariant_vectors,
+ 1, l, m, element)
+ grad[v, 1, l, m, element] = (grad[v, 1, l, m, element] +
+ surface_flux_values[v, l, m, 1,
+ element] *
+ factor_1 * normal_direction[dim])
+
+ # surface at +x
+ normal_direction = get_normal_direction(2, contravariant_vectors,
+ nnodes(dg), l, m, element)
+ grad[v, nnodes(dg), l, m, element] = (grad[v, nnodes(dg), l, m,
+ element] +
+ surface_flux_values[v, l, m,
+ 2,
+ element] *
+ factor_2 *
+ normal_direction[dim])
+
+ # surface at -y
+ normal_direction = get_normal_direction(3, contravariant_vectors,
+ l, m, 1, element)
+ grad[v, l, 1, m, element] = (grad[v, l, 1, m, element] +
+ surface_flux_values[v, l, m, 3,
+ element] *
+ factor_1 * normal_direction[dim])
+
+ # surface at +y
+ normal_direction = get_normal_direction(4, contravariant_vectors,
+ l, nnodes(dg), m, element)
+ grad[v, l, nnodes(dg), m, element] = (grad[v, l, nnodes(dg), m,
+ element] +
+ surface_flux_values[v, l, m,
+ 4,
+ element] *
+ factor_2 *
+ normal_direction[dim])
+
+ # surface at -z
+ normal_direction = get_normal_direction(5, contravariant_vectors,
+ l, m, 1, element)
+ grad[v, l, m, 1, element] = (grad[v, l, m, 1, element] +
+ surface_flux_values[v, l, m, 5,
+ element] *
+ factor_1 * normal_direction[dim])
+
+ # surface at +z
+ normal_direction = get_normal_direction(6, contravariant_vectors,
+ l, m, nnodes(dg), element)
+ grad[v, l, m, nnodes(dg), element] = (grad[v, l, m, nnodes(dg),
+ element] +
+ surface_flux_values[v, l, m,
+ 6,
+ element] *
+ factor_2 *
+ normal_direction[dim])
+ end
+ end
+ end
+ end
+ end
+
+ # Apply Jacobian from mapping to reference element
+ @trixi_timeit timer() "Jacobian" begin
+ apply_jacobian_parabolic!(gradients_x, mesh, equations_parabolic, dg,
+ cache_parabolic)
+ apply_jacobian_parabolic!(gradients_y, mesh, equations_parabolic, dg,
+ cache_parabolic)
+ apply_jacobian_parabolic!(gradients_z, mesh, equations_parabolic, dg,
+ cache_parabolic)
+ end
+
+ return nothing
+end
+
+# This version is used for parabolic gradient computations
+@inline function calc_interface_flux!(surface_flux_values, mesh::P4estMesh{3},
+ nonconservative_terms::False,
+ equations::AbstractEquationsParabolic,
+ surface_integral, dg::DG, cache,
+ interface_index, normal_direction,
+ primary_i_node_index, primary_j_node_index,
+ primary_direction_index, primary_element_index,
+ secondary_i_node_index, secondary_j_node_index,
+ secondary_direction_index,
+ secondary_element_index)
+ @unpack u = cache.interfaces
+ @unpack surface_flux = surface_integral
+
+ u_ll, u_rr = get_surface_node_vars(u, equations, dg, primary_i_node_index,
+ primary_j_node_index,
+ interface_index)
+
+ flux_ = 0.5 * (u_ll + u_rr) # we assume that the gradient computations utilize a central flux
+
+ # Note that we don't flip the sign on the secondondary flux. This is because for parabolic terms,
+ # the normals are not embedded in `flux_` for the parabolic gradient computations.
+ for v in eachvariable(equations)
+ surface_flux_values[v, primary_i_node_index, primary_j_node_index, primary_direction_index, primary_element_index] = flux_[v]
+ surface_flux_values[v, secondary_i_node_index, secondary_j_node_index, secondary_direction_index, secondary_element_index] = flux_[v]
+ end
+end
+
+# This is the version used when calculating the divergence of the viscous fluxes
+function calc_volume_integral!(du, flux_viscous,
+ mesh::P4estMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ dg::DGSEM, cache)
+ (; derivative_dhat) = dg.basis
+ (; contravariant_vectors) = cache.elements
+ flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous
+
+ @threaded for element in eachelement(dg, cache)
+ # Calculate volume terms in one element
+ for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
+ flux1 = get_node_vars(flux_viscous_x, equations_parabolic, dg, i, j, k, element)
+ flux2 = get_node_vars(flux_viscous_y, equations_parabolic, dg, i, j, k, element)
+ flux3 = get_node_vars(flux_viscous_z, equations_parabolic, dg, i, j, k, element)
+
+ # Compute the contravariant flux by taking the scalar product of the
+ # first contravariant vector Ja^1 and the flux vector
+ Ja11, Ja12, Ja13 = get_contravariant_vector(1, contravariant_vectors, i, j, k,
+ element)
+ contravariant_flux1 = Ja11 * flux1 + Ja12 * flux2 + Ja13 * flux3
+ for ii in eachnode(dg)
+ multiply_add_to_node_vars!(du, derivative_dhat[ii, i], contravariant_flux1,
+ equations_parabolic, dg, ii, j, k, element)
+ end
+
+ # Compute the contravariant flux by taking the scalar product of the
+ # second contravariant vector Ja^2 and the flux vector
+ Ja21, Ja22, Ja23 = get_contravariant_vector(2, contravariant_vectors, i, j, k,
+ element)
+ contravariant_flux2 = Ja21 * flux1 + Ja22 * flux2 + Ja23 * flux3
+ for jj in eachnode(dg)
+ multiply_add_to_node_vars!(du, derivative_dhat[jj, j], contravariant_flux2,
+ equations_parabolic, dg, i, jj, k, element)
+ end
+
+ # Compute the contravariant flux by taking the scalar product of the
+ # second contravariant vector Ja^2 and the flux vector
+ Ja31, Ja32, Ja33 = get_contravariant_vector(3, contravariant_vectors, i, j, k,
+ element)
+ contravariant_flux3 = Ja31 * flux1 + Ja32 * flux2 + Ja33 * flux3
+ for kk in eachnode(dg)
+ multiply_add_to_node_vars!(du, derivative_dhat[kk, k], contravariant_flux3,
+ equations_parabolic, dg, i, j, kk, element)
+ end
+ end
+ end
+
+ return nothing
+end
+
+# This is the version used when calculating the divergence of the viscous fluxes
+# We pass the `surface_integral` argument solely for dispatch
+function prolong2interfaces!(cache_parabolic, flux_viscous,
+ mesh::P4estMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ surface_integral, dg::DG, cache)
+ (; interfaces) = cache_parabolic
+ (; contravariant_vectors) = cache_parabolic.elements
+ index_range = eachnode(dg)
+ flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous
+
+ @threaded for interface in eachinterface(dg, cache)
+ # Copy solution data from the primary element using "delayed indexing" with
+ # a start value and a step size to get the correct face and orientation.
+ # Note that in the current implementation, the interface will be
+ # "aligned at the primary element", i.e., the index of the primary side
+ # will always run forwards.
+ primary_element = interfaces.neighbor_ids[1, interface]
+ primary_indices = interfaces.node_indices[1, interface]
+ primary_direction = indices2direction(primary_indices)
+
+ i_primary_start, i_primary_step_i, i_primary_step_j = index_to_start_step_3d(primary_indices[1],
+ index_range)
+ j_primary_start, j_primary_step_i, j_primary_step_j = index_to_start_step_3d(primary_indices[2],
+ index_range)
+ k_primary_start, k_primary_step_i, k_primary_step_j = index_to_start_step_3d(primary_indices[3],
+ index_range)
+
+ i_primary = i_primary_start
+ j_primary = j_primary_start
+ k_primary = k_primary_start
+
+ for j in eachnode(dg)
+ for i in eachnode(dg)
+ # this is the outward normal direction on the primary element
+ normal_direction = get_normal_direction(primary_direction,
+ contravariant_vectors,
+ i_primary, j_primary, k_primary,
+ primary_element)
+
+ for v in eachvariable(equations_parabolic)
+ # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
+ flux_viscous = SVector(flux_viscous_x[v, i_primary, j_primary,
+ k_primary,
+ primary_element],
+ flux_viscous_y[v, i_primary, j_primary,
+ k_primary,
+ primary_element],
+ flux_viscous_z[v, i_primary, j_primary,
+ k_primary,
+ primary_element])
+
+ interfaces.u[1, v, i, j, interface] = dot(flux_viscous,
+ normal_direction)
+ end
+ i_primary += i_primary_step_i
+ j_primary += j_primary_step_i
+ k_primary += k_primary_step_i
+ end
+ i_primary += i_primary_step_j
+ j_primary += j_primary_step_j
+ k_primary += k_primary_step_j
+ end
+
+ # Copy solution data from the secondary element using "delayed indexing" with
+ # a start value and a step size to get the correct face and orientation.
+ secondary_element = interfaces.neighbor_ids[2, interface]
+ secondary_indices = interfaces.node_indices[2, interface]
+ secondary_direction = indices2direction(secondary_indices)
+
+ i_secondary_start, i_secondary_step_i, i_secondary_step_j = index_to_start_step_3d(secondary_indices[1],
+ index_range)
+ j_secondary_start, j_secondary_step_i, j_secondary_step_j = index_to_start_step_3d(secondary_indices[2],
+ index_range)
+ k_secondary_start, k_secondary_step_i, k_secondary_step_j = index_to_start_step_3d(secondary_indices[3],
+ index_range)
+
+ i_secondary = i_secondary_start
+ j_secondary = j_secondary_start
+ k_secondary = k_secondary_start
+ for j in eachnode(dg)
+ for i in eachnode(dg)
+ # This is the outward normal direction on the secondary element.
+ # Here, we assume that normal_direction on the secondary element is
+ # the negative of normal_direction on the primary element.
+ normal_direction = get_normal_direction(secondary_direction,
+ contravariant_vectors,
+ i_secondary, j_secondary,
+ k_secondary,
+ secondary_element)
+
+ for v in eachvariable(equations_parabolic)
+ # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
+ flux_viscous = SVector(flux_viscous_x[v, i_secondary, j_secondary,
+ k_secondary,
+ secondary_element],
+ flux_viscous_y[v, i_secondary, j_secondary,
+ k_secondary,
+ secondary_element],
+ flux_viscous_z[v, i_secondary, j_secondary,
+ k_secondary,
+ secondary_element])
+ # store the normal flux with respect to the primary normal direction
+ interfaces.u[2, v, i, j, interface] = -dot(flux_viscous,
+ normal_direction)
+ end
+ i_secondary += i_secondary_step_i
+ j_secondary += j_secondary_step_i
+ k_secondary += k_secondary_step_i
+ end
+ i_secondary += i_secondary_step_j
+ j_secondary += j_secondary_step_j
+ k_secondary += k_secondary_step_j
+ end
+ end
+
+ return nothing
+end
+
+# This version is used for divergence flux computations
+function calc_interface_flux!(surface_flux_values,
+ mesh::P4estMesh{3}, equations_parabolic,
+ dg::DG, cache_parabolic)
+ (; neighbor_ids, node_indices) = cache_parabolic.interfaces
+ index_range = eachnode(dg)
+
+ @threaded for interface in eachinterface(dg, cache_parabolic)
+ # Get element and side index information on the primary element
+ primary_element = neighbor_ids[1, interface]
+ primary_indices = node_indices[1, interface]
+ primary_direction_index = indices2direction(primary_indices)
+
+ i_primary_start, i_primary_step_i, i_primary_step_j = index_to_start_step_3d(primary_indices[1],
+ index_range)
+ j_primary_start, j_primary_step_i, j_primary_step_j = index_to_start_step_3d(primary_indices[2],
+ index_range)
+ k_primary_start, k_primary_step_i, k_primary_step_j = index_to_start_step_3d(primary_indices[3],
+ index_range)
+
+ i_primary = i_primary_start
+ j_primary = j_primary_start
+ k_primary = k_primary_start
+
+ # Get element and side index information on the secondary element
+ secondary_element = neighbor_ids[2, interface]
+ secondary_indices = node_indices[2, interface]
+ secondary_direction_index = indices2direction(secondary_indices)
+ secondary_surface_indices = surface_indices(secondary_indices)
+
+ # Initiate the secondary index to be used in the surface for loop.
+ # This index on the primary side will always run forward but
+ # the secondary index might need to run backwards for flipped sides.
+ # Get the surface indexing on the secondary element.
+ # Note that the indices of the primary side will always run forward but
+ # the secondary indices might need to run backwards for flipped sides.
+ i_secondary_start, i_secondary_step_i, i_secondary_step_j = index_to_start_step_3d(secondary_surface_indices[1],
+ index_range)
+ j_secondary_start, j_secondary_step_i, j_secondary_step_j = index_to_start_step_3d(secondary_surface_indices[2],
+ index_range)
+ i_secondary = i_secondary_start
+ j_secondary = j_secondary_start
+
+ for j in eachnode(dg)
+ for i in eachnode(dg)
+ # We prolong the viscous flux dotted with respect the outward normal on the
+ # primary element. We assume a BR-1 type of flux.
+ viscous_flux_normal_ll, viscous_flux_normal_rr = get_surface_node_vars(cache_parabolic.interfaces.u,
+ equations_parabolic,
+ dg,
+ i, j,
+ interface)
+
+ flux = 0.5 * (viscous_flux_normal_ll + viscous_flux_normal_rr)
+
+ for v in eachvariable(equations_parabolic)
+ surface_flux_values[v, i, j, primary_direction_index, primary_element] = flux[v]
+ surface_flux_values[v, i_secondary, j_secondary, secondary_direction_index, secondary_element] = -flux[v]
+ end
+
+ # Increment the primary element indices
+ i_primary += i_primary_step_i
+ j_primary += j_primary_step_i
+ k_primary += k_primary_step_i
+ # Increment the secondary element surface indices
+ i_secondary += i_secondary_step_i
+ j_secondary += j_secondary_step_i
+ end
+ # Increment the primary element indices
+ i_primary += i_primary_step_j
+ j_primary += j_primary_step_j
+ k_primary += k_primary_step_j
+ # Increment the secondary element surface indices
+ i_secondary += i_secondary_step_j
+ j_secondary += j_secondary_step_j
+ end
+ end
+
+ return nothing
+end
+
+# TODO: parabolic, finish implementing `calc_boundary_flux_gradients!` and `calc_boundary_flux_divergence!`
+function prolong2boundaries!(cache_parabolic, flux_viscous,
+ mesh::P4estMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ surface_integral, dg::DG, cache)
+ (; boundaries) = cache_parabolic
+ (; contravariant_vectors) = cache_parabolic.elements
+ index_range = eachnode(dg)
+
+ flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous
+
+ @threaded for boundary in eachboundary(dg, cache_parabolic)
+ # Copy solution data from the element using "delayed indexing" with
+ # a start value and a step size to get the correct face and orientation.
+ element = boundaries.neighbor_ids[boundary]
+ node_indices = boundaries.node_indices[boundary]
+ direction = indices2direction(node_indices)
+
+ i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(node_indices[1],
+ index_range)
+ j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(node_indices[2],
+ index_range)
+ k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(node_indices[3],
+ index_range)
+
+ i_node = i_node_start
+ j_node = j_node_start
+ k_node = k_node_start
+
+ for j in eachnode(dg)
+ for i in eachnode(dg)
+ # this is the outward normal direction on the primary element
+ normal_direction = get_normal_direction(direction, contravariant_vectors,
+ i_node, j_node, k_node, element)
+
+ for v in eachvariable(equations_parabolic)
+ flux_viscous = SVector(flux_viscous_x[v, i_node, j_node, k_node,
+ element],
+ flux_viscous_y[v, i_node, j_node, k_node,
+ element],
+ flux_viscous_z[v, i_node, j_node, k_node,
+ element])
+
+ boundaries.u[v, i, j, boundary] = dot(flux_viscous, normal_direction)
+ end
+ i_node += i_node_step_i
+ j_node += j_node_step_i
+ k_node += k_node_step_i
+ end
+ i_node += i_node_step_j
+ j_node += j_node_step_j
+ k_node += k_node_step_j
+ end
+ end
+ return nothing
+end
+
+# # Function barrier for type stability
+# !!! TODO: Figure out why this cannot removed eventhough it exists in the dg_2d_parabolic.jl file
+function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh,
+ equations, surface_integral, dg::DG)
+ (; boundary_condition_types, boundary_indices) = boundary_conditions
+
+ calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
+ Gradient(), mesh, equations, surface_integral, dg)
+ return nothing
+end
+
+function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh,
+ equations, surface_integral, dg::DG)
+ (; boundary_condition_types, boundary_indices) = boundary_conditions
+
+ calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
+ Divergence(), mesh, equations, surface_integral, dg)
+ return nothing
+end
+
+# Iterate over tuples of boundary condition types and associated indices
+# in a type-stable way using "lispy tuple programming".
+function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any},
+ BC_indices::NTuple{N, Vector{Int}},
+ operator_type,
+ mesh::P4estMesh,
+ equations, surface_integral, dg::DG) where {N}
+ # Extract the boundary condition type and index vector
+ boundary_condition = first(BCs)
+ boundary_condition_indices = first(BC_indices)
+ # Extract the remaining types and indices to be processed later
+ remaining_boundary_conditions = Base.tail(BCs)
+ remaining_boundary_condition_indices = Base.tail(BC_indices)
+
+ # process the first boundary condition type
+ calc_boundary_flux!(cache, t, boundary_condition, boundary_condition_indices,
+ operator_type, mesh, equations, surface_integral, dg)
+
+ # recursively call this method with the unprocessed boundary types
+ calc_boundary_flux_by_type!(cache, t, remaining_boundary_conditions,
+ remaining_boundary_condition_indices,
+ operator_type,
+ mesh, equations, surface_integral, dg)
+
+ return nothing
+end
+
+# terminate the type-stable iteration over tuples
+function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{},
+ operator_type, mesh::P4estMesh, equations,
+ surface_integral, dg::DG)
+ nothing
+end
+
+function calc_boundary_flux!(cache, t,
+ boundary_condition_parabolic, # works with Dict types
+ boundary_condition_indices,
+ operator_type, mesh::P4estMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ surface_integral, dg::DG)
+ (; boundaries) = cache
+ (; node_coordinates, surface_flux_values) = cache.elements
+ (; contravariant_vectors) = cache.elements
+ index_range = eachnode(dg)
+
+ @threaded for local_index in eachindex(boundary_condition_indices)
+ # Use the local index to get the global boundary index from the pre-sorted list
+ boundary_index = boundary_condition_indices[local_index]
+
+ # Get information on the adjacent element, compute the surface fluxes,
+ # and store them
+ element = boundaries.neighbor_ids[boundary_index]
+ node_indices = boundaries.node_indices[boundary_index]
+ direction_index = indices2direction(node_indices)
+
+ i_node_start, i_node_step_i, i_node_step_j = index_to_start_step_3d(node_indices[1],
+ index_range)
+ j_node_start, j_node_step_i, j_node_step_j = index_to_start_step_3d(node_indices[2],
+ index_range)
+ k_node_start, k_node_step_i, k_node_step_j = index_to_start_step_3d(node_indices[3],
+ index_range)
+
+ i_node = i_node_start
+ j_node = j_node_start
+ k_node = k_node_start
+
+ for j in eachnode(dg)
+ for i in eachnode(dg)
+ # Extract solution data from boundary container
+ u_inner = get_node_vars(boundaries.u, equations_parabolic, dg, i, j,
+ boundary_index)
+
+ # Outward-pointing normal direction (not normalized)
+ normal_direction = get_normal_direction(direction_index,
+ contravariant_vectors,
+ i_node, j_node, k_node, element)
+
+ # TODO: revisit if we want more general boundary treatments.
+ # This assumes the gradient numerical flux at the boundary is the gradient variable,
+ # which is consistent with BR1, LDG.
+ flux_inner = u_inner
+
+ # Coordinates at boundary node
+ x = get_node_coords(node_coordinates, equations_parabolic, dg, i_node,
+ j_node, k_node,
+ element)
+
+ flux_ = boundary_condition_parabolic(flux_inner, u_inner, normal_direction,
+ x, t, operator_type,
+ equations_parabolic)
+
+ # Copy flux to element storage in the correct orientation
+ for v in eachvariable(equations_parabolic)
+ surface_flux_values[v, i, j, direction_index, element] = flux_[v]
+ end
+
+ i_node += i_node_step_i
+ j_node += j_node_step_i
+ k_node += k_node_step_i
+ end
+ i_node += i_node_step_j
+ j_node += j_node_step_j
+ k_node += k_node_step_j
+ end
+ end
+end
diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
index d6d74637021..5b63b971cd8 100644
--- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
@@ -13,7 +13,7 @@
# 2. compute f(u, grad(u))
# 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call)
# boundary conditions will be applied to both grad(u) and div(f(u, grad(u))).
-function rhs_parabolic!(du, u, t, mesh::TreeMesh{3},
+function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations_parabolic::AbstractEquationsParabolic,
initial_condition, boundary_conditions_parabolic, source_terms,
dg::DG, parabolic_scheme, cache, cache_parabolic)
@@ -105,7 +105,7 @@ end
# Transform solution variables prior to taking the gradient
# (e.g., conservative to primitive variables). Defaults to doing nothing.
# TODO: can we avoid copying data?
-function transform_variables!(u_transformed, u, mesh::TreeMesh{3},
+function transform_variables!(u_transformed, u, mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations_parabolic::AbstractEquationsParabolic,
dg::DG, parabolic_scheme, cache, cache_parabolic)
@threaded for element in eachelement(dg, cache)
@@ -325,7 +325,8 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
return nothing
end
-function calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh::TreeMesh{3},
+function calc_viscous_fluxes!(flux_viscous, gradients, u_transformed,
+ mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations_parabolic::AbstractEquationsParabolic,
dg::DG, cache, cache_parabolic)
gradients_x, gradients_y, gradients_z = gradients
@@ -379,7 +380,7 @@ end
function calc_boundary_flux_gradients!(cache, t,
boundary_conditions_parabolic::BoundaryConditionPeriodic,
- mesh::TreeMesh{3},
+ mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG)
return nothing
@@ -387,7 +388,7 @@ end
function calc_boundary_flux_divergence!(cache, t,
boundary_conditions_parabolic::BoundaryConditionPeriodic,
- mesh::TreeMesh{3},
+ mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG)
return nothing
@@ -806,7 +807,7 @@ end
# This is because the parabolic fluxes are assumed to be of the form
# `du/dt + df/dx = dg/dx + source(x,t)`,
# where f(u) is the inviscid flux and g(u) is the viscous flux.
-function apply_jacobian_parabolic!(du, mesh::TreeMesh{3},
+function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations::AbstractEquationsParabolic, dg::DG, cache)
@threaded for element in eachelement(dg, cache)
factor = cache.elements.inverse_jacobian[element]
diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl
index 1ae5eed44ae..67a27238969 100644
--- a/test/test_parabolic_3d.jl
+++ b/test/test_parabolic_3d.jl
@@ -86,9 +86,24 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
-end
+ @trixi_testset "P4estMesh3D: elixir_navierstokes_convergence.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_3d_dgsem", "elixir_navierstokes_convergence.jl"),
+ initial_refinement_level = 2, tspan=(0.0, 0.1),
+ l2 = [0.00026599105554982194, 0.000461877794472316, 0.0005424899076052261, 0.0004618777944723191, 0.0015846392581126832],
+ linf = [0.0025241668929956163, 0.006308461681816373, 0.004334939663169113, 0.006308461681804009, 0.03176343480493493]
+ )
+ end
+ @trixi_testset "P4estMesh3D: elixir_navierstokes_taylor_green_vortex.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"),
+ initial_refinement_level = 2, tspan=(0.0, 0.25),
+ l2 = [0.0001547509861140407, 0.015637861347119624, 0.015637861347119687, 0.022024699158522523, 0.009711013505930812],
+ linf = [0.0006696415247340326, 0.03442565722527785, 0.03442565722577423, 0.06295407168705314, 0.032857472756916195]
+ )
+ end
+
+end
# Clean up afterwards: delete Trixi.jl output directory
@test_nowarn isdir(outdir) && rm(outdir, recursive=true)
-end # module
+end # module
\ No newline at end of file
From d7ea40b19b98cc18d18e5f047131f141d3c08acc Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 25 Jul 2023 18:42:12 +0200
Subject: [PATCH 052/263] reset threads also when initializing the summary
callback (#1587)
* reset threads also when initializing the summary callback
I added the option to reset the threads from Polyester.jl in also in the summary callback.
The idea is that this supports another development workflow where we just modify the RHS
implementation and call solve again without re-ccreating the ODE.
The same comment as in 036eaed82b92be9376c5b610d8d40eddf45ca1fa applies:
However, I did not document it in the docstring since we have not documented
that we use Polyester.jl threads in general - and the resetting is specific
to Polyester.jl. I was not sure whether we still would like to keep the option
to change the threading backend any time - although I do not see a good reason
why we should do so.
---
src/callbacks_step/summary.jl | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/callbacks_step/summary.jl b/src/callbacks_step/summary.jl
index 08e13d0b98d..26981a58b73 100644
--- a/src/callbacks_step/summary.jl
+++ b/src/callbacks_step/summary.jl
@@ -15,10 +15,14 @@ Create and return a callback that prints a human-readable summary of the simulat
beginning of a simulation and then resets the timer. When the returned callback is executed
directly, the current timer values are shown.
"""
-function SummaryCallback()
+function SummaryCallback(reset_threads = true)
+ function initialize(cb, u, t, integrator)
+ initialize_summary_callback(cb, u, t, integrator;
+ reset_threads)
+ end
DiscreteCallback(summary_callback, summary_callback,
save_positions = (false, false),
- initialize = initialize_summary_callback)
+ initialize = initialize)
end
function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:typeof(summary_callback)})
@@ -139,7 +143,15 @@ end
# Print information about the current simulation setup
# Note: This is called *after* all initialization is done, but *before* the first time step
-function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator)
+function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator;
+ reset_threads = true)
+ # Optionally reset Polyester.jl threads. See
+ # https://github.com/trixi-framework/Trixi.jl/issues/1583
+ # https://github.com/JuliaSIMD/Polyester.jl/issues/30
+ if reset_threads
+ Polyester.reset_threads!()
+ end
+
mpi_isroot() || return nothing
print_startup_message()
From 53a826b62241fc0f58c0a3cd0a0acc1789a79509 Mon Sep 17 00:00:00 2001
From: Arpit Babbar
Date: Wed, 26 Jul 2023 10:47:07 +0530
Subject: [PATCH 053/263] Timestep stamp in mesh file (#1580)
* Timestep stamp in mesh file
* Update src/callbacks_step/save_solution.jl
Fixes other mesh type issue
Co-authored-by: Hendrik Ranocha
* Add test for multiple mesh files
* Keep within pre-existing tests
---------
Co-authored-by: Hendrik Ranocha
---
src/callbacks_step/save_solution.jl | 9 ++++++++-
test/test_mpi_tree.jl | 6 ++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl
index 1fe0d6b1e15..14ea33368f8 100644
--- a/src/callbacks_step/save_solution.jl
+++ b/src/callbacks_step/save_solution.jl
@@ -155,7 +155,14 @@ function save_mesh(semi::AbstractSemidiscretization, output_directory, timestep
mesh, _, _, _ = mesh_equations_solver_cache(semi)
if mesh.unsaved_changes
- mesh.current_filename = save_mesh_file(mesh, output_directory)
+ # We only append the time step number to the mesh file name if it has
+ # changed during the simulation due to AMR. We do not append it for
+ # the first time step.
+ if timestep == 0
+ mesh.current_filename = save_mesh_file(mesh, output_directory)
+ else
+ mesh.current_filename = save_mesh_file(mesh, output_directory, timestep)
+ end
mesh.unsaved_changes = false
end
end
diff --git a/test/test_mpi_tree.jl b/test/test_mpi_tree.jl
index 84d2609cbb1..8403fcf1b04 100644
--- a/test/test_mpi_tree.jl
+++ b/test/test_mpi_tree.jl
@@ -55,10 +55,16 @@ CI_ON_WINDOWS = (get(ENV, "GITHUB_ACTIONS", false) == "true") && Sys.iswindows()
# Linear scalar advection with AMR
# These example files are only for testing purposes and have no practical use
@trixi_testset "elixir_advection_amr_refine_twice.jl" begin
+ # Here, we also test that SaveSolutionCallback prints multiple mesh files with AMR
+ # Start with a clean environment: remove Trixi.jl output directory if it exists
+ outdir = "out"
+ isdir(outdir) && rm(outdir, recursive=true)
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_amr_refine_twice.jl"),
l2 = [0.00020547512522578292],
linf = [0.007831753383083506],
coverage_override = (maxiters=6,))
+ meshfiles = filter(file -> endswith(file,".h5") && startswith(file,"mesh"), readdir(outdir))
+ @test length(meshfiles) > 1
end
@trixi_testset "elixir_advection_amr_coarsen_twice.jl" begin
From fe0e78c658283db21e581bbbc6d48d0adcf39510 Mon Sep 17 00:00:00 2001
From: Johannes Markert <10619309+jmark@users.noreply.github.com>
Date: Wed, 26 Jul 2023 10:47:54 +0200
Subject: [PATCH 054/263] Feature: t8code as meshing backend (#1426)
* Initial commit for the new feature using t8code as meshing backend.
* Delete t8code_2d_dgsem
* Added new examples and tests. Testing updates for T8code.jl.
* Worked in the comments.
* Fixed spelling.
* Update src/auxiliary/auxiliary.jl
Co-authored-by: Hendrik Ranocha
* Added whitespace in Unions.
* Adapted commented out code block reporting the no. of elements per level.
* Added dummy save mesh support for .
* Added test .
* Added to method signature.
* Deleted unnecessary comments.
* Removed commented out tests.
* Fixed Morton ordering bug in 2D at mortar interfaces.
* Disabled `save_solution` callbacks and added more tests.
* Added more tests.
* Updated code according to the review.
* Update src/auxiliary/t8code.jl
Co-authored-by: Hendrik Ranocha
* Update src/auxiliary/t8code.jl
Co-authored-by: Hendrik Ranocha
* Update src/auxiliary/t8code.jl
Co-authored-by: Hendrik Ranocha
* Update src/auxiliary/t8code.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Update src/solvers/dgsem_t8code/containers_2d.jl
Co-authored-by: Hendrik Ranocha
* Update src/meshes/t8code_mesh.jl
Co-authored-by: Hendrik Ranocha
* Code cleanup.
* Updated to T8code@0.3.0
* Fixing minor issues.
* Fixed typo.
* Code cleanup.
* Enabled `set_ghost` in examples.
* Generalized type info in function signature.
* Added namespace qualifier.
* Updated comments.
* Refactored code and deleted lots of it.
* Removed a copy operation.
* Fixed some merging issues and formatting.
* Fixed spelling.
* Fixed spelling and changed assert macro.
* Applied automatic formatting.
* Backup.
* Removed superfluous outer constructor for T8codeMesh.
* Added return statement for consistency.
* Fixed wrong indentation by autoformatter.
* Added comments.
* Made sure an exception is thrown.
* Changed flags for sc_init for t8code initialization.
* Updated formatting.
* Workaround for error about calling MPI routines after MPI has been finalized.
* Upped to T8code v0.4.1.
* Added mpi_finailize_hook for proper memory cleanup.
* Added t8code to test_threaded.jl
* Added a `save_mesh_file` call in order to satisfy code coverage.
* Improved finalizer logic for T8coeMesh.
* Refined code.
* Restructured to do blocks.
* Moved save_mesh_file call to test file.
* Fixed spelling error.
---------
Co-authored-by: Johannes Markert
Co-authored-by: Hendrik Ranocha
---
.github/workflows/ci.yml | 1 +
Project.toml | 2 +
...ixir_advection_amr_solution_independent.jl | 143 ++++++
.../elixir_advection_amr_unstructured_flag.jl | 87 ++++
.../t8code_2d_dgsem/elixir_advection_basic.jl | 59 +++
.../elixir_advection_nonconforming_flag.jl | 109 ++++
.../elixir_advection_unstructured_flag.jl | 81 +++
.../elixir_euler_free_stream.jl | 122 +++++
.../t8code_2d_dgsem/elixir_euler_sedov.jl | 97 ++++
.../elixir_euler_shockcapturing_ec.jl | 68 +++
...e_terms_nonconforming_unstructured_flag.jl | 122 +++++
.../elixir_eulergravity_convergence.jl | 77 +++
.../t8code_2d_dgsem/elixir_mhd_alfven_wave.jl | 60 +++
examples/t8code_2d_dgsem/elixir_mhd_rotor.jl | 134 +++++
.../elixir_shallowwater_source_terms.jl | 60 +++
src/Trixi.jl | 5 +-
src/auxiliary/t8code.jl | 486 ++++++++++++++++++
src/callbacks_step/amr.jl | 63 +++
src/callbacks_step/amr_dg2d.jl | 72 ++-
src/callbacks_step/analysis.jl | 30 ++
src/callbacks_step/analysis_dg2d.jl | 16 +-
src/callbacks_step/save_restart_dg.jl | 3 +-
src/callbacks_step/save_solution_dg.jl | 3 +-
src/callbacks_step/stepsize_dg2d.jl | 8 +-
src/meshes/mesh_io.jl | 9 +-
src/meshes/meshes.jl | 1 +
src/meshes/t8code_mesh.jl | 345 +++++++++++++
src/solvers/dg.jl | 4 +-
src/solvers/dgsem_p4est/containers.jl | 9 +-
src/solvers/dgsem_p4est/containers_2d.jl | 5 +-
src/solvers/dgsem_p4est/dg_2d.jl | 33 +-
src/solvers/dgsem_structured/dg_2d.jl | 19 +-
src/solvers/dgsem_t8code/containers.jl | 60 +++
src/solvers/dgsem_t8code/containers_2d.jl | 58 +++
src/solvers/dgsem_t8code/dg.jl | 31 ++
src/solvers/dgsem_tree/dg_2d.jl | 22 +-
src/solvers/dgsem_tree/indicators_2d.jl | 3 +-
src/solvers/dgsem_unstructured/dg_2d.jl | 10 +-
test/runtests.jl | 220 ++++----
test/test_t8code_2d.jl | 182 +++++++
test/test_threaded.jl | 16 +
41 files changed, 2767 insertions(+), 168 deletions(-)
create mode 100644 examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_advection_basic.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_euler_free_stream.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_euler_sedov.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_mhd_rotor.jl
create mode 100644 examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl
create mode 100644 src/auxiliary/t8code.jl
create mode 100644 src/meshes/t8code_mesh.jl
create mode 100644 src/solvers/dgsem_t8code/containers.jl
create mode 100644 src/solvers/dgsem_t8code/containers_2d.jl
create mode 100644 src/solvers/dgsem_t8code/dg.jl
create mode 100644 test/test_t8code_2d.jl
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b0a2c93db3c..4790f93d913 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -69,6 +69,7 @@ jobs:
- structured
- p4est_part1
- p4est_part2
+ - t8code_part1
- unstructured_dgmulti
- parabolic
- paper_self_gravitating_gas_dynamics
diff --git a/Project.toml b/Project.toml
index 94c47a35ac1..db410317851 100644
--- a/Project.toml
+++ b/Project.toml
@@ -37,6 +37,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StrideArrays = "d1fa6d79-ef01-42a6-86c9-f7c551f8593b"
StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
SummationByPartsOperators = "9f78cca6-572e-554e-b819-917d2f1cf240"
+T8code = "d0cc0030-9a40-4274-8435-baadcfd54fa1"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
Triangulate = "f7e6ffb2-c36d-4f8f-a77e-16e897189344"
TriplotBase = "981d1d27-644d-49a2-9326-4793e63143c3"
@@ -80,6 +81,7 @@ StaticArrays = "1"
StrideArrays = "0.1.18"
StructArrays = "0.6"
SummationByPartsOperators = "0.5.41"
+T8code = "0.4.1"
TimerOutputs = "0.5"
Triangulate = "2.0"
TriplotBase = "0.1"
diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl
new file mode 100644
index 00000000000..653bab41e2d
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_advection_amr_solution_independent.jl
@@ -0,0 +1,143 @@
+using OrdinaryDiffEq
+using Trixi
+
+# Define new structs inside a module to allow re-evaluating the file.
+module TrixiExtension
+
+using Trixi
+
+struct IndicatorSolutionIndependent{Cache <: NamedTuple} <: Trixi.AbstractIndicator
+ cache::Cache
+end
+
+function IndicatorSolutionIndependent(semi)
+ basis = semi.solver.basis
+ alpha = Vector{real(basis)}()
+ cache = (; semi.mesh, alpha)
+ return IndicatorSolutionIndependent{typeof(cache)}(cache)
+end
+
+function (indicator::IndicatorSolutionIndependent)(u::AbstractArray{<:Any, 4},
+ mesh, equations, dg, cache;
+ t, kwargs...)
+ mesh = indicator.cache.mesh
+ alpha = indicator.cache.alpha
+ resize!(alpha, nelements(dg, cache))
+
+ # Predict the theoretical center.
+ advection_velocity = (0.2, -0.7)
+ center = t .* advection_velocity
+
+ inner_distance = 1
+ outer_distance = 1.85
+
+ # Iterate over all elements.
+ for element in 1:length(alpha)
+ # Calculate periodic distance between cell and center.
+ # This requires an uncurved mesh!
+ coordinates = SVector(0.5 * (cache.elements.node_coordinates[1, 1, 1, element] +
+ cache.elements.node_coordinates[1, end, 1, element]),
+ 0.5 * (cache.elements.node_coordinates[2, 1, 1, element] +
+ cache.elements.node_coordinates[2, 1, end, element]))
+
+ # The geometric shape of the amr should be preserved when the base_level is increased.
+ # This is done by looking at the original coordinates of each cell.
+ cell_coordinates = original_coordinates(coordinates, 5 / 8)
+ cell_distance = periodic_distance_2d(cell_coordinates, center, 10)
+ if cell_distance < (inner_distance + outer_distance) / 2
+ cell_coordinates = original_coordinates(coordinates, 5 / 16)
+ cell_distance = periodic_distance_2d(cell_coordinates, center, 10)
+ end
+
+ # Set alpha according to cells position inside the circles.
+ target_level = (cell_distance < inner_distance) + (cell_distance < outer_distance)
+ alpha[element] = target_level / 2
+ end
+ return alpha
+end
+
+# For periodic domains, distance between two points must take into account
+# periodic extensions of the domain.
+function periodic_distance_2d(coordinates, center, domain_length)
+ dx = coordinates .- center
+ dx_shifted = abs.(dx .% domain_length)
+ dx_periodic = min.(dx_shifted, domain_length .- dx_shifted)
+ return sqrt(sum(dx_periodic .^ 2))
+end
+
+# This takes a cells coordinates and transforms them into the coordinates of a
+# parent-cell it originally refined from. It does it so that the parent-cell
+# has given cell_length.
+function original_coordinates(coordinates, cell_length)
+ offset = coordinates .% cell_length
+ offset_sign = sign.(offset)
+ border = coordinates - offset
+ center = border + (offset_sign .* cell_length / 2)
+ return center
+end
+
+end # module TrixiExtension
+
+import .TrixiExtension
+
+###############################################################################
+# Semidiscretization of the linear advection equation.
+
+advection_velocity = (0.2, -0.7)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+
+initial_condition = initial_condition_gauss
+
+solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
+
+coordinates_min = (-5.0, -5.0)
+coordinates_max = (5.0, 5.0)
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+trees_per_dimension = (1, 1)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg = 3,
+ mapping = mapping,
+ initial_refinement_level = 1)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
+ extra_analysis_integrals = (entropy,))
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+amr_controller = ControllerThreeLevel(semi,
+ TrixiExtension.IndicatorSolutionIndependent(semi),
+ base_level = 4,
+ med_level = 5, med_threshold = 0.1,
+ max_level = 6, max_threshold = 0.6)
+
+amr_callback = AMRCallback(semi, amr_controller,
+ interval = 5,
+ adapt_initial_condition = true,
+ adapt_initial_condition_only_refine = true)
+
+stepsize_callback = StepsizeCallback(cfl = 1.6)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback,
+ amr_callback, stepsize_callback);
+
+###############################################################################
+# Run the simulation.
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl
new file mode 100644
index 00000000000..adf1d009a59
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_advection_amr_unstructured_flag.jl
@@ -0,0 +1,87 @@
+using Downloads: download
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the linear advection equation.
+
+advection_velocity = (0.2, -0.7)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+
+initial_condition = initial_condition_gauss
+
+boundary_condition = BoundaryConditionDirichlet(initial_condition)
+boundary_conditions = Dict(:all => boundary_condition)
+
+solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
+
+# Deformed rectangle that looks like a waving flag, lower and upper faces are
+# sinus curves, left and right are vertical lines.
+f1(s) = SVector(-5.0, 5 * s - 5.0)
+f2(s) = SVector(5.0, 5 * s + 5.0)
+f3(s) = SVector(5 * s, -5.0 + 5 * sin(0.5 * pi * s))
+f4(s) = SVector(5 * s, 5.0 + 5 * sin(0.5 * pi * s))
+faces = (f1, f2, f3, f4)
+
+# This creates a mapping that transforms [-1, 1]^2 to the domain with the faces
+# defined above. It generally doesn't work for meshes loaded from mesh files
+# because these can be meshes of arbitrary domains, but the mesh below is
+# specifically built on the domain [-1, 1]^2.
+Trixi.validate_faces(faces)
+mapping_flag = Trixi.transfinite_mapping(faces)
+
+# Unstructured mesh with 24 cells of the square domain [-1, 1]^n
+mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp")
+isfile(mesh_file) ||
+ download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp",
+ mesh_file)
+
+# INP mesh files are only support by p4est. Hence, we
+# create a p4est connecvity object first from which
+# we can create a t8code mesh.
+conn = Trixi.read_inp_p4est(mesh_file, Val(2))
+
+mesh = T8codeMesh{2}(conn, polydeg = 3,
+ mapping = mapping_flag,
+ initial_refinement_level = 1)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions = boundary_conditions)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
+ extra_analysis_integrals = (entropy,))
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable = first),
+ base_level = 1,
+ med_level = 2, med_threshold = 0.1,
+ max_level = 3, max_threshold = 0.6)
+amr_callback = AMRCallback(semi, amr_controller,
+ interval = 5,
+ adapt_initial_condition = true,
+ adapt_initial_condition_only_refine = true)
+
+stepsize_callback = StepsizeCallback(cfl = 0.7)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback,
+ amr_callback, stepsize_callback)
+
+###############################################################################
+# Run the simulation.
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_advection_basic.jl b/examples/t8code_2d_dgsem/elixir_advection_basic.jl
new file mode 100644
index 00000000000..efc51226586
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_advection_basic.jl
@@ -0,0 +1,59 @@
+# The same setup as tree_2d_dgsem/elixir_advection_basic.jl
+# to verify the StructuredMesh implementation against TreeMesh
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the linear advection equation
+
+advection_velocity = (0.2, -0.7)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
+
+coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y))
+coordinates_max = (1.0, 1.0) # maximum coordinates (max(x), max(y))
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+trees_per_dimension = (8, 8)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg = 3,
+ mapping = mapping,
+ initial_refinement_level = 1)
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test,
+ solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 1.0
+ode = semidiscretize(semi, (0.0, 1.0));
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
+# and resets the timers
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
+analysis_callback = AnalysisCallback(semi, interval = 100)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
+stepsize_callback = StepsizeCallback(cfl = 1.6)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+
+# Print the timer summary
+summary_callback()
diff --git a/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl
new file mode 100644
index 00000000000..31a8bc93697
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_advection_nonconforming_flag.jl
@@ -0,0 +1,109 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the linear advection equation
+
+advection_velocity = (0.2, -0.7)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+
+# Create DG solver with polynomial degree = 4 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg = 4, surface_flux = flux_lax_friedrichs)
+
+# Deformed rectangle that looks like a waving flag,
+# lower and upper faces are sinus curves, left and right are vertical lines.
+f1(s) = SVector(-1.0, s - 1.0)
+f2(s) = SVector(1.0, s + 1.0)
+f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s))
+f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s))
+
+faces = (f1, f2, f3, f4)
+mapping = Trixi.transfinite_mapping(faces)
+
+# Create P4estMesh with 3 x 2 trees and 6 x 4 elements,
+# approximate the geometry with a smaller polydeg for testing.
+trees_per_dimension = (3, 2)
+mesh = T8codeMesh(trees_per_dimension, polydeg = 3,
+ mapping = mapping,
+ initial_refinement_level = 1)
+
+function adapt_callback(forest,
+ forest_from,
+ which_tree,
+ lelement_id,
+ ts,
+ is_family,
+ num_elements,
+ elements_ptr)::Cint
+ vertex = Vector{Cdouble}(undef, 3)
+
+ elements = unsafe_wrap(Array, elements_ptr, num_elements)
+
+ Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex))
+
+ level = Trixi.t8_element_level(ts, elements[1])
+
+ # TODO: Make this condition more general.
+ if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 4
+ # return true (refine)
+ return 1
+ else
+ # return false (don't refine)
+ return 0
+ end
+end
+
+Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0);
+
+# Init new forest.
+new_forest_ref = Ref{Trixi.t8_forest_t}()
+Trixi.t8_forest_init(new_forest_ref);
+new_forest = new_forest_ref[]
+
+# Check out `examples/t8_step4_partition_balance_ghost.jl` in
+# https://github.com/DLR-AMR/T8code.jl for detailed explanations.
+let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0
+ Trixi.t8_forest_set_user_data(new_forest, C_NULL)
+ Trixi.t8_forest_set_adapt(new_forest, mesh.forest,
+ Trixi.@t8_adapt_callback(adapt_callback), recursive)
+ Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition)
+ Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening)
+ Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES)
+ Trixi.t8_forest_commit(new_forest)
+end
+
+mesh.forest = new_forest
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_convergence_test,
+ solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 0.2
+ode = semidiscretize(semi, (0.0, 0.2));
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
+# and resets the timers
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
+analysis_callback = AnalysisCallback(semi, interval = 100)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
+stepsize_callback = StepsizeCallback(cfl = 1.6)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+
+# Print the timer summary
+summary_callback()
diff --git a/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl
new file mode 100644
index 00000000000..df9cbc26f6e
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_advection_unstructured_flag.jl
@@ -0,0 +1,81 @@
+using Downloads: download
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the linear advection equation.
+
+advection_velocity = (0.2, -0.7)
+equations = LinearScalarAdvectionEquation2D(advection_velocity)
+
+initial_condition = initial_condition_convergence_test
+
+boundary_condition = BoundaryConditionDirichlet(initial_condition)
+boundary_conditions = Dict(:all => boundary_condition)
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux.
+solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
+
+# Deformed rectangle that looks like a waving flag,
+# lower and upper faces are sinus curves, left and right are vertical lines.
+f1(s) = SVector(-1.0, s - 1.0)
+f2(s) = SVector(1.0, s + 1.0)
+f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s))
+f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s))
+faces = (f1, f2, f3, f4)
+
+Trixi.validate_faces(faces)
+mapping_flag = Trixi.transfinite_mapping(faces)
+
+# Unstructured mesh with 24 cells of the square domain [-1, 1]^n.
+mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp")
+isfile(mesh_file) ||
+ download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp",
+ mesh_file)
+
+# INP mesh files are only support by p4est. Hence, we
+# create a p4est connecvity object first from which
+# we can create a t8code mesh.
+conn = Trixi.read_inp_p4est(mesh_file, Val(2))
+
+mesh = T8codeMesh{2}(conn, polydeg = 3,
+ mapping = mapping_flag,
+ initial_refinement_level = 2)
+
+# A semidiscretization collects data structures and functions for the spatial discretization.
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions = boundary_conditions)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 0.2.
+tspan = (0.0, 0.2)
+ode = semidiscretize(semi, tspan)
+
+# At the beginning of the main loop, the SummaryCallback prints a summary of
+# the simulation setup and resets the timers.
+summary_callback = SummaryCallback()
+
+# The AnalysisCallback allows to analyse the solution in regular intervals and
+# prints the results.
+analysis_callback = AnalysisCallback(semi, interval = 100)
+
+# The StepsizeCallback handles the re-calculation of the maximum Δt after each
+# time step.
+stepsize_callback = StepsizeCallback(cfl = 1.4)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to
+# the ODE solver.
+callbacks = CallbackSet(summary_callback, analysis_callback, stepsize_callback)
+
+###############################################################################
+# Run the simulation.
+
+# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks.
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # Solve needs some value here but it will be overwritten by the stepsize_callback.
+ save_everystep = false, callback = callbacks);
+
+# Print the timer summary.
+summary_callback()
diff --git a/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl
new file mode 100644
index 00000000000..01e0449c67e
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_euler_free_stream.jl
@@ -0,0 +1,122 @@
+using Downloads: download
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the compressible Euler equations.
+
+equations = CompressibleEulerEquations2D(1.4)
+
+initial_condition = initial_condition_constant
+
+solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
+
+# Mapping as described in https://arxiv.org/abs/2012.12040 but reduced to 2D
+function mapping(xi_, eta_)
+ # Transform input variables between -1 and 1 onto [0,3]
+ xi = 1.5 * xi_ + 1.5
+ eta = 1.5 * eta_ + 1.5
+
+ y = eta + 3 / 8 * (cos(1.5 * pi * (2 * xi - 3) / 3) *
+ cos(0.5 * pi * (2 * eta - 3) / 3))
+
+ x = xi + 3 / 8 * (cos(0.5 * pi * (2 * xi - 3) / 3) *
+ cos(2 * pi * (2 * y - 3) / 3))
+
+ return SVector(x, y)
+end
+
+###############################################################################
+# Get the uncurved mesh from a file (downloads the file if not available locally)
+
+# Unstructured mesh with 48 cells of the square domain [-1, 1]^n
+mesh_file = joinpath(@__DIR__, "square_unstructured_1.inp")
+isfile(mesh_file) ||
+ download("https://gist.githubusercontent.com/efaulhaber/a075f8ec39a67fa9fad8f6f84342cbca/raw/a7206a02ed3a5d3cadacd8d9694ac154f9151db7/square_unstructured_1.inp",
+ mesh_file)
+
+# INP mesh files are only support by p4est. Hence, we
+# create a p4est connecvity object first from which
+# we can create a t8code mesh.
+conn = Trixi.read_inp_p4est(mesh_file, Val(2))
+
+mesh = T8codeMesh{2}(conn, polydeg = 3,
+ mapping = mapping,
+ initial_refinement_level = 1)
+
+function adapt_callback(forest,
+ forest_from,
+ which_tree,
+ lelement_id,
+ ts,
+ is_family,
+ num_elements,
+ elements_ptr)::Cint
+ vertex = Vector{Cdouble}(undef, 3)
+
+ elements = unsafe_wrap(Array, elements_ptr, num_elements)
+
+ Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex))
+
+ level = Trixi.t8_element_level(ts, elements[1])
+
+ # TODO: Make this condition more general.
+ if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 3
+ # return true (refine)
+ return 1
+ else
+ # return false (don't refine)
+ return 0
+ end
+end
+
+Trixi.@T8_ASSERT(Trixi.t8_forest_is_committed(mesh.forest)!=0);
+
+# Init new forest.
+new_forest_ref = Ref{Trixi.t8_forest_t}()
+Trixi.t8_forest_init(new_forest_ref);
+new_forest = new_forest_ref[]
+
+# Check out `examples/t8_step4_partition_balance_ghost.jl` in
+# https://github.com/DLR-AMR/T8code.jl for detailed explanations.
+let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0
+ Trixi.t8_forest_set_user_data(new_forest, C_NULL)
+ Trixi.t8_forest_set_adapt(new_forest, mesh.forest,
+ Trixi.@t8_adapt_callback(adapt_callback), recursive)
+ Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition)
+ Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening)
+ Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES)
+ Trixi.t8_forest_commit(new_forest)
+end
+
+mesh.forest = new_forest
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions = Dict(:all => BoundaryConditionDirichlet(initial_condition)))
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+stepsize_callback = StepsizeCallback(cfl = 2.0)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback,
+ stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_euler_sedov.jl b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl
new file mode 100644
index 00000000000..965d794f8dc
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_euler_sedov.jl
@@ -0,0 +1,97 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the compressible Euler equations.
+
+equations = CompressibleEulerEquations2D(1.4)
+
+"""
+ initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
+
+The Sedov blast wave setup based on Flash
+- http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+"""
+function initial_condition_sedov_blast_wave(x, t, equations::CompressibleEulerEquations2D)
+ # Set up polar coordinates
+ inicenter = SVector(0.0, 0.0)
+ x_norm = x[1] - inicenter[1]
+ y_norm = x[2] - inicenter[2]
+ r = sqrt(x_norm^2 + y_norm^2)
+
+ # Setup based on http://flash.uchicago.edu/site/flashcode/user_support/flash_ug_devel/node184.html#SECTION010114000000000000000
+ r0 = 0.21875 # = 3.5 * smallest dx (for domain length=4 and max-ref=6)
+ E = 1.0
+ p0_inner = 3 * (equations.gamma - 1) * E / (3 * pi * r0^2)
+ p0_outer = 1.0e-5 # = true Sedov setup
+
+ # Calculate primitive variables
+ rho = 1.0
+ v1 = 0.0
+ v2 = 0.0
+ p = r > r0 ? p0_outer : p0_inner
+
+ return prim2cons(SVector(rho, v1, v2, p), equations)
+end
+
+initial_condition = initial_condition_sedov_blast_wave
+
+# Get the DG approximation space
+surface_flux = flux_lax_friedrichs
+volume_flux = flux_ranocha
+polydeg = 4
+basis = LobattoLegendreBasis(polydeg)
+indicator_sc = IndicatorHennemannGassner(equations, basis,
+ alpha_max = 1.0,
+ alpha_min = 0.001,
+ alpha_smooth = true,
+ variable = density_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg = volume_flux,
+ volume_flux_fv = surface_flux)
+
+solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux,
+ volume_integral = volume_integral)
+
+###############################################################################
+
+coordinates_min = (-1.0, -1.0)
+coordinates_max = (1.0, 1.0)
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+trees_per_dimension = (4, 4)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg = 4,
+ mapping = mapping,
+ initial_refinement_level = 2, periodicity = true)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 12.5)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 300
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+stepsize_callback = StepsizeCallback(cfl = 0.5)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback,
+ stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl
new file mode 100644
index 00000000000..55a9063a001
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_euler_shockcapturing_ec.jl
@@ -0,0 +1,68 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the compressible Euler equations.
+
+equations = CompressibleEulerEquations2D(1.4)
+
+initial_condition = initial_condition_weak_blast_wave
+
+surface_flux = flux_ranocha
+volume_flux = flux_ranocha
+polydeg = 4
+basis = LobattoLegendreBasis(polydeg)
+indicator_sc = IndicatorHennemannGassner(equations, basis,
+ alpha_max = 1.0,
+ alpha_min = 0.001,
+ alpha_smooth = true,
+ variable = density_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg = volume_flux,
+ volume_flux_fv = surface_flux)
+
+solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux,
+ volume_integral = volume_integral)
+
+###############################################################################
+
+coordinates_min = (-1.0, -1.0)
+coordinates_max = (1.0, 1.0)
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+trees_per_dimension = (4, 4)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg = 4,
+ mapping = mapping,
+ initial_refinement_level = 2, periodicity = true)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 2.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+stepsize_callback = StepsizeCallback(cfl = 1.0)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback,
+ stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl
new file mode 100644
index 00000000000..21f26d79ba8
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_euler_source_terms_nonconforming_unstructured_flag.jl
@@ -0,0 +1,122 @@
+using Downloads: download
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Euler equations
+
+equations = CompressibleEulerEquations2D(1.4)
+
+initial_condition = initial_condition_convergence_test
+
+source_terms = source_terms_convergence_test
+
+# BCs must be passed as Dict
+boundary_condition = BoundaryConditionDirichlet(initial_condition)
+boundary_conditions = Dict(:all => boundary_condition)
+
+solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
+
+# Deformed rectangle that looks like a waving flag,
+# lower and upper faces are sinus curves, left and right are vertical lines.
+f1(s) = SVector(-1.0, s - 1.0)
+f2(s) = SVector(1.0, s + 1.0)
+f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s))
+f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s))
+faces = (f1, f2, f3, f4)
+
+Trixi.validate_faces(faces)
+mapping_flag = Trixi.transfinite_mapping(faces)
+
+# Get the uncurved mesh from a file (downloads the file if not available locally)
+# Unstructured mesh with 24 cells of the square domain [-1, 1]^n
+mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp")
+isfile(mesh_file) ||
+ download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp",
+ mesh_file)
+
+# INP mesh files are only support by p4est. Hence, we
+# create a p4est connecvity object first from which
+# we can create a t8code mesh.
+conn = Trixi.read_inp_p4est(mesh_file, Val(2))
+
+mesh = T8codeMesh{2}(conn, polydeg = 3,
+ mapping = mapping_flag,
+ initial_refinement_level = 1)
+
+function adapt_callback(forest,
+ forest_from,
+ which_tree,
+ lelement_id,
+ ts,
+ is_family,
+ num_elements,
+ elements_ptr)::Cint
+ vertex = Vector{Cdouble}(undef, 3)
+
+ elements = unsafe_wrap(Array, elements_ptr, num_elements)
+
+ Trixi.t8_element_vertex_reference_coords(ts, elements[1], 0, pointer(vertex))
+
+ level = Trixi.t8_element_level(ts, elements[1])
+
+ # TODO: Make this condition more general.
+ if vertex[1] < 1e-8 && vertex[2] < 1e-8 && level < 2
+ # return true (refine)
+ return 1
+ else
+ # return false (don't refine)
+ return 0
+ end
+end
+
+@assert(Trixi.t8_forest_is_committed(mesh.forest)!=0);
+
+# Init new forest.
+new_forest_ref = Ref{Trixi.t8_forest_t}()
+Trixi.t8_forest_init(new_forest_ref);
+new_forest = new_forest_ref[]
+
+# Check out `examples/t8_step4_partition_balance_ghost.jl` in
+# https://github.com/DLR-AMR/T8code.jl for detailed explanations.
+let set_from = C_NULL, recursive = 1, set_for_coarsening = 0, no_repartition = 0
+ Trixi.t8_forest_set_user_data(new_forest, C_NULL)
+ Trixi.t8_forest_set_adapt(new_forest, mesh.forest,
+ Trixi.@t8_adapt_callback(adapt_callback), recursive)
+ Trixi.t8_forest_set_balance(new_forest, set_from, no_repartition)
+ Trixi.t8_forest_set_partition(new_forest, set_from, set_for_coarsening)
+ Trixi.t8_forest_set_ghost(new_forest, 1, Trixi.T8_GHOST_FACES)
+ Trixi.t8_forest_commit(new_forest)
+end
+
+mesh.forest = new_forest
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ source_terms = source_terms,
+ boundary_conditions = boundary_conditions)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+stepsize_callback = StepsizeCallback(cfl = 0.8)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback,
+ stepsize_callback)
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl b/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl
new file mode 100644
index 00000000000..32649eacff4
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_eulergravity_convergence.jl
@@ -0,0 +1,77 @@
+using OrdinaryDiffEq
+using Trixi
+
+initial_condition = initial_condition_eoc_test_coupled_euler_gravity
+
+###############################################################################
+# semidiscretization of the compressible Euler equations
+gamma = 2.0
+equations_euler = CompressibleEulerEquations2D(gamma)
+
+polydeg = 3
+solver_euler = DGSEM(polydeg, flux_hll)
+
+coordinates_min = (0.0, 0.0)
+coordinates_max = (2.0, 2.0)
+
+trees_per_dimension = (1, 1)
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg = 1,
+ mapping = mapping,
+ initial_refinement_level = 2)
+
+semi_euler = SemidiscretizationHyperbolic(mesh, equations_euler, initial_condition, solver_euler,
+ source_terms=source_terms_eoc_test_coupled_euler_gravity)
+
+
+###############################################################################
+# semidiscretization of the hyperbolic diffusion equations
+equations_gravity = HyperbolicDiffusionEquations2D()
+
+solver_gravity = DGSEM(polydeg, flux_lax_friedrichs)
+
+semi_gravity = SemidiscretizationHyperbolic(mesh, equations_gravity, initial_condition, solver_gravity,
+ source_terms=source_terms_harmonic)
+
+
+###############################################################################
+# combining both semidiscretizations for Euler + self-gravity
+parameters = ParametersEulerGravity(background_density=2.0, # aka rho0
+ # rho0 is (ab)used to add a "+8π" term to the source terms
+ # for the manufactured solution
+ gravitational_constant=1.0, # aka G
+ cfl=1.1,
+ resid_tol=1.0e-10,
+ n_iterations_max=1000,
+ timestep_gravity=timestep_gravity_erk52_3Sstar!)
+
+semi = SemidiscretizationEulerGravity(semi_euler, semi_gravity, parameters)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+tspan = (0.0, 0.5)
+ode = semidiscretize(semi, tspan);
+
+summary_callback = SummaryCallback()
+
+stepsize_callback = StepsizeCallback(cfl=0.8)
+
+analysis_interval = 100
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+analysis_callback = AnalysisCallback(semi_euler, interval=analysis_interval,
+ save_analysis=true)
+
+callbacks = CallbackSet(summary_callback, stepsize_callback,
+ analysis_callback, alive_callback)
+
+###############################################################################
+# run the simulation
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+summary_callback() # print the timer summary
+println("Number of gravity subcycles: ", semi.gravity_counter.ncalls_since_readout)
diff --git a/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl b/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl
new file mode 100644
index 00000000000..463f916fa2e
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_mhd_alfven_wave.jl
@@ -0,0 +1,60 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the compressible ideal GLM-MHD equations.
+
+gamma = 5/3
+equations = IdealGlmMhdEquations2D(gamma)
+
+initial_condition = initial_condition_convergence_test
+
+# Get the DG approximation space
+volume_flux = (flux_central, flux_nonconservative_powell)
+solver = DGSEM(polydeg=4, surface_flux=(flux_hll, flux_nonconservative_powell),
+ volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
+
+coordinates_min = (0.0 , 0.0 )
+coordinates_max = (sqrt(2.0), sqrt(2.0))
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+trees_per_dimension = (8, 8)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg=3,
+ mapping=mapping,
+ initial_refinement_level=0, periodicity=true)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+cfl = 0.9
+stepsize_callback = StepsizeCallback(cfl=cfl)
+
+glm_speed_callback = GlmSpeedCallback(glm_scale=0.5, cfl=cfl)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback,
+ stepsize_callback,
+ glm_speed_callback)
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl
new file mode 100644
index 00000000000..9a4bd99e444
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_mhd_rotor.jl
@@ -0,0 +1,134 @@
+using Downloads: download
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible ideal GLM-MHD equations
+equations = IdealGlmMhdEquations2D(1.4)
+
+"""
+ initial_condition_rotor(x, t, equations::IdealGlmMhdEquations2D)
+
+The classical MHD rotor test case. Here, the setup is taken from
+- Dominik Derigs, Gregor J. Gassner, Stefanie Walch & Andrew R. Winters (2018)
+ Entropy Stable Finite Volume Approximations for Ideal Magnetohydrodynamics
+ [doi: 10.1365/s13291-018-0178-9](https://doi.org/10.1365/s13291-018-0178-9)
+"""
+function initial_condition_rotor(x, t, equations::IdealGlmMhdEquations2D)
+ # setup taken from Derigs et al. DMV article (2018)
+ # domain must be [0, 1] x [0, 1], γ = 1.4
+ dx = x[1] - 0.5
+ dy = x[2] - 0.5
+ r = sqrt(dx^2 + dy^2)
+ f = (0.115 - r) / 0.015
+ if r <= 0.1
+ rho = 10.0
+ v1 = -20.0 * dy
+ v2 = 20.0 * dx
+ elseif r >= 0.115
+ rho = 1.0
+ v1 = 0.0
+ v2 = 0.0
+ else
+ rho = 1.0 + 9.0 * f
+ v1 = -20.0 * f * dy
+ v2 = 20.0 * f * dx
+ end
+ v3 = 0.0
+ p = 1.0
+ B1 = 5.0 / sqrt(4.0 * pi)
+ B2 = 0.0
+ B3 = 0.0
+ psi = 0.0
+ return prim2cons(SVector(rho, v1, v2, v3, p, B1, B2, B3, psi), equations)
+end
+initial_condition = initial_condition_rotor
+
+surface_flux = (flux_lax_friedrichs, flux_nonconservative_powell)
+volume_flux = (flux_hindenlang_gassner, flux_nonconservative_powell)
+polydeg = 4
+basis = LobattoLegendreBasis(polydeg)
+indicator_sc = IndicatorHennemannGassner(equations, basis,
+ alpha_max = 0.5,
+ alpha_min = 0.001,
+ alpha_smooth = true,
+ variable = density_pressure)
+volume_integral = VolumeIntegralShockCapturingHG(indicator_sc;
+ volume_flux_dg = volume_flux,
+ volume_flux_fv = surface_flux)
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+# Affine type mapping to take the [-1,1]^2 domain from the mesh file
+# and put it onto the rotor domain [0,1]^2 and then warp it with a mapping
+# as described in https://arxiv.org/abs/2012.12040
+function mapping_twist(xi, eta)
+ y = 0.5 * (eta + 1.0) +
+ 0.05 * cos(1.5 * pi * (2.0 * xi - 1.0)) * cos(0.5 * pi * (2.0 * eta - 1.0))
+ x = 0.5 * (xi + 1.0) + 0.05 * cos(0.5 * pi * (2.0 * xi - 1.0)) * cos(2.0 * pi * y)
+ return SVector(x, y)
+end
+
+mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp")
+isfile(mesh_file) ||
+ download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp",
+ mesh_file)
+
+# INP mesh files are only support by p4est. Hence, we
+# create a p4est connecvity object first from which
+# we can create a t8code mesh.
+conn = Trixi.read_inp_p4est(mesh_file, Val(2))
+
+mesh = T8codeMesh{2}(conn, polydeg = 4,
+ mapping = mapping_twist,
+ initial_refinement_level = 1)
+
+boundary_condition = BoundaryConditionDirichlet(initial_condition)
+boundary_conditions = Dict(:all => boundary_condition)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ boundary_conditions = boundary_conditions)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 0.15)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+amr_indicator = IndicatorLöhner(semi,
+ variable = density_pressure)
+
+amr_controller = ControllerThreeLevel(semi, amr_indicator,
+ base_level = 1,
+ med_level = 3, med_threshold = 0.05,
+ max_level = 5, max_threshold = 0.1)
+amr_callback = AMRCallback(semi, amr_controller,
+ interval = 5,
+ adapt_initial_condition = true,
+ adapt_initial_condition_only_refine = true)
+
+cfl = 0.5
+stepsize_callback = StepsizeCallback(cfl = cfl)
+
+glm_speed_callback = GlmSpeedCallback(glm_scale = 0.5, cfl = cfl)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback,
+ amr_callback,
+ stepsize_callback,
+ glm_speed_callback)
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl b/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl
new file mode 100644
index 00000000000..c19f440ebc7
--- /dev/null
+++ b/examples/t8code_2d_dgsem/elixir_shallowwater_source_terms.jl
@@ -0,0 +1,60 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the shallow water equations.
+
+equations = ShallowWaterEquations2D(gravity_constant=9.81)
+
+initial_condition = initial_condition_convergence_test # MMS EOC test
+
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
+solver = DGSEM(polydeg=3, surface_flux=(flux_lax_friedrichs, flux_nonconservative_fjordholm_etal),
+ volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
+
+###############################################################################
+# Get the P4estMesh and setup a periodic mesh
+
+coordinates_min = (0.0, 0.0) # minimum coordinates (min(x), min(y))
+coordinates_max = (sqrt(2.0), sqrt(2.0)) # maximum coordinates (max(x), max(y))
+
+mapping = Trixi.coordinates2mapping(coordinates_min, coordinates_max)
+
+trees_per_dimension = (8, 8)
+
+mesh = T8codeMesh(trees_per_dimension, polydeg=3,
+ mapping=mapping,
+ initial_refinement_level=1)
+
+# A semidiscretization collects data structures and functions for the spatial discretization
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ source_terms=source_terms_convergence_test)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span from 0.0 to 1.0
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 500
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback)
+
+###############################################################################
+# run the simulation
+
+# use a Runge-Kutta method with automatic (error based) time step size control
+sol = solve(ode, RDPK3SpFSAL49(); abstol=1.0e-8, reltol=1.0e-8,
+ ode_default_options()..., callback=callbacks);
+summary_callback() # print the timer summary
diff --git a/src/Trixi.jl b/src/Trixi.jl
index b0c872b1904..990c33f3c94 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -54,6 +54,7 @@ using Octavian: Octavian, matmul!
using Polyester: Polyester, @batch # You know, the cheapest threads you can find...
using OffsetArrays: OffsetArray, OffsetVector
using P4est
+using T8code
using Setfield: @set
using RecipesBase: RecipesBase
using Requires: @require
@@ -110,6 +111,7 @@ include("basic_types.jl")
include("auxiliary/auxiliary.jl")
include("auxiliary/mpi.jl")
include("auxiliary/p4est.jl")
+include("auxiliary/t8code.jl")
include("equations/equations.jl")
include("meshes/meshes.jl")
include("solvers/solvers.jl")
@@ -210,7 +212,7 @@ export entropy, energy_total, energy_kinetic, energy_internal, energy_magnetic,
export lake_at_rest_error
export ncomponents, eachcomponent
-export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh
+export TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh, T8codeMesh
export DG,
DGSEM, LobattoLegendreBasis,
@@ -277,6 +279,7 @@ function __init__()
init_mpi()
init_p4est()
+ init_t8code()
register_error_hints()
diff --git a/src/auxiliary/t8code.jl b/src/auxiliary/t8code.jl
new file mode 100644
index 00000000000..37cb782bb93
--- /dev/null
+++ b/src/auxiliary/t8code.jl
@@ -0,0 +1,486 @@
+"""
+ init_t8code()
+
+Initialize `t8code` by calling `sc_init`, `p4est_init`, and `t8_init` while
+setting the log level to `SC_LP_ERROR`. This function will check if `t8code`
+is already initialized and if yes, do nothing, thus it is safe to call it
+multiple times.
+"""
+function init_t8code()
+ t8code_package_id = t8_get_package_id()
+ if t8code_package_id >= 0
+ return nothing
+ end
+
+ # Initialize the sc library, has to happen before we initialize t8code.
+ let catch_signals = 0, print_backtrace = 0, log_handler = C_NULL
+ T8code.Libt8.sc_init(mpi_comm(), catch_signals, print_backtrace, log_handler,
+ T8code.Libt8.SC_LP_ERROR)
+ end
+
+ if T8code.Libt8.p4est_is_initialized() == 0
+ # Initialize `p4est` with log level ERROR to prevent a lot of output in AMR simulations
+ T8code.Libt8.p4est_init(C_NULL, T8code.Libt8.SC_LP_ERROR)
+ end
+
+ # Initialize t8code with log level ERROR to prevent a lot of output in AMR simulations.
+ t8_init(T8code.Libt8.SC_LP_ERROR)
+
+ if haskey(ENV, "TRIXI_T8CODE_SC_FINALIZE")
+ # Normally, `sc_finalize` should always be called during shutdown of an
+ # application. It checks whether there is still un-freed memory by t8code
+ # and/or T8code.jl and throws an exception if this is the case. For
+ # production runs this is not mandatory, but is helpful during
+ # development. Hence, this option is only activated when environment
+ # variable TRIXI_T8CODE_SC_FINALIZE exists.
+ @warn "T8code.jl: sc_finalize will be called during shutdown of Trixi.jl."
+ MPI.add_finalize_hook!(T8code.Libt8.sc_finalize)
+ end
+
+ return nothing
+end
+
+function trixi_t8_unref_forest(forest)
+ t8_forest_unref(Ref(forest))
+end
+
+function t8_free(ptr)
+ T8code.Libt8.sc_free(t8_get_package_id(), ptr)
+end
+
+function trixi_t8_count_interfaces(forest)
+ # Check that forest is a committed, that is valid and usable, forest.
+ @assert t8_forest_is_committed(forest) != 0
+
+ # Get the number of local elements of forest.
+ num_local_elements = t8_forest_get_local_num_elements(forest)
+ # Get the number of ghost elements of forest.
+ num_ghost_elements = t8_forest_get_num_ghosts(forest)
+ # Get the number of trees that have elements of this process.
+ num_local_trees = t8_forest_get_num_local_trees(forest)
+
+ current_index = t8_locidx_t(0)
+
+ local_num_conform = 0
+ local_num_mortars = 0
+ local_num_boundary = 0
+
+ for itree in 0:(num_local_trees - 1)
+ tree_class = t8_forest_get_tree_class(forest, itree)
+ eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class)
+
+ # Get the number of elements of this tree.
+ num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree)
+
+ for ielement in 0:(num_elements_in_tree - 1)
+ element = t8_forest_get_element_in_tree(forest, itree, ielement)
+
+ level = t8_element_level(eclass_scheme, element)
+
+ num_faces = t8_element_num_faces(eclass_scheme, element)
+
+ for iface in 0:(num_faces - 1)
+ pelement_indices_ref = Ref{Ptr{t8_locidx_t}}()
+ pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}()
+ pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}()
+
+ dual_faces_ref = Ref{Ptr{Cint}}()
+ num_neighbors_ref = Ref{Cint}()
+
+ forest_is_balanced = Cint(1)
+
+ t8_forest_leaf_face_neighbors(forest, itree, element,
+ pneighbor_leafs_ref, iface, dual_faces_ref,
+ num_neighbors_ref,
+ pelement_indices_ref, pneigh_scheme_ref,
+ forest_is_balanced)
+
+ num_neighbors = num_neighbors_ref[]
+ neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[],
+ num_neighbors)
+ neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors)
+ neighbor_scheme = pneigh_scheme_ref[]
+
+ if num_neighbors > 0
+ neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1])
+
+ # Conforming interface: The second condition ensures we only visit the interface once.
+ if level == neighbor_level && current_index <= neighbor_ielements[1]
+ local_num_conform += 1
+ elseif level < neighbor_level
+ local_num_mortars += 1
+ end
+
+ else
+ local_num_boundary += 1
+ end
+
+ t8_free(dual_faces_ref[])
+ t8_free(pneighbor_leafs_ref[])
+ t8_free(pelement_indices_ref[])
+ end # for
+
+ current_index += 1
+ end # for
+ end # for
+
+ return (interfaces = local_num_conform,
+ mortars = local_num_mortars,
+ boundaries = local_num_boundary)
+end
+
+function trixi_t8_fill_mesh_info(forest, elements, interfaces, mortars, boundaries,
+ boundary_names)
+ # Check that forest is a committed, that is valid and usable, forest.
+ @assert t8_forest_is_committed(forest) != 0
+
+ # Get the number of local elements of forest.
+ num_local_elements = t8_forest_get_local_num_elements(forest)
+ # Get the number of ghost elements of forest.
+ num_ghost_elements = t8_forest_get_num_ghosts(forest)
+ # Get the number of trees that have elements of this process.
+ num_local_trees = t8_forest_get_num_local_trees(forest)
+
+ current_index = t8_locidx_t(0)
+
+ local_num_conform = 0
+ local_num_mortars = 0
+ local_num_boundary = 0
+
+ for itree in 0:(num_local_trees - 1)
+ tree_class = t8_forest_get_tree_class(forest, itree)
+ eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class)
+
+ # Get the number of elements of this tree.
+ num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree)
+
+ for ielement in 0:(num_elements_in_tree - 1)
+ element = t8_forest_get_element_in_tree(forest, itree, ielement)
+
+ level = t8_element_level(eclass_scheme, element)
+
+ num_faces = t8_element_num_faces(eclass_scheme, element)
+
+ for iface in 0:(num_faces - 1)
+
+ # Compute the `orientation` of the touching faces.
+ if t8_element_is_root_boundary(eclass_scheme, element, iface) == 1
+ cmesh = t8_forest_get_cmesh(forest)
+ itree_in_cmesh = t8_forest_ltreeid_to_cmesh_ltreeid(forest, itree)
+ iface_in_tree = t8_element_tree_face(eclass_scheme, element, iface)
+ orientation_ref = Ref{Cint}()
+
+ t8_cmesh_get_face_neighbor(cmesh, itree_in_cmesh, iface_in_tree, C_NULL,
+ orientation_ref)
+ orientation = orientation_ref[]
+ else
+ orientation = zero(Cint)
+ end
+
+ pelement_indices_ref = Ref{Ptr{t8_locidx_t}}()
+ pneighbor_leafs_ref = Ref{Ptr{Ptr{t8_element}}}()
+ pneigh_scheme_ref = Ref{Ptr{t8_eclass_scheme}}()
+
+ dual_faces_ref = Ref{Ptr{Cint}}()
+ num_neighbors_ref = Ref{Cint}()
+
+ forest_is_balanced = Cint(1)
+
+ t8_forest_leaf_face_neighbors(forest, itree, element,
+ pneighbor_leafs_ref, iface, dual_faces_ref,
+ num_neighbors_ref,
+ pelement_indices_ref, pneigh_scheme_ref,
+ forest_is_balanced)
+
+ num_neighbors = num_neighbors_ref[]
+ dual_faces = unsafe_wrap(Array, dual_faces_ref[], num_neighbors)
+ neighbor_ielements = unsafe_wrap(Array, pelement_indices_ref[],
+ num_neighbors)
+ neighbor_leafs = unsafe_wrap(Array, pneighbor_leafs_ref[], num_neighbors)
+ neighbor_scheme = pneigh_scheme_ref[]
+
+ if num_neighbors > 0
+ neighbor_level = t8_element_level(neighbor_scheme, neighbor_leafs[1])
+
+ # Conforming interface: The second condition ensures we only visit the interface once.
+ if level == neighbor_level && current_index <= neighbor_ielements[1]
+ local_num_conform += 1
+
+ faces = (iface, dual_faces[1])
+ interface_id = local_num_conform
+
+ # Write data to interfaces container.
+ interfaces.neighbor_ids[1, interface_id] = current_index + 1
+ interfaces.neighbor_ids[2, interface_id] = neighbor_ielements[1] + 1
+
+ # Iterate over primary and secondary element.
+ for side in 1:2
+ # Align interface in positive coordinate direction of primary element.
+ # For orientation == 1, the secondary element needs to be indexed backwards
+ # relative to the interface.
+ if side == 1 || orientation == 0
+ # Forward indexing
+ indexing = :i_forward
+ else
+ # Backward indexing
+ indexing = :i_backward
+ end
+
+ if faces[side] == 0
+ # Index face in negative x-direction
+ interfaces.node_indices[side, interface_id] = (:begin,
+ indexing)
+ elseif faces[side] == 1
+ # Index face in positive x-direction
+ interfaces.node_indices[side, interface_id] = (:end,
+ indexing)
+ elseif faces[side] == 2
+ # Index face in negative y-direction
+ interfaces.node_indices[side, interface_id] = (indexing,
+ :begin)
+ else # faces[side] == 3
+ # Index face in positive y-direction
+ interfaces.node_indices[side, interface_id] = (indexing,
+ :end)
+ end
+ end
+
+ # Non-conforming interface.
+ elseif level < neighbor_level
+ local_num_mortars += 1
+
+ faces = (dual_faces[1], iface)
+
+ mortar_id = local_num_mortars
+
+ # Last entry is the large element.
+ mortars.neighbor_ids[end, mortar_id] = current_index + 1
+
+ # First `1:end-1` entries are the smaller elements.
+ mortars.neighbor_ids[1:(end - 1), mortar_id] .= neighbor_ielements .+
+ 1
+
+ for side in 1:2
+ # Align mortar in positive coordinate direction of small side.
+ # For orientation == 1, the large side needs to be indexed backwards
+ # relative to the mortar.
+ if side == 1 || orientation == 0
+ # Forward indexing for small side or orientation == 0.
+ indexing = :i_forward
+ else
+ # Backward indexing for large side with reversed orientation.
+ indexing = :i_backward
+ # Since the orientation is reversed we have to account for this
+ # when filling the `neighbor_ids` array.
+ mortars.neighbor_ids[1, mortar_id] = neighbor_ielements[2] +
+ 1
+ mortars.neighbor_ids[2, mortar_id] = neighbor_ielements[1] +
+ 1
+ end
+
+ if faces[side] == 0
+ # Index face in negative x-direction
+ mortars.node_indices[side, mortar_id] = (:begin, indexing)
+ elseif faces[side] == 1
+ # Index face in positive x-direction
+ mortars.node_indices[side, mortar_id] = (:end, indexing)
+ elseif faces[side] == 2
+ # Index face in negative y-direction
+ mortars.node_indices[side, mortar_id] = (indexing, :begin)
+ else # faces[side] == 3
+ # Index face in positive y-direction
+ mortars.node_indices[side, mortar_id] = (indexing, :end)
+ end
+ end
+
+ # else: "level > neighbor_level" is skipped since we visit the mortar interface only once.
+ end
+
+ # Domain boundary.
+ else
+ local_num_boundary += 1
+ boundary_id = local_num_boundary
+
+ boundaries.neighbor_ids[boundary_id] = current_index + 1
+
+ if iface == 0
+ # Index face in negative x-direction.
+ boundaries.node_indices[boundary_id] = (:begin, :i_forward)
+ elseif iface == 1
+ # Index face in positive x-direction.
+ boundaries.node_indices[boundary_id] = (:end, :i_forward)
+ elseif iface == 2
+ # Index face in negative y-direction.
+ boundaries.node_indices[boundary_id] = (:i_forward, :begin)
+ else # iface == 3
+ # Index face in positive y-direction.
+ boundaries.node_indices[boundary_id] = (:i_forward, :end)
+ end
+
+ # One-based indexing.
+ boundaries.name[boundary_id] = boundary_names[iface + 1, itree + 1]
+ end
+
+ t8_free(dual_faces_ref[])
+ t8_free(pneighbor_leafs_ref[])
+ t8_free(pelement_indices_ref[])
+ end # for iface = ...
+
+ current_index += 1
+ end # for
+ end # for
+
+ return (interfaces = local_num_conform,
+ mortars = local_num_mortars,
+ boundaries = local_num_boundary)
+end
+
+function trixi_t8_get_local_element_levels(forest)
+ # Check that forest is a committed, that is valid and usable, forest.
+ @assert t8_forest_is_committed(forest) != 0
+
+ levels = Vector{Int}(undef, t8_forest_get_local_num_elements(forest))
+
+ # Get the number of trees that have elements of this process.
+ num_local_trees = t8_forest_get_num_local_trees(forest)
+
+ current_index = 0
+
+ for itree in 0:(num_local_trees - 1)
+ tree_class = t8_forest_get_tree_class(forest, itree)
+ eclass_scheme = t8_forest_get_eclass_scheme(forest, tree_class)
+
+ # Get the number of elements of this tree.
+ num_elements_in_tree = t8_forest_get_tree_num_elements(forest, itree)
+
+ for ielement in 0:(num_elements_in_tree - 1)
+ element = t8_forest_get_element_in_tree(forest, itree, ielement)
+ current_index += 1
+ levels[current_index] = t8_element_level(eclass_scheme, element)
+ end # for
+ end # for
+
+ return levels
+end
+
+# Callback function prototype to decide for refining and coarsening.
+# If `is_family` equals 1, the first `num_elements` in elements
+# form a family and we decide whether this family should be coarsened
+# or only the first element should be refined.
+# Otherwise `is_family` must equal zero and we consider the first entry
+# of the element array for refinement.
+# Entries of the element array beyond the first `num_elements` are undefined.
+# \param [in] forest the forest to which the new elements belong
+# \param [in] forest_from the forest that is adapted.
+# \param [in] which_tree the local tree containing `elements`
+# \param [in] lelement_id the local element id in `forest_old` in the tree of the current element
+# \param [in] ts the eclass scheme of the tree
+# \param [in] is_family if 1, the first `num_elements` entries in `elements` form a family. If 0, they do not.
+# \param [in] num_elements the number of entries in `elements` that are defined
+# \param [in] elements Pointers to a family or, if `is_family` is zero,
+# pointer to one element.
+# \return greater zero if the first entry in `elements` should be refined,
+# smaller zero if the family `elements` shall be coarsened,
+# zero else.
+function adapt_callback(forest,
+ forest_from,
+ which_tree,
+ lelement_id,
+ ts,
+ is_family,
+ num_elements,
+ elements)::Cint
+ num_levels = t8_forest_get_local_num_elements(forest_from)
+
+ indicator_ptr = Ptr{Int}(t8_forest_get_user_data(forest))
+ indicators = unsafe_wrap(Array, indicator_ptr, num_levels)
+
+ offset = t8_forest_get_tree_element_offset(forest_from, which_tree)
+
+ # Only allow coarsening for complete families.
+ if indicators[offset + lelement_id + 1] < 0 && is_family == 0
+ return Cint(0)
+ end
+
+ return Cint(indicators[offset + lelement_id + 1])
+end
+
+function trixi_t8_adapt_new(old_forest, indicators)
+ # Check that forest is a committed, that is valid and usable, forest.
+ @assert t8_forest_is_committed(old_forest) != 0
+
+ # Init new forest.
+ new_forest_ref = Ref{t8_forest_t}()
+ t8_forest_init(new_forest_ref)
+ new_forest = new_forest_ref[]
+
+ let set_from = C_NULL, recursive = 0, set_for_coarsening = 0, no_repartition = 0
+ t8_forest_set_user_data(new_forest, pointer(indicators))
+ t8_forest_set_adapt(new_forest, old_forest, @t8_adapt_callback(adapt_callback),
+ recursive)
+ t8_forest_set_balance(new_forest, set_from, no_repartition)
+ t8_forest_set_partition(new_forest, set_from, set_for_coarsening)
+ t8_forest_set_ghost(new_forest, 1, T8_GHOST_FACES) # Note: MPI support not available yet so it is a dummy call.
+ t8_forest_commit(new_forest)
+ end
+
+ return new_forest
+end
+
+function trixi_t8_get_difference(old_levels, new_levels, num_children)
+ old_nelems = length(old_levels)
+ new_nelems = length(new_levels)
+
+ changes = Vector{Int}(undef, old_nelems)
+
+ # Local element indices.
+ old_index = 1
+ new_index = 1
+
+ while old_index <= old_nelems && new_index <= new_nelems
+ if old_levels[old_index] < new_levels[new_index]
+ # Refined.
+
+ changes[old_index] = 1
+
+ old_index += 1
+ new_index += num_children
+
+ elseif old_levels[old_index] > new_levels[new_index]
+ # Coarsend.
+
+ for child_index in old_index:(old_index + num_children - 1)
+ changes[child_index] = -1
+ end
+
+ old_index += num_children
+ new_index += 1
+
+ else
+ # No changes.
+
+ changes[old_index] = 0
+
+ old_index += 1
+ new_index += 1
+ end
+ end
+
+ return changes
+end
+
+# Coarsen or refine marked cells and rebalance forest. Return a difference between
+# old and new mesh.
+function trixi_t8_adapt!(mesh, indicators)
+ old_levels = trixi_t8_get_local_element_levels(mesh.forest)
+
+ forest_cached = trixi_t8_adapt_new(mesh.forest, indicators)
+
+ new_levels = trixi_t8_get_local_element_levels(forest_cached)
+
+ differences = trixi_t8_get_difference(old_levels, new_levels, 2^ndims(mesh))
+
+ mesh.forest = forest_cached
+
+ return differences
+end
diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl
index bef49b4c482..4d80e6e1139 100644
--- a/src/callbacks_step/amr.jl
+++ b/src/callbacks_step/amr.jl
@@ -471,6 +471,65 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::P4estMesh,
return has_changed
end
+function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::SerialT8codeMesh,
+ equations, dg::DG, cache, semi,
+ t, iter;
+ only_refine = false, only_coarsen = false,
+ passive_args = ())
+ has_changed = false
+
+ @unpack controller, adaptor = amr_callback
+
+ u = wrap_array(u_ode, mesh, equations, dg, cache)
+ indicators = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg,
+ cache, t = t, iter = iter)
+
+ if only_coarsen
+ indicators[indicators .> 0] .= 0
+ end
+
+ if only_refine
+ indicators[indicators .< 0] .= 0
+ end
+
+ @boundscheck begin
+ @assert axes(indicators)==(Base.OneTo(ncells(mesh)),) ("Indicator array (axes = $(axes(indicators))) and mesh cells (axes = $(Base.OneTo(ncells(mesh)))) have different axes")
+ end
+
+ @trixi_timeit timer() "adapt" begin
+ difference = @trixi_timeit timer() "mesh" trixi_t8_adapt!(mesh, indicators)
+
+ @trixi_timeit timer() "solver" adapt!(u_ode, adaptor, mesh, equations, dg,
+ cache, difference)
+ end
+
+ # Store whether there were any cells coarsened or refined and perform load balancing.
+ has_changed = any(difference .!= 0)
+
+ # TODO: T8codeMesh for MPI not implemented yet.
+ # Check if mesh changed on other processes
+ # if mpi_isparallel()
+ # has_changed = MPI.Allreduce!(Ref(has_changed), |, mpi_comm())[]
+ # end
+
+ if has_changed
+ # TODO: T8codeMesh for MPI not implemented yet.
+ # if mpi_isparallel() && amr_callback.dynamic_load_balancing
+ # @trixi_timeit timer() "dynamic load balancing" begin
+ # global_first_quadrant = unsafe_wrap(Array, mesh.p4est.global_first_quadrant, mpi_nranks() + 1)
+ # old_global_first_quadrant = copy(global_first_quadrant)
+ # partition!(mesh)
+ # rebalance_solver!(u_ode, mesh, equations, dg, cache, old_global_first_quadrant)
+ # end
+ # end
+
+ reinitialize_boundaries!(semi.boundary_conditions, cache)
+ end
+
+ # Return true if there were any cells coarsened or refined, otherwise false.
+ return has_changed
+end
+
function reinitialize_boundaries!(boundary_conditions::UnstructuredSortedBoundaryTypes,
cache)
# Reinitialize boundary types container because boundaries may have changed.
@@ -639,6 +698,10 @@ function current_element_levels(mesh::P4estMesh, solver, cache)
return current_levels
end
+function current_element_levels(mesh::T8codeMesh, solver, cache)
+ return trixi_t8_get_local_element_levels(mesh.forest)
+end
+
# TODO: Taal refactor, merge the two loops of ControllerThreeLevel and IndicatorLöhner etc.?
# But that would remove the simplest possibility to write that stuff to a file...
# We could of course implement some additional logic and workarounds, but is it worth the effort?
diff --git a/src/callbacks_step/amr_dg2d.jl b/src/callbacks_step/amr_dg2d.jl
index 400d16347d5..1d37dfce034 100644
--- a/src/callbacks_step/amr_dg2d.jl
+++ b/src/callbacks_step/amr_dg2d.jl
@@ -333,9 +333,79 @@ function coarsen_elements!(u::AbstractArray{<:Any, 4}, element_id,
end
end
+# Coarsen and refine elements in the DG solver based on a difference list.
+function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{2}, equations,
+ dg::DGSEM, cache, difference)
+
+ # Return early if there is nothing to do.
+ if !any(difference .!= 0)
+ return nothing
+ end
+
+ # Number of (local) cells/elements.
+ old_nelems = nelements(dg, cache)
+ new_nelems = ncells(mesh)
+
+ # Local element indices.
+ old_index = 1
+ new_index = 1
+
+ # Note: This is true for `quads` only.
+ T8_CHILDREN = 4
+
+ # Retain current solution data.
+ old_u_ode = copy(u_ode)
+
+ GC.@preserve old_u_ode begin
+ old_u = wrap_array(old_u_ode, mesh, equations, dg, cache)
+
+ reinitialize_containers!(mesh, equations, dg, cache)
+
+ resize!(u_ode,
+ nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache))
+ u = wrap_array(u_ode, mesh, equations, dg, cache)
+
+ while old_index <= old_nelems && new_index <= new_nelems
+ if difference[old_index] > 0 # Refine.
+
+ # Refine element and store solution directly in new data structure.
+ refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg)
+
+ old_index += 1
+ new_index += T8_CHILDREN
+
+ elseif difference[old_index] < 0 # Coarsen.
+
+ # 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(difference[old_index:(old_index + T8_CHILDREN - 1)] .< 0) "bad cell/element order"
+
+ # Coarsen elements and store solution directly in new data structure.
+ coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations,
+ dg)
+
+ old_index += T8_CHILDREN
+ new_index += 1
+
+ else # No changes.
+
+ # Copy old element data to new element container.
+ @views u[:, .., new_index] .= old_u[:, .., old_index]
+
+ old_index += 1
+ new_index += 1
+ end
+ end # while
+ end # GC.@preserve old_u_ode
+
+ return nothing
+end
+
# this method is called when an `ControllerThreeLevel` is constructed
function create_cache(::Type{ControllerThreeLevel},
- mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations, dg::DG, cache)
+ mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations,
+ dg::DG, cache)
controller_value = Vector{Int}(undef, nelements(dg, cache))
return (; controller_value)
end
diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl
index 7c453aab633..fad42b11098 100644
--- a/src/callbacks_step/analysis.jl
+++ b/src/callbacks_step/analysis.jl
@@ -534,6 +534,36 @@ function print_amr_information(callbacks, mesh::P4estMesh, solver, cache)
return nothing
end
+# Print level information only if AMR is enabled
+function print_amr_information(callbacks, mesh::T8codeMesh, solver, cache)
+
+ # Return early if there is nothing to print
+ uses_amr(callbacks) || return nothing
+
+ # TODO: Switch to global element levels array when MPI supported or find
+ # another solution.
+ levels = trixi_t8_get_local_element_levels(mesh.forest)
+
+ min_level = minimum(levels)
+ max_level = maximum(levels)
+
+ mpi_println(" minlevel = $min_level")
+ mpi_println(" maxlevel = $max_level")
+
+ if min_level > 0
+ elements_per_level = [count(==(l), levels) for l in 1:max_level]
+
+ for level in max_level:-1:(min_level + 1)
+ mpi_println(" ├── level $level: " *
+ @sprintf("% 14d", elements_per_level[level]))
+ end
+ mpi_println(" └── level $min_level: " *
+ @sprintf("% 14d", elements_per_level[min_level]))
+ end
+
+ return nothing
+end
+
# Iterate over tuples of analysis integrals in a type-stable way using "lispy tuple programming".
function analyze_integrals(analysis_integrals::NTuple{N, Any}, io, du, u, t,
semi) where {N}
diff --git a/src/callbacks_step/analysis_dg2d.jl b/src/callbacks_step/analysis_dg2d.jl
index 6c74e172e46..4e456f79872 100644
--- a/src/callbacks_step/analysis_dg2d.jl
+++ b/src/callbacks_step/analysis_dg2d.jl
@@ -31,7 +31,7 @@ end
function create_cache_analysis(analyzer,
mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
equations, dg::DG, cache,
RealT, uEltype)
@@ -108,7 +108,7 @@ end
function calc_error_norms(func, u, t, analyzer,
mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}}, equations,
+ P4estMesh{2}, T8codeMesh{2}}, equations,
initial_condition, dg::DGSEM, cache, cache_analysis)
@unpack vandermonde, weights = analyzer
@unpack node_coordinates, inverse_jacobian = cache.elements
@@ -176,7 +176,7 @@ end
function integrate_via_indices(func::Func, u,
mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}}, equations,
+ P4estMesh{2}, T8codeMesh{2}}, equations,
dg::DGSEM, cache, args...; normalize = true) where {Func}
@unpack weights = dg.basis
@@ -204,7 +204,7 @@ end
function integrate(func::Func, u,
mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
equations, dg::DG, cache; normalize = true) where {Func}
integrate_via_indices(u, mesh, equations, dg, cache;
normalize = normalize) do u, i, j, element, equations, dg
@@ -215,7 +215,7 @@ end
function analyze(::typeof(entropy_timederivative), du, u, t,
mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
equations, dg::DG, cache)
# Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ
integrate_via_indices(u, mesh, equations, dg, cache,
@@ -259,7 +259,8 @@ function analyze(::Val{:l2_divb}, du, u, t,
end
function analyze(::Val{:l2_divb}, du, u, t,
- mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}},
+ mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
equations::IdealGlmMhdEquations2D, dg::DGSEM, cache)
@unpack contravariant_vectors = cache.elements
integrate_via_indices(u, mesh, equations, dg, cache, cache,
@@ -326,7 +327,8 @@ function analyze(::Val{:linf_divb}, du, u, t,
end
function analyze(::Val{:linf_divb}, du, u, t,
- mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}},
+ mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
equations::IdealGlmMhdEquations2D, dg::DGSEM, cache)
@unpack derivative_matrix, weights = dg.basis
@unpack contravariant_vectors = cache.elements
diff --git a/src/callbacks_step/save_restart_dg.jl b/src/callbacks_step/save_restart_dg.jl
index 5695eb8bede..8db6db2d2b8 100644
--- a/src/callbacks_step/save_restart_dg.jl
+++ b/src/callbacks_step/save_restart_dg.jl
@@ -7,7 +7,8 @@
function save_restart_file(u, time, dt, timestep,
mesh::Union{SerialTreeMesh, StructuredMesh,
- UnstructuredMesh2D, SerialP4estMesh},
+ UnstructuredMesh2D, SerialP4estMesh,
+ SerialT8codeMesh},
equations, dg::DG, cache,
restart_callback)
@unpack output_directory = restart_callback
diff --git a/src/callbacks_step/save_solution_dg.jl b/src/callbacks_step/save_solution_dg.jl
index 6cd4a0ec9c1..6d5004ff65f 100644
--- a/src/callbacks_step/save_solution_dg.jl
+++ b/src/callbacks_step/save_solution_dg.jl
@@ -7,7 +7,8 @@
function save_solution_file(u, time, dt, timestep,
mesh::Union{SerialTreeMesh, StructuredMesh,
- UnstructuredMesh2D, SerialP4estMesh},
+ UnstructuredMesh2D, SerialP4estMesh,
+ SerialT8codeMesh},
equations, dg::DG, cache,
solution_callback, element_variables = Dict{Symbol, Any}();
system = "")
diff --git a/src/callbacks_step/stepsize_dg2d.jl b/src/callbacks_step/stepsize_dg2d.jl
index 89a2b2b8350..673c3ba6aa6 100644
--- a/src/callbacks_step/stepsize_dg2d.jl
+++ b/src/callbacks_step/stepsize_dg2d.jl
@@ -75,7 +75,9 @@ function max_dt(u, t, mesh::ParallelTreeMesh{2},
return dt
end
-function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}},
+function max_dt(u, t,
+ mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
constant_speed::False, equations, dg::DG, cache)
# to avoid a division by zero if the speed vanishes everywhere,
# e.g. for steady-state linear advection
@@ -109,7 +111,9 @@ function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMe
return 2 / (nnodes(dg) * max_scaled_speed)
end
-function max_dt(u, t, mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2}},
+function max_dt(u, t,
+ mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
constant_speed::True, equations, dg::DG, cache)
@unpack contravariant_vectors, inverse_jacobian = cache.elements
diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl
index da67fe23e0e..b9895e7d454 100644
--- a/src/meshes/mesh_io.jl
+++ b/src/meshes/mesh_io.jl
@@ -6,7 +6,7 @@
#! format: noindent
# Save current mesh with some context information as an HDF5 file.
-function save_mesh_file(mesh::Union{TreeMesh, P4estMesh}, output_directory,
+function save_mesh_file(mesh::Union{TreeMesh, P4estMesh, T8codeMesh}, output_directory,
timestep = 0)
save_mesh_file(mesh, output_directory, timestep, mpi_parallel(mesh))
end
@@ -220,6 +220,13 @@ function save_mesh_file(mesh::P4estMesh, output_directory, timestep, mpi_paralle
return filename
end
+# TODO: Implement this function as soon as there is support for this in `t8code`.
+function save_mesh_file(mesh::T8codeMesh, output_directory, timestep, mpi_parallel)
+ error("Mesh file output not supported yet for `T8codeMesh`.")
+
+ return joinpath(output_directory, "dummy_mesh.h5")
+end
+
"""
load_mesh(restart_file::AbstractString; n_cells_max)
diff --git a/src/meshes/meshes.jl b/src/meshes/meshes.jl
index 2716aa2007b..ed2158b169a 100644
--- a/src/meshes/meshes.jl
+++ b/src/meshes/meshes.jl
@@ -12,6 +12,7 @@ include("unstructured_mesh.jl")
include("face_interpolant.jl")
include("transfinite_mappings_3d.jl")
include("p4est_mesh.jl")
+include("t8code_mesh.jl")
include("mesh_io.jl")
include("dgmulti_meshes.jl")
end # @muladd
diff --git a/src/meshes/t8code_mesh.jl b/src/meshes/t8code_mesh.jl
new file mode 100644
index 00000000000..13edcc29711
--- /dev/null
+++ b/src/meshes/t8code_mesh.jl
@@ -0,0 +1,345 @@
+"""
+ T8codeMesh{NDIMS} <: AbstractMesh{NDIMS}
+
+An unstructured curved mesh based on trees that uses the C library
+['t8code'](https://github.com/DLR-AMR/t8code)
+to manage trees and mesh refinement.
+"""
+mutable struct T8codeMesh{NDIMS, RealT <: Real, IsParallel, NDIMSP2, NNODES} <:
+ AbstractMesh{NDIMS}
+ cmesh :: Ptr{t8_cmesh} # cpointer to coarse mesh
+ scheme :: Ptr{t8_eclass_scheme} # cpointer to element scheme
+ forest :: Ptr{t8_forest} # cpointer to forest
+ is_parallel :: IsParallel
+
+ # This specifies the geometry interpolation for each tree.
+ tree_node_coordinates::Array{RealT, NDIMSP2} # [dimension, i, j, k, tree]
+
+ # Stores the quadrature nodes.
+ nodes::SVector{NNODES, RealT}
+
+ boundary_names :: Array{Symbol, 2} # [face direction, tree]
+ current_filename :: String
+
+ ninterfaces :: Int
+ nmortars :: Int
+ nboundaries :: Int
+
+ function T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes,
+ boundary_names,
+ current_filename) where {NDIMS}
+ is_parallel = False()
+
+ mesh = new{NDIMS, Float64, typeof(is_parallel), NDIMS + 2, length(nodes)}(cmesh,
+ scheme,
+ forest,
+ is_parallel)
+
+ mesh.nodes = nodes
+ mesh.boundary_names = boundary_names
+ mesh.current_filename = current_filename
+ mesh.tree_node_coordinates = tree_node_coordinates
+
+ finalizer(mesh) do mesh
+ # When finalizing `mesh.forest`, `mesh.scheme` and `mesh.cmesh` are
+ # also cleaned up from within `t8code`. The cleanup code for
+ # `cmesh` does some MPI calls for deallocating shared memory
+ # arrays. Due to garbage collection in Julia the order of shutdown
+ # is not deterministic. The following code might happen after MPI
+ # is already in finalized state.
+ # If the environment variable `TRIXI_T8CODE_SC_FINALIZE` is set the
+ # `finalize_hook` of the MPI module takes care of the cleanup. See
+ # further down. However, this might cause a pile-up of `mesh`
+ # objects during long-running sessions.
+ if !MPI.Finalized()
+ trixi_t8_unref_forest(mesh.forest)
+ end
+ end
+
+ # This finalizer call is only recommended during development and not for
+ # production runs, especially long-running sessions since a reference to
+ # the `mesh` object will be kept throughout the lifetime of the session.
+ # See comments in `init_t8code()` in file `src/auxiliary/t8code.jl` for
+ # more information.
+ if haskey(ENV, "TRIXI_T8CODE_SC_FINALIZE")
+ MPI.add_finalize_hook!() do
+ trixi_t8_unref_forest(mesh.forest)
+ end
+ end
+
+ return mesh
+ end
+end
+
+const SerialT8codeMesh{NDIMS} = T8codeMesh{NDIMS, <:Real, <:False}
+@inline mpi_parallel(mesh::SerialT8codeMesh) = False()
+
+@inline Base.ndims(::T8codeMesh{NDIMS}) where {NDIMS} = NDIMS
+@inline Base.real(::T8codeMesh{NDIMS, RealT}) where {NDIMS, RealT} = RealT
+
+@inline ntrees(mesh::T8codeMesh) = Int(t8_forest_get_num_local_trees(mesh.forest))
+@inline ncells(mesh::T8codeMesh) = Int(t8_forest_get_local_num_elements(mesh.forest))
+@inline ninterfaces(mesh::T8codeMesh) = mesh.ninterfaces
+@inline nmortars(mesh::T8codeMesh) = mesh.nmortars
+@inline nboundaries(mesh::T8codeMesh) = mesh.nboundaries
+
+function Base.show(io::IO, mesh::T8codeMesh)
+ print(io, "T8codeMesh{", ndims(mesh), ", ", real(mesh), "}")
+end
+
+function Base.show(io::IO, ::MIME"text/plain", mesh::T8codeMesh)
+ if get(io, :compact, false)
+ show(io, mesh)
+ else
+ setup = [
+ "#trees" => ntrees(mesh),
+ "current #cells" => ncells(mesh),
+ "polydeg" => length(mesh.nodes) - 1,
+ ]
+ summary_box(io,
+ "T8codeMesh{" * string(ndims(mesh)) * ", " * string(real(mesh)) * "}",
+ setup)
+ end
+end
+
+"""
+ T8codeMesh(trees_per_dimension; polydeg, mapping=identity,
+ RealT=Float64, initial_refinement_level=0, periodicity=true)
+
+Create a structured potentially curved 'T8codeMesh' of the specified size.
+
+Non-periodic boundaries will be called ':x_neg', ':x_pos', ':y_neg', ':y_pos', ':z_neg', ':z_pos'.
+
+# Arguments
+- 'trees_per_dimension::NTupleE{NDIMS, Int}': the number of trees in each dimension.
+- 'polydeg::Integer': polynomial degree used to store the geometry of the mesh.
+ The mapping will be approximated by an interpolation polynomial
+ of the specified degree for each tree.
+- 'mapping': a function of 'NDIMS' variables to describe the mapping that transforms
+ the reference mesh ('[-1, 1]^n') to the physical domain.
+- 'RealT::Type': the type that should be used for coordinates.
+- 'initial_refinement_level::Integer': refine the mesh uniformly to this level before the simulation starts.
+- 'periodicity': either a 'Bool' deciding if all of the boundaries are periodic or an 'NTuple{NDIMS, Bool}'
+ deciding for each dimension if the boundaries in this dimension are periodic.
+"""
+function T8codeMesh(trees_per_dimension; polydeg,
+ mapping = coordinates2mapping((-1.0, -1.0), (1.0, 1.0)),
+ RealT = Float64, initial_refinement_level = 0, periodicity = true)
+ NDIMS = length(trees_per_dimension)
+
+ @assert NDIMS == 2 # Only support for NDIMS = 2 yet.
+
+ # Convert periodicity to a Tuple of a Bool for every dimension
+ if all(periodicity)
+ # Also catches case where periodicity = true
+ periodicity = ntuple(_ -> true, NDIMS)
+ elseif !any(periodicity)
+ # Also catches case where periodicity = false
+ periodicity = ntuple(_ -> false, NDIMS)
+ else
+ # Default case if periodicity is an iterable
+ periodicity = Tuple(periodicity)
+ end
+
+ conn = T8code.Libt8.p4est_connectivity_new_brick(trees_per_dimension..., periodicity...)
+ do_partition = 0
+ cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), do_partition)
+ T8code.Libt8.p4est_connectivity_destroy(conn)
+
+ scheme = t8_scheme_new_default_cxx()
+ forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm())
+
+ basis = LobattoLegendreBasis(RealT, polydeg)
+ nodes = basis.nodes
+
+ tree_node_coordinates = Array{RealT, NDIMS + 2}(undef, NDIMS,
+ ntuple(_ -> length(nodes), NDIMS)...,
+ prod(trees_per_dimension))
+
+ # Get cell length in reference mesh: Omega_ref = [-1,1]^2.
+ dx = 2 / trees_per_dimension[1]
+ dy = 2 / trees_per_dimension[2]
+
+ num_local_trees = t8_cmesh_get_num_local_trees(cmesh)
+
+ # Non-periodic boundaries.
+ boundary_names = fill(Symbol("---"), 2 * NDIMS, prod(trees_per_dimension))
+
+ for itree in 1:num_local_trees
+ veptr = t8_cmesh_get_tree_vertices(cmesh, itree - 1)
+ verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS))
+
+ # Calculate node coordinates of reference mesh.
+ cell_x_offset = (verts[1, 1] - 1 / 2 * (trees_per_dimension[1] - 1)) * dx
+ cell_y_offset = (verts[2, 1] - 1 / 2 * (trees_per_dimension[2] - 1)) * dy
+
+ for j in eachindex(nodes), i in eachindex(nodes)
+ tree_node_coordinates[:, i, j, itree] .= mapping(cell_x_offset +
+ dx * nodes[i] / 2,
+ cell_y_offset +
+ dy * nodes[j] / 2)
+ end
+
+ if !periodicity[1]
+ boundary_names[1, itree] = :x_neg
+ boundary_names[2, itree] = :x_pos
+ end
+
+ if !periodicity[2]
+ boundary_names[3, itree] = :y_neg
+ boundary_names[4, itree] = :y_pos
+ end
+ end
+
+ return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes,
+ boundary_names, "")
+end
+
+"""
+ T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh},
+ mapping=nothing, polydeg=1, RealT=Float64,
+ initial_refinement_level=0)
+
+Main mesh constructor for the `T8codeMesh` that imports an unstructured,
+conforming mesh from a `t8_cmesh` data structure.
+
+# Arguments
+- `cmesh::Ptr{t8_cmesh}`: Pointer to a cmesh object.
+- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms
+ the imported mesh to the physical domain. Use `nothing` for the identity map.
+- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh.
+ The mapping will be approximated by an interpolation polynomial
+ of the specified degree for each tree.
+ The default of `1` creates an uncurved geometry. Use a higher value if the mapping
+ will curve the imported uncurved mesh.
+- `RealT::Type`: the type that should be used for coordinates.
+- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts.
+"""
+function T8codeMesh{NDIMS}(cmesh::Ptr{t8_cmesh};
+ mapping = nothing, polydeg = 1, RealT = Float64,
+ initial_refinement_level = 0) where {NDIMS}
+ @assert NDIMS == 2 # Only support for NDIMS = 2 yet.
+
+ scheme = t8_scheme_new_default_cxx()
+ forest = t8_forest_new_uniform(cmesh, scheme, initial_refinement_level, 0, mpi_comm())
+
+ basis = LobattoLegendreBasis(RealT, polydeg)
+ nodes = basis.nodes
+
+ num_local_trees = t8_cmesh_get_num_local_trees(cmesh)
+
+ tree_node_coordinates = Array{RealT, NDIMS + 2}(undef, NDIMS,
+ ntuple(_ -> length(nodes), NDIMS)...,
+ num_local_trees)
+
+ nodes_in = [-1.0, 1.0]
+ matrix = polynomial_interpolation_matrix(nodes_in, nodes)
+ data_in = Array{RealT, 3}(undef, 2, 2, 2)
+ tmp1 = zeros(RealT, 2, length(nodes), length(nodes_in))
+
+ for itree in 0:(num_local_trees - 1)
+ veptr = t8_cmesh_get_tree_vertices(cmesh, itree)
+ verts = unsafe_wrap(Array, veptr, (3, 1 << NDIMS))
+
+ u = verts[:, 2] - verts[:, 1]
+ v = verts[:, 3] - verts[:, 1]
+ w = [0.0, 0.0, 1.0]
+
+ vol = dot(cross(u, v), w)
+
+ if vol < 0.0
+ @warn "Discovered negative volumes in `cmesh`: vol = $vol"
+ end
+
+ # Tree vertices are stored in z-order.
+ @views data_in[:, 1, 1] .= verts[1:2, 1]
+ @views data_in[:, 2, 1] .= verts[1:2, 2]
+ @views data_in[:, 1, 2] .= verts[1:2, 3]
+ @views data_in[:, 2, 2] .= verts[1:2, 4]
+
+ # Interpolate corner coordinates to specified nodes.
+ multiply_dimensionwise!(view(tree_node_coordinates, :, :, :, itree + 1),
+ matrix, matrix,
+ data_in,
+ tmp1)
+ end
+
+ map_node_coordinates!(tree_node_coordinates, mapping)
+
+ # There's no simple and generic way to distinguish boundaries. Name all of them :all.
+ boundary_names = fill(:all, 2 * NDIMS, num_local_trees)
+
+ return T8codeMesh{NDIMS}(cmesh, scheme, forest, tree_node_coordinates, nodes,
+ boundary_names, "")
+end
+
+"""
+ T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity},
+ mapping=nothing, polydeg=1, RealT=Float64,
+ initial_refinement_level=0)
+
+Main mesh constructor for the `T8codeMesh` that imports an unstructured,
+conforming mesh from a `p4est_connectivity` data structure.
+
+# Arguments
+- `conn::Ptr{p4est_connectivity}`: Pointer to a P4est connectivity object.
+- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms
+ the imported mesh to the physical domain. Use `nothing` for the identity map.
+- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh.
+ The mapping will be approximated by an interpolation polynomial
+ of the specified degree for each tree.
+ The default of `1` creates an uncurved geometry. Use a higher value if the mapping
+ will curve the imported uncurved mesh.
+- `RealT::Type`: the type that should be used for coordinates.
+- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts.
+"""
+function T8codeMesh{NDIMS}(conn::Ptr{p4est_connectivity}; kwargs...) where {NDIMS}
+ @assert NDIMS == 2 # Only support for NDIMS = 2 yet.
+
+ cmesh = t8_cmesh_new_from_p4est(conn, mpi_comm(), 0)
+
+ return T8codeMesh{NDIMS}(cmesh; kwargs...)
+end
+
+"""
+ T8codeMesh{NDIMS}(meshfile::String;
+ mapping=nothing, polydeg=1, RealT=Float64,
+ initial_refinement_level=0)
+
+Main mesh constructor for the `T8codeMesh` that imports an unstructured, conforming
+mesh from a Gmsh mesh file (`.msh`).
+
+# Arguments
+- `meshfile::String`: path to a Gmsh mesh file.
+- `mapping`: a function of `NDIMS` variables to describe the mapping that transforms
+ the imported mesh to the physical domain. Use `nothing` for the identity map.
+- `polydeg::Integer`: polynomial degree used to store the geometry of the mesh.
+ The mapping will be approximated by an interpolation polynomial
+ of the specified degree for each tree.
+ The default of `1` creates an uncurved geometry. Use a higher value if the mapping
+ will curve the imported uncurved mesh.
+- `RealT::Type`: the type that should be used for coordinates.
+- `initial_refinement_level::Integer`: refine the mesh uniformly to this level before the simulation starts.
+"""
+function T8codeMesh{NDIMS}(meshfile::String; kwargs...) where {NDIMS}
+ @assert NDIMS == 2 # Only support for NDIMS = 2 yet.
+
+ # Prevent `t8code` from crashing Julia if the file doesn't exist.
+ @assert isfile(meshfile)
+
+ meshfile_prefix, meshfile_suffix = splitext(meshfile)
+
+ cmesh = t8_cmesh_from_msh_file(meshfile_prefix, 0, mpi_comm(), NDIMS, 0, 0)
+
+ return T8codeMesh{NDIMS}(cmesh; kwargs...)
+end
+
+# TODO: Just a placeholder. Will be implemented later when MPI is supported.
+function balance!(mesh::T8codeMesh, init_fn = C_NULL)
+ return nothing
+end
+
+# TODO: Just a placeholder. Will be implemented later when MPI is supported.
+function partition!(mesh::T8codeMesh; allow_coarsening = true, weight_fn = C_NULL)
+ return nothing
+end
diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl
index 2536cfe0bf2..495e0ffc4a4 100644
--- a/src/solvers/dg.jl
+++ b/src/solvers/dg.jl
@@ -363,7 +363,8 @@ function get_element_variables!(element_variables, u, mesh, equations, dg::DG, c
dg, cache)
end
-const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh}
+const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh,
+ T8codeMesh}
@inline function ndofs(mesh::MeshesDGSEM, dg::DG, cache)
nelements(cache.elements) * nnodes(dg)^ndims(mesh)
@@ -679,4 +680,5 @@ include("dgsem_tree/dg.jl")
include("dgsem_structured/dg.jl")
include("dgsem_unstructured/dg.jl")
include("dgsem_p4est/dg.jl")
+include("dgsem_t8code/dg.jl")
end # @muladd
diff --git a/src/solvers/dgsem_p4est/containers.jl b/src/solvers/dgsem_p4est/containers.jl
index 2b9c6987d24..0176f5c6346 100644
--- a/src/solvers/dgsem_p4est/containers.jl
+++ b/src/solvers/dgsem_p4est/containers.jl
@@ -81,7 +81,8 @@ function Base.resize!(elements::P4estElementContainer, capacity)
end
# Create element container and initialize element data
-function init_elements(mesh::P4estMesh{NDIMS, RealT}, equations,
+function init_elements(mesh::Union{P4estMesh{NDIMS, RealT}, T8codeMesh{NDIMS, RealT}},
+ equations,
basis,
::Type{uEltype}) where {NDIMS, RealT <: Real, uEltype <: Real}
nelements = ncells(mesh)
@@ -165,7 +166,7 @@ function Base.resize!(interfaces::P4estInterfaceContainer, capacity)
end
# Create interface container and initialize interface data.
-function init_interfaces(mesh::P4estMesh, equations, basis, elements)
+function init_interfaces(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements)
NDIMS = ndims(elements)
uEltype = eltype(elements)
@@ -240,7 +241,7 @@ function Base.resize!(boundaries::P4estBoundaryContainer, capacity)
end
# Create interface container and initialize interface data in `elements`.
-function init_boundaries(mesh::P4estMesh, equations, basis, elements)
+function init_boundaries(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements)
NDIMS = ndims(elements)
uEltype = eltype(elements)
@@ -371,7 +372,7 @@ function Base.resize!(mortars::P4estMortarContainer, capacity)
end
# Create mortar container and initialize mortar data.
-function init_mortars(mesh::P4estMesh, equations, basis, elements)
+function init_mortars(mesh::Union{P4estMesh, T8codeMesh}, equations, basis, elements)
NDIMS = ndims(elements)
uEltype = eltype(elements)
diff --git a/src/solvers/dgsem_p4est/containers_2d.jl b/src/solvers/dgsem_p4est/containers_2d.jl
index 11747f1f175..236d7d24c06 100644
--- a/src/solvers/dgsem_p4est/containers_2d.jl
+++ b/src/solvers/dgsem_p4est/containers_2d.jl
@@ -6,7 +6,8 @@
#! format: noindent
# Initialize data structures in element container
-function init_elements!(elements, mesh::P4estMesh{2}, basis::LobattoLegendreBasis)
+function init_elements!(elements, mesh::Union{P4estMesh{2}, T8codeMesh{2}},
+ basis::LobattoLegendreBasis)
@unpack node_coordinates, jacobian_matrix,
contravariant_vectors, inverse_jacobian = elements
@@ -25,7 +26,7 @@ end
# Interpolate tree_node_coordinates to each quadrant at the nodes of the specified basis
function calc_node_coordinates!(node_coordinates,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
basis::LobattoLegendreBasis)
# Hanging nodes will cause holes in the mesh if its polydeg is higher
# than the polydeg of the solver.
diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl
index bc7d9edb6ef..97b931fa325 100644
--- a/src/solvers/dgsem_p4est/dg_2d.jl
+++ b/src/solvers/dgsem_p4est/dg_2d.jl
@@ -7,8 +7,8 @@
# The methods below are specialized on the mortar type
# and called from the basic `create_cache` method at the top.
-function create_cache(mesh::P4estMesh{2}, equations, mortar_l2::LobattoLegendreMortarL2,
- uEltype)
+function create_cache(mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations,
+ mortar_l2::LobattoLegendreMortarL2, uEltype)
# TODO: Taal performance using different types
MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)},
uEltype, 2,
@@ -58,7 +58,7 @@ end
# We pass the `surface_integral` argument solely for dispatch
function prolong2interfaces!(cache, u,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
equations, surface_integral, dg::DG)
@unpack interfaces = cache
index_range = eachnode(dg)
@@ -114,7 +114,7 @@ function prolong2interfaces!(cache, u,
end
function calc_interface_flux!(surface_flux_values,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms,
equations, surface_integral, dg::DG, cache)
@unpack neighbor_ids, node_indices = cache.interfaces
@@ -182,7 +182,7 @@ end
# Inlined version of the interface flux computation for conservation laws
@inline function calc_interface_flux!(surface_flux_values,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::False, equations,
surface_integral, dg::DG, cache,
interface_index, normal_direction,
@@ -206,7 +206,7 @@ end
# Inlined version of the interface flux computation for equations with conservative and nonconservative terms
@inline function calc_interface_flux!(surface_flux_values,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::True, equations,
surface_integral, dg::DG, cache,
interface_index, normal_direction,
@@ -247,7 +247,7 @@ end
end
function prolong2boundaries!(cache, u,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
equations, surface_integral, dg::DG)
@unpack boundaries = cache
index_range = eachnode(dg)
@@ -276,7 +276,7 @@ function prolong2boundaries!(cache, u,
end
function calc_boundary_flux!(cache, t, boundary_condition, boundary_indexing,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
equations, surface_integral, dg::DG)
@unpack boundaries = cache
@unpack surface_flux_values = cache.elements
@@ -312,7 +312,7 @@ end
# inlined version of the boundary flux calculation along a physical interface
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::False, equations,
surface_integral, dg::DG, cache,
i_index, j_index,
@@ -343,7 +343,7 @@ end
# inlined version of the boundary flux with nonconservative terms calculation along a physical interface
@inline function calc_boundary_flux!(surface_flux_values, t, boundary_condition,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::True, equations,
surface_integral, dg::DG, cache,
i_index, j_index,
@@ -385,7 +385,7 @@ end
end
function prolong2mortars!(cache, u,
- mesh::P4estMesh{2}, equations,
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}}, equations,
mortar_l2::LobattoLegendreMortarL2,
surface_integral, dg::DGSEM)
@unpack neighbor_ids, node_indices = cache.mortars
@@ -452,7 +452,7 @@ function prolong2mortars!(cache, u,
end
function calc_mortar_flux!(surface_flux_values,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms, equations,
mortar_l2::LobattoLegendreMortarL2,
surface_integral, dg::DG, cache)
@@ -511,7 +511,7 @@ end
# Inlined version of the mortar flux computation on small elements for conservation laws
@inline function calc_mortar_flux!(fstar,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::False, equations,
surface_integral, dg::DG, cache,
mortar_index, position_index, normal_direction,
@@ -531,7 +531,7 @@ end
# Inlined version of the mortar flux computation on small elements for equations with conservative and
# nonconservative terms
@inline function calc_mortar_flux!(fstar,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::True, equations,
surface_integral, dg::DG, cache,
mortar_index, position_index, normal_direction,
@@ -559,7 +559,8 @@ end
end
@inline function mortar_fluxes_to_elements!(surface_flux_values,
- mesh::P4estMesh{2}, equations,
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
+ equations,
mortar_l2::LobattoLegendreMortarL2,
dg::DGSEM, cache, mortar, fstar, u_buffer)
@unpack neighbor_ids, node_indices = cache.mortars
@@ -620,7 +621,7 @@ end
end
function calc_surface_integral!(du, u,
- mesh::P4estMesh{2},
+ mesh::Union{P4estMesh{2}, T8codeMesh{2}},
equations,
surface_integral::SurfaceIntegralWeakForm,
dg::DGSEM, cache)
diff --git a/src/solvers/dgsem_structured/dg_2d.jl b/src/solvers/dgsem_structured/dg_2d.jl
index c013bf62d98..3e8ce759b30 100644
--- a/src/solvers/dgsem_structured/dg_2d.jl
+++ b/src/solvers/dgsem_structured/dg_2d.jl
@@ -52,7 +52,7 @@ end
@inline function weak_form_kernel!(du, u,
element,
mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::False, equations,
dg::DGSEM, cache, alpha = true)
# true * [some floating point value] == [exactly the same floating point value]
@@ -93,8 +93,8 @@ end
@inline function flux_differencing_kernel!(du, u,
element,
mesh::Union{StructuredMesh{2},
- UnstructuredMesh2D, P4estMesh{2}
- },
+ UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
nonconservative_terms::False, equations,
volume_flux, dg::DGSEM, cache, alpha = true)
@unpack derivative_split = dg.basis
@@ -150,8 +150,8 @@ end
@inline function flux_differencing_kernel!(du, u,
element,
mesh::Union{StructuredMesh{2},
- UnstructuredMesh2D, P4estMesh{2}
- },
+ UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
nonconservative_terms::True, equations,
volume_flux, dg::DGSEM, cache, alpha = true)
@unpack derivative_split = dg.basis
@@ -219,7 +219,7 @@ end
# [arXiv: 2008.12044v2](https://arxiv.org/pdf/2008.12044)
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u,
mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::False, equations,
volume_flux_fv, dg::DGSEM, element, cache)
@unpack contravariant_vectors = cache.elements
@@ -289,7 +289,7 @@ end
@inline function calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R,
u::AbstractArray{<:Any, 4},
mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
nonconservative_terms::True, equations,
volume_flux_fv, dg::DGSEM, element, cache)
@unpack contravariant_vectors = cache.elements
@@ -609,9 +609,8 @@ function calc_boundary_flux!(cache, u, t, boundary_conditions::NamedTuple,
end
function apply_jacobian!(du,
- mesh::Union{StructuredMesh{2}, UnstructuredMesh2D, P4estMesh{2
- }
- },
+ mesh::Union{StructuredMesh{2}, UnstructuredMesh2D,
+ P4estMesh{2}, T8codeMesh{2}},
equations, dg::DG, cache)
@unpack inverse_jacobian = cache.elements
diff --git a/src/solvers/dgsem_t8code/containers.jl b/src/solvers/dgsem_t8code/containers.jl
new file mode 100644
index 00000000000..093feb2985a
--- /dev/null
+++ b/src/solvers/dgsem_t8code/containers.jl
@@ -0,0 +1,60 @@
+function reinitialize_containers!(mesh::T8codeMesh, equations, dg::DGSEM, cache)
+ # Re-initialize elements container.
+ @unpack elements = cache
+ resize!(elements, ncells(mesh))
+ init_elements!(elements, mesh, dg.basis)
+
+ count_required_surfaces!(mesh)
+
+ # Resize interfaces container.
+ @unpack interfaces = cache
+ resize!(interfaces, mesh.ninterfaces)
+
+ # Resize mortars container.
+ @unpack mortars = cache
+ resize!(mortars, mesh.nmortars)
+
+ # Resize boundaries container.
+ @unpack boundaries = cache
+ resize!(boundaries, mesh.nboundaries)
+
+ trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries,
+ mesh.boundary_names)
+
+ return nothing
+end
+
+function count_required_surfaces!(mesh::T8codeMesh)
+ counts = trixi_t8_count_interfaces(mesh.forest)
+
+ mesh.nmortars = counts.mortars
+ mesh.ninterfaces = counts.interfaces
+ mesh.nboundaries = counts.boundaries
+
+ return counts
+end
+
+# Compatibility to `dgsem_p4est/containers.jl`.
+function count_required_surfaces(mesh::T8codeMesh)
+ return (interfaces = mesh.ninterfaces,
+ mortars = mesh.nmortars,
+ boundaries = mesh.nboundaries)
+end
+
+# Compatibility to `dgsem_p4est/containers.jl`.
+function init_interfaces!(interfaces, mesh::T8codeMesh)
+ # Already computed. Do nothing.
+ return nothing
+end
+
+# Compatibility to `dgsem_p4est/containers.jl`.
+function init_mortars!(mortars, mesh::T8codeMesh)
+ # Already computed. Do nothing.
+ return nothing
+end
+
+# Compatibility to `dgsem_p4est/containers.jl`.
+function init_boundaries!(boundaries, mesh::T8codeMesh)
+ # Already computed. Do nothing.
+ return nothing
+end
diff --git a/src/solvers/dgsem_t8code/containers_2d.jl b/src/solvers/dgsem_t8code/containers_2d.jl
new file mode 100644
index 00000000000..029e6674afb
--- /dev/null
+++ b/src/solvers/dgsem_t8code/containers_2d.jl
@@ -0,0 +1,58 @@
+@muladd begin
+#! format: noindent
+
+# Interpolate tree_node_coordinates to each quadrant at the specified nodes.
+function calc_node_coordinates!(node_coordinates,
+ mesh::T8codeMesh{2},
+ nodes::AbstractVector)
+ # We use `StrideArray`s here since these buffers are used in performance-critical
+ # places and the additional information passed to the compiler makes them faster
+ # than native `Array`s.
+ tmp1 = StrideArray(undef, real(mesh),
+ StaticInt(2), static_length(nodes), static_length(mesh.nodes))
+ matrix1 = StrideArray(undef, real(mesh),
+ static_length(nodes), static_length(mesh.nodes))
+ matrix2 = similar(matrix1)
+ baryweights_in = barycentric_weights(mesh.nodes)
+
+ num_local_trees = t8_forest_get_num_local_trees(mesh.forest)
+
+ current_index = 0
+ for itree in 0:(num_local_trees - 1)
+ tree_class = t8_forest_get_tree_class(mesh.forest, itree)
+ eclass_scheme = t8_forest_get_eclass_scheme(mesh.forest, tree_class)
+ num_elements_in_tree = t8_forest_get_tree_num_elements(mesh.forest, itree)
+
+ for ielement in 0:(num_elements_in_tree - 1)
+ element = t8_forest_get_element_in_tree(mesh.forest, itree, ielement)
+ element_level = t8_element_level(eclass_scheme, element)
+
+ element_length = t8_quad_len(element_level) / t8_quad_root_len
+
+ element_coords = Array{Float64}(undef, 3)
+ t8_element_vertex_reference_coords(eclass_scheme, element, 0,
+ pointer(element_coords))
+
+ nodes_out_x = 2 *
+ (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[1]) .-
+ 1
+ nodes_out_y = 2 *
+ (element_length * 1 / 2 * (nodes .+ 1) .+ element_coords[2]) .-
+ 1
+
+ polynomial_interpolation_matrix!(matrix1, mesh.nodes, nodes_out_x,
+ baryweights_in)
+ polynomial_interpolation_matrix!(matrix2, mesh.nodes, nodes_out_y,
+ baryweights_in)
+
+ multiply_dimensionwise!(view(node_coordinates, :, :, :, current_index += 1),
+ matrix1, matrix2,
+ view(mesh.tree_node_coordinates, :, :, :,
+ itree + 1),
+ tmp1)
+ end
+ end
+
+ return node_coordinates
+end
+end # @muladd
diff --git a/src/solvers/dgsem_t8code/dg.jl b/src/solvers/dgsem_t8code/dg.jl
new file mode 100644
index 00000000000..16a9d7d35b1
--- /dev/null
+++ b/src/solvers/dgsem_t8code/dg.jl
@@ -0,0 +1,31 @@
+@muladd begin
+#! format: noindent
+
+# This method is called when a SemidiscretizationHyperbolic is constructed.
+# It constructs the basic `cache` used throughout the simulation to compute
+# the RHS etc.
+function create_cache(mesh::T8codeMesh, equations::AbstractEquations, dg::DG, ::Any,
+ ::Type{uEltype}) where {uEltype <: Real}
+ count_required_surfaces!(mesh)
+
+ elements = init_elements(mesh, equations, dg.basis, uEltype)
+ interfaces = init_interfaces(mesh, equations, dg.basis, elements)
+ boundaries = init_boundaries(mesh, equations, dg.basis, elements)
+ mortars = init_mortars(mesh, equations, dg.basis, elements)
+
+ trixi_t8_fill_mesh_info(mesh.forest, elements, interfaces, mortars, boundaries,
+ mesh.boundary_names)
+
+ cache = (; elements, interfaces, boundaries, mortars)
+
+ # Add specialized parts of the cache required to compute the volume integral etc.
+ cache = (; cache...,
+ create_cache(mesh, equations, dg.volume_integral, dg, uEltype)...)
+ cache = (; cache..., create_cache(mesh, equations, dg.mortar, uEltype)...)
+
+ return cache
+end
+
+include("containers.jl")
+include("containers_2d.jl")
+end # @muladd
diff --git a/src/solvers/dgsem_tree/dg_2d.jl b/src/solvers/dgsem_tree/dg_2d.jl
index 6c5e0cee0cf..c30d0a8e01a 100644
--- a/src/solvers/dgsem_tree/dg_2d.jl
+++ b/src/solvers/dgsem_tree/dg_2d.jl
@@ -37,14 +37,14 @@ end
# The methods below are specialized on the volume integral type
# and called from the basic `create_cache` method at the top.
function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
equations, volume_integral::VolumeIntegralFluxDifferencing,
dg::DG, uEltype)
NamedTuple()
end
function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}}, equations,
+ P4estMesh{2}, T8codeMesh{2}}, equations,
volume_integral::VolumeIntegralShockCapturingHG, dg::DG, uEltype)
element_ids_dg = Int[]
element_ids_dgfv = Int[]
@@ -70,7 +70,7 @@ function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMe
end
function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}}, equations,
+ P4estMesh{2}, T8codeMesh{2}}, equations,
volume_integral::VolumeIntegralPureLGLFiniteVolume, dg::DG,
uEltype)
A3dp1_x = Array{uEltype, 3}
@@ -92,7 +92,7 @@ end
# The methods below are specialized on the mortar type
# and called from the basic `create_cache` method at the top.
function create_cache(mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
- P4estMesh{2}},
+ P4estMesh{2}, T8codeMesh{2}},
equations, mortar_l2::LobattoLegendreMortarL2, uEltype)
# TODO: Taal performance using different types
MA2d = MArray{Tuple{nvariables(equations), nnodes(mortar_l2)}, uEltype, 2,
@@ -110,7 +110,7 @@ end
# TODO: Taal discuss/refactor timer, allowing users to pass a custom timer?
function rhs!(du, u, t,
- mesh::Union{TreeMesh{2}, P4estMesh{2}}, equations,
+ mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, equations,
initial_condition, boundary_conditions, source_terms::Source,
dg::DG, cache) where {Source}
# Reset du
@@ -180,7 +180,8 @@ end
function calc_volume_integral!(du, u,
mesh::Union{TreeMesh{2}, StructuredMesh{2},
- UnstructuredMesh2D, P4estMesh{2}},
+ UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
nonconservative_terms, equations,
volume_integral::VolumeIntegralWeakForm,
dg::DGSEM, cache)
@@ -226,7 +227,8 @@ end
# from the evaluation of the physical fluxes in each Cartesian direction
function calc_volume_integral!(du, u,
mesh::Union{TreeMesh{2}, StructuredMesh{2},
- UnstructuredMesh2D, P4estMesh{2}},
+ UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
nonconservative_terms, equations,
volume_integral::VolumeIntegralFluxDifferencing,
dg::DGSEM, cache)
@@ -322,7 +324,8 @@ end
# TODO: Taal dimension agnostic
function calc_volume_integral!(du, u,
mesh::Union{TreeMesh{2}, StructuredMesh{2},
- UnstructuredMesh2D, P4estMesh{2}},
+ UnstructuredMesh2D, P4estMesh{2},
+ T8codeMesh{2}},
nonconservative_terms, equations,
volume_integral::VolumeIntegralShockCapturingHG,
dg::DGSEM, cache)
@@ -381,7 +384,8 @@ end
@inline function fv_kernel!(du, u,
mesh::Union{TreeMesh{2}, StructuredMesh{2},
- UnstructuredMesh2D, P4estMesh{2}},
+ UnstructuredMesh2D, P4estMesh{2}, T8codeMesh{2}
+ },
nonconservative_terms, equations,
volume_flux_fv, dg::DGSEM, cache, element, alpha = true)
@unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded = cache
diff --git a/src/solvers/dgsem_tree/indicators_2d.jl b/src/solvers/dgsem_tree/indicators_2d.jl
index f7c78547174..2f34e0eb661 100644
--- a/src/solvers/dgsem_tree/indicators_2d.jl
+++ b/src/solvers/dgsem_tree/indicators_2d.jl
@@ -208,7 +208,8 @@ end
end
# Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha
-function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}}, alpha, alpha_tmp, dg,
+function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha,
+ alpha_tmp, dg,
cache)
# Copy alpha values such that smoothing is indpedenent of the element access order
alpha_tmp .= alpha
diff --git a/src/solvers/dgsem_unstructured/dg_2d.jl b/src/solvers/dgsem_unstructured/dg_2d.jl
index 95dec027a82..7b8dafdddd2 100644
--- a/src/solvers/dgsem_unstructured/dg_2d.jl
+++ b/src/solvers/dgsem_unstructured/dg_2d.jl
@@ -307,14 +307,14 @@ end
# TODO: Taal dimension agnostic
function calc_boundary_flux!(cache, t, boundary_condition::BoundaryConditionPeriodic,
- mesh::Union{UnstructuredMesh2D, P4estMesh},
+ mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh},
equations, surface_integral, dg::DG)
@assert isempty(eachboundary(dg, cache))
end
# Function barrier for type stability
function calc_boundary_flux!(cache, t, boundary_conditions,
- mesh::Union{UnstructuredMesh2D, P4estMesh},
+ mesh::Union{UnstructuredMesh2D, P4estMesh, T8codeMesh},
equations, surface_integral, dg::DG)
@unpack boundary_condition_types, boundary_indices = boundary_conditions
@@ -327,7 +327,8 @@ end
# in a type-stable way using "lispy tuple programming".
function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any},
BC_indices::NTuple{N, Vector{Int}},
- mesh::Union{UnstructuredMesh2D, P4estMesh},
+ mesh::Union{UnstructuredMesh2D, P4estMesh,
+ T8codeMesh},
equations, surface_integral, dg::DG) where {N}
# Extract the boundary condition type and index vector
boundary_condition = first(BCs)
@@ -350,7 +351,8 @@ end
# terminate the type-stable iteration over tuples
function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{},
- mesh::Union{UnstructuredMesh2D, P4estMesh},
+ mesh::Union{UnstructuredMesh2D, P4estMesh,
+ T8codeMesh},
equations, surface_integral, dg::DG)
nothing
end
diff --git a/test/runtests.jl b/test/runtests.jl
index f76811dddbf..1d7eefe1fcb 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -4,113 +4,119 @@ using MPI: mpiexec
# run tests on Travis CI in parallel
const TRIXI_TEST = get(ENV, "TRIXI_TEST", "all")
const TRIXI_MPI_NPROCS = clamp(Sys.CPU_THREADS, 2, 3)
-const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3)
+const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3)
@time @testset "Trixi.jl tests" begin
- # This is placed first since tests error out otherwise if `TRIXI_TEST == "all"`,
- # at least on some systems.
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "mpi"
- # Do a dummy `@test true`:
- # If the process errors out the testset would error out as well,
- # cf. https://github.com/JuliaParallel/MPI.jl/pull/391
- @test true
-
- # There are spurious test failures of Trixi.jl with MPI on Windows, see
- # https://github.com/trixi-framework/Trixi.jl/issues/901
- # To reduce their impact, we do not test MPI with coverage on Windows.
- # This reduces the chance to hit a spurious test failure by one half.
- # In addition, it looks like the Linux GitHub runners run out of memory during the 3D tests
- # with coverage, so we currently do not test MPI with coverage on Linux. For more details,
- # see the discussion at https://github.com/trixi-framework/Trixi.jl/pull/1062#issuecomment-1035901020
- cmd = string(Base.julia_cmd())
- coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none", cmd)
- if !(coverage && Sys.iswindows()) && !(coverage && Sys.islinux())
- # We provide a `--heap-size-hint` to avoid/reduce out-of-memory errors during CI testing
- mpiexec() do cmd
- run(`$cmd -n $TRIXI_MPI_NPROCS $(Base.julia_cmd()) --threads=1 --check-bounds=yes --heap-size-hint=1G $(abspath("test_mpi.jl"))`)
- end
- end
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "threaded" || TRIXI_TEST == "threaded_legacy"
- # Do a dummy `@test true`:
- # If the process errors out the testset would error out as well,
- # cf. https://github.com/JuliaParallel/MPI.jl/pull/391
- @test true
-
- run(`$(Base.julia_cmd()) --threads=$TRIXI_NTHREADS --check-bounds=yes --code-coverage=none $(abspath("test_threaded.jl"))`)
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part1"
- include("test_tree_1d.jl")
- include("test_tree_2d_part1.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part2"
- include("test_tree_2d_part2.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part3"
- include("test_tree_2d_part3.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part4"
- include("test_tree_3d_part1.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part5"
- include("test_tree_3d_part2.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part6"
- include("test_tree_3d_part3.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "structured"
- include("test_structured_1d.jl")
- include("test_structured_2d.jl")
- include("test_structured_3d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part1"
- include("test_p4est_2d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part2"
- include("test_p4est_3d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "unstructured_dgmulti"
- include("test_unstructured_2d.jl")
- include("test_dgmulti_1d.jl")
- include("test_dgmulti_2d.jl")
- include("test_dgmulti_3d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "parabolic"
- include("test_parabolic_1d.jl")
- include("test_parabolic_2d.jl")
- include("test_parabolic_3d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part1"
- include("test_unit.jl")
- include("test_visualization.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part2"
- include("test_special_elixirs.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part1"
- include("test_performance_specializations_2d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part2"
- include("test_performance_specializations_3d.jl")
- end
-
- @time if TRIXI_TEST == "all" || TRIXI_TEST == "paper_self_gravitating_gas_dynamics"
- include("test_paper_self_gravitating_gas_dynamics.jl")
- end
+ # This is placed first since tests error out otherwise if `TRIXI_TEST == "all"`,
+ # at least on some systems.
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "mpi"
+ # Do a dummy `@test true`:
+ # If the process errors out the testset would error out as well,
+ # cf. https://github.com/JuliaParallel/MPI.jl/pull/391
+ @test true
+
+ # There are spurious test failures of Trixi.jl with MPI on Windows, see
+ # https://github.com/trixi-framework/Trixi.jl/issues/901
+ # To reduce their impact, we do not test MPI with coverage on Windows.
+ # This reduces the chance to hit a spurious test failure by one half.
+ # In addition, it looks like the Linux GitHub runners run out of memory during the 3D tests
+ # with coverage, so we currently do not test MPI with coverage on Linux. For more details,
+ # see the discussion at https://github.com/trixi-framework/Trixi.jl/pull/1062#issuecomment-1035901020
+ cmd = string(Base.julia_cmd())
+ coverage = occursin("--code-coverage", cmd) &&
+ !occursin("--code-coverage=none", cmd)
+ if !(coverage && Sys.iswindows()) && !(coverage && Sys.islinux())
+ # We provide a `--heap-size-hint` to avoid/reduce out-of-memory errors during CI testing
+ mpiexec() do cmd
+ run(`$cmd -n $TRIXI_MPI_NPROCS $(Base.julia_cmd()) --threads=1 --check-bounds=yes --heap-size-hint=1G $(abspath("test_mpi.jl"))`)
+ end
+ end
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "threaded" ||
+ TRIXI_TEST == "threaded_legacy"
+ # Do a dummy `@test true`:
+ # If the process errors out the testset would error out as well,
+ # cf. https://github.com/JuliaParallel/MPI.jl/pull/391
+ @test true
+
+ run(`$(Base.julia_cmd()) --threads=$TRIXI_NTHREADS --check-bounds=yes --code-coverage=none $(abspath("test_threaded.jl"))`)
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part1"
+ include("test_tree_1d.jl")
+ include("test_tree_2d_part1.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part2"
+ include("test_tree_2d_part2.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part3"
+ include("test_tree_2d_part3.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part4"
+ include("test_tree_3d_part1.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part5"
+ include("test_tree_3d_part2.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "tree_part6"
+ include("test_tree_3d_part3.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "structured"
+ include("test_structured_1d.jl")
+ include("test_structured_2d.jl")
+ include("test_structured_3d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part1"
+ include("test_p4est_2d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "p4est_part2"
+ include("test_p4est_3d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "t8code_part1"
+ include("test_t8code_2d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "unstructured_dgmulti"
+ include("test_unstructured_2d.jl")
+ include("test_dgmulti_1d.jl")
+ include("test_dgmulti_2d.jl")
+ include("test_dgmulti_3d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "parabolic"
+ include("test_parabolic_1d.jl")
+ include("test_parabolic_2d.jl")
+ include("test_parabolic_3d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part1"
+ include("test_unit.jl")
+ include("test_visualization.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part2"
+ include("test_special_elixirs.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part1"
+ include("test_performance_specializations_2d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part2"
+ include("test_performance_specializations_3d.jl")
+ end
+
+ @time if TRIXI_TEST == "all" || TRIXI_TEST == "paper_self_gravitating_gas_dynamics"
+ include("test_paper_self_gravitating_gas_dynamics.jl")
+ end
end
diff --git a/test/test_t8code_2d.jl b/test/test_t8code_2d.jl
new file mode 100644
index 00000000000..a424c9df84b
--- /dev/null
+++ b/test/test_t8code_2d.jl
@@ -0,0 +1,182 @@
+module TestExamplesT8codeMesh2D
+
+using Test
+using Trixi
+
+include("test_trixi.jl")
+
+EXAMPLES_DIR = joinpath(examples_dir(), "t8code_2d_dgsem")
+
+# Start with a clean environment: remove Trixi.jl output directory if it exists
+outdir = "out"
+isdir(outdir) && rm(outdir, recursive = true)
+mkdir(outdir)
+
+@testset "T8codeMesh2D" begin
+
+ @trixi_testset "test save_mesh_file" begin
+ @test_throws Exception begin
+ # Save mesh file support will be added in the future. The following
+ # lines of code are here for satisfying code coverage.
+
+ # Create dummy mesh.
+ mesh = T8codeMesh((1, 1), polydeg = 1,
+ mapping = Trixi.coordinates2mapping((-1.0, -1.0), ( 1.0, 1.0)),
+ initial_refinement_level = 1)
+
+ # This call throws an error.
+ Trixi.save_mesh_file(mesh, "dummy")
+ end
+ end
+
+ @trixi_testset "elixir_advection_basic.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_basic.jl"),
+ # Expected errors are exactly the same as with TreeMesh!
+ l2=[8.311947673061856e-6],
+ linf=[6.627000273229378e-5])
+ end
+
+ @trixi_testset "elixir_advection_nonconforming_flag.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR,
+ "elixir_advection_nonconforming_flag.jl"),
+ l2=[3.198940059144588e-5],
+ linf=[0.00030636069494005547])
+ end
+
+ @trixi_testset "elixir_advection_unstructured_flag.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_unstructured_flag.jl"),
+ l2=[0.0005379687442422346],
+ linf=[0.007438525029884735])
+ end
+
+ @trixi_testset "elixir_advection_amr_unstructured_flag.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR,
+ "elixir_advection_amr_unstructured_flag.jl"),
+ l2=[0.001993165013217687],
+ linf=[0.032891018571625796],
+ coverage_override=(maxiters = 6,))
+ end
+
+ @trixi_testset "elixir_advection_amr_solution_independent.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR,
+ "elixir_advection_amr_solution_independent.jl"),
+ # Expected errors are exactly the same as with StructuredMesh!
+ l2=[4.949660644033807e-5],
+ linf=[0.0004867846262313763],
+ coverage_override=(maxiters = 6,))
+ end
+
+ @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR,
+ "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"),
+ l2=[
+ 0.0034516244508588046,
+ 0.0023420334036925493,
+ 0.0024261923964557187,
+ 0.004731710454271893,
+ ],
+ linf=[
+ 0.04155789011775046,
+ 0.024772109862748914,
+ 0.03759938693042297,
+ 0.08039824959535657,
+ ])
+ end
+
+ @trixi_testset "elixir_euler_free_stream.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_free_stream.jl"),
+ l2=[
+ 2.063350241405049e-15,
+ 1.8571016296925367e-14,
+ 3.1769447886391905e-14,
+ 1.4104095258528071e-14,
+ ],
+ linf=[1.9539925233402755e-14, 2e-12, 4.8e-12, 4e-12],
+ atol=2.0e-12,)
+ end
+
+ @trixi_testset "elixir_euler_shockcapturing_ec.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_ec.jl"),
+ l2=[
+ 9.53984675e-02,
+ 1.05633455e-01,
+ 1.05636158e-01,
+ 3.50747237e-01,
+ ],
+ linf=[
+ 2.94357464e-01,
+ 4.07893014e-01,
+ 3.97334516e-01,
+ 1.08142520e+00,
+ ],
+ tspan=(0.0, 1.0))
+ end
+
+ @trixi_testset "elixir_euler_sedov.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov.jl"),
+ l2=[
+ 3.76149952e-01,
+ 2.46970327e-01,
+ 2.46970327e-01,
+ 1.28889042e+00,
+ ],
+ linf=[
+ 1.22139001e+00,
+ 1.17742626e+00,
+ 1.17742626e+00,
+ 6.20638482e+00,
+ ],
+ tspan=(0.0, 0.3))
+ end
+
+ @trixi_testset "elixir_shallowwater_source_terms.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_source_terms.jl"),
+ l2=[
+ 9.168126407325352e-5,
+ 0.0009795410115453788,
+ 0.002546408320320785,
+ 3.941189812642317e-6,
+ ],
+ linf=[
+ 0.0009903782521019089,
+ 0.0059752684687262025,
+ 0.010941106525454103,
+ 1.2129488214718265e-5,
+ ],
+ tspan=(0.0, 0.1))
+ end
+
+ @trixi_testset "elixir_mhd_alfven_wave.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_alfven_wave.jl"),
+ l2=[1.0513414461545583e-5, 1.0517900957166411e-6,
+ 1.0517900957304043e-6, 1.511816606372376e-6,
+ 1.0443997728645063e-6, 7.879639064990798e-7,
+ 7.879639065049896e-7, 1.0628631669056271e-6,
+ 4.3382328912336153e-7],
+ linf=[4.255466285174592e-5, 1.0029706745823264e-5,
+ 1.0029706747467781e-5, 1.2122265939010224e-5,
+ 5.4791097160444835e-6, 5.18922042269665e-6,
+ 5.189220422141538e-6, 9.552667261422676e-6,
+ 1.4237578427628152e-6])
+ end
+
+ @trixi_testset "elixir_mhd_rotor.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_mhd_rotor.jl"),
+ l2=[0.44211360369891683, 0.8805178316216257, 0.8262710688468049,
+ 0.0,
+ 0.9616090460973586, 0.10386643568745411,
+ 0.15403457366543802, 0.0,
+ 2.8399715649715473e-5],
+ linf=[10.04369305341599, 17.995640564998403, 9.576041548174265,
+ 0.0,
+ 19.429658884314534, 1.3821395681242314, 1.818559351543182,
+ 0.0,
+ 0.002261930217575465],
+ tspan=(0.0, 0.02))
+ end
+end
+
+# Clean up afterwards: delete Trixi.jl output directory
+@test_nowarn rm(outdir, recursive = true)
+
+end # module
diff --git a/test/test_threaded.jl b/test/test_threaded.jl
index 77fa16ad33e..9b30836d0ed 100644
--- a/test/test_threaded.jl
+++ b/test/test_threaded.jl
@@ -235,6 +235,22 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
end
+ @testset "T8codeMesh" begin
+ @trixi_testset "elixir_euler_source_terms_nonconforming_unstructured_flag.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "t8code_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"),
+ l2 = [0.0034516244508588046, 0.0023420334036925493, 0.0024261923964557187, 0.004731710454271893],
+ linf = [0.04155789011775046, 0.024772109862748914, 0.03759938693042297, 0.08039824959535657])
+ end
+
+ @trixi_testset "elixir_eulergravity_convergence.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "t8code_2d_dgsem", "elixir_eulergravity_convergence.jl"),
+ l2 = [0.00024871265138964204, 0.0003370077102132591, 0.0003370077102131964, 0.0007231525513793697],
+ linf = [0.0015813032944647087, 0.0020494288423820173, 0.0020494288423824614, 0.004793821195083758],
+ tspan = (0.0, 0.1))
+ end
+ end
+
+
@testset "DGMulti" begin
@trixi_testset "elixir_euler_weakform.jl (SBP, EC)" begin
@test_trixi_include(joinpath(examples_dir(), "dgmulti_2d", "elixir_euler_weakform.jl"),
From e1e680ca8574acd10daa2e5bc5e1f49e1ce008f9 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Fri, 28 Jul 2023 04:35:33 +0200
Subject: [PATCH 055/263] Enstrophy for 2D Navier-Stokes (#1591)
* Doubly periodic shear layer
* test if prject toml shows up in git diff
* remove chnages
* Enstrophy for 2D Navier-Stokes
---
Project.toml | 2 +-
.../elixir_navierstokes_shear_layer.jl | 71 +++++++++++++++++++
src/callbacks_step/analysis_dg2d.jl | 18 +++++
.../compressible_navier_stokes_2d.jl | 15 ++++
test/test_parabolic_2d.jl | 4 ++
5 files changed, 109 insertions(+), 1 deletion(-)
create mode 100644 examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl
diff --git a/Project.toml b/Project.toml
index db410317851..b3ca99be9ec 100644
--- a/Project.toml
+++ b/Project.toml
@@ -60,8 +60,8 @@ HDF5 = "0.14, 0.15, 0.16"
IfElse = "0.1"
LinearMaps = "2.7, 3.0"
LoopVectorization = "0.12.118"
-Makie = "0.19"
MPI = "0.20"
+Makie = "0.19"
MuladdMacro = "0.2.2"
Octavian = "0.3.5"
OffsetArrays = "1.3"
diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl b/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl
new file mode 100644
index 00000000000..a7cb2fc89f1
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl
@@ -0,0 +1,71 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Navier-Stokes equations
+
+# TODO: parabolic; unify names of these accessor functions
+prandtl_number() = 0.72
+mu() = 1.0/3.0 * 10^(-3) # equivalent to Re = 3000
+
+equations = CompressibleEulerEquations2D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(),
+ Prandtl=prandtl_number())
+
+function initial_condition_shear_layer(x, t, equations::CompressibleEulerEquations2D)
+ k = 80
+ delta = 0.05
+ u0 = 1.0
+ Ms = 0.1 # maximum Mach number
+
+ rho = 1.0
+ v1 = x[2] <= 0.5 ? u0*tanh(k*(x[2]*0.5 - 0.25)) : tanh(k*(0.75 -x[2]*0.5))
+ v2 = u0*delta * sin(2*pi*(x[1]*0.5 + 0.25))
+ p = (u0 / Ms)^2 * rho / equations.gamma # scaling to get Ms
+
+ return prim2cons(SVector(rho, v1, v2, p), equations)
+end
+initial_condition = initial_condition_shear_layer
+
+volume_flux = flux_ranocha
+solver = DGSEM(polydeg=3, surface_flux=flux_hllc,
+ volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
+
+coordinates_min = (0.0, 0.0)
+coordinates_max = (1.0, 1.0)
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=4,
+ n_cells_max=100_000)
+
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
+ initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 2.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 50
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal,
+ enstrophy))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval,)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/src/callbacks_step/analysis_dg2d.jl b/src/callbacks_step/analysis_dg2d.jl
index 4e456f79872..aecabf0e4b7 100644
--- a/src/callbacks_step/analysis_dg2d.jl
+++ b/src/callbacks_step/analysis_dg2d.jl
@@ -213,6 +213,24 @@ function integrate(func::Func, u,
end
end
+function integrate(func::Func, u,
+ mesh::Union{TreeMesh{2}, P4estMesh{2}},
+ equations, equations_parabolic,
+ dg::DGSEM,
+ cache, cache_parabolic; normalize = true) where {Func}
+ gradients_x, gradients_y = cache_parabolic.gradients
+ integrate_via_indices(u, mesh, equations, dg, cache;
+ normalize = normalize) do u, i, j, element, equations, dg
+ u_local = get_node_vars(u, equations, dg, i, j, element)
+ gradients_1_local = get_node_vars(gradients_x, equations_parabolic, dg, i, j,
+ element)
+ gradients_2_local = get_node_vars(gradients_y, equations_parabolic, dg, i, j,
+ element)
+ return func(u_local, (gradients_1_local, gradients_2_local),
+ equations_parabolic)
+ end
+end
+
function analyze(::typeof(entropy_timederivative), du, u, t,
mesh::Union{TreeMesh{2}, StructuredMesh{2}, UnstructuredMesh2D,
P4estMesh{2}, T8codeMesh{2}},
diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl
index 9b06e0b5abf..a1f11717e69 100644
--- a/src/equations/compressible_navier_stokes_2d.jl
+++ b/src/equations/compressible_navier_stokes_2d.jl
@@ -300,6 +300,21 @@ end
return T
end
+@inline function enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion2D)
+ # Enstrophy is 0.5 rho ω⋅ω where ω = ∇ × v
+
+ omega = vorticity(u, gradients, equations)
+ return 0.5 * u[1] * omega^2
+end
+
+@inline function vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion2D)
+ # Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.
+ _, dv1dx, dv2dx, _ = convert_derivative_to_primitive(u, gradients[1], equations)
+ _, dv1dy, dv2dy, _ = convert_derivative_to_primitive(u, gradients[2], equations)
+
+ return dv2dx - dv1dy
+end
+
# TODO: can we generalize this to MHD?
"""
struct BoundaryConditionNavierStokesWall
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index 471b976e990..57f296b55fe 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -136,6 +136,10 @@ isdir(outdir) && rm(outdir, recursive=true)
@trixi_testset "TreeMesh2D: elixir_navierstokes_convergence.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_navierstokes_convergence.jl"),
initial_refinement_level = 2, tspan=(0.0, 0.1),
+ analysis_callback = AnalysisCallback(semi, interval=analysis_interval,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal,
+ enstrophy)),
l2 = [0.002111672530658797, 0.0034322351490857846, 0.0038742528195910416, 0.012469246082568561],
linf = [0.012006418939223495, 0.035520871209746126, 0.024512747492231427, 0.11191122588756564]
)
From 73e58dc59ad0e06616507b9338c8fe0bee5b99b4 Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Fri, 28 Jul 2023 23:28:53 -0500
Subject: [PATCH 056/263] remove CI functions that cause preocmpilation errors
(#1593)
---
src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 54 ----------------------
1 file changed, 54 deletions(-)
diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
index 5370c927e05..6439cad69bb 100644
--- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
+++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
@@ -563,60 +563,6 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
return nothing
end
-# # Function barrier for type stability
-# !!! TODO: Figure out why this cannot removed eventhough it exists in the dg_2d_parabolic.jl file
-function calc_boundary_flux_gradients!(cache, t, boundary_conditions, mesh::P4estMesh,
- equations, surface_integral, dg::DG)
- (; boundary_condition_types, boundary_indices) = boundary_conditions
-
- calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
- Gradient(), mesh, equations, surface_integral, dg)
- return nothing
-end
-
-function calc_boundary_flux_divergence!(cache, t, boundary_conditions, mesh::P4estMesh,
- equations, surface_integral, dg::DG)
- (; boundary_condition_types, boundary_indices) = boundary_conditions
-
- calc_boundary_flux_by_type!(cache, t, boundary_condition_types, boundary_indices,
- Divergence(), mesh, equations, surface_integral, dg)
- return nothing
-end
-
-# Iterate over tuples of boundary condition types and associated indices
-# in a type-stable way using "lispy tuple programming".
-function calc_boundary_flux_by_type!(cache, t, BCs::NTuple{N, Any},
- BC_indices::NTuple{N, Vector{Int}},
- operator_type,
- mesh::P4estMesh,
- equations, surface_integral, dg::DG) where {N}
- # Extract the boundary condition type and index vector
- boundary_condition = first(BCs)
- boundary_condition_indices = first(BC_indices)
- # Extract the remaining types and indices to be processed later
- remaining_boundary_conditions = Base.tail(BCs)
- remaining_boundary_condition_indices = Base.tail(BC_indices)
-
- # process the first boundary condition type
- calc_boundary_flux!(cache, t, boundary_condition, boundary_condition_indices,
- operator_type, mesh, equations, surface_integral, dg)
-
- # recursively call this method with the unprocessed boundary types
- calc_boundary_flux_by_type!(cache, t, remaining_boundary_conditions,
- remaining_boundary_condition_indices,
- operator_type,
- mesh, equations, surface_integral, dg)
-
- return nothing
-end
-
-# terminate the type-stable iteration over tuples
-function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{},
- operator_type, mesh::P4estMesh, equations,
- surface_integral, dg::DG)
- nothing
-end
-
function calc_boundary_flux!(cache, t,
boundary_condition_parabolic, # works with Dict types
boundary_condition_indices,
From d05f9c5bfc329db3448a7af18bb1c24cfb75deb2 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 30 Jul 2023 08:19:37 +0200
Subject: [PATCH 057/263] run only threaded tests by default (#1592)
---
test/runtests.jl | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/test/runtests.jl b/test/runtests.jl
index 1d7eefe1fcb..1b0c745dbfd 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -1,8 +1,11 @@
using Test
using MPI: mpiexec
-# run tests on Travis CI in parallel
-const TRIXI_TEST = get(ENV, "TRIXI_TEST", "all")
+# We run tests in parallel with CI jobs setting the `TRIXI_TEST` environment
+# variable to determine the subset of tests to execute.
+# By default, we just run the threaded tests since they are relatively cheap
+# and test a good amount of different functionality.
+const TRIXI_TEST = get(ENV, "TRIXI_TEST", "threaded")
const TRIXI_MPI_NPROCS = clamp(Sys.CPU_THREADS, 2, 3)
const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3)
From d208cee2690fb5b1d63a0511ad5f73967d340205 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 30 Jul 2023 09:47:44 +0200
Subject: [PATCH 058/263] set version to v0.5.37
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index b3ca99be9ec..1d06317f53a 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.37-pre"
+version = "0.5.37"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From e76ea3932d875774220811ff0c14c8e966c312bf Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 30 Jul 2023 09:47:57 +0200
Subject: [PATCH 059/263] set development version to v0.5.38-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 1d06317f53a..c22d4b90642 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.37"
+version = "0.5.38-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 6c97c48e53feb9fe372dc020cbd5f3e1e8fef458 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 8 Aug 2023 07:15:15 +0200
Subject: [PATCH 060/263] Bump crate-ci/typos from 1.16.1 to 1.16.2 (#1598)
* Bump crate-ci/typos from 1.16.1 to 1.16.2
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.1 to 1.16.2.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.16.1...v1.16.2)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
* f_sur -> f_surface
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Hendrik Ranocha
---
.github/workflows/SpellCheck.yml | 2 +-
docs/literate/src/files/DGSEM_FluxDiff.jl | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index f72c3b0947b..a1a429cad97 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.16.1
+ uses: crate-ci/typos@v1.16.2
diff --git a/docs/literate/src/files/DGSEM_FluxDiff.jl b/docs/literate/src/files/DGSEM_FluxDiff.jl
index cf3b0a1dbd4..5ec156ebbe3 100644
--- a/docs/literate/src/files/DGSEM_FluxDiff.jl
+++ b/docs/literate/src/files/DGSEM_FluxDiff.jl
@@ -96,13 +96,13 @@
# \begin{align*}
# J \underline{\dot{u}}(t) &= - M^{-1} B (\underline{f}^* - \underline{f}) - 2D \underline{f}_{vol}(u^-, u^+)\\[5pt]
# &= - M^{-1} B (\underline{f}^* - \underline{f}_{vol}(\underline{u}, \underline{u})) - 2D \underline{f}_{vol}(u^-, u^+)\\[5pt]
-# &= - M^{-1} B \underline{f}_{sur}^* - (2D - M^{-1} B) \underline{f}_{vol}\\[5pt]
-# &= - M^{-1} B \underline{f}_{sur}^* - D_{split} \underline{f}_{vol}
+# &= - M^{-1} B \underline{f}_{surface}^* - (2D - M^{-1} B) \underline{f}_{vol}\\[5pt]
+# &= - M^{-1} B \underline{f}_{surface}^* - D_{split} \underline{f}_{vol}
# \end{align*}
# ```
# This formulation is in a weak form type formulation and can be implemented by using the derivative
# split matrix $D_{split}=(2D-M^{-1}B)$ and two different fluxes. We divide between the surface
-# flux $f=f_{sur}$ used for the numerical flux $f_{sur}^*$ and the already mentioned volume
+# flux $f=f_{surface}$ used for the numerical flux $f_{surface}^*$ and the already mentioned volume
# flux $f_{vol}$ especially for this formulation.
From ddf089271c65d82b466711e59b5f791c0bd21021 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Tue, 8 Aug 2023 10:17:31 +0200
Subject: [PATCH 061/263] Avoid allocations in `boundary flux` for parabolic
RHS (#1594)
* Remove doubled implementations
* kepp main updated with true main
* Avoid allocations in parabolic boundary fluxes
* Correct shear layer IC
* Whitespaces
* Update examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl
Co-authored-by: Hendrik Ranocha
* Update examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl
Co-authored-by: Hendrik Ranocha
---------
Co-authored-by: Hendrik Ranocha
---
examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl | 5 ++++-
examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl | 6 ++++--
examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl | 5 ++++-
src/solvers/dgsem_tree/containers_2d.jl | 6 +++---
src/solvers/dgsem_tree/containers_3d.jl | 8 ++++----
5 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl
index 36a9f52e39d..b68e9e6c97e 100644
--- a/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl
+++ b/examples/tree_2d_dgsem/elixir_navierstokes_convergence.jl
@@ -170,7 +170,10 @@ end
initial_condition = initial_condition_navier_stokes_convergence_test
# BC types
-velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3])
+velocity_bc_top_bottom = NoSlip() do x, t, equations
+ u = initial_condition_navier_stokes_convergence_test(x, t, equations)
+ return SVector(u[2], u[3])
+end
heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl b/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl
index a7cb2fc89f1..dd26fd8097b 100644
--- a/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl
+++ b/examples/tree_2d_dgsem/elixir_navierstokes_shear_layer.jl
@@ -14,14 +14,16 @@ equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(),
Prandtl=prandtl_number())
function initial_condition_shear_layer(x, t, equations::CompressibleEulerEquations2D)
+ # Shear layer parameters
k = 80
delta = 0.05
u0 = 1.0
+
Ms = 0.1 # maximum Mach number
rho = 1.0
- v1 = x[2] <= 0.5 ? u0*tanh(k*(x[2]*0.5 - 0.25)) : tanh(k*(0.75 -x[2]*0.5))
- v2 = u0*delta * sin(2*pi*(x[1]*0.5 + 0.25))
+ v1 = x[2] <= 0.5 ? u0 * tanh(k*(x[2]*0.5 - 0.25)) : u0 * tanh(k*(0.75 -x[2]*0.5))
+ v2 = u0 * delta * sin(2*pi*(x[1]*0.5 + 0.25))
p = (u0 / Ms)^2 * rho / equations.gamma # scaling to get Ms
return prim2cons(SVector(rho, v1, v2, p), equations)
diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl
index b32355c48df..ebb0137a1bb 100644
--- a/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl
+++ b/examples/tree_3d_dgsem/elixir_navierstokes_convergence.jl
@@ -220,7 +220,10 @@ end
initial_condition = initial_condition_navier_stokes_convergence_test
# BC types
-velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:4])
+velocity_bc_top_bottom = NoSlip() do x, t, equations
+ u = initial_condition_navier_stokes_convergence_test(x, t, equations)
+ return SVector(u[2], u[3], u[4])
+end
heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
diff --git a/src/solvers/dgsem_tree/containers_2d.jl b/src/solvers/dgsem_tree/containers_2d.jl
index 5cf256d3499..d80522d42fd 100644
--- a/src/solvers/dgsem_tree/containers_2d.jl
+++ b/src/solvers/dgsem_tree/containers_2d.jl
@@ -764,10 +764,10 @@ end
# Container data structure (structure-of-arrays style) for DG MPI interfaces
mutable struct MPIInterfaceContainer2D{uEltype <: Real} <: AbstractContainer
- u::Array{uEltype, 4} # [leftright, variables, i, interfaces]
+ u::Array{uEltype, 4} # [leftright, variables, i, interfaces]
local_neighbor_ids::Vector{Int} # [interfaces]
- orientations::Vector{Int} # [interfaces]
- remote_sides::Vector{Int} # [interfaces]
+ orientations::Vector{Int} # [interfaces]
+ remote_sides::Vector{Int} # [interfaces]
# internal `resize!`able storage
_u::Vector{uEltype}
end
diff --git a/src/solvers/dgsem_tree/containers_3d.jl b/src/solvers/dgsem_tree/containers_3d.jl
index 0318946e34d..5fc027ad001 100644
--- a/src/solvers/dgsem_tree/containers_3d.jl
+++ b/src/solvers/dgsem_tree/containers_3d.jl
@@ -520,14 +520,14 @@ end
# Left and right are used *both* for the numbering of the mortar faces *and* for the position of the
# elements with respect to the axis orthogonal to the mortar.
mutable struct L2MortarContainer3D{uEltype <: Real} <: AbstractContainer
- u_upper_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
+ u_upper_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
u_upper_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
- u_lower_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
+ u_lower_left::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
u_lower_right::Array{uEltype, 5} # [leftright, variables, i, j, mortars]
- neighbor_ids::Array{Int, 2} # [position, mortars]
+ neighbor_ids::Array{Int, 2} # [position, mortars]
# Large sides: left -> 1, right -> 2
large_sides::Vector{Int} # [mortars]
- orientations::Vector{Int} # [mortars]
+ orientations::Vector{Int} # [mortars]
# internal `resize!`able storage
_u_upper_left::Vector{uEltype}
_u_upper_right::Vector{uEltype}
From 7936e61b46b6a61ac0854b42a6082204700c7eca Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Wed, 9 Aug 2023 13:33:59 +0200
Subject: [PATCH 062/263] Adapt `jacobian_ad_forward` for hyperbolic-parabolic
semidiscretizations (#1589)
* JacobianAD calls correct RHS for Hyperbolic-Parabolic
* Nonlinear test
* Format
* Bring default _jacobian_ad_forward back
* CI for 2D Taylor-Green
* covered by standard version
* implement rhs directly in jacobina_ad_forward
* Update src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Add reference for 3D Taylor-Green Vortex
* Update doc
* Update tests 2D Taylor-Green Vortex
* Fix copy-paste error
* Viscous TGV comment
---------
Co-authored-by: Hendrik Ranocha
---
...elixir_navierstokes_taylor_green_vortex.jl | 78 +++++++++++++++++++
...elixir_navierstokes_taylor_green_vortex.jl | 6 +-
...semidiscretization_hyperbolic_parabolic.jl | 15 ++++
test/test_parabolic_2d.jl | 7 ++
test/test_special_elixirs.jl | 18 +++++
5 files changed, 123 insertions(+), 1 deletion(-)
create mode 100644 examples/tree_2d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_taylor_green_vortex.jl b/examples/tree_2d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
new file mode 100644
index 00000000000..c3cbc858f7b
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
@@ -0,0 +1,78 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Navier-Stokes equations
+
+# TODO: parabolic; unify names of these accessor functions
+prandtl_number() = 0.72
+mu() = 6.25e-4 # equivalent to Re = 1600
+
+equations = CompressibleEulerEquations2D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(),
+ Prandtl=prandtl_number())
+
+"""
+ initial_condition_taylor_green_vortex(x, t, equations::CompressibleEulerEquations2D)
+
+The classical viscous Taylor-Green vortex in 2D.
+This forms the basis behind the 3D case found for instance in
+ - Jonathan R. Bull and Antony Jameson
+ Simulation of the Compressible Taylor Green Vortex using High-Order Flux Reconstruction Schemes
+ [DOI: 10.2514/6.2014-3210](https://doi.org/10.2514/6.2014-3210)
+"""
+function initial_condition_taylor_green_vortex(x, t, equations::CompressibleEulerEquations2D)
+ A = 1.0 # magnitude of speed
+ Ms = 0.1 # maximum Mach number
+
+ rho = 1.0
+ v1 = A * sin(x[1]) * cos(x[2])
+ v2 = -A * cos(x[1]) * sin(x[2])
+ p = (A / Ms)^2 * rho / equations.gamma # scaling to get Ms
+ p = p + 1.0/4.0 * A^2 * rho * (cos(2*x[1]) + cos(2*x[2]))
+
+ return prim2cons(SVector(rho, v1, v2, p), equations)
+end
+initial_condition = initial_condition_taylor_green_vortex
+
+volume_flux = flux_ranocha
+solver = DGSEM(polydeg=3, surface_flux=flux_hllc,
+ volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
+
+coordinates_min = (-1.0, -1.0) .* pi
+coordinates_max = ( 1.0, 1.0) .* pi
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=4,
+ n_cells_max=100_000)
+
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
+ initial_condition, solver)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 20.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval,)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-9
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/examples/tree_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl b/examples/tree_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
index 9cb73a462b7..5556831a59d 100644
--- a/examples/tree_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
+++ b/examples/tree_3d_dgsem/elixir_navierstokes_taylor_green_vortex.jl
@@ -16,7 +16,11 @@ equations_parabolic = CompressibleNavierStokesDiffusion3D(equations, mu=mu(),
"""
initial_condition_taylor_green_vortex(x, t, equations::CompressibleEulerEquations3D)
-The classical inviscid Taylor-Green vortex.
+The classical viscous Taylor-Green vortex, as found for instance in
+
+- Jonathan R. Bull and Antony Jameson
+ Simulation of the Compressible Taylor Green Vortex using High-Order Flux Reconstruction Schemes
+ [DOI: 10.2514/6.2014-3210](https://doi.org/10.2514/6.2014-3210)
"""
function initial_condition_taylor_green_vortex(x, t, equations::CompressibleEulerEquations3D)
A = 1.0 # magnitude of speed
diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
index 8f1e38c891b..b12ecadb58b 100644
--- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
+++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
@@ -330,4 +330,19 @@ function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationHyperbolicParabol
return nothing
end
+
+function _jacobian_ad_forward(semi::SemidiscretizationHyperbolicParabolic, t0, u0_ode,
+ du_ode, config)
+ new_semi = remake(semi, uEltype = eltype(config))
+
+ du_ode_hyp = Vector{eltype(config)}(undef, length(du_ode))
+ J = ForwardDiff.jacobian(du_ode, u0_ode, config) do du_ode, u_ode
+ # Implementation of split ODE problem in OrdinaryDiffEq
+ rhs!(du_ode_hyp, u_ode, new_semi, t0)
+ rhs_parabolic!(du_ode, u_ode, new_semi, t0)
+ du_ode .+= du_ode_hyp
+ end
+
+ return J
+end
end # @muladd
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index 57f296b55fe..e3bb1ed9fb1 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -188,6 +188,13 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "TreeMesh2D: elixir_navierstokes_taylor_green_vortex.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"),
+ l2 = [0.0009279657228109691, 0.012454661988687185, 0.012454661988689886, 0.030487112728612178],
+ linf = [0.002435582543096171, 0.024824039368199546, 0.024824039368212758, 0.06731583711777489]
+ )
+ end
+
@trixi_testset "P4estMesh2D: elixir_advection_diffusion_periodic.jl" begin
@test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_advection_diffusion_periodic.jl"),
trees_per_dimension = (1, 1), initial_refinement_level = 2, tspan=(0.0, 0.5),
diff --git a/test/test_special_elixirs.jl b/test/test_special_elixirs.jl
index 23017059eaa..c05dfbdfca1 100644
--- a/test/test_special_elixirs.jl
+++ b/test/test_special_elixirs.jl
@@ -107,6 +107,15 @@ coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none",
@test maximum(real, λ) < 10 * sqrt(eps(real(semi)))
end
+ @timed_testset "Linear advection-diffusion" begin
+ trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_advection_diffusion.jl"),
+ tspan=(0.0, 0.0), initial_refinement_level=2)
+
+ J = jacobian_ad_forward(semi)
+ λ = eigvals(J)
+ @test maximum(real, λ) < 10 * sqrt(eps(real(semi)))
+ end
+
@timed_testset "Compressible Euler equations" begin
trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_euler_density_wave.jl"),
tspan=(0.0, 0.0), initial_refinement_level=1)
@@ -165,6 +174,15 @@ coverage = occursin("--code-coverage", cmd) && !occursin("--code-coverage=none",
end
end
+ @timed_testset "Navier-Stokes" begin
+ trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"),
+ tspan=(0.0, 0.0), initial_refinement_level=2)
+
+ J = jacobian_ad_forward(semi)
+ λ = eigvals(J)
+ @test maximum(real, λ) < 0.2
+ end
+
@timed_testset "MHD" begin
trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "tree_2d_dgsem", "elixir_mhd_alfven_wave.jl"),
tspan=(0.0, 0.0), initial_refinement_level=0)
From ce81702ef7c092e6b8c783a405312e7c461dfc04 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Wed, 9 Aug 2023 14:39:32 +0200
Subject: [PATCH 063/263] Fix typo (#1600)
* Fix typo
---------
Co-authored-by: Hendrik Ranocha
---
src/semidiscretization/semidiscretization.jl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl
index fbdcd73e2a8..c784f716426 100644
--- a/src/semidiscretization/semidiscretization.jl
+++ b/src/semidiscretization/semidiscretization.jl
@@ -363,7 +363,7 @@ end
#
# In some sense, having plain multidimensional `Array`s not support `resize!`
# isn't necessarily a bug (although it would be nice to add this possibility to
-# base Julia) but can turn out to be a feature for us, because it will aloow us
+# base Julia) but can turn out to be a feature for us, because it will allow us
# more specializations.
# Since we can use multiple dispatch, these kinds of specializations can be
# tailored specifically to each combinations of mesh/solver etc.
From 3ca93afed4ab4efbd6022f65aa88b9a3a7608906 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Thu, 10 Aug 2023 22:25:36 +0200
Subject: [PATCH 064/263] L2 Mortars for Parabolic Terms on TreeMeshes (#1571)
* First try mortars for parabolic terms
* Use correct interface values in calc_fstar!
* Format parabolic 2d dgsem
* Remove unused function parameters
* L2 Mortars for 3D DGSEM TreeMesh
* Format
* Back to original example
* Dispatch 2D DGSEm rhs_parabolic for p4est and classic tree
* Re-use standard prolong2mortars in gradient comp
* Back to original version
* Add tests for L2 mortars for hyp-para
* remove whitespaces
* Use original analysis callback
* Test Taylor-Green with different integrator
* Remove whitespace
* check coverage status
* Stick to CK2N54 for 3D test
* Add more explicit dispatch
* Less invasive treatment for mortars and p4est
* Revert "Add more explicit dispatch"
This reverts commit 491c923d09ba335c03524894d9f59acf1d6ee699.
* More explicit dispatch
* Remove additional end
* Remove doubled implementations
* kepp main updated with true main
* Add comment
* comment parabolic 3d
* whitespace
* Avoid allocations in parabolic boundary fluxes
* Update src/solvers/dgsem_tree/dg_2d_parabolic.jl
Co-authored-by: Andrew Winters
* Update src/solvers/dgsem_tree/dg_3d_parabolic.jl
Co-authored-by: Andrew Winters
* Update src/solvers/dgsem_tree/dg_3d_parabolic.jl
Co-authored-by: Andrew Winters
* revert alloc BC (other PR)
* Revert alloc BC (other PR)
* Name & News
* Update NEWS.md
Co-authored-by: Andrew Winters
* Update src/solvers/dgsem_p4est/dg_2d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Update src/solvers/dgsem_p4est/dg_3d_parabolic.jl
Co-authored-by: Hendrik Ranocha
* Check allocations
---------
Co-authored-by: Hendrik Ranocha
Co-authored-by: Andrew Winters
---
AUTHORS.md | 1 +
NEWS.md | 1 +
src/solvers/dgsem_p4est/dg_2d_parabolic.jl | 91 ++++++
src/solvers/dgsem_p4est/dg_3d_parabolic.jl | 99 +++++++
src/solvers/dgsem_tree/dg_2d_parabolic.jl | 252 +++++++++++++++-
src/solvers/dgsem_tree/dg_3d_parabolic.jl | 323 ++++++++++++++++++++-
test/test_parabolic_2d.jl | 54 ++++
test/test_parabolic_3d.jl | 58 +++-
8 files changed, 870 insertions(+), 9 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index abaa3e7e037..74bfaa9c852 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -28,6 +28,7 @@ are listed in alphabetical order:
* Jesse Chan
* Lars Christmann
* Christof Czernik
+* Daniel Doehring
* Patrick Ersing
* Erik Faulhaber
* Gregor Gassner
diff --git a/NEWS.md b/NEWS.md
index 8e374d9ce99..10125c40d17 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -9,6 +9,7 @@ for human readability.
#### Added
- Experimental support for 3D parabolic diffusion terms has been added.
+- Non-uniform `TreeMesh` available for hyperbolic-parabolic equations.
- Capability to set truly discontinuous initial conditions in 1D.
- Wetting and drying feature and examples for 1D and 2D shallow water equations
diff --git a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
index 7e90a83a9ca..a04523d2fb4 100644
--- a/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
+++ b/src/solvers/dgsem_p4est/dg_2d_parabolic.jl
@@ -22,6 +22,97 @@ function create_cache_parabolic(mesh::P4estMesh{2}, equations_hyperbolic::Abstra
return cache
end
+# TODO: Remove in favor of the implementation for the TreeMesh
+# once the P4estMesh can handle mortars as well
+function rhs_parabolic!(du, u, t, mesh::P4estMesh{2},
+ equations_parabolic::AbstractEquationsParabolic,
+ initial_condition, boundary_conditions_parabolic, source_terms,
+ dg::DG, parabolic_scheme, cache, cache_parabolic)
+ (; u_transformed, gradients, flux_viscous) = cache_parabolic
+
+ # Convert conservative variables to a form more suitable for viscous flux calculations
+ @trixi_timeit timer() "transform variables" begin
+ transform_variables!(u_transformed, u, mesh, equations_parabolic,
+ dg, parabolic_scheme, cache, cache_parabolic)
+ end
+
+ # Compute the gradients of the transformed variables
+ @trixi_timeit timer() "calculate gradient" begin
+ calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic,
+ boundary_conditions_parabolic, dg, cache, cache_parabolic)
+ end
+
+ # Compute and store the viscous fluxes
+ @trixi_timeit timer() "calculate viscous fluxes" begin
+ calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh,
+ equations_parabolic, dg, cache, cache_parabolic)
+ end
+
+ # The remainder of this function is essentially a regular rhs! for parabolic
+ # equations (i.e., it computes the divergence of the viscous fluxes)
+ #
+ # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have
+ # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the
+ # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the
+ # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it
+ # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values*
+ # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we
+ # do not need to recreate the existing data structure only with a different name, and c) we do not
+ # need to interpolate solutions *and* gradients to the surfaces.
+
+ # TODO: parabolic; reconsider current data structure reuse strategy
+
+ # Reset du
+ @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache)
+
+ # Calculate volume integral
+ @trixi_timeit timer() "volume integral" begin
+ calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache)
+ end
+
+ # Prolong solution to interfaces
+ @trixi_timeit timer() "prolong2interfaces" begin
+ prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic,
+ dg.surface_integral, dg, cache)
+ end
+
+ # Calculate interface fluxes
+ @trixi_timeit timer() "interface flux" begin
+ calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh,
+ equations_parabolic, dg, cache_parabolic)
+ end
+
+ # Prolong solution to boundaries
+ @trixi_timeit timer() "prolong2boundaries" begin
+ prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic,
+ dg.surface_integral, dg, cache)
+ end
+
+ # Calculate boundary fluxes
+ @trixi_timeit timer() "boundary flux" begin
+ calc_boundary_flux_divergence!(cache_parabolic, t,
+ boundary_conditions_parabolic, mesh,
+ equations_parabolic,
+ dg.surface_integral, dg)
+ end
+
+ # TODO: parabolic; extend to mortars
+ @assert nmortars(dg, cache) == 0
+
+ # Calculate surface integrals
+ @trixi_timeit timer() "surface integral" begin
+ calc_surface_integral!(du, u, mesh, equations_parabolic,
+ dg.surface_integral, dg, cache_parabolic)
+ end
+
+ # Apply Jacobian from mapping to reference element
+ @trixi_timeit timer() "Jacobian" begin
+ apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic)
+ end
+
+ return nothing
+end
+
function calc_gradient!(gradients, u_transformed, t,
mesh::P4estMesh{2}, equations_parabolic,
boundary_conditions_parabolic, dg::DG,
diff --git a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
index 6439cad69bb..2d26c1aff50 100644
--- a/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
+++ b/src/solvers/dgsem_p4est/dg_3d_parabolic.jl
@@ -22,6 +22,105 @@ function create_cache_parabolic(mesh::P4estMesh{3}, equations_hyperbolic::Abstra
return cache
end
+# This file collects all methods that have been updated to work with parabolic systems of equations
+#
+# assumptions: parabolic terms are of the form div(f(u, grad(u))) and
+# will be discretized first order form as follows:
+# 1. compute grad(u)
+# 2. compute f(u, grad(u))
+# 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call)
+# boundary conditions will be applied to both grad(u) and div(f(u, grad(u))).
+# TODO: Remove in favor of the implementation for the TreeMesh
+# once the P4estMesh can handle mortars as well
+function rhs_parabolic!(du, u, t, mesh::P4estMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ initial_condition, boundary_conditions_parabolic, source_terms,
+ dg::DG, parabolic_scheme, cache, cache_parabolic)
+ @unpack u_transformed, gradients, flux_viscous = cache_parabolic
+
+ # Convert conservative variables to a form more suitable for viscous flux calculations
+ @trixi_timeit timer() "transform variables" begin
+ transform_variables!(u_transformed, u, mesh, equations_parabolic,
+ dg, parabolic_scheme, cache, cache_parabolic)
+ end
+
+ # Compute the gradients of the transformed variables
+ @trixi_timeit timer() "calculate gradient" begin
+ calc_gradient!(gradients, u_transformed, t, mesh, equations_parabolic,
+ boundary_conditions_parabolic, dg, cache, cache_parabolic)
+ end
+
+ # Compute and store the viscous fluxes
+ @trixi_timeit timer() "calculate viscous fluxes" begin
+ calc_viscous_fluxes!(flux_viscous, gradients, u_transformed, mesh,
+ equations_parabolic, dg, cache, cache_parabolic)
+ end
+
+ # The remainder of this function is essentially a regular rhs! for parabolic
+ # equations (i.e., it computes the divergence of the viscous fluxes)
+ #
+ # OBS! In `calc_viscous_fluxes!`, the viscous flux values at the volume nodes of each element have
+ # been computed and stored in `fluxes_viscous`. In the following, we *reuse* (abuse) the
+ # `interfaces` and `boundaries` containers in `cache_parabolic` to interpolate and store the
+ # *fluxes* at the element surfaces, as opposed to interpolating and storing the *solution* (as it
+ # is done in the hyperbolic operator). That is, `interfaces.u`/`boundaries.u` store *viscous flux values*
+ # and *not the solution*. The advantage is that a) we do not need to allocate more storage, b) we
+ # do not need to recreate the existing data structure only with a different name, and c) we do not
+ # need to interpolate solutions *and* gradients to the surfaces.
+
+ # TODO: parabolic; reconsider current data structure reuse strategy
+
+ # Reset du
+ @trixi_timeit timer() "reset ∂u/∂t" reset_du!(du, dg, cache)
+
+ # Calculate volume integral
+ @trixi_timeit timer() "volume integral" begin
+ calc_volume_integral!(du, flux_viscous, mesh, equations_parabolic, dg, cache)
+ end
+
+ # Prolong solution to interfaces
+ @trixi_timeit timer() "prolong2interfaces" begin
+ prolong2interfaces!(cache_parabolic, flux_viscous, mesh, equations_parabolic,
+ dg.surface_integral, dg, cache)
+ end
+
+ # Calculate interface fluxes
+ @trixi_timeit timer() "interface flux" begin
+ calc_interface_flux!(cache_parabolic.elements.surface_flux_values, mesh,
+ equations_parabolic, dg, cache_parabolic)
+ end
+
+ # Prolong solution to boundaries
+ @trixi_timeit timer() "prolong2boundaries" begin
+ prolong2boundaries!(cache_parabolic, flux_viscous, mesh, equations_parabolic,
+ dg.surface_integral, dg, cache)
+ end
+
+ # Calculate boundary fluxes
+ @trixi_timeit timer() "boundary flux" begin
+ calc_boundary_flux_divergence!(cache_parabolic, t,
+ boundary_conditions_parabolic,
+ mesh, equations_parabolic,
+ dg.surface_integral, dg)
+ end
+
+ # TODO: parabolic; extend to mortars
+ @assert nmortars(dg, cache) == 0
+
+ # Calculate surface integrals
+ @trixi_timeit timer() "surface integral" begin
+ calc_surface_integral!(du, u, mesh, equations_parabolic,
+ dg.surface_integral, dg, cache_parabolic)
+ end
+
+ # Apply Jacobian from mapping to reference element
+ @trixi_timeit timer() "Jacobian" begin
+ apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache_parabolic)
+ end
+
+ return nothing
+end
+
function calc_gradient!(gradients, u_transformed, t,
mesh::P4estMesh{3}, equations_parabolic,
boundary_conditions_parabolic, dg::DG,
diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl
index c5862579992..0da25230380 100644
--- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl
@@ -13,7 +13,7 @@
# 2. compute f(u, grad(u))
# 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call)
# boundary conditions will be applied to both grad(u) and div(f(u, grad(u))).
-function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, P4estMesh{2}},
+function rhs_parabolic!(du, u, t, mesh::TreeMesh{2},
equations_parabolic::AbstractEquationsParabolic,
initial_condition, boundary_conditions_parabolic, source_terms,
dg::DG, parabolic_scheme, cache, cache_parabolic)
@@ -85,8 +85,18 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{2}, P4estMesh{2}},
dg.surface_integral, dg)
end
- # TODO: parabolic; extend to mortars
- @assert nmortars(dg, cache) == 0
+ # Prolong solution to mortars
+ @trixi_timeit timer() "prolong2mortars" begin
+ prolong2mortars!(cache, flux_viscous, mesh, equations_parabolic,
+ dg.mortar, dg.surface_integral, dg)
+ end
+
+ # Calculate mortar fluxes
+ @trixi_timeit timer() "mortar flux" begin
+ calc_mortar_flux!(cache_parabolic.elements.surface_flux_values, mesh,
+ equations_parabolic,
+ dg.mortar, dg.surface_integral, dg, cache)
+ end
# Calculate surface integrals
@trixi_timeit timer() "surface integral" begin
@@ -500,6 +510,227 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra
return nothing
end
+function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArray},
+ mesh::TreeMesh{2},
+ equations_parabolic::AbstractEquationsParabolic,
+ mortar_l2::LobattoLegendreMortarL2, surface_integral,
+ dg::DGSEM)
+ flux_viscous_x, flux_viscous_y = flux_viscous
+ @threaded for mortar in eachmortar(dg, cache)
+ large_element = cache.mortars.neighbor_ids[3, mortar]
+ upper_element = cache.mortars.neighbor_ids[2, mortar]
+ lower_element = cache.mortars.neighbor_ids[1, mortar]
+
+ # Copy solution small to small
+ if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ for l in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_x[v, 1, l,
+ upper_element]
+ cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_x[v, 1, l,
+ lower_element]
+ end
+ end
+ else
+ # L2 mortars in y-direction
+ for l in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper[2, v, l, mortar] = flux_viscous_y[v, l, 1,
+ upper_element]
+ cache.mortars.u_lower[2, v, l, mortar] = flux_viscous_y[v, l, 1,
+ lower_element]
+ end
+ end
+ end
+ else # large_sides[mortar] == 2 -> small elements on left side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ for l in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_x[v,
+ nnodes(dg),
+ l,
+ upper_element]
+ cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_x[v,
+ nnodes(dg),
+ l,
+ lower_element]
+ end
+ end
+ else
+ # L2 mortars in y-direction
+ for l in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper[1, v, l, mortar] = flux_viscous_y[v, l,
+ nnodes(dg),
+ upper_element]
+ cache.mortars.u_lower[1, v, l, mortar] = flux_viscous_y[v, l,
+ nnodes(dg),
+ lower_element]
+ end
+ end
+ end
+ end
+
+ # Interpolate large element face data to small interface locations
+ if cache.mortars.large_sides[mortar] == 1 # -> large element on left side
+ leftright = 1
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ u_large = view(flux_viscous_x, :, nnodes(dg), :, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large)
+ else
+ # L2 mortars in y-direction
+ u_large = view(flux_viscous_y, :, :, nnodes(dg), large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large)
+ end
+ else # large_sides[mortar] == 2 -> large element on right side
+ leftright = 2
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ u_large = view(flux_viscous_x, :, 1, :, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large)
+ else
+ # L2 mortars in y-direction
+ u_large = view(flux_viscous_y, :, :, 1, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large)
+ end
+ end
+ end
+
+ return nothing
+end
+
+# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms.
+# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for
+# hyperbolic terms with conserved terms only, i.e., no nonconservative terms.
+function calc_mortar_flux!(surface_flux_values,
+ mesh::TreeMesh{2},
+ equations_parabolic::AbstractEquationsParabolic,
+ mortar_l2::LobattoLegendreMortarL2,
+ surface_integral, dg::DG, cache)
+ @unpack surface_flux = surface_integral
+ @unpack u_lower, u_upper, orientations = cache.mortars
+ @unpack fstar_upper_threaded, fstar_lower_threaded = cache
+
+ @threaded for mortar in eachmortar(dg, cache)
+ # Choose thread-specific pre-allocated container
+ fstar_upper = fstar_upper_threaded[Threads.threadid()]
+ fstar_lower = fstar_lower_threaded[Threads.threadid()]
+
+ # Calculate fluxes
+ orientation = orientations[mortar]
+ calc_fstar!(fstar_upper, equations_parabolic, surface_flux, dg, u_upper, mortar,
+ orientation)
+ calc_fstar!(fstar_lower, equations_parabolic, surface_flux, dg, u_lower, mortar,
+ orientation)
+
+ mortar_fluxes_to_elements!(surface_flux_values,
+ mesh, equations_parabolic, mortar_l2, dg, cache,
+ mortar, fstar_upper, fstar_lower)
+ end
+
+ return nothing
+end
+
+@inline function calc_fstar!(destination::AbstractArray{<:Any, 2},
+ equations_parabolic::AbstractEquationsParabolic,
+ surface_flux, dg::DGSEM,
+ u_interfaces, interface, orientation)
+ for i in eachnode(dg)
+ # Call pointwise two-point numerical flux function
+ u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i,
+ interface)
+ # TODO: parabolic; only BR1 at the moment
+ flux = 0.5 * (u_ll + u_rr)
+
+ # Copy flux to left and right element storage
+ set_node_vars!(destination, flux, equations_parabolic, dg, i)
+ end
+
+ return nothing
+end
+
+@inline function mortar_fluxes_to_elements!(surface_flux_values,
+ mesh::TreeMesh{2},
+ equations_parabolic::AbstractEquationsParabolic,
+ mortar_l2::LobattoLegendreMortarL2,
+ dg::DGSEM, cache,
+ mortar, fstar_upper, fstar_lower)
+ large_element = cache.mortars.neighbor_ids[3, mortar]
+ upper_element = cache.mortars.neighbor_ids[2, mortar]
+ lower_element = cache.mortars.neighbor_ids[1, mortar]
+
+ # Copy flux small to small
+ if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ direction = 1
+ else
+ # L2 mortars in y-direction
+ direction = 3
+ end
+ else # large_sides[mortar] == 2 -> small elements on left side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ direction = 2
+ else
+ # L2 mortars in y-direction
+ direction = 4
+ end
+ end
+ surface_flux_values[:, :, direction, upper_element] .= fstar_upper
+ surface_flux_values[:, :, direction, lower_element] .= fstar_lower
+
+ # Project small fluxes to large element
+ if cache.mortars.large_sides[mortar] == 1 # -> large element on left side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ direction = 2
+ else
+ # L2 mortars in y-direction
+ direction = 4
+ end
+ else # large_sides[mortar] == 2 -> large element on right side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ direction = 1
+ else
+ # L2 mortars in y-direction
+ direction = 3
+ end
+ end
+
+ # TODO: Taal performance
+ # for v in eachvariable(equations)
+ # # The code below is semantically equivalent to
+ # # surface_flux_values[v, :, direction, large_element] .=
+ # # (mortar_l2.reverse_upper * fstar_upper[v, :] + mortar_l2.reverse_lower * fstar_lower[v, :])
+ # # but faster and does not allocate.
+ # # Note that `true * some_float == some_float` in Julia, i.e. `true` acts as
+ # # a universal `one`. Hence, the second `mul!` means "add the matrix-vector
+ # # product to the current value of the destination".
+ # @views mul!(surface_flux_values[v, :, direction, large_element],
+ # mortar_l2.reverse_upper, fstar_upper[v, :])
+ # @views mul!(surface_flux_values[v, :, direction, large_element],
+ # mortar_l2.reverse_lower, fstar_lower[v, :], true, true)
+ # end
+ # The code above could be replaced by the following code. However, the relative efficiency
+ # depends on the types of fstar_upper/fstar_lower and dg.l2mortar_reverse_upper.
+ # Using StaticArrays for both makes the code above faster for common test cases.
+ multiply_dimensionwise!(view(surface_flux_values, :, :, direction, large_element),
+ mortar_l2.reverse_upper, fstar_upper,
+ mortar_l2.reverse_lower, fstar_lower)
+
+ return nothing
+end
+
# Calculate the gradient of the transformed variables
function calc_gradient!(gradients, u_transformed, t,
mesh::TreeMesh{2}, equations_parabolic,
@@ -589,7 +820,20 @@ function calc_gradient!(gradients, u_transformed, t,
dg.surface_integral, dg)
end
- # TODO: parabolic; mortars
+ # Prolong solution to mortars
+ # NOTE: This re-uses the implementation for hyperbolic terms in "dg_2d.jl"
+ @trixi_timeit timer() "prolong2mortars" begin
+ prolong2mortars!(cache, u_transformed, mesh, equations_parabolic,
+ dg.mortar, dg.surface_integral, dg)
+ end
+
+ # Calculate mortar fluxes
+ @trixi_timeit timer() "mortar flux" begin
+ calc_mortar_flux!(surface_flux_values,
+ mesh,
+ equations_parabolic,
+ dg.mortar, dg.surface_integral, dg, cache)
+ end
# Calculate surface integrals
@trixi_timeit timer() "surface integral" begin
diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
index 5b63b971cd8..2745d312b37 100644
--- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
@@ -13,7 +13,7 @@
# 2. compute f(u, grad(u))
# 3. compute div(f(u, grad(u))) (i.e., the "regular" rhs! call)
# boundary conditions will be applied to both grad(u) and div(f(u, grad(u))).
-function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{3}, P4estMesh{3}},
+function rhs_parabolic!(du, u, t, mesh::TreeMesh{3},
equations_parabolic::AbstractEquationsParabolic,
initial_condition, boundary_conditions_parabolic, source_terms,
dg::DG, parabolic_scheme, cache, cache_parabolic)
@@ -85,8 +85,18 @@ function rhs_parabolic!(du, u, t, mesh::Union{TreeMesh{3}, P4estMesh{3}},
dg.surface_integral, dg)
end
- # TODO: parabolic; extend to mortars
- @assert nmortars(dg, cache) == 0
+ # Prolong solution to mortars
+ @trixi_timeit timer() "prolong2mortars" begin
+ prolong2mortars!(cache, flux_viscous, mesh, equations_parabolic,
+ dg.mortar, dg.surface_integral, dg)
+ end
+
+ # Calculate mortar fluxes
+ @trixi_timeit timer() "mortar flux" begin
+ calc_mortar_flux!(cache_parabolic.elements.surface_flux_values, mesh,
+ equations_parabolic,
+ dg.mortar, dg.surface_integral, dg, cache)
+ end
# Calculate surface integrals
@trixi_timeit timer() "surface integral" begin
@@ -583,6 +593,298 @@ function calc_boundary_flux_by_direction_divergence!(surface_flux_values::Abstra
return nothing
end
+function prolong2mortars!(cache,
+ flux_viscous::Tuple{AbstractArray, AbstractArray,
+ AbstractArray},
+ mesh::TreeMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ mortar_l2::LobattoLegendreMortarL2,
+ surface_integral, dg::DGSEM)
+ # temporary buffer for projections
+ @unpack fstar_tmp1_threaded = cache
+
+ flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous
+ @threaded for mortar in eachmortar(dg, cache)
+ fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]
+
+ lower_left_element = cache.mortars.neighbor_ids[1, mortar]
+ lower_right_element = cache.mortars.neighbor_ids[2, mortar]
+ upper_left_element = cache.mortars.neighbor_ids[3, mortar]
+ upper_right_element = cache.mortars.neighbor_ids[4, mortar]
+ large_element = cache.mortars.neighbor_ids[5, mortar]
+
+ # Copy solution small to small
+ if cache.mortars.large_sides[mortar] == 1 # -> small elements on right side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ for k in eachnode(dg), j in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper_left[2, v, j, k, mortar] = flux_viscous_x[v,
+ 1,
+ j,
+ k,
+ upper_left_element]
+ cache.mortars.u_upper_right[2, v, j, k, mortar] = flux_viscous_x[v,
+ 1,
+ j,
+ k,
+ upper_right_element]
+ cache.mortars.u_lower_left[2, v, j, k, mortar] = flux_viscous_x[v,
+ 1,
+ j,
+ k,
+ lower_left_element]
+ cache.mortars.u_lower_right[2, v, j, k, mortar] = flux_viscous_x[v,
+ 1,
+ j,
+ k,
+ lower_right_element]
+ end
+ end
+ elseif cache.mortars.orientations[mortar] == 2
+ # L2 mortars in y-direction
+ for k in eachnode(dg), i in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper_left[2, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ 1,
+ k,
+ upper_left_element]
+ cache.mortars.u_upper_right[2, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ 1,
+ k,
+ upper_right_element]
+ cache.mortars.u_lower_left[2, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ 1,
+ k,
+ lower_left_element]
+ cache.mortars.u_lower_right[2, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ 1,
+ k,
+ lower_right_element]
+ end
+ end
+ else # orientations[mortar] == 3
+ # L2 mortars in z-direction
+ for j in eachnode(dg), i in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper_left[2, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ 1,
+ upper_left_element]
+ cache.mortars.u_upper_right[2, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ 1,
+ upper_right_element]
+ cache.mortars.u_lower_left[2, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ 1,
+ lower_left_element]
+ cache.mortars.u_lower_right[2, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ 1,
+ lower_right_element]
+ end
+ end
+ end
+ else # large_sides[mortar] == 2 -> small elements on left side
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ for k in eachnode(dg), j in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper_left[1, v, j, k, mortar] = flux_viscous_x[v,
+ nnodes(dg),
+ j,
+ k,
+ upper_left_element]
+ cache.mortars.u_upper_right[1, v, j, k, mortar] = flux_viscous_x[v,
+ nnodes(dg),
+ j,
+ k,
+ upper_right_element]
+ cache.mortars.u_lower_left[1, v, j, k, mortar] = flux_viscous_x[v,
+ nnodes(dg),
+ j,
+ k,
+ lower_left_element]
+ cache.mortars.u_lower_right[1, v, j, k, mortar] = flux_viscous_x[v,
+ nnodes(dg),
+ j,
+ k,
+ lower_right_element]
+ end
+ end
+ elseif cache.mortars.orientations[mortar] == 2
+ # L2 mortars in y-direction
+ for k in eachnode(dg), i in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper_left[1, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ nnodes(dg),
+ k,
+ upper_left_element]
+ cache.mortars.u_upper_right[1, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ nnodes(dg),
+ k,
+ upper_right_element]
+ cache.mortars.u_lower_left[1, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ nnodes(dg),
+ k,
+ lower_left_element]
+ cache.mortars.u_lower_right[1, v, i, k, mortar] = flux_viscous_y[v,
+ i,
+ nnodes(dg),
+ k,
+ lower_right_element]
+ end
+ end
+ else # if cache.mortars.orientations[mortar] == 3
+ # L2 mortars in z-direction
+ for j in eachnode(dg), i in eachnode(dg)
+ for v in eachvariable(equations_parabolic)
+ cache.mortars.u_upper_left[1, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ nnodes(dg),
+ upper_left_element]
+ cache.mortars.u_upper_right[1, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ nnodes(dg),
+ upper_right_element]
+ cache.mortars.u_lower_left[1, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ nnodes(dg),
+ lower_left_element]
+ cache.mortars.u_lower_right[1, v, i, j, mortar] = flux_viscous_z[v,
+ i,
+ j,
+ nnodes(dg),
+ lower_right_element]
+ end
+ end
+ end
+ end
+
+ # Interpolate large element face data to small interface locations
+ if cache.mortars.large_sides[mortar] == 1 # -> large element on left side
+ leftright = 1
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ u_large = view(flux_viscous_x, :, nnodes(dg), :, :, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large, fstar_tmp1)
+ elseif cache.mortars.orientations[mortar] == 2
+ # L2 mortars in y-direction
+ u_large = view(flux_viscous_y, :, :, nnodes(dg), :, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large, fstar_tmp1)
+ else # cache.mortars.orientations[mortar] == 3
+ # L2 mortars in z-direction
+ u_large = view(flux_viscous_z, :, :, :, nnodes(dg), large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large, fstar_tmp1)
+ end
+ else # large_sides[mortar] == 2 -> large element on right side
+ leftright = 2
+ if cache.mortars.orientations[mortar] == 1
+ # L2 mortars in x-direction
+ u_large = view(flux_viscous_x, :, 1, :, :, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large, fstar_tmp1)
+ elseif cache.mortars.orientations[mortar] == 2
+ # L2 mortars in y-direction
+ u_large = view(flux_viscous_y, :, :, 1, :, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large, fstar_tmp1)
+ else # cache.mortars.orientations[mortar] == 3
+ # L2 mortars in z-direction
+ u_large = view(flux_viscous_z, :, :, :, 1, large_element)
+ element_solutions_to_mortars!(cache.mortars, mortar_l2, leftright,
+ mortar, u_large, fstar_tmp1)
+ end
+ end
+ end
+
+ return nothing
+end
+
+# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms.
+# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for
+# hyperbolic terms with conserved terms only, i.e., no nonconservative terms.
+function calc_mortar_flux!(surface_flux_values,
+ mesh::TreeMesh{3},
+ equations_parabolic::AbstractEquationsParabolic,
+ mortar_l2::LobattoLegendreMortarL2,
+ surface_integral, dg::DG, cache)
+ @unpack surface_flux = surface_integral
+ @unpack u_lower_left, u_lower_right, u_upper_left, u_upper_right, orientations = cache.mortars
+ @unpack (fstar_upper_left_threaded, fstar_upper_right_threaded,
+ fstar_lower_left_threaded, fstar_lower_right_threaded,
+ fstar_tmp1_threaded) = cache
+
+ @threaded for mortar in eachmortar(dg, cache)
+ # Choose thread-specific pre-allocated container
+ fstar_upper_left = fstar_upper_left_threaded[Threads.threadid()]
+ fstar_upper_right = fstar_upper_right_threaded[Threads.threadid()]
+ fstar_lower_left = fstar_lower_left_threaded[Threads.threadid()]
+ fstar_lower_right = fstar_lower_right_threaded[Threads.threadid()]
+ fstar_tmp1 = fstar_tmp1_threaded[Threads.threadid()]
+
+ # Calculate fluxes
+ orientation = orientations[mortar]
+ calc_fstar!(fstar_upper_left, equations_parabolic, surface_flux, dg,
+ u_upper_left, mortar,
+ orientation)
+ calc_fstar!(fstar_upper_right, equations_parabolic, surface_flux, dg,
+ u_upper_right,
+ mortar, orientation)
+ calc_fstar!(fstar_lower_left, equations_parabolic, surface_flux, dg,
+ u_lower_left, mortar,
+ orientation)
+ calc_fstar!(fstar_lower_right, equations_parabolic, surface_flux, dg,
+ u_lower_right,
+ mortar, orientation)
+
+ mortar_fluxes_to_elements!(surface_flux_values,
+ mesh, equations_parabolic, mortar_l2, dg, cache,
+ mortar,
+ fstar_upper_left, fstar_upper_right,
+ fstar_lower_left, fstar_lower_right,
+ fstar_tmp1)
+ end
+
+ return nothing
+end
+
+@inline function calc_fstar!(destination::AbstractArray{<:Any, 3},
+ equations_parabolic::AbstractEquationsParabolic,
+ surface_flux, dg::DGSEM,
+ u_interfaces, interface, orientation)
+ for j in eachnode(dg), i in eachnode(dg)
+ # Call pointwise two-point numerical flux function
+ u_ll, u_rr = get_surface_node_vars(u_interfaces, equations_parabolic, dg, i, j,
+ interface)
+ # TODO: parabolic; only BR1 at the moment
+ flux = 0.5 * (u_ll + u_rr)
+
+ # Copy flux to left and right element storage
+ set_node_vars!(destination, flux, equations_parabolic, dg, i, j)
+ end
+
+ return nothing
+end
+
# Calculate the gradient of the transformed variables
function calc_gradient!(gradients, u_transformed, t,
mesh::TreeMesh{3}, equations_parabolic,
@@ -679,7 +981,20 @@ function calc_gradient!(gradients, u_transformed, t,
dg.surface_integral, dg)
end
- # TODO: parabolic; mortars
+ # Prolong solution to mortars
+ # NOTE: This re-uses the implementation for hyperbolic terms in "dg_3d.jl"
+ @trixi_timeit timer() "prolong2mortars" begin
+ prolong2mortars!(cache, u_transformed, mesh, equations_parabolic,
+ dg.mortar, dg.surface_integral, dg)
+ end
+
+ # Calculate mortar fluxes
+ @trixi_timeit timer() "mortar flux" begin
+ calc_mortar_flux!(surface_flux_values,
+ mesh,
+ equations_parabolic,
+ dg.mortar, dg.surface_integral, dg, cache)
+ end
# Calculate surface integrals
@trixi_timeit timer() "surface integral" begin
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index e3bb1ed9fb1..1564a33dc41 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -125,6 +125,39 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "TreeMesh2D: elixir_advection_diffusion.jl (Refined mesh)" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_diffusion.jl"),
+ tspan=(0.0, 0.0))
+ LLID = Trixi.local_leaf_cells(mesh.tree)
+ num_leafs = length(LLID)
+ @assert num_leafs % 8 == 0
+ Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/8)])
+ tspan=(0.0, 1.5)
+ semi = SemidiscretizationHyperbolicParabolic(mesh,
+ (equations, equations_parabolic),
+ initial_condition, solver;
+ boundary_conditions=(boundary_conditions,
+ boundary_conditions_parabolic))
+ ode = semidiscretize(semi, tspan)
+ analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+ sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+ ac_sol = analysis_callback(sol)
+ @test ac_sol.l2[1] ≈ 1.67452550744728e-6
+ @test ac_sol.linf[1] ≈ 7.905059166368744e-6
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 100
+ @test (@allocated Trixi.rhs_parabolic!(du_ode, u_ode, semi, t)) < 100
+ end
+ end
+
@trixi_testset "TreeMesh2D: elixir_advection_diffusion_nonperiodic.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_diffusion_nonperiodic.jl"),
initial_refinement_level = 2, tspan=(0.0, 0.1),
@@ -180,6 +213,27 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "TreeMesh2D: elixir_navierstokes_convergence.jl (Refined mesh)" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_navierstokes_convergence.jl"),
+ tspan=(0.0, 0.0), initial_refinement_level=3)
+ LLID = Trixi.local_leaf_cells(mesh.tree)
+ num_leafs = length(LLID)
+ @assert num_leafs % 4 == 0
+ Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/4)])
+ tspan=(0.0, 0.5)
+ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+ ode = semidiscretize(semi, tspan)
+ analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+ sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+ ac_sol = analysis_callback(sol)
+ @test ac_sol.l2 ≈ [0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537]
+ @test ac_sol.linf ≈ [0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271]
+ end
+
@trixi_testset "TreeMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_navierstokes_lid_driven_cavity.jl"),
initial_refinement_level = 2, tspan=(0.0, 0.5),
diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl
index 67a27238969..d607962afa0 100644
--- a/test/test_parabolic_3d.jl
+++ b/test/test_parabolic_3d.jl
@@ -78,6 +78,27 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "TreeMesh3D: elixir_navierstokes_convergence.jl (Refined mesh)" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_convergence.jl"),
+ tspan=(0.0, 0.0))
+ LLID = Trixi.local_leaf_cells(mesh.tree)
+ num_leafs = length(LLID)
+ @assert num_leafs % 16 == 0
+ Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/16)])
+ tspan=(0.0, 1.0)
+ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+ ode = semidiscretize(semi, tspan)
+ analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+ sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+ ac_sol = analysis_callback(sol)
+ @test ac_sol.l2 ≈ [0.0003991794175622818; 0.0008853745163670504; 0.0010658655552066817; 0.0008785559918324284; 0.001403163458422815]
+ @test ac_sol.linf ≈ [0.0035306410538458177; 0.01505692306169911; 0.008862444161110705; 0.015065647972869856; 0.030402714743065218]
+ end
+
@trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"),
initial_refinement_level = 2, tspan=(0.0, 0.25),
@@ -86,6 +107,41 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl (Refined mesh)" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_navierstokes_taylor_green_vortex.jl"),
+ tspan=(0.0, 0.0))
+ LLID = Trixi.local_leaf_cells(mesh.tree)
+ num_leafs = length(LLID)
+ @assert num_leafs % 32 == 0
+ Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/32)])
+ tspan=(0.0, 10.0)
+ semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
+ initial_condition, solver)
+ ode = semidiscretize(semi, tspan)
+ analysis_callback = AnalysisCallback(semi, interval=analysis_interval, save_analysis=true,
+ extra_analysis_integrals=(energy_kinetic,
+ energy_internal,
+ enstrophy))
+ callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+ # Use CarpenterKennedy2N54 since `RDPK3SpFSAL49` gives slightly different results on different machines
+ sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+ dt=5e-3,
+ save_everystep=false, callback=callbacks);
+ ac_sol = analysis_callback(sol)
+ @test ac_sol.l2 ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005]
+ @test ac_sol.linf ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645]
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 100
+ @test (@allocated Trixi.rhs_parabolic!(du_ode, u_ode, semi, t)) < 100
+ end
+ end
+
@trixi_testset "P4estMesh3D: elixir_navierstokes_convergence.jl" begin
@test_trixi_include(joinpath(examples_dir(), "p4est_3d_dgsem", "elixir_navierstokes_convergence.jl"),
initial_refinement_level = 2, tspan=(0.0, 0.1),
@@ -101,8 +157,8 @@ isdir(outdir) && rm(outdir, recursive=true)
linf = [0.0006696415247340326, 0.03442565722527785, 0.03442565722577423, 0.06295407168705314, 0.032857472756916195]
)
end
-
end
+
# Clean up afterwards: delete Trixi.jl output directory
@test_nowarn isdir(outdir) && rm(outdir, recursive=true)
From d52a0419f0fe5dbee8dd58e3b12a23bc9fa67fc1 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Fri, 11 Aug 2023 09:43:31 +0200
Subject: [PATCH 065/263] Navier-Stokes 1D (#1597)
* Remove doubled implementations
* kepp main updated with true main
* Avoid allocations in parabolic boundary fluxes
* Correct shear layer IC
* Whitespaces
* Restore main
* restore main
* 1D Navier Stokes
* Conventional notation for heat flux
* remove multi-dim artefacts
* Move general part into own file
* Slip Wall BC for 1D Compressible Euler
* Correct arguments for 1D BCs
* format
* Add convergence test with walls
* Test gradient with entropy variables
* Test isothermal BC, test gradient in entropy vars
* Correct test data
---------
Co-authored-by: Hendrik Ranocha
---
...lixir_navierstokes_convergence_periodic.jl | 136 ++++++
.../elixir_navierstokes_convergence_walls.jl | 160 +++++++
src/Trixi.jl | 3 +-
src/equations/compressible_euler_1d.jl | 51 +++
src/equations/compressible_navier_stokes.jl | 70 +++
.../compressible_navier_stokes_1d.jl | 403 ++++++++++++++++++
.../compressible_navier_stokes_2d.jl | 77 +---
.../compressible_navier_stokes_3d.jl | 6 +-
src/equations/equations_parabolic.jl | 2 +
test/test_parabolic_1d.jl | 35 +-
10 files changed, 864 insertions(+), 79 deletions(-)
create mode 100644 examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic.jl
create mode 100644 examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl
create mode 100644 src/equations/compressible_navier_stokes.jl
create mode 100644 src/equations/compressible_navier_stokes_1d.jl
diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic.jl
new file mode 100644
index 00000000000..3f72d319b0b
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_periodic.jl
@@ -0,0 +1,136 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Navier-Stokes equations
+
+# TODO: parabolic; unify names of these accessor functions
+prandtl_number() = 0.72
+mu() = 6.25e-4 # equivalent to Re = 1600
+
+equations = CompressibleEulerEquations1D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
+ Prandtl=prandtl_number())
+
+# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
+# (Simplified version of the 2D)
+function initial_condition_navier_stokes_convergence_test(x, t, equations)
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_t = pi * t
+
+ rho = c + A * sin(pi_x) * cos(pi_t)
+ v1 = sin(pi_x) * cos(pi_t)
+ p = rho^2
+
+ return prim2cons(SVector(rho, v1, p), equations)
+end
+initial_condition = initial_condition_navier_stokes_convergence_test
+
+@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
+ # we currently need to hardcode these parameters until we fix the "combined equation" issue
+ # see also https://github.com/trixi-framework/Trixi.jl/pull/1160
+ inv_gamma_minus_one = inv(equations.gamma - 1)
+ Pr = prandtl_number()
+ mu_ = mu()
+
+ # Same settings as in `initial_condition`
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_t = pi * t
+
+ # compute the manufactured solution and all necessary derivatives
+ rho = c + A * sin(pi_x) * cos(pi_t)
+ rho_t = -pi * A * sin(pi_x) * sin(pi_t)
+ rho_x = pi * A * cos(pi_x) * cos(pi_t)
+ rho_xx = -pi * pi * A * sin(pi_x) * cos(pi_t)
+
+ v1 = sin(pi_x) * cos(pi_t)
+ v1_t = -pi * sin(pi_x) * sin(pi_t)
+ v1_x = pi * cos(pi_x) * cos(pi_t)
+ v1_xx = -pi * pi * sin(pi_x) * cos(pi_t)
+
+ p = rho * rho
+ p_t = 2.0 * rho * rho_t
+ p_x = 2.0 * rho * rho_x
+ p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x
+
+ E = p * inv_gamma_minus_one + 0.5 * rho * v1^2
+ E_t = p_t * inv_gamma_minus_one + 0.5 * rho_t * v1^2 + rho * v1 * v1_t
+ E_x = p_x * inv_gamma_minus_one + 0.5 * rho_x * v1^2 + rho * v1 * v1_x
+
+ # Some convenience constants
+ T_const = equations.gamma * inv_gamma_minus_one / Pr
+ inv_rho_cubed = 1.0 / (rho^3)
+
+ # compute the source terms
+ # density equation
+ du1 = rho_t + rho_x * v1 + rho * v1_x
+
+ # x-momentum equation
+ du2 = ( rho_t * v1 + rho * v1_t
+ + p_x + rho_x * v1^2 + 2.0 * rho * v1 * v1_x
+ # stress tensor from x-direction
+ - v1_xx * mu_)
+
+ # total energy equation
+ du3 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x)
+ # stress tensor and temperature gradient terms from x-direction
+ - v1_xx * v1 * mu_
+ - v1_x * v1_x * mu_
+ - T_const * inv_rho_cubed * ( p_xx * rho * rho
+ - 2.0 * p_x * rho * rho_x
+ + 2.0 * p * rho_x * rho_x
+ - p * rho * rho_xx ) * mu_)
+
+ return SVector(du1, du2, du3)
+end
+
+volume_flux = flux_ranocha
+solver = DGSEM(polydeg=3, surface_flux=flux_hllc,
+ volume_integral=VolumeIntegralFluxDifferencing(volume_flux))
+
+coordinates_min = -1.0
+coordinates_max = 1.0
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=4,
+ n_cells_max=100_000)
+
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
+ initial_condition, solver,
+ source_terms = source_terms_navier_stokes_convergence_test)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 10.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval,)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-9
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl
new file mode 100644
index 00000000000..181a2cb209f
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls.jl
@@ -0,0 +1,160 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the ideal compressible Navier-Stokes equations
+
+prandtl_number() = 0.72
+mu() = 0.01
+
+equations = CompressibleEulerEquations1D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(), Prandtl=prandtl_number(),
+ gradient_variables=GradientVariablesPrimitive())
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs,
+ volume_integral=VolumeIntegralWeakForm())
+
+coordinates_min = -1.0
+coordinates_max = 1.0
+
+# Create a uniformly refined mesh with periodic boundaries
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=3,
+ periodicity=false,
+ n_cells_max=30_000) # set maximum capacity of tree data structure
+
+# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion1D`
+# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion1D`)
+# and by the initial condition (which passes in `CompressibleEulerEquations1D`).
+# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
+function initial_condition_navier_stokes_convergence_test(x, t, equations)
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_t = pi * t
+
+ rho = c + A * cos(pi_x) * cos(pi_t)
+ v1 = log(x[1] + 2.0) * (1.0 - exp(-A * (x[1] - 1.0)) ) * cos(pi_t)
+ p = rho^2
+
+ return prim2cons(SVector(rho, v1, p), equations)
+end
+
+@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
+ x = x[1]
+
+ # TODO: parabolic
+ # we currently need to hardcode these parameters until we fix the "combined equation" issue
+ # see also https://github.com/trixi-framework/Trixi.jl/pull/1160
+ inv_gamma_minus_one = inv(equations.gamma - 1)
+ Pr = prandtl_number()
+ mu_ = mu()
+
+ # Same settings as in `initial_condition`
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x
+ pi_t = pi * t
+
+ # compute the manufactured solution and all necessary derivatives
+ rho = c + A * cos(pi_x) * cos(pi_t)
+ rho_t = -pi * A * cos(pi_x) * sin(pi_t)
+ rho_x = -pi * A * sin(pi_x) * cos(pi_t)
+ rho_xx = -pi * pi * A * cos(pi_x) * cos(pi_t)
+
+ v1 = log(x + 2.0) * (1.0 - exp(-A * (x - 1.0))) * cos(pi_t)
+ v1_t = -pi * log(x + 2.0) * (1.0 - exp(-A * (x - 1.0))) * sin(pi_t)
+ v1_x = (A * log(x + 2.0) * exp(-A * (x - 1.0)) + (1.0 - exp(-A * (x - 1.0))) / (x + 2.0)) * cos(pi_t)
+ v1_xx = (( 2.0 * A * exp(-A * (x - 1.0)) / (x + 2.0)
+ - A * A * log(x + 2.0) * exp(-A * (x - 1.0))
+ - (1.0 - exp(-A * (x - 1.0))) / ((x + 2.0) * (x + 2.0))) * cos(pi_t))
+
+ p = rho * rho
+ p_t = 2.0 * rho * rho_t
+ p_x = 2.0 * rho * rho_x
+ p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x
+
+ # Note this simplifies slightly because the ansatz assumes that v1 = v2
+ E = p * inv_gamma_minus_one + 0.5 * rho * v1^2
+ E_t = p_t * inv_gamma_minus_one + 0.5 * rho_t * v1^2 + rho * v1 * v1_t
+ E_x = p_x * inv_gamma_minus_one + 0.5 * rho_x * v1^2 + rho * v1 * v1_x
+
+ # Some convenience constants
+ T_const = equations.gamma * inv_gamma_minus_one / Pr
+ inv_rho_cubed = 1.0 / (rho^3)
+
+ # compute the source terms
+ # density equation
+ du1 = rho_t + rho_x * v1 + rho * v1_x
+
+ # y-momentum equation
+ du2 = ( rho_t * v1 + rho * v1_t
+ + p_x + rho_x * v1^2 + 2.0 * rho * v1 * v1_x
+ # stress tensor from y-direction
+ - v1_xx * mu_)
+
+ # total energy equation
+ du3 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x)
+ # stress tensor and temperature gradient terms from x-direction
+ - v1_xx * v1 * mu_
+ - v1_x * v1_x * mu_
+ - T_const * inv_rho_cubed * ( p_xx * rho * rho
+ - 2.0 * p_x * rho * rho_x
+ + 2.0 * p * rho_x * rho_x
+ - p * rho * rho_xx ) * mu_ )
+
+ return SVector(du1, du2, du3)
+end
+
+initial_condition = initial_condition_navier_stokes_convergence_test
+
+# BC types
+velocity_bc_left_right = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2])
+
+heat_bc_left = Isothermal((x, t, equations) ->
+ Trixi.temperature(initial_condition_navier_stokes_convergence_test(x, t, equations),
+ equations_parabolic))
+heat_bc_right = Adiabatic((x, t, equations) -> 0.0)
+
+boundary_condition_left = BoundaryConditionNavierStokesWall(velocity_bc_left_right, heat_bc_left)
+boundary_condition_right = BoundaryConditionNavierStokesWall(velocity_bc_left_right, heat_bc_right)
+
+# define inviscid boundary conditions
+boundary_conditions = (; x_neg = boundary_condition_slip_wall,
+ x_pos = boundary_condition_slip_wall)
+
+# define viscous boundary conditions
+boundary_conditions_parabolic = (; x_neg = boundary_condition_left,
+ x_pos = boundary_condition_right)
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+alive_callback = AliveCallback(alive_interval=10)
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/src/Trixi.jl b/src/Trixi.jl
index 990c33f3c94..78ddaa3ca7f 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -152,7 +152,8 @@ export AcousticPerturbationEquations2D,
LinearizedEulerEquations2D
export LaplaceDiffusion1D, LaplaceDiffusion2D,
- CompressibleNavierStokesDiffusion2D, CompressibleNavierStokesDiffusion3D
+ CompressibleNavierStokesDiffusion1D, CompressibleNavierStokesDiffusion2D,
+ CompressibleNavierStokesDiffusion3D
export GradientVariablesPrimitive, GradientVariablesEntropy
diff --git a/src/equations/compressible_euler_1d.jl b/src/equations/compressible_euler_1d.jl
index e4fd0997eae..9204989e8be 100644
--- a/src/equations/compressible_euler_1d.jl
+++ b/src/equations/compressible_euler_1d.jl
@@ -198,6 +198,57 @@ function initial_condition_eoc_test_coupled_euler_gravity(x, t,
return prim2cons(SVector(rho, v1, p), equations)
end
+"""
+ boundary_condition_slip_wall(u_inner, orientation, direction, x, t,
+ surface_flux_function, equations::CompressibleEulerEquations1D)
+Determine the boundary numerical surface flux for a slip wall condition.
+Imposes a zero normal velocity at the wall.
+Density is taken from the internal solution state and pressure is computed as an
+exact solution of a 1D Riemann problem. Further details about this boundary state
+are available in the paper:
+- J. J. W. van der Vegt and H. van der Ven (2002)
+ Slip flow boundary conditions in discontinuous Galerkin discretizations of
+ the Euler equations of gas dynamics
+ [PDF](https://reports.nlr.nl/bitstream/handle/10921/692/TP-2002-300.pdf?sequence=1)
+
+ Should be used together with [`TreeMesh`](@ref).
+"""
+@inline function boundary_condition_slip_wall(u_inner, orientation,
+ direction, x, t,
+ surface_flux_function,
+ equations::CompressibleEulerEquations1D)
+ # compute the primitive variables
+ rho_local, v_normal, p_local = cons2prim(u_inner, equations)
+
+ if isodd(direction) # flip sign of normal to make it outward pointing
+ v_normal *= -1
+ end
+
+ # Get the solution of the pressure Riemann problem
+ # See Section 6.3.3 of
+ # Eleuterio F. Toro (2009)
+ # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction
+ # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761)
+ if v_normal <= 0.0
+ sound_speed = sqrt(equations.gamma * p_local / rho_local) # local sound speed
+ p_star = p_local *
+ (1 + 0.5 * (equations.gamma - 1) * v_normal / sound_speed)^(2 *
+ equations.gamma *
+ equations.inv_gamma_minus_one)
+ else # v_normal > 0.0
+ A = 2 / ((equations.gamma + 1) * rho_local)
+ B = p_local * (equations.gamma - 1) / (equations.gamma + 1)
+ p_star = p_local +
+ 0.5 * v_normal / A *
+ (v_normal + sqrt(v_normal^2 + 4 * A * (p_local + B)))
+ end
+
+ # For the slip wall we directly set the flux as the normal velocity is zero
+ return SVector(zero(eltype(u_inner)),
+ p_star,
+ zero(eltype(u_inner)))
+end
+
# Calculate 1D flux for a single point
@inline function flux(u, orientation::Integer, equations::CompressibleEulerEquations1D)
rho, rho_v1, rho_e = u
diff --git a/src/equations/compressible_navier_stokes.jl b/src/equations/compressible_navier_stokes.jl
new file mode 100644
index 00000000000..af7897d4586
--- /dev/null
+++ b/src/equations/compressible_navier_stokes.jl
@@ -0,0 +1,70 @@
+# TODO: can we generalize this to MHD?
+"""
+ struct BoundaryConditionNavierStokesWall
+
+Creates a wall-type boundary conditions for the compressible Navier-Stokes equations.
+The fields `boundary_condition_velocity` and `boundary_condition_heat_flux` are intended
+to be boundary condition types such as the `NoSlip` velocity boundary condition and the
+`Adiabatic` or `Isothermal` heat boundary condition.
+
+!!! warning "Experimental feature"
+ This is an experimental feature and may change in future releases.
+"""
+struct BoundaryConditionNavierStokesWall{V, H}
+ boundary_condition_velocity::V
+ boundary_condition_heat_flux::H
+end
+
+"""
+ struct NoSlip
+
+Use to create a no-slip boundary condition with `BoundaryConditionNavierStokesWall`. The field `boundary_value_function`
+should be a function with signature `boundary_value_function(x, t, equations)`
+and should return a `SVector{NDIMS}` whose entries are the velocity vector at a
+point `x` and time `t`.
+"""
+struct NoSlip{F}
+ boundary_value_function::F # value of the velocity vector on the boundary
+end
+
+"""
+ struct Isothermal
+
+Used to create a no-slip boundary condition with [`BoundaryConditionNavierStokesWall`](@ref).
+The field `boundary_value_function` should be a function with signature
+`boundary_value_function(x, t, equations)` and return a scalar value for the
+temperature at point `x` and time `t`.
+"""
+struct Isothermal{F}
+ boundary_value_function::F # value of the temperature on the boundary
+end
+
+"""
+ struct Adiabatic
+
+Used to create a no-slip boundary condition with [`BoundaryConditionNavierStokesWall`](@ref).
+The field `boundary_value_normal_flux_function` should be a function with signature
+`boundary_value_normal_flux_function(x, t, equations)` and return a scalar value for the
+normal heat flux at point `x` and time `t`.
+"""
+struct Adiabatic{F}
+ boundary_value_normal_flux_function::F # scaled heat flux 1/T * kappa * dT/dn
+end
+
+"""
+!!! warning "Experimental code"
+ This code is experimental and may be changed or removed in any future release.
+
+`GradientVariablesPrimitive` and `GradientVariablesEntropy` are gradient variable type parameters
+for `CompressibleNavierStokesDiffusion1D`. By default, the gradient variables are set to be
+`GradientVariablesPrimitive`. Specifying `GradientVariablesEntropy` instead uses the entropy variable
+formulation from
+- Hughes, Mallet, Franca (1986)
+ A new finite element formulation for computational fluid dynamics: I. Symmetric forms of the
+ compressible Euler and Navier-Stokes equations and the second law of thermodynamics.
+ [https://doi.org/10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1)
+
+Under `GradientVariablesEntropy`, the Navier-Stokes discretization is provably entropy stable.
+"""
+struct GradientVariablesPrimitive end
+struct GradientVariablesEntropy end
diff --git a/src/equations/compressible_navier_stokes_1d.jl b/src/equations/compressible_navier_stokes_1d.jl
new file mode 100644
index 00000000000..dca846cac1e
--- /dev/null
+++ b/src/equations/compressible_navier_stokes_1d.jl
@@ -0,0 +1,403 @@
+@doc raw"""
+ CompressibleNavierStokesDiffusion1D(equations; mu, Pr,
+ gradient_variables=GradientVariablesPrimitive())
+
+Contains the diffusion (i.e. parabolic) terms applied
+to mass, momenta, and total energy together with the advective terms from
+the [`CompressibleEulerEquations1D`](@ref).
+
+- `equations`: instance of the [`CompressibleEulerEquations1D`](@ref)
+- `mu`: dynamic viscosity,
+- `Pr`: Prandtl number,
+- `gradient_variables`: which variables the gradients are taken with respect to.
+ Defaults to `GradientVariablesPrimitive()`.
+
+Fluid properties such as the dynamic viscosity ``\mu`` can be provided in any consistent unit system, e.g.,
+[``\mu``] = kg m⁻¹ s⁻¹.
+
+The particular form of the compressible Navier-Stokes implemented is
+```math
+\frac{\partial}{\partial t}
+\begin{pmatrix}
+\rho \\ \rho v \\ \rho e
+\end{pmatrix}
++
+\frac{\partial}{\partial x}
+\begin{pmatrix}
+ \rho v \\ \rho v^2 + p \\ (\rho e + p) v
+\end{pmatrix}
+=
+\frac{\partial}{\partial x}
+\begin{pmatrix}
+0 \\ \tau \\ \tau v - q
+\end{pmatrix}
+```
+where the system is closed with the ideal gas assumption giving
+```math
+p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho v^2 \right)
+```
+as the pressure. The value of the adiabatic constant `gamma` is taken from the [`CompressibleEulerEquations1D`](@ref).
+The terms on the right hand side of the system above
+are built from the viscous stress
+```math
+\tau = \mu \frac{\partial}{\partial x} v
+```
+where the heat flux is
+```math
+q = -\kappa \frac{\partial}{\partial x} \left(T\right),\quad T = \frac{p}{R\rho}
+```
+where ``T`` is the temperature and ``\kappa`` is the thermal conductivity for Fick's law.
+Under the assumption that the gas has a constant Prandtl number,
+the thermal conductivity is
+```math
+\kappa = \frac{\gamma \mu R}{(\gamma - 1)\textrm{Pr}}.
+```
+From this combination of temperature ``T`` and thermal conductivity ``\kappa`` we see
+that the gas constant `R` cancels and the heat flux becomes
+```math
+q = -\kappa \frac{\partial}{\partial x} \left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}} \frac{\partial}{\partial x} \left(\frac{p}{\rho}\right)
+```
+which is the form implemented below in the [`flux`](@ref) function.
+
+In one spatial dimensions we require gradients for two quantities, e.g.,
+primitive quantities
+```math
+\frac{\partial}{\partial x} v,\, \frac{\partial}{\partial x} T
+```
+or the entropy variables
+```math
+\frac{\partial}{\partial x} w_2,\, \frac{\partial}{\partial x} w_3
+```
+where
+```math
+w_2 = \frac{\rho v1}{p},\, w_3 = -\frac{\rho}{p}
+```
+
+!!! warning "Experimental code"
+ This code is experimental and may be changed or removed in any future release.
+"""
+struct CompressibleNavierStokesDiffusion1D{GradientVariables, RealT <: Real,
+ E <: AbstractCompressibleEulerEquations{1}} <:
+ AbstractCompressibleNavierStokesDiffusion{1, 3}
+ # TODO: parabolic
+ # 1) For now save gamma and inv(gamma-1) again, but could potentially reuse them from the Euler equations
+ # 2) Add NGRADS as a type parameter here and in AbstractEquationsParabolic, add `ngradients(...)` accessor function
+ gamma::RealT # ratio of specific heats
+ inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications
+
+ mu::RealT # viscosity
+ Pr::RealT # Prandtl number
+ kappa::RealT # thermal diffusivity for Fick's law
+
+ equations_hyperbolic::E # CompressibleEulerEquations1D
+ gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy
+end
+
+# default to primitive gradient variables
+function CompressibleNavierStokesDiffusion1D(equations::CompressibleEulerEquations1D;
+ mu, Prandtl,
+ gradient_variables = GradientVariablesPrimitive())
+ gamma = equations.gamma
+ inv_gamma_minus_one = equations.inv_gamma_minus_one
+ μ, Pr = promote(mu, Prandtl)
+
+ # Under the assumption of constant Prandtl number the thermal conductivity
+ # constant is kappa = gamma μ / ((gamma-1) Pr).
+ # Important note! Factor of μ is accounted for later in `flux`.
+ kappa = gamma * inv_gamma_minus_one / Pr
+
+ CompressibleNavierStokesDiffusion1D{typeof(gradient_variables), typeof(gamma),
+ typeof(equations)}(gamma, inv_gamma_minus_one,
+ μ, Pr, kappa,
+ equations, gradient_variables)
+end
+
+# TODO: parabolic
+# This is the flexibility a user should have to select the different gradient variable types
+# varnames(::typeof(cons2prim) , ::CompressibleNavierStokesDiffusion1D) = ("v1", "v2", "T")
+# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion1D) = ("w2", "w3", "w4")
+
+function varnames(variable_mapping,
+ equations_parabolic::CompressibleNavierStokesDiffusion1D)
+ varnames(variable_mapping, equations_parabolic.equations_hyperbolic)
+end
+
+# we specialize this function to compute gradients of primitive variables instead of
+# conservative variables.
+function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ cons2prim
+end
+function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+ cons2entropy
+end
+
+# Explicit formulas for the diffusive Navier-Stokes fluxes are available, e.g., in Section 2
+# of the paper by Rueda-Ramírez, Hennemann, Hindenlang, Winters, and Gassner
+# "An Entropy Stable Nodal Discontinuous Galerkin Method for the resistive
+# MHD Equations. Part II: Subcell Finite Volume Shock Capturing"
+# where one sets the magnetic field components equal to 0.
+function flux(u, gradients, orientation::Integer,
+ equations::CompressibleNavierStokesDiffusion1D)
+ # Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.
+ rho, v1, _ = convert_transformed_to_primitive(u, equations)
+ # Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, v2, T)
+ # either computed directly or reverse engineered from the gradient of the entropy variables
+ # by way of the `convert_gradient_variables` function.
+ _, dv1dx, dTdx = convert_derivative_to_primitive(u, gradients, equations)
+
+ # Viscous stress (tensor)
+ tau_11 = dv1dx
+
+ # Fick's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))
+ # with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)
+ # Note, the gas constant cancels under this formulation, so it is not present
+ # in the implementation
+ q1 = equations.kappa * dTdx
+
+ # Constant dynamic viscosity is copied to a variable for readability.
+ # Offers flexibility for dynamic viscosity via Sutherland's law where it depends
+ # on temperature and reference values, Ts and Tref such that mu(T)
+ mu = equations.mu
+
+ # viscous flux components in the x-direction
+ f1 = zero(rho)
+ f2 = tau_11 * mu
+ f3 = (v1 * tau_11 + q1) * mu
+
+ return SVector(f1, f2, f3)
+end
+
+# Convert conservative variables to primitive
+@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion1D)
+ rho, rho_v1, _ = u
+
+ v1 = rho_v1 / rho
+ T = temperature(u, equations)
+
+ return SVector(rho, v1, T)
+end
+
+# Convert conservative variables to entropy
+# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms
+# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion1D`,
+# but this may be confusing to new users.
+function cons2entropy(u, equations::CompressibleNavierStokesDiffusion1D)
+ cons2entropy(u, equations.equations_hyperbolic)
+end
+function entropy2cons(w, equations::CompressibleNavierStokesDiffusion1D)
+ entropy2cons(w, equations.equations_hyperbolic)
+end
+
+# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.
+# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed
+# variables into primitive variables.
+@inline function convert_transformed_to_primitive(u_transformed,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ return u_transformed
+end
+
+# TODO: parabolic. Make this more efficient!
+@inline function convert_transformed_to_primitive(u_transformed,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+ # note: this uses CompressibleNavierStokesDiffusion1D versions of cons2prim and entropy2cons
+ return cons2prim(entropy2cons(u_transformed, equations), equations)
+end
+
+# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3, w_4) and
+# reverse engineers the gradients to be terms of the primitive variables (v1, v2, T).
+# Helpful because then the diffusive fluxes have the same form as on paper.
+# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.
+# TODO: parabolic; entropy stable viscous terms
+@inline function convert_derivative_to_primitive(u, gradient,
+ ::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ return gradient
+end
+
+# the first argument is always the "transformed" variables.
+@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+
+ # TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.
+ # We can fix this if we directly compute v1, v2, T from the entropy variables
+ u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion1D
+ rho, rho_v1, _ = u
+
+ v1 = rho_v1 / rho
+ T = temperature(u, equations)
+
+ return SVector(gradient_entropy_vars[1],
+ T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[3]), # grad(u) = T*(grad(w_2)+v1*grad(w_3))
+ T * T * gradient_entropy_vars[3])
+end
+
+# This routine is required because `prim2cons` is called in `initial_condition`, which
+# is called with `equations::CompressibleEulerEquations1D`. This means it is inconsistent
+# with `cons2prim(..., ::CompressibleNavierStokesDiffusion1D)` as defined above.
+# TODO: parabolic. Is there a way to clean this up?
+@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion1D)
+ prim2cons(u, equations.equations_hyperbolic)
+end
+
+@inline function temperature(u, equations::CompressibleNavierStokesDiffusion1D)
+ rho, rho_v1, rho_e = u
+
+ p = (equations.gamma - 1) * (rho_e - 0.5 * rho_v1^2 / rho)
+ T = p / rho
+ return T
+end
+
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Adiabatic})(flux_inner,
+ u_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Gradient,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ equations)
+ return SVector(u_inner[1], v1, u_inner[3])
+end
+
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Adiabatic})(flux_inner,
+ u_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Divergence,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ # rho, v1, v2, _ = u_inner
+ normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
+ t,
+ equations)
+ v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ equations)
+ _, tau_1n, _ = flux_inner # extract fluxes for 2nd equation
+ normal_energy_flux = v1 * tau_1n + normal_heat_flux
+ return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)
+end
+
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Isothermal})(flux_inner,
+ u_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Gradient,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ equations)
+ T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
+ equations)
+ return SVector(u_inner[1], v1, T)
+end
+
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Isothermal})(flux_inner,
+ u_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Divergence,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesPrimitive
+ })
+ return flux_inner
+end
+
+# specialized BC impositions for GradientVariablesEntropy.
+
+# This should return a SVector containing the boundary values of entropy variables.
+# Here, `w_inner` are the transformed variables (e.g., entropy variables).
+#
+# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
+# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
+# DOI: 10.1016/j.jcp.2021.110723
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Adiabatic})(flux_inner,
+ w_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Gradient,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+ v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ equations)
+ negative_rho_inv_p = w_inner[3] # w_3 = -rho / p
+ return SVector(w_inner[1], -v1 * negative_rho_inv_p, negative_rho_inv_p)
+end
+
+# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Adiabatic})(flux_inner,
+ w_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Divergence,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+ normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
+ t,
+ equations)
+ v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ equations)
+ _, tau_1n, _ = flux_inner # extract fluxes for 2nd equation
+ normal_energy_flux = v1 * tau_1n + normal_heat_flux
+ return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)
+end
+
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Isothermal})(flux_inner,
+ w_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Gradient,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+ v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ equations)
+ T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
+ equations)
+
+ # the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w3.
+ w3 = -1 / T
+ return SVector(w_inner[1], -v1 * w3, w3)
+end
+
+@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
+ <:Isothermal})(flux_inner,
+ w_inner,
+ orientation::Integer,
+ direction,
+ x, t,
+ operator_type::Divergence,
+ equations::CompressibleNavierStokesDiffusion1D{
+ GradientVariablesEntropy
+ })
+ return SVector(flux_inner[1], flux_inner[2], flux_inner[3])
+end
diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl
index a1f11717e69..f762fe5d5ee 100644
--- a/src/equations/compressible_navier_stokes_2d.jl
+++ b/src/equations/compressible_navier_stokes_2d.jl
@@ -29,7 +29,7 @@ The particular form of the compressible Navier-Stokes implemented is
=
\nabla \cdot
\begin{pmatrix}
-0 \\ \underline{\tau} \\ \underline{\tau}\mathbf{v} - \nabla q
+0 \\ \underline{\tau} \\ \underline{\tau}\mathbf{v} - \mathbf{q}
\end{pmatrix}
```
where the system is closed with the ideal gas assumption giving
@@ -44,7 +44,7 @@ are built from the viscous stress tensor
```
where ``\underline{I}`` is the ``2\times 2`` identity matrix and the heat flux is
```math
-\nabla q = -\kappa\nabla\left(T\right),\quad T = \frac{p}{R\rho}
+\mathbf{q} = -\kappa\nabla\left(T\right),\quad T = \frac{p}{R\rho}
```
where ``T`` is the temperature and ``\kappa`` is the thermal conductivity for Fick's law.
Under the assumption that the gas has a constant Prandtl number,
@@ -55,7 +55,7 @@ the thermal conductivity is
From this combination of temperature ``T`` and thermal conductivity ``\kappa`` we see
that the gas constant `R` cancels and the heat flux becomes
```math
-\nabla q = -\kappa\nabla\left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}}\nabla\left(\frac{p}{\rho}\right)
+\mathbf{q} = -\kappa\nabla\left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}}\nabla\left(\frac{p}{\rho}\right)
```
which is the form implemented below in the [`flux`](@ref) function.
@@ -93,24 +93,6 @@ struct CompressibleNavierStokesDiffusion2D{GradientVariables, RealT <: Real,
gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy
end
-"""
-!!! warning "Experimental code"
- This code is experimental and may be changed or removed in any future release.
-
-`GradientVariablesPrimitive` and `GradientVariablesEntropy` are gradient variable type parameters
-for `CompressibleNavierStokesDiffusion2D`. By default, the gradient variables are set to be
-`GradientVariablesPrimitive`. Specifying `GradientVariablesEntropy` instead uses the entropy variable
-formulation from
-- Hughes, Mallet, Franca (1986)
- A new finite element formulation for computational fluid dynamics: I. Symmetric forms of the
- compressible Euler and Navier-Stokes equations and the second law of thermodynamics.
- [https://doi.org/10.1016/0045-7825(86)90127-1](https://doi.org/10.1016/0045-7825(86)90127-1)
-
-Under `GradientVariablesEntropy`, the Navier-Stokes discretization is provably entropy stable.
-"""
-struct GradientVariablesPrimitive end
-struct GradientVariablesEntropy end
-
# default to primitive gradient variables
function CompressibleNavierStokesDiffusion2D(equations::CompressibleEulerEquations2D;
mu, Prandtl,
@@ -315,59 +297,6 @@ end
return dv2dx - dv1dy
end
-# TODO: can we generalize this to MHD?
-"""
- struct BoundaryConditionNavierStokesWall
-
-Creates a wall-type boundary conditions for the compressible Navier-Stokes equations.
-The fields `boundary_condition_velocity` and `boundary_condition_heat_flux` are intended
-to be boundary condition types such as the `NoSlip` velocity boundary condition and the
-`Adiabatic` or `Isothermal` heat boundary condition.
-
-!!! warning "Experimental feature"
- This is an experimental feature and may change in future releases.
-"""
-struct BoundaryConditionNavierStokesWall{V, H}
- boundary_condition_velocity::V
- boundary_condition_heat_flux::H
-end
-
-"""
- struct NoSlip
-
-Use to create a no-slip boundary condition with `BoundaryConditionNavierStokesWall`. The field `boundary_value_function`
-should be a function with signature `boundary_value_function(x, t, equations)`
-and should return a `SVector{NDIMS}` whose entries are the velocity vector at a
-point `x` and time `t`.
-"""
-struct NoSlip{F}
- boundary_value_function::F # value of the velocity vector on the boundary
-end
-
-"""
- struct Isothermal
-
-Used to create a no-slip boundary condition with [`BoundaryConditionNavierStokesWall`](@ref).
-The field `boundary_value_function` should be a function with signature
-`boundary_value_function(x, t, equations)` and return a scalar value for the
-temperature at point `x` and time `t`.
-"""
-struct Isothermal{F}
- boundary_value_function::F # value of the temperature on the boundary
-end
-
-"""
- struct Adiabatic
-
-Used to create a no-slip boundary condition with [`BoundaryConditionNavierStokesWall`](@ref).
-The field `boundary_value_normal_flux_function` should be a function with signature
-`boundary_value_normal_flux_function(x, t, equations)` and return a scalar value for the
-normal heat flux at point `x` and time `t`.
-"""
-struct Adiabatic{F}
- boundary_value_normal_flux_function::F # scaled heat flux 1/T * kappa * dT/dn
-end
-
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
<:Adiabatic})(flux_inner,
u_inner,
diff --git a/src/equations/compressible_navier_stokes_3d.jl b/src/equations/compressible_navier_stokes_3d.jl
index 0b770dff1ca..166b53bf615 100644
--- a/src/equations/compressible_navier_stokes_3d.jl
+++ b/src/equations/compressible_navier_stokes_3d.jl
@@ -29,7 +29,7 @@ The particular form of the compressible Navier-Stokes implemented is
=
\nabla \cdot
\begin{pmatrix}
-0 \\ \underline{\tau} \\ \underline{\tau}\mathbf{v} - \nabla q
+0 \\ \underline{\tau} \\ \underline{\tau}\mathbf{v} - \mathbf{q}
\end{pmatrix}
```
where the system is closed with the ideal gas assumption giving
@@ -44,7 +44,7 @@ are built from the viscous stress tensor
```
where ``\underline{I}`` is the ``3\times 3`` identity matrix and the heat flux is
```math
-\nabla q = -\kappa\nabla\left(T\right),\quad T = \frac{p}{R\rho}
+\mathbf{q} = -\kappa\nabla\left(T\right),\quad T = \frac{p}{R\rho}
```
where ``T`` is the temperature and ``\kappa`` is the thermal conductivity for Fick's law.
Under the assumption that the gas has a constant Prandtl number,
@@ -55,7 +55,7 @@ the thermal conductivity is
From this combination of temperature ``T`` and thermal conductivity ``\kappa`` we see
that the gas constant `R` cancels and the heat flux becomes
```math
-\nabla q = -\kappa\nabla\left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}}\nabla\left(\frac{p}{\rho}\right)
+\mathbf{q} = -\kappa\nabla\left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}}\nabla\left(\frac{p}{\rho}\right)
```
which is the form implemented below in the [`flux`](@ref) function.
diff --git a/src/equations/equations_parabolic.jl b/src/equations/equations_parabolic.jl
index 6c0be43798a..66214025044 100644
--- a/src/equations/equations_parabolic.jl
+++ b/src/equations/equations_parabolic.jl
@@ -11,5 +11,7 @@ include("laplace_diffusion_2d.jl")
# Compressible Navier-Stokes equations
abstract type AbstractCompressibleNavierStokesDiffusion{NDIMS, NVARS} <:
AbstractEquationsParabolic{NDIMS, NVARS} end
+include("compressible_navier_stokes.jl")
+include("compressible_navier_stokes_1d.jl")
include("compressible_navier_stokes_2d.jl")
include("compressible_navier_stokes_3d.jl")
diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl
index 1aaf23d576a..06a55100d62 100644
--- a/test/test_parabolic_1d.jl
+++ b/test/test_parabolic_1d.jl
@@ -19,7 +19,40 @@ isdir(outdir) && rm(outdir, recursive=true)
linf = [2.847421658558336e-05]
)
end
-
+
+ @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_periodic.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_periodic.jl"),
+ l2 = [0.0001133835907077494, 6.226282245610444e-5, 0.0002820171699999139],
+ linf = [0.0006255102377159538, 0.00036195501456059986, 0.0016147729485886941]
+ )
+ end
+
+ @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_periodic.jl: GradientVariablesEntropy" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_periodic.jl"),
+ equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
+ Prandtl=prandtl_number(),
+ gradient_variables = GradientVariablesEntropy()),
+ l2 = [0.00011310615871043463, 6.216495207074201e-5, 0.00028195843110817814],
+ linf = [0.0006240837363233886, 0.0003616694320713876, 0.0016147339542413874]
+ )
+ end
+
+ @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_walls.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls.jl"),
+ l2 = [0.00047023310868269237, 0.00032181736027057234, 0.0014966266486095025],
+ linf = [0.002996375101363302, 0.002863904256059634, 0.012691132946258676]
+ )
+ end
+
+ @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_walls.jl: GradientVariablesEntropy" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls.jl"),
+ equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
+ Prandtl=prandtl_number(),
+ gradient_variables = GradientVariablesEntropy()),
+ l2 = [0.0004608500483647771, 0.00032431091222851285, 0.0015159733360626845],
+ linf = [0.002754803146635787, 0.0028567714697580906, 0.012941794048176192]
+ )
+ end
end
# Clean up afterwards: delete Trixi output directory
From 68df09d5a21bd8f7393df90dab915247f9498505 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Fri, 11 Aug 2023 09:44:19 +0200
Subject: [PATCH 066/263] fix typo in Davis wave speed estimate for 1d swe
(#1601)
Co-authored-by: Andrew Winters
Co-authored-by: Hendrik Ranocha
---
src/equations/shallow_water_1d.jl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/equations/shallow_water_1d.jl b/src/equations/shallow_water_1d.jl
index 57bcb1212e1..32782d5478c 100644
--- a/src/equations/shallow_water_1d.jl
+++ b/src/equations/shallow_water_1d.jl
@@ -653,7 +653,7 @@ end
c_rr = sqrt(equations.gravity * h_rr)
λ_min = min(v_ll - c_ll, v_rr - c_rr)
- λ_max = max(v_rr + c_rr, v_rr + c_rr)
+ λ_max = max(v_ll + c_ll, v_rr + c_rr)
return λ_min, λ_max
end
From 34c123c285d44af6725b68ae176029736c357542 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 13 Aug 2023 05:50:12 +0200
Subject: [PATCH 067/263] set version to v0.5.38
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index c22d4b90642..6419be4d8fc 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.38-pre"
+version = "0.5.38"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 0b8405a0950944b0883818eb5756ad9b7cd4094e Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Sun, 13 Aug 2023 05:50:27 +0200
Subject: [PATCH 068/263] set development version to v0.5.39-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 6419be4d8fc..dd937ed213b 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.38"
+version = "0.5.39-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From add2542c0076dc6526d969f78cec2f732430bc15 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Aug 2023 19:05:34 +0200
Subject: [PATCH 069/263] Bump crate-ci/typos from 1.16.2 to 1.16.5 (#1606)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.2 to 1.16.5.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.16.2...v1.16.5)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index a1a429cad97..6ebb288ea30 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.16.2
+ uses: crate-ci/typos@v1.16.5
From 7f83a1a938eecd9b841efe215a6e482e67cfdcc1 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 15 Aug 2023 11:58:32 +0200
Subject: [PATCH 070/263] Enable MPI coverage with Linux and reduce heap size
hint (#1603)
* Enable MPI coverage with Linux and reduce heap size hint
* Update runtests.jl
* no MPI coverage CI on macOS
* Update runtests.jl
* Update runtests.jl
---
test/runtests.jl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/runtests.jl b/test/runtests.jl
index 1b0c745dbfd..f1adbaaf1df 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -28,10 +28,10 @@ const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3)
cmd = string(Base.julia_cmd())
coverage = occursin("--code-coverage", cmd) &&
!occursin("--code-coverage=none", cmd)
- if !(coverage && Sys.iswindows()) && !(coverage && Sys.islinux())
+ if !(coverage && Sys.iswindows()) && !(coverage && Sys.isapple())
# We provide a `--heap-size-hint` to avoid/reduce out-of-memory errors during CI testing
mpiexec() do cmd
- run(`$cmd -n $TRIXI_MPI_NPROCS $(Base.julia_cmd()) --threads=1 --check-bounds=yes --heap-size-hint=1G $(abspath("test_mpi.jl"))`)
+ run(`$cmd -n $TRIXI_MPI_NPROCS $(Base.julia_cmd()) --threads=1 --check-bounds=yes --heap-size-hint=0.5G $(abspath("test_mpi.jl"))`)
end
end
end
From a4283e1e8253f7ddf2cabf22e2c7b39ce29a644f Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 15 Aug 2023 17:20:52 +0200
Subject: [PATCH 071/263] Update dependabot.yml (#1608)
---
.github/dependabot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 700707ced32..d60f0707fc2 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,4 +4,4 @@ updates:
- package-ecosystem: "github-actions"
directory: "/" # Location of package manifests
schedule:
- interval: "weekly"
+ interval: "monthly"
From 4da5c53776c1d617a2b9bb656da02640f1d6a211 Mon Sep 17 00:00:00 2001
From: Benjamin Bolm <74359358+bennibolm@users.noreply.github.com>
Date: Fri, 18 Aug 2023 12:37:06 +0200
Subject: [PATCH 072/263] Subcell positivity IDP limiting for conservative
variables (#1476)
* Add IDP positivity limiting for conservative variables
* Add elixir with modified blast wave
* Add documentation
* Fix parameter type
* Adjust output of summary callback
* Merge changes from `subcell-limiting` and `main`
* Fix test with right time stepping
* Implement first suggestions
* Implement suggestions
* Fix elixir
* Relocate `perform_idp_correction!`
* Rename variable in `snake_case`
* Implement other suggestions
* Rename container variables using `snake_case`
* Delete timer
* Merge `subcell-limiting` (Adapt docstrings)
* Merge `subcell-limiting`
* Merge `subcell-limiting` (Renaming and dispatch)
* Fix documentation
* Implement positivty limiter with numbers of cons vars
* Merge suggestions already implemented in `subcell-limiting`
* Fix elixir
* Update docstring and output
* Restructure parameter for positivity limiting
* Add test for "show" routine
* Rename Limiters and Containers
* Rename antidiffusive stage callback
* Relocate subcell limiter code
* Move create_cache routine to specific file
* Implement suggestions
* Implement suggestions
---------
Co-authored-by: Hendrik Ranocha
Co-authored-by: Michael Schlottke-Lakemper
---
NEWS.md | 1 +
.../elixir_euler_shockcapturing_subcell.jl | 92 +++++++
...ubble_shockcapturing_subcell_positivity.jl | 140 ++++++++++
src/Trixi.jl | 5 +-
src/callbacks_stage/callbacks_stage.jl | 1 +
.../subcell_limiter_idp_correction.jl | 69 +++++
.../subcell_limiter_idp_correction_2d.jl | 44 ++++
src/solvers/dg.jl | 40 +++
src/solvers/dgsem_tree/containers_2d.jl | 136 +++++++++-
src/solvers/dgsem_tree/dg.jl | 5 +
.../dgsem_tree/dg_2d_subcell_limiters.jl | 193 ++++++++++++++
src/solvers/dgsem_tree/subcell_limiters.jl | 103 ++++++++
src/solvers/dgsem_tree/subcell_limiters_2d.jl | 114 +++++++++
src/time_integration/methods_SSP.jl | 241 ++++++++++++++++++
src/time_integration/time_integration.jl | 1 +
test/test_tree_2d_euler.jl | 6 +
test/test_tree_2d_eulermulti.jl | 8 +
test/test_unit.jl | 39 +--
18 files changed, 1218 insertions(+), 20 deletions(-)
create mode 100644 examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl
create mode 100644 examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl
create mode 100644 src/callbacks_stage/subcell_limiter_idp_correction.jl
create mode 100644 src/callbacks_stage/subcell_limiter_idp_correction_2d.jl
create mode 100644 src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl
create mode 100644 src/solvers/dgsem_tree/subcell_limiters.jl
create mode 100644 src/solvers/dgsem_tree/subcell_limiters_2d.jl
create mode 100644 src/time_integration/methods_SSP.jl
diff --git a/NEWS.md b/NEWS.md
index 10125c40d17..4b96e1e2834 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -12,6 +12,7 @@ for human readability.
- Non-uniform `TreeMesh` available for hyperbolic-parabolic equations.
- Capability to set truly discontinuous initial conditions in 1D.
- Wetting and drying feature and examples for 1D and 2D shallow water equations
+- Subcell positivity limiting support for conservative variables in 2D for `TreeMesh`
#### Changed
diff --git a/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl b/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl
new file mode 100644
index 00000000000..6b69e4db563
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_euler_shockcapturing_subcell.jl
@@ -0,0 +1,92 @@
+
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Euler equations
+
+equations = CompressibleEulerEquations2D(1.4)
+
+"""
+ initial_condition_blast_wave(x, t, equations::CompressibleEulerEquations2D)
+
+A medium blast wave (modified to lower density and higher pressure) taken from
+- Sebastian Hennemann, Gregor J. Gassner (2020)
+ A provably entropy stable subcell shock capturing approach for high order split form DG
+ [arXiv: 2008.12044](https://arxiv.org/abs/2008.12044)
+"""
+function initial_condition_blast_wave(x, t, equations::CompressibleEulerEquations2D)
+ # Modified From Hennemann & Gassner JCP paper 2020 (Sec. 6.3) -> modified to lower density, higher pressure
+ # 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 "normal" medium blast wave
+ rho = r > 0.5 ? 0.1 : 0.2691 # rho = r > 0.5 ? 1 : 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-1 : 1.245 # p = r > 0.5 ? 1.0E-3 : 1.245
+
+ return prim2cons(SVector(rho, v1, v2, p), equations)
+end
+initial_condition = initial_condition_blast_wave
+
+surface_flux = flux_lax_friedrichs
+volume_flux = flux_ranocha
+basis = LobattoLegendreBasis(3)
+limiter_idp = SubcellLimiterIDP(equations, basis;
+ positivity_variables_cons=[1],
+ positivity_correction_factor=0.5)
+volume_integral = VolumeIntegralSubcellLimiting(limiter_idp;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+coordinates_min = (-2.0, -2.0)
+coordinates_max = ( 2.0, 2.0)
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=5,
+ n_cells_max=100_000)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=100,
+ save_initial_solution=true,
+ save_final_solution=true,
+ solution_variables=cons2prim)
+
+stepsize_callback = StepsizeCallback(cfl=0.6)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback, alive_callback,
+ save_solution,
+ stepsize_callback)
+
+
+###############################################################################
+# run the simulation
+
+stage_callbacks = (SubcellLimiterIDPCorrection(),)
+
+sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks=stage_callbacks);
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl b/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl
new file mode 100644
index 00000000000..a67eaeb5b2b
--- /dev/null
+++ b/examples/tree_2d_dgsem/elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl
@@ -0,0 +1,140 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the compressible Euler multicomponent equations
+
+# 1) Dry Air 2) Helium + 28% Air
+equations = CompressibleEulerMulticomponentEquations2D(gammas = (1.4, 1.648),
+ gas_constants = (0.287, 1.578))
+
+"""
+ initial_condition_shock_bubble(x, t, equations::CompressibleEulerMulticomponentEquations2D{5, 2})
+
+A shock-bubble testcase for multicomponent Euler equations
+- Ayoub Gouasmi, Karthik Duraisamy, Scott Murman
+ Formulation of Entropy-Stable schemes for the multicomponent compressible Euler equations
+ [arXiv: 1904.00972](https://arxiv.org/abs/1904.00972)
+"""
+function initial_condition_shock_bubble(x, t, equations::CompressibleEulerMulticomponentEquations2D{5, 2})
+ # bubble test case, see Gouasmi et al. https://arxiv.org/pdf/1904.00972
+ # other reference: https://www.researchgate.net/profile/Pep_Mulet/publication/222675930_A_flux-split_algorithm_applied_to_conservative_models_for_multicomponent_compressible_flows/links/568da54508aeaa1481ae7af0.pdf
+ # typical domain is rectangular, we change it to a square, as Trixi can only do squares
+ @unpack gas_constants = equations
+
+ # Positivity Preserving Parameter, can be set to zero if scheme is positivity preserving
+ delta = 0.03
+
+ # Region I
+ rho1_1 = delta
+ rho2_1 = 1.225 * gas_constants[1]/gas_constants[2] - delta
+ v1_1 = zero(delta)
+ v2_1 = zero(delta)
+ p_1 = 101325
+
+ # Region II
+ rho1_2 = 1.225-delta
+ rho2_2 = delta
+ v1_2 = zero(delta)
+ v2_2 = zero(delta)
+ p_2 = 101325
+
+ # Region III
+ rho1_3 = 1.6861 - delta
+ rho2_3 = delta
+ v1_3 = -113.5243
+ v2_3 = zero(delta)
+ p_3 = 159060
+
+ # Set up Region I & II:
+ inicenter = SVector(zero(delta), zero(delta))
+ x_norm = x[1] - inicenter[1]
+ y_norm = x[2] - inicenter[2]
+ r = sqrt(x_norm^2 + y_norm^2)
+
+ if (x[1] > 0.50)
+ # Set up Region III
+ rho1 = rho1_3
+ rho2 = rho2_3
+ v1 = v1_3
+ v2 = v2_3
+ p = p_3
+ elseif (r < 0.25)
+ # Set up Region I
+ rho1 = rho1_1
+ rho2 = rho2_1
+ v1 = v1_1
+ v2 = v2_1
+ p = p_1
+ else
+ # Set up Region II
+ rho1 = rho1_2
+ rho2 = rho2_2
+ v1 = v1_2
+ v2 = v2_2
+ p = p_2
+ end
+
+ return prim2cons(SVector(v1, v2, p, rho1, rho2), equations)
+end
+initial_condition = initial_condition_shock_bubble
+
+surface_flux = flux_lax_friedrichs
+volume_flux = flux_ranocha
+basis = LobattoLegendreBasis(3)
+
+limiter_idp = SubcellLimiterIDP(equations, basis;
+ positivity_variables_cons=[(i+3 for i in eachcomponent(equations))...])
+
+volume_integral = VolumeIntegralSubcellLimiting(limiter_idp;
+ volume_flux_dg=volume_flux,
+ volume_flux_fv=surface_flux)
+
+solver = DGSEM(basis, surface_flux, volume_integral)
+
+coordinates_min = (-2.25, -2.225)
+coordinates_max = ( 2.20, 2.225)
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=3,
+ n_cells_max=1_000_000)
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 0.01)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 300
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval,
+ extra_analysis_integrals=(Trixi.density,))
+
+alive_callback = AliveCallback(analysis_interval=analysis_interval)
+
+save_solution = SaveSolutionCallback(interval=300,
+ save_initial_solution=true,
+ save_final_solution=true,
+ solution_variables=cons2prim)
+
+stepsize_callback = StepsizeCallback(cfl=0.9)
+
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback,
+ save_solution,
+ stepsize_callback)
+
+
+###############################################################################
+# run the simulation
+
+stage_callbacks = (SubcellLimiterIDPCorrection(),)
+
+sol = Trixi.solve(ode, Trixi.SimpleSSPRK33(stage_callbacks=stage_callbacks);
+ dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep=false, callback=callbacks);
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/src/Trixi.jl b/src/Trixi.jl
index 78ddaa3ca7f..ec4d20558e5 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -121,10 +121,10 @@ include("semidiscretization/semidiscretization_hyperbolic.jl")
include("semidiscretization/semidiscretization_hyperbolic_parabolic.jl")
include("semidiscretization/semidiscretization_euler_acoustics.jl")
include("semidiscretization/semidiscretization_coupled.jl")
+include("time_integration/time_integration.jl")
include("callbacks_step/callbacks_step.jl")
include("callbacks_stage/callbacks_stage.jl")
include("semidiscretization/semidiscretization_euler_gravity.jl")
-include("time_integration/time_integration.jl")
# `trixi_include` and special elixirs such as `convergence_test`
include("auxiliary/special_elixirs.jl")
@@ -229,6 +229,9 @@ export DG,
SurfaceIntegralUpwind,
MortarL2
+export VolumeIntegralSubcellLimiting,
+ SubcellLimiterIDP, SubcellLimiterIDPCorrection
+
export nelements, nnodes, nvariables,
eachelement, eachnode, eachvariable
diff --git a/src/callbacks_stage/callbacks_stage.jl b/src/callbacks_stage/callbacks_stage.jl
index ab0f34efb78..976af327e6f 100644
--- a/src/callbacks_stage/callbacks_stage.jl
+++ b/src/callbacks_stage/callbacks_stage.jl
@@ -6,6 +6,7 @@
#! format: noindent
include("positivity_zhang_shu.jl")
+include("subcell_limiter_idp_correction.jl")
# TODO: TrixiShallowWater: move specific limiter file
include("positivity_shallow_water.jl")
end # @muladd
diff --git a/src/callbacks_stage/subcell_limiter_idp_correction.jl b/src/callbacks_stage/subcell_limiter_idp_correction.jl
new file mode 100644
index 00000000000..69125ebecd9
--- /dev/null
+++ b/src/callbacks_stage/subcell_limiter_idp_correction.jl
@@ -0,0 +1,69 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+"""
+ SubcellLimiterIDPCorrection()
+
+Perform antidiffusive correction stage for the a posteriori IDP limiter [`SubcellLimiterIDP`](@ref)
+called with [`VolumeIntegralSubcellLimiting`](@ref).
+
+!!! note
+ This callback and the actual limiter [`SubcellLimiterIDP`](@ref) only work together.
+ This is not a replacement but a necessary addition.
+
+## References
+
+- Rueda-Ramírez, Pazner, Gassner (2022)
+ Subcell Limiting Strategies for Discontinuous Galerkin Spectral Element Methods
+ [DOI: 10.1016/j.compfluid.2022.105627](https://doi.org/10.1016/j.compfluid.2022.105627)
+- Pazner (2020)
+ Sparse invariant domain preserving discontinuous Galerkin methods with subcell convex limiting
+ [DOI: 10.1016/j.cma.2021.113876](https://doi.org/10.1016/j.cma.2021.113876)
+
+!!! warning "Experimental implementation"
+ This is an experimental feature and may change in future releases.
+"""
+struct SubcellLimiterIDPCorrection end
+
+function (limiter!::SubcellLimiterIDPCorrection)(u_ode,
+ integrator::Trixi.SimpleIntegratorSSP,
+ stage)
+ semi = integrator.p
+ limiter!(u_ode, semi, integrator.t, integrator.dt,
+ semi.solver.volume_integral)
+end
+
+function (limiter!::SubcellLimiterIDPCorrection)(u_ode, semi, t, dt,
+ volume_integral::VolumeIntegralSubcellLimiting)
+ @trixi_timeit timer() "a posteriori limiter" limiter!(u_ode, semi, t, dt,
+ volume_integral.limiter)
+end
+
+function (limiter!::SubcellLimiterIDPCorrection)(u_ode, semi, t, dt,
+ limiter::SubcellLimiterIDP)
+ mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
+
+ u = wrap_array(u_ode, mesh, equations, solver, cache)
+
+ # Calculate blending factor alpha in [0,1]
+ # f_ij = alpha_ij * f^(FV)_ij + (1 - alpha_ij) * f^(DG)_ij
+ # = f^(FV)_ij + (1 - alpha_ij) * f^(antidiffusive)_ij
+ @trixi_timeit timer() "blending factors" solver.volume_integral.limiter(u, semi,
+ solver, t,
+ dt)
+
+ perform_idp_correction!(u, dt, mesh, equations, solver, cache)
+
+ return nothing
+end
+
+init_callback(limiter!::SubcellLimiterIDPCorrection, semi) = nothing
+
+finalize_callback(limiter!::SubcellLimiterIDPCorrection, semi) = nothing
+
+include("subcell_limiter_idp_correction_2d.jl")
+end # @muladd
diff --git a/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl b/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl
new file mode 100644
index 00000000000..f6b91444578
--- /dev/null
+++ b/src/callbacks_stage/subcell_limiter_idp_correction_2d.jl
@@ -0,0 +1,44 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+function perform_idp_correction!(u, dt, mesh::TreeMesh2D, equations, dg, cache)
+ @unpack inverse_weights = dg.basis
+ @unpack antidiffusive_flux1, antidiffusive_flux2 = cache.antidiffusive_fluxes
+ @unpack alpha1, alpha2 = dg.volume_integral.limiter.cache.subcell_limiter_coefficients
+
+ @threaded for element in eachelement(dg, cache)
+ # Sign switch as in apply_jacobian!
+ inverse_jacobian = -cache.elements.inverse_jacobian[element]
+
+ for j in eachnode(dg), i in eachnode(dg)
+ # Note: antidiffusive_flux1[v, i, xi, element] = antidiffusive_flux2[v, xi, i, element] = 0 for all i in 1:nnodes and xi in {1, nnodes+1}
+ alpha_flux1 = (1 - alpha1[i, j, element]) *
+ get_node_vars(antidiffusive_flux1, equations, dg, i, j,
+ element)
+ alpha_flux1_ip1 = (1 - alpha1[i + 1, j, element]) *
+ get_node_vars(antidiffusive_flux1, equations, dg, i + 1,
+ j, element)
+ alpha_flux2 = (1 - alpha2[i, j, element]) *
+ get_node_vars(antidiffusive_flux2, equations, dg, i, j,
+ element)
+ alpha_flux2_jp1 = (1 - alpha2[i, j + 1, element]) *
+ get_node_vars(antidiffusive_flux2, equations, dg, i,
+ j + 1, element)
+
+ for v in eachvariable(equations)
+ u[v, i, j, element] += dt * inverse_jacobian *
+ (inverse_weights[i] *
+ (alpha_flux1_ip1[v] - alpha_flux1[v]) +
+ inverse_weights[j] *
+ (alpha_flux2_jp1[v] - alpha_flux2[v]))
+ end
+ end
+ end
+
+ return nothing
+end
+end # @muladd
diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl
index 495e0ffc4a4..36bbc6de361 100644
--- a/src/solvers/dg.jl
+++ b/src/solvers/dg.jl
@@ -174,6 +174,46 @@ function Base.show(io::IO, ::MIME"text/plain",
end
end
+"""
+ VolumeIntegralSubcellLimiting(limiter;
+ volume_flux_dg, volume_flux_fv)
+
+A subcell limiting volume integral type for DG methods based on subcell blending approaches
+with a low-order FV method. Used with limiter [`SubcellLimiterIDP`](@ref).
+
+!!! warning "Experimental implementation"
+ This is an experimental feature and may change in future releases.
+"""
+struct VolumeIntegralSubcellLimiting{VolumeFluxDG, VolumeFluxFV, Limiter} <:
+ AbstractVolumeIntegral
+ volume_flux_dg::VolumeFluxDG
+ volume_flux_fv::VolumeFluxFV
+ limiter::Limiter
+end
+
+function VolumeIntegralSubcellLimiting(limiter; volume_flux_dg,
+ volume_flux_fv)
+ VolumeIntegralSubcellLimiting{typeof(volume_flux_dg), typeof(volume_flux_fv),
+ typeof(limiter)}(volume_flux_dg, volume_flux_fv,
+ limiter)
+end
+
+function Base.show(io::IO, mime::MIME"text/plain",
+ integral::VolumeIntegralSubcellLimiting)
+ @nospecialize integral # reduce precompilation time
+
+ if get(io, :compact, false)
+ show(io, integral)
+ else
+ summary_header(io, "VolumeIntegralSubcellLimiting")
+ summary_line(io, "volume flux DG", integral.volume_flux_dg)
+ summary_line(io, "volume flux FV", integral.volume_flux_fv)
+ summary_line(io, "limiter", integral.limiter |> typeof |> nameof)
+ show(increment_indent(io), mime, integral.limiter)
+ summary_footer(io)
+ end
+end
+
# TODO: FD. Should this definition live in a different file because it is
# not strictly a DG method?
"""
diff --git a/src/solvers/dgsem_tree/containers_2d.jl b/src/solvers/dgsem_tree/containers_2d.jl
index d80522d42fd..9148b936312 100644
--- a/src/solvers/dgsem_tree/containers_2d.jl
+++ b/src/solvers/dgsem_tree/containers_2d.jl
@@ -77,7 +77,7 @@ end
eachelement(elements::ElementContainer2D)
Return an iterator over the indices that specify the location in relevant data structures
-for the elements in `elements`.
+for the elements in `elements`.
In particular, not the elements themselves are returned.
"""
@inline eachelement(elements::ElementContainer2D) = Base.OneTo(nelements(elements))
@@ -1254,4 +1254,138 @@ function init_mpi_mortars!(mpi_mortars, elements, mesh::TreeMesh2D)
return nothing
end
+
+# Container data structure (structure-of-arrays style) for FCT-type antidiffusive fluxes
+# (i, j+1)
+# |
+# flux2(i, j+1)
+# |
+# (i-1, j) ---flux1(i, j)--- (i, j) ---flux1(i+1, j)--- (i+1, j)
+# |
+# flux2(i, j)
+# |
+# (i, j-1)
+mutable struct ContainerAntidiffusiveFlux2D{uEltype <: Real}
+ antidiffusive_flux1::Array{uEltype, 4} # [variables, i, j, elements]
+ antidiffusive_flux2::Array{uEltype, 4} # [variables, i, j, elements]
+ # internal `resize!`able storage
+ _antidiffusive_flux1::Vector{uEltype}
+ _antidiffusive_flux2::Vector{uEltype}
+end
+
+function ContainerAntidiffusiveFlux2D{uEltype}(capacity::Integer, n_variables,
+ n_nodes) where {uEltype <: Real}
+ nan_uEltype = convert(uEltype, NaN)
+
+ # Initialize fields with defaults
+ _antidiffusive_flux1 = fill(nan_uEltype,
+ n_variables * (n_nodes + 1) * n_nodes * capacity)
+ antidiffusive_flux1 = unsafe_wrap(Array, pointer(_antidiffusive_flux1),
+ (n_variables, n_nodes + 1, n_nodes, capacity))
+
+ _antidiffusive_flux2 = fill(nan_uEltype,
+ n_variables * n_nodes * (n_nodes + 1) * capacity)
+ antidiffusive_flux2 = unsafe_wrap(Array, pointer(_antidiffusive_flux2),
+ (n_variables, n_nodes, n_nodes + 1, capacity))
+
+ return ContainerAntidiffusiveFlux2D{uEltype}(antidiffusive_flux1,
+ antidiffusive_flux2,
+ _antidiffusive_flux1,
+ _antidiffusive_flux2)
+end
+
+nvariables(fluxes::ContainerAntidiffusiveFlux2D) = size(fluxes.antidiffusive_flux1, 1)
+nnodes(fluxes::ContainerAntidiffusiveFlux2D) = size(fluxes.antidiffusive_flux1, 3)
+
+# Only one-dimensional `Array`s are `resize!`able in Julia.
+# Hence, we use `Vector`s as internal storage and `resize!`
+# them whenever needed. Then, we reuse the same memory by
+# `unsafe_wrap`ping multi-dimensional `Array`s around the
+# internal storage.
+function Base.resize!(fluxes::ContainerAntidiffusiveFlux2D, capacity)
+ n_nodes = nnodes(fluxes)
+ n_variables = nvariables(fluxes)
+
+ @unpack _antidiffusive_flux1, _antidiffusive_flux2 = fluxes
+
+ resize!(_antidiffusive_flux1, n_variables * (n_nodes + 1) * n_nodes * capacity)
+ fluxes.antidiffusive_flux1 = unsafe_wrap(Array, pointer(_antidiffusive_flux1),
+ (n_variables, n_nodes + 1, n_nodes,
+ capacity))
+ resize!(_antidiffusive_flux2, n_variables * n_nodes * (n_nodes + 1) * capacity)
+ fluxes.antidiffusive_flux2 = unsafe_wrap(Array, pointer(_antidiffusive_flux2),
+ (n_variables, n_nodes, n_nodes + 1,
+ capacity))
+
+ return nothing
+end
+
+# Container data structure (structure-of-arrays style) for variables used for IDP limiting
+mutable struct ContainerSubcellLimiterIDP2D{uEltype <: Real}
+ alpha::Array{uEltype, 3} # [i, j, element]
+ alpha1::Array{uEltype, 3}
+ alpha2::Array{uEltype, 3}
+ variable_bounds::Vector{Array{uEltype, 3}}
+ # internal `resize!`able storage
+ _alpha::Vector{uEltype}
+ _alpha1::Vector{uEltype}
+ _alpha2::Vector{uEltype}
+ _variable_bounds::Vector{Vector{uEltype}}
+end
+
+function ContainerSubcellLimiterIDP2D{uEltype}(capacity::Integer, n_nodes,
+ length) where {uEltype <: Real}
+ nan_uEltype = convert(uEltype, NaN)
+
+ # Initialize fields with defaults
+ _alpha = fill(nan_uEltype, n_nodes * n_nodes * capacity)
+ alpha = unsafe_wrap(Array, pointer(_alpha), (n_nodes, n_nodes, capacity))
+ _alpha1 = fill(nan_uEltype, (n_nodes + 1) * n_nodes * capacity)
+ alpha1 = unsafe_wrap(Array, pointer(_alpha1), (n_nodes + 1, n_nodes, capacity))
+ _alpha2 = fill(nan_uEltype, n_nodes * (n_nodes + 1) * capacity)
+ alpha2 = unsafe_wrap(Array, pointer(_alpha2), (n_nodes, n_nodes + 1, capacity))
+
+ _variable_bounds = Vector{Vector{uEltype}}(undef, length)
+ variable_bounds = Vector{Array{uEltype, 3}}(undef, length)
+ for i in 1:length
+ _variable_bounds[i] = fill(nan_uEltype, n_nodes * n_nodes * capacity)
+ variable_bounds[i] = unsafe_wrap(Array, pointer(_variable_bounds[i]),
+ (n_nodes, n_nodes, capacity))
+ end
+
+ return ContainerSubcellLimiterIDP2D{uEltype}(alpha, alpha1, alpha2,
+ variable_bounds,
+ _alpha, _alpha1, _alpha2,
+ _variable_bounds)
+end
+
+nnodes(container::ContainerSubcellLimiterIDP2D) = size(container.alpha, 1)
+
+# Only one-dimensional `Array`s are `resize!`able in Julia.
+# Hence, we use `Vector`s as internal storage and `resize!`
+# them whenever needed. Then, we reuse the same memory by
+# `unsafe_wrap`ping multi-dimensional `Array`s around the
+# internal storage.
+function Base.resize!(container::ContainerSubcellLimiterIDP2D, capacity)
+ n_nodes = nnodes(container)
+
+ @unpack _alpha, _alpha1, _alpha2 = container
+ resize!(_alpha, n_nodes * n_nodes * capacity)
+ container.alpha = unsafe_wrap(Array, pointer(_alpha), (n_nodes, n_nodes, capacity))
+ resize!(_alpha1, (n_nodes + 1) * n_nodes * capacity)
+ container.alpha1 = unsafe_wrap(Array, pointer(_alpha1),
+ (n_nodes + 1, n_nodes, capacity))
+ resize!(_alpha2, n_nodes * (n_nodes + 1) * capacity)
+ container.alpha2 = unsafe_wrap(Array, pointer(_alpha2),
+ (n_nodes, n_nodes + 1, capacity))
+
+ @unpack _variable_bounds = container
+ for i in 1:length(_variable_bounds)
+ resize!(_variable_bounds[i], n_nodes * n_nodes * capacity)
+ container.variable_bounds[i] = unsafe_wrap(Array, pointer(_variable_bounds[i]),
+ (n_nodes, n_nodes, capacity))
+ end
+
+ return nothing
+end
end # @muladd
diff --git a/src/solvers/dgsem_tree/dg.jl b/src/solvers/dgsem_tree/dg.jl
index cb28dad968c..6e02bc1d94a 100644
--- a/src/solvers/dgsem_tree/dg.jl
+++ b/src/solvers/dgsem_tree/dg.jl
@@ -71,4 +71,9 @@ include("dg_3d_parabolic.jl")
# as well as specialized implementations used to improve performance
include("dg_2d_compressible_euler.jl")
include("dg_3d_compressible_euler.jl")
+
+# Subcell limiters
+include("subcell_limiters.jl")
+include("subcell_limiters_2d.jl")
+include("dg_2d_subcell_limiters.jl")
end # @muladd
diff --git a/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl b/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl
new file mode 100644
index 00000000000..70ff346740d
--- /dev/null
+++ b/src/solvers/dgsem_tree/dg_2d_subcell_limiters.jl
@@ -0,0 +1,193 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+function create_cache(mesh::TreeMesh{2}, equations,
+ volume_integral::VolumeIntegralSubcellLimiting, dg::DG, uEltype)
+ cache = create_cache(mesh, equations,
+ VolumeIntegralPureLGLFiniteVolume(volume_integral.volume_flux_fv),
+ dg, uEltype)
+
+ A3dp1_x = Array{uEltype, 3}
+ A3dp1_y = Array{uEltype, 3}
+ A3d = Array{uEltype, 3}
+
+ fhat1_threaded = A3dp1_x[A3dp1_x(undef, nvariables(equations), nnodes(dg) + 1,
+ nnodes(dg)) for _ in 1:Threads.nthreads()]
+ fhat2_threaded = A3dp1_y[A3dp1_y(undef, nvariables(equations), nnodes(dg),
+ nnodes(dg) + 1) for _ in 1:Threads.nthreads()]
+ flux_temp_threaded = A3d[A3d(undef, nvariables(equations), nnodes(dg), nnodes(dg))
+ for _ in 1:Threads.nthreads()]
+
+ antidiffusive_fluxes = Trixi.ContainerAntidiffusiveFlux2D{uEltype}(0,
+ nvariables(equations),
+ nnodes(dg))
+
+ return (; cache..., antidiffusive_fluxes, fhat1_threaded, fhat2_threaded,
+ flux_temp_threaded)
+end
+
+function calc_volume_integral!(du, u,
+ mesh::TreeMesh{2},
+ nonconservative_terms, equations,
+ volume_integral::VolumeIntegralSubcellLimiting,
+ dg::DGSEM, cache)
+ @unpack limiter = volume_integral
+
+ @threaded for element in eachelement(dg, cache)
+ subcell_limiting_kernel!(du, u, element, mesh,
+ nonconservative_terms, equations,
+ volume_integral, limiter,
+ dg, cache)
+ end
+end
+
+@inline function subcell_limiting_kernel!(du, u,
+ element, mesh::TreeMesh{2},
+ nonconservative_terms::False, equations,
+ volume_integral, limiter::SubcellLimiterIDP,
+ dg::DGSEM, cache)
+ @unpack inverse_weights = dg.basis
+ @unpack volume_flux_dg, volume_flux_fv = volume_integral
+
+ # high-order DG fluxes
+ @unpack fhat1_threaded, fhat2_threaded = cache
+
+ fhat1 = fhat1_threaded[Threads.threadid()]
+ fhat2 = fhat2_threaded[Threads.threadid()]
+ calcflux_fhat!(fhat1, fhat2, u, mesh,
+ nonconservative_terms, equations, volume_flux_dg, dg, element, cache)
+
+ # low-order FV fluxes
+ @unpack fstar1_L_threaded, fstar1_R_threaded, fstar2_L_threaded, fstar2_R_threaded = cache
+
+ fstar1_L = fstar1_L_threaded[Threads.threadid()]
+ fstar2_L = fstar2_L_threaded[Threads.threadid()]
+ fstar1_R = fstar1_R_threaded[Threads.threadid()]
+ fstar2_R = fstar2_R_threaded[Threads.threadid()]
+ calcflux_fv!(fstar1_L, fstar1_R, fstar2_L, fstar2_R, u, mesh,
+ nonconservative_terms, equations, volume_flux_fv, dg, element, cache)
+
+ # antidiffusive flux
+ calcflux_antidiffusive!(fhat1, fhat2, fstar1_L, fstar2_L, u, mesh,
+ nonconservative_terms, equations, limiter, dg, element,
+ cache)
+
+ # Calculate volume integral contribution of low-order FV flux
+ for j in eachnode(dg), i in eachnode(dg)
+ for v in eachvariable(equations)
+ du[v, i, j, element] += inverse_weights[i] *
+ (fstar1_L[v, i + 1, j] - fstar1_R[v, i, j]) +
+ inverse_weights[j] *
+ (fstar2_L[v, i, j + 1] - fstar2_R[v, i, j])
+ end
+ end
+
+ return nothing
+end
+
+# Calculate the DG staggered volume fluxes `fhat` in subcell FV-form inside the element
+# (**without non-conservative terms**).
+#
+# See also `flux_differencing_kernel!`.
+@inline function calcflux_fhat!(fhat1, fhat2, u,
+ mesh::TreeMesh{2}, nonconservative_terms::False,
+ equations,
+ volume_flux, dg::DGSEM, element, cache)
+ @unpack weights, derivative_split = dg.basis
+ @unpack flux_temp_threaded = cache
+
+ flux_temp = flux_temp_threaded[Threads.threadid()]
+
+ # The FV-form fluxes are calculated in a recursive manner, i.e.:
+ # fhat_(0,1) = w_0 * FVol_0,
+ # fhat_(j,j+1) = fhat_(j-1,j) + w_j * FVol_j, for j=1,...,N-1,
+ # with the split form volume fluxes FVol_j = -2 * sum_i=0^N D_ji f*_(j,i).
+
+ # To use the symmetry of the `volume_flux`, the split form volume flux is precalculated
+ # like in `calc_volume_integral!` for the `VolumeIntegralFluxDifferencing`
+ # and saved in in `flux_temp`.
+
+ # Split form volume flux in orientation 1: x direction
+ flux_temp .= zero(eltype(flux_temp))
+
+ for j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, j, element)
+
+ # All diagonal entries of `derivative_split` are zero. Thus, we can skip
+ # the computation of the diagonal terms. In addition, we use the symmetry
+ # of the `volume_flux` to save half of the possible two-point flux
+ # computations.
+ for ii in (i + 1):nnodes(dg)
+ u_node_ii = get_node_vars(u, equations, dg, ii, j, element)
+ flux1 = volume_flux(u_node, u_node_ii, 1, equations)
+ multiply_add_to_node_vars!(flux_temp, derivative_split[i, ii], flux1,
+ equations, dg, i, j)
+ multiply_add_to_node_vars!(flux_temp, derivative_split[ii, i], flux1,
+ equations, dg, ii, j)
+ end
+ end
+
+ # FV-form flux `fhat` in x direction
+ fhat1[:, 1, :] .= zero(eltype(fhat1))
+ fhat1[:, nnodes(dg) + 1, :] .= zero(eltype(fhat1))
+
+ for j in eachnode(dg), i in 1:(nnodes(dg) - 1), v in eachvariable(equations)
+ fhat1[v, i + 1, j] = fhat1[v, i, j] + weights[i] * flux_temp[v, i, j]
+ end
+
+ # Split form volume flux in orientation 2: y direction
+ flux_temp .= zero(eltype(flux_temp))
+
+ for j in eachnode(dg), i in eachnode(dg)
+ u_node = get_node_vars(u, equations, dg, i, j, element)
+ for jj in (j + 1):nnodes(dg)
+ u_node_jj = get_node_vars(u, equations, dg, i, jj, element)
+ flux2 = volume_flux(u_node, u_node_jj, 2, equations)
+ multiply_add_to_node_vars!(flux_temp, derivative_split[j, jj], flux2,
+ equations, dg, i, j)
+ multiply_add_to_node_vars!(flux_temp, derivative_split[jj, j], flux2,
+ equations, dg, i, jj)
+ end
+ end
+
+ # FV-form flux `fhat` in y direction
+ fhat2[:, :, 1] .= zero(eltype(fhat2))
+ fhat2[:, :, nnodes(dg) + 1] .= zero(eltype(fhat2))
+
+ for j in 1:(nnodes(dg) - 1), i in eachnode(dg), v in eachvariable(equations)
+ fhat2[v, i, j + 1] = fhat2[v, i, j] + weights[j] * flux_temp[v, i, j]
+ end
+
+ return nothing
+end
+
+# Calculate the antidiffusive flux `antidiffusive_flux` as the subtraction between `fhat` and `fstar`.
+@inline function calcflux_antidiffusive!(fhat1, fhat2, fstar1, fstar2, u, mesh,
+ nonconservative_terms, equations,
+ limiter::SubcellLimiterIDP, dg, element, cache)
+ @unpack antidiffusive_flux1, antidiffusive_flux2 = cache.antidiffusive_fluxes
+
+ for j in eachnode(dg), i in 2:nnodes(dg)
+ for v in eachvariable(equations)
+ antidiffusive_flux1[v, i, j, element] = fhat1[v, i, j] - fstar1[v, i, j]
+ end
+ end
+ for j in 2:nnodes(dg), i in eachnode(dg)
+ for v in eachvariable(equations)
+ antidiffusive_flux2[v, i, j, element] = fhat2[v, i, j] - fstar2[v, i, j]
+ end
+ end
+
+ antidiffusive_flux1[:, 1, :, element] .= zero(eltype(antidiffusive_flux1))
+ antidiffusive_flux1[:, nnodes(dg) + 1, :, element] .= zero(eltype(antidiffusive_flux1))
+
+ antidiffusive_flux2[:, :, 1, element] .= zero(eltype(antidiffusive_flux2))
+ antidiffusive_flux2[:, :, nnodes(dg) + 1, element] .= zero(eltype(antidiffusive_flux2))
+
+ return nothing
+end
+end # @muladd
diff --git a/src/solvers/dgsem_tree/subcell_limiters.jl b/src/solvers/dgsem_tree/subcell_limiters.jl
new file mode 100644
index 00000000000..3a707de3bc7
--- /dev/null
+++ b/src/solvers/dgsem_tree/subcell_limiters.jl
@@ -0,0 +1,103 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+abstract type AbstractSubcellLimiter end
+
+function create_cache(typ::Type{LimiterType},
+ semi) where {LimiterType <: AbstractSubcellLimiter}
+ create_cache(typ, mesh_equations_solver_cache(semi)...)
+end
+
+"""
+ SubcellLimiterIDP(equations::AbstractEquations, basis;
+ positivity_variables_cons = [],
+ positivity_correction_factor = 0.1)
+
+Subcell invariant domain preserving (IDP) limiting used with [`VolumeIntegralSubcellLimiting`](@ref)
+including:
+- positivity limiting for conservative variables (`positivity_variables_cons`)
+
+The bounds are calculated using the low-order FV solution. The positivity limiter uses
+`positivity_correction_factor` such that `u^new >= positivity_correction_factor * u^FV`.
+
+!!! note
+ This limiter and the correction callback [`SubcellLimiterIDPCorrection`](@ref) only work together.
+ Without the callback, no limiting takes place, leading to a standard flux-differencing DGSEM scheme.
+
+## References
+
+- Rueda-Ramírez, Pazner, Gassner (2022)
+ Subcell Limiting Strategies for Discontinuous Galerkin Spectral Element Methods
+ [DOI: 10.1016/j.compfluid.2022.105627](https://doi.org/10.1016/j.compfluid.2022.105627)
+- Pazner (2020)
+ Sparse invariant domain preserving discontinuous Galerkin methods with subcell convex limiting
+ [DOI: 10.1016/j.cma.2021.113876](https://doi.org/10.1016/j.cma.2021.113876)
+
+!!! warning "Experimental implementation"
+ This is an experimental feature and may change in future releases.
+"""
+struct SubcellLimiterIDP{RealT <: Real, Cache} <: AbstractSubcellLimiter
+ positivity::Bool
+ positivity_variables_cons::Vector{Int} # Positivity for conservative variables
+ positivity_correction_factor::RealT
+ cache::Cache
+end
+
+# this method is used when the indicator is constructed as for shock-capturing volume integrals
+function SubcellLimiterIDP(equations::AbstractEquations, basis;
+ positivity_variables_cons = [],
+ positivity_correction_factor = 0.1)
+ positivity = (length(positivity_variables_cons) > 0)
+ number_bounds = length(positivity_variables_cons)
+
+ cache = create_cache(SubcellLimiterIDP, equations, basis, number_bounds)
+
+ SubcellLimiterIDP{typeof(positivity_correction_factor), typeof(cache)}(positivity,
+ positivity_variables_cons,
+ positivity_correction_factor,
+ cache)
+end
+
+function Base.show(io::IO, limiter::SubcellLimiterIDP)
+ @nospecialize limiter # reduce precompilation time
+ @unpack positivity = limiter
+
+ print(io, "SubcellLimiterIDP(")
+ if !(positivity)
+ print(io, "No limiter selected => pure DG method")
+ else
+ print(io, "limiter=(")
+ positivity && print(io, "positivity")
+ print(io, "), ")
+ end
+ print(io, ")")
+end
+
+function Base.show(io::IO, ::MIME"text/plain", limiter::SubcellLimiterIDP)
+ @nospecialize limiter # reduce precompilation time
+ @unpack positivity = limiter
+
+ if get(io, :compact, false)
+ show(io, limiter)
+ else
+ if !(positivity)
+ setup = ["limiter" => "No limiter selected => pure DG method"]
+ else
+ setup = ["limiter" => ""]
+ if positivity
+ string = "positivity with conservative variables $(limiter.positivity_variables_cons)"
+ setup = [setup..., "" => string]
+ setup = [
+ setup...,
+ "" => " positivity correction factor = $(limiter.positivity_correction_factor)",
+ ]
+ end
+ end
+ summary_box(io, "SubcellLimiterIDP", setup)
+ end
+end
+end # @muladd
diff --git a/src/solvers/dgsem_tree/subcell_limiters_2d.jl b/src/solvers/dgsem_tree/subcell_limiters_2d.jl
new file mode 100644
index 00000000000..09ab84ed11a
--- /dev/null
+++ b/src/solvers/dgsem_tree/subcell_limiters_2d.jl
@@ -0,0 +1,114 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+# this method is used when the limiter is constructed as for shock-capturing volume integrals
+function create_cache(indicator::Type{SubcellLimiterIDP},
+ equations::AbstractEquations{2},
+ basis::LobattoLegendreBasis, number_bounds)
+ subcell_limiter_coefficients = Trixi.ContainerSubcellLimiterIDP2D{real(basis)
+ }(0,
+ nnodes(basis),
+ number_bounds)
+
+ cache = (; subcell_limiter_coefficients)
+
+ return cache
+end
+
+function (limiter::SubcellLimiterIDP)(u::AbstractArray{<:Any, 4}, semi, dg::DGSEM, t,
+ dt;
+ kwargs...)
+ @unpack alpha = limiter.cache.subcell_limiter_coefficients
+ alpha .= zero(eltype(alpha))
+
+ if limiter.positivity
+ @trixi_timeit timer() "positivity" idp_positivity!(alpha, limiter, u, dt,
+ semi)
+ end
+
+ # Calculate alpha1 and alpha2
+ @unpack alpha1, alpha2 = limiter.cache.subcell_limiter_coefficients
+ @threaded for element in eachelement(dg, semi.cache)
+ for j in eachnode(dg), i in 2:nnodes(dg)
+ alpha1[i, j, element] = max(alpha[i - 1, j, element], alpha[i, j, element])
+ end
+ for j in 2:nnodes(dg), i in eachnode(dg)
+ alpha2[i, j, element] = max(alpha[i, j - 1, element], alpha[i, j, element])
+ end
+ alpha1[1, :, element] .= zero(eltype(alpha1))
+ alpha1[nnodes(dg) + 1, :, element] .= zero(eltype(alpha1))
+ alpha2[:, 1, element] .= zero(eltype(alpha2))
+ alpha2[:, nnodes(dg) + 1, element] .= zero(eltype(alpha2))
+ end
+
+ return nothing
+end
+
+@inline function idp_positivity!(alpha, limiter, u, dt, semi)
+ # Conservative variables
+ for (index, variable) in enumerate(limiter.positivity_variables_cons)
+ idp_positivity!(alpha, limiter, u, dt, semi, variable, index)
+ end
+
+ return nothing
+end
+
+@inline function idp_positivity!(alpha, limiter, u, dt, semi, variable, index)
+ mesh, equations, dg, cache = mesh_equations_solver_cache(semi)
+ @unpack antidiffusive_flux1, antidiffusive_flux2 = cache.antidiffusive_fluxes
+ @unpack inverse_weights = dg.basis
+ @unpack positivity_correction_factor = limiter
+
+ @unpack variable_bounds = limiter.cache.subcell_limiter_coefficients
+
+ var_min = variable_bounds[index]
+
+ @threaded for element in eachelement(dg, semi.cache)
+ inverse_jacobian = cache.elements.inverse_jacobian[element]
+ for j in eachnode(dg), i in eachnode(dg)
+ var = u[variable, i, j, element]
+ if var < 0
+ error("Safe $variable is not safe. element=$element, node: $i $j, value=$var")
+ end
+
+ # Compute bound
+ var_min[i, j, element] = positivity_correction_factor * var
+
+ # Real one-sided Zalesak-type limiter
+ # * Zalesak (1979). "Fully multidimensional flux-corrected transport algorithms for fluids"
+ # * Kuzmin et al. (2010). "Failsafe flux limiting and constrained data projections for equations of gas dynamics"
+ # Note: The Zalesak limiter has to be computed, even if the state is valid, because the correction is
+ # for each interface, not each node
+ Qm = min(0, (var_min[i, j, element] - var) / dt)
+
+ # Calculate Pm
+ # Note: Boundaries of antidiffusive_flux1/2 are constant 0, so they make no difference here.
+ val_flux1_local = inverse_weights[i] *
+ antidiffusive_flux1[variable, i, j, element]
+ val_flux1_local_ip1 = -inverse_weights[i] *
+ antidiffusive_flux1[variable, i + 1, j, element]
+ val_flux2_local = inverse_weights[j] *
+ antidiffusive_flux2[variable, i, j, element]
+ val_flux2_local_jp1 = -inverse_weights[j] *
+ antidiffusive_flux2[variable, i, j + 1, element]
+
+ Pm = min(0, val_flux1_local) + min(0, val_flux1_local_ip1) +
+ min(0, val_flux2_local) + min(0, val_flux2_local_jp1)
+ Pm = inverse_jacobian * Pm
+
+ # Compute blending coefficient avoiding division by zero
+ # (as in paper of [Guermond, Nazarov, Popov, Thomas] (4.8))
+ Qm = abs(Qm) / (abs(Pm) + eps(typeof(Qm)) * 100)
+
+ # Calculate alpha
+ alpha[i, j, element] = max(alpha[i, j, element], 1 - Qm)
+ end
+ end
+
+ return nothing
+end
+end # @muladd
diff --git a/src/time_integration/methods_SSP.jl b/src/time_integration/methods_SSP.jl
new file mode 100644
index 00000000000..8ecad69748b
--- /dev/null
+++ b/src/time_integration/methods_SSP.jl
@@ -0,0 +1,241 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+# Abstract base type for time integration schemes of explicit strong stability-preserving (SSP)
+# Runge-Kutta (RK) methods. They are high-order time discretizations that guarantee the TVD property.
+abstract type SimpleAlgorithmSSP end
+
+"""
+ SimpleSSPRK33(; stage_callbacks=())
+
+The third-order SSP Runge-Kutta method of Shu and Osher.
+
+## References
+
+- Shu, Osher (1988)
+ "Efficient Implementation of Essentially Non-oscillatory Shock-Capturing Schemes" (Eq. 2.18)
+ [DOI: 10.1016/0021-9991(88)90177-5](https://doi.org/10.1016/0021-9991(88)90177-5)
+
+!!! warning "Experimental implementation"
+ This is an experimental feature and may change in future releases.
+"""
+struct SimpleSSPRK33{StageCallbacks} <: SimpleAlgorithmSSP
+ a::SVector{3, Float64}
+ b::SVector{3, Float64}
+ c::SVector{3, Float64}
+ stage_callbacks::StageCallbacks
+
+ function SimpleSSPRK33(; stage_callbacks = ())
+ a = SVector(0.0, 3 / 4, 1 / 3)
+ b = SVector(1.0, 1 / 4, 2 / 3)
+ c = SVector(0.0, 1.0, 1 / 2)
+
+ # Butcher tableau
+ # c | a
+ # 0 |
+ # 1 | 1
+ # 1/2 | 1/4 1/4
+ # --------------------
+ # b | 1/6 1/6 2/3
+
+ new{typeof(stage_callbacks)}(a, b, c, stage_callbacks)
+ end
+end
+
+# This struct is needed to fake https://github.com/SciML/OrdinaryDiffEq.jl/blob/0c2048a502101647ac35faabd80da8a5645beac7/src/integrators/type.jl#L1
+mutable struct SimpleIntegratorSSPOptions{Callback}
+ callback::Callback # callbacks; used in Trixi
+ adaptive::Bool # whether the algorithm is adaptive; ignored
+ dtmax::Float64 # ignored
+ maxiters::Int # maximal number of time steps
+ tstops::Vector{Float64} # tstops from https://diffeq.sciml.ai/v6.8/basics/common_solver_opts/#Output-Control-1; ignored
+end
+
+function SimpleIntegratorSSPOptions(callback, tspan; maxiters = typemax(Int), kwargs...)
+ SimpleIntegratorSSPOptions{typeof(callback)}(callback, false, Inf, maxiters,
+ [last(tspan)])
+end
+
+# This struct is needed to fake https://github.com/SciML/OrdinaryDiffEq.jl/blob/0c2048a502101647ac35faabd80da8a5645beac7/src/integrators/type.jl#L77
+# This implements the interface components described at
+# https://diffeq.sciml.ai/v6.8/basics/integrator/#Handing-Integrators-1
+# which are used in Trixi.
+mutable struct SimpleIntegratorSSP{RealT <: Real, uType, Params, Sol, F, Alg,
+ SimpleIntegratorSSPOptions}
+ u::uType
+ du::uType
+ r0::uType
+ t::RealT
+ dt::RealT # current time step
+ dtcache::RealT # ignored
+ iter::Int # current number of time steps (iteration)
+ p::Params # will be the semidiscretization from Trixi
+ sol::Sol # faked
+ f::F
+ alg::Alg
+ opts::SimpleIntegratorSSPOptions
+ finalstep::Bool # added for convenience
+end
+
+# Forward integrator.stats.naccept to integrator.iter (see GitHub PR#771)
+function Base.getproperty(integrator::SimpleIntegratorSSP, field::Symbol)
+ if field === :stats
+ return (naccept = getfield(integrator, :iter),)
+ end
+ # general fallback
+ return getfield(integrator, field)
+end
+
+"""
+ solve(ode, alg; dt, callbacks, kwargs...)
+
+The following structures and methods provide the infrastructure for SSP Runge-Kutta methods
+of type `SimpleAlgorithmSSP`.
+
+!!! warning "Experimental implementation"
+ This is an experimental feature and may change in future releases.
+"""
+function solve(ode::ODEProblem, alg = SimpleSSPRK33()::SimpleAlgorithmSSP;
+ dt, callback = nothing, kwargs...)
+ u = copy(ode.u0)
+ du = similar(u)
+ r0 = similar(u)
+ t = first(ode.tspan)
+ iter = 0
+ integrator = SimpleIntegratorSSP(u, du, r0, t, dt, zero(dt), iter, ode.p,
+ (prob = ode,), ode.f, alg,
+ SimpleIntegratorSSPOptions(callback, ode.tspan;
+ kwargs...), false)
+
+ # resize container
+ resize!(integrator.p, nelements(integrator.p.solver, integrator.p.cache))
+
+ # initialize callbacks
+ if callback isa CallbackSet
+ for cb in callback.continuous_callbacks
+ error("unsupported")
+ end
+ for cb in callback.discrete_callbacks
+ cb.initialize(cb, integrator.u, integrator.t, integrator)
+ end
+ elseif !isnothing(callback)
+ error("unsupported")
+ end
+
+ for stage_callback in alg.stage_callbacks
+ init_callback(stage_callback, integrator.p)
+ end
+
+ solve!(integrator)
+end
+
+function solve!(integrator::SimpleIntegratorSSP)
+ @unpack prob = integrator.sol
+ @unpack alg = integrator
+ t_end = last(prob.tspan)
+ callbacks = integrator.opts.callback
+
+ integrator.finalstep = false
+ while !integrator.finalstep
+ if isnan(integrator.dt)
+ error("time step size `dt` is NaN")
+ end
+
+ # if the next iteration would push the simulation beyond the end time, set dt accordingly
+ if integrator.t + integrator.dt > t_end ||
+ isapprox(integrator.t + integrator.dt, t_end)
+ integrator.dt = t_end - integrator.t
+ terminate!(integrator)
+ end
+
+ @. integrator.r0 = integrator.u
+ for stage in eachindex(alg.c)
+ t_stage = integrator.t + integrator.dt * alg.c[stage]
+ # compute du
+ integrator.f(integrator.du, integrator.u, integrator.p, t_stage)
+
+ # perform forward Euler step
+ @. integrator.u = integrator.u + integrator.dt * integrator.du
+
+ for stage_callback in alg.stage_callbacks
+ stage_callback(integrator.u, integrator, stage)
+ end
+
+ # perform convex combination
+ @. integrator.u = alg.a[stage] * integrator.r0 + alg.b[stage] * integrator.u
+ end
+
+ integrator.iter += 1
+ integrator.t += integrator.dt
+
+ # handle callbacks
+ if callbacks isa CallbackSet
+ for cb in callbacks.discrete_callbacks
+ if cb.condition(integrator.u, integrator.t, integrator)
+ cb.affect!(integrator)
+ end
+ end
+ end
+
+ # respect maximum number of iterations
+ if integrator.iter >= integrator.opts.maxiters && !integrator.finalstep
+ @warn "Interrupted. Larger maxiters is needed."
+ terminate!(integrator)
+ end
+ end
+
+ for stage_callback in alg.stage_callbacks
+ finalize_callback(stage_callback, integrator.p)
+ end
+
+ return TimeIntegratorSolution((first(prob.tspan), integrator.t),
+ (prob.u0, integrator.u), prob)
+end
+
+# get a cache where the RHS can be stored
+get_du(integrator::SimpleIntegratorSSP) = integrator.du
+get_tmp_cache(integrator::SimpleIntegratorSSP) = (integrator.r0,)
+
+# some algorithms from DiffEq like FSAL-ones need to be informed when a callback has modified u
+u_modified!(integrator::SimpleIntegratorSSP, ::Bool) = false
+
+# used by adaptive timestepping algorithms in DiffEq
+function set_proposed_dt!(integrator::SimpleIntegratorSSP, dt)
+ integrator.dt = dt
+end
+
+# stop the time integration
+function terminate!(integrator::SimpleIntegratorSSP)
+ integrator.finalstep = true
+ empty!(integrator.opts.tstops)
+end
+
+# used for AMR
+function Base.resize!(integrator::SimpleIntegratorSSP, new_size)
+ resize!(integrator.u, new_size)
+ resize!(integrator.du, new_size)
+ resize!(integrator.r0, new_size)
+
+ # Resize container
+ resize!(integrator.p, new_size)
+end
+
+function Base.resize!(semi::AbstractSemidiscretization, new_size)
+ resize!(semi, semi.solver.volume_integral, new_size)
+end
+
+Base.resize!(semi, volume_integral::AbstractVolumeIntegral, new_size) = nothing
+
+function Base.resize!(semi, volume_integral::VolumeIntegralSubcellLimiting, new_size)
+ # Resize container antidiffusive_fluxes
+ resize!(semi.cache.antidiffusive_fluxes, new_size)
+
+ # Resize container subcell_limiter_coefficients
+ @unpack limiter = volume_integral
+ resize!(limiter.cache.subcell_limiter_coefficients, new_size)
+end
+end # @muladd
diff --git a/src/time_integration/time_integration.jl b/src/time_integration/time_integration.jl
index 539e00ff700..c1e53527121 100644
--- a/src/time_integration/time_integration.jl
+++ b/src/time_integration/time_integration.jl
@@ -15,4 +15,5 @@ end
include("methods_2N.jl")
include("methods_3Sstar.jl")
+include("methods_SSP.jl")
end # @muladd
diff --git a/test/test_tree_2d_euler.jl b/test/test_tree_2d_euler.jl
index 6de380288db..e1e3ad32e7d 100644
--- a/test/test_tree_2d_euler.jl
+++ b/test/test_tree_2d_euler.jl
@@ -63,6 +63,12 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_dgsem")
linf = [0.18527440131928286, 0.2404798030563736, 0.23269573860381076, 0.6874012187446894])
end
+ @trixi_testset "elixir_euler_shockcapturing_subcell.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_shockcapturing_subcell.jl"),
+ l2 = [0.08508147906199143, 0.04510299017724501, 0.045103019801950375, 0.6930704343869766],
+ linf = [0.31123546471463326, 0.5616274869594462, 0.5619692712224448, 2.88670199345138])
+ end
+
@trixi_testset "elixir_euler_blast_wave.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_blast_wave.jl"),
l2 = [0.14170569763947993, 0.11647068900798814, 0.11647072556898294, 0.3391989213659599],
diff --git a/test/test_tree_2d_eulermulti.jl b/test/test_tree_2d_eulermulti.jl
index 800dc31f84f..606afca1034 100644
--- a/test/test_tree_2d_eulermulti.jl
+++ b/test/test_tree_2d_eulermulti.jl
@@ -19,6 +19,14 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_dgsem")
tspan = (0.0, 0.001))
end
+ @trixi_testset "elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_shock_bubble_shockcapturing_subcell_positivity.jl"),
+ l2 = [81.52845664909304, 2.5455678559421346, 63229.190712645846, 0.19929478404550321, 0.011068604228443425],
+ linf = [249.21708417382013, 40.33299887640794, 174205.0118831558, 0.6881458768113586, 0.11274401158173972],
+ initial_refinement_level = 3,
+ tspan = (0.0, 0.001))
+ end
+
@trixi_testset "elixir_eulermulti_ec.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_eulermulti_ec.jl"),
l2 = [0.050182236154087095, 0.050189894464434635, 0.2258715597305131, 0.06175171559771687],
diff --git a/test/test_unit.jl b/test/test_unit.jl
index e70a9be6a4a..5c5291c2430 100644
--- a/test/test_unit.jl
+++ b/test/test_unit.jl
@@ -402,6 +402,9 @@ isdir(outdir) && rm(outdir, recursive=true)
indicator_hg = IndicatorHennemannGassner(1.0, 0.0, true, "variable", "cache")
@test_nowarn show(stdout, indicator_hg)
+ limiter_idp = SubcellLimiterIDP(true, [1], 0.1, "cache")
+ @test_nowarn show(stdout, limiter_idp)
+
# TODO: TrixiShallowWater: move unit test
indicator_hg_swe = IndicatorHennemannGassnerShallowWater(1.0, 0.0, true, "variable", "cache")
@test_nowarn show(stdout, indicator_hg_swe)
@@ -637,7 +640,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
end
@timed_testset "Consistency check for HLL flux (naive): SWE" begin
@@ -674,7 +677,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(0.0, 1.0),
SVector(0.5, -0.5),
SVector(-1.2, 0.3)]
- orientations = [1, 2]
+ orientations = [1, 2]
u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
@@ -704,7 +707,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for u in u_values, normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
end
@timed_testset "Consistency check for HLL flux with Davis wave speed estimates: CEE" begin
@@ -718,7 +721,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for orientation in orientations
@test flux_hll(u, u, orientation, equations) ≈ flux(u, orientation, equations)
end
-
+
equations = CompressibleEulerEquations2D(1.4)
u = SVector(1.1, -0.5, 2.34, 5.5)
@@ -734,7 +737,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
equations = CompressibleEulerEquations3D(1.4)
u = SVector(1.1, -0.5, 2.34, 2.4, 5.5)
@@ -752,7 +755,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
end
@timed_testset "Consistency check for HLL flux with Davis wave speed estimates: LEE" begin
@@ -815,7 +818,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(0.0, 1.0),
SVector(0.5, -0.5),
SVector(-1.2, 0.3)]
- orientations = [1, 2]
+ orientations = [1, 2]
u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
@@ -845,7 +848,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for u in u_values, normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
end
@timed_testset "Consistency check for HLLE flux: CEE" begin
@@ -873,7 +876,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
equations = CompressibleEulerEquations3D(1.4)
u = SVector(1.1, -0.5, 2.34, 2.4, 5.5)
@@ -891,7 +894,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
end
@timed_testset "Consistency check for HLLE flux: SWE" begin
@@ -907,7 +910,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(0.0, 1.0),
SVector(0.5, -0.5),
SVector(-1.2, 0.3)]
- orientations = [1, 2]
+ orientations = [1, 2]
u = SVector(1, 0.5, 0.5, 0.0)
@@ -937,7 +940,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(0.0, 1.0),
SVector(0.5, -0.5),
SVector(-1.2, 0.3)]
- orientations = [1, 2]
+ orientations = [1, 2]
u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
@@ -956,7 +959,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(0.0, 0.0, 1.0),
SVector(0.5, -0.5, 0.2),
SVector(-1.2, 0.3, 1.4)]
- orientations = [1, 2, 3]
+ orientations = [1, 2, 3]
u_values = [SVector(1.0, 0.4, -0.5, 0.1, 1.0, 0.1, -0.2, 0.1, 0.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0, -0.1, 0.1, 0.2, 0.2),]
@@ -967,7 +970,7 @@ isdir(outdir) && rm(outdir, recursive=true)
for u in u_values, normal_direction in normal_directions
@test flux_hll(u, u, normal_direction, equations) ≈ flux(u, normal_direction, equations)
- end
+ end
end
@timed_testset "Consistency check for Godunov flux" begin
@@ -1137,7 +1140,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(-1.2, 0.3)]
u_values = [SVector(1.0, 0.5, -0.7, 1.0),
SVector(1.5, -0.2, 0.1, 5.0),]
- fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber,
+ fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber,
flux_hll, FluxHLL(min_max_speed_davis)]
for f_std in fluxes
@@ -1157,7 +1160,7 @@ isdir(outdir) && rm(outdir, recursive=true)
SVector(-1.2, 0.3, 1.4)]
u_values = [SVector(1.0, 0.5, -0.7, 0.1, 1.0),
SVector(1.5, -0.2, 0.1, 0.2, 5.0),]
- fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber, FluxLMARS(340),
+ fluxes = [flux_central, flux_ranocha, flux_shima_etal, flux_kennedy_gruber, FluxLMARS(340),
flux_hll, FluxHLL(min_max_speed_davis)]
for f_std in fluxes
@@ -1173,11 +1176,11 @@ isdir(outdir) && rm(outdir, recursive=true)
normal_directions = [SVector(1.0, 0.0),
SVector(0.0, 1.0),
SVector(0.5, -0.5),
- SVector(-1.2, 0.3)]
+ SVector(-1.2, 0.3)]
u = SVector(1, 0.5, 0.5, 0.0)
- fluxes = [flux_central, flux_fjordholm_etal, flux_wintermeyer_etal,
+ fluxes = [flux_central, flux_fjordholm_etal, flux_wintermeyer_etal,
flux_hll, FluxHLL(min_max_speed_davis), FluxHLL(min_max_speed_einfeldt)]
end
From 925c0474938a2b6e095a795a4c155b40965625e0 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 22 Aug 2023 15:58:08 +0200
Subject: [PATCH 073/263] set version to v0.5.39
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index dd937ed213b..6a11655d345 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.39-pre"
+version = "0.5.39"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From add3a7f9e9c4b7445ee669e43afd8b84350e04d4 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 22 Aug 2023 15:58:20 +0200
Subject: [PATCH 074/263] set development version to v0.5.40-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 6a11655d345..4374eaa3b0a 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.39"
+version = "0.5.40-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From bfefef15d3ccd71f43a5518560d0cd96b599881b Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Tue, 22 Aug 2023 17:21:45 +0200
Subject: [PATCH 075/263] Update visualization.md (#1612)
Compare
[examples/tree\_2d\_dgsem/elixir\_advection\_amr\_visualization.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl):
to (existing)
[examples/tree_2d_dgsem/elixir\_advection\_amr\_visualization.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl):
---
docs/src/visualization.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/src/visualization.md b/docs/src/visualization.md
index 8f72bb4b1c6..4e4b780004d 100644
--- a/docs/src/visualization.md
+++ b/docs/src/visualization.md
@@ -375,7 +375,7 @@ During the simulation, the visualization callback creates and displays
visualizations of the current solution in regular intervals. This can be useful
to, e.g., monitor the validity of a long-running simulation or for illustrative
purposes. An example for how to create a `VisualizationCallback` can be found in
-[examples/tree_2d_dgsem/elixir\_advection\_amr\_visualization.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl):
+[examples/tree\_2d\_dgsem/elixir\_advection\_amr\_visualization.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl):
```julia
[...]
From e22b11e5859b5168e24dabb4cb34a6a35fce3e66 Mon Sep 17 00:00:00 2001
From: Michael Schlottke-Lakemper
Date: Tue, 22 Aug 2023 19:30:35 +0200
Subject: [PATCH 076/263] Add review checklist to new PRs (#1609)
Co-authored-by: Hendrik Ranocha
---
.github/review-checklist.md | 38 +++++++++++++++++++++++++++
.github/workflows/ReviewChecklist.yml | 20 ++++++++++++++
2 files changed, 58 insertions(+)
create mode 100644 .github/review-checklist.md
create mode 100644 .github/workflows/ReviewChecklist.yml
diff --git a/.github/review-checklist.md b/.github/review-checklist.md
new file mode 100644
index 00000000000..2d8a24f1971
--- /dev/null
+++ b/.github/review-checklist.md
@@ -0,0 +1,38 @@
+### Review checklist
+
+This checklist is meant to assist creators of PRs (to let them know what reviewers will typically look for) and reviewers (to guide them in a structured review process). Items do not need to be checked explicitly for a PR to be eligible for merging.
+
+#### Purpose and scope
+- [ ] The PR has a single goal that is clear from the PR title and/or description.
+- [ ] All code changes represent a single set of modifications that logically belong together.
+- [ ] No more than 500 lines of code are changed or there is no obvious way to split the PR into multiple PRs.
+
+#### Code quality
+- [ ] The code can be understood easily.
+- [ ] Newly introduced names for variables etc. are self-descriptive and consistent with existing naming conventions.
+- [ ] There are no redundancies that can be removed by simple modularization/refactoring.
+- [ ] There are no leftover debug statements or commented code sections.
+- [ ] The code adheres to our [conventions](https://trixi-framework.github.io/Trixi.jl/stable/conventions/) and [style guide](https://trixi-framework.github.io/Trixi.jl/stable/styleguide/), and to the [Julia guidelines](https://docs.julialang.org/en/v1/manual/style-guide/).
+
+#### Documentation
+- [ ] New functions and types are documented with a docstring or top-level comment.
+- [ ] Relevant publications are referenced in docstrings (see [example](https://github.com/trixi-framework/Trixi.jl/blob/7f83a1a938eecd9b841efe215a6e482e67cfdcc1/src/equations/compressible_euler_2d.jl#L601-L615) for formatting).
+- [ ] Inline comments are used to document longer or unusual code sections.
+- [ ] Comments describe intent ("why?") and not just functionality ("what?").
+- [ ] If the PR introduces a significant change or new feature, it is documented in `NEWS.md`.
+
+#### Testing
+- [ ] The PR passes all tests.
+- [ ] New or modified lines of code are covered by tests.
+- [ ] New or modified tests run in less then 10 seconds.
+
+#### Performance
+- [ ] There are no type instabilities or memory allocations in performance-critical parts.
+- [ ] If the PR intent is to improve performance, before/after [time measurements](https://trixi-framework.github.io/Trixi.jl/stable/performance/#Manual-benchmarking) are posted in the PR.
+
+#### Verification
+- [ ] The correctness of the code was verified using appropriate tests.
+- [ ] If new equations/methods are added, a convergence test has been run and the results
+ are posted in the PR.
+
+*Created with :heart: by the Trixi.jl community.*
\ No newline at end of file
diff --git a/.github/workflows/ReviewChecklist.yml b/.github/workflows/ReviewChecklist.yml
new file mode 100644
index 00000000000..959a04752d7
--- /dev/null
+++ b/.github/workflows/ReviewChecklist.yml
@@ -0,0 +1,20 @@
+name: Add review checklist
+
+on:
+ pull_request_target:
+ types: [opened]
+
+permissions:
+ pull-requests: write
+
+jobs:
+ add-review-checklist:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ - name: Add review checklist
+ uses: trixi-framework/add-pr-review-checklist@v1
+ with:
+ file: '.github/review-checklist.md'
+ no-checklist-keyword: '[no checklist]'
\ No newline at end of file
From e82ebb557a280abb87e7b6f3db9d90bcac715321 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Wed, 30 Aug 2023 20:16:45 +0200
Subject: [PATCH 077/263] unify how we refer to links to elixirs in docs
(#1620)
---
.../src/files/differentiable_programming.jl | 2 +-
docs/literate/src/files/index.jl | 2 +-
docs/src/callbacks.md | 22 +++++++++----------
docs/src/meshes/dgmulti_mesh.md | 14 +++++++-----
docs/src/overview.md | 4 ++--
docs/src/restart.md | 10 ++++-----
docs/src/visualization.md | 2 +-
7 files changed, 30 insertions(+), 26 deletions(-)
diff --git a/docs/literate/src/files/differentiable_programming.jl b/docs/literate/src/files/differentiable_programming.jl
index ecc09d05dcf..5c5a7cd7440 100644
--- a/docs/literate/src/files/differentiable_programming.jl
+++ b/docs/literate/src/files/differentiable_programming.jl
@@ -128,7 +128,7 @@ condition_number = cond(V)
# you can compute the gradient of an entropy-dissipative semidiscretization with respect to the
# ideal gas constant of the compressible Euler equations as described in the following. This example
# is also available as the elixir
-# [examples/special\_elixirs/elixir\_euler\_ad.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/special_elixirs/elixir_euler_ad.jl)
+# [`examples/special_elixirs/elixir_euler_ad.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/special_elixirs/elixir_euler_ad.jl)
# First, we create a semidiscretization of the compressible Euler equations.
diff --git a/docs/literate/src/files/index.jl b/docs/literate/src/files/index.jl
index 5b669881502..0c8de66bf42 100644
--- a/docs/literate/src/files/index.jl
+++ b/docs/literate/src/files/index.jl
@@ -116,7 +116,7 @@
# ## Examples in Trixi.jl
# Trixi.jl already contains several more coding examples, the so-called `elixirs`. You can find them
-# in the folder [`examples`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/).
+# in the folder [`examples/`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/).
# They are structured by the underlying mesh type and the respective number of spatial dimensions.
# The name of an elixir is composed of the underlying system of conservation equations (for instance
# `advection` or `euler`) and other special characteristics like the initial condition
diff --git a/docs/src/callbacks.md b/docs/src/callbacks.md
index a85f8e8191b..1d3e5e34b51 100644
--- a/docs/src/callbacks.md
+++ b/docs/src/callbacks.md
@@ -15,7 +15,7 @@ control, adaptive mesh refinement, I/O, and more.
### CFL-based time step control
Time step control can be performed with a [`StepsizeCallback`](@ref). An example making use
-of this can be found at [examples/tree_2d_dgsem/elixir\_advection\_basic.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_basic.jl)
+of this can be found at [`examples/tree_2d_dgsem/elixir_advection_basic.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_basic.jl)
### Adaptive mesh refinement
Trixi.jl uses a hierarchical Cartesian mesh which can be locally refined in a solution-adaptive way.
@@ -24,12 +24,12 @@ passing an [`AMRCallback`](@ref) to the ODE solver. The `AMRCallback` requires a
[`ControllerThreeLevel`](@ref) or [`ControllerThreeLevelCombined`](@ref) to tell the AMR
algorithm which cells to refine/coarsen.
-An example elixir using AMR can be found at [examples/tree_2d_dgsem/elixir\_advection\_amr.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr.jl).
+An example elixir using AMR can be found at [`examples/tree_2d_dgsem/elixir_advection_amr.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr.jl).
### Analyzing the numerical solution
The [`AnalysisCallback`](@ref) can be used to analyze the numerical solution, e.g. calculate
errors or user-specified integrals, and print the results to the screen. The results can also be
-saved in a file. An example can be found at [examples/tree_2d_dgsem/elixir\_euler\_vortex.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_vortex.jl).
+saved in a file. An example can be found at [`examples/tree_2d_dgsem/elixir_euler_vortex.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_vortex.jl).
In [Performance metrics of the `AnalysisCallback`](@ref) you can find a detailed
description of the different performance metrics the `AnalysisCallback` computes.
@@ -38,15 +38,15 @@ description of the different performance metrics the `AnalysisCallback` computes
#### Solution and restart files
To save the solution in regular intervals you can use a [`SaveSolutionCallback`](@ref). It is also
possible to create restart files using the [`SaveRestartCallback`](@ref). An example making use
-of these can be found at [examples/tree_2d_dgsem/elixir\_advection\_extended.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_extended.jl).
+of these can be found at [`examples/tree_2d_dgsem/elixir_advection_extended.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_extended.jl).
An example showing how to restart a simulation from a restart file can be found at
-[examples/tree_2d_dgsem/elixir\_advection\_restart.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_restart.jl).
+[`examples/tree_2d_dgsem/elixir_advection_restart.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_restart.jl).
#### Time series
Sometimes it is useful to record the evaluations of state variables over time at
a given set of points. This can be achieved by the [`TimeSeriesCallback`](@ref), which is used,
e.g., in
-[examples/tree_2d_dgsem/elixir\_acoustics\_gaussian\_source.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_acoustics_gaussian_source.jl).
+[`examples/tree_2d_dgsem/elixir_acoustics_gaussian_source.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_acoustics_gaussian_source.jl).
The `TimeSeriesCallback` constructor expects a semidiscretization and a list of points at
which the solution should be recorded in regular time step intervals. After the
last time step, the entire record is stored in an HDF5 file.
@@ -113,12 +113,12 @@ will yield the following plot:
Some callbacks provided by Trixi.jl implement specific features for certain equations:
* The [`LBMCollisionCallback`](@ref) implements the Lattice-Boltzmann method (LBM) collision
operator and should only be used when solving the Lattice-Boltzmann equations. See e.g.
- [examples/tree_2d_dgsem/elixir\_lbm\_constant.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_lbm_constant.jl)
+ [`examples/tree_2d_dgsem/elixir_lbm_constant.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_lbm_constant.jl)
* The [`SteadyStateCallback`](@ref) terminates the time integration when the residual steady state
falls below a certain threshold. This checks the convergence of the potential ``\phi`` for
- hyperbolic diffusion. See e.g. [examples/tree_2d_dgsem/elixir\_hypdiff\_nonperiodic.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_hypdiff_nonperiodic.jl).
+ hyperbolic diffusion. See e.g. [`examples/tree_2d_dgsem/elixir_hypdiff_nonperiodic.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_hypdiff_nonperiodic.jl).
* The [`GlmSpeedCallback`](@ref) updates the divergence cleaning wave speed `c_h` for the ideal
- GLM-MHD equations. See e.g. [examples/tree_2d_dgsem/elixir\_mhd\_alfven\_wave.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_mhd_alfven_wave.jl).
+ GLM-MHD equations. See e.g. [`examples/tree_2d_dgsem/elixir_mhd_alfven_wave.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_mhd_alfven_wave.jl).
## Usage of step callbacks
Step callbacks are passed to the `solve` method from the ODE solver via the keyword argument
@@ -152,7 +152,7 @@ more callbacks, you need to turn them into a `CallbackSet` first by calling
## Stage callbacks
[`PositivityPreservingLimiterZhangShu`](@ref) is a positivity-preserving limiter, used to enforce
physical constraints. An example elixir using this feature can be found at
-[examples/tree_2d_dgsem/elixir\_euler\_positivity.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_positivity.jl).
+[`examples/tree_2d_dgsem/elixir_euler_positivity.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_positivity.jl).
## Implementing new callbacks
Since Trixi.jl is compatible with [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl),
@@ -162,4 +162,4 @@ Step callbacks are just called [callbacks](https://diffeq.sciml.ai/latest/featur
Stage callbacks are called [`stage_limiter!`](https://diffeq.sciml.ai/latest/solvers/ode_solve/#Explicit-Strong-Stability-Preserving-Runge-Kutta-Methods-for-Hyperbolic-PDEs-(Conservation-Laws)).
An example elixir showing how to implement a new simple stage callback and a new simple step
-callback can be found at [examples/tree_2d_dgsem/elixir\_advection\_callbacks.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_callbacks.jl).
+callback can be found at [`examples/tree_2d_dgsem/elixir_advection_callbacks.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_callbacks.jl).
diff --git a/docs/src/meshes/dgmulti_mesh.md b/docs/src/meshes/dgmulti_mesh.md
index e07ba70a80a..fc086bba146 100644
--- a/docs/src/meshes/dgmulti_mesh.md
+++ b/docs/src/meshes/dgmulti_mesh.md
@@ -81,16 +81,20 @@ type, but will be more efficient at high orders of approximation.
## Trixi.jl elixirs on simplicial and tensor product element meshes
Example elixirs with triangular, quadrilateral, and tetrahedral meshes can be found in
-the `examples/dgmulti_2d` and `examples/dgmulti_3d` folders. Some key elixirs to look at:
+the [`examples/dgmulti_2d/`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/dgmulti_2d/)
+and [`examples/dgmulti_3d/`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/dgmulti_3d/)
+folders. Some key elixirs to look at:
-* `examples/dgmulti_2d/elixir_euler_weakform.jl`: basic weak form DG discretization on a uniform triangular mesh.
+* [`examples/dgmulti_2d/elixir_euler_weakform.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/dgmulti_2d/elixir_euler_weakform.jl):
+ basic weak form DG discretization on a uniform triangular mesh.
Changing `element_type = Quad()` or `approximation_type = SBP()` will switch to a quadrilateral mesh
or an SBP-type discretization. Changing `surface_integral = SurfaceIntegralWeakForm(flux_ec)` and
`volume_integral = VolumeIntegralFluxDifferencing(flux_ec)` for some entropy conservative flux
(e.g., [`flux_chandrashekar`](@ref) or [`flux_ranocha`](@ref)) will switch to an entropy conservative formulation.
-* `examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl`: uses an unstructured mesh generated by
- [Triangulate.jl](https://github.com/JuliaGeometry/Triangulate.jl).
-* `examples/dgmulti_3d/elixir_euler_weakform.jl`: basic weak form DG discretization on a uniform tetrahedral mesh.
+* [`examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/dgmulti_2d/elixir_euler_triangulate_pkg_mesh.jl):
+ uses an unstructured mesh generated by [Triangulate.jl](https://github.com/JuliaGeometry/Triangulate.jl).
+* [`examples/dgmulti_3d/elixir_euler_weakform.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/dgmulti_3d/elixir_euler_weakform.jl):
+ ´basic weak form DG discretization on a uniform tetrahedral mesh.
Changing `element_type = Hex()` will switch to a hexahedral mesh. Changing
`surface_integral = SurfaceIntegralWeakForm(flux_ec)` and
`volume_integral = VolumeIntegralFluxDifferencing(flux_ec)` for some entropy conservative flux
diff --git a/docs/src/overview.md b/docs/src/overview.md
index 519ec2ca424..46bc28b6025 100644
--- a/docs/src/overview.md
+++ b/docs/src/overview.md
@@ -5,7 +5,7 @@ conservation laws. Thus, it is not a monolithic PDE solver that is configured at
via parameter files, as it is often found in classical numerical simulation codes.
Instead, each simulation is configured by pure Julia code. Many examples of such
simulation setups, called *elixirs* in Trixi.jl, are provided in the
-[examples](https://github.com/trixi-framework/Trixi.jl/blob/main/examples)
+[`examples/`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples)
folder.
Trixi.jl uses the method of lines, i.e., the full space-time discretization is separated into two steps;
@@ -77,7 +77,7 @@ Further information can be found in the
## Next steps
We explicitly encourage people interested in Trixi.jl to have a look at the
-[examples](https://github.com/trixi-framework/Trixi.jl/blob/main/examples)
+[`examples/`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples)
bundled with Trixi.jl to get an impression of what is possible and the general
look and feel of Trixi.jl.
Before doing that, it is usually good to get an idea of
diff --git a/docs/src/restart.md b/docs/src/restart.md
index d24d93cb297..767269ff27d 100644
--- a/docs/src/restart.md
+++ b/docs/src/restart.md
@@ -18,7 +18,7 @@ save_restart = SaveRestartCallback(interval=100,
Make this part of your `CallbackSet`.
An example is
-[```examples/examples/structured_2d_dgsem/elixir_advection_extended.jl```](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_extended.jl).
+[`examples/examples/structured_2d_dgsem/elixir_advection_extended.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_extended.jl).
## [Perform the simulation restart](@id restart_perform)
@@ -26,7 +26,7 @@ Since all of the information about the simulation can be obtained from the
last snapshot, the restart can be done with relatively few lines
in an extra elixir file.
However, some might prefer to keep everything in one elixir and
-conditionals like ```if restart``` with a boolean variable ```restart``` that is user defined.
+conditionals like `if restart` with a boolean variable `restart` that is user defined.
First we need to define from which file we want to restart, e.g.
```julia
@@ -50,7 +50,7 @@ time the one form the snapshot:
tspan = (load_time(restart_filename), 2.0)
```
-We now also take the last ```dt```, so that our solver does not need to first find
+We now also take the last `dt`, so that our solver does not need to first find
one to fulfill the CFL condition:
```julia
dt = load_dt(restart_filename)
@@ -63,7 +63,7 @@ ode = semidiscretize(semi, tspan, restart_filename)
You should now define a [`SaveSolutionCallback`](@ref) similar to the
[original simulation](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_extended.jl),
-but with ```save_initial_solution=false```, otherwise our initial snapshot will be overwritten.
+but with `save_initial_solution=false`, otherwise our initial snapshot will be overwritten.
If you are using one file for the original simulation and the restart
you can reuse your [`SaveSolutionCallback`](@ref), but need to set
```julia
@@ -86,4 +86,4 @@ Now we can compute the solution:
sol = solve!(integrator)
```
-An example is in `[``examples/structured_2d_dgsem/elixir_advection_restart.jl```](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_restart.jl).
+An example is in [`examples/structured_2d_dgsem/elixir_advection_restart.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/structured_2d_dgsem/elixir_advection_restart.jl).
diff --git a/docs/src/visualization.md b/docs/src/visualization.md
index 4e4b780004d..36a7e8f5ac8 100644
--- a/docs/src/visualization.md
+++ b/docs/src/visualization.md
@@ -375,7 +375,7 @@ During the simulation, the visualization callback creates and displays
visualizations of the current solution in regular intervals. This can be useful
to, e.g., monitor the validity of a long-running simulation or for illustrative
purposes. An example for how to create a `VisualizationCallback` can be found in
-[examples/tree\_2d\_dgsem/elixir\_advection\_amr\_visualization.jl](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl):
+[`examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl):
```julia
[...]
From af70d89eb35b30561833a20a6d6d3bb6e9567264 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 1 Sep 2023 15:35:30 +0200
Subject: [PATCH 078/263] update affiliation of HR (#1621)
---
.zenodo.json | 2 +-
AUTHORS.md | 2 +-
README.md | 2 +-
docs/src/index.md | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.zenodo.json b/.zenodo.json
index 95879af1e90..905c0170ab9 100644
--- a/.zenodo.json
+++ b/.zenodo.json
@@ -15,7 +15,7 @@
"orcid": "0000-0002-1752-1158"
},
{
- "affiliation": "Applied Mathematics, University of Hamburg, Germany",
+ "affiliation": "Numerical Mathematics, Johannes Gutenberg University Mainz, Germany",
"name": "Ranocha, Hendrik",
"orcid": "0000-0002-3456-2277"
},
diff --git a/AUTHORS.md b/AUTHORS.md
index 74bfaa9c852..f1debf8ba76 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -12,7 +12,7 @@ provided substantial additions or modifications. Together, these two groups form
* [Gregor Gassner](https://www.mi.uni-koeln.de/NumSim/gregor-gassner),
University of Cologne, Germany
* [Hendrik Ranocha](https://ranocha.de),
- University of Hamburg, Germany
+ Johannes Gutenberg University Mainz, Germany
* [Andrew Winters](https://liu.se/en/employee/andwi94),
Linköping University, Sweden
* [Jesse Chan](https://jlchan.github.io),
diff --git a/README.md b/README.md
index 7eaee8750dd..63540b1f640 100644
--- a/README.md
+++ b/README.md
@@ -247,7 +247,7 @@ Schlottke-Lakemper](https://lakemper.eu)
(RWTH Aachen University/High-Performance Computing Center Stuttgart (HLRS), Germany) and
[Gregor Gassner](https://www.mi.uni-koeln.de/NumSim/gregor-gassner)
(University of Cologne, Germany). Together with [Hendrik Ranocha](https://ranocha.de)
-(University of Hamburg, Germany), [Andrew Winters](https://liu.se/en/employee/andwi94)
+(Johannes Gutenberg University Mainz, Germany), [Andrew Winters](https://liu.se/en/employee/andwi94)
(Linköping University, Sweden), and [Jesse Chan](https://jlchan.github.io) (Rice University, US),
they are the principal developers of Trixi.jl.
The full list of contributors can be found in [AUTHORS.md](AUTHORS.md).
diff --git a/docs/src/index.md b/docs/src/index.md
index 3af785bc681..bb2afd1019f 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -324,7 +324,7 @@ Schlottke-Lakemper](https://lakemper.eu)
(RWTH Aachen University/High-Performance Computing Center Stuttgart (HLRS), Germany) and
[Gregor Gassner](https://www.mi.uni-koeln.de/NumSim/gregor-gassner)
(University of Cologne, Germany). Together with [Hendrik Ranocha](https://ranocha.de)
-(University of Hamburg, Germany) and [Andrew Winters](https://liu.se/en/employee/andwi94)
+(Johannes Gutenberg University Mainz, Germany) and [Andrew Winters](https://liu.se/en/employee/andwi94)
(Linköping University, Sweden), and [Jesse Chan](https://jlchan.github.io) (Rice University, US),
they are the principal developers of Trixi.jl.
The full list of contributors can be found under [Authors](@ref).
From 6403a480825dcebdd10cb90584c5cc877b4b2c5d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 1 Sep 2023 19:13:47 +0200
Subject: [PATCH 079/263] Bump crate-ci/typos from 1.16.5 to 1.16.9 (#1622)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.5 to 1.16.9.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.16.5...v1.16.9)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index 6ebb288ea30..a06121e7ca1 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.16.5
+ uses: crate-ci/typos@v1.16.9
From bd5ba865478a889c48a7675072d921906c27c0c4 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Wed, 6 Sep 2023 09:15:38 +0200
Subject: [PATCH 080/263] Update docs on how to use a system-provided MPI
installation with T8code.jl (#1613)
* update docs on how to use a system-provided MPI installation with T8code.jl
* reduce number of characters per line
* adjust path of shared object files
* fix typo
---
docs/src/parallelization.md | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/docs/src/parallelization.md b/docs/src/parallelization.md
index 08470fd064a..245fdc11852 100644
--- a/docs/src/parallelization.md
+++ b/docs/src/parallelization.md
@@ -53,16 +53,24 @@ a system-provided MPI installation with Trixi.jl can be found in the following s
### [Using a system-provided MPI installation](@id parallel_system_MPI)
-When using Trixi.jl with a system-provided MPI backend the underlying [`p4est`](https://github.com/cburstedde/p4est)
-library needs to be compiled with the same MPI installation. Therefore, you also need to use
-a system-provided `p4est` installation (for notes on how to install `p4est` see e.g.
-[here](https://github.com/cburstedde/p4est/blob/master/README), use the configure option
-`--enable-mpi`). In addition, [P4est.jl](https://github.com/trixi-framework/P4est.jl) needs to
-be configured to use the custom `p4est` installation. Follow the steps described
-[here](https://github.com/trixi-framework/P4est.jl/blob/main/README.md) for the configuration.
+When using Trixi.jl with a system-provided MPI backend the underlying
+[`p4est`](https://github.com/cburstedde/p4est) and [`t8code`](https://github.com/DLR-AMR/t8code)
+libraries need to be compiled with the same MPI installation. Therefore, you also need to
+use system-provided `p4est` and `t8code` installations (for notes on how to install `p4est`
+and `t8code` see e.g. [here](https://github.com/cburstedde/p4est/blob/master/README) and
+[here](https://github.com/DLR-AMR/t8code/wiki/Installation), use the configure option
+`--enable-mpi`). Note that `t8code` already comes with a `p4est` installation, so it suffices
+to install `t8code`. In addition, [P4est.jl](https://github.com/trixi-framework/P4est.jl) and
+[T8code.jl](https://github.com/DLR-AMR/T8code.jl) need to be configured to use the custom
+installations. Follow the steps described
+[here](https://github.com/DLR-AMR/T8code.jl/blob/main/README.md#installation) and
+[here](https://github.com/trixi-framework/P4est.jl/blob/main/README.md#installation) for the
+configuration. The paths that point to `libp4est.so` (and potentially to `libsc.so`) need to be
+the same for P4est.jl and T8code.jl. This could e.g. be `libp4est.so` that usually can be found
+in `lib/` or `local/lib/` in the installation directory of `t8code`.
In total, in your active Julia project you should have a LocalPreferences.toml file with sections
-`[MPIPreferences]` and `[P4est]` as well as an entry `MPIPreferences` in your Project.toml to
-use a custom MPI installation.
+`[MPIPreferences]`, `[T8code]` and `[P4est]` as well as an entry `MPIPreferences` in your
+Project.toml to use a custom MPI installation.
### [Usage](@id parallel_usage)
From 76b4c5fc842e47c4bd9f33c044ae99bd2dfbf789 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 6 Sep 2023 09:16:48 +0200
Subject: [PATCH 081/263] set version to v0.5.40
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 4374eaa3b0a..0d27c0fd6e8 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.40-pre"
+version = "0.5.40"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From f098ea20f545007d741745c160c7d1e1919733c4 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 6 Sep 2023 09:17:13 +0200
Subject: [PATCH 082/263] set development version to v0.5.41-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 0d27c0fd6e8..e14dbcd0c03 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.40"
+version = "0.5.41-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 81d2b70965f361b0bfd9b113ebe4fb360b1437cb Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 6 Sep 2023 15:33:48 +0200
Subject: [PATCH 083/263] workaround for allocations when broadcasting
equations (#1626)
---
src/equations/equations.jl | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/equations/equations.jl b/src/equations/equations.jl
index 90b2cd62191..570a25cece9 100644
--- a/src/equations/equations.jl
+++ b/src/equations/equations.jl
@@ -75,8 +75,14 @@ end
@inline Base.ndims(::AbstractEquations{NDIMS}) where {NDIMS} = NDIMS
-# equations act like scalars in broadcasting
-Base.broadcastable(equations::AbstractEquations) = Ref(equations)
+# Equations act like scalars in broadcasting.
+# Using `Ref(equations)` would be more convenient in some circumstances.
+# However, this does not work with Julia v1.9.3 correctly due to a (performance)
+# bug in Julia, see
+# - https://github.com/trixi-framework/Trixi.jl/pull/1618
+# - https://github.com/JuliaLang/julia/issues/51118
+# Thus, we use the workaround below.
+Base.broadcastable(equations::AbstractEquations) = (equations,)
"""
flux(u, orientation_or_normal, equations)
From 4e6d1638a279130e0f5008ff75acadb8307b0a6d Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Wed, 6 Sep 2023 17:00:08 +0200
Subject: [PATCH 084/263] increase absolute tolerance (#1625)
Co-authored-by: Hendrik Ranocha
---
test/test_tree_1d_shallowwater.jl | 3 ++-
test/test_trixi.jl | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/test/test_tree_1d_shallowwater.jl b/test/test_tree_1d_shallowwater.jl
index cafa17edd4c..1e5aeac1786 100644
--- a/test/test_tree_1d_shallowwater.jl
+++ b/test/test_tree_1d_shallowwater.jl
@@ -102,7 +102,8 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_beach.jl"),
l2 = [0.17979210479598923, 1.2377495706611434, 6.289818963361573e-8],
linf = [0.845938394800688, 3.3740800777086575, 4.4541473087633676e-7],
- tspan = (0.0, 0.05))
+ tspan = (0.0, 0.05),
+ atol = 3e-10) # see https://github.com/trixi-framework/Trixi.jl/issues/1617
end
@trixi_testset "elixir_shallowwater_parabolic_bowl.jl" begin
diff --git a/test/test_trixi.jl b/test/test_trixi.jl
index ddace6b4fbe..f2cd0cab94d 100644
--- a/test/test_trixi.jl
+++ b/test/test_trixi.jl
@@ -5,7 +5,7 @@ import Trixi
# inside an elixir.
"""
@test_trixi_include(elixir; l2=nothing, linf=nothing,
- atol=10*eps(), rtol=0.001,
+ atol=500*eps(), rtol=sqrt(eps()),
parameters...)
Test Trixi by calling `trixi_include(elixir; parameters...)`.
From a7867e7376541d1326f2796661f5ca35bc9fe499 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Wed, 6 Sep 2023 18:38:45 +0200
Subject: [PATCH 085/263] Update docs for parallel HDF5 (#1504)
* update docs for parallel HDF5
* Update docs/src/parallelization.md
Co-authored-by: Hendrik Ranocha
* update docs on parallel HDF5
* bump compat for HDF5
* mention T8code
* reduce number of characters per line
* add information for older HDF5.jl versions
---------
Co-authored-by: Hendrik Ranocha
---
Project.toml | 2 +-
docs/src/parallelization.md | 41 +++++++++++++++++++++++++++----------
2 files changed, 31 insertions(+), 12 deletions(-)
diff --git a/Project.toml b/Project.toml
index e14dbcd0c03..41dde8662ab 100644
--- a/Project.toml
+++ b/Project.toml
@@ -56,7 +56,7 @@ DiffEqCallbacks = "2.25"
EllipsisNotation = "1.0"
FillArrays = "0.13.2, 1"
ForwardDiff = "0.10.18"
-HDF5 = "0.14, 0.15, 0.16"
+HDF5 = "0.14, 0.15, 0.16, 0.17"
IfElse = "0.1"
LinearMaps = "2.7, 3.0"
LoopVectorization = "0.12.118"
diff --git a/docs/src/parallelization.md b/docs/src/parallelization.md
index 245fdc11852..d56777c9af4 100644
--- a/docs/src/parallelization.md
+++ b/docs/src/parallelization.md
@@ -166,17 +166,36 @@ section, specifically at the descriptions of the performance index (PID).
### Using error-based step size control with MPI
-If you use error-based step size control (see also the section on [error-based adaptive step sizes](@ref adaptive_step_sizes))
-together with MPI you need to pass `internalnorm=ode_norm` and you should pass
-`unstable_check=ode_unstable_check` to OrdinaryDiffEq's [`solve`](https://docs.sciml.ai/DiffEqDocs/latest/basics/common_solver_opts/),
+If you use error-based step size control (see also the section on
+[error-based adaptive step sizes](@ref adaptive_step_sizes)) together with MPI you need to pass
+`internalnorm=ode_norm` and you should pass `unstable_check=ode_unstable_check` to
+OrdinaryDiffEq's [`solve`](https://docs.sciml.ai/DiffEqDocs/latest/basics/common_solver_opts/),
which are both included in [`ode_default_options`](@ref).
### Using parallel input and output
-Trixi.jl allows parallel I/O using MPI by leveraging parallel HDF5.jl. To enable this, you first need
-to use a system-provided MPI library, see also [here](@ref parallel_system_MPI) and you need to tell
-[HDF5.jl](https://github.com/JuliaIO/HDF5.jl) to use this library.
-To do so, set the environment variable `JULIA_HDF5_PATH` to the local path
-that contains the `libhdf5.so` shared object file and build HDF5.jl by executing `using Pkg; Pkg.build("HDF5")`.
-For more information see also the [documentation of HDF5.jl](https://juliaio.github.io/HDF5.jl/stable/mpi/).
-
-If you do not perform these steps to use parallel HDF5 or if the HDF5 is not MPI-enabled, Trixi.jl will fall back on a less efficient I/O mechanism. In that case, all disk I/O is performed only on rank zero and data is distributed to/gathered from the other ranks using regular MPI communication.
+Trixi.jl allows parallel I/O using MPI by leveraging parallel HDF5.jl. On most systems, this is
+enabled by default. Additionally, you can also use a local installation of the HDF5 library
+(with MPI support). For this, you first need to use a system-provided MPI library, see also
+[here](@ref parallel_system_MPI) and you need to tell [HDF5.jl](https://github.com/JuliaIO/HDF5.jl)
+to use this library. To do so with HDF5.jl v0.17 and newer, set the preferences `libhdf5` and
+`libhdf5_hl` to the local paths of the libraries `libhdf5` and `libhdf5_hl`, which can be done by
+```julia
+julia> using Preferences, UUIDs
+julia> set_preferences!(
+ UUID("f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"), # UUID of HDF5.jl
+ "libhdf5" => "/path/to/your/libhdf5.so",
+ "libhdf5_hl" => "/path/to/your/libhdf5_hl.so", force = true)
+```
+For more information see also the
+[documentation of HDF5.jl](https://juliaio.github.io/HDF5.jl/stable/mpi/). In total, you should
+have a file called LocalPreferences.toml in the project directory that contains a section
+`[MPIPreferences]`, a section `[HDF5]` with entries `libhdf5` and `libhdf5_hl`, a section `[P4est]`
+with the entry `libp4est` as well as a section `[T8code]` with the entries `libt8`, `libp4est`
+and `libsc`.
+If you use HDF5.jl v0.16 or older, instead of setting the preferences for HDF5.jl, you need to set
+the environment variable `JULIA_HDF5_PATH` to the path, where the HDF5 binaries are located and
+then call `]build HDF5` from Julia.
+
+If HDF5 is not MPI-enabled, Trixi.jl will fall back on a less efficient I/O mechanism. In that
+case, all disk I/O is performed only on rank zero and data is distributed to/gathered from the
+other ranks using regular MPI communication.
From 13260284dcbed67e9c430623cef049e171d19bfd Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Thu, 7 Sep 2023 08:10:57 +0200
Subject: [PATCH 086/263] set version to v0.5.41
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 41dde8662ab..37553fb70f4 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.41-pre"
+version = "0.5.41"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 953f88a78688969b893f34b3cf99693674217381 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Thu, 7 Sep 2023 08:11:09 +0200
Subject: [PATCH 087/263] set development version to v0.5.42-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 37553fb70f4..d37c0548a6a 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.41"
+version = "0.5.42-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 7791faa0ca116c047b41b8c556ec5175c4507a24 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 12 Sep 2023 10:53:52 +0200
Subject: [PATCH 088/263] Some multi-threading improvements (#1630)
* fix multi-threaded parabolic terms on ARM
On ARM, the previous versions resulted in
cfunction: closures are not supported on this platform
With this change, everything seems to work fine locally.
At least test/test_threaded.jl runs fine with two threads.
* reduce alloactions of multi-threaded parabolic terms a bit
Polyester.jl passes arrays as pointer arrays to the closures without requiring allocations.
More complicated structs may still require allocations, so unpacking some arrays before entering a threaded loop can reduce allocations.
* format
---
src/solvers/dgmulti/dg_parabolic.jl | 5 +-
src/solvers/dgsem_tree/dg_1d_parabolic.jl | 34 +++++++-----
src/solvers/dgsem_tree/dg_2d_parabolic.jl | 51 ++++++++++--------
src/solvers/dgsem_tree/dg_3d_parabolic.jl | 65 ++++++++++++-----------
4 files changed, 86 insertions(+), 69 deletions(-)
diff --git a/src/solvers/dgmulti/dg_parabolic.jl b/src/solvers/dgmulti/dg_parabolic.jl
index 72dbe2c4256..7dfe4430244 100644
--- a/src/solvers/dgmulti/dg_parabolic.jl
+++ b/src/solvers/dgmulti/dg_parabolic.jl
@@ -62,9 +62,10 @@ end
function transform_variables!(u_transformed, u, mesh,
equations_parabolic::AbstractEquationsParabolic,
dg::DGMulti, parabolic_scheme, cache, cache_parabolic)
+ transformation = gradient_variable_transformation(equations_parabolic)
+
@threaded for i in eachindex(u)
- u_transformed[i] = gradient_variable_transformation(equations_parabolic)(u[i],
- equations_parabolic)
+ u_transformed[i] = transformation(u[i], equations_parabolic)
end
end
diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl
index c2aa75388c8..7602331d7c8 100644
--- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl
@@ -105,12 +105,13 @@ end
function transform_variables!(u_transformed, u, mesh::TreeMesh{1},
equations_parabolic::AbstractEquationsParabolic,
dg::DG, parabolic_scheme, cache, cache_parabolic)
+ transformation = gradient_variable_transformation(equations_parabolic)
+
@threaded for element in eachelement(dg, cache)
# Calculate volume terms in one element
for i in eachnode(dg)
u_node = get_node_vars(u, equations_parabolic, dg, i, element)
- u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node,
- equations_parabolic)
+ u_transformed_node = transformation(u_node, equations_parabolic)
set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg,
i, element)
end
@@ -147,16 +148,18 @@ function prolong2interfaces!(cache_parabolic, flux_viscous,
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG, cache)
@unpack interfaces = cache_parabolic
+ @unpack neighbor_ids = interfaces
+ interfaces_u = interfaces.u
@threaded for interface in eachinterface(dg, cache)
- left_element = interfaces.neighbor_ids[1, interface]
- right_element = interfaces.neighbor_ids[2, interface]
+ left_element = neighbor_ids[1, interface]
+ right_element = neighbor_ids[2, interface]
# interface in x-direction
for v in eachvariable(equations_parabolic)
- # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
- interfaces.u[1, v, interface] = flux_viscous[v, nnodes(dg), left_element]
- interfaces.u[2, v, interface] = flux_viscous[v, 1, right_element]
+ # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*!
+ interfaces_u[1, v, interface] = flux_viscous[v, nnodes(dg), left_element]
+ interfaces_u[2, v, interface] = flux_viscous[v, 1, right_element]
end
end
@@ -204,21 +207,22 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG, cache)
@unpack boundaries = cache_parabolic
- @unpack neighbor_sides = boundaries
+ @unpack neighbor_sides, neighbor_ids = boundaries
+ boundaries_u = boundaries.u
@threaded for boundary in eachboundary(dg, cache_parabolic)
- element = boundaries.neighbor_ids[boundary]
+ element = neighbor_ids[boundary]
if neighbor_sides[boundary] == 1
# element in -x direction of boundary
for v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[1, v, boundary] = flux_viscous[v, nnodes(dg), element]
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[1, v, boundary] = flux_viscous[v, nnodes(dg), element]
end
else # Element in +x direction of boundary
for v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[2, v, boundary] = flux_viscous[v, 1, element]
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[2, v, boundary] = flux_viscous[v, 1, element]
end
end
end
@@ -552,8 +556,10 @@ end
# where f(u) is the inviscid flux and g(u) is the viscous flux.
function apply_jacobian_parabolic!(du, mesh::TreeMesh{1},
equations::AbstractEquationsParabolic, dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
@threaded for element in eachelement(dg, cache)
- factor = cache.elements.inverse_jacobian[element]
+ factor = inverse_jacobian[element]
for i in eachnode(dg)
for v in eachvariable(equations)
diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl
index 0da25230380..3dbc55412ad 100644
--- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl
@@ -118,12 +118,13 @@ end
function transform_variables!(u_transformed, u, mesh::Union{TreeMesh{2}, P4estMesh{2}},
equations_parabolic::AbstractEquationsParabolic,
dg::DG, parabolic_scheme, cache, cache_parabolic)
+ transformation = gradient_variable_transformation(equations_parabolic)
+
@threaded for element in eachelement(dg, cache)
# Calculate volume terms in one element
for j in eachnode(dg), i in eachnode(dg)
u_node = get_node_vars(u, equations_parabolic, dg, i, j, element)
- u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node,
- equations_parabolic)
+ u_transformed_node = transformation(u_node, equations_parabolic)
set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg,
i, j, element)
end
@@ -168,30 +169,31 @@ function prolong2interfaces!(cache_parabolic, flux_viscous,
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG, cache)
@unpack interfaces = cache_parabolic
- @unpack orientations = interfaces
+ @unpack orientations, neighbor_ids = interfaces
+ interfaces_u = interfaces.u
flux_viscous_x, flux_viscous_y = flux_viscous
@threaded for interface in eachinterface(dg, cache)
- left_element = interfaces.neighbor_ids[1, interface]
- right_element = interfaces.neighbor_ids[2, interface]
+ left_element = neighbor_ids[1, interface]
+ right_element = neighbor_ids[2, interface]
if orientations[interface] == 1
# interface in x-direction
for j in eachnode(dg), v in eachvariable(equations_parabolic)
- # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
- interfaces.u[1, v, j, interface] = flux_viscous_x[v, nnodes(dg), j,
+ # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*!
+ interfaces_u[1, v, j, interface] = flux_viscous_x[v, nnodes(dg), j,
left_element]
- interfaces.u[2, v, j, interface] = flux_viscous_x[v, 1, j,
+ interfaces_u[2, v, j, interface] = flux_viscous_x[v, 1, j,
right_element]
end
else # if orientations[interface] == 2
# interface in y-direction
for i in eachnode(dg), v in eachvariable(equations_parabolic)
- # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
- interfaces.u[1, v, i, interface] = flux_viscous_y[v, i, nnodes(dg),
+ # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*!
+ interfaces_u[1, v, i, interface] = flux_viscous_y[v, i, nnodes(dg),
left_element]
- interfaces.u[2, v, i, interface] = flux_viscous_y[v, i, 1,
+ interfaces_u[2, v, i, interface] = flux_viscous_y[v, i, 1,
right_element]
end
end
@@ -244,25 +246,26 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG, cache)
@unpack boundaries = cache_parabolic
- @unpack orientations, neighbor_sides = boundaries
+ @unpack orientations, neighbor_sides, neighbor_ids = boundaries
+ boundaries_u = boundaries.u
flux_viscous_x, flux_viscous_y = flux_viscous
@threaded for boundary in eachboundary(dg, cache_parabolic)
- element = boundaries.neighbor_ids[boundary]
+ element = neighbor_ids[boundary]
if orientations[boundary] == 1
# boundary in x-direction
if neighbor_sides[boundary] == 1
# element in -x direction of boundary
for l in eachnode(dg), v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[1, v, l, boundary] = flux_viscous_x[v, nnodes(dg), l,
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[1, v, l, boundary] = flux_viscous_x[v, nnodes(dg), l,
element]
end
else # Element in +x direction of boundary
for l in eachnode(dg), v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[2, v, l, boundary] = flux_viscous_x[v, 1, l, element]
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[2, v, l, boundary] = flux_viscous_x[v, 1, l, element]
end
end
else # if orientations[boundary] == 2
@@ -270,15 +273,15 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
if neighbor_sides[boundary] == 1
# element in -y direction of boundary
for l in eachnode(dg), v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[1, v, l, boundary] = flux_viscous_y[v, l, nnodes(dg),
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[1, v, l, boundary] = flux_viscous_y[v, l, nnodes(dg),
element]
end
else
# element in +y direction of boundary
for l in eachnode(dg), v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[2, v, l, boundary] = flux_viscous_y[v, l, 1, element]
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[2, v, l, boundary] = flux_viscous_y[v, l, 1, element]
end
end
end
@@ -608,7 +611,7 @@ function prolong2mortars!(cache, flux_viscous::Tuple{AbstractArray, AbstractArra
end
# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms.
-# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for
+# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for
# hyperbolic terms with conserved terms only, i.e., no nonconservative terms.
function calc_mortar_flux!(surface_flux_values,
mesh::TreeMesh{2},
@@ -934,8 +937,10 @@ end
# where f(u) is the inviscid flux and g(u) is the viscous flux.
function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{2}, P4estMesh{2}},
equations::AbstractEquationsParabolic, dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
@threaded for element in eachelement(dg, cache)
- factor = cache.elements.inverse_jacobian[element]
+ factor = inverse_jacobian[element]
for j in eachnode(dg), i in eachnode(dg)
for v in eachvariable(equations)
diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
index 2745d312b37..9817e0e5f0e 100644
--- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
@@ -118,12 +118,13 @@ end
function transform_variables!(u_transformed, u, mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations_parabolic::AbstractEquationsParabolic,
dg::DG, parabolic_scheme, cache, cache_parabolic)
+ transformation = gradient_variable_transformation(equations_parabolic)
+
@threaded for element in eachelement(dg, cache)
# Calculate volume terms in one element
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
u_node = get_node_vars(u, equations_parabolic, dg, i, j, k, element)
- u_transformed_node = gradient_variable_transformation(equations_parabolic)(u_node,
- equations_parabolic)
+ u_transformed_node = transformation(u_node, equations_parabolic)
set_node_vars!(u_transformed, u_transformed_node, equations_parabolic, dg,
i, j, k, element)
end
@@ -175,43 +176,44 @@ function prolong2interfaces!(cache_parabolic, flux_viscous,
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG, cache)
@unpack interfaces = cache_parabolic
- @unpack orientations = interfaces
+ @unpack orientations, neighbor_ids = interfaces
+ interfaces_u = interfaces.u
flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous
@threaded for interface in eachinterface(dg, cache)
- left_element = interfaces.neighbor_ids[1, interface]
- right_element = interfaces.neighbor_ids[2, interface]
+ left_element = neighbor_ids[1, interface]
+ right_element = neighbor_ids[2, interface]
if orientations[interface] == 1
# interface in x-direction
for k in eachnode(dg), j in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
- interfaces.u[1, v, j, k, interface] = flux_viscous_x[v, nnodes(dg), j,
+ # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*!
+ interfaces_u[1, v, j, k, interface] = flux_viscous_x[v, nnodes(dg), j,
k, left_element]
- interfaces.u[2, v, j, k, interface] = flux_viscous_x[v, 1, j, k,
+ interfaces_u[2, v, j, k, interface] = flux_viscous_x[v, 1, j, k,
right_element]
end
elseif orientations[interface] == 2
# interface in y-direction
for k in eachnode(dg), i in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
- interfaces.u[1, v, i, k, interface] = flux_viscous_y[v, i, nnodes(dg),
+ # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*!
+ interfaces_u[1, v, i, k, interface] = flux_viscous_y[v, i, nnodes(dg),
k, left_element]
- interfaces.u[2, v, i, k, interface] = flux_viscous_y[v, i, 1, k,
+ interfaces_u[2, v, i, k, interface] = flux_viscous_y[v, i, 1, k,
right_element]
end
else # if orientations[interface] == 3
# interface in z-direction
for j in eachnode(dg), i in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `interfaces.u` stores the interpolated *fluxes* and *not the solution*!
- interfaces.u[1, v, i, j, interface] = flux_viscous_z[v, i, j,
+ # OBS! `interfaces_u` stores the interpolated *fluxes* and *not the solution*!
+ interfaces_u[1, v, i, j, interface] = flux_viscous_z[v, i, j,
nnodes(dg),
left_element]
- interfaces.u[2, v, i, j, interface] = flux_viscous_z[v, i, j, 1,
+ interfaces_u[2, v, i, j, interface] = flux_viscous_z[v, i, j, 1,
right_element]
end
end
@@ -265,11 +267,12 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
equations_parabolic::AbstractEquationsParabolic,
surface_integral, dg::DG, cache)
@unpack boundaries = cache_parabolic
- @unpack orientations, neighbor_sides = boundaries
+ @unpack orientations, neighbor_sides, neighbor_ids = boundaries
+ boundaries_u = boundaries.u
flux_viscous_x, flux_viscous_y, flux_viscous_z = flux_viscous
@threaded for boundary in eachboundary(dg, cache_parabolic)
- element = boundaries.neighbor_ids[boundary]
+ element = neighbor_ids[boundary]
if orientations[boundary] == 1
# boundary in x-direction
@@ -277,15 +280,15 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
# element in -x direction of boundary
for k in eachnode(dg), j in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[1, v, j, k, boundary] = flux_viscous_x[v, nnodes(dg),
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[1, v, j, k, boundary] = flux_viscous_x[v, nnodes(dg),
j, k, element]
end
else # Element in +x direction of boundary
for k in eachnode(dg), j in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[2, v, j, k, boundary] = flux_viscous_x[v, 1, j, k,
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[2, v, j, k, boundary] = flux_viscous_x[v, 1, j, k,
element]
end
end
@@ -295,8 +298,8 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
# element in -y direction of boundary
for k in eachnode(dg), i in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[1, v, i, k, boundary] = flux_viscous_y[v, i,
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[1, v, i, k, boundary] = flux_viscous_y[v, i,
nnodes(dg), k,
element]
end
@@ -304,8 +307,8 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
# element in +y direction of boundary
for k in eachnode(dg), i in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[2, v, i, k, boundary] = flux_viscous_y[v, i, 1, k,
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[2, v, i, k, boundary] = flux_viscous_y[v, i, 1, k,
element]
end
end
@@ -315,8 +318,8 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
# element in -z direction of boundary
for j in eachnode(dg), i in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[1, v, i, j, boundary] = flux_viscous_z[v, i, j,
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[1, v, i, j, boundary] = flux_viscous_z[v, i, j,
nnodes(dg),
element]
end
@@ -324,8 +327,8 @@ function prolong2boundaries!(cache_parabolic, flux_viscous,
# element in +z direction of boundary
for j in eachnode(dg), i in eachnode(dg),
v in eachvariable(equations_parabolic)
- # OBS! `boundaries.u` stores the interpolated *fluxes* and *not the solution*!
- boundaries.u[2, v, i, j, boundary] = flux_viscous_z[v, i, j, 1,
+ # OBS! `boundaries_u` stores the interpolated *fluxes* and *not the solution*!
+ boundaries_u[2, v, i, j, boundary] = flux_viscous_z[v, i, j, 1,
element]
end
end
@@ -820,7 +823,7 @@ function prolong2mortars!(cache,
end
# NOTE: Use analogy to "calc_mortar_flux!" for hyperbolic eqs with no nonconservative terms.
-# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for
+# Reasoning: "calc_interface_flux!" for parabolic part is implemented as the version for
# hyperbolic terms with conserved terms only, i.e., no nonconservative terms.
function calc_mortar_flux!(surface_flux_values,
mesh::TreeMesh{3},
@@ -1124,8 +1127,10 @@ end
# where f(u) is the inviscid flux and g(u) is the viscous flux.
function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{3}, P4estMesh{3}},
equations::AbstractEquationsParabolic, dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
@threaded for element in eachelement(dg, cache)
- factor = cache.elements.inverse_jacobian[element]
+ factor = inverse_jacobian[element]
for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
for v in eachvariable(equations)
From cfbf048308b1074591e08f8627c0089871bf91f3 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 12 Sep 2023 12:11:06 +0200
Subject: [PATCH 089/263] remove JuliaCOn 2023 announcement (#1631)
---
README.md | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/README.md b/README.md
index 63540b1f640..c177ad2347f 100644
--- a/README.md
+++ b/README.md
@@ -17,16 +17,6 @@
-***
-**Trixi.jl at JuliaCon 2023**
-At this year's JuliaCon, we will be present with an online contribution that involves Trixi.jl:
-
-* [Scaling Trixi.jl to more than 10,000 cores using MPI](https://pretalx.com/juliacon2023/talk/PC8PZ8/),
- 27th July 2023, 10:30–11:30 (US/Eastern), 32-G449 (Kiva)
-
-We are looking forward to seeing you there ♥️
-***
-
**Trixi.jl** is a numerical simulation framework for hyperbolic conservation
laws written in [Julia](https://julialang.org). A key objective for the
framework is to be useful to both scientists and students. Therefore, next to
From b9f3f3051c483e8ad09cb51857eec9bb228e267c Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 12 Sep 2023 12:50:44 +0200
Subject: [PATCH 090/263] set version to v0.5.42
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index d37c0548a6a..9f27fbb2710 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.42-pre"
+version = "0.5.42"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From daf18a5352a80ca2fbb2077ace991b9e5cc33c16 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 12 Sep 2023 12:50:58 +0200
Subject: [PATCH 091/263] set development version to v0.5.43-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 9f27fbb2710..06fd29ba590 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.42"
+version = "0.5.43-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 3523c49120d7c282518769a5b3d40ce7c9cc5882 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Tue, 12 Sep 2023 15:00:58 +0200
Subject: [PATCH 092/263] AMR for 1D Parabolic Eqs (Clean branch) (#1605)
* Clean branch
* Un-Comment
* un-comment
* test coarsen
* remove redundancy
* Remove support for passive terms
* expand resize
* comments
* format
* Avoid code duplication
* Update src/callbacks_step/amr_dg1d.jl
Co-authored-by: Michael Schlottke-Lakemper
* comment
* comment & format
* Try to increase coverage
* Slightly more expressive names
* Apply suggestions from code review
---------
Co-authored-by: Michael Schlottke-Lakemper
---
...ixir_navierstokes_convergence_walls_amr.jl | 172 ++++++++++++++++++
src/callbacks_step/amr.jl | 158 ++++++++++++++++
src/callbacks_step/amr_dg1d.jl | 73 ++++++++
.../dgsem_tree/container_viscous_1d.jl | 58 ++++++
src/solvers/dgsem_tree/dg.jl | 3 +
src/solvers/dgsem_tree/dg_1d_parabolic.jl | 14 +-
test/test_parabolic_1d.jl | 41 +++++
test/test_parabolic_2d.jl | 12 +-
test/test_parabolic_3d.jl | 12 +-
9 files changed, 523 insertions(+), 20 deletions(-)
create mode 100644 examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl
create mode 100644 src/solvers/dgsem_tree/container_viscous_1d.jl
diff --git a/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl
new file mode 100644
index 00000000000..1daeab04a71
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_navierstokes_convergence_walls_amr.jl
@@ -0,0 +1,172 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the ideal compressible Navier-Stokes equations
+
+prandtl_number() = 0.72
+mu() = 0.01
+
+equations = CompressibleEulerEquations1D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(), Prandtl=prandtl_number(),
+ gradient_variables=GradientVariablesEntropy())
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs,
+ volume_integral=VolumeIntegralWeakForm())
+
+coordinates_min = -1.0
+coordinates_max = 1.0
+
+# Create a uniformly refined mesh with periodic boundaries
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level=3,
+ periodicity=false,
+ n_cells_max=30_000) # set maximum capacity of tree data structure
+
+# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion1D`
+# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion1D`)
+# and by the initial condition (which passes in `CompressibleEulerEquations1D`).
+# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
+function initial_condition_navier_stokes_convergence_test(x, t, equations)
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_t = pi * t
+
+ rho = c + A * cos(pi_x) * cos(pi_t)
+ v1 = log(x[1] + 2.0) * (1.0 - exp(-A * (x[1] - 1.0)) ) * cos(pi_t)
+ p = rho^2
+
+ return prim2cons(SVector(rho, v1, p), equations)
+end
+
+@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
+ x = x[1]
+
+ # TODO: parabolic
+ # we currently need to hardcode these parameters until we fix the "combined equation" issue
+ # see also https://github.com/trixi-framework/Trixi.jl/pull/1160
+ inv_gamma_minus_one = inv(equations.gamma - 1)
+ Pr = prandtl_number()
+ mu_ = mu()
+
+ # Same settings as in `initial_condition`
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x
+ pi_t = pi * t
+
+ # compute the manufactured solution and all necessary derivatives
+ rho = c + A * cos(pi_x) * cos(pi_t)
+ rho_t = -pi * A * cos(pi_x) * sin(pi_t)
+ rho_x = -pi * A * sin(pi_x) * cos(pi_t)
+ rho_xx = -pi * pi * A * cos(pi_x) * cos(pi_t)
+
+ v1 = log(x + 2.0) * (1.0 - exp(-A * (x - 1.0))) * cos(pi_t)
+ v1_t = -pi * log(x + 2.0) * (1.0 - exp(-A * (x - 1.0))) * sin(pi_t)
+ v1_x = (A * log(x + 2.0) * exp(-A * (x - 1.0)) + (1.0 - exp(-A * (x - 1.0))) / (x + 2.0)) * cos(pi_t)
+ v1_xx = (( 2.0 * A * exp(-A * (x - 1.0)) / (x + 2.0)
+ - A * A * log(x + 2.0) * exp(-A * (x - 1.0))
+ - (1.0 - exp(-A * (x - 1.0))) / ((x + 2.0) * (x + 2.0))) * cos(pi_t))
+
+ p = rho * rho
+ p_t = 2.0 * rho * rho_t
+ p_x = 2.0 * rho * rho_x
+ p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x
+
+ # Note this simplifies slightly because the ansatz assumes that v1 = v2
+ E = p * inv_gamma_minus_one + 0.5 * rho * v1^2
+ E_t = p_t * inv_gamma_minus_one + 0.5 * rho_t * v1^2 + rho * v1 * v1_t
+ E_x = p_x * inv_gamma_minus_one + 0.5 * rho_x * v1^2 + rho * v1 * v1_x
+
+ # Some convenience constants
+ T_const = equations.gamma * inv_gamma_minus_one / Pr
+ inv_rho_cubed = 1.0 / (rho^3)
+
+ # compute the source terms
+ # density equation
+ du1 = rho_t + rho_x * v1 + rho * v1_x
+
+ # y-momentum equation
+ du2 = ( rho_t * v1 + rho * v1_t
+ + p_x + rho_x * v1^2 + 2.0 * rho * v1 * v1_x
+ # stress tensor from y-direction
+ - v1_xx * mu_)
+
+ # total energy equation
+ du3 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x)
+ # stress tensor and temperature gradient terms from x-direction
+ - v1_xx * v1 * mu_
+ - v1_x * v1_x * mu_
+ - T_const * inv_rho_cubed * ( p_xx * rho * rho
+ - 2.0 * p_x * rho * rho_x
+ + 2.0 * p * rho_x * rho_x
+ - p * rho * rho_xx ) * mu_ )
+
+ return SVector(du1, du2, du3)
+end
+
+initial_condition = initial_condition_navier_stokes_convergence_test
+
+# BC types
+velocity_bc_left_right = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2])
+
+heat_bc_left = Isothermal((x, t, equations) ->
+ Trixi.temperature(initial_condition_navier_stokes_convergence_test(x, t, equations),
+ equations_parabolic))
+heat_bc_right = Adiabatic((x, t, equations) -> 0.0)
+
+boundary_condition_left = BoundaryConditionNavierStokesWall(velocity_bc_left_right, heat_bc_left)
+boundary_condition_right = BoundaryConditionNavierStokesWall(velocity_bc_left_right, heat_bc_right)
+
+# define inviscid boundary conditions
+boundary_conditions = (; x_neg = boundary_condition_slip_wall,
+ x_pos = boundary_condition_slip_wall)
+
+# define viscous boundary conditions
+boundary_conditions_parabolic = (; x_neg = boundary_condition_left,
+ x_pos = boundary_condition_right)
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+alive_callback = AliveCallback(alive_interval=10)
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+
+amr_controller = ControllerThreeLevel(semi,
+ IndicatorLöhner(semi, variable=Trixi.density),
+ base_level=3,
+ med_level=4, med_threshold=0.005,
+ max_level=5, max_threshold=0.01)
+
+amr_callback = AMRCallback(semi, amr_controller,
+ interval=5,
+ adapt_initial_condition=true)
+
+# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
\ No newline at end of file
diff --git a/src/callbacks_step/amr.jl b/src/callbacks_step/amr.jl
index 4d80e6e1139..ba840ff9675 100644
--- a/src/callbacks_step/amr.jl
+++ b/src/callbacks_step/amr.jl
@@ -192,6 +192,16 @@ end
amr_callback(u_ode, mesh_equations_solver_cache(semi)..., semi, t, iter; kwargs...)
end
+@inline function (amr_callback::AMRCallback)(u_ode::AbstractVector,
+ semi::SemidiscretizationHyperbolicParabolic,
+ t, iter;
+ kwargs...)
+ # Note that we don't `wrap_array` the vector `u_ode` to be able to `resize!`
+ # it when doing AMR while still dispatching on the `mesh` etc.
+ amr_callback(u_ode, mesh_equations_solver_cache(semi)..., semi.cache_parabolic,
+ semi, t, iter; kwargs...)
+end
+
# `passive_args` is currently used for Euler with self-gravity to adapt the gravity solver
# passively without querying its indicator, based on the assumption that both solvers use
# the same mesh. That's a hack and should be improved in the future once we have more examples
@@ -346,6 +356,154 @@ function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh,
return has_changed
end
+function (amr_callback::AMRCallback)(u_ode::AbstractVector, mesh::TreeMesh,
+ equations, dg::DG,
+ cache, cache_parabolic,
+ semi::SemidiscretizationHyperbolicParabolic,
+ t, iter;
+ only_refine = false, only_coarsen = false)
+ @unpack controller, adaptor = amr_callback
+
+ u = wrap_array(u_ode, mesh, equations, dg, cache)
+ # Indicator kept based on hyperbolic variables
+ lambda = @trixi_timeit timer() "indicator" controller(u, mesh, equations, dg, cache,
+ t = t, iter = iter)
+
+ if mpi_isparallel()
+ error("MPI has not been verified yet for parabolic AMR")
+
+ # Collect lambda for all elements
+ lambda_global = Vector{eltype(lambda)}(undef, nelementsglobal(dg, cache))
+ # Use parent because n_elements_by_rank is an OffsetArray
+ recvbuf = MPI.VBuffer(lambda_global, parent(cache.mpi_cache.n_elements_by_rank))
+ MPI.Allgatherv!(lambda, recvbuf, mpi_comm())
+ lambda = lambda_global
+ end
+
+ leaf_cell_ids = leaf_cells(mesh.tree)
+ @boundscheck begin
+ @assert axes(lambda)==axes(leaf_cell_ids) ("Indicator (axes = $(axes(lambda))) and leaf cell (axes = $(axes(leaf_cell_ids))) arrays have different axes")
+ end
+
+ @unpack to_refine, to_coarsen = amr_callback.amr_cache
+ empty!(to_refine)
+ empty!(to_coarsen)
+ for element in 1:length(lambda)
+ controller_value = lambda[element]
+ if controller_value > 0
+ push!(to_refine, leaf_cell_ids[element])
+ elseif controller_value < 0
+ push!(to_coarsen, leaf_cell_ids[element])
+ end
+ end
+
+ @trixi_timeit timer() "refine" if !only_coarsen && !isempty(to_refine)
+ # refine mesh
+ refined_original_cells = @trixi_timeit timer() "mesh" refine!(mesh.tree,
+ to_refine)
+
+ # Find all indices of elements whose cell ids are in refined_original_cells
+ # Note: This assumes same indices for hyperbolic and parabolic part.
+ elements_to_refine = findall(in(refined_original_cells),
+ cache.elements.cell_ids)
+
+ # refine solver
+ @trixi_timeit timer() "solver" refine!(u_ode, adaptor, mesh, equations, dg,
+ cache, cache_parabolic,
+ elements_to_refine)
+ else
+ # If there is nothing to refine, create empty array for later use
+ refined_original_cells = Int[]
+ end
+
+ @trixi_timeit timer() "coarsen" if !only_refine && !isempty(to_coarsen)
+ # Since the cells may have been shifted due to refinement, first we need to
+ # translate the old cell ids to the new cell ids
+ if !isempty(to_coarsen)
+ to_coarsen = original2refined(to_coarsen, refined_original_cells, mesh)
+ end
+
+ # Next, determine the parent cells from which the fine cells are to be
+ # removed, since these are needed for the coarsen! function. However, since
+ # we only want to coarsen if *all* child cells are marked for coarsening,
+ # we count the coarsening indicators for each parent cell and only coarsen
+ # if all children are marked as such (i.e., where the count is 2^ndims). At
+ # the same time, check if a cell is marked for coarsening even though it is
+ # *not* a leaf cell -> this can only happen if it was refined due to 2:1
+ # smoothing during the preceding refinement operation.
+ parents_to_coarsen = zeros(Int, length(mesh.tree))
+ for cell_id in to_coarsen
+ # If cell has no parent, it cannot be coarsened
+ if !has_parent(mesh.tree, cell_id)
+ continue
+ end
+
+ # If cell is not leaf (anymore), it cannot be coarsened
+ if !is_leaf(mesh.tree, cell_id)
+ continue
+ end
+
+ # Increase count for parent cell
+ parent_id = mesh.tree.parent_ids[cell_id]
+ parents_to_coarsen[parent_id] += 1
+ end
+
+ # Extract only those parent cells for which all children should be coarsened
+ to_coarsen = collect(1:length(parents_to_coarsen))[parents_to_coarsen .== 2^ndims(mesh)]
+
+ # Finally, coarsen mesh
+ coarsened_original_cells = @trixi_timeit timer() "mesh" coarsen!(mesh.tree,
+ to_coarsen)
+
+ # Convert coarsened parent cell ids to the list of child cell ids that have
+ # been removed, since this is the information that is expected by the solver
+ removed_child_cells = zeros(Int,
+ n_children_per_cell(mesh.tree) *
+ length(coarsened_original_cells))
+ for (index, coarse_cell_id) in enumerate(coarsened_original_cells)
+ for child in 1:n_children_per_cell(mesh.tree)
+ removed_child_cells[n_children_per_cell(mesh.tree) * (index - 1) + child] = coarse_cell_id +
+ child
+ end
+ end
+
+ # Find all indices of elements whose cell ids are in removed_child_cells
+ # Note: This assumes same indices for hyperbolic and parabolic part.
+ elements_to_remove = findall(in(removed_child_cells), cache.elements.cell_ids)
+
+ # coarsen solver
+ @trixi_timeit timer() "solver" coarsen!(u_ode, adaptor, mesh, equations, dg,
+ cache, cache_parabolic,
+ elements_to_remove)
+ else
+ # If there is nothing to coarsen, create empty array for later use
+ coarsened_original_cells = Int[]
+ end
+
+ # Store whether there were any cells coarsened or refined
+ has_changed = !isempty(refined_original_cells) || !isempty(coarsened_original_cells)
+ if has_changed # TODO: Taal decide, where shall we set this?
+ # don't set it to has_changed since there can be changes from earlier calls
+ mesh.unsaved_changes = true
+ end
+
+ # Dynamically balance computational load by first repartitioning the mesh and then redistributing the cells/elements
+ if has_changed && mpi_isparallel() && amr_callback.dynamic_load_balancing
+ error("MPI has not been verified yet for parabolic AMR")
+
+ @trixi_timeit timer() "dynamic load balancing" begin
+ old_mpi_ranks_per_cell = copy(mesh.tree.mpi_ranks)
+
+ partition!(mesh)
+
+ rebalance_solver!(u_ode, mesh, equations, dg, cache, old_mpi_ranks_per_cell)
+ end
+ end
+
+ # Return true if there were any cells coarsened or refined, otherwise false
+ return has_changed
+end
+
# Copy controller values to quad user data storage, will be called below
function copy_to_quad_iter_volume(info, user_data)
info_pw = PointerWrapper(info)
diff --git a/src/callbacks_step/amr_dg1d.jl b/src/callbacks_step/amr_dg1d.jl
index e31a74730ea..e721ccc61cb 100644
--- a/src/callbacks_step/amr_dg1d.jl
+++ b/src/callbacks_step/amr_dg1d.jl
@@ -76,6 +76,44 @@ function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
return nothing
end
+function refine!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
+ equations, dg::DGSEM, cache, cache_parabolic,
+ elements_to_refine)
+ # Call `refine!` for the hyperbolic part, which does the heavy lifting of
+ # actually transferring the solution to the refined cells
+ refine!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_refine)
+
+ # The remaining function only handles the necessary adaptation of the data structures
+ # for the parabolic part of the semidiscretization
+
+ # Get new list of leaf cells
+ leaf_cell_ids = local_leaf_cells(mesh.tree)
+
+ @unpack elements, viscous_container = cache_parabolic
+ resize!(elements, length(leaf_cell_ids))
+ init_elements!(elements, leaf_cell_ids, mesh, dg.basis)
+
+ # Resize parabolic helper variables
+ resize!(viscous_container, equations, dg, cache)
+
+ # re-initialize interfaces container
+ @unpack interfaces = cache_parabolic
+ resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids))
+ init_interfaces!(interfaces, elements, mesh)
+
+ # re-initialize boundaries container
+ @unpack boundaries = cache_parabolic
+ resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids))
+ init_boundaries!(boundaries, elements, mesh)
+
+ # Sanity check
+ if isperiodic(mesh.tree)
+ @assert ninterfaces(interfaces)==1 * nelements(dg, cache_parabolic) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements")
+ end
+
+ return nothing
+end
+
# TODO: Taal compare performance of different implementations
# Refine solution data u for an element, using L2 projection (interpolation)
function refine_element!(u::AbstractArray{<:Any, 3}, element_id,
@@ -201,6 +239,41 @@ function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
return nothing
end
+function coarsen!(u_ode::AbstractVector, adaptor, mesh::TreeMesh{1},
+ equations, dg::DGSEM, cache, cache_parabolic,
+ elements_to_remove)
+ # Call `coarsen!` for the hyperbolic part, which does the heavy lifting of
+ # actually transferring the solution to the coarsened cells
+ coarsen!(u_ode, adaptor, mesh, equations, dg, cache, elements_to_remove)
+
+ # Get new list of leaf cells
+ leaf_cell_ids = local_leaf_cells(mesh.tree)
+
+ @unpack elements, viscous_container = cache_parabolic
+ resize!(elements, length(leaf_cell_ids))
+ init_elements!(elements, leaf_cell_ids, mesh, dg.basis)
+
+ # Resize parabolic helper variables
+ resize!(viscous_container, equations, dg, cache)
+
+ # re-initialize interfaces container
+ @unpack interfaces = cache_parabolic
+ resize!(interfaces, count_required_interfaces(mesh, leaf_cell_ids))
+ init_interfaces!(interfaces, elements, mesh)
+
+ # re-initialize boundaries container
+ @unpack boundaries = cache_parabolic
+ resize!(boundaries, count_required_boundaries(mesh, leaf_cell_ids))
+ init_boundaries!(boundaries, elements, mesh)
+
+ # Sanity check
+ if isperiodic(mesh.tree)
+ @assert ninterfaces(interfaces)==1 * nelements(dg, cache_parabolic) ("For 1D and periodic domains, the number of interfaces must be the same as the number of elements")
+ end
+
+ return nothing
+end
+
# TODO: Taal compare performance of different implementations
# Coarsen solution data u for two elements, using L2 projection
function coarsen_elements!(u::AbstractArray{<:Any, 3}, element_id,
diff --git a/src/solvers/dgsem_tree/container_viscous_1d.jl b/src/solvers/dgsem_tree/container_viscous_1d.jl
new file mode 100644
index 00000000000..a4919f75396
--- /dev/null
+++ b/src/solvers/dgsem_tree/container_viscous_1d.jl
@@ -0,0 +1,58 @@
+mutable struct ViscousContainer1D{uEltype <: Real}
+ u_transformed::Array{uEltype, 3}
+ gradients::Array{uEltype, 3}
+ flux_viscous::Array{uEltype, 3}
+
+ # internal `resize!`able storage
+ _u_transformed::Vector{uEltype}
+ _gradients::Vector{uEltype}
+ _flux_viscous::Vector{uEltype}
+
+ function ViscousContainer1D{uEltype}(n_vars::Integer, n_nodes::Integer,
+ n_elements::Integer) where {uEltype <: Real}
+ new(Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements),
+ Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements),
+ Array{uEltype, 3}(undef, n_vars, n_nodes, n_elements),
+ Vector{uEltype}(undef, n_vars * n_nodes * n_elements),
+ Vector{uEltype}(undef, n_vars * n_nodes * n_elements),
+ Vector{uEltype}(undef, n_vars * n_nodes * n_elements))
+ end
+end
+
+function init_viscous_container(n_vars::Integer, n_nodes::Integer,
+ n_elements::Integer,
+ ::Type{uEltype}) where {uEltype <: Real}
+ return ViscousContainer1D{uEltype}(n_vars, n_nodes, n_elements)
+end
+
+# Only one-dimensional `Array`s are `resize!`able in Julia.
+# Hence, we use `Vector`s as internal storage and `resize!`
+# them whenever needed. Then, we reuse the same memory by
+# `unsafe_wrap`ping multi-dimensional `Array`s around the
+# internal storage.
+function Base.resize!(viscous_container::ViscousContainer1D, equations, dg, cache)
+ capacity = nvariables(equations) * nnodes(dg) * nelements(dg, cache)
+ resize!(viscous_container._u_transformed, capacity)
+ resize!(viscous_container._gradients, capacity)
+ resize!(viscous_container._flux_viscous, capacity)
+
+ viscous_container.u_transformed = unsafe_wrap(Array,
+ pointer(viscous_container._u_transformed),
+ (nvariables(equations),
+ nnodes(dg),
+ nelements(dg, cache)))
+
+ viscous_container.gradients = unsafe_wrap(Array,
+ pointer(viscous_container._gradients),
+ (nvariables(equations),
+ nnodes(dg),
+ nelements(dg, cache)))
+
+ viscous_container.flux_viscous = unsafe_wrap(Array,
+ pointer(viscous_container._flux_viscous),
+ (nvariables(equations),
+ nnodes(dg),
+ nelements(dg, cache)))
+
+ return nothing
+end
diff --git a/src/solvers/dgsem_tree/dg.jl b/src/solvers/dgsem_tree/dg.jl
index 6e02bc1d94a..ff37bad3b3a 100644
--- a/src/solvers/dgsem_tree/dg.jl
+++ b/src/solvers/dgsem_tree/dg.jl
@@ -54,6 +54,9 @@ include("containers.jl")
# Dimension-agnostic parallel setup
include("dg_parallel.jl")
+# Helper struct for parabolic AMR
+include("container_viscous_1d.jl")
+
# 1D DG implementation
include("dg_1d.jl")
include("dg_1d_parabolic.jl")
diff --git a/src/solvers/dgsem_tree/dg_1d_parabolic.jl b/src/solvers/dgsem_tree/dg_1d_parabolic.jl
index 7602331d7c8..97e31e0e22b 100644
--- a/src/solvers/dgsem_tree/dg_1d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_1d_parabolic.jl
@@ -17,7 +17,8 @@ function rhs_parabolic!(du, u, t, mesh::TreeMesh{1},
equations_parabolic::AbstractEquationsParabolic,
initial_condition, boundary_conditions_parabolic, source_terms,
dg::DG, parabolic_scheme, cache, cache_parabolic)
- @unpack u_transformed, gradients, flux_viscous = cache_parabolic
+ @unpack viscous_container = cache_parabolic
+ @unpack u_transformed, gradients, flux_viscous = viscous_container
# Convert conservative variables to a form more suitable for viscous flux calculations
@trixi_timeit timer() "transform variables" begin
@@ -534,18 +535,15 @@ function create_cache_parabolic(mesh::TreeMesh{1},
elements = init_elements(leaf_cell_ids, mesh, equations_hyperbolic, dg.basis, RealT,
uEltype)
- n_vars = nvariables(equations_hyperbolic)
- n_nodes = nnodes(elements)
- n_elements = nelements(elements)
- u_transformed = Array{uEltype}(undef, n_vars, n_nodes, n_elements)
- gradients = similar(u_transformed)
- flux_viscous = similar(u_transformed)
+ viscous_container = init_viscous_container(nvariables(equations_hyperbolic),
+ nnodes(elements), nelements(elements),
+ uEltype)
interfaces = init_interfaces(leaf_cell_ids, mesh, elements)
boundaries = init_boundaries(leaf_cell_ids, mesh, elements)
- cache = (; elements, interfaces, boundaries, gradients, flux_viscous, u_transformed)
+ cache = (; elements, interfaces, boundaries, viscous_container)
return cache
end
diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl
index 06a55100d62..3c2b8855ce8 100644
--- a/test/test_parabolic_1d.jl
+++ b/test/test_parabolic_1d.jl
@@ -20,6 +20,28 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "TreeMesh1D: elixir_advection_diffusion.jl (AMR)" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_advection_diffusion.jl"),
+ tspan=(0.0, 0.0), initial_refinement_level = 5)
+ tspan=(0.0, 1.0)
+ ode = semidiscretize(semi, tspan)
+ amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first),
+ base_level=4,
+ med_level=5, med_threshold=0.1,
+ max_level=6, max_threshold=0.6)
+ amr_callback = AMRCallback(semi, amr_controller,
+ interval=5,
+ adapt_initial_condition=true)
+
+ # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
+ callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, amr_callback)
+ sol = solve(ode, KenCarp4(autodiff=false), abstol=time_abs_tol, reltol=time_int_tol,
+ save_everystep=false, callback=callbacks)
+ l2_error, linf_error = analysis_callback(sol)
+ @test l2_error ≈ [6.4878111416468355e-6]
+ @test linf_error ≈ [3.258075790424364e-5]
+ end
+
@trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_periodic.jl" begin
@test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_periodic.jl"),
l2 = [0.0001133835907077494, 6.226282245610444e-5, 0.0002820171699999139],
@@ -53,6 +75,25 @@ isdir(outdir) && rm(outdir, recursive=true)
linf = [0.002754803146635787, 0.0028567714697580906, 0.012941794048176192]
)
end
+
+ @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_walls_amr.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls_amr.jl"),
+ equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
+ Prandtl=prandtl_number()),
+ l2 = [2.527877257772131e-5, 2.5539911566937718e-5, 0.0001211860451244785],
+ linf = [0.00014663867588948776, 0.00019422448348348196, 0.0009556439394007299]
+ )
+ end
+
+ @trixi_testset "TreeMesh1D: elixir_navierstokes_convergence_walls_amr.jl: GradientVariablesEntropy" begin
+ @test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls_amr.jl"),
+ equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
+ Prandtl=prandtl_number(),
+ gradient_variables = GradientVariablesEntropy()),
+ l2 = [2.4593699163175966e-5, 2.392863645712634e-5, 0.00011252526651714956],
+ linf = [0.00011850555445525046, 0.0001898777490968537, 0.0009597561467877824]
+ )
+ end
end
# Clean up afterwards: delete Trixi output directory
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index 1564a33dc41..3fff4382cd1 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -143,9 +143,9 @@ isdir(outdir) && rm(outdir, recursive=true)
callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
ode_default_options()..., callback=callbacks)
- ac_sol = analysis_callback(sol)
- @test ac_sol.l2[1] ≈ 1.67452550744728e-6
- @test ac_sol.linf[1] ≈ 7.905059166368744e-6
+ l2_error, linf_error = analysis_callback(sol)
+ @test l2_error ≈ [1.67452550744728e-6]
+ @test linf_error ≈ [7.905059166368744e-6]
# Ensure that we do not have excessive memory allocations
# (e.g., from type instabilities)
@@ -229,9 +229,9 @@ isdir(outdir) && rm(outdir, recursive=true)
callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
ode_default_options()..., callback=callbacks)
- ac_sol = analysis_callback(sol)
- @test ac_sol.l2 ≈ [0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537]
- @test ac_sol.linf ≈ [0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271]
+ l2_error, linf_error = analysis_callback(sol)
+ @test l2_error ≈ [0.00024296959173852447; 0.0002093263158670915; 0.0005390572390977262; 0.00026753561392341537]
+ @test linf_error ≈ [0.0016210102053424436; 0.002593287648655501; 0.002953907343823712; 0.002077119120180271]
end
@trixi_testset "TreeMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin
diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl
index d607962afa0..ded052fb9d3 100644
--- a/test/test_parabolic_3d.jl
+++ b/test/test_parabolic_3d.jl
@@ -94,9 +94,9 @@ isdir(outdir) && rm(outdir, recursive=true)
callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
ode_default_options()..., callback=callbacks)
- ac_sol = analysis_callback(sol)
- @test ac_sol.l2 ≈ [0.0003991794175622818; 0.0008853745163670504; 0.0010658655552066817; 0.0008785559918324284; 0.001403163458422815]
- @test ac_sol.linf ≈ [0.0035306410538458177; 0.01505692306169911; 0.008862444161110705; 0.015065647972869856; 0.030402714743065218]
+ l2_error, linf_error = analysis_callback(sol)
+ @test l2_error ≈ [0.0003991794175622818; 0.0008853745163670504; 0.0010658655552066817; 0.0008785559918324284; 0.001403163458422815]
+ @test linf_error ≈ [0.0035306410538458177; 0.01505692306169911; 0.008862444161110705; 0.015065647972869856; 0.030402714743065218]
end
@trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl" begin
@@ -127,9 +127,9 @@ isdir(outdir) && rm(outdir, recursive=true)
sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
dt=5e-3,
save_everystep=false, callback=callbacks);
- ac_sol = analysis_callback(sol)
- @test ac_sol.l2 ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005]
- @test ac_sol.linf ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645]
+ l2_error, linf_error = analysis_callback(sol)
+ @test l2_error ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005]
+ @test linf_error ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645]
# Ensure that we do not have excessive memory allocations
# (e.g., from type instabilities)
From 27d4fd190bd7a8c76a56f9fefd062169a2682d46 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Tue, 12 Sep 2023 17:54:08 +0200
Subject: [PATCH 093/263] Shorten 3d parabolic test times (#1634)
* Shorten 3d parabolic test times
* fix typo
* clear notation
---
test/test_parabolic_3d.jl | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/test/test_parabolic_3d.jl b/test/test_parabolic_3d.jl
index ded052fb9d3..86076460294 100644
--- a/test/test_parabolic_3d.jl
+++ b/test/test_parabolic_3d.jl
@@ -85,7 +85,7 @@ isdir(outdir) && rm(outdir, recursive=true)
num_leafs = length(LLID)
@assert num_leafs % 16 == 0
Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/16)])
- tspan=(0.0, 1.0)
+ tspan=(0.0, 0.25)
semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
source_terms=source_terms_navier_stokes_convergence_test)
@@ -95,8 +95,8 @@ isdir(outdir) && rm(outdir, recursive=true)
sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
ode_default_options()..., callback=callbacks)
l2_error, linf_error = analysis_callback(sol)
- @test l2_error ≈ [0.0003991794175622818; 0.0008853745163670504; 0.0010658655552066817; 0.0008785559918324284; 0.001403163458422815]
- @test linf_error ≈ [0.0035306410538458177; 0.01505692306169911; 0.008862444161110705; 0.015065647972869856; 0.030402714743065218]
+ @test l2_error ≈ [0.0003109336253407314, 0.0006473493036803503, 0.0007705277238213672, 0.0006280517917198335, 0.000903927789884075]
+ @test linf_error ≈ [0.0023694155365339142, 0.010634932622402863, 0.006772070862236412, 0.010640551561726901, 0.019256819038719897]
end
@trixi_testset "TreeMesh3D: elixir_navierstokes_taylor_green_vortex.jl" begin
@@ -114,7 +114,7 @@ isdir(outdir) && rm(outdir, recursive=true)
num_leafs = length(LLID)
@assert num_leafs % 32 == 0
Trixi.refine!(mesh.tree, LLID[1:Int(num_leafs/32)])
- tspan=(0.0, 10.0)
+ tspan=(0.0, 0.1)
semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
initial_condition, solver)
ode = semidiscretize(semi, tspan)
@@ -128,8 +128,8 @@ isdir(outdir) && rm(outdir, recursive=true)
dt=5e-3,
save_everystep=false, callback=callbacks);
l2_error, linf_error = analysis_callback(sol)
- @test l2_error ≈ [0.0013666103707729502; 0.2313581629543744; 0.2308164306264533; 0.17460246787819503; 0.28121914446544005]
- @test linf_error ≈ [0.006938093883741336; 1.028235074139312; 1.0345438209717241; 1.0821111605203542; 1.2669636522564645]
+ @test l2_error ≈ [7.314319856736271e-5, 0.006266480163542894, 0.006266489911815533, 0.008829222305770226, 0.0032859166842329228]
+ @test linf_error ≈ [0.0002943968186086554, 0.013876261980614757, 0.013883619864959451, 0.025201279960491936, 0.018679364985388247]
# Ensure that we do not have excessive memory allocations
# (e.g., from type instabilities)
From d206b766e3e0aad36a9808ed65c5549b8b284f73 Mon Sep 17 00:00:00 2001
From: ArseniyKholod <119304909+ArseniyKholod@users.noreply.github.com>
Date: Tue, 12 Sep 2023 19:16:44 +0200
Subject: [PATCH 094/263] Add load_timestep! for restart setup (#1614)
* add load_timestep!
* Update save_restart.jl
* Update save_restart.jl
* Update src/callbacks_step/save_restart.jl
Co-authored-by: Michael Schlottke-Lakemper
* use new function in elixirs and docs
---------
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
---
docs/src/restart.md | 3 +--
examples/p4est_2d_dgsem/elixir_advection_restart.jl | 3 +--
examples/p4est_3d_dgsem/elixir_advection_restart.jl | 3 +--
.../structured_2d_dgsem/elixir_advection_restart.jl | 3 +--
.../structured_3d_dgsem/elixir_advection_restart.jl | 3 +--
examples/tree_2d_dgsem/elixir_advection_restart.jl | 3 +--
examples/tree_3d_dgsem/elixir_advection_restart.jl | 3 +--
.../unstructured_2d_dgsem/elixir_euler_restart.jl | 3 +--
src/Trixi.jl | 2 +-
src/callbacks_step/save_restart.jl | 12 ++++++++++++
10 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/docs/src/restart.md b/docs/src/restart.md
index 767269ff27d..c7cbcd11852 100644
--- a/docs/src/restart.md
+++ b/docs/src/restart.md
@@ -77,8 +77,7 @@ and its time step number, e.g.:
```julia
integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
dt=dt, save_everystep=false, callback=callbacks);
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
```
Now we can compute the solution:
diff --git a/examples/p4est_2d_dgsem/elixir_advection_restart.jl b/examples/p4est_2d_dgsem/elixir_advection_restart.jl
index 79a35199b83..52917616a6a 100644
--- a/examples/p4est_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/p4est_2d_dgsem/elixir_advection_restart.jl
@@ -35,8 +35,7 @@ integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
save_everystep=false, callback=callbacks);
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
diff --git a/examples/p4est_3d_dgsem/elixir_advection_restart.jl b/examples/p4est_3d_dgsem/elixir_advection_restart.jl
index b27eaab62e2..26d10cf8826 100644
--- a/examples/p4est_3d_dgsem/elixir_advection_restart.jl
+++ b/examples/p4est_3d_dgsem/elixir_advection_restart.jl
@@ -32,8 +32,7 @@ integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
save_everystep=false, callback=callbacks);
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
diff --git a/examples/structured_2d_dgsem/elixir_advection_restart.jl b/examples/structured_2d_dgsem/elixir_advection_restart.jl
index 98c44fac71a..82eaa21333a 100644
--- a/examples/structured_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/structured_2d_dgsem/elixir_advection_restart.jl
@@ -34,8 +34,7 @@ integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
save_everystep=false, callback=callbacks);
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
# run the simulation
diff --git a/examples/structured_3d_dgsem/elixir_advection_restart.jl b/examples/structured_3d_dgsem/elixir_advection_restart.jl
index 39d28848c77..921c5310340 100644
--- a/examples/structured_3d_dgsem/elixir_advection_restart.jl
+++ b/examples/structured_3d_dgsem/elixir_advection_restart.jl
@@ -32,8 +32,7 @@ integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
save_everystep=false, callback=callbacks);
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
diff --git a/examples/tree_2d_dgsem/elixir_advection_restart.jl b/examples/tree_2d_dgsem/elixir_advection_restart.jl
index 72efb7d0c84..771ec5aefe7 100644
--- a/examples/tree_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/tree_2d_dgsem/elixir_advection_restart.jl
@@ -32,8 +32,7 @@ integrator = init(ode, alg,
save_everystep=false, callback=callbacks)
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
# run the simulation
diff --git a/examples/tree_3d_dgsem/elixir_advection_restart.jl b/examples/tree_3d_dgsem/elixir_advection_restart.jl
index 3061f165874..b7835ed061f 100644
--- a/examples/tree_3d_dgsem/elixir_advection_restart.jl
+++ b/examples/tree_3d_dgsem/elixir_advection_restart.jl
@@ -31,8 +31,7 @@ integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
save_everystep=false, callback=callbacks);
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
diff --git a/examples/unstructured_2d_dgsem/elixir_euler_restart.jl b/examples/unstructured_2d_dgsem/elixir_euler_restart.jl
index b85cc2c6d70..6653f8662d9 100644
--- a/examples/unstructured_2d_dgsem/elixir_euler_restart.jl
+++ b/examples/unstructured_2d_dgsem/elixir_euler_restart.jl
@@ -33,8 +33,7 @@ integrator = init(ode, CarpenterKennedy2N54(williamson_condition=false),
save_everystep=false, callback=callbacks);
# Get the last time index and work with that.
-integrator.iter = load_timestep(restart_filename)
-integrator.stats.naccept = integrator.iter
+load_timestep!(integrator, restart_filename)
###############################################################################
diff --git a/src/Trixi.jl b/src/Trixi.jl
index ec4d20558e5..be43c45b93d 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -253,7 +253,7 @@ export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback,
GlmSpeedCallback, LBMCollisionCallback, EulerAcousticsCouplingCallback,
TrivialCallback, AnalysisCallbackCoupled
-export load_mesh, load_time, load_timestep, load_dt
+export load_mesh, load_time, load_timestep, load_timestep!, load_dt
export ControllerThreeLevel, ControllerThreeLevelCombined,
IndicatorLöhner, IndicatorLoehner, IndicatorMax,
diff --git a/src/callbacks_step/save_restart.jl b/src/callbacks_step/save_restart.jl
index f567a5c7fda..06817a9b730 100644
--- a/src/callbacks_step/save_restart.jl
+++ b/src/callbacks_step/save_restart.jl
@@ -141,6 +141,18 @@ function load_timestep(restart_file::AbstractString)
end
end
+"""
+ load_timestep!(integrator, restart_file::AbstractString)
+
+Load the time step number saved in a `restart_file` and assign it to both the time step
+number and and the number of accepted steps
+(`iter` and `stats.naccept` in OrdinaryDiffEq.jl, respectively) in `integrator`.
+"""
+function load_timestep!(integrator, restart_file::AbstractString)
+ integrator.iter = load_timestep(restart_file)
+ integrator.stats.naccept = integrator.iter
+end
+
"""
load_dt(restart_file::AbstractString)
From bc6736183271ec191645e9a38f95790a76671d25 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 13 Sep 2023 09:07:41 +0200
Subject: [PATCH 095/263] fix allocations of P4estMesh2D BCs (#1636)
---
src/solvers/dgsem_p4est/dg_2d.jl | 4 ++--
test/test_p4est_2d.jl | 11 ++++++++++-
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl
index 97b931fa325..a665aa4b19d 100644
--- a/src/solvers/dgsem_p4est/dg_2d.jl
+++ b/src/solvers/dgsem_p4est/dg_2d.jl
@@ -275,9 +275,9 @@ function prolong2boundaries!(cache, u,
return nothing
end
-function calc_boundary_flux!(cache, t, boundary_condition, boundary_indexing,
+function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,
mesh::Union{P4estMesh{2}, T8codeMesh{2}},
- equations, surface_integral, dg::DG)
+ equations, surface_integral, dg::DG) where {BC}
@unpack boundaries = cache
@unpack surface_flux_values = cache.elements
index_range = eachnode(dg)
diff --git a/test/test_p4est_2d.jl b/test/test_p4est_2d.jl
index c4ce2619e15..31dfe1d35a5 100644
--- a/test/test_p4est_2d.jl
+++ b/test/test_p4est_2d.jl
@@ -24,7 +24,7 @@ isdir(outdir) && rm(outdir, recursive=true)
l2 = [3.198940059144588e-5],
linf = [0.00030636069494005547])
- # Ensure that we do not have excessive memory allocations
+ # Ensure that we do not have excessive memory allocations
# (e.g., from type instabilities)
let
t = sol.t[end]
@@ -102,6 +102,15 @@ isdir(outdir) && rm(outdir, recursive=true)
l2 = [0.020291447969983396, 0.017479614254319948, 0.011387644425613437, 0.0514420126021293],
linf = [0.3582779022370579, 0.32073537890751663, 0.221818049107692, 0.9209559420400415],
tspan = (0.0, 0.15))
+
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 1000
+ end
end
@trixi_testset "elixir_euler_forward_step_amr.jl" begin
From 547556dafd3c84ae8ed50fc1911e924cb4237468 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Wed, 13 Sep 2023 10:58:33 +0200
Subject: [PATCH 096/263] Avoid slicing (#1637)
Co-authored-by: Hendrik Ranocha
---
examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl | 5 ++++-
examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
index 8111df8251a..b0c6086ad63 100644
--- a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
+++ b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence.jl
@@ -170,7 +170,10 @@ end
initial_condition = initial_condition_navier_stokes_convergence_test
# BC types
-velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3])
+velocity_bc_top_bottom = NoSlip() do x, t, equations
+ u = initial_condition_navier_stokes_convergence_test(x, t, equations)
+ return SVector(u[2], u[3])
+end
heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
diff --git a/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl b/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl
index c426fe95f5b..0109e58dfb3 100644
--- a/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl
+++ b/examples/p4est_3d_dgsem/elixir_navierstokes_convergence.jl
@@ -220,7 +220,10 @@ end
initial_condition = initial_condition_navier_stokes_convergence_test
# BC types
-velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:4])
+velocity_bc_top_bottom = NoSlip() do x, t, equations
+ u = initial_condition_navier_stokes_convergence_test(x, t, equations)
+ return SVector(u[2], u[3], u[4])
+end
heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
From 32d837b0920c3cd9218f448080495c4a29d53566 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Wed, 13 Sep 2023 11:46:52 +0200
Subject: [PATCH 097/263] new tutorial on custom RHS functions and
semidiscretizations (#1633)
* fix list of tutorials
* WIP: tutorial on semidiscretizations
* WIP: tutorial on semidiscretizations
* custom RHS
* custom semidiscretization
* update make.jl script
* WIP: trying to make Literate.jl testsets safeer
* fix
* fix reference
* comment on safe testsets
* some minor fixes
* the SciML ecosystem
* mention package versions
---
docs/literate/make.jl | 17 +-
.../src/files/custom_semidiscretization.jl | 324 ++++++++++++++++++
docs/literate/src/files/index.jl | 24 +-
docs/make.jl | 1 +
docs/src/callbacks.md | 4 +-
docs/src/overview.md | 2 +-
docs/src/parallelization.md | 6 +-
docs/src/performance.md | 2 +-
8 files changed, 366 insertions(+), 14 deletions(-)
create mode 100644 docs/literate/src/files/custom_semidiscretization.jl
diff --git a/docs/literate/make.jl b/docs/literate/make.jl
index b620f85c975..a04d8a0b333 100644
--- a/docs/literate/make.jl
+++ b/docs/literate/make.jl
@@ -51,12 +51,25 @@ function create_tutorials(files)
# Run tests on all tutorial files
@testset "TrixiTutorials" begin
for (i, (title, filename)) in enumerate(files)
+ # Evaluate each tutorial in its own module to avoid leaking of
+ # function/variable names, polluting the namespace of later tutorials
+ # by stuff defined in earlier tutorials.
if filename isa Vector # Several files of one topic
for j in eachindex(filename)
- @testset "$(filename[j][2][2])" begin include(joinpath(repo_src, filename[j][2][1], filename[j][2][2])) end
+ mod = gensym(filename[j][2][2])
+ @testset "$(filename[j][2][2])" begin
+ @eval module $mod
+ include(joinpath($repo_src, $(filename[j][2][1]), $(filename[j][2][2])))
+ end
+ end
end
else # Single files
- @testset "$title" begin include(joinpath(repo_src, filename)) end
+ mod = gensym(title)
+ @testset "$title" begin
+ @eval module $mod
+ include(joinpath($repo_src, $filename))
+ end
+ end
end
end
end
diff --git a/docs/literate/src/files/custom_semidiscretization.jl b/docs/literate/src/files/custom_semidiscretization.jl
new file mode 100644
index 00000000000..fd432fb0826
--- /dev/null
+++ b/docs/literate/src/files/custom_semidiscretization.jl
@@ -0,0 +1,324 @@
+#src # Custom semidiscretizations
+
+# As described in the [overview section](@ref overview-semidiscretizations),
+# semidiscretizations are high-level descriptions of spatial discretizations
+# in Trixi.jl. Trixi.jl's main focus is on hyperbolic conservation
+# laws represented in a [`SemidiscretizationHyperbolic`](@ref).
+# Hyperbolic-parabolic problems based on the advection-diffusion equation or
+# the compressible Navier-Stokes equations can be represented in a
+# [`SemidiscretizationHyperbolicParabolic`](@ref). This is described in the
+# [basic tutorial on parabolic terms](@ref parabolic_terms) and its extension to
+# [custom parabolic terms](@ref adding_new_parabolic_terms).
+# In this tutorial, we will describe how these semidiscretizations work and how
+# they can be used to create custom semidiscretizations involving also other tasks.
+
+
+# ## Overview of the right-hand side evaluation
+
+# The semidiscretizations provided by Trixi.jl are set up to create `ODEProblem`s from the
+# [SciML ecosystem for ordinary differential equations](https://diffeq.sciml.ai/latest/).
+# In particular, a spatial semidiscretization can be wrapped in an ODE problem
+# using [`semidiscretize`](@ref), which returns an `ODEProblem`. This `ODEProblem`
+# bundles an initial condition, a right-hand side (RHS) function, the time span,
+# and possible parameters. The `ODEProblem`s created by Trixi.jl use the semidiscretization
+# passed to [`semidiscretize`](@ref) as a parameter.
+# For a [`SemidiscretizationHyperbolic`](@ref), the `ODEProblem` wraps
+# `Trixi.rhs!` as ODE RHS.
+# For a [`SemidiscretizationHyperbolicParabolic`](@ref), Trixi.jl
+# uses a `SplitODEProblem` combining `Trixi.rhs_parabolic!` for the
+# (potentially) stiff part and `Trixi.rhs!` for the other part.
+
+
+# ## Standard Trixi.jl setup
+
+# In this tutorial, we will consider the linear advection equation
+# with source term
+# ```math
+# \partial_t u(t,x) + \partial_x u(t,x) = -\exp(-t) \sin\bigl(\pi (x - t) \bigr)
+# ```
+# with periodic boundary conditions in the domain `[-1, 1]` as a
+# model problem.
+# The initial condition is
+# ```math
+# u(0,x) = \sin(\pi x).
+# ```
+# The source term results in some damping and the analytical solution
+# ```math
+# u(t,x) = \exp(-t) \sin\bigl(\pi (x - t) \bigr).
+# ```
+# First, we discretize this equation using the standard functionality
+# of Trixi.jl.
+
+using Trixi, OrdinaryDiffEq, Plots
+
+# The linear scalar advection equation is already implemented in
+# Trixi.jl as [`LinearScalarAdvectionEquation1D`](@ref). We construct
+# it with an advection velocity `1.0`.
+
+equations = LinearScalarAdvectionEquation1D(1.0)
+
+# Next, we use a standard [`DGSEM`](@ref) solver.
+
+solver = DGSEM(polydeg = 3)
+
+# We create a simple [`TreeMesh`](@ref) in 1D.
+
+coordinates_min = (-1.0,)
+coordinates_max = (+1.0,)
+mesh = TreeMesh(coordinates_min, coordinates_max;
+ initial_refinement_level = 4,
+ n_cells_max = 10^4,
+ periodicity = true)
+
+# We wrap everything in in a semidiscretization and pass the source
+# terms as a standard Julia function. Please note that Trixi.jl uses
+# `SVector`s from
+# [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl)
+# to store the conserved variables `u`. Thus, the return value of the
+# source terms must be wrapped in an `SVector` - even if we consider
+# just a scalar problem.
+
+function initial_condition(x, t, equations)
+ return SVector(exp(-t) * sinpi(x[1] - t))
+end
+
+function source_terms_standard(u, x, t, equations)
+ return -initial_condition(x, t, equations)
+end
+
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition,
+ solver;
+ source_terms = source_terms_standard)
+
+# Now, we can create the `ODEProblem`, solve the resulting ODE
+# using a time integration method from
+# [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl),
+# and visualize the numerical solution at the final time using
+# [Plots.jl](https://github.com/JuliaPlots/Plots.jl).
+
+tspan = (0.0, 3.0)
+ode = semidiscretize(semi, tspan)
+
+sol = solve(ode, RDPK3SpFSAL49(); ode_default_options()...)
+
+plot(sol; label = "numerical sol.", legend = :topright)
+
+# We can also plot the analytical solution for comparison.
+# Since Trixi.jl uses `SVector`s for the variables, we take their `first`
+# (and only) component to get the scalar value for manual plotting.
+
+let
+ x = range(-1.0, 1.0; length = 200)
+ plot!(x, first.(initial_condition.(x, sol.t[end], equations)),
+ label = "analytical sol.", linestyle = :dash, legend = :topright)
+end
+
+# We can also add the initial condition to the plot.
+
+plot!(sol.u[1], semi, label = "u0", linestyle = :dot, legend = :topleft)
+
+# You can of course also use some
+# [callbacks](https://trixi-framework.github.io/Trixi.jl/stable/callbacks/)
+# provided by Trixi.jl as usual.
+
+summary_callback = SummaryCallback()
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi; interval = analysis_interval)
+alive_callback = AliveCallback(; analysis_interval)
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback)
+
+sol = solve(ode, RDPK3SpFSAL49();
+ ode_default_options()..., callback = callbacks)
+summary_callback()
+
+
+# ## Using a custom ODE right-hand side function
+
+# Next, we will solve the same problem but use our own ODE RHS function.
+# To demonstrate this, we will artificially create a global variable
+# containing the current time of the simulation.
+
+const GLOBAL_TIME = Ref(0.0)
+
+function source_terms_custom(u, x, t, equations)
+ t = GLOBAL_TIME[]
+ return -initial_condition(x, t, equations)
+end
+
+# Next, we create our own RHS function to update the global time of
+# the simulation before calling the RHS function from Trixi.jl.
+
+function rhs_source_custom!(du_ode, u_ode, semi, t)
+ GLOBAL_TIME[] = t
+ Trixi.rhs!(du_ode, u_ode, semi, t)
+end
+
+# Next, we create an `ODEProblem` manually copying over the data from
+# the one we got from [`semidiscretize`](@ref) earlier.
+
+ode_source_custom = ODEProblem(rhs_source_custom!,
+ ode.u0,
+ ode.tspan,
+ ode.p #= semi =#)
+sol_source_custom = solve(ode_source_custom, RDPK3SpFSAL49();
+ ode_default_options()...)
+
+plot(sol_source_custom; label = "numerical sol.")
+let
+ x = range(-1.0, 1.0; length = 200)
+ plot!(x, first.(initial_condition.(x, sol_source_custom.t[end], equations)),
+ label = "analytical sol.", linestyle = :dash, legend = :topleft)
+end
+plot!(sol_source_custom.u[1], semi, label = "u0", linestyle = :dot, legend = :topleft)
+
+# This also works with callbacks as usual.
+
+summary_callback = SummaryCallback()
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi; interval = analysis_interval)
+alive_callback = AliveCallback(; analysis_interval)
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback)
+
+sol = solve(ode_source_custom, RDPK3SpFSAL49();
+ ode_default_options()..., callback = callbacks)
+summary_callback()
+
+
+# ## Setting up a custom semidiscretization
+
+# Using a global constant is of course not really nice from a software
+# engineering point of view. Thus, it can often be useful to collect
+# additional data in the parameters of the `ODEProblem`. Thus, it is
+# time to create our own semidiscretization. Here, we create a small
+# wrapper of a standard semidiscretization of Trixi.jl and the current
+# global time of the simulation.
+
+struct CustomSemidiscretization{Semi, T} <: Trixi.AbstractSemidiscretization
+ semi::Semi
+ t::T
+end
+
+semi_custom = CustomSemidiscretization(semi, Ref(0.0))
+
+# To get pretty printing in the REPL, you can consider specializing
+#
+# - `Base.show(io::IO, parameters::CustomSemidiscretization)`
+# - `Base.show(io::IO, ::MIME"text/plain", parameters::CustomSemidiscretization)`
+#
+# for your custom semidiscretiation.
+
+# Next, we create our own source terms that use the global time stored
+# in the custom semidiscretiation.
+
+source_terms_custom_semi = let semi_custom = semi_custom
+ function source_terms_custom_semi(u, x, t, equations)
+ t = semi_custom.t[]
+ return -initial_condition(x, t, equations)
+ end
+end
+
+# We also create a custom ODE RHS to update the current global time
+# stored in the custom semidiscretization. We unpack the standard
+# semidiscretization created by Trixi.jl and pass it to `Trixi.rhs!`.
+
+function rhs_semi_custom!(du_ode, u_ode, semi_custom, t)
+ semi_custom.t[] = t
+ Trixi.rhs!(du_ode, u_ode, semi_custom.semi, t)
+end
+
+# Finally, we set up an `ODEProblem` and solve it numerically.
+
+ode_semi_custom = ODEProblem(rhs_semi_custom!,
+ ode.u0,
+ ode.tspan,
+ semi_custom)
+sol_semi_custom = solve(ode_semi_custom, RDPK3SpFSAL49();
+ ode_default_options()...)
+
+# If we want to make use of additional functionality provided by
+# Trixi.jl, e.g., for plotting, we need to implement a few additional
+# specializations. In this case, we forward everything to the standard
+# semidiscretization provided by Trixi.jl wrapped in our custom
+# semidiscretization.
+
+Base.ndims(semi::CustomSemidiscretization) = ndims(semi.semi)
+function Trixi.mesh_equations_solver_cache(semi::CustomSemidiscretization)
+ Trixi.mesh_equations_solver_cache(semi.semi)
+end
+
+# Now, we can plot the numerical solution as usual.
+
+plot(sol_semi_custom; label = "numerical sol.")
+let
+ x = range(-1.0, 1.0; length = 200)
+ plot!(x, first.(initial_condition.(x, sol_semi_custom.t[end], equations)),
+ label = "analytical sol.", linestyle = :dash, legend = :topleft)
+end
+plot!(sol_semi_custom.u[1], semi, label = "u0", linestyle = :dot, legend = :topleft)
+
+# This also works with many callbacks as usual. However, the
+# [`AnalysisCallback`](@ref) requires some special handling since it
+# makes use of a performance counter contained in the standard
+# semidiscretizations of Trixi.jl to report some
+# [performance metrics](@ref performance-metrics).
+# Here, we forward all accesses to the performance counter to the
+# wrapped semidiscretization.
+
+function Base.getproperty(semi::CustomSemidiscretization, s::Symbol)
+ if s === :performance_counter
+ wrapped_semi = getfield(semi, :semi)
+ wrapped_semi.performance_counter
+ else
+ getfield(semi, s)
+ end
+end
+
+# Moreover, the [`AnalysisCallback`](@ref) also performs some error
+# calculations. We also need to forward them to the wrapped
+# semidiscretization.
+
+function Trixi.calc_error_norms(func, u, t, analyzer,
+ semi::CustomSemidiscretization,
+ cache_analysis)
+ Trixi.calc_error_norms(func, u, t, analyzer,
+ semi.semi,
+ cache_analysis)
+end
+
+# Now, we can work with the callbacks used before as usual.
+
+summary_callback = SummaryCallback()
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi_custom;
+ interval = analysis_interval)
+alive_callback = AliveCallback(; analysis_interval)
+callbacks = CallbackSet(summary_callback,
+ analysis_callback,
+ alive_callback)
+
+sol = solve(ode_semi_custom, RDPK3SpFSAL49();
+ ode_default_options()..., callback = callbacks)
+summary_callback()
+
+# For even more advanced usage of custom semidiscretizations, you
+# may look at the source code of the ones contained in Trixi.jl, e.g.,
+# - [`SemidiscretizationHyperbolicParabolic`](@ref)
+# - [`SemidiscretizationEulerGravity`](@ref)
+# - [`SemidiscretizationEulerAcoustics`](@ref)
+# - [`SemidiscretizationCoupled`](@ref)
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/index.jl b/docs/literate/src/files/index.jl
index 0c8de66bf42..d42695611f6 100644
--- a/docs/literate/src/files/index.jl
+++ b/docs/literate/src/files/index.jl
@@ -76,21 +76,30 @@
# In this part, another physics model is implemented, the nonconservative linear advection equation.
# We run two different simulations with different levels of refinement and compare the resulting errors.
-# ### [10 Adaptive mesh refinement](@ref adaptive_mesh_refinement)
+# ### [10 Parabolic terms](@ref parabolic_terms)
+#-
+# This tutorial describes how parabolic terms are implemented in Trixi.jl, e.g.,
+# to solve the advection-diffusion equation.
+
+# ### [11 Adding new parabolic terms](@ref adding_new_parabolic_terms)
+#-
+# This tutorial describes how new parabolic terms can be implemented using Trixi.jl.
+
+# ### [12 Adaptive mesh refinement](@ref adaptive_mesh_refinement)
#-
# Adaptive mesh refinement (AMR) helps to increase the accuracy in sensitive or turbolent regions while
# not wasting resources for less interesting parts of the domain. This leads to much more efficient
# simulations. This tutorial presents the implementation strategy of AMR in Trixi.jl, including the use of
# different indicators and controllers.
-# ### [11 Structured mesh with curvilinear mapping](@ref structured_mesh_mapping)
+# ### [13 Structured mesh with curvilinear mapping](@ref structured_mesh_mapping)
#-
# In this tutorial, the use of Trixi.jl's structured curved mesh type [`StructuredMesh`](@ref) is explained.
# We present the two basic option to initialize such a mesh. First, the curved domain boundaries
# of a circular cylinder are set by explicit boundary functions. Then, a fully curved mesh is
# defined by passing the transformation mapping.
-# ### [12 Unstructured meshes with HOHQMesh.jl](@ref hohqmesh_tutorial)
+# ### [14 Unstructured meshes with HOHQMesh.jl](@ref hohqmesh_tutorial)
#-
# The purpose of this tutorial is to demonstrate how to use the [`UnstructuredMesh2D`](@ref)
# functionality of Trixi.jl. This begins by running and visualizing an available unstructured
@@ -99,19 +108,24 @@
# software in the Trixi.jl ecosystem, and then run a simulation using Trixi.jl on said mesh.
# In the end, the tutorial briefly explains how to simulate an example using AMR via `P4estMesh`.
-# ### [13 Explicit time stepping](@ref time_stepping)
+# ### [15 Explicit time stepping](@ref time_stepping)
#-
# This tutorial is about time integration using [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl).
# It explains how to use their algorithms and presents two types of time step choices - with error-based
# and CFL-based adaptive step size control.
-# ### [14 Differentiable programming](@ref differentiable_programming)
+# ### [16 Differentiable programming](@ref differentiable_programming)
#-
# This part deals with some basic differentiable programming topics. For example, a Jacobian, its
# eigenvalues and a curve of total energy (through the simulation) are calculated and plotted for
# a few semidiscretizations. Moreover, we calculate an example for propagating errors with Measurement.jl
# at the end.
+# ### [17 Custom semidiscretization](@ref custom_semidiscretization)
+#-
+# This tutorial describes the [semidiscretiations](@ref overview-semidiscretizations) of Trixi.jl
+# and explains how to extend them for custom tasks.
+
# ## Examples in Trixi.jl
diff --git a/docs/make.jl b/docs/make.jl
index 57629577ddb..f882fcf1219 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -68,6 +68,7 @@ files = [
# Topic: other stuff
"Explicit time stepping" => "time_stepping.jl",
"Differentiable programming" => "differentiable_programming.jl",
+ "Custom semidiscretizations" => "custom_semidiscretization.jl"
]
tutorials = create_tutorials(files)
diff --git a/docs/src/callbacks.md b/docs/src/callbacks.md
index 1d3e5e34b51..7f44dfd5925 100644
--- a/docs/src/callbacks.md
+++ b/docs/src/callbacks.md
@@ -30,7 +30,7 @@ An example elixir using AMR can be found at [`examples/tree_2d_dgsem/elixir_adve
The [`AnalysisCallback`](@ref) can be used to analyze the numerical solution, e.g. calculate
errors or user-specified integrals, and print the results to the screen. The results can also be
saved in a file. An example can be found at [`examples/tree_2d_dgsem/elixir_euler_vortex.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_vortex.jl).
-In [Performance metrics of the `AnalysisCallback`](@ref) you can find a detailed
+In [Performance metrics of the `AnalysisCallback`](@ref performance-metrics) you can find a detailed
description of the different performance metrics the `AnalysisCallback` computes.
### I/O
@@ -106,7 +106,7 @@ will yield the following plot:
the automated performance measurements, including an output of the recorded timers after a simulation.
* The [`VisualizationCallback`](@ref) can be used for in-situ visualization. See
[Visualizing results during a simulation](@ref).
-* The [`TrivialCallback`](@ref) does nothing and can be used to to easily disable some callbacks
+* The [`TrivialCallback`](@ref) does nothing and can be used to easily disable some callbacks
via [`trixi_include`](@ref).
### Equation-specific callbacks
diff --git a/docs/src/overview.md b/docs/src/overview.md
index 46bc28b6025..51a6272ae8e 100644
--- a/docs/src/overview.md
+++ b/docs/src/overview.md
@@ -16,7 +16,7 @@ to solve a PDE numerically are the spatial semidiscretization and the time
integration scheme.
-## Semidiscretizations
+## [Semidiscretizations](@id overview-semidiscretizations)
Semidiscretizations are high-level descriptions of spatial discretizations
specialized for certain PDEs. Trixi.jl's main focus is on hyperbolic conservation
diff --git a/docs/src/parallelization.md b/docs/src/parallelization.md
index d56777c9af4..e55471bb256 100644
--- a/docs/src/parallelization.md
+++ b/docs/src/parallelization.md
@@ -22,7 +22,7 @@ julia --threads=4
If both the environment variable and the command line argument are specified at
the same time, the latter takes precedence.
-If you use time integration methods from
+If you use time integration methods from
[OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl)
and want to use multiple threads therein, you need to set the keyword argument
`thread=OrdinaryDiffEq.True()` of the algorithms, as described in the
@@ -143,7 +143,7 @@ To start Trixi.jl in parallel with MPI, there are three options:
Switching between panes can be done by `Ctrl+b` followed by `o`.
As of March 2022, newer versions of tmpi also support mpich, which is the default
backend of MPI.jl (via MPICH_Jll.jl). To use this setup, you need to install
- `mpiexecjl` as described in the
+ `mpiexecjl` as described in the
[documentation of MPI.jl](https://juliaparallel.org/MPI.jl/v0.20/usage/#Julia-wrapper-for-mpiexec)
and make it available as `mpirun`, e.g., via a symlink of the form
```bash
@@ -161,7 +161,7 @@ To start Trixi.jl in parallel with MPI, there are three options:
### [Performance](@id parallel_performance)
For information on how to evaluate the parallel performance of Trixi.jl, please
-have a look at the [Performance metrics of the `AnalysisCallback`](@ref)
+have a look at the [Performance metrics of the `AnalysisCallback`](@ref performance-metrics)
section, specifically at the descriptions of the performance index (PID).
diff --git a/docs/src/performance.md b/docs/src/performance.md
index 428672ec75f..bbe3a3390b7 100644
--- a/docs/src/performance.md
+++ b/docs/src/performance.md
@@ -170,7 +170,7 @@ As a rule of thumb:
- Consider using `@nospecialize` for methods like custom implementations of `Base.show`.
-## Performance metrics of the `AnalysisCallback`
+## [Performance metrics of the `AnalysisCallback`](@id performance-metrics)
The [`AnalysisCallback`](@ref) computes two performance indicators that you can use to
evaluate the serial and parallel performance of Trixi.jl. They represent
measured run times that are normalized by the number of `rhs!` evaluations and
From b942775af0677ac83d77934c2326ab2b5db5ba77 Mon Sep 17 00:00:00 2001
From: Krissh Chawla <127906314+KrisshChawla@users.noreply.github.com>
Date: Wed, 13 Sep 2023 20:08:08 -0500
Subject: [PATCH 098/263] Adding quasi 1d shallow water equations (#1619)
* implementation of quasi shallow water equations 1d.
* added example elixer for shallow_water_quasi_1d
* changed the names of Quasi1d equations
* including and exported ShallowWaterEquationsQuasi1D
* exporting flux_chan_etal and flux_chan_nonconservative_etal
* minor comment fix
* adding tests
* Apply suggestions from code review
* Apply suggestions from code review
* Update src/equations/shallow_water_quasi_1d.jl
* formatting
* formatting
* forgot comma
* Apply suggestions from code review
Co-authored-by: Hendrik Ranocha
* renamed example elixir to elixir_shallow_water_quasi_1d_source_terms.jl
* Apply suggestions from code review
Co-authored-by: Andrew Winters
* Update test_tree_1d_shallowwater.jl with renamed example elixir
* comment fix
* comment fix for elixir_shallow_water_quasi_1d_source_terms.jl
* Added well-balancedness test for shallow_water_quasi_1d
The initial condition in the elixir is intended to test a discontinuous channel width 'a(x)' and bottom topography 'b(x)' on a periodic mesh.
* Added 'max_abs_speeds' function and 'lake_at_rest_error'
* Updated test_tree_1d_shallowwater with quasi well-balancedness test
* File name fix in test_tree_1d_shallowwater
* Update examples/tree_1d_dgsem/elixir_shallowwater_quasi1d_well_balanced.jl
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
* Renamed to "elixir_shallowwater_quasi_1d_well_balanced.jl"
---------
Co-authored-by: Jesse Chan
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Co-authored-by: Hendrik Ranocha
Co-authored-by: Andrew Winters
---
...xir_shallow_water_quasi_1d_source_terms.jl | 60 ++++
...xir_shallowwater_quasi_1d_well_balanced.jl | 84 +++++
src/Trixi.jl | 2 +
src/equations/equations.jl | 1 +
src/equations/shallow_water_quasi_1d.jl | 323 ++++++++++++++++++
test/test_tree_1d_shallowwater.jl | 14 +
6 files changed, 484 insertions(+)
create mode 100644 examples/tree_1d_dgsem/elixir_shallow_water_quasi_1d_source_terms.jl
create mode 100644 examples/tree_1d_dgsem/elixir_shallowwater_quasi_1d_well_balanced.jl
create mode 100644 src/equations/shallow_water_quasi_1d.jl
diff --git a/examples/tree_1d_dgsem/elixir_shallow_water_quasi_1d_source_terms.jl b/examples/tree_1d_dgsem/elixir_shallow_water_quasi_1d_source_terms.jl
new file mode 100644
index 00000000000..72747c669e2
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_shallow_water_quasi_1d_source_terms.jl
@@ -0,0 +1,60 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# Semidiscretization of the quasi 1d shallow water equations
+# See Chan et al. https://doi.org/10.48550/arXiv.2307.12089 for details
+
+equations = ShallowWaterEquationsQuasi1D(gravity_constant = 9.81)
+
+initial_condition = initial_condition_convergence_test
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_chan_etal, flux_nonconservative_chan_etal)
+surface_flux = (FluxPlusDissipation(flux_chan_etal, DissipationLocalLaxFriedrichs()),
+ flux_nonconservative_chan_etal)
+solver = DGSEM(polydeg = 3, surface_flux = surface_flux,
+ volume_integral = VolumeIntegralFluxDifferencing(volume_flux))
+
+###############################################################################
+# Get the TreeMesh and setup a periodic mesh
+
+coordinates_min = 0.0
+coordinates_max = sqrt(2.0)
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level = 3,
+ n_cells_max = 10_000,
+ periodicity = true)
+
+# create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
+ source_terms = source_terms_convergence_test)
+
+###############################################################################
+# ODE solvers, callbacks etc.
+
+tspan = (0.0, 1.0)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 500
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+save_solution = SaveSolutionCallback(interval = 200,
+ save_initial_solution = true,
+ save_final_solution = true)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution)
+
+###############################################################################
+# run the simulation
+
+# use a Runge-Kutta method with automatic (error based) time step size control
+sol = solve(ode, RDPK3SpFSAL49(); abstol = 1.0e-8, reltol = 1.0e-8,
+ ode_default_options()..., callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/examples/tree_1d_dgsem/elixir_shallowwater_quasi_1d_well_balanced.jl b/examples/tree_1d_dgsem/elixir_shallowwater_quasi_1d_well_balanced.jl
new file mode 100644
index 00000000000..d9f1a52b500
--- /dev/null
+++ b/examples/tree_1d_dgsem/elixir_shallowwater_quasi_1d_well_balanced.jl
@@ -0,0 +1,84 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the shallow water equations with a discontinuous
+# bottom topography function and channel width function
+
+equations = ShallowWaterEquationsQuasi1D(gravity_constant = 9.81, H0 = 2.0)
+
+# Setup a truly discontinuous bottom topography function and channel width for
+# this academic testcase of well-balancedness. The errors from the analysis
+# callback are not important but the error for this lake-at-rest test case
+# `∑|H0-(h+b)|` should be around machine roundoff.
+# Works as intended for TreeMesh1D with `initial_refinement_level=3`. If the mesh
+# refinement level is changed the initial condition below may need changed as well to
+# ensure that the discontinuities lie on an element interface.
+function initial_condition_discontinuous_well_balancedness(x, t,
+ equations::ShallowWaterEquationsQuasi1D)
+ H = equations.H0
+ v = 0.0
+
+ # for a periodic domain, this choice of `b` and `a` mimic
+ # discontinuity across the periodic boundary.
+ b = 0.5 * (x[1] + 1)
+ a = 2 + x[1]
+
+ return prim2cons(SVector(H, v, b, a), equations)
+end
+
+initial_condition = initial_condition_discontinuous_well_balancedness
+
+###############################################################################
+# Get the DG approximation space
+
+volume_flux = (flux_chan_etal, flux_nonconservative_chan_etal)
+surface_flux = volume_flux
+solver = DGSEM(polydeg = 4, surface_flux = surface_flux,
+ volume_integral = VolumeIntegralFluxDifferencing(volume_flux))
+
+###############################################################################
+# Get the TreeMesh and setup a periodic mesh
+
+coordinates_min = -1.0
+coordinates_max = 1.0
+mesh = TreeMesh(coordinates_min, coordinates_max,
+ initial_refinement_level = 3,
+ n_cells_max = 10_000)
+
+# Create the semi discretization object
+semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
+
+###############################################################################
+# ODE solver
+
+tspan = (0.0, 100.0)
+ode = semidiscretize(semi, tspan)
+
+###############################################################################
+# Callbacks
+
+summary_callback = SummaryCallback()
+
+analysis_interval = 1000
+analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
+ extra_analysis_integrals = (lake_at_rest_error,))
+
+alive_callback = AliveCallback(analysis_interval = analysis_interval)
+
+save_solution = SaveSolutionCallback(interval = 1000,
+ save_initial_solution = true,
+ save_final_solution = true)
+
+stepsize_callback = StepsizeCallback(cfl = 3.0)
+
+callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, save_solution,
+ stepsize_callback)
+
+###############################################################################
+# run the simulation
+
+sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
+ dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
+ save_everystep = false, callback = callbacks);
+summary_callback() # print the timer summary
diff --git a/src/Trixi.jl b/src/Trixi.jl
index be43c45b93d..c883c3bf19f 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -149,6 +149,7 @@ export AcousticPerturbationEquations2D,
LatticeBoltzmannEquations2D, LatticeBoltzmannEquations3D,
ShallowWaterEquations1D, ShallowWaterEquations2D,
ShallowWaterTwoLayerEquations1D, ShallowWaterTwoLayerEquations2D,
+ ShallowWaterEquationsQuasi1D,
LinearizedEulerEquations2D
export LaplaceDiffusion1D, LaplaceDiffusion2D,
@@ -164,6 +165,7 @@ export flux, flux_central, flux_lax_friedrichs, flux_hll, flux_hllc, flux_hlle,
flux_kennedy_gruber, flux_shima_etal, flux_ec,
flux_fjordholm_etal, flux_nonconservative_fjordholm_etal, flux_es_fjordholm_etal,
flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal,
+ flux_chan_etal, flux_nonconservative_chan_etal,
hydrostatic_reconstruction_audusse_etal, flux_nonconservative_audusse_etal,
# TODO: TrixiShallowWater: move anything with "chen_noelle" to new file
hydrostatic_reconstruction_chen_noelle, flux_nonconservative_chen_noelle,
diff --git a/src/equations/equations.jl b/src/equations/equations.jl
index 570a25cece9..9bae563d85f 100644
--- a/src/equations/equations.jl
+++ b/src/equations/equations.jl
@@ -356,6 +356,7 @@ include("shallow_water_1d.jl")
include("shallow_water_2d.jl")
include("shallow_water_two_layer_1d.jl")
include("shallow_water_two_layer_2d.jl")
+include("shallow_water_quasi_1d.jl")
# CompressibleEulerEquations
abstract type AbstractCompressibleEulerEquations{NDIMS, NVARS} <:
diff --git a/src/equations/shallow_water_quasi_1d.jl b/src/equations/shallow_water_quasi_1d.jl
new file mode 100644
index 00000000000..217a764e173
--- /dev/null
+++ b/src/equations/shallow_water_quasi_1d.jl
@@ -0,0 +1,323 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
+@doc raw"""
+ ShallowWaterEquationsQuasi1D(; gravity, H0 = 0, threshold_limiter = nothing threshold_wet = nothing)
+
+The quasi-1D shallow water equations (SWE). The equations are given by
+```math
+\begin{aligned}
+ \frac{\partial}{\partial t}(a h) + \frac{\partial}{\partial x}(a h v) &= 0 \\
+ \frac{\partial}{\partial t}(a h v) + \frac{\partial}{\partial x}(a h v^2)
+ + g a h \frac{\partial}{\partial x}(h + b) &= 0
+\end{aligned}
+```
+The unknown quantities of the Quasi-1D SWE are the water height ``h`` and the scaled velocity ``v``.
+The gravitational constant is denoted by `g`, the (possibly) variable bottom topography function ``b(x)``, and (possibly) variable channel width ``a(x)``. The water height ``h`` is measured from the bottom topography ``b``, therefore one also defines the total water height as ``H = h + b``.
+
+The additional quantity ``H_0`` is also available to store a reference value for the total water height that
+is useful to set initial conditions or test the "lake-at-rest" well-balancedness.
+
+Also, there are two thresholds which prevent numerical problems as well as instabilities. Both of them do not
+have to be passed, as default values are defined within the struct. The first one, `threshold_limiter`, is
+used in [`PositivityPreservingLimiterShallowWater`](@ref) on the water height, as a (small) shift on the initial
+condition and cutoff before the next time step. The second one, `threshold_wet`, is applied on the water height to
+define when the flow is "wet" before calculating the numerical flux.
+
+The bottom topography function ``b(x)`` and channel width ``a(x)`` are set inside the initial condition routine
+for a particular problem setup. To test the conservative form of the SWE one can set the bottom topography
+variable `b` to zero and ``a`` to one.
+
+In addition to the unknowns, Trixi.jl currently stores the bottom topography and channel width values at the approximation points
+despite being fixed in time. This is done for convenience of computing the bottom topography gradients
+on the fly during the approximation as well as computing auxiliary quantities like the total water height ``H``
+or the entropy variables.
+This affects the implementation and use of these equations in various ways:
+* The flux values corresponding to the bottom topography and channel width must be zero.
+* The bottom topography and channel width values must be included when defining initial conditions, boundary conditions or
+ source terms.
+* [`AnalysisCallback`](@ref) analyzes this variable.
+* Trixi.jl's visualization tools will visualize the bottom topography and channel width by default.
+"""
+struct ShallowWaterEquationsQuasi1D{RealT <: Real} <:
+ AbstractShallowWaterEquations{1, 4}
+ gravity::RealT # gravitational constant
+ H0::RealT # constant "lake-at-rest" total water height
+ # `threshold_limiter` used in `PositivityPreservingLimiterShallowWater` on water height,
+ # as a (small) shift on the initial condition and cutoff before the next time step.
+ # Default is 500*eps() which in double precision is ≈1e-13.
+ threshold_limiter::RealT
+ # `threshold_wet` applied on water height to define when the flow is "wet"
+ # before calculating the numerical flux.
+ # Default is 5*eps() which in double precision is ≈1e-15.
+ threshold_wet::RealT
+end
+
+# Allow for flexibility to set the gravitational constant within an elixir depending on the
+# application where `gravity_constant=1.0` or `gravity_constant=9.81` are common values.
+# The reference total water height H0 defaults to 0.0 but is used for the "lake-at-rest"
+# well-balancedness test cases.
+# Strict default values for thresholds that performed well in many numerical experiments
+function ShallowWaterEquationsQuasi1D(; gravity_constant, H0 = zero(gravity_constant),
+ threshold_limiter = nothing,
+ threshold_wet = nothing)
+ T = promote_type(typeof(gravity_constant), typeof(H0))
+ if threshold_limiter === nothing
+ threshold_limiter = 500 * eps(T)
+ end
+ if threshold_wet === nothing
+ threshold_wet = 5 * eps(T)
+ end
+ ShallowWaterEquationsQuasi1D(gravity_constant, H0, threshold_limiter, threshold_wet)
+end
+
+have_nonconservative_terms(::ShallowWaterEquationsQuasi1D) = True()
+function varnames(::typeof(cons2cons), ::ShallowWaterEquationsQuasi1D)
+ ("a_h", "a_h_v", "b", "a")
+end
+# Note, we use the total water height, H = h + b, as the first primitive variable for easier
+# visualization and setting initial conditions
+varnames(::typeof(cons2prim), ::ShallowWaterEquationsQuasi1D) = ("H", "v", "b", "a")
+
+# Set initial conditions at physical location `x` for time `t`
+"""
+ initial_condition_convergence_test(x, t, equations::ShallowWaterEquationsQuasi1D)
+
+A smooth initial condition used for convergence tests in combination with
+[`source_terms_convergence_test`](@ref)
+(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).
+"""
+function initial_condition_convergence_test(x, t,
+ equations::ShallowWaterEquationsQuasi1D)
+ # generates a manufactured solution.
+ # some constants are chosen such that the function is periodic on the domain [0,sqrt(2)]
+ Omega = sqrt(2) * pi
+ H = 2.0 + 0.5 * sin(Omega * x[1] - t)
+ v = 0.25
+ b = 0.2 - 0.05 * sin(Omega * x[1])
+ a = 1 + 0.1 * cos(Omega * x[1])
+ return prim2cons(SVector(H, v, b, a), equations)
+end
+
+"""
+ source_terms_convergence_test(u, x, t, equations::ShallowWaterEquationsQuasi1D)
+
+Source terms used for convergence tests in combination with
+[`initial_condition_convergence_test`](@ref)
+(and [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref) in non-periodic domains).
+
+This manufactured solution source term is specifically designed for the bottom topography function
+`b(x) = 0.2 - 0.05 * sin(sqrt(2) * pi *x[1])` and channel width 'a(x)= 1 + 0.1 * cos(sqrt(2) * pi * x[1])'
+as defined in [`initial_condition_convergence_test`](@ref).
+"""
+@inline function source_terms_convergence_test(u, x, t,
+ equations::ShallowWaterEquationsQuasi1D)
+ # Same settings as in `initial_condition_convergence_test`. Some derivative simplify because
+ # this manufactured solution velocity is taken to be constant
+ Omega = sqrt(2) * pi
+ H = 2.0 + 0.5 * sin(Omega * x[1] - t)
+ H_x = 0.5 * cos(Omega * x[1] - t) * Omega
+ H_t = -0.5 * cos(Omega * x[1] - t)
+
+ v = 0.25
+
+ b = 0.2 - 0.05 * sin(Omega * x[1])
+ b_x = -0.05 * cos(Omega * x[1]) * Omega
+
+ a = 1 + 0.1 * cos(Omega * x[1])
+ a_x = -0.1 * sin(Omega * x[1]) * Omega
+
+ du1 = a * H_t + v * (a_x * (H - b) + a * (H_x - b_x))
+ du2 = v * du1 + a * (equations.gravity * (H - b) * H_x)
+
+ return SVector(du1, du2, 0.0, 0.0)
+end
+
+# Calculate 1D flux for a single point
+# Note, the bottom topography and channel width have no flux
+@inline function flux(u, orientation::Integer, equations::ShallowWaterEquationsQuasi1D)
+ a_h, a_h_v, _, a = u
+ h = waterheight(u, equations)
+ v = velocity(u, equations)
+
+ p = 0.5 * a * equations.gravity * h^2
+
+ f1 = a_h_v
+ f2 = a_h_v * v + p
+
+ return SVector(f1, f2, zero(eltype(u)), zero(eltype(u)))
+end
+
+"""
+ flux_nonconservative_chan_etal(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquationsQuasi1D)
+
+Non-symmetric two-point volume flux discretizing the nonconservative (source) term
+that contains the gradient of the bottom topography [`ShallowWaterEquationsQuasi1D`](@ref)
+and the channel width.
+
+Further details are available in the paper:
+- Jesse Chan, Khemraj Shukla, Xinhui Wu, Ruofeng Liu, Prani Nalluri (2023)
+ High order entropy stable schemes for the quasi-one-dimensional
+ shallow water and compressible Euler equations
+ [DOI: 10.48550/arXiv.2307.12089](https://doi.org/10.48550/arXiv.2307.12089)
+"""
+@inline function flux_nonconservative_chan_etal(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquationsQuasi1D)
+ a_h_ll, _, b_ll, a_ll = u_ll
+ a_h_rr, _, b_rr, a_rr = u_rr
+
+ h_ll = waterheight(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+
+ z = zero(eltype(u_ll))
+
+ return SVector(z, equations.gravity * a_ll * h_ll * (h_rr + b_rr), z, z)
+end
+
+"""
+ flux_chan_etal(u_ll, u_rr, orientation,
+ equations::ShallowWaterEquationsQuasi1D)
+
+Total energy conservative (mathematical entropy for quasi 1D shallow water equations) split form.
+When the bottom topography is nonzero this scheme will be well-balanced when used as a `volume_flux`.
+The `surface_flux` should still use, e.g., [`FluxPlusDissipation(flux_chan_etal, DissipationLocalLaxFriedrichs())`](@ref).
+
+Further details are available in the paper:
+- Jesse Chan, Khemraj Shukla, Xinhui Wu, Ruofeng Liu, Prani Nalluri (2023)
+ High order entropy stable schemes for the quasi-one-dimensional
+ shallow water and compressible Euler equations
+ [DOI: 10.48550/arXiv.2307.12089](https://doi.org/10.48550/arXiv.2307.12089)
+"""
+@inline function flux_chan_etal(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquationsQuasi1D)
+ a_h_ll, a_h_v_ll, _, _ = u_ll
+ a_h_rr, a_h_v_rr, _, _ = u_rr
+
+ v_ll = velocity(u_ll, equations)
+ v_rr = velocity(u_rr, equations)
+
+ f1 = 0.5 * (a_h_v_ll + a_h_v_rr)
+ f2 = f1 * 0.5 * (v_ll + v_rr)
+
+ return SVector(f1, f2, zero(eltype(u_ll)), zero(eltype(u_ll)))
+end
+
+# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation as the
+# maximum velocity magnitude plus the maximum speed of sound
+@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,
+ equations::ShallowWaterEquationsQuasi1D)
+ # Get the velocity quantities
+ v_ll = velocity(u_ll, equations)
+ v_rr = velocity(u_rr, equations)
+
+ # Calculate the wave celerity on the left and right
+ h_ll = waterheight(u_ll, equations)
+ h_rr = waterheight(u_rr, equations)
+ c_ll = sqrt(equations.gravity * h_ll)
+ c_rr = sqrt(equations.gravity * h_rr)
+
+ return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr)
+end
+
+# Specialized `DissipationLocalLaxFriedrichs` to avoid spurious dissipation in the bottom topography
+# and channel width
+@inline function (dissipation::DissipationLocalLaxFriedrichs)(u_ll, u_rr,
+ orientation_or_normal_direction,
+ equations::ShallowWaterEquationsQuasi1D)
+ λ = dissipation.max_abs_speed(u_ll, u_rr, orientation_or_normal_direction,
+ equations)
+ diss = -0.5 * λ * (u_rr - u_ll)
+ return SVector(diss[1], diss[2], zero(eltype(u_ll)), zero(eltype(u_ll)))
+end
+
+@inline function max_abs_speeds(u, equations::ShallowWaterEquationsQuasi1D)
+ h = waterheight(u, equations)
+ v = velocity(u, equations)
+
+ c = equations.gravity * sqrt(h)
+ return (abs(v) + c,)
+end
+
+# Helper function to extract the velocity vector from the conservative variables
+@inline function velocity(u, equations::ShallowWaterEquationsQuasi1D)
+ a_h, a_h_v, _, _ = u
+
+ v = a_h_v / a_h
+
+ return v
+end
+
+# Convert conservative variables to primitive
+@inline function cons2prim(u, equations::ShallowWaterEquationsQuasi1D)
+ a_h, _, b, a = u
+ h = a_h / a
+ H = h + b
+ v = velocity(u, equations)
+ return SVector(H, v, b, a)
+end
+
+# Convert conservative variables to entropy variables
+# Note, only the first two are the entropy variables, the third and fourth entries still
+# just carry the bottom topography and channel width values for convenience
+@inline function cons2entropy(u, equations::ShallowWaterEquationsQuasi1D)
+ a_h, a_h_v, b, a = u
+ h = waterheight(u, equations)
+ v = velocity(u, equations)
+ #entropy variables are the same as ones in standard shallow water equations
+ w1 = equations.gravity * (h + b) - 0.5 * v^2
+ w2 = v
+
+ return SVector(w1, w2, b, a)
+end
+
+# Convert primitive to conservative variables
+@inline function prim2cons(prim, equations::ShallowWaterEquationsQuasi1D)
+ H, v, b, a = prim
+
+ a_h = a * (H - b)
+ a_h_v = a_h * v
+ return SVector(a_h, a_h_v, b, a)
+end
+
+@inline function waterheight(u, equations::ShallowWaterEquationsQuasi1D)
+ return u[1] / u[4]
+end
+
+# Entropy function for the shallow water equations is the total energy
+@inline function entropy(cons, equations::ShallowWaterEquationsQuasi1D)
+ a = cons[4]
+ return a * energy_total(cons, equations)
+end
+
+# Calculate total energy for a conservative state `cons`
+@inline function energy_total(cons, equations::ShallowWaterEquationsQuasi1D)
+ a_h, a_h_v, b, a = cons
+ e = (a_h_v^2) / (2 * a * a_h) + 0.5 * equations.gravity * (a_h^2 / a) +
+ equations.gravity * a_h * b
+ return e
+end
+
+# Calculate the error for the "lake-at-rest" test case where H = h+b should
+# be a constant value over time. Note, assumes there is a single reference
+# water height `H0` with which to compare.
+#
+# TODO: TrixiShallowWater: where should `threshold_limiter` live? May need
+# to modify or have different versions of the `lake_at_rest_error` function
+@inline function lake_at_rest_error(u, equations::ShallowWaterEquationsQuasi1D)
+ _, _, b, _ = u
+ h = waterheight(u, equations)
+
+ # For well-balancedness testing with possible wet/dry regions the reference
+ # water height `H0` accounts for the possibility that the bottom topography
+ # can emerge out of the water as well as for the threshold offset to avoid
+ # division by a "hard" zero water heights as well.
+ H0_wet_dry = max(equations.H0, b + equations.threshold_limiter)
+
+ return abs(H0_wet_dry - (h + b))
+end
+end # @muladd
diff --git a/test/test_tree_1d_shallowwater.jl b/test/test_tree_1d_shallowwater.jl
index 1e5aeac1786..09fb2d9e432 100644
--- a/test/test_tree_1d_shallowwater.jl
+++ b/test/test_tree_1d_shallowwater.jl
@@ -112,6 +112,20 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_1d_dgsem")
linf = [0.00041080213807871235, 0.00014823261488938177, 2.220446049250313e-16],
tspan = (0.0, 0.05))
end
+
+ @trixi_testset "elixir_shallow_water_quasi_1d_source_terms.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallow_water_quasi_1d_source_terms.jl"),
+ l2 = [6.37048760275098e-5, 0.0002745658116815704, 4.436491725647962e-6, 8.872983451152218e-6],
+ linf = [0.00026747526881631956, 0.0012106730729152249, 9.098379777500165e-6, 1.8196759554278685e-5],
+ tspan = (0.0, 0.05))
+ end
+
+ @trixi_testset "elixir_shallowwater_quasi_1d_well_balanced.jl" begin
+ @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_shallowwater_quasi_1d_well_balanced.jl"),
+ l2 = [1.4250229186905198e-14, 2.495109919406496e-12, 7.408599286788738e-17, 2.7205812409138776e-16],
+ linf = [5.284661597215745e-14, 2.74056233065078e-12, 2.220446049250313e-16, 8.881784197001252e-16],
+ tspan = (0.0, 100.0))
+ end
end
end # module
From f7b09734e1a86077a35c9cab0b9e97e68aaeb232 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Thu, 14 Sep 2023 06:41:14 +0200
Subject: [PATCH 099/263] add package versions to tutorials (#1638)
---
docs/literate/src/files/DGMulti_1.jl | 12 ++++++++++++
docs/literate/src/files/DGMulti_2.jl | 12 ++++++++++++
docs/literate/src/files/DGSEM_FluxDiff.jl | 12 ++++++++++++
.../literate/src/files/adaptive_mesh_refinement.jl | 12 ++++++++++++
.../src/files/adding_new_parabolic_terms.jl | 12 ++++++++++++
.../src/files/adding_new_scalar_equations.jl | 12 ++++++++++++
.../src/files/adding_nonconservative_equation.jl | 12 ++++++++++++
.../src/files/differentiable_programming.jl | 12 ++++++++++++
docs/literate/src/files/hohqmesh_tutorial.jl | 12 ++++++++++++
docs/literate/src/files/non_periodic_boundaries.jl | 12 ++++++++++++
docs/literate/src/files/parabolic_terms.jl | 11 +++++++++++
.../src/files/scalar_linear_advection_1d.jl | 12 ++++++++++++
docs/literate/src/files/shock_capturing.jl | 12 ++++++++++++
docs/literate/src/files/structured_mesh_mapping.jl | 12 ++++++++++++
docs/literate/src/files/time_stepping.jl | 14 +++++++++++++-
docs/literate/src/files/upwind_fdsbp.jl | 12 ++++++++++++
16 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/docs/literate/src/files/DGMulti_1.jl b/docs/literate/src/files/DGMulti_1.jl
index 0d78e79907c..5ef577e8eeb 100644
--- a/docs/literate/src/files/DGMulti_1.jl
+++ b/docs/literate/src/files/DGMulti_1.jl
@@ -194,3 +194,15 @@ plot(pd["rho"])
plot!(getmesh(pd))
# For more information, please have a look in the [StartUpDG.jl documentation](https://jlchan.github.io/StartUpDG.jl/stable/).
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "StartUpDG", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/DGMulti_2.jl b/docs/literate/src/files/DGMulti_2.jl
index 92dce43cdab..06248562343 100644
--- a/docs/literate/src/files/DGMulti_2.jl
+++ b/docs/literate/src/files/DGMulti_2.jl
@@ -38,3 +38,15 @@ D = couple_continuously(legendre_derivative_operator(xmin=0.0, xmax=1.0, N=4),
# For more information and other SBP operators, see the documentations of [StartUpDG.jl](https://jlchan.github.io/StartUpDG.jl/dev/)
# and [SummationByPartsOperators.jl](https://ranocha.de/SummationByPartsOperators.jl/stable/).
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "StartUpDG", "SummationByPartsOperators"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/DGSEM_FluxDiff.jl b/docs/literate/src/files/DGSEM_FluxDiff.jl
index 5ec156ebbe3..a5769900269 100644
--- a/docs/literate/src/files/DGSEM_FluxDiff.jl
+++ b/docs/literate/src/files/DGSEM_FluxDiff.jl
@@ -236,3 +236,15 @@ plot(sol)
# [`flux_chandrashekar`](@ref), [`flux_kennedy_gruber`](@ref).
# As surface flux you can use all volume fluxes and additionally for instance [`flux_lax_friedrichs`](@ref),
# [`flux_hll`](@ref), [`flux_hllc`](@ref).
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/adaptive_mesh_refinement.jl b/docs/literate/src/files/adaptive_mesh_refinement.jl
index d6150e887a8..46af8f79523 100644
--- a/docs/literate/src/files/adaptive_mesh_refinement.jl
+++ b/docs/literate/src/files/adaptive_mesh_refinement.jl
@@ -202,3 +202,15 @@ plot!(getmesh(pd))
# Source: Trixi.jl's YouTube channel [`Trixi Framework`](https://www.youtube.com/channel/UCpd92vU2HjjTPup-AIN0pkg)
# For more information, please have a look at the respective links.
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/adding_new_parabolic_terms.jl b/docs/literate/src/files/adding_new_parabolic_terms.jl
index 882f73f66ff..f5c2b815f33 100644
--- a/docs/literate/src/files/adding_new_parabolic_terms.jl
+++ b/docs/literate/src/files/adding_new_parabolic_terms.jl
@@ -158,3 +158,15 @@ sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
using Plots
plot(sol)
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
+
diff --git a/docs/literate/src/files/adding_new_scalar_equations.jl b/docs/literate/src/files/adding_new_scalar_equations.jl
index fec7bcf667a..a65b4de7f1a 100644
--- a/docs/literate/src/files/adding_new_scalar_equations.jl
+++ b/docs/literate/src/files/adding_new_scalar_equations.jl
@@ -211,3 +211,15 @@ semi = remake(semi, solver=DGSEM(3, flux_godunov, VolumeIntegralFluxDifferencing
ode = semidiscretize(semi, (0.0, 0.5))
sol = solve(ode, SSPRK43(); ode_default_options()...)
plot(sol)
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/adding_nonconservative_equation.jl b/docs/literate/src/files/adding_nonconservative_equation.jl
index 110fa486070..b40e21fb11a 100644
--- a/docs/literate/src/files/adding_nonconservative_equation.jl
+++ b/docs/literate/src/files/adding_nonconservative_equation.jl
@@ -288,3 +288,15 @@ sol = solve(ode, Tsit5(), abstol=1.0e-6, reltol=1.0e-6,
## Plot the numerical solution at the final time
using Plots: plot
plot(sol);
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/differentiable_programming.jl b/docs/literate/src/files/differentiable_programming.jl
index 5c5a7cd7440..33427803afc 100644
--- a/docs/literate/src/files/differentiable_programming.jl
+++ b/docs/literate/src/files/differentiable_programming.jl
@@ -446,3 +446,15 @@ scatter(real.(λ), imag.(λ))
λ = eigvals(Matrix(A))
relative_maximum = maximum(real, λ) / maximum(abs, λ)
@test relative_maximum < 1.0e-15 #src
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots", "ForwardDiff"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/hohqmesh_tutorial.jl b/docs/literate/src/files/hohqmesh_tutorial.jl
index 87076108d91..b19d363c4bf 100644
--- a/docs/literate/src/files/hohqmesh_tutorial.jl
+++ b/docs/literate/src/files/hohqmesh_tutorial.jl
@@ -566,3 +566,15 @@ mesh = UnstructuredMesh2D(mesh_file);
# for details.
# ![simulation_straight_sides_p4est_amr](https://user-images.githubusercontent.com/74359358/168049930-8abce6ac-cd47-4d04-b40b-0fa459bbd98d.png)
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots", "Trixi2Vtk", "HOHQMesh"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/non_periodic_boundaries.jl b/docs/literate/src/files/non_periodic_boundaries.jl
index 54da88a64aa..7ed6324ff99 100644
--- a/docs/literate/src/files/non_periodic_boundaries.jl
+++ b/docs/literate/src/files/non_periodic_boundaries.jl
@@ -155,3 +155,15 @@ end
#
# ```
# Source: [`Video`](https://www.youtube.com/watch?v=w0A9X38cSe4) on Trixi.jl's YouTube channel [`Trixi Framework`](https://www.youtube.com/watch?v=WElqqdMhY4A)
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/parabolic_terms.jl b/docs/literate/src/files/parabolic_terms.jl
index bac0098f8e9..d0a355bbc19 100644
--- a/docs/literate/src/files/parabolic_terms.jl
+++ b/docs/literate/src/files/parabolic_terms.jl
@@ -86,3 +86,14 @@ sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol,
using Plots
plot(sol)
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/scalar_linear_advection_1d.jl b/docs/literate/src/files/scalar_linear_advection_1d.jl
index 42c831c98ba..77ba7b087cc 100644
--- a/docs/literate/src/files/scalar_linear_advection_1d.jl
+++ b/docs/literate/src/files/scalar_linear_advection_1d.jl
@@ -511,3 +511,15 @@ sol_trixi = solve(ode_trixi, RDPK3SpFSAL49(); abstol=1.0e-6, reltol=1.0e-6, ode
plot!(sol_trixi, label="solution at t=$(tspan[2]) with Trixi.jl", legend=:topleft, linestyle=:dash, lw=2)
@test maximum(abs.(vec(u0) - sol_trixi.u[end])) ≈ maximum(abs.(u0 - sol.u[end])) #src
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/shock_capturing.jl b/docs/literate/src/files/shock_capturing.jl
index afa34cbf06a..dd6698c2a86 100644
--- a/docs/literate/src/files/shock_capturing.jl
+++ b/docs/literate/src/files/shock_capturing.jl
@@ -224,3 +224,15 @@ sol = solve(ode, CarpenterKennedy2N54(stage_limiter!, williamson_condition=false
using Plots
plot(sol)
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/structured_mesh_mapping.jl b/docs/literate/src/files/structured_mesh_mapping.jl
index 0ae9cf723f8..c8da30bc2bf 100644
--- a/docs/literate/src/files/structured_mesh_mapping.jl
+++ b/docs/literate/src/files/structured_mesh_mapping.jl
@@ -201,3 +201,15 @@ plot!(getmesh(pd))
# unstructured mesh type [`UnstructuredMesh2D`] and its use of the
# [High-Order Hex-Quad Mesh (HOHQMesh) generator](https://github.com/trixi-framework/HOHQMesh),
# created and developed by David Kopriva.
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq", "Plots"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/time_stepping.jl b/docs/literate/src/files/time_stepping.jl
index d400c4a94be..de7a2a83a41 100644
--- a/docs/literate/src/files/time_stepping.jl
+++ b/docs/literate/src/files/time_stepping.jl
@@ -49,7 +49,7 @@
# ```math
# \Delta t_n = \text{CFL} * \min_i \frac{\Delta x_i}{\lambda_{\max}(u_i^n)}
# ```
-# We compute $\Delta x_i$ by scaling the element size by a factor of $1/(N+1)$, cf.
+# We compute $\Delta x_i$ by scaling the element size by a factor of $1/(N+1)$, cf.
# [Gassner and Kopriva (2011)](https://doi.org/10.1137/100807211), Section 5.
# Trixi.jl provides such a CFL-based step size control. It is implemented as the callback
@@ -73,3 +73,15 @@
# You can find simple examples with a CFL-based step size control for instance in the elixirs
# [`elixir_advection_basic.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_advection_basic.jl)
# or [`elixir_euler_source_terms.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_source_terms.jl).
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "OrdinaryDiffEq"],
+ mode=PKGMODE_MANIFEST)
diff --git a/docs/literate/src/files/upwind_fdsbp.jl b/docs/literate/src/files/upwind_fdsbp.jl
index 36ca1b57404..6d3379fa30d 100644
--- a/docs/literate/src/files/upwind_fdsbp.jl
+++ b/docs/literate/src/files/upwind_fdsbp.jl
@@ -62,3 +62,15 @@ Matrix(D_upw.plus)
# flux vector splitting, e.g.,
# - [`elixir_euler_vortex.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_fdsbp/elixir_euler_vortex.jl)
# - [`elixir_euler_taylor_green_vortex.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_3d_fdsbp/elixir_euler_taylor_green_vortex.jl)
+
+
+# ## Package versions
+
+# These results were obtained using the following versions.
+
+using InteractiveUtils
+versioninfo()
+
+using Pkg
+Pkg.status(["Trixi", "SummationByPartsOperators"],
+ mode=PKGMODE_MANIFEST)
From 15543f28b7070dbc403857a047d5dc2ca2d0c1c3 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Thu, 14 Sep 2023 06:45:19 +0200
Subject: [PATCH 100/263] set version to v0.5.43
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 06fd29ba590..943d0d48005 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.43-pre"
+version = "0.5.43"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 92dedde81e12f8ac2259785a9eab40f475d82251 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Thu, 14 Sep 2023 06:45:34 +0200
Subject: [PATCH 101/263] set development version to v0.5.44-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 943d0d48005..d134a8e548b 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.43"
+version = "0.5.44-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 6069149fef0590c824ddee54b6d12d7531b6c790 Mon Sep 17 00:00:00 2001
From: ArseniyKholod <119304909+ArseniyKholod@users.noreply.github.com>
Date: Fri, 15 Sep 2023 08:10:03 +0200
Subject: [PATCH 102/263] Handles restarting problem of adaptive time
integration methods (#1565)
* Create test.jl
* Delete test.jl
* loadcallback
* adding_parallel_support
* formatting
* minimize dependencies
* combine loadrestart and saverestart
* fix
* Update test_threaded.jl
* fix
* test fix
* fix
* MODULE add
* fix
* runtime macros
* Update test_threaded.jl
* handle MPI issues
* enable PIDController test
* Update test_mpi_tree.jl
* fix
* add asserts
* Update save_restart.jl
* add IController tests
* enable HDF5 parallel
* fix shot
* fix shot 2
* fix shot 3
* fix shot 4
* fix shot 5
* fix shot 6
* fix shot 7
* fix shot 8
* fix shot 9
* fix shot 10
* fix shot 11
* fix shot 12
* fix shot 13
* fix shot 14
* fix shot 15
* fix shot 16
* fix shot 17
* fix shot 18
* enable additional configuration only in mpi test on linux
* enable environment
* test coverage issue
* disable mpi macOs CI because of failure
* disable new configurations to test coverage
* disable PID and I test to test coverage issue
* enable old coverage all
* undo last commit and enable coverage on windows
* enable new tests, mpi macOs and HDF5 parallel
* fix
* enable coverage on threads
* test HDF5 parallel
* test HDF5 parallel 2
* test HDF5 parallel 3
* fix
* Update save_restart_dg.jl
* test HDF5 parallel 4
* test HDF5 parallel 5
* Update configure_packages.jl
* delete unnecessary changes
* Update save_restart_dg.jl
* Update save_restart_dg.jl
* remove dependency on OrdinaryDiffEq
* format
* discard unrelated changes
* delete barrier
* delete eval()
* comments & delete mpi_parallel
* format
* Update runtests.jl
* Update runtests.jl
* simplify tests
* test failing MPI on windiws and macOs
* test with RDPK3SpFSAL49
* test with RDPK3SpFSAL35
* change tests
* fix and new test
* Update test_tree_2d_euler.jl
* fix and delete unnecessary test
* add printing format
* add docstrings
* Update src/callbacks_step/save_restart.jl
Co-authored-by: Michael Schlottke-Lakemper
* fix
* formatting
* Update src/callbacks_step/save_restart_dg.jl
Co-authored-by: Michael Schlottke-Lakemper
* Update src/callbacks_step/save_restart_dg.jl
Co-authored-by: Michael Schlottke-Lakemper
* Update src/callbacks_step/save_restart.jl
Co-authored-by: Michael Schlottke-Lakemper
* Update src/callbacks_step/save_restart.jl
Co-authored-by: Michael Schlottke-Lakemper
* Update src/callbacks_step/save_restart.jl
Co-authored-by: Michael Schlottke-Lakemper
* suggested changes
* new test
* fix
* fix
* Update test_tree_2d_advection.jl
* fix
* fix error mpi on windows
* rerun
* Update src/callbacks_step/save_restart.jl
Co-authored-by: Hendrik Ranocha
* Add comments
* format
---------
Co-authored-by: Michael Schlottke-Lakemper
Co-authored-by: Hendrik Ranocha
---
.../elixir_advection_extended.jl | 7 ++--
.../tree_2d_dgsem/elixir_advection_restart.jl | 19 +++++----
src/Trixi.jl | 3 +-
src/callbacks_step/save_restart.jl | 36 +++++++++++++++++
src/callbacks_step/save_restart_dg.jl | 24 ++++++++++++
test/test_mpi_tree.jl | 20 ++++++++--
test/test_threaded.jl | 39 ++++++++++++-------
test/test_tree_2d_advection.jl | 20 ++++++++--
8 files changed, 135 insertions(+), 33 deletions(-)
diff --git a/examples/tree_2d_dgsem/elixir_advection_extended.jl b/examples/tree_2d_dgsem/elixir_advection_extended.jl
index 8c837957ffd..278dc85386d 100644
--- a/examples/tree_2d_dgsem/elixir_advection_extended.jl
+++ b/examples/tree_2d_dgsem/elixir_advection_extended.jl
@@ -54,7 +54,7 @@ analysis_callback = AnalysisCallback(semi, interval=analysis_interval,
alive_callback = AliveCallback(analysis_interval=analysis_interval)
# The SaveRestartCallback allows to save a file from which a Trixi.jl simulation can be restarted
-save_restart = SaveRestartCallback(interval=100,
+save_restart = SaveRestartCallback(interval=40,
save_final_restart=true)
# The SaveSolutionCallback allows to save the solution to a file in regular intervals
@@ -77,9 +77,10 @@ callbacks = CallbackSet(summary_callback,
# run the simulation
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
-sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
+alg = CarpenterKennedy2N54(williamson_condition=false)
+sol = solve(ode, alg,
dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks);
+ save_everystep=false, callback=callbacks; ode_default_options()...);
# Print the timer summary
summary_callback()
diff --git a/examples/tree_2d_dgsem/elixir_advection_restart.jl b/examples/tree_2d_dgsem/elixir_advection_restart.jl
index 771ec5aefe7..b63a8d1f7bc 100644
--- a/examples/tree_2d_dgsem/elixir_advection_restart.jl
+++ b/examples/tree_2d_dgsem/elixir_advection_restart.jl
@@ -3,9 +3,10 @@ using OrdinaryDiffEq
using Trixi
###############################################################################
-# create a restart file
-
-trixi_include(@__MODULE__, joinpath(@__DIR__, "elixir_advection_extended.jl"))
+# Define time integration algorithm
+alg = CarpenterKennedy2N54(williamson_condition=false)
+# Create a restart file
+trixi_include(@__MODULE__, joinpath(@__DIR__, "elixir_advection_extended.jl"), alg = alg, tspan = (0.0, 10.0))
###############################################################################
@@ -14,22 +15,26 @@ trixi_include(@__MODULE__, joinpath(@__DIR__, "elixir_advection_extended.jl"))
# Note: If you get a restart file from somewhere else, you need to provide
# appropriate setups in the elixir loading a restart file
-restart_filename = joinpath("out", "restart_000018.h5")
+restart_filename = joinpath("out", "restart_000040.h5")
mesh = load_mesh(restart_filename)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
-tspan = (load_time(restart_filename), 2.0)
+tspan = (load_time(restart_filename), 10.0)
dt = load_dt(restart_filename)
ode = semidiscretize(semi, tspan, restart_filename);
# Do not overwrite the initial snapshot written by elixir_advection_extended.jl.
save_solution.condition.save_initial_solution = false
-alg = CarpenterKennedy2N54(williamson_condition=false)
integrator = init(ode, alg,
dt=dt, # solve needs some value here but it will be overwritten by the stepsize_callback
- save_everystep=false, callback=callbacks)
+ save_everystep=false, callback=callbacks; ode_default_options()...)
+
+# Load saved context for adaptive time integrator
+if integrator.opts.adaptive
+ load_adaptive_time_integrator!(integrator, restart_filename)
+end
# Get the last time index and work with that.
load_timestep!(integrator, restart_filename)
diff --git a/src/Trixi.jl b/src/Trixi.jl
index c883c3bf19f..b65d03e7975 100644
--- a/src/Trixi.jl
+++ b/src/Trixi.jl
@@ -255,7 +255,8 @@ export SummaryCallback, SteadyStateCallback, AnalysisCallback, AliveCallback,
GlmSpeedCallback, LBMCollisionCallback, EulerAcousticsCouplingCallback,
TrivialCallback, AnalysisCallbackCoupled
-export load_mesh, load_time, load_timestep, load_timestep!, load_dt
+export load_mesh, load_time, load_timestep, load_timestep!, load_dt,
+ load_adaptive_time_integrator!
export ControllerThreeLevel, ControllerThreeLevelCombined,
IndicatorLöhner, IndicatorLoehner, IndicatorMax,
diff --git a/src/callbacks_step/save_restart.jl b/src/callbacks_step/save_restart.jl
index 06817a9b730..0d174d85805 100644
--- a/src/callbacks_step/save_restart.jl
+++ b/src/callbacks_step/save_restart.jl
@@ -105,6 +105,11 @@ function (restart_callback::SaveRestartCallback)(integrator)
end
save_restart_file(u_ode, t, dt, iter, semi, restart_callback)
+ # If using an adaptive time stepping scheme, store controller values for restart
+ if integrator.opts.adaptive
+ save_adaptive_time_integrator(integrator, integrator.opts.controller,
+ restart_callback)
+ end
end
# avoid re-evaluating possible FSAL stages
@@ -168,5 +173,36 @@ function load_restart_file(semi::AbstractSemidiscretization, restart_file)
load_restart_file(mesh_equations_solver_cache(semi)..., restart_file)
end
+"""
+ load_adaptive_time_integrator!(integrator, restart_file::AbstractString)
+
+Load the context information for time integrators with error-based step size control
+saved in a `restart_file`.
+"""
+function load_adaptive_time_integrator!(integrator, restart_file::AbstractString)
+ controller = integrator.opts.controller
+ # Read context information for controller
+ h5open(restart_file, "r") do file
+ # Ensure that the necessary information was saved
+ if !("time_integrator_qold" in keys(attributes(file))) ||
+ !("time_integrator_dtpropose" in keys(attributes(file))) ||
+ (hasproperty(controller, :err) &&
+ !("time_integrator_controller_err" in keys(attributes(file))))
+ error("Missing data in restart file: check the consistency of adaptive time controller with initial setup!")
+ end
+ # Load data that is required both for PIController and PIDController
+ integrator.qold = read(attributes(file)["time_integrator_qold"])
+ integrator.dtpropose = read(attributes(file)["time_integrator_dtpropose"])
+ # Accept step to use dtpropose already in the first step
+ integrator.accept_step = true
+ # Reevaluate integrator.fsal_first on the first step
+ integrator.reeval_fsal = true
+ # Load additional parameters for PIDController
+ if hasproperty(controller, :err) # Distinguish PIDController from PIController
+ controller.err[:] = read(attributes(file)["time_integrator_controller_err"])
+ end
+ end
+end
+
include("save_restart_dg.jl")
end # @muladd
diff --git a/src/callbacks_step/save_restart_dg.jl b/src/callbacks_step/save_restart_dg.jl
index 8db6db2d2b8..cddeef77bb2 100644
--- a/src/callbacks_step/save_restart_dg.jl
+++ b/src/callbacks_step/save_restart_dg.jl
@@ -327,4 +327,28 @@ function load_restart_file_on_root(mesh::Union{ParallelTreeMesh, ParallelP4estMe
return u_ode
end
+
+# Store controller values for an adaptive time stepping scheme
+function save_adaptive_time_integrator(integrator,
+ controller, restart_callback)
+ # Save only on root
+ if mpi_isroot()
+ @unpack output_directory = restart_callback
+ timestep = integrator.stats.naccept
+
+ # Filename based on current time step
+ filename = joinpath(output_directory, @sprintf("restart_%06d.h5", timestep))
+
+ # Open file (preserve existing content)
+ h5open(filename, "r+") do file
+ # Add context information as attributes both for PIController and PIDController
+ attributes(file)["time_integrator_qold"] = integrator.qold
+ attributes(file)["time_integrator_dtpropose"] = integrator.dtpropose
+ # For PIDController is necessary to save additional parameters
+ if hasproperty(controller, :err) # Distinguish PIDController from PIController
+ attributes(file)["time_integrator_controller_err"] = controller.err
+ end
+ end
+ end
+end
end # @muladd
diff --git a/test/test_mpi_tree.jl b/test/test_mpi_tree.jl
index 8403fcf1b04..8f08a9d72e7 100644
--- a/test/test_mpi_tree.jl
+++ b/test/test_mpi_tree.jl
@@ -23,10 +23,22 @@ CI_ON_WINDOWS = (get(ENV, "GITHUB_ACTIONS", false) == "true") && Sys.iswindows()
end
@trixi_testset "elixir_advection_restart.jl" begin
- @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"),
- # Expected errors are exactly the same as in the serial test!
- l2 = [7.81674284320524e-6],
- linf = [6.314906965243505e-5])
+ using OrdinaryDiffEq: RDPK3SpFSAL49
+ Trixi.mpi_isroot() && println("═"^100)
+ Trixi.mpi_isroot() && println(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"))
+ trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
+ alg = RDPK3SpFSAL49(), tspan = (0.0, 10.0))
+ l2_expected, linf_expected = analysis_callback(sol)
+
+ Trixi.mpi_isroot() && println("═"^100)
+ Trixi.mpi_isroot() && println(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"))
+ # Errors are exactly the same as in the elixir_advection_extended.jl
+ trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"),
+ alg = RDPK3SpFSAL49())
+ l2_actual, linf_actual = analysis_callback(sol)
+
+ Trixi.mpi_isroot() && @test l2_actual == l2_expected
+ Trixi.mpi_isroot() && @test linf_actual == linf_expected
end
@trixi_testset "elixir_advection_mortar.jl" begin
diff --git a/test/test_threaded.jl b/test/test_threaded.jl
index 9b30836d0ed..2337d73f30a 100644
--- a/test/test_threaded.jl
+++ b/test/test_threaded.jl
@@ -12,27 +12,38 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
@testset "Threaded tests" begin
@testset "TreeMesh" begin
@trixi_testset "elixir_advection_restart.jl" begin
- @test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_restart.jl"),
- # Expected errors are exactly the same as in the serial test!
- l2 = [7.81674284320524e-6],
- linf = [6.314906965243505e-5])
+ elixir = joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_extended.jl")
+ Trixi.mpi_isroot() && println("═"^100)
+ Trixi.mpi_isroot() && println(elixir)
+ trixi_include(@__MODULE__, elixir, tspan = (0.0, 10.0))
+ l2_expected, linf_expected = analysis_callback(sol)
+
+ elixir = joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_restart.jl")
+ Trixi.mpi_isroot() && println("═"^100)
+ Trixi.mpi_isroot() && println(elixir)
+ # Errors are exactly the same as in the elixir_advection_extended.jl
+ trixi_include(@__MODULE__, elixir)
+ l2_actual, linf_actual = analysis_callback(sol)
+
+ Trixi.mpi_isroot() && @test l2_actual == l2_expected
+ Trixi.mpi_isroot() && @test linf_actual == linf_expected
- # Ensure that we do not have excessive memory allocations
- # (e.g., from type instabilities)
- let
- t = sol.t[end]
- u_ode = sol.u[end]
- du_ode = similar(u_ode)
- @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
- end
+ # Ensure that we do not have excessive memory allocations
+ # (e.g., from type instabilities)
+ let
+ t = sol.t[end]
+ u_ode = sol.u[end]
+ du_ode = similar(u_ode)
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
+ end
end
@trixi_testset "elixir_advection_restart.jl with threaded time integration" begin
@test_trixi_include(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_restart.jl"),
alg = CarpenterKennedy2N54(williamson_condition = false, thread = OrdinaryDiffEq.True()),
# Expected errors are exactly the same as in the serial test!
- l2 = [7.81674284320524e-6],
- linf = [6.314906965243505e-5])
+ l2 = [8.005068880114254e-6],
+ linf = [6.39093577996519e-5])
end
@trixi_testset "elixir_advection_amr_refine_twice.jl" begin
diff --git a/test/test_tree_2d_advection.jl b/test/test_tree_2d_advection.jl
index 973d0caf88b..36cb1e882cc 100644
--- a/test/test_tree_2d_advection.jl
+++ b/test/test_tree_2d_advection.jl
@@ -25,10 +25,22 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_dgsem")
end
@trixi_testset "elixir_advection_restart.jl" begin
- @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"),
- # Expected errors are exactly the same as in the parallel test!
- l2 = [7.81674284320524e-6],
- linf = [6.314906965243505e-5])
+ using OrdinaryDiffEq: SSPRK43
+ println("═"^100)
+ println(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"))
+ trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"),
+ alg = SSPRK43(), tspan = (0.0, 10.0))
+ l2_expected, linf_expected = analysis_callback(sol)
+
+ println("═"^100)
+ println(joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"))
+ # Errors are exactly the same as in the elixir_advection_extended.jl
+ trixi_include(@__MODULE__, joinpath(EXAMPLES_DIR, "elixir_advection_restart.jl"),
+ alg = SSPRK43())
+ l2_actual, linf_actual = analysis_callback(sol)
+
+ @test l2_actual == l2_expected
+ @test linf_actual == linf_expected
end
@trixi_testset "elixir_advection_mortar.jl" begin
From 73384acbf45cf10710cfc817bc91a1812a0db1fd Mon Sep 17 00:00:00 2001
From: Benjamin Bolm <74359358+bennibolm@users.noreply.github.com>
Date: Sat, 16 Sep 2023 16:16:12 +0200
Subject: [PATCH 103/263] Assure conservation for SSP scheme (#1640)
* Add denominator variable for SSP scheme
* Fix format
* Implement suggestions
---
src/time_integration/methods_SSP.jl | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/time_integration/methods_SSP.jl b/src/time_integration/methods_SSP.jl
index 8ecad69748b..a0ed889968a 100644
--- a/src/time_integration/methods_SSP.jl
+++ b/src/time_integration/methods_SSP.jl
@@ -24,14 +24,16 @@ The third-order SSP Runge-Kutta method of Shu and Osher.
This is an experimental feature and may change in future releases.
"""
struct SimpleSSPRK33{StageCallbacks} <: SimpleAlgorithmSSP
- a::SVector{3, Float64}
- b::SVector{3, Float64}
+ numerator_a::SVector{3, Float64}
+ numerator_b::SVector{3, Float64}
+ denominator::SVector{3, Float64}
c::SVector{3, Float64}
stage_callbacks::StageCallbacks
function SimpleSSPRK33(; stage_callbacks = ())
- a = SVector(0.0, 3 / 4, 1 / 3)
- b = SVector(1.0, 1 / 4, 2 / 3)
+ numerator_a = SVector(0.0, 3.0, 1.0) # a = numerator_a / denominator
+ numerator_b = SVector(1.0, 1.0, 2.0) # b = numerator_b / denominator
+ denominator = SVector(1.0, 4.0, 3.0)
c = SVector(0.0, 1.0, 1 / 2)
# Butcher tableau
@@ -42,7 +44,8 @@ struct SimpleSSPRK33{StageCallbacks} <: SimpleAlgorithmSSP
# --------------------
# b | 1/6 1/6 2/3
- new{typeof(stage_callbacks)}(a, b, c, stage_callbacks)
+ new{typeof(stage_callbacks)}(numerator_a, numerator_b, denominator, c,
+ stage_callbacks)
end
end
@@ -166,7 +169,9 @@ function solve!(integrator::SimpleIntegratorSSP)
end
# perform convex combination
- @. integrator.u = alg.a[stage] * integrator.r0 + alg.b[stage] * integrator.u
+ @. integrator.u = (alg.numerator_a[stage] * integrator.r0 +
+ alg.numerator_b[stage] * integrator.u) /
+ alg.denominator[stage]
end
integrator.iter += 1
From a64004d98c7a1b0c4894663c176f21c2178a3630 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 19 Sep 2023 11:49:30 +0200
Subject: [PATCH 104/263] CompatHelper: bump compat for Documenter to 1 for
package docs, (keep existing compat) (#1641)
* CompatHelper: bump compat for Documenter to 1 for package docs, (keep existing compat)
* remove strict since it is removed and active by default
* allow only v1 of Documenter.jl
* ignore size threshold for API reference of Trixi.jl
* try to fix size_threshold_ignore
---------
Co-authored-by: CompatHelper Julia
Co-authored-by: Hendrik Ranocha
Co-authored-by: Hendrik Ranocha
---
docs/Project.toml | 2 +-
docs/make.jl | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/docs/Project.toml b/docs/Project.toml
index 9fc974d6f38..ffa86e0b9f7 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -13,7 +13,7 @@ Trixi2Vtk = "bc1476a1-1ca6-4cc3-950b-c312b255ff95"
[compat]
CairoMakie = "0.6, 0.7, 0.8, 0.9, 0.10"
-Documenter = "0.27"
+Documenter = "1"
ForwardDiff = "0.10"
HOHQMesh = "0.1, 0.2"
LaTeXStrings = "1.2"
diff --git a/docs/make.jl b/docs/make.jl
index f882fcf1219..df8ac04be12 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -77,7 +77,7 @@ makedocs(
# Specify modules for which docstrings should be shown
modules = [Trixi, Trixi2Vtk],
# Set sitename to Trixi.jl
- sitename="Trixi.jl",
+ sitename = "Trixi.jl",
# Provide additional formatting options
format = Documenter.HTML(
# Disable pretty URLs during manual testing
@@ -85,7 +85,8 @@ makedocs(
# Explicitly add favicon as asset
assets = ["assets/favicon.ico"],
# Set canonical URL to GitHub pages URL
- canonical = "https://trixi-framework.github.io/Trixi.jl/stable"
+ canonical = "https://trixi-framework.github.io/Trixi.jl/stable",
+ size_threshold_ignore = ["reference-trixi.md"]
),
# Explicitly specify documentation structure
pages = [
@@ -124,9 +125,8 @@ makedocs(
"Authors" => "authors.md",
"Contributing" => "contributing.md",
"Code of Conduct" => "code_of_conduct.md",
- "License" => "license.md"
- ],
- strict = true # to make the GitHub action fail when doctests fail, see https://github.com/neuropsychology/Psycho.jl/issues/34
+ "License" => "license.md",
+ ]
)
deploydocs(
From 7e228985a993e6e50249e8a13d9116db9f14e3bb Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Wed, 20 Sep 2023 08:03:13 +0200
Subject: [PATCH 105/263] Increase type stab, avoid allocs (#1642)
* Increase type stab, avoid allocs
* format
* test seems no longer broken
* only essentials
Co-authored-by: Hendrik Ranocha
---
src/solvers/dgsem_unstructured/dg_2d.jl | 4 ++--
test/test_threaded.jl | 6 +-----
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/src/solvers/dgsem_unstructured/dg_2d.jl b/src/solvers/dgsem_unstructured/dg_2d.jl
index 7b8dafdddd2..b12a96c4c31 100644
--- a/src/solvers/dgsem_unstructured/dg_2d.jl
+++ b/src/solvers/dgsem_unstructured/dg_2d.jl
@@ -357,9 +357,9 @@ function calc_boundary_flux_by_type!(cache, t, BCs::Tuple{}, BC_indices::Tuple{}
nothing
end
-function calc_boundary_flux!(cache, t, boundary_condition, boundary_indexing,
+function calc_boundary_flux!(cache, t, boundary_condition::BC, boundary_indexing,
mesh::UnstructuredMesh2D, equations,
- surface_integral, dg::DG)
+ surface_integral, dg::DG) where {BC}
@unpack surface_flux_values = cache.elements
@unpack element_id, element_side_id = cache.boundaries
diff --git a/test/test_threaded.jl b/test/test_threaded.jl
index 2337d73f30a..b13b5d0f5fc 100644
--- a/test/test_threaded.jl
+++ b/test/test_threaded.jl
@@ -312,11 +312,7 @@ Trixi.mpi_isroot() && isdir(outdir) && rm(outdir, recursive=true)
t = sol.t[end]
u_ode = sol.u[end]
du_ode = similar(u_ode)
- if (Threads.nthreads() < 2) || (VERSION < v"1.9")
- @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
- else
- @test_broken (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
- end
+ @test (@allocated Trixi.rhs!(du_ode, u_ode, semi, t)) < 5000
end
end
From 09441e1c0553cfc1e88f582354457233d4d872b2 Mon Sep 17 00:00:00 2001
From: Michael Schlottke-Lakemper
Date: Wed, 20 Sep 2023 09:15:27 +0200
Subject: [PATCH 106/263] Add Aqua.jl testing (#1628)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Add Aqua.jl testing
* [deps] before [compat]
* Skip ambiguities (too many false positives) and account for @jlchan being a 🏴☠️
* Bump Flux minimum version
* Adapt test values
* Add back pre-v0.14 version for Flux.jl to satisfy Julia v1.8
* Add Aqua badge
* Explain piracy exceptions
---------
Co-authored-by: Hendrik Ranocha
---
README.md | 1 +
docs/src/index.md | 1 +
test/Project.toml | 20 +++++++++++---------
test/runtests.jl | 1 +
test/test_aqua.jl | 18 ++++++++++++++++++
test/test_tree_2d_euler.jl | 2 +-
6 files changed, 33 insertions(+), 10 deletions(-)
create mode 100644 test/test_aqua.jl
diff --git a/README.md b/README.md
index c177ad2347f..673708d8b89 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@
[![Build Status](https://github.com/trixi-framework/Trixi.jl/workflows/CI/badge.svg)](https://github.com/trixi-framework/Trixi.jl/actions?query=workflow%3ACI)
[![Codecov](https://codecov.io/gh/trixi-framework/Trixi.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/trixi-framework/Trixi.jl)
[![Coveralls](https://coveralls.io/repos/github/trixi-framework/Trixi.jl/badge.svg?branch=main)](https://coveralls.io/github/trixi-framework/Trixi.jl?branch=main)
+[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)
[![License: MIT](https://img.shields.io/badge/License-MIT-success.svg)](https://opensource.org/licenses/MIT)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3996439.svg)](https://doi.org/10.5281/zenodo.3996439)
[![Downloads](https://shields.io/endpoint?url=https://pkgs.genieframework.com/api/v1/badge/Trixi)](https://pkgs.genieframework.com?packages=Trixi)
diff --git a/docs/src/index.md b/docs/src/index.md
index bb2afd1019f..9ffaee26c40 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -7,6 +7,7 @@
[![Build Status](https://github.com/trixi-framework/Trixi.jl/workflows/CI/badge.svg)](https://github.com/trixi-framework/Trixi.jl/actions?query=workflow%3ACI)
[![Codecov](https://codecov.io/gh/trixi-framework/Trixi.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/trixi-framework/Trixi.jl)
[![Coveralls](https://coveralls.io/repos/github/trixi-framework/Trixi.jl/badge.svg?branch=main)](https://coveralls.io/github/trixi-framework/Trixi.jl?branch=main)
+[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)
[![License: MIT](https://img.shields.io/badge/License-MIT-success.svg)](https://opensource.org/licenses/MIT)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3996439.svg)](https://doi.org/10.5281/zenodo.3996439)
diff --git a/test/Project.toml b/test/Project.toml
index 7115a19b441..c45be49a5d0 100644
--- a/test/Project.toml
+++ b/test/Project.toml
@@ -1,13 +1,5 @@
-[compat]
-BSON = "0.3.3"
-CairoMakie = "0.6, 0.7, 0.8, 0.9, 0.10"
-Flux = "0.13 - 0.13.12" # TODO: Return to "0.13" once https://github.com/FluxML/Flux.jl/issues/2204 is resolved
-ForwardDiff = "0.10"
-MPI = "0.20"
-OrdinaryDiffEq = "6.49.1"
-Plots = "1.16"
-
[deps]
+Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
BSON = "fbb218c0-5317-5bc6-957e-2ee96dd4b1f0"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
@@ -21,6 +13,16 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
+[compat]
+Aqua = "0.7"
+BSON = "0.3.3"
+CairoMakie = "0.6, 0.7, 0.8, 0.9, 0.10"
+Flux = "0.13.15, 0.14"
+ForwardDiff = "0.10"
+MPI = "0.20"
+OrdinaryDiffEq = "6.49.1"
+Plots = "1.16"
+
[preferences.OrdinaryDiffEq]
PrecompileAutoSpecialize = false
PrecompileAutoSwitch = false
diff --git a/test/runtests.jl b/test/runtests.jl
index f1adbaaf1df..7e195fe7402 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -109,6 +109,7 @@ const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3)
@time if TRIXI_TEST == "all" || TRIXI_TEST == "misc_part2"
include("test_special_elixirs.jl")
+ include("test_aqua.jl")
end
@time if TRIXI_TEST == "all" || TRIXI_TEST == "performance_specializations_part1"
diff --git a/test/test_aqua.jl b/test/test_aqua.jl
new file mode 100644
index 00000000000..f7ab4f545d0
--- /dev/null
+++ b/test/test_aqua.jl
@@ -0,0 +1,18 @@
+module TestAqua
+
+using Aqua
+using Test
+using Trixi
+
+include("test_trixi.jl")
+
+@timed_testset "Aqua.jl" begin
+ Aqua.test_all(Trixi,
+ ambiguities = false,
+ # exceptions necessary for adding a new method `StartUpDG.estimate_h`
+ # in src/solvers/dgmulti/sbp.jl
+ piracy = (treat_as_own = [Trixi.StartUpDG.RefElemData,
+ Trixi.StartUpDG.MeshData],))
+end
+
+end #module
diff --git a/test/test_tree_2d_euler.jl b/test/test_tree_2d_euler.jl
index e1e3ad32e7d..1b8a261a60d 100644
--- a/test/test_tree_2d_euler.jl
+++ b/test/test_tree_2d_euler.jl
@@ -140,7 +140,7 @@ EXAMPLES_DIR = pkgdir(Trixi, "examples", "tree_2d_dgsem")
@trixi_testset "elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl" begin
@test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_euler_sedov_blast_wave_neuralnetwork_perssonperaire.jl"),
l2 = [0.0845430093623868, 0.09271459184623232, 0.09271459184623232, 0.4377291875101709],
- linf = [1.3608553480069898, 1.6822884847136004, 1.6822884847135997, 4.220147414536653],
+ linf = [1.3608553480069898, 1.6822884847136004, 1.6822884847135997, 4.2201475428867035],
maxiters = 30,
coverage_override = (maxiters=6,))
end
From ea4e2cd0863893c16ff5cb2b0091e76c5a9b9b4e Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Wed, 20 Sep 2023 16:15:47 +0200
Subject: [PATCH 107/263] @muladd for Navier Stokes (#1644)
* introduce muladd, update AMR tests
* format
---
.../compressible_navier_stokes_1d.jl | 38 +++++++++----
.../compressible_navier_stokes_2d.jl | 56 +++++++++++++------
.../compressible_navier_stokes_3d.jl | 47 +++++++++++-----
test/test_parabolic_1d.jl | 8 +--
4 files changed, 106 insertions(+), 43 deletions(-)
diff --git a/src/equations/compressible_navier_stokes_1d.jl b/src/equations/compressible_navier_stokes_1d.jl
index dca846cac1e..74d672ce7ae 100644
--- a/src/equations/compressible_navier_stokes_1d.jl
+++ b/src/equations/compressible_navier_stokes_1d.jl
@@ -1,3 +1,10 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
@doc raw"""
CompressibleNavierStokesDiffusion1D(equations; mu, Pr,
gradient_variables=GradientVariablesPrimitive())
@@ -77,7 +84,8 @@ w_2 = \frac{\rho v1}{p},\, w_3 = -\frac{\rho}{p}
This code is experimental and may be changed or removed in any future release.
"""
struct CompressibleNavierStokesDiffusion1D{GradientVariables, RealT <: Real,
- E <: AbstractCompressibleEulerEquations{1}} <:
+ E <: AbstractCompressibleEulerEquations{1}
+ } <:
AbstractCompressibleNavierStokesDiffusion{1, 3}
# TODO: parabolic
# 1) For now save gamma and inv(gamma-1) again, but could potentially reuse them from the Euler equations
@@ -109,7 +117,8 @@ function CompressibleNavierStokesDiffusion1D(equations::CompressibleEulerEquatio
CompressibleNavierStokesDiffusion1D{typeof(gradient_variables), typeof(gamma),
typeof(equations)}(gamma, inv_gamma_minus_one,
μ, Pr, kappa,
- equations, gradient_variables)
+ equations,
+ gradient_variables)
end
# TODO: parabolic
@@ -263,7 +272,8 @@ end
u_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesPrimitive
@@ -278,7 +288,8 @@ end
u_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesPrimitive
@@ -299,7 +310,8 @@ end
u_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesPrimitive
@@ -316,7 +328,8 @@ end
u_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesPrimitive
@@ -337,7 +350,8 @@ end
w_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesEntropy
@@ -354,7 +368,8 @@ end
w_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesEntropy
@@ -374,7 +389,8 @@ end
w_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesEntropy
@@ -394,10 +410,12 @@ end
w_inner,
orientation::Integer,
direction,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion1D{
GradientVariablesEntropy
})
return SVector(flux_inner[1], flux_inner[2], flux_inner[3])
end
+end # @muladd
diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl
index f762fe5d5ee..b10ffa3b9d3 100644
--- a/src/equations/compressible_navier_stokes_2d.jl
+++ b/src/equations/compressible_navier_stokes_2d.jl
@@ -1,3 +1,10 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
@doc raw"""
CompressibleNavierStokesDiffusion2D(equations; mu, Pr,
gradient_variables=GradientVariablesPrimitive())
@@ -77,7 +84,8 @@ w_2 = \frac{\rho v_1}{p},\, w_3 = \frac{\rho v_2}{p},\, w_4 = -\frac{\rho}{p}
This code is experimental and may be changed or removed in any future release.
"""
struct CompressibleNavierStokesDiffusion2D{GradientVariables, RealT <: Real,
- E <: AbstractCompressibleEulerEquations{2}} <:
+ E <: AbstractCompressibleEulerEquations{2}
+ } <:
AbstractCompressibleNavierStokesDiffusion{2, 4}
# TODO: parabolic
# 1) For now save gamma and inv(gamma-1) again, but could potentially reuse them from the Euler equations
@@ -109,7 +117,8 @@ function CompressibleNavierStokesDiffusion2D(equations::CompressibleEulerEquatio
CompressibleNavierStokesDiffusion2D{typeof(gradient_variables), typeof(gamma),
typeof(equations)}(gamma, inv_gamma_minus_one,
μ, Pr, kappa,
- equations, gradient_variables)
+ equations,
+ gradient_variables)
end
# TODO: parabolic
@@ -301,12 +310,14 @@ end
<:Adiabatic})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesPrimitive
})
- v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
+ t,
equations)
return SVector(u_inner[1], v1, v2, u_inner[4])
end
@@ -315,7 +326,8 @@ end
<:Adiabatic})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesPrimitive
@@ -324,7 +336,8 @@ end
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
t,
equations)
- v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
+ t,
equations)
_, tau_1n, tau_2n, _ = flux_inner # extract fluxes for 2nd and 3rd equations
normal_energy_flux = v1 * tau_1n + v2 * tau_2n + normal_heat_flux
@@ -335,12 +348,14 @@ end
<:Isothermal})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesPrimitive
})
- v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
+ t,
equations)
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
equations)
@@ -351,7 +366,8 @@ end
<:Isothermal})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesPrimitive
@@ -371,12 +387,14 @@ end
<:Adiabatic})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesEntropy
})
- v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
+ t,
equations)
negative_rho_inv_p = w_inner[4] # w_4 = -rho / p
return SVector(w_inner[1], -v1 * negative_rho_inv_p, -v2 * negative_rho_inv_p,
@@ -388,7 +406,8 @@ end
<:Adiabatic})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesEntropy
@@ -396,7 +415,8 @@ end
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
t,
equations)
- v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
+ t,
equations)
_, tau_1n, tau_2n, _ = flux_inner # extract fluxes for 2nd and 3rd equations
normal_energy_flux = v1 * tau_1n + v2 * tau_2n + normal_heat_flux
@@ -407,12 +427,14 @@ end
<:Isothermal})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesEntropy
})
- v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
+ v1, v2 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
+ t,
equations)
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
equations)
@@ -426,10 +448,12 @@ end
<:Isothermal})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion2D{
GradientVariablesEntropy
})
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4])
end
+end # @muladd
diff --git a/src/equations/compressible_navier_stokes_3d.jl b/src/equations/compressible_navier_stokes_3d.jl
index 166b53bf615..de2cad99ea8 100644
--- a/src/equations/compressible_navier_stokes_3d.jl
+++ b/src/equations/compressible_navier_stokes_3d.jl
@@ -1,3 +1,10 @@
+# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
+# Since these FMAs can increase the performance of many numerical algorithms,
+# we need to opt-in explicitly.
+# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
+@muladd begin
+#! format: noindent
+
@doc raw"""
CompressibleNavierStokesDiffusion3D(equations; mu, Pr,
gradient_variables=GradientVariablesPrimitive())
@@ -77,7 +84,8 @@ w_2 = \frac{\rho v_1}{p},\, w_3 = \frac{\rho v_2}{p},\, w_4 = \frac{\rho v_3}{p}
This code is experimental and may be changed or removed in any future release.
"""
struct CompressibleNavierStokesDiffusion3D{GradientVariables, RealT <: Real,
- E <: AbstractCompressibleEulerEquations{3}} <:
+ E <: AbstractCompressibleEulerEquations{3}
+ } <:
AbstractCompressibleNavierStokesDiffusion{3, 5}
# TODO: parabolic
# 1) For now save gamma and inv(gamma-1) again, but could potentially reuse them from the Euler equations
@@ -109,7 +117,8 @@ function CompressibleNavierStokesDiffusion3D(equations::CompressibleEulerEquatio
CompressibleNavierStokesDiffusion3D{typeof(gradient_variables), typeof(gamma),
typeof(equations)}(gamma, inv_gamma_minus_one,
μ, Pr, kappa,
- equations, gradient_variables)
+ equations,
+ gradient_variables)
end
# TODO: parabolic
@@ -319,9 +328,12 @@ end
@inline function vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
# Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.
- _, dv1dx, dv2dx, dv3dx, _ = convert_derivative_to_primitive(u, gradients[1], equations)
- _, dv1dy, dv2dy, dv3dy, _ = convert_derivative_to_primitive(u, gradients[2], equations)
- _, dv1dz, dv2dz, dv3dz, _ = convert_derivative_to_primitive(u, gradients[3], equations)
+ _, dv1dx, dv2dx, dv3dx, _ = convert_derivative_to_primitive(u, gradients[1],
+ equations)
+ _, dv1dy, dv2dy, dv3dy, _ = convert_derivative_to_primitive(u, gradients[2],
+ equations)
+ _, dv1dz, dv2dz, dv3dz, _ = convert_derivative_to_primitive(u, gradients[3],
+ equations)
return SVector(dv3dy - dv2dz, dv1dz - dv3dx, dv2dx - dv1dy)
end
@@ -330,7 +342,8 @@ end
<:Adiabatic})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesPrimitive
@@ -345,7 +358,8 @@ end
<:Adiabatic})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesPrimitive
@@ -367,7 +381,8 @@ end
<:Isothermal})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesPrimitive
@@ -384,7 +399,8 @@ end
<:Isothermal})(flux_inner,
u_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesPrimitive
@@ -404,7 +420,8 @@ end
<:Adiabatic})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesEntropy
@@ -422,7 +439,8 @@ end
<:Adiabatic})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesEntropy
@@ -443,7 +461,8 @@ end
<:Isothermal})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Gradient,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesEntropy
@@ -463,7 +482,8 @@ end
<:Isothermal})(flux_inner,
w_inner,
normal::AbstractVector,
- x, t,
+ x,
+ t,
operator_type::Divergence,
equations::CompressibleNavierStokesDiffusion3D{
GradientVariablesEntropy
@@ -471,3 +491,4 @@ end
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
flux_inner[5])
end
+end # @muladd
diff --git a/test/test_parabolic_1d.jl b/test/test_parabolic_1d.jl
index 3c2b8855ce8..f00138c698c 100644
--- a/test/test_parabolic_1d.jl
+++ b/test/test_parabolic_1d.jl
@@ -80,8 +80,8 @@ isdir(outdir) && rm(outdir, recursive=true)
@test_trixi_include(joinpath(examples_dir(), "tree_1d_dgsem", "elixir_navierstokes_convergence_walls_amr.jl"),
equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
Prandtl=prandtl_number()),
- l2 = [2.527877257772131e-5, 2.5539911566937718e-5, 0.0001211860451244785],
- linf = [0.00014663867588948776, 0.00019422448348348196, 0.0009556439394007299]
+ l2 = [2.5278824700860636e-5, 2.5540078777006958e-5, 0.00012118655083858043],
+ linf = [0.0001466387075579334, 0.00019422427462629705, 0.0009556446847707178]
)
end
@@ -90,8 +90,8 @@ isdir(outdir) && rm(outdir, recursive=true)
equations_parabolic = CompressibleNavierStokesDiffusion1D(equations, mu=mu(),
Prandtl=prandtl_number(),
gradient_variables = GradientVariablesEntropy()),
- l2 = [2.4593699163175966e-5, 2.392863645712634e-5, 0.00011252526651714956],
- linf = [0.00011850555445525046, 0.0001898777490968537, 0.0009597561467877824]
+ l2 = [2.459359632523962e-5, 2.3928390718460263e-5, 0.00011252414117082376],
+ linf = [0.0001185052018830568, 0.00018987717854305393, 0.0009597503607920999]
)
end
end
From 3bda0519dbd084937cd05eb339a4d35b1a6f4d9f Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Fri, 22 Sep 2023 13:14:40 +0200
Subject: [PATCH 108/263] Print leaf cells for tree meshes (#1645)
---
src/meshes/tree_mesh.jl | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/meshes/tree_mesh.jl b/src/meshes/tree_mesh.jl
index 93ba982bce9..05699d17d16 100644
--- a/src/meshes/tree_mesh.jl
+++ b/src/meshes/tree_mesh.jl
@@ -199,6 +199,7 @@ function Base.show(io::IO, ::MIME"text/plain",
"length" => mesh.tree.length_level_0,
"periodicity" => mesh.tree.periodicity,
"current #cells" => mesh.tree.length,
+ "#leaf-cells" => count_leaf_cells(mesh.tree),
"maximum #cells" => mesh.tree.capacity,
]
summary_box(io, "TreeMesh{" * string(NDIMS) * ", " * string(TreeType) * "}",
From 5b203620b2b2b5eb009c2076a43d99b42f37f852 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 22 Sep 2023 14:40:55 +0200
Subject: [PATCH 109/263] set version to v0.5.44
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index d134a8e548b..fe062b1afaf 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.44-pre"
+version = "0.5.44"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 0b4bf985846388b8cfee8dc4a2ce462b20b5d533 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Fri, 22 Sep 2023 14:41:17 +0200
Subject: [PATCH 110/263] set development version to v0.5.45-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index fe062b1afaf..69b2e872b6f 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.44"
+version = "0.5.45-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From dc9b89fc48e2239dae1108d0719496d956ec19b3 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Tue, 26 Sep 2023 09:04:46 +0200
Subject: [PATCH 111/263] bump compat for SciMLBase.jl to v2 (#1647)
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index 69b2e872b6f..d318389a6d2 100644
--- a/Project.toml
+++ b/Project.toml
@@ -71,7 +71,7 @@ PrecompileTools = "1.1"
RecipesBase = "1.1"
Reexport = "1.0"
Requires = "1.1"
-SciMLBase = "1.90"
+SciMLBase = "1.90, 2"
Setfield = "0.8, 1"
SimpleUnPack = "1.1"
StartUpDG = "0.17"
From e45700d8453e848e069739ea3dee51d721185f71 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 1 Oct 2023 20:48:04 +0200
Subject: [PATCH 112/263] Bump crate-ci/typos from 1.16.9 to 1.16.15 (#1652)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.16.9 to 1.16.15.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.16.9...v1.16.15)
---
updated-dependencies:
- dependency-name: crate-ci/typos
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/SpellCheck.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index a06121e7ca1..e608dc8d7c1 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -10,4 +10,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling
- uses: crate-ci/typos@v1.16.9
+ uses: crate-ci/typos@v1.16.15
From 0cf3e6768684f20e025411e5a10e4c6f41c928c6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 1 Oct 2023 22:17:39 +0200
Subject: [PATCH 113/263] Bump actions/checkout from 3 to 4 (#1653)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Schlottke-Lakemper
---
.github/workflows/CacheNotebooks.yml | 2 +-
.github/workflows/DocPreviewCleanup.yml | 2 +-
.github/workflows/Documenter.yml | 2 +-
.github/workflows/FormatCheck.yml | 2 +-
.github/workflows/Invalidations.yml | 4 ++--
.github/workflows/ReviewChecklist.yml | 2 +-
.github/workflows/SpellCheck.yml | 2 +-
.github/workflows/benchmark.yml | 2 +-
.github/workflows/ci.yml | 4 ++--
9 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/CacheNotebooks.yml b/.github/workflows/CacheNotebooks.yml
index c8599d13f26..f89560d8158 100644
--- a/.github/workflows/CacheNotebooks.yml
+++ b/.github/workflows/CacheNotebooks.yml
@@ -11,7 +11,7 @@ jobs:
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout caching branch
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
ref: tutorial_notebooks
diff --git a/.github/workflows/DocPreviewCleanup.yml b/.github/workflows/DocPreviewCleanup.yml
index 66d0b342b2e..0850369c9cc 100644
--- a/.github/workflows/DocPreviewCleanup.yml
+++ b/.github/workflows/DocPreviewCleanup.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout gh-pages branch
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
ref: gh-pages
diff --git a/.github/workflows/Documenter.yml b/.github/workflows/Documenter.yml
index 6b557960c89..129c41a3b5c 100644
--- a/.github/workflows/Documenter.yml
+++ b/.github/workflows/Documenter.yml
@@ -33,7 +33,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v1
with:
version: '1.9'
diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml
index 628d938dd76..ce46360b832 100644
--- a/.github/workflows/FormatCheck.yml
+++ b/.github/workflows/FormatCheck.yml
@@ -20,7 +20,7 @@ jobs:
with:
version: ${{ matrix.julia-version }}
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install JuliaFormatter and format
# This will use the latest version by default but you can set the version like so:
#
diff --git a/.github/workflows/Invalidations.yml b/.github/workflows/Invalidations.yml
index ba81f83e0ad..18048d26be8 100644
--- a/.github/workflows/Invalidations.yml
+++ b/.github/workflows/Invalidations.yml
@@ -19,12 +19,12 @@ jobs:
- uses: julia-actions/setup-julia@v1
with:
version: '1'
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-invalidations@v1
id: invs_pr
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
ref: ${{ github.event.repository.default_branch }}
- uses: julia-actions/julia-buildpkg@v1
diff --git a/.github/workflows/ReviewChecklist.yml b/.github/workflows/ReviewChecklist.yml
index 959a04752d7..d8854411804 100644
--- a/.github/workflows/ReviewChecklist.yml
+++ b/.github/workflows/ReviewChecklist.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Add review checklist
uses: trixi-framework/add-pr-review-checklist@v1
with:
diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml
index e608dc8d7c1..eae6d8e0be9 100644
--- a/.github/workflows/SpellCheck.yml
+++ b/.github/workflows/SpellCheck.yml
@@ -8,6 +8,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Check spelling
uses: crate-ci/typos@v1.16.15
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index c5c95558c8c..2ea30d6fddb 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -15,7 +15,7 @@ jobs:
arch:
- x64
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- run: |
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4790f93d913..cf8107736e9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -101,7 +101,7 @@ jobs:
arch: x64
trixi_test: threaded
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
@@ -175,7 +175,7 @@ jobs:
# Instead, we use the more tedious approach described above.
# At first, we check out the repository and download all artifacts
# (and list files for debugging).
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- run: ls -R
# Next, we merge the individual coverage files and upload
From 9e41775351ddf1d92b32bbd6c685536ee004466c Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Mon, 2 Oct 2023 01:16:37 -0500
Subject: [PATCH 114/263] Add updates and "parabolic terms" row to overview.md
(#1651)
* update overview.md
* add note on parabolic terms
---
docs/src/overview.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/docs/src/overview.md b/docs/src/overview.md
index 51a6272ae8e..9cd11a5df93 100644
--- a/docs/src/overview.md
+++ b/docs/src/overview.md
@@ -55,14 +55,15 @@ different features on different mesh types.
| Element type | line, square, cube | line, quadᵃ, hexᵃ | quadᵃ | quadᵃ, hexᵃ | simplex, quadᵃ, hexᵃ |
| Adaptive mesh refinement | ✅ | ❌ | ❌ | ✅ | ❌ | [`AMRCallback`](@ref)
| Solver type | [`DGSEM`](@ref) | [`DGSEM`](@ref) | [`DGSEM`](@ref) | [`DGSEM`](@ref) | [`DGMulti`](@ref) |
-| Domain | hypercube | mapped hypercube | arbitrary | arbitrary | arbitraryᵇ |
+| Domain | hypercube | mapped hypercube | arbitrary | arbitrary | arbitrary |
| Weak form | ✅ | ✅ | ✅ | ✅ | ✅ | [`VolumeIntegralWeakForm`](@ref)
| Flux differencing | ✅ | ✅ | ✅ | ✅ | ✅ | [`VolumeIntegralFluxDifferencing`](@ref)
| Shock capturing | ✅ | ✅ | ✅ | ✅ | ❌ | [`VolumeIntegralShockCapturingHG`](@ref)
| Nonconservative equations | ✅ | ✅ | ✅ | ✅ | ✅ | e.g., GLM MHD or shallow water equations
+| Parabolic termsᵇ | ✅ | ✅ | ❌ | ✅ | ✅ | e.g., [`CompressibleNavierStokesDiffusion2D`](@ref)
ᵃ: quad = quadrilateral, hex = hexahedron
-ᵇ: curved meshes supported for `SBP` and `GaussSBP` approximation types for `VolumeIntegralFluxDifferencing` solvers on quadrilateral and hexahedral `DGMultiMesh`es (non-conservative terms not yet supported)
+ᵇ: Parabolic terms do not currently support adaptivity.
## Time integration methods
From 7ba1f2ef98768f297442b026e4acf168bad73ff8 Mon Sep 17 00:00:00 2001
From: Benjamin Bolm <74359358+bennibolm@users.noreply.github.com>
Date: Wed, 4 Oct 2023 10:00:36 +0200
Subject: [PATCH 115/263] Fix n_elements in resizing containers (#1655)
---
src/time_integration/methods_SSP.jl | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/time_integration/methods_SSP.jl b/src/time_integration/methods_SSP.jl
index a0ed889968a..33eb6ebf926 100644
--- a/src/time_integration/methods_SSP.jl
+++ b/src/time_integration/methods_SSP.jl
@@ -226,7 +226,9 @@ function Base.resize!(integrator::SimpleIntegratorSSP, new_size)
resize!(integrator.r0, new_size)
# Resize container
- resize!(integrator.p, new_size)
+ # new_size = n_variables * n_nodes^n_dims * n_elements
+ n_elements = nelements(integrator.p.solver, integrator.p.cache)
+ resize!(integrator.p, n_elements)
end
function Base.resize!(semi::AbstractSemidiscretization, new_size)
From e245dc2638e22a0ec396786ad86992f4de189192 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Wed, 4 Oct 2023 16:07:09 +0200
Subject: [PATCH 116/263] Use Analysis CB (#1650)
* Use Analysis CB
* Apply suggestions from code review
---------
Co-authored-by: Hendrik Ranocha
---
examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl b/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl
index 81e48737e79..3314343ccca 100644
--- a/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl
+++ b/examples/tree_2d_dgsem/elixir_navierstokes_lid_driven_cavity.jl
@@ -66,7 +66,7 @@ summary_callback = SummaryCallback()
alive_callback = AliveCallback(alive_interval=100)
analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
-callbacks = CallbackSet(summary_callback, alive_callback)
+callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
###############################################################################
# run the simulation
From 3f4b0f445493df3cf122e20e7a8d71b1f7def686 Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Thu, 5 Oct 2023 00:32:50 -0500
Subject: [PATCH 117/263] add comments on sign flips (#1657)
---
src/solvers/dgsem_p4est/dg_2d.jl | 5 +++++
src/solvers/dgsem_p4est/dg_3d.jl | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/src/solvers/dgsem_p4est/dg_2d.jl b/src/solvers/dgsem_p4est/dg_2d.jl
index a665aa4b19d..36624f2ce8a 100644
--- a/src/solvers/dgsem_p4est/dg_2d.jl
+++ b/src/solvers/dgsem_p4est/dg_2d.jl
@@ -501,6 +501,11 @@ function calc_mortar_flux!(surface_flux_values,
# copying in the correct orientation
u_buffer = cache.u_threaded[Threads.threadid()]
+ # in calc_interface_flux!, the interface flux is computed once over each
+ # interface using the normal from the "primary" element. The result is then
+ # passed back to the "secondary" element, flipping the sign to account for the
+ # change in the normal direction. For mortars, this sign flip occurs in
+ # "mortar_fluxes_to_elements!" instead.
mortar_fluxes_to_elements!(surface_flux_values,
mesh, equations, mortar_l2, dg, cache,
mortar, fstar, u_buffer)
diff --git a/src/solvers/dgsem_p4est/dg_3d.jl b/src/solvers/dgsem_p4est/dg_3d.jl
index dc69329474f..4c0845ba9af 100644
--- a/src/solvers/dgsem_p4est/dg_3d.jl
+++ b/src/solvers/dgsem_p4est/dg_3d.jl
@@ -580,6 +580,11 @@ function calc_mortar_flux!(surface_flux_values,
# copying in the correct orientation
u_buffer = cache.u_threaded[Threads.threadid()]
+ # in calc_interface_flux!, the interface flux is computed once over each
+ # interface using the normal from the "primary" element. The result is then
+ # passed back to the "secondary" element, flipping the sign to account for the
+ # change in the normal direction. For mortars, this sign flip occurs in
+ # "mortar_fluxes_to_elements!" instead.
mortar_fluxes_to_elements!(surface_flux_values,
mesh, equations, mortar_l2, dg, cache,
mortar, fstar, u_buffer, fstar_tmp)
From bd98469a827b0d0d5a9be50b95e841738ef85c6c Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Thu, 5 Oct 2023 07:37:05 +0200
Subject: [PATCH 118/263] add docs of set_libraries! for setting custom HDF5
library (#1658)
---
docs/src/parallelization.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/src/parallelization.md b/docs/src/parallelization.md
index e55471bb256..610aa2cbd95 100644
--- a/docs/src/parallelization.md
+++ b/docs/src/parallelization.md
@@ -186,6 +186,11 @@ julia> set_preferences!(
"libhdf5" => "/path/to/your/libhdf5.so",
"libhdf5_hl" => "/path/to/your/libhdf5_hl.so", force = true)
```
+Alternatively, with HDF5.jl v0.17.1 or higher you can use
+```julia
+julia> using HDF5
+julia> HDF5.API.set_libraries!("/path/to/your/libhdf5.so", "/path/to/your/libhdf5_hl.so")
+```
For more information see also the
[documentation of HDF5.jl](https://juliaio.github.io/HDF5.jl/stable/mpi/). In total, you should
have a file called LocalPreferences.toml in the project directory that contains a section
From adce486c11fb20adc4802c959f3b63be3df6bb8a Mon Sep 17 00:00:00 2001
From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com>
Date: Thu, 5 Oct 2023 07:39:33 +0200
Subject: [PATCH 119/263] Fix mapping string in `StructuredMesh` (#1654)
* Fix mapping string in `StructuredMesh`
* `eval` full string to avoid using `Meta.parseall`
* Make `mapping_as_string` consistent
---------
Co-authored-by: Hendrik Ranocha
---
src/meshes/mesh_io.jl | 23 +++++++++--------------
src/meshes/structured_mesh.jl | 19 ++++++++++++-------
2 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/src/meshes/mesh_io.jl b/src/meshes/mesh_io.jl
index b9895e7d454..92e38ce1bf3 100644
--- a/src/meshes/mesh_io.jl
+++ b/src/meshes/mesh_io.jl
@@ -263,32 +263,27 @@ function load_mesh_serial(mesh_file::AbstractString; n_cells_max, RealT)
size = Tuple(size_)
# TODO: `@eval` is evil
- # A temporary workaround to evaluate the code that defines the domain mapping in a local scope.
- # This prevents errors when multiple restart elixirs are executed in one session, where one
- # defines `mapping` as a variable, while the other defines it as a function.
#
# This should be replaced with something more robust and secure,
# see https://github.com/trixi-framework/Trixi.jl/issues/541).
- expr = Meta.parse(mapping_as_string)
- if expr.head == :toplevel
- expr.head = :block
- end
-
if ndims == 1
- mapping = @eval function (xi)
- $expr
+ mapping = eval(Meta.parse("""function (xi)
+ $mapping_as_string
mapping(xi)
end
+ """))
elseif ndims == 2
- mapping = @eval function (xi, eta)
- $expr
+ mapping = eval(Meta.parse("""function (xi, eta)
+ $mapping_as_string
mapping(xi, eta)
end
+ """))
else # ndims == 3
- mapping = @eval function (xi, eta, zeta)
- $expr
+ mapping = eval(Meta.parse("""function (xi, eta, zeta)
+ $mapping_as_string
mapping(xi, eta, zeta)
end
+ """))
end
mesh = StructuredMesh(size, mapping; RealT = RealT, unsaved_changes = false,
diff --git a/src/meshes/structured_mesh.jl b/src/meshes/structured_mesh.jl
index df067db833d..553aabbbc20 100644
--- a/src/meshes/structured_mesh.jl
+++ b/src/meshes/structured_mesh.jl
@@ -96,13 +96,17 @@ function StructuredMesh(cells_per_dimension, faces::Tuple; RealT = Float64,
# Collect definitions of face functions in one string (separated by semicolons)
face2substring(face) = code_string(face, ntuple(_ -> Float64, NDIMS - 1))
- join_semicolon(strings) = join(strings, "; ")
+ join_newline(strings) = join(strings, "\n")
- faces_definition = faces .|> face2substring .|> string |> join_semicolon
+ faces_definition = faces .|> face2substring .|> string |> join_newline
# Include faces definition in `mapping_as_string` to allow for evaluation
# without knowing the face functions
- mapping_as_string = "$faces_definition; faces = $(string(faces)); mapping = transfinite_mapping(faces)"
+ mapping_as_string = """
+ $faces_definition
+ faces = $(string(faces))
+ mapping = transfinite_mapping(faces)
+ """
return StructuredMesh(cells_per_dimension, mapping; RealT = RealT,
periodicity = periodicity,
@@ -123,13 +127,14 @@ Create a StructuredMesh that represents a uncurved structured mesh with a rectan
"""
function StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max;
periodicity = true)
- NDIMS = length(cells_per_dimension)
RealT = promote_type(eltype(coordinates_min), eltype(coordinates_max))
mapping = coordinates2mapping(coordinates_min, coordinates_max)
- mapping_as_string = "coordinates_min = $coordinates_min; " *
- "coordinates_max = $coordinates_max; " *
- "mapping = coordinates2mapping(coordinates_min, coordinates_max)"
+ mapping_as_string = """
+ coordinates_min = $coordinates_min
+ coordinates_max = $coordinates_max
+ mapping = coordinates2mapping(coordinates_min, coordinates_max)
+ """
return StructuredMesh(cells_per_dimension, mapping; RealT = RealT,
periodicity = periodicity,
mapping_as_string = mapping_as_string)
From 67ddfbba006f40334e415e1bb4e60aea413d8388 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Thu, 5 Oct 2023 15:52:49 +0200
Subject: [PATCH 120/263] Avoid allocations due to usage of broadcasting
operation (#1656)
* Avoid allocations due to usage of broadcasting operation
* Comments & correcting copy-paste error
---------
Co-authored-by: Hendrik Ranocha
---
src/solvers/dgmulti/dg.jl | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/solvers/dgmulti/dg.jl b/src/solvers/dgmulti/dg.jl
index bc76aa1a9d2..182a486dce5 100644
--- a/src/solvers/dgmulti/dg.jl
+++ b/src/solvers/dgmulti/dg.jl
@@ -302,7 +302,12 @@ function calc_volume_integral!(du, u, mesh::DGMultiMesh,
@threaded for e in eachelement(mesh, dg, cache)
flux_values = local_values_threaded[Threads.threadid()]
for i in eachdim(mesh)
- flux_values .= flux.(view(u_values, :, e), i, equations)
+ # Here, the broadcasting operation does allocate
+ #flux_values .= flux.(view(u_values, :, e), i, equations)
+ # Use loop instead
+ for j in eachindex(flux_values)
+ flux_values[j] = flux(u_values[j, e], i, equations)
+ end
for j in eachdim(mesh)
apply_to_each_field(mul_by_accum!(weak_differentiation_matrices[j],
dxidxhatj[i, j][1, e]),
@@ -327,6 +332,7 @@ function calc_volume_integral!(du, u, mesh::DGMultiMesh{NDIMS, <:NonAffine},
@threaded for e in eachelement(mesh, dg, cache)
flux_values = cache.flux_threaded[Threads.threadid()]
for i in eachdim(mesh)
+ # Here, the broadcasting operation does not allocate
flux_values[i] .= flux.(view(u_values, :, e), i, equations)
end
From 22856f4af27a06d38935d4f373a46f3492a4bf08 Mon Sep 17 00:00:00 2001
From: Ahmad Peyvan <115842305+apey236@users.noreply.github.com>
Date: Sun, 8 Oct 2023 03:22:36 -0400
Subject: [PATCH 121/263] Adding primitive variable Dirichlet BCs for
Navier-Stokes parabolic terms for `P4estMesh{2}` (#1553)
* Adding parabolic Dirichlet boundary condition example
* add test
* Correcting the format
* Update src/equations/compressible_navier_stokes_2d.jl
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
* Update src/equations/compressible_navier_stokes_2d.jl
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
---------
Co-authored-by: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Co-authored-by: Hendrik Ranocha
---
...ir_navierstokes_convergence_nonperiodic.jl | 215 ++++++++++++++++++
.../compressible_navier_stokes_2d.jl | 29 +++
test/test_parabolic_2d.jl | 8 +
3 files changed, 252 insertions(+)
create mode 100644 examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl
diff --git a/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl
new file mode 100644
index 00000000000..935f132ba4b
--- /dev/null
+++ b/examples/p4est_2d_dgsem/elixir_navierstokes_convergence_nonperiodic.jl
@@ -0,0 +1,215 @@
+using OrdinaryDiffEq
+using Trixi
+
+###############################################################################
+# semidiscretization of the ideal compressible Navier-Stokes equations
+
+prandtl_number() = 0.72
+mu() = 0.01
+
+equations = CompressibleEulerEquations2D(1.4)
+equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu=mu(), Prandtl=prandtl_number(),
+ gradient_variables=GradientVariablesPrimitive())
+
+# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
+solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs,
+ volume_integral=VolumeIntegralWeakForm())
+
+coordinates_min = (-1.0, -1.0) # minimum coordinates (min(x), min(y))
+coordinates_max = ( 1.0, 1.0) # maximum coordinates (max(x), max(y))
+
+trees_per_dimension = (4, 4)
+mesh = P4estMesh(trees_per_dimension,
+ polydeg=3, initial_refinement_level=2,
+ coordinates_min=coordinates_min, coordinates_max=coordinates_max,
+ periodicity=(false, false))
+
+# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D`
+# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`)
+# and by the initial condition (which passes in `CompressibleEulerEquations2D`).
+# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
+function initial_condition_navier_stokes_convergence_test(x, t, equations)
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_y = pi * x[2]
+ pi_t = pi * t
+
+ rho = c + A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+ v1 = sin(pi_x) * log(x[2] + 2.0) * (1.0 - exp(-A * (x[2] - 1.0)) ) * cos(pi_t)
+ v2 = v1
+ p = rho^2
+
+ return prim2cons(SVector(rho, v1, v2, p), equations)
+end
+
+@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
+ y = x[2]
+
+ # TODO: parabolic
+ # we currently need to hardcode these parameters until we fix the "combined equation" issue
+ # see also https://github.com/trixi-framework/Trixi.jl/pull/1160
+ inv_gamma_minus_one = inv(equations.gamma - 1)
+ Pr = prandtl_number()
+ mu_ = mu()
+
+ # Same settings as in `initial_condition`
+ # Amplitude and shift
+ A = 0.5
+ c = 2.0
+
+ # convenience values for trig. functions
+ pi_x = pi * x[1]
+ pi_y = pi * x[2]
+ pi_t = pi * t
+
+ # compute the manufactured solution and all necessary derivatives
+ rho = c + A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+ rho_t = -pi * A * sin(pi_x) * cos(pi_y) * sin(pi_t)
+ rho_x = pi * A * cos(pi_x) * cos(pi_y) * cos(pi_t)
+ rho_y = -pi * A * sin(pi_x) * sin(pi_y) * cos(pi_t)
+ rho_xx = -pi * pi * A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+ rho_yy = -pi * pi * A * sin(pi_x) * cos(pi_y) * cos(pi_t)
+
+ v1 = sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
+ v1_t = -pi * sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * sin(pi_t)
+ v1_x = pi * cos(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
+ v1_y = sin(pi_x) * (A * log(y + 2.0) * exp(-A * (y - 1.0)) + (1.0 - exp(-A * (y - 1.0))) / (y + 2.0)) * cos(pi_t)
+ v1_xx = -pi * pi * sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
+ v1_xy = pi * cos(pi_x) * (A * log(y + 2.0) * exp(-A * (y - 1.0)) + (1.0 - exp(-A * (y - 1.0))) / (y + 2.0)) * cos(pi_t)
+ v1_yy = (sin(pi_x) * ( 2.0 * A * exp(-A * (y - 1.0)) / (y + 2.0)
+ - A * A * log(y + 2.0) * exp(-A * (y - 1.0))
+ - (1.0 - exp(-A * (y - 1.0))) / ((y + 2.0) * (y + 2.0))) * cos(pi_t))
+ v2 = v1
+ v2_t = v1_t
+ v2_x = v1_x
+ v2_y = v1_y
+ v2_xx = v1_xx
+ v2_xy = v1_xy
+ v2_yy = v1_yy
+
+ p = rho * rho
+ p_t = 2.0 * rho * rho_t
+ p_x = 2.0 * rho * rho_x
+ p_y = 2.0 * rho * rho_y
+ p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x
+ p_yy = 2.0 * rho * rho_yy + 2.0 * rho_y * rho_y
+
+ # Note this simplifies slightly because the ansatz assumes that v1 = v2
+ E = p * inv_gamma_minus_one + 0.5 * rho * (v1^2 + v2^2)
+ E_t = p_t * inv_gamma_minus_one + rho_t * v1^2 + 2.0 * rho * v1 * v1_t
+ E_x = p_x * inv_gamma_minus_one + rho_x * v1^2 + 2.0 * rho * v1 * v1_x
+ E_y = p_y * inv_gamma_minus_one + rho_y * v1^2 + 2.0 * rho * v1 * v1_y
+
+ # Some convenience constants
+ T_const = equations.gamma * inv_gamma_minus_one / Pr
+ inv_rho_cubed = 1.0 / (rho^3)
+
+ # compute the source terms
+ # density equation
+ du1 = rho_t + rho_x * v1 + rho * v1_x + rho_y * v2 + rho * v2_y
+
+ # x-momentum equation
+ du2 = ( rho_t * v1 + rho * v1_t + p_x + rho_x * v1^2
+ + 2.0 * rho * v1 * v1_x
+ + rho_y * v1 * v2
+ + rho * v1_y * v2
+ + rho * v1 * v2_y
+ # stress tensor from x-direction
+ - 4.0 / 3.0 * v1_xx * mu_
+ + 2.0 / 3.0 * v2_xy * mu_
+ - v1_yy * mu_
+ - v2_xy * mu_ )
+ # y-momentum equation
+ du3 = ( rho_t * v2 + rho * v2_t + p_y + rho_x * v1 * v2
+ + rho * v1_x * v2
+ + rho * v1 * v2_x
+ + rho_y * v2^2
+ + 2.0 * rho * v2 * v2_y
+ # stress tensor from y-direction
+ - v1_xy * mu_
+ - v2_xx * mu_
+ - 4.0 / 3.0 * v2_yy * mu_
+ + 2.0 / 3.0 * v1_xy * mu_ )
+ # total energy equation
+ du4 = ( E_t + v1_x * (E + p) + v1 * (E_x + p_x)
+ + v2_y * (E + p) + v2 * (E_y + p_y)
+ # stress tensor and temperature gradient terms from x-direction
+ - 4.0 / 3.0 * v1_xx * v1 * mu_
+ + 2.0 / 3.0 * v2_xy * v1 * mu_
+ - 4.0 / 3.0 * v1_x * v1_x * mu_
+ + 2.0 / 3.0 * v2_y * v1_x * mu_
+ - v1_xy * v2 * mu_
+ - v2_xx * v2 * mu_
+ - v1_y * v2_x * mu_
+ - v2_x * v2_x * mu_
+ - T_const * inv_rho_cubed * ( p_xx * rho * rho
+ - 2.0 * p_x * rho * rho_x
+ + 2.0 * p * rho_x * rho_x
+ - p * rho * rho_xx ) * mu_
+ # stress tensor and temperature gradient terms from y-direction
+ - v1_yy * v1 * mu_
+ - v2_xy * v1 * mu_
+ - v1_y * v1_y * mu_
+ - v2_x * v1_y * mu_
+ - 4.0 / 3.0 * v2_yy * v2 * mu_
+ + 2.0 / 3.0 * v1_xy * v2 * mu_
+ - 4.0 / 3.0 * v2_y * v2_y * mu_
+ + 2.0 / 3.0 * v1_x * v2_y * mu_
+ - T_const * inv_rho_cubed * ( p_yy * rho * rho
+ - 2.0 * p_y * rho * rho_y
+ + 2.0 * p * rho_y * rho_y
+ - p * rho * rho_yy ) * mu_ )
+
+ return SVector(du1, du2, du3, du4)
+end
+
+initial_condition = initial_condition_navier_stokes_convergence_test
+
+# BC types
+velocity_bc_top_bottom = NoSlip((x, t, equations) -> initial_condition_navier_stokes_convergence_test(x, t, equations)[2:3])
+heat_bc_top_bottom = Adiabatic((x, t, equations) -> 0.0)
+boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom, heat_bc_top_bottom)
+
+boundary_condition_left_right = BoundaryConditionDirichlet(initial_condition_navier_stokes_convergence_test)
+
+# define inviscid boundary conditions
+boundary_conditions = Dict(:x_neg => boundary_condition_left_right,
+ :x_pos => boundary_condition_left_right,
+ :y_neg => boundary_condition_slip_wall,
+ :y_pos => boundary_condition_slip_wall)
+
+# define viscous boundary conditions
+boundary_conditions_parabolic = Dict(:x_neg => boundary_condition_left_right,
+ :x_pos => boundary_condition_left_right,
+ :y_neg => boundary_condition_top_bottom,
+ :y_pos => boundary_condition_top_bottom)
+
+semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic), initial_condition, solver;
+ boundary_conditions=(boundary_conditions, boundary_conditions_parabolic),
+ source_terms=source_terms_navier_stokes_convergence_test)
+
+# ###############################################################################
+# # ODE solvers, callbacks etc.
+
+# Create ODE problem with time span `tspan`
+tspan = (0.0, 0.5)
+ode = semidiscretize(semi, tspan)
+
+summary_callback = SummaryCallback()
+alive_callback = AliveCallback(alive_interval=10)
+analysis_interval = 100
+analysis_callback = AnalysisCallback(semi, interval=analysis_interval)
+callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback)
+
+###############################################################################
+# run the simulation
+
+time_int_tol = 1e-8
+sol = solve(ode, RDPK3SpFSAL49(); abstol=time_int_tol, reltol=time_int_tol, dt = 1e-5,
+ ode_default_options()..., callback=callbacks)
+summary_callback() # print the timer summary
+
diff --git a/src/equations/compressible_navier_stokes_2d.jl b/src/equations/compressible_navier_stokes_2d.jl
index b10ffa3b9d3..80857999017 100644
--- a/src/equations/compressible_navier_stokes_2d.jl
+++ b/src/equations/compressible_navier_stokes_2d.jl
@@ -456,4 +456,33 @@ end
})
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4])
end
+
+# Dirichlet Boundary Condition for P4est mesh
+
+@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,
+ u_inner,
+ normal::AbstractVector,
+ x, t,
+ operator_type::Gradient,
+ equations::CompressibleNavierStokesDiffusion2D{
+ GradientVariablesPrimitive
+ })
+ # BCs are usually specified as conservative variables so we convert them to primitive variables
+ # because the gradients are assumed to be with respect to the primitive variables
+ u_boundary = boundary_condition.boundary_value_function(x, t, equations)
+
+ return cons2prim(u_boundary, equations)
+end
+
+@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,
+ u_inner,
+ normal::AbstractVector,
+ x, t,
+ operator_type::Divergence,
+ equations::CompressibleNavierStokesDiffusion2D{
+ GradientVariablesPrimitive
+ })
+ # for Dirichlet boundary conditions, we do not impose any conditions on the viscous fluxes
+ return flux_inner
+end
end # @muladd
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index 3fff4382cd1..967aa1069a0 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -289,6 +289,14 @@ isdir(outdir) && rm(outdir, recursive=true)
)
end
+ @trixi_testset "P4estMesh2D: elixir_navierstokes_convergence_nonperiodic.jl" begin
+ @test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_navierstokes_convergence_nonperiodic.jl"),
+ initial_refinement_level = 1, tspan=(0.0, 0.2),
+ l2 = [0.00040364962558511795, 0.0005869762481506936, 0.00091488537427274, 0.0011984191566376762],
+ linf = [0.0024993634941723464, 0.009487866203944725, 0.004505829506628117, 0.011634902776245681]
+ )
+ end
+
@trixi_testset "P4estMesh2D: elixir_navierstokes_lid_driven_cavity.jl" begin
@test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_navierstokes_lid_driven_cavity.jl"),
initial_refinement_level = 2, tspan=(0.0, 0.5),
From 1ce4c328bbb8e1552c63306547decdee3656ffeb Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Sun, 8 Oct 2023 23:20:48 -0500
Subject: [PATCH 122/263] remove StartUpDG type piracy (#1665)
---
src/solvers/dgmulti/sbp.jl | 330 -------------------------------------
1 file changed, 330 deletions(-)
diff --git a/src/solvers/dgmulti/sbp.jl b/src/solvers/dgmulti/sbp.jl
index ba02d812041..d434d3146ce 100644
--- a/src/solvers/dgmulti/sbp.jl
+++ b/src/solvers/dgmulti/sbp.jl
@@ -36,312 +36,6 @@ function DGMulti(element_type::AbstractElemShape,
surface_integral = surface_integral, volume_integral = volume_integral)
end
-function construct_1d_operators(D::AbstractDerivativeOperator, tol)
- nodes_1d = collect(grid(D))
- M = SummationByPartsOperators.mass_matrix(D)
- if M isa UniformScaling
- weights_1d = M * ones(Bool, length(nodes_1d))
- else
- weights_1d = diag(M)
- end
-
- # StartUpDG assumes nodes from -1 to +1. Thus, we need to re-scale everything.
- # We can adjust the grid spacing as follows.
- xmin = SummationByPartsOperators.xmin(D)
- xmax = SummationByPartsOperators.xmax(D)
- factor = 2 / (xmax - xmin)
- @. nodes_1d = factor * (nodes_1d - xmin) - 1
- @. weights_1d = factor * weights_1d
-
- D_1d = droptol!(inv(factor) * sparse(D), tol)
- I_1d = Diagonal(ones(Bool, length(nodes_1d)))
-
- return nodes_1d, weights_1d, D_1d, I_1d
-end
-
-function StartUpDG.RefElemData(element_type::Line,
- D::AbstractDerivativeOperator;
- tol = 100 * eps())
- approximation_type = D
- N = SummationByPartsOperators.accuracy_order(D) # kind of polynomial degree
-
- # 1D operators
- nodes_1d, weights_1d, D_1d = construct_1d_operators(D, tol)
-
- # volume
- rq = r = nodes_1d
- wq = weights_1d
- Dr = D_1d
- M = Diagonal(wq)
- Pq = LinearAlgebra.I
- Vq = LinearAlgebra.I
-
- VDM = nothing # unused generalized Vandermonde matrix
-
- rst = (r,)
- rstq = (rq,)
- Drst = (Dr,)
-
- # face
- face_vertices = StartUpDG.face_vertices(element_type)
- face_mask = [1, length(nodes_1d)]
-
- rf = [-1.0; 1.0]
- nrJ = [-1.0; 1.0]
- wf = [1.0; 1.0]
- if D isa AbstractPeriodicDerivativeOperator
- # we do not need any face stuff for periodic operators
- Vf = spzeros(length(wf), length(wq))
- else
- Vf = sparse([1, 2], [1, length(nodes_1d)], [1.0, 1.0])
- end
- LIFT = Diagonal(wq) \ (Vf' * Diagonal(wf))
-
- rstf = (rf,)
- nrstJ = (nrJ,)
-
- # low order interpolation nodes
- r1 = StartUpDG.nodes(element_type, 1)
- V1 = StartUpDG.vandermonde(element_type, 1, r) /
- StartUpDG.vandermonde(element_type, 1, r1)
-
- return RefElemData(element_type, approximation_type, N,
- face_vertices, V1,
- rst, VDM, face_mask,
- rst, LinearAlgebra.I, # plotting
- rstq, wq, Vq, # quadrature
- rstf, wf, Vf, nrstJ, # faces
- M, Pq, Drst, LIFT)
-end
-
-function StartUpDG.RefElemData(element_type::Quad,
- D::AbstractDerivativeOperator;
- tol = 100 * eps())
- approximation_type = D
- N = SummationByPartsOperators.accuracy_order(D) # kind of polynomial degree
-
- # 1D operators
- nodes_1d, weights_1d, D_1d, I_1d = construct_1d_operators(D, tol)
-
- # volume
- s, r = vec.(StartUpDG.NodesAndModes.meshgrid(nodes_1d)) # this is to match
- # ordering of nrstJ
- rq = r
- sq = s
- wr, ws = vec.(StartUpDG.NodesAndModes.meshgrid(weights_1d))
- wq = wr .* ws
- Dr = kron(I_1d, D_1d)
- Ds = kron(D_1d, I_1d)
- M = Diagonal(wq)
- Pq = LinearAlgebra.I
- Vq = LinearAlgebra.I
-
- VDM = nothing # unused generalized Vandermonde matrix
-
- rst = (r, s)
- rstq = (rq, sq)
- Drst = (Dr, Ds)
-
- # face
- face_vertices = StartUpDG.face_vertices(element_type)
- face_mask = vcat(StartUpDG.find_face_nodes(element_type, r, s)...)
-
- rf, sf, wf, nrJ, nsJ = StartUpDG.init_face_data(element_type,
- quad_rule_face = (nodes_1d, weights_1d))
- if D isa AbstractPeriodicDerivativeOperator
- # we do not need any face stuff for periodic operators
- Vf = spzeros(length(wf), length(wq))
- else
- Vf = sparse(eachindex(face_mask), face_mask, ones(Bool, length(face_mask)))
- end
- LIFT = Diagonal(wq) \ (Vf' * Diagonal(wf))
-
- rstf = (rf, sf)
- nrstJ = (nrJ, nsJ)
-
- # low order interpolation nodes
- r1, s1 = StartUpDG.nodes(element_type, 1)
- V1 = StartUpDG.vandermonde(element_type, 1, r, s) /
- StartUpDG.vandermonde(element_type, 1, r1, s1)
-
- return RefElemData(element_type, approximation_type, N,
- face_vertices, V1,
- rst, VDM, face_mask,
- rst, LinearAlgebra.I, # plotting
- rstq, wq, Vq, # quadrature
- rstf, wf, Vf, nrstJ, # faces
- M, Pq, Drst, LIFT)
-end
-
-function StartUpDG.RefElemData(element_type::Hex,
- D::AbstractDerivativeOperator;
- tol = 100 * eps())
- approximation_type = D
- N = SummationByPartsOperators.accuracy_order(D) # kind of polynomial degree
-
- # 1D operators
- nodes_1d, weights_1d, D_1d, I_1d = construct_1d_operators(D, tol)
-
- # volume
- # to match ordering of nrstJ
- s, r, t = vec.(StartUpDG.NodesAndModes.meshgrid(nodes_1d, nodes_1d, nodes_1d))
- rq = r
- sq = s
- tq = t
- wr, ws, wt = vec.(StartUpDG.NodesAndModes.meshgrid(weights_1d, weights_1d, weights_1d))
- wq = wr .* ws .* wt
- Dr = kron(I_1d, I_1d, D_1d)
- Ds = kron(I_1d, D_1d, I_1d)
- Dt = kron(D_1d, I_1d, I_1d)
- M = Diagonal(wq)
- Pq = LinearAlgebra.I
- Vq = LinearAlgebra.I
-
- VDM = nothing # unused generalized Vandermonde matrix
-
- rst = (r, s, t)
- rstq = (rq, sq, tq)
- Drst = (Dr, Ds, Dt)
-
- # face
- face_vertices = StartUpDG.face_vertices(element_type)
- face_mask = vcat(StartUpDG.find_face_nodes(element_type, r, s, t)...)
-
- rf, sf, tf, wf, nrJ, nsJ, ntJ = let
- rf, sf = vec.(StartUpDG.NodesAndModes.meshgrid(nodes_1d, nodes_1d))
- wr, ws = vec.(StartUpDG.NodesAndModes.meshgrid(weights_1d, weights_1d))
- wf = wr .* ws
- StartUpDG.init_face_data(element_type, quad_rule_face = (rf, sf, wf))
- end
- Vf = sparse(eachindex(face_mask), face_mask, ones(Bool, length(face_mask)))
- LIFT = Diagonal(wq) \ (Vf' * Diagonal(wf))
-
- rstf = (rf, sf, tf)
- nrstJ = (nrJ, nsJ, ntJ)
-
- # low order interpolation nodes
- r1, s1, t1 = StartUpDG.nodes(element_type, 1)
- V1 = StartUpDG.vandermonde(element_type, 1, r, s, t) /
- StartUpDG.vandermonde(element_type, 1, r1, s1, t1)
-
- return RefElemData(element_type, approximation_type, N,
- face_vertices, V1,
- rst, VDM, face_mask,
- rst, LinearAlgebra.I, # plotting
- rstq, wq, Vq, # quadrature
- rstf, wf, Vf, nrstJ, # faces
- M, Pq, Drst, LIFT)
-end
-
-# specialized Hex constructor in 3D to reduce memory usage.
-function StartUpDG.RefElemData(element_type::Hex,
- D::AbstractPeriodicDerivativeOperator;
- tol = 100 * eps())
- approximation_type = D
- N = SummationByPartsOperators.accuracy_order(D) # kind of polynomial degree
-
- # 1D operators
- nodes_1d, weights_1d, D_1d, I_1d = construct_1d_operators(D, tol)
-
- # volume
- # to match ordering of nrstJ
- s, r, t = vec.(StartUpDG.NodesAndModes.meshgrid(nodes_1d, nodes_1d, nodes_1d))
- rq = r
- sq = s
- tq = t
- wr, ws, wt = vec.(StartUpDG.NodesAndModes.meshgrid(weights_1d, weights_1d, weights_1d))
- wq = wr .* ws .* wt
- Dr = kron(I_1d, I_1d, D_1d)
- Ds = kron(I_1d, D_1d, I_1d)
- Dt = kron(D_1d, I_1d, I_1d)
- M = Diagonal(wq)
- Pq = LinearAlgebra.I
- Vq = LinearAlgebra.I
-
- VDM = nothing # unused generalized Vandermonde matrix
-
- rst = (r, s, t)
- rstq = (rq, sq, tq)
- Drst = (Dr, Ds, Dt)
-
- # face
- # We do not need any face data for periodic operators. Thus, we just
- # pass `nothing` to save memory.
- face_vertices = ntuple(_ -> nothing, 3)
- face_mask = nothing
- wf = nothing
- rstf = ntuple(_ -> nothing, 3)
- nrstJ = ntuple(_ -> nothing, 3)
- Vf = nothing
- LIFT = nothing
-
- # low order interpolation nodes
- V1 = nothing # do not need to store V1, since we specialize StartUpDG.MeshData to avoid using it.
-
- return RefElemData(element_type, approximation_type, N,
- face_vertices, V1,
- rst, VDM, face_mask,
- rst, LinearAlgebra.I, # plotting
- rstq, wq, Vq, # quadrature
- rstf, wf, Vf, nrstJ, # faces
- M, Pq, Drst, LIFT)
-end
-
-function Base.show(io::IO, mime::MIME"text/plain",
- rd::RefElemData{NDIMS, ElementType, ApproximationType}) where {NDIMS,
- ElementType <:
- StartUpDG.AbstractElemShape,
- ApproximationType <:
- AbstractDerivativeOperator
- }
- @nospecialize rd
- print(io, "RefElemData for an approximation using an ")
- show(IOContext(io, :compact => true), rd.approximation_type)
- print(io, " on $(rd.element_type) element")
-end
-
-function Base.show(io::IO,
- rd::RefElemData{NDIMS, ElementType, ApproximationType}) where {NDIMS,
- ElementType <:
- StartUpDG.AbstractElemShape,
- ApproximationType <:
- AbstractDerivativeOperator
- }
- @nospecialize rd
- print(io, "RefElemData{", summary(rd.approximation_type), ", ", rd.element_type, "}")
-end
-
-function StartUpDG.inverse_trace_constant(rd::RefElemData{NDIMS, ElementType,
- ApproximationType}) where {NDIMS,
- ElementType <:
- Union{
- Line,
- Quad,
- Hex
- },
- ApproximationType <:
- AbstractDerivativeOperator
- }
- D = rd.approximation_type
-
- # the inverse trace constant is the maximum eigenvalue corresponding to
- # M_f * v = λ * M * v
- # where M_f is the face mass matrix and M is the volume mass matrix.
- # Since M is diagonal and since M_f is just the boundary "mask" matrix
- # (which extracts the first and last entries of a vector), the maximum
- # eigenvalue is the inverse of the first or last mass matrix diagonal.
- left_weight = SummationByPartsOperators.left_boundary_weight(D)
- right_weight = SummationByPartsOperators.right_boundary_weight(D)
- max_eigenvalue = max(inv(left_weight), inv(right_weight))
-
- # For tensor product elements, the trace constant for higher dimensional
- # elements is the one-dimensional trace constant multiplied by `NDIMS`. See
- # "GPU-accelerated discontinuous Galerkin methods on hybrid meshes."
- # Chan, Jesse, et al (2016), https://doi.org/10.1016/j.jcp.2016.04.003
- # for more details (specifically, Appendix A.1, Theorem A.4).
- return NDIMS * max_eigenvalue
-end
-
# type alias for specializing on a periodic SBP operator
const DGMultiPeriodicFDSBP{NDIMS, ApproxType, ElemType} = DGMulti{NDIMS, ElemType,
ApproxType,
@@ -450,30 +144,6 @@ end
@muladd begin
#! format: noindent
-# This is used in `estimate_dt`. `estimate_h` uses that `Jf / J = O(h^{NDIMS-1}) / O(h^{NDIMS}) = O(1/h)`.
-# However, since we do not initialize `Jf` for periodic FDSBP operators, we specialize `estimate_h`
-# based on the reference grid provided by SummationByPartsOperators.jl and information about the domain size
-# provided by `md::MeshData``.
-function StartUpDG.estimate_h(e, rd::RefElemData{NDIMS, ElementType, ApproximationType},
- md::MeshData) where {NDIMS,
- ElementType <:
- StartUpDG.AbstractElemShape,
- ApproximationType <:
- SummationByPartsOperators.AbstractPeriodicDerivativeOperator
- }
- D = rd.approximation_type
- x = grid(D)
-
- # we assume all SummationByPartsOperators.jl reference grids are rescaled to [-1, 1]
- xmin = SummationByPartsOperators.xmin(D)
- xmax = SummationByPartsOperators.xmax(D)
- factor = 2 / (xmax - xmin)
-
- # If the domain has size L^NDIMS, then `minimum(md.J)^(1 / NDIMS) = L`.
- # WARNING: this is not a good estimate on anisotropic grids.
- return minimum(diff(x)) * factor * minimum(md.J)^(1 / NDIMS)
-end
-
# specialized for DGMultiPeriodicFDSBP since there are no face nodes
# and thus no inverse trace constant for periodic domains.
function estimate_dt(mesh::DGMultiMesh, dg::DGMultiPeriodicFDSBP)
From ae3a0530d2edcd6c22fd3292319d771bd53e18c1 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Mon, 9 Oct 2023 06:22:16 +0200
Subject: [PATCH 123/263] set version to v0.5.45
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index d318389a6d2..ecb515b27ed 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.45-pre"
+version = "0.5.45"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 00292d1bde0d4c6eb1b38d9ae7134b6853d5e9e0 Mon Sep 17 00:00:00 2001
From: Hendrik Ranocha
Date: Mon, 9 Oct 2023 06:22:34 +0200
Subject: [PATCH 124/263] set development version to v0.5.46-pre
---
Project.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Project.toml b/Project.toml
index ecb515b27ed..4efbc40b098 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
name = "Trixi"
uuid = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
authors = ["Michael Schlottke-Lakemper ", "Gregor Gassner ", "Hendrik Ranocha ", "Andrew R. Winters ", "Jesse Chan "]
-version = "0.5.45"
+version = "0.5.46-pre"
[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
From 10c8d948a9fce3991bf6f21ac761f513de978a56 Mon Sep 17 00:00:00 2001
From: Daniel Doehring
Date: Tue, 10 Oct 2023 11:59:50 +0200
Subject: [PATCH 125/263] Print addition to DOF count (#1669)
---
src/callbacks_step/analysis.jl | 2 +-
src/semidiscretization/semidiscretization_coupled.jl | 2 +-
src/semidiscretization/semidiscretization_hyperbolic.jl | 2 +-
.../semidiscretization_hyperbolic_parabolic.jl | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl
index fad42b11098..a3e49007fb6 100644
--- a/src/callbacks_step/analysis.jl
+++ b/src/callbacks_step/analysis.jl
@@ -297,7 +297,7 @@ function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi)
mpi_println(" " * " " *
" " *
" PID: " * @sprintf("%10.8e s", performance_index))
- mpi_println(" #DOF: " * @sprintf("% 14d", ndofs(semi)) *
+ mpi_println(" #DOFs per field:" * @sprintf("% 14d", ndofs(semi)) *
" " *
" alloc'd memory: " * @sprintf("%14.3f MiB", memory_use))
mpi_println(" #elements: " *
diff --git a/src/semidiscretization/semidiscretization_coupled.jl b/src/semidiscretization/semidiscretization_coupled.jl
index b7adff78425..49763b12e5d 100644
--- a/src/semidiscretization/semidiscretization_coupled.jl
+++ b/src/semidiscretization/semidiscretization_coupled.jl
@@ -71,7 +71,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationCoupled)
summary_line(increment_indent(io), "source terms", semi.semis[i].source_terms)
summary_line(increment_indent(io), "solver", solver |> typeof |> nameof)
end
- summary_line(io, "total #DOFs", ndofs(semi))
+ summary_line(io, "total #DOFs per field", ndofs(semi))
summary_footer(io)
end
end
diff --git a/src/semidiscretization/semidiscretization_hyperbolic.jl b/src/semidiscretization/semidiscretization_hyperbolic.jl
index 50b2c21c14e..9d465bfcc5f 100644
--- a/src/semidiscretization/semidiscretization_hyperbolic.jl
+++ b/src/semidiscretization/semidiscretization_hyperbolic.jl
@@ -243,7 +243,7 @@ function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationHyperboli
summary_line(io, "source terms", semi.source_terms)
summary_line(io, "solver", semi.solver |> typeof |> nameof)
- summary_line(io, "total #DOFs", ndofs(semi))
+ summary_line(io, "total #DOFs per field", ndofs(semi))
summary_footer(io)
end
end
diff --git a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
index b12ecadb58b..35340d65b1e 100644
--- a/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
+++ b/src/semidiscretization/semidiscretization_hyperbolic_parabolic.jl
@@ -228,7 +228,7 @@ function Base.show(io::IO, ::MIME"text/plain",
summary_line(io, "source terms", semi.source_terms)
summary_line(io, "solver", semi.solver |> typeof |> nameof)
summary_line(io, "parabolic solver", semi.solver_parabolic |> typeof |> nameof)
- summary_line(io, "total #DOFs", ndofs(semi))
+ summary_line(io, "total #DOFs per field", ndofs(semi))
summary_footer(io)
end
end
From 8bc5469b3d883be0ce088386e1ec794e448003ef Mon Sep 17 00:00:00 2001
From: Jesse Chan <1156048+jlchan@users.noreply.github.com>
Date: Wed, 11 Oct 2023 06:22:49 -0500
Subject: [PATCH 126/263] fix bug in `apply_jacobian_parabolic!` for
`P4estMesh` (#1668)
* fix bug in parabolic jacobian computations
* formatting
* updating test results and removing duplicate test
---------
Co-authored-by: Hendrik Ranocha
---
src/solvers/dgsem_tree/dg_2d_parabolic.jl | 20 +++++++++++++++++++-
src/solvers/dgsem_tree/dg_3d_parabolic.jl | 23 +++++++++++++++++++++--
test/test_parabolic_2d.jl | 16 ++++------------
3 files changed, 44 insertions(+), 15 deletions(-)
diff --git a/src/solvers/dgsem_tree/dg_2d_parabolic.jl b/src/solvers/dgsem_tree/dg_2d_parabolic.jl
index 3dbc55412ad..1c32703c7c3 100644
--- a/src/solvers/dgsem_tree/dg_2d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_2d_parabolic.jl
@@ -935,7 +935,7 @@ end
# This is because the parabolic fluxes are assumed to be of the form
# `du/dt + df/dx = dg/dx + source(x,t)`,
# where f(u) is the inviscid flux and g(u) is the viscous flux.
-function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{2}, P4estMesh{2}},
+function apply_jacobian_parabolic!(du, mesh::TreeMesh{2},
equations::AbstractEquationsParabolic, dg::DG, cache)
@unpack inverse_jacobian = cache.elements
@@ -951,4 +951,22 @@ function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{2}, P4estMesh{2}},
return nothing
end
+
+function apply_jacobian_parabolic!(du, mesh::P4estMesh{2},
+ equations::AbstractEquationsParabolic,
+ dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
+ @threaded for element in eachelement(dg, cache)
+ for j in eachnode(dg), i in eachnode(dg)
+ factor = inverse_jacobian[i, j, element]
+
+ for v in eachvariable(equations)
+ du[v, i, j, element] *= factor
+ end
+ end
+ end
+
+ return nothing
+end
end # @muladd
diff --git a/src/solvers/dgsem_tree/dg_3d_parabolic.jl b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
index 9817e0e5f0e..37492dbcb91 100644
--- a/src/solvers/dgsem_tree/dg_3d_parabolic.jl
+++ b/src/solvers/dgsem_tree/dg_3d_parabolic.jl
@@ -1125,8 +1125,9 @@ end
# This is because the parabolic fluxes are assumed to be of the form
# `du/dt + df/dx = dg/dx + source(x,t)`,
# where f(u) is the inviscid flux and g(u) is the viscous flux.
-function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{3}, P4estMesh{3}},
- equations::AbstractEquationsParabolic, dg::DG, cache)
+function apply_jacobian_parabolic!(du, mesh::TreeMesh{3},
+ equations::AbstractEquationsParabolic,
+ dg::DG, cache)
@unpack inverse_jacobian = cache.elements
@threaded for element in eachelement(dg, cache)
@@ -1141,4 +1142,22 @@ function apply_jacobian_parabolic!(du, mesh::Union{TreeMesh{3}, P4estMesh{3}},
return nothing
end
+
+function apply_jacobian_parabolic!(du, mesh::P4estMesh{3},
+ equations::AbstractEquationsParabolic,
+ dg::DG, cache)
+ @unpack inverse_jacobian = cache.elements
+
+ @threaded for element in eachelement(dg, cache)
+ for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)
+ factor = inverse_jacobian[i, j, k, element]
+
+ for v in eachvariable(equations)
+ du[v, i, j, k, element] *= factor
+ end
+ end
+ end
+
+ return nothing
+end
end # @muladd
diff --git a/test/test_parabolic_2d.jl b/test/test_parabolic_2d.jl
index 967aa1069a0..98aa337afb4 100644
--- a/test/test_parabolic_2d.jl
+++ b/test/test_parabolic_2d.jl
@@ -260,24 +260,16 @@ isdir(outdir) && rm(outdir, recursive=true)
@trixi_testset "P4estMesh2D: elixir_advection_diffusion_periodic_curved.jl" begin
@test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_advection_diffusion_periodic_curved.jl"),
trees_per_dimension = (1, 1), initial_refinement_level = 2, tspan=(0.0, 0.5),
- l2 = [0.012380458938507371],
- linf = [0.10860506906472567]
- )
- end
-
- @trixi_testset "P4estMesh2D: elixir_advection_diffusion_periodic_curved.jl" begin
- @test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_advection_diffusion_periodic_curved.jl"),
- trees_per_dimension = (1, 1), initial_refinement_level = 2, tspan=(0.0, 0.5),
- l2 = [0.012380458938507371],
- linf = [0.10860506906472567]
+ l2 = [0.006708147442490916],
+ linf = [0.04807038397976693]
)
end
@trixi_testset "P4estMesh2D: elixir_advection_diffusion_nonperiodic_curved.jl" begin
@test_trixi_include(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_advection_diffusion_nonperiodic_curved.jl"),
trees_per_dimension = (1, 1), initial_refinement_level = 2, tspan=(0.0, 0.5),
- l2 = [0.04933902988507035],
- linf = [0.2550261714590271]
+ l2 = [0.00919917034843865],
+ linf = [0.14186297438393505]
)
end
From 1727b072fa9f269bcde73b516bc7b00f13fc9c99 Mon Sep 17 00:00:00 2001
From: Joshua Lampert <51029046+JoshuaLampert@users.noreply.github.com>
Date: Wed, 11 Oct 2023 16:23:12 +0200
Subject: [PATCH 127/263] Show percentage of simulation progress (#1659)
* show percentage of simulation progress
* add notes about AnalysisCallback in docs
* generalize for general initial time
---------
Co-authored-by: Hendrik Ranocha
---
docs/src/callbacks.md | 5 +++++
src/callbacks_step/alive.jl | 10 ++++++++--
src/callbacks_step/analysis.jl | 10 ++++++++--
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/docs/src/callbacks.md b/docs/src/callbacks.md
index 7f44dfd5925..f018bcf7c39 100644
--- a/docs/src/callbacks.md
+++ b/docs/src/callbacks.md
@@ -30,6 +30,11 @@ An example elixir using AMR can be found at [`examples/tree_2d_dgsem/elixir_adve
The [`AnalysisCallback`](@ref) can be used to analyze the numerical solution, e.g. calculate
errors or user-specified integrals, and print the results to the screen. The results can also be
saved in a file. An example can be found at [`examples/tree_2d_dgsem/elixir_euler_vortex.jl`](https://github.com/trixi-framework/Trixi.jl/blob/main/examples/tree_2d_dgsem/elixir_euler_vortex.jl).
+Note that the errors (e.g. `L2 error` or `Linf error`) are computed with respect to the initial condition.
+The percentage of the simulation time refers to the ratio of the current time and the final time, i.e. it does
+not consider the maximal number of iterations. So the simulation could finish before 100% are reached.
+Note that, e.g., due to AMR or smaller time step sizes, the simulation can actually take longer than
+the percentage indicates.
In [Performance metrics of the `AnalysisCallback`](@ref performance-metrics) you can find a detailed
description of the different performance metrics the `AnalysisCallback` computes.
diff --git a/src/callbacks_step/alive.jl b/src/callbacks_step/alive.jl
index eeacd9681d8..9df7181521e 100644
--- a/src/callbacks_step/alive.jl
+++ b/src/callbacks_step/alive.jl
@@ -86,9 +86,15 @@ function (alive_callback::AliveCallback)(integrator)
println("─"^100)
println()
elseif mpi_isroot()
+ t = integrator.t
+ t_initial = first(integrator.sol.prob.tspan)
+ t_final = last(integrator.sol.prob.tspan)
+ sim_time_percentage = (t - t_initial) / (t_final - t_initial) * 100
runtime_absolute = 1.0e-9 * (time_ns() - alive_callback.start_time)
- @printf("#timesteps: %6d │ Δt: %.4e │ sim. time: %.4e │ run time: %.4e s\n",
- integrator.stats.naccept, integrator.dt, integrator.t, runtime_absolute)
+ println(rpad(@sprintf("#timesteps: %6d │ Δt: %.4e │ sim. time: %.4e (%5.3f%%)",
+ integrator.stats.naccept, integrator.dt, t,
+ sim_time_percentage), 71) *
+ @sprintf("│ run time: %.4e s", runtime_absolute))
end
# avoid re-evaluating possible FSAL stages
diff --git a/src/callbacks_step/analysis.jl b/src/callbacks_step/analysis.jl
index a3e49007fb6..e5b4a01a885 100644
--- a/src/callbacks_step/analysis.jl
+++ b/src/callbacks_step/analysis.jl
@@ -232,6 +232,12 @@ function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi)
@unpack dt, t = integrator
iter = integrator.stats.naccept
+ # Compute the percentage of the simulation that is done
+ t = integrator.t
+ t_initial = first(integrator.sol.prob.tspan)
+ t_final = last(integrator.sol.prob.tspan)
+ sim_time_percentage = (t - t_initial) / (t_final - t_initial) * 100
+
# Record performance measurements and compute performance index (PID)
runtime_since_last_analysis = 1.0e-9 * (time_ns() -
analysis_callback.start_time_last_analysis)
@@ -291,8 +297,8 @@ function (analysis_callback::AnalysisCallback)(u_ode, du_ode, integrator, semi)
" " *
" └── GC time: " *
@sprintf("%10.8e s (%5.3f%%)", gc_time_absolute, gc_time_percentage))
- mpi_println(" sim. time: " * @sprintf("%10.8e", t) *
- " " *
+ mpi_println(rpad(" sim. time: " *
+ @sprintf("%10.8e (%5.3f%%)", t, sim_time_percentage), 46) *
" time/DOF/rhs!: " * @sprintf("%10.8e s", runtime_relative))
mpi_println(" " * " " *
" " *
From 7fd4503af9dfdf7022ae75f7398cac8fda811c85 Mon Sep 17 00:00:00 2001
From: Benjamin Bolm <74359358+bennibolm@users.noreply.github.com>
Date: Thu, 12 Oct 2023 15:31:43 +0200
Subject: [PATCH 128/263] Subcell limiting: Revise order of bounds using a
`Dict` (#1649)
* Implement order of bounds as Dict
* Implement `variable_bounds` directly as Dictionary
* Remove unnecessary line of code
* Use Symbols instead of Strings
---
src/solvers/dgsem_tree/containers_2d.jl | 31 ++++++++++---------
src/solvers/dgsem_tree/subcell_limiters.jl | 5 +--
src/solvers/dgsem_tree/subcell_limiters_2d.jl | 31 ++++++++-----------
3 files changed, 32 insertions(+), 35 deletions(-)
diff --git a/src/solvers/dgsem_tree/containers_2d.jl b/src/solvers/dgsem_tree/containers_2d.jl
index 9148b936312..fc916b41dd2 100644
--- a/src/solvers/dgsem_tree/containers_2d.jl
+++ b/src/solvers/dgsem_tree/containers_2d.jl
@@ -1325,16 +1325,16 @@ mutable struct ContainerSubcellLimiterIDP2D{uEltype <: Real}
alpha::Array{uEltype, 3} # [i, j, element]
alpha1::Array{uEltype, 3}
alpha2::Array{uEltype, 3}
- variable_bounds::Vector{Array{uEltype, 3}}
+ variable_bounds::Dict{Symbol, Array{uEltype, 3}}
# internal `resize!`able storage
_alpha::Vector{uEltype}
_alpha1::Vector{uEltype}
_alpha2::Vector{uEltype}
- _variable_bounds::Vector{Vector{uEltype}}
+ _variable_bounds::Dict{Symbol, Vector{uEltype}}
end
function ContainerSubcellLimiterIDP2D{uEltype}(capacity::Integer, n_nodes,
- length) where {uEltype <: Real}
+ bound_keys) where {uEltype <: Real}
nan_uEltype = convert(uEltype, NaN)
# Initialize fields with defaults
@@ -1345,12 +1345,12 @@ function ContainerSubcellLimiterIDP2D{uEltype}(capacity::Integer, n_nodes,
_alpha2 = fill(nan_uEltype, n_nodes * (n_nodes + 1) * capacity)
alpha2 = unsafe_wrap(Array, pointer(_alpha2), (n_nodes, n_nodes + 1, capacity))
- _variable_bounds = Vector{Vector{uEltype}}(undef, length)
- variable_bounds = Vector{Array{uEltype, 3}}(undef, length)
- for i in 1:length
- _variable_bounds[i] = fill(nan_uEltype, n_nodes * n_nodes * capacity)
- variable_bounds[i] = unsafe_wrap(Array, pointer(_variable_bounds[i]),
- (n_nodes, n_nodes, capacity))
+ _variable_bounds = Dict{Symbol, Vector{uEltype}}()
+ variable_bounds = Dict{Symbol, Array{uEltype, 3}}()
+ for key in bound_keys
+ _variable_bounds[key] = fill(nan_uEltype, n_nodes * n_nodes * capacity)
+ variable_bounds[key] = unsafe_wrap(Array, pointer(_variable_bounds[key]),
+ (n_nodes, n_nodes, capacity))
end
return ContainerSubcellLimiterIDP2D{uEltype}(alpha, alpha1, alpha2,
@@ -1369,7 +1369,7 @@ nnodes(container::ContainerSubcellLimiterIDP2D) = size(container.alpha, 1)
function Base.resize!(container::ContainerSubcellLimiterIDP2D, capacity)
n_nodes = nnodes(container)
- @unpack _alpha, _alpha1, _alpha2 = container
+ (; _alpha, _alpha1, _alpha2) = container
resize!(_alpha, n_nodes * n_nodes * capacity)
container.alpha = unsafe_wrap(Array, pointer(_alpha), (n_nodes, n_nodes, capacity))
resize!(_alpha1, (n_nodes + 1) * n_nodes * capacity)
@@ -1379,11 +1379,12 @@ function Base.resize!(container::ContainerSubcellLimiterIDP2D, capacity)
container.alpha2 = unsafe_wrap(Array, pointer(_alpha2),
(n_nodes, n_nodes + 1, capacity))
- @unpack _variable_bounds = container
- for i in 1:length(_variable_bounds)
- resize!(_variable_bounds[i], n_nodes * n_nodes * capacity)
- container.variable_bounds[i] = unsafe_wrap(Array, pointer(_variable_bounds[i]),
- (n_nodes, n_nodes, capacity))
+ (; _variable_bounds) = container
+ for (key, _) in _variable_bounds
+ resize!(_variable_bounds[key], n_nodes * n_nodes * capacity)
+ container.variable_bounds[key] = unsafe_wrap(Array,
+ pointer(_variable_bounds[key]),
+ (n_nodes, n_nodes, capacity))
end
return nothing
diff --git a/src/solvers/dgsem_tree/subcell_limiters.jl b/src/solvers/dgsem_tree/subcell_limiters.jl
index 3a707de3bc7..4d9eec25a89 100644
--- a/src/solvers/dgsem_tree/subcell_limiters.jl
+++ b/src/solvers/dgsem_tree/subcell_limiters.jl
@@ -52,9 +52,10 @@ function SubcellLimiterIDP(equations::AbstractEquations, basis;
positivity_variables_cons = [],
positivity_correction_factor = 0.1)
positivity = (length(positivity_variables_cons) > 0)
- number_bounds = length(positivity_variables_cons)
- cache = create_cache(SubcellLimiterIDP, equations, basis, number_bounds)
+ bound_keys = Tuple(Symbol("$(i)_min") for i in positivity_variables_cons)
+
+ cache = create_cache(SubcellLimiterIDP, equations, basis, bound_keys)
SubcellLimiterIDP{typeof(positivity_correction_factor), typeof(cache)}(positivity,
positivity_variables_cons,
diff --git a/src/solvers/dgsem_tree/subcell_limiters_2d.jl b/src/solvers/dgsem_tree/subcell_limiters_2d.jl
index 09ab84ed11a..4497217fb56 100644
--- a/src/solvers/dgsem_tree/subcell_limiters_2d.jl
+++ b/src/solvers/dgsem_tree/subcell_limiters_2d.jl
@@ -6,17 +6,14 @@
#! format: noindent
# this method is used when the limiter is constructed as for shock-capturing volume integrals
-function create_cache(indicator::Type{SubcellLimiterIDP},
- equations::AbstractEquations{2},
- basis::LobattoLegendreBasis, number_bounds)
+function create_cache(limiter::Type{SubcellLimiterIDP}, equations::AbstractEquations{2},
+ basis::LobattoLegendreBasis, bound_keys)
subcell_limiter_coefficients = Trixi.ContainerSubcellLimiterIDP2D{real(basis)
}(0,
nnodes(basis),
- number_bounds)
+ bound_keys)
- cache = (; subcell_limiter_coefficients)
-
- return cache
+ return (; subcell_limiter_coefficients)
end
function (limiter::SubcellLimiterIDP)(u::AbstractArray{<:Any, 4}, semi, dg::DGSEM, t,
@@ -26,8 +23,7 @@ function (limiter::SubcellLimiterIDP)(u::AbstractArray{<:Any, 4}, semi, dg::DGSE
alpha .= zero(eltype(alpha))
if limiter.positivity
- @trixi_timeit timer() "positivity" idp_positivity!(alpha, limiter, u, dt,
- semi)
+ @trixi_timeit timer() "positivity" idp_positivity!(alpha, limiter, u, dt, semi)
end
# Calculate alpha1 and alpha2
@@ -50,22 +46,21 @@ end
@inline function idp_positivity!(alpha, limiter, u, dt, semi)
# Conservative variables
- for (index, variable) in enumerate(limiter.positivity_variables_cons)
- idp_positivity!(alpha, limiter, u, dt, semi, variable, index)
+ for variable in limiter.positivity_variables_cons
+ idp_positivity!(alpha, limiter, u, dt, semi, variable)
end
return nothing
end
-@inline function idp_positivity!(alpha, limiter, u, dt, semi, variable, index)
+@inline function idp_positivity!(alpha, limiter, u, dt, semi, variable)
mesh, equations, dg, cache = mesh_equations_solver_cache(semi)
- @unpack antidiffusive_flux1, antidiffusive_flux2 = cache.antidiffusive_fluxes
- @unpack inverse_weights = dg.basis
- @unpack positivity_correction_factor = limiter
-
- @unpack variable_bounds = limiter.cache.subcell_limiter_coefficients
+ (; antidiffusive_flux1, antidiffusive_flux2) = cache.antidiffusive_fluxes
+ (; inverse_weights) = dg.basis
+ (; positivity_correction_factor) = limiter
- var_min = variable_bounds[index]
+ (; variable_bounds) = limiter.cache.subcell_limiter_coefficients
+ var_min = variable_bounds[Symbol("$(variable)_min")]
@threaded for element in eachelement(dg, semi.cache)
inverse_jacobian = cache.elements.inverse_jacobian[element]
From dfdaef5adc243fcd6c55262a69ede59fe3a32a8f Mon Sep 17 00:00:00 2001
From: Benjamin Bolm <74359358+bennibolm@users.noreply.github.com>
Date: Wed, 18 Oct 2023 16:58:17 +0200
Subject: [PATCH 129/263] Node-level visualization support for coefficients of
Subcell limiting (#1611)
* Add node-level visualization for IDP limiting
* Fix renaming error
* Init alphas with 0 after resizing
* Remove note
* Relocate overwriting of alphas
* Implement suggestions
* Add comment about split into nominator/denominator
* Set uninitialized alphas to NaN
---
src/callbacks_step/save_solution.jl | 11 ++++++++--
src/callbacks_step/save_solution_dg.jl | 18 +++++++++++++++--
src/semidiscretization/semidiscretization.jl | 4 ++++
src/solvers/dg.jl | 21 ++++++++++++++++++++
src/solvers/dgsem_tree/containers_2d.jl | 1 +
src/solvers/dgsem_tree/subcell_limiters.jl | 7 +++++++
src/time_integration/methods_SSP.jl | 5 +++++
utils/trixi2txt.jl | 13 ++++++++++--
8 files changed, 74 insertions(+), 6 deletions(-)
diff --git a/src/callbacks_step/save_solution.jl b/src/callbacks_step/save_solution.jl
index 14ea33368f8..31fe0e87c77 100644
--- a/src/callbacks_step/save_solution.jl
+++ b/src/callbacks_step/save_solution.jl
@@ -222,21 +222,28 @@ end
end
end
+ node_variables = Dict{Symbol, Any}()
+ @trixi_timeit timer() "get node variables" get_node_variables!(node_variables,
+ semi)
+
@trixi_timeit timer() "save solution" save_solution_file(u_ode, t, dt, iter, semi,
solution_callback,
element_variables,
+ node_variables,
system = system)
end
@inline function save_solution_file(u_ode, t, dt, iter,
semi::AbstractSemidiscretization, solution_callback,
- element_variables = Dict{Symbol, Any}();
+ element_variables = Dict{Symbol, Any}(),
+ node_variables = Dict{Symbol, Any}();
system = "")
mesh, equations, solver, cache = mesh_equations_solver_cache(semi)
u = wrap_array_native(u_ode, mesh, equations, solver, cache)
save_solution_file(u, t, dt, iter, mesh, equations, solver, cache,
solution_callback,
- element_variables; system = system)
+ element_variables,
+ node_variables; system = system)
end
# TODO: Taal refactor, move save_mesh_file?
diff --git a/src/callbacks_step/save_solution_dg.jl b/src/callbacks_step/save_solution_dg.jl
index 6d5004ff65f..7c015999035 100644
--- a/src/callbacks_step/save_solution_dg.jl
+++ b/src/callbacks_step/save_solution_dg.jl
@@ -10,7 +10,9 @@ function save_solution_file(u, time, dt, timestep,
UnstructuredMesh2D, SerialP4estMesh,
SerialT8codeMesh},
equations, dg::DG, cache,
- solution_callback, element_variables = Dict{Symbol, Any}();
+ solution_callback,
+ element_variables = Dict{Symbol, Any}(),
+ node_variables = Dict{Symbol, Any}();
system = "")
@unpack output_directory, solution_variables = solution_callback
@@ -73,6 +75,16 @@ function save_solution_file(u, time, dt, timestep,
var = file["element_variables_$v"]
attributes(var)["name"] = string(key)
end
+
+ # Store node variables
+ for (v, (key, node_variable)) in enumerate(node_variables)
+ # Add to file
+ file["node_variables_$v"] = node_variable
+
+ # Add variable name as attribute
+ var = file["node_variables_$v"]
+ attributes(var)["name"] = string(key)
+ end
end
return filename
@@ -81,7 +93,9 @@ end
function save_solution_file(u, time, dt, timestep,
mesh::Union{ParallelTreeMesh, ParallelP4estMesh}, equations,
dg::DG, cache,
- solution_callback, element_variables = Dict{Symbol, Any}();
+ solution_callback,
+ element_variables = Dict{Symbol, Any}(),
+ node_variables = Dict{Symbol, Any}();
system = "")
@unpack output_directory, solution_variables = solution_callback
diff --git a/src/semidiscretization/semidiscretization.jl b/src/semidiscretization/semidiscretization.jl
index c784f716426..fe7858e31ee 100644
--- a/src/semidiscretization/semidiscretization.jl
+++ b/src/semidiscretization/semidiscretization.jl
@@ -335,6 +335,10 @@ function get_element_variables!(element_variables, u_ode,
get_element_variables!(element_variables, u, mesh_equations_solver_cache(semi)...)
end
+function get_node_variables!(node_variables, semi::AbstractSemidiscretization)
+ get_node_variables!(node_variables, mesh_equations_solver_cache(semi)...)
+end
+
# To implement AMR and use OrdinaryDiffEq.jl etc., we have to be a bit creative.
# Since the caches of the SciML ecosystem are immutable structs, we cannot simply
# change the underlying arrays therein. Hence, to support changing the number of
diff --git a/src/solvers/dg.jl b/src/solvers/dg.jl
index 36bbc6de361..30b4f67474f 100644
--- a/src/solvers/dg.jl
+++ b/src/solvers/dg.jl
@@ -12,6 +12,11 @@ function get_element_variables!(element_variables, u, mesh, equations,
nothing
end
+function get_node_variables!(node_variables, mesh, equations,
+ volume_integral::AbstractVolumeIntegral, dg, cache)
+ nothing
+end
+
"""
VolumeIntegralStrongForm()
@@ -214,6 +219,18 @@ function Base.show(io::IO, mime::MIME"text/plain",
end
end
+function get_node_variables!(node_variables, mesh, equations,
+ volume_integral::VolumeIntegralSubcellLimiting, dg, cache)
+ # While for the element-wise limiting with `VolumeIntegralShockCapturingHG` the indicator is
+ # called here to get up-to-date values for IO, this is not easily possible in this case
+ # because the calculation is very integrated into the method.
+ # See also https://github.com/trixi-framework/Trixi.jl/pull/1611#discussion_r1334553206.
+ # Therefore, the coefficients at `t=t^{n-1}` are saved. Thus, the coefficients of the first
+ # stored solution (initial condition) are not yet defined and were manually set to `NaN`.
+ get_node_variables!(node_variables, volume_integral.limiter, volume_integral,
+ equations)
+end
+
# TODO: FD. Should this definition live in a different file because it is
# not strictly a DG method?
"""
@@ -403,6 +420,10 @@ function get_element_variables!(element_variables, u, mesh, equations, dg::DG, c
dg, cache)
end
+function get_node_variables!(node_variables, mesh, equations, dg::DG, cache)
+ get_node_variables!(node_variables, mesh, equations, dg.volume_integral, dg, cache)
+end
+
const MeshesDGSEM = Union{TreeMesh, StructuredMesh, UnstructuredMesh2D, P4estMesh,
T8codeMesh}
diff --git a/src/solvers/dgsem_tree/containers_2d.jl b/src/solvers/dgsem_tree/containers_2d.jl
index fc916b41dd2..9e9fe88c15b 100644
--- a/src/solvers/dgsem_tree/containers_2d.jl
+++ b/src/solvers/dgsem_tree/containers_2d.jl
@@ -1372,6 +1372,7 @@ function Base.resize!(container::ContainerSubcellLimiterIDP2D, capacity)
(; _alpha, _alpha1, _alpha2) = container
resize!(_alpha, n_nodes * n_nodes * capacity)
container.alpha = unsafe_wrap(Array, pointer(_alpha), (n_nodes, n_nodes, capacity))
+ container.alpha .= convert(eltype(container.alpha), NaN)
resize!(_alpha1, (n_nodes + 1) * n_nodes * capacity)
container.alpha1 = unsafe_wrap(Array, pointer(_alpha1),
(n_nodes + 1, n_nodes, capacity))
diff --git a/src/solvers/dgsem_tree/subcell_limiters.jl b/src/solvers/dgsem_tree/subcell_limiters.jl
index 4d9eec25a89..6197ca6b85d 100644
--- a/src/solvers/dgsem_tree/subcell_limiters.jl
+++ b/src/solvers/dgsem_tree/subcell_limiters.jl
@@ -101,4 +101,11 @@ function Base.show(io::IO, ::MIME"text/plain", limiter::SubcellLimiterIDP)
summary_box(io, "SubcellLimiterIDP", setup)
end
end
+
+function get_node_variables!(node_variables, limiter::SubcellLimiterIDP,
+ ::VolumeIntegralSubcellLimiting, equations)
+ node_variables[:limiting_coefficient] = limiter.cache.subcell_limiter_coefficients.alpha
+
+ return nothing
+end
end # @muladd
diff --git a/src/time_integration/methods_SSP.jl b/src/time_integration/methods_SSP.jl
index 33eb6ebf926..5b72682d48e 100644
--- a/src/time_integration/methods_SSP.jl
+++ b/src/time_integration/methods_SSP.jl
@@ -31,6 +31,11 @@ struct SimpleSSPRK33{StageCallbacks} <: SimpleAlgorithmSSP
stage_callbacks::StageCallbacks
function SimpleSSPRK33(; stage_callbacks = ())
+ # Mathematically speaking, it is not necessary for the algorithm to split the factors
+ # into numerator and denominator. Otherwise, however, rounding errors of the order of
+ # the machine accuracy will occur, which will add up over time and thus endanger the
+ # conservation of the simulation.
+ # See also https://github.com/trixi-framework/Trixi.jl/pull/1640.
numerator_a = SVector(0.0, 3.0, 1.0) # a = numerator_a / denominator
numerator_b = SVector(1.0, 1.0, 2.0) # b = numerator_b / denominator
denominator = SVector(1.0, 4.0, 3.0)
diff --git a/utils/trixi2txt.jl b/utils/trixi2txt.jl
index b386f150da4..12a3d46760e 100644
--- a/utils/trixi2txt.jl
+++ b/utils/trixi2txt.jl
@@ -70,7 +70,7 @@ function trixi2txt(filename::AbstractString...;
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)
+ labels, data, n_elements, n_nodes, element_variables, node_variables, time = read_datafile(filename)
# Check if dimensions match
if length(leaf_cells) != n_elements
@@ -263,7 +263,16 @@ function read_datafile(filename::String)
index += 1
end
- return labels, data, n_elements, n_nodes, element_variables, time
+ # Extract node variable arrays
+ node_variables = Dict{String, Union{Vector{Float64}, Vector{Int}}}()
+ index = 1
+ while haskey(file, "node_variables_$index")
+ varname = read(attributes(file["node_variables_$index"])["name"])
+ node_variables[varname] = read(file["node_variables_$index"])
+ index += 1
+ end
+
+ return labels, data, n_elements, n_nodes, element_variables, node_variables, time
end
end
From bb60b5d973ff4d4e00c37476ef858d37083d2209 Mon Sep 17 00:00:00 2001
From: Simon Candelaresi <10759273+SimonCan@users.noreply.github.com>
Date: Thu, 19 Oct 2023 04:51:53 +0100
Subject: [PATCH 130/263] Sc/polytropic (#1526)
* Added polytropic Euler equations.
* Added polytropic Euler equations.
* Added polytropic Euler equations.
* Minor clean up in polytropic equations.
* Added example for the compressible polytropic Euler equations in 2d on a structured mesh.
* Cleaned up example elicir for polytropic Euler in 2d.
* Added polytrropic Euler in 2d in tests.
* Remoed unused routines.
* Update src/equations/polytropic_euler_2d.jl
Co-authored-by: Michael Schlottke-Lakemper
* Update src/equations/polytropic_euler_2d.jl
Co-authored-by: Michael Schlottke-Lakemper
* Update src/equations/polytropic_euler_2d.jl
Co-authored-by: Michael Schlottke-Lakemper