Path: blob/main/src/solvers/dgsem_tree/indicators_2d.jl
5591 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# this method is directly used when the indicator is constructed as for shock-capturing volume integrals8# and by the dimension-independent method called for AMR9function create_cache(::Type{IndicatorHennemannGassner},10equations::AbstractEquations{2}, basis::LobattoLegendreBasis)11uEltype = real(basis)12alpha = Vector{uEltype}()13alpha_tmp = similar(alpha)1415MA2d = MArray{Tuple{nnodes(basis), nnodes(basis)},16uEltype, 2, nnodes(basis)^ndims(equations)}1718indicator_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]19modal_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]20modal_tmp1_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]2122return (; alpha, alpha_tmp, indicator_threaded, modal_threaded, modal_tmp1_threaded)23end2425# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl26# with @batch (@threaded).27# Otherwise, @threaded does not work here with Julia ARM on macOS.28# See https://github.com/JuliaSIMD/Polyester.jl/issues/88.29@inline function calc_indicator_hennemann_gassner!(indicator_hg, threshold, parameter_s,30u, element, mesh::AbstractMesh{2},31equations, dg, cache)32@unpack alpha_max, alpha_min, alpha_smooth, variable = indicator_hg33@unpack alpha, alpha_tmp, indicator_threaded, modal_threaded,34modal_tmp1_threaded = indicator_hg.cache3536indicator = indicator_threaded[Threads.threadid()]37modal = modal_threaded[Threads.threadid()]38modal_tmp1 = modal_tmp1_threaded[Threads.threadid()]3940# Calculate indicator variables at Gauss-Lobatto nodes41for j in eachnode(dg), i in eachnode(dg)42u_local = get_node_vars(u, equations, dg, i, j, element)43indicator[i, j] = indicator_hg.variable(u_local, equations)44end4546# Convert to modal representation47multiply_scalar_dimensionwise!(modal, dg.basis.inverse_vandermonde_legendre,48indicator, modal_tmp1)4950# Calculate total energies without two highest, without highest, and for all modes51total_energy_clip2 = zero(eltype(modal))52for j in 1:(nnodes(dg) - 2), i in 1:(nnodes(dg) - 2)53total_energy_clip2 += modal[i, j]^254end5556total_energy_clip1 = copy(total_energy_clip2)57for i in 1:(nnodes(dg) - 1)58total_energy_clip1 += modal[i, nnodes(dg) - 1]^259end60for j in 1:(nnodes(dg) - 2) # stop at N-2 to avoid adding the (N-1, N-1) mode twice61total_energy_clip1 += modal[nnodes(dg) - 1, j]^262end6364total_energy = copy(total_energy_clip1)65for i in 1:nnodes(dg)66total_energy += modal[i, nnodes(dg)]^267end68for j in 1:(nnodes(dg) - 1) # stop at N-1 to avoid adding the (N, N) mode twice69total_energy += modal[nnodes(dg), j]^270end7172# Calculate energy in higher modes73if !(iszero(total_energy))74energy_frac_1 = (total_energy - total_energy_clip1) / total_energy75else76energy_frac_1 = zero(total_energy)77end78if !(iszero(total_energy_clip1))79energy_frac_2 = (total_energy_clip1 - total_energy_clip2) / total_energy_clip180else81energy_frac_2 = zero(total_energy_clip1)82end83energy = max(energy_frac_1, energy_frac_2)8485alpha_element = 1 / (1 + exp(-parameter_s / threshold * (energy - threshold)))8687# Take care of the case close to pure DG88if alpha_element < alpha_min89alpha_element = zero(alpha_element)90end9192# Take care of the case close to pure FV93if alpha_element > 1 - alpha_min94alpha_element = one(alpha_element)95end9697# Clip the maximum amount of FV allowed98alpha[element] = min(alpha_max, alpha_element)99return nothing100end101102# Diffuse alpha values by setting each alpha to at least 50% of neighboring elements' alpha103function apply_smoothing!(mesh::Union{TreeMesh{2}, P4estMesh{2}, T8codeMesh{2}}, alpha,104alpha_tmp, dg,105cache)106# Copy alpha values such that smoothing is indpedenent of the element access order107alpha_tmp .= alpha108109# Loop over interfaces110for interface in eachinterface(dg, cache)111# Get neighboring element ids112left = cache.interfaces.neighbor_ids[1, interface]113right = cache.interfaces.neighbor_ids[2, interface]114115# Apply smoothing116alpha[left] = max(alpha_tmp[left], 0.5f0 * alpha_tmp[right], alpha[left])117alpha[right] = max(alpha_tmp[right], 0.5f0 * alpha_tmp[left], alpha[right])118end119120# Loop over L2 mortars121for mortar in eachmortar(dg, cache)122# Get neighboring element ids123lower = cache.mortars.neighbor_ids[1, mortar]124upper = cache.mortars.neighbor_ids[2, mortar]125large = cache.mortars.neighbor_ids[3, mortar]126127# Apply smoothing128alpha[lower] = max(alpha_tmp[lower], 0.5f0 * alpha_tmp[large], alpha[lower])129alpha[upper] = max(alpha_tmp[upper], 0.5f0 * alpha_tmp[large], alpha[upper])130alpha[large] = max(alpha_tmp[large], 0.5f0 * alpha_tmp[lower], alpha[large])131alpha[large] = max(alpha_tmp[large], 0.5f0 * alpha_tmp[upper], alpha[large])132end133134return nothing135end136137# this method is directly used when the indicator is constructed as for shock-capturing volume integrals138# and by the dimension-independent method called for AMR139function create_cache(::Union{Type{IndicatorLöhner}, Type{IndicatorMax}},140equations::AbstractEquations{2}, basis::LobattoLegendreBasis)141uEltype = real(basis)142alpha = Vector{uEltype}()143144MA2d = MArray{Tuple{nnodes(basis), nnodes(basis)},145uEltype, 2, nnodes(basis)^ndims(equations)}146147indicator_threaded = MA2d[MA2d(undef) for _ in 1:Threads.maxthreadid()]148149return (; alpha, indicator_threaded)150end151152function (löhner::IndicatorLöhner)(u::AbstractArray{<:Any, 4},153mesh, equations, dg::DGSEM, cache;154kwargs...)155@assert nnodes(dg)>=3 "IndicatorLöhner only works for nnodes >= 3 (polydeg > 1)"156@unpack alpha, indicator_threaded = löhner.cache157@unpack variable = löhner158resize!(alpha, nelements(dg, cache))159160@threaded for element in eachelement(dg, cache)161indicator = indicator_threaded[Threads.threadid()]162163# Calculate indicator variables at Gauss-Lobatto nodes164for j in eachnode(dg), i in eachnode(dg)165u_local = get_node_vars(u, equations, dg, i, j, element)166indicator[i, j] = variable(u_local, equations)167end168169estimate = zero(real(dg))170for j in eachnode(dg), i in 2:(nnodes(dg) - 1)171# x direction172u0 = indicator[i, j]173up = indicator[i + 1, j]174um = indicator[i - 1, j]175estimate = max(estimate, local_löhner_estimate(um, u0, up, löhner))176end177178for j in 2:(nnodes(dg) - 1), i in eachnode(dg)179# y direction180u0 = indicator[i, j]181up = indicator[i, j + 1]182um = indicator[i, j - 1]183estimate = max(estimate, local_löhner_estimate(um, u0, up, löhner))184end185186# use the maximum as DG element indicator187alpha[element] = estimate188end189190return alpha191end192193function (indicator_max::IndicatorMax)(u::AbstractArray{<:Any, 4},194mesh, equations, dg::DGSEM, cache;195kwargs...)196@unpack alpha, indicator_threaded = indicator_max.cache197resize!(alpha, nelements(dg, cache))198indicator_variable = indicator_max.variable199200@threaded for element in eachelement(dg, cache)201indicator = indicator_threaded[Threads.threadid()]202203# Calculate indicator variables at Gauss-Lobatto nodes204for j in eachnode(dg), i in eachnode(dg)205u_local = get_node_vars(u, equations, dg, i, j, element)206indicator[i, j] = indicator_variable(u_local, equations)207end208209alpha[element] = maximum(indicator)210end211212return alpha213end214215function (indicator::IndicatorNodalFunction)(u::AbstractArray{<:Any, 4},216mesh, equations, dg::DGSEM, cache;217t, kwargs...)218node_coordinates = cache.elements.node_coordinates219@unpack alpha = indicator.cache220resize!(alpha, nelements(dg, cache))221# Extract function to local variable to avoid capturing `indicator` in the threaded loop222indicator_function = indicator.indicator_function223224@threaded for element in eachelement(dg, cache)225estimate = typemin(eltype(alpha))226for j in eachnode(dg), i in eachnode(dg)227u_nodal = get_node_vars(u, equations, dg, i, j, element)228x_nodal = get_node_coords(node_coordinates, equations, dg,229i, j, element)230estimate = max(estimate, indicator_function(u_nodal, x_nodal, t))231end232alpha[element] = estimate233end234235return alpha236end237end # @muladd238239240