Path: blob/main/src/callbacks_step/analysis_dg1d.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: noindent67function create_cache_analysis(analyzer, mesh::TreeMesh{1},8equations, dg::DG, cache,9RealT, uEltype)1011# pre-allocate buffers12# We use `StrideArray`s here since these buffers are used in performance-critical13# places and the additional information passed to the compiler makes them faster14# than native `Array`s.15u_local = StrideArray(undef, uEltype,16StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)))17x_local = StrideArray(undef, RealT,18StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)))1920return (; u_local, x_local)21end2223function create_cache_analysis(analyzer, mesh::StructuredMesh{1},24equations, dg::DG, cache,25RealT, uEltype)2627# pre-allocate buffers28# We use `StrideArray`s here since these buffers are used in performance-critical29# places and the additional information passed to the compiler makes them faster30# than native `Array`s.31u_local = StrideArray(undef, uEltype,32StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)))33x_local = StrideArray(undef, RealT,34StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)))35jacobian_local = StrideArray(undef, RealT,36StaticInt(nnodes(analyzer)))3738return (; u_local, x_local, jacobian_local)39end4041function calc_error_norms(func, u, t, analyzer,42mesh::TreeMesh{1}, equations, initial_condition,43dg::DGSEM, cache, cache_analysis)44@unpack vandermonde, weights = analyzer45@unpack node_coordinates = cache.elements46@unpack u_local, x_local = cache_analysis4748# Set up data structures49l2_error = zero(func(get_node_vars(u, equations, dg, 1, 1), equations))50linf_error = copy(l2_error)5152# Iterate over all elements for error calculations53for element in eachelement(dg, cache)54# Interpolate solution and node locations to analysis nodes55multiply_dimensionwise!(u_local, vandermonde, view(u, :, :, element))56multiply_dimensionwise!(x_local, vandermonde,57view(node_coordinates, :, :, element))5859# Calculate errors at each analysis node60volume_jacobian_ = volume_jacobian(element, mesh, cache)6162for i in eachnode(analyzer)63u_exact = initial_condition(get_node_coords(x_local, equations, dg, i), t,64equations)65diff = func(u_exact, equations) -66func(get_node_vars(u_local, equations, dg, i), equations)67l2_error += diff .^ 2 * (weights[i] * volume_jacobian_)68linf_error = @. max(linf_error, abs(diff))69end70end7172# For L2 error, divide by total volume73total_volume_ = total_volume(mesh)74l2_error = @. sqrt(l2_error / total_volume_)7576return l2_error, linf_error77end7879function calc_error_norms(func, u, t, analyzer,80mesh::StructuredMesh{1}, equations, initial_condition,81dg::DGSEM, cache, cache_analysis)82@unpack vandermonde, weights = analyzer83@unpack node_coordinates, inverse_jacobian = cache.elements84@unpack u_local, x_local, jacobian_local = cache_analysis8586# Set up data structures87l2_error = zero(func(get_node_vars(u, equations, dg, 1, 1), equations))88linf_error = copy(l2_error)89total_volume = zero(real(mesh))9091# Iterate over all elements for error calculations92for element in eachelement(dg, cache)93# Interpolate solution and node locations to analysis nodes94multiply_dimensionwise!(u_local, vandermonde, view(u, :, :, element))95multiply_dimensionwise!(x_local, vandermonde,96view(node_coordinates, :, :, element))97multiply_scalar_dimensionwise!(jacobian_local, vandermonde,98inv.(view(inverse_jacobian, :, element)))99100# Calculate errors at each analysis node101for i in eachnode(analyzer)102u_exact = initial_condition(get_node_coords(x_local, equations, dg, i), t,103equations)104diff = func(u_exact, equations) -105func(get_node_vars(u_local, equations, dg, i), equations)106# We take absolute value as we need the Jacobian here for the volume calculation107abs_jacobian_local_i = abs(jacobian_local[i])108109l2_error += diff .^ 2 * (weights[i] * abs_jacobian_local_i)110linf_error = @. max(linf_error, abs(diff))111total_volume += weights[i] * abs_jacobian_local_i112end113end114115# For L2 error, divide by total volume116l2_error = @. sqrt(l2_error / total_volume)117118return l2_error, linf_error119end120121function integrate_via_indices(func::Func, u,122mesh::TreeMesh{1}, equations, dg::DGSEM, cache,123args...; normalize = true) where {Func}124@unpack weights = dg.basis125126# Initialize integral with zeros of the right shape127integral = zero(func(u, 1, 1, equations, dg, args...))128129# Use quadrature to numerically integrate over entire domain130@batch reduction=(+, integral) for element in eachelement(dg, cache)131volume_jacobian_ = volume_jacobian(element, mesh, cache)132for i in eachnode(dg)133integral += volume_jacobian_ * weights[i] *134func(u, i, element, equations, dg, args...)135end136end137138# Normalize with total volume139if normalize140integral = integral / total_volume(mesh)141end142143return integral144end145146function integrate_via_indices(func::Func, u,147mesh::StructuredMesh{1}, equations, dg::DGSEM, cache,148args...; normalize = true) where {Func}149@unpack weights = dg.basis150151# Initialize integral with zeros of the right shape152integral = zero(func(u, 1, 1, equations, dg, args...))153total_volume = zero(real(mesh))154155# Use quadrature to numerically integrate over entire domain156@batch reduction=((+, integral), (+, total_volume)) for element in eachelement(dg,157cache)158for i in eachnode(dg)159jacobian_volume = abs(inv(cache.elements.inverse_jacobian[i, element]))160integral += jacobian_volume * weights[i] *161func(u, i, element, equations, dg, args...)162total_volume += jacobian_volume * weights[i]163end164end165# Normalize with total volume166if normalize167integral = integral / total_volume168end169170return integral171end172173function integrate(func::Func, u,174mesh::Union{TreeMesh{1}, StructuredMesh{1}},175equations, dg::DG, cache; normalize = true) where {Func}176integrate_via_indices(u, mesh, equations, dg, cache;177normalize = normalize) do u, i, element, equations, dg178u_local = get_node_vars(u, equations, dg, i, element)179return func(u_local, equations)180end181end182183function analyze(::typeof(entropy_timederivative), du, u, t,184mesh::Union{TreeMesh{1}, StructuredMesh{1}}, equations, dg::DG, cache)185# Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ186integrate_via_indices(u, mesh, equations, dg, cache,187du) do u, i, element, equations, dg, du188u_node = get_node_vars(u, equations, dg, i, element)189du_node = get_node_vars(du, equations, dg, i, element)190dot(cons2entropy(u_node, equations), du_node)191end192end193194function analyze(::Val{:l2_divb}, du, u, t,195mesh::TreeMesh{1}, equations::IdealGlmMhdEquations1D,196dg::DG, cache)197integrate_via_indices(u, mesh, equations, dg, cache,198dg.basis.derivative_matrix) do u, i, element, equations, dg,199derivative_matrix200divb = zero(eltype(u))201for k in eachnode(dg)202divb += derivative_matrix[i, k] * u[6, k, element]203end204divb *= cache.elements.inverse_jacobian[element]205divb^2206end |> sqrt207end208209function analyze(::Val{:linf_divb}, du, u, t,210mesh::TreeMesh{1}, equations::IdealGlmMhdEquations1D,211dg::DG, cache)212@unpack derivative_matrix, weights = dg.basis213214# integrate over all elements to get the divergence-free condition errors215linf_divb = zero(eltype(u))216@batch reduction=(max, linf_divb) for element in eachelement(dg, cache)217for i in eachnode(dg)218divb = zero(eltype(u))219for k in eachnode(dg)220divb += derivative_matrix[i, k] * u[6, k, element]221end222divb *= cache.elements.inverse_jacobian[element]223linf_divb = max(linf_divb, abs(divb))224end225end226227return linf_divb228end229end # @muladd230231232