Path: blob/main/src/auxiliary/special_elixirs.jl
2055 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67"""8convergence_test([mod::Module=Main,] elixir::AbstractString, iterations, RealT = Float64; kwargs...)910Run `iterations` Trixi.jl simulations using the setup given in `elixir` and compute11the experimental order of convergence (EOC) in the ``L^2`` and ``L^\\infty`` norm.12Use `RealT` as the data type to represent the errors.13In each iteration, the resolution of the respective mesh will be doubled.14Additional keyword arguments `kwargs...` and the optional module `mod` are passed directly15to [`trixi_include`](@ref).1617This function assumes that the spatial resolution is set via the keywords18`initial_refinement_level` (an integer) or `cells_per_dimension` (a tuple of19integers, one per spatial dimension).20"""21function convergence_test(mod::Module, elixir::AbstractString, iterations,22RealT = Float64;23kwargs...)24@assert(iterations>1,25"Number of iterations must be bigger than 1 for a convergence analysis")2627# Types of errors to be calculated28errors = Dict(:l2 => RealT[], :linf => RealT[])2930initial_resolution = extract_initial_resolution(elixir, kwargs)3132# run simulations and extract errors33for iter in 1:iterations34println("Running convtest iteration ", iter, "/", iterations)3536include_refined(mod, elixir, initial_resolution, iter; kwargs)3738l2_error, linf_error = mod.analysis_callback(mod.sol)3940# collect errors as one vector to reshape later41append!(errors[:l2], l2_error)42append!(errors[:linf], linf_error)4344println("\n\n")45println("#"^100)46end4748# Use raw error values to compute EOC49analyze_convergence(errors, iterations, mod.semi)50end5152# Analyze convergence for any semidiscretization53# Note: this intermediate method is to allow dispatching on the semidiscretization54function analyze_convergence(errors, iterations, semi::AbstractSemidiscretization)55_, equations, _, _ = mesh_equations_solver_cache(semi)56variablenames = varnames(cons2cons, equations)57analyze_convergence(errors, iterations, variablenames)58end5960# This method is called with the collected error values to actually compute and print the EOC61function analyze_convergence(errors, iterations,62variablenames::Union{Tuple, AbstractArray})63nvariables = length(variablenames)6465# Reshape errors to get a matrix where the i-th row represents the i-th iteration66# and the j-th column represents the j-th variable67errorsmatrix = Dict(kind => transpose(reshape(error, (nvariables, iterations)))68for (kind, error) in errors)6970# Calculate EOCs where the columns represent the variables71# As dx halves in every iteration the denominator needs to be log(1/2)72eocs = Dict(kind => log.(error[2:end, :] ./ error[1:(end - 1), :]) ./ log(1 / 2)73for (kind, error) in errorsmatrix)7475eoc_mean_values = Dict{Symbol, Any}()76eoc_mean_values[:variables] = variablenames7778for (kind, error) in errorsmatrix79println(kind)8081for v in variablenames82@printf("%-20s", v)83end84println("")8586for k in 1:nvariables87@printf("%-10s", "error")88@printf("%-10s", "EOC")89end90println("")9192# Print errors for the first iteration93for k in 1:nvariables94@printf("%-10.2e", error[1, k])95@printf("%-10s", "-")96end97println("")9899# For the following iterations print errors and EOCs100for j in 2:iterations101for k in 1:nvariables102@printf("%-10.2e", error[j, k])103@printf("%-10.2f", eocs[kind][j - 1, k])104end105println("")106end107println("")108109# Print mean EOCs110mean_values = zeros(eltype(errors[:l2]), nvariables)111for v in 1:nvariables112mean_values[v] = sum(eocs[kind][:, v]) ./ length(eocs[kind][:, v])113@printf("%-10s", "mean")114@printf("%-10.2f", mean_values[v])115end116eoc_mean_values[kind] = mean_values117println("")118println("-"^100)119end120121return eoc_mean_values122end123124function convergence_test(elixir::AbstractString, iterations, RealT = Float64;125kwargs...)126convergence_test(Main, elixir::AbstractString, iterations, RealT; kwargs...)127end128129# Helper methods used in the functions defined above130131# Searches for the assignment that specifies the mesh resolution in the elixir132function extract_initial_resolution(elixir, kwargs)133code = read(elixir, String)134expr = Meta.parse("begin \n$code \nend")135136try137# get the initial_refinement_level from the elixir138initial_refinement_level = TrixiBase.find_assignment(expr,139:initial_refinement_level)140141if haskey(kwargs, :initial_refinement_level)142return kwargs[:initial_refinement_level]143else144return initial_refinement_level145end146catch e147# If `initial_refinement_level` is not found, we will get an `ArgumentError`148if isa(e, ArgumentError)149try150# get cells_per_dimension from the elixir151cells_per_dimension = eval(TrixiBase.find_assignment(expr,152:cells_per_dimension))153154if haskey(kwargs, :cells_per_dimension)155return kwargs[:cells_per_dimension]156else157return cells_per_dimension158end159catch e2160# If `cells_per_dimension` is not found either161if isa(e2, ArgumentError)162throw(ArgumentError("`convergence_test` requires the elixir to define " *163"`initial_refinement_level` or `cells_per_dimension`"))164else165rethrow()166end167end168else169rethrow()170end171end172end173174# runs the specified elixir with a doubled resolution each time iter is increased by 1175# works for TreeMesh176function include_refined(mod, elixir, initial_refinement_level::Int, iter; kwargs)177trixi_include(mod, elixir; kwargs...,178initial_refinement_level = initial_refinement_level + iter - 1)179end180181# runs the specified elixir with a doubled resolution each time iter is increased by 1182# works for StructuredMesh183function include_refined(mod, elixir, cells_per_dimension::NTuple{NDIMS, Int}, iter;184kwargs) where {NDIMS}185new_cells_per_dimension = cells_per_dimension .* 2^(iter - 1)186187trixi_include(mod, elixir; kwargs..., cells_per_dimension = new_cells_per_dimension)188end189end # @muladd190191192