Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ Trixi.jl follows the interpretation of
used in the Julia ecosystem. Notable changes will be documented in this file
for human readability.

## Changes when updating to v0.15 from v0.14.x

#### Changed

- `convergence_test` now returns the complete convergence orders and the full errors matrix. To obtain the mean convergence rates, use `Trixi.calc_mean_convergence` on the convergence orders ([#2753]).

## Changes in the v0.14 lifecycle

#### Added
- Added `NonIdealCompressibleEulerEquations1D`, which allows users to specify a non-ideal equation of state. Currently `IdealGas` and `VanDerWaals` are supported ([#2739]).
- Added the APEC (approximate pressure equilibrium preserving with conservation) fluxes of `flux_terashima_etal` and `flux_central_terashima_etal` from [Terashima, Ly, Ihme (2025)](https://doi.org/10.1016/j.jcp.2024.113701) ([#2756]).
- Support for second-order finite volume subcell volume integral (`VolumeIntegralPureLGLFiniteVolumeO2`) and
stabilized DG-FV blending volume integral (`VolumeIntegralShockCapturingRRG`) on
stabilized DG-FV blending volume integral (`VolumeIntegralShockCapturingRRG`) on
3D meshes for conservative systems ([#2734], [#2755]).
- Extended 3D support for subcell limiting with `P4estMesh` was added ([#2733]).
In the new version, local (minimum or maximum) limiting for nonlinear variables (using
Expand All @@ -23,8 +29,8 @@ for human readability.

#### Changed

- van Leer's limiters changed to snake case: `vanLeer` => `vanleer`
- A couple `struct`s have been made completely immutable, or only a couple fields thereof. Most notably, `save_solution.condition.save_initial_solution` where `save_solution isa SavesolutionCallback` can no longer be directly changed. Instead, the `@reset` macro from [Accessors.jl](https://github.com/JuliaObjects/Accessors.jl) is used in the elixirs.
- van Leer's limiters changed to snake case: `vanLeer` => `vanleer` ([#2744])
- A couple `struct`s have been made completely immutable, or only a couple fields thereof ([#2640]). Most notably, `save_solution.condition.save_initial_solution` where `save_solution isa SavesolutionCallback` can no longer be directly changed. Instead, the `@reset` macro from [Accessors.jl](https://github.com/JuliaObjects/Accessors.jl) is used in the elixirs.

## Changes in the v0.13 lifecycle

Expand All @@ -40,7 +46,7 @@ for human readability.
`flux_cons(u_ll, u_rr) + 0.5f0 * flux_noncons(u_rr, u_ll)`.
- Added support for `source_terms_parabolic`, which allows users to specify gradient-dependent source terms when solving parabolic equations ([#2721]).
- Support for second-order finite volume subcell volume integral (`VolumeIntegralPureLGLFiniteVolumeO2`) and
stabilized DG-FV blending volume integral (`VolumeIntegralShockCapturingRRG`) on
stabilized DG-FV blending volume integral (`VolumeIntegralShockCapturingRRG`) on
1D & 2D meshes for conservative systems ([#2022], [#2695], [#2718]).

## Changes when updating to v0.13 from v0.12.x
Expand Down
6 changes: 4 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ instance, in the example above the first execution of `trixi_include` takes abou
To automatically determine the experimental order of convergence (EOC) for a
given setup, execute
```julia
julia> convergence_test(default_example(), 4)
julia> convergence_test(default_example(), 4);
```
This will run a convergence test with the elixir `default_example()`,
using four iterations with different initial refinement levels. The initial
Expand Down Expand Up @@ -250,7 +250,7 @@ mean 3.99

An example with multiple variables looks like this:
```julia
julia> convergence_test(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_euler_source_terms.jl"), 3)
julia> eocs, errorsmatrix = convergence_test(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_euler_source_terms.jl"), 3);
```
```
[...]
Expand All @@ -273,6 +273,8 @@ error EOC error EOC error EOC error EOC
mean 3.94 mean 3.94 mean 3.94 mean 3.93
--------------------------------------------------------------------------------
```
The function `convergence_test` returns the experimental orders of convergence and the full errors
matrix. To obtain the mean convergence rates, use `Trixi.calc_mean_convergence` on the convergence orders.

### Showcase of advanced features
The presentation [From Mesh Generation to Adaptive Simulation: A Journey in Julia](https://youtu.be/_N4ozHr-t9E),
Expand Down
29 changes: 20 additions & 9 deletions src/auxiliary/special_elixirs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function convergence_test(mod::Module, elixir::AbstractString, iterations,

include_refined(mod, elixir, initial_resolution, iter; kwargs)

# @invokelatest is required for interactive use
# @invokelatest is required for interactive use
# due to world age issues on Julia 1.12 (and newer)
l2_error, linf_error = @invokelatest mod.analysis_callback(@invokelatest mod.sol)

Expand All @@ -49,11 +49,26 @@ function convergence_test(mod::Module, elixir::AbstractString, iterations,
end

# Use raw error values to compute EOC
# @invokelatest is required for interactive use
# @invokelatest is required for interactive use
# due to world age issues on Julia 1.12 (and newer)
return analyze_convergence(errors, iterations, (@invokelatest mod.semi))
end

"""
calc_mean_convergence(eocs)

Calculate the mean convergence rates from the given experimental orders of convergence `eocs`.
The `eocs` are expected to be in the format returned by `convergence_test`, i.e., a `Dict` where
Comment thread
JoshuaLampert marked this conversation as resolved.
Outdated
the keys are the error types (e.g., `:l2`, `:linf`) and the values are matrices with the EOCs for each
variable in the columns and the iterations in the rows.
Returns a `Dict` with the same keys as `eocs` and the mean convergence rates for all variables as values.
"""
function calc_mean_convergence(eocs)
return Dict(kind => [sum(eocs[kind][:, v]) / length(eocs[kind][:, v])
for v in 1:size(eocs[kind], 2)]
for kind in keys(eocs))
end

# Analyze convergence for any semidiscretization
# Note: this intermediate method is to allow dispatching on the semidiscretization
function analyze_convergence(errors, iterations, semi::AbstractSemidiscretization)
Expand All @@ -77,8 +92,7 @@ function analyze_convergence(errors, iterations,
eocs = Dict(kind => log.(error[2:end, :] ./ error[1:(end - 1), :]) ./ log(1 / 2)
for (kind, error) in errorsmatrix)

eoc_mean_values = Dict{Symbol, Any}()
eoc_mean_values[:variables] = variablenames
eoc_mean_values = calc_mean_convergence(eocs)

for (kind, error) in errorsmatrix
println(kind)
Expand Down Expand Up @@ -112,18 +126,15 @@ function analyze_convergence(errors, iterations,
println("")

# Print mean EOCs
mean_values = zeros(eltype(errors[:l2]), nvariables)
for v in 1:nvariables
mean_values[v] = sum(eocs[kind][:, v]) ./ length(eocs[kind][:, v])
@printf("%-10s", "mean")
@printf("%-10.2f", mean_values[v])
@printf("%-10.2f", eoc_mean_values[kind][v])
end
eoc_mean_values[kind] = mean_values
println("")
println("-"^100)
end

return eoc_mean_values
return eocs, errorsmatrix
end

function convergence_test(elixir::AbstractString, iterations, RealT = Float64;
Expand Down
8 changes: 5 additions & 3 deletions src/semidiscretization/semidiscretization_coupled.jl
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,8 @@ function analyze_convergence(errors_coupled, iterations,
append!(errors[i][:linf], errors_coupled[:linf][first:last])
end

eoc_mean_values = Vector{Dict{Symbol, Any}}(undef, nsystems(semi_coupled))
eocs = Vector{Dict{Symbol, Any}}(undef, nsystems(semi_coupled))
errorsmatrix = Vector{Dict{Symbol, Matrix{Float64}}}(undef, nsystems(semi_coupled))
for i in eachsystem(semi_coupled)
# Use visual cues to separate output from multiple systems
println()
Expand All @@ -790,9 +791,10 @@ function analyze_convergence(errors_coupled, iterations,
_, equations, _, _ = mesh_equations_solver_cache(semi)
variablenames = varnames(cons2cons, equations)

eoc_mean_values[i] = analyze_convergence(errors[i], iterations, variablenames)
eocs[i], errorsmatrix[i] = analyze_convergence(errors[i], iterations,
variablenames)
end

return eoc_mean_values
return eocs, errorsmatrix
end
end # @muladd
7 changes: 4 additions & 3 deletions test/test_dgmulti_1d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ end

@trixi_testset "elixir_euler_flux_diff.jl (convergence)" begin
using Trixi: convergence_test
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"elixir_euler_flux_diff.jl"), 3)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"elixir_euler_flux_diff.jl"), 3)
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:l2],
[4.1558759698638434, 3.977911306037128, 4.041421206468769],
rtol = 0.05)
Expand Down
7 changes: 4 additions & 3 deletions test/test_dgmulti_2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,10 @@ end

@trixi_testset "elixir_euler_weakform.jl (convergence)" begin
using Trixi: convergence_test
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"elixir_euler_weakform.jl"), 2)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"elixir_euler_weakform.jl"), 2)
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:l2],
[
4.243843382379403,
Expand Down
79 changes: 44 additions & 35 deletions test/test_special_elixirs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,68 +21,77 @@ EXAMPLES_DIR = examples_dir()

@testset "Convergence test" begin
@timed_testset "tree_2d_dgsem" begin
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR, "tree_2d_dgsem",
"elixir_advection_extended.jl"),
3, initial_refinement_level = 2)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR, "tree_2d_dgsem",
"elixir_advection_extended.jl"),
3, initial_refinement_level = 2)
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:l2], [4.0], rtol = 0.05)
end

@timed_testset "structured_2d_dgsem" begin
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"structured_2d_dgsem",
"elixir_advection_extended.jl"),
3, cells_per_dimension = (5, 9))
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"structured_2d_dgsem",
"elixir_advection_extended.jl"),
3, cells_per_dimension = (5, 9))
mean_convergence = Trixi.calc_mean_convergence(eocs)
@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)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"structured_2d_dgsem",
"elixir_advection_coupled.jl"),
3)
for i in Trixi.eachsystem(semi)
mean_convergence = Trixi.calc_mean_convergence(eocs[i])
@test isapprox(mean_convergence[:l2], [4.0], rtol = 0.05)
@test isapprox(mean_convergence[:l2], [4.0], rtol = 0.05)
end
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}))
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR, "p4est_2d_dgsem",
"elixir_euler_source_terms_nonconforming_unstructured_flag.jl"),
2, refine_fn_c = no_refine)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR, "p4est_2d_dgsem",
"elixir_euler_source_terms_nonconforming_unstructured_flag.jl"),
2, refine_fn_c = no_refine)
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:linf], [3.2, 3.2, 4.0, 3.7], rtol = 0.05)
end

@timed_testset "structured_3d_dgsem" begin
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"structured_3d_dgsem",
"elixir_advection_basic.jl"),
2, cells_per_dimension = (7, 4, 5))
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"structured_3d_dgsem",
"elixir_advection_basic.jl"),
2, cells_per_dimension = (7, 4, 5))
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:l2], [4.0], rtol = 0.05)
end

@timed_testset "p4est_3d_dgsem" begin
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR, "p4est_3d_dgsem",
"elixir_advection_unstructured_curved.jl"),
2, initial_refinement_level = 0)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR, "p4est_3d_dgsem",
"elixir_advection_unstructured_curved.jl"),
2, initial_refinement_level = 0)
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:l2], [2.7], rtol = 0.05)
end

@timed_testset "paper_self_gravitating_gas_dynamics" begin
mean_convergence = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"paper_self_gravitating_gas_dynamics",
"elixir_eulergravity_convergence.jl"),
2, tspan = (0.0, 0.25),
initial_refinement_level = 1)
eocs, _ = convergence_test(@__MODULE__,
joinpath(EXAMPLES_DIR,
"paper_self_gravitating_gas_dynamics",
"elixir_eulergravity_convergence.jl"),
2, tspan = (0.0, 0.25),
initial_refinement_level = 1)
mean_convergence = Trixi.calc_mean_convergence(eocs)
@test isapprox(mean_convergence[:l2], 4 * ones(4), atol = 0.4)
end
end
Expand Down
Loading