Path: blob/main/src/callbacks_step/analysis_dg3d.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{3},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)),17StaticInt(nnodes(analyzer)), StaticInt(nnodes(analyzer)))18u_tmp1 = StrideArray(undef, uEltype,19StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),20StaticInt(nnodes(dg)), StaticInt(nnodes(dg)))21u_tmp2 = StrideArray(undef, uEltype,22StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),23StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))24x_local = StrideArray(undef, RealT,25StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)),26StaticInt(nnodes(analyzer)), StaticInt(nnodes(analyzer)))27x_tmp1 = StrideArray(undef, RealT,28StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)),29StaticInt(nnodes(dg)), StaticInt(nnodes(dg)))30x_tmp2 = StrideArray(undef, RealT,31StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)),32StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))3334return (; u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2)35end3637# Specialized cache for P4estMesh to allow for different ambient dimension from mesh dimension38function create_cache_analysis(analyzer,39mesh::P4estMesh{3, NDIMS_AMBIENT},40equations, dg::DG, cache,41RealT, uEltype) where {NDIMS_AMBIENT}4243# pre-allocate buffers44# We use `StrideArray`s here since these buffers are used in performance-critical45# places and the additional information passed to the compiler makes them faster46# than native `Array`s.47u_local = StrideArray(undef, uEltype,48StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),49StaticInt(nnodes(analyzer)), StaticInt(nnodes(analyzer)))50u_tmp1 = StrideArray(undef, uEltype,51StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),52StaticInt(nnodes(dg)), StaticInt(nnodes(dg)))53u_tmp2 = StrideArray(undef, uEltype,54StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),55StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))56x_local = StrideArray(undef, RealT,57StaticInt(NDIMS_AMBIENT), StaticInt(nnodes(analyzer)),58StaticInt(nnodes(analyzer)), StaticInt(nnodes(analyzer)))59x_tmp1 = StrideArray(undef, RealT,60StaticInt(NDIMS_AMBIENT), StaticInt(nnodes(analyzer)),61StaticInt(nnodes(dg)), StaticInt(nnodes(dg)))62x_tmp2 = StrideArray(undef, RealT,63StaticInt(NDIMS_AMBIENT), StaticInt(nnodes(analyzer)),64StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))65jacobian_local = StrideArray(undef, RealT,66StaticInt(nnodes(analyzer)),67StaticInt(nnodes(analyzer)),68StaticInt(nnodes(analyzer)))69jacobian_tmp1 = StrideArray(undef, RealT,70StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)),71StaticInt(nnodes(dg)))72jacobian_tmp2 = StrideArray(undef, RealT,73StaticInt(nnodes(analyzer)),74StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))7576return (; u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2, jacobian_local,77jacobian_tmp1, jacobian_tmp2)78end7980function create_cache_analysis(analyzer,81mesh::Union{StructuredMesh{3}, T8codeMesh{3}},82equations, dg::DG, cache,83RealT, uEltype)8485# pre-allocate buffers86# We use `StrideArray`s here since these buffers are used in performance-critical87# places and the additional information passed to the compiler makes them faster88# than native `Array`s.89u_local = StrideArray(undef, uEltype,90StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),91StaticInt(nnodes(analyzer)), StaticInt(nnodes(analyzer)))92u_tmp1 = StrideArray(undef, uEltype,93StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),94StaticInt(nnodes(dg)), StaticInt(nnodes(dg)))95u_tmp2 = StrideArray(undef, uEltype,96StaticInt(nvariables(equations)), StaticInt(nnodes(analyzer)),97StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))98x_local = StrideArray(undef, RealT,99StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)),100StaticInt(nnodes(analyzer)), StaticInt(nnodes(analyzer)))101x_tmp1 = StrideArray(undef, RealT,102StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)),103StaticInt(nnodes(dg)), StaticInt(nnodes(dg)))104x_tmp2 = StrideArray(undef, RealT,105StaticInt(ndims(equations)), StaticInt(nnodes(analyzer)),106StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))107jacobian_local = StrideArray(undef, RealT,108StaticInt(nnodes(analyzer)),109StaticInt(nnodes(analyzer)),110StaticInt(nnodes(analyzer)))111jacobian_tmp1 = StrideArray(undef, RealT,112StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)),113StaticInt(nnodes(dg)))114jacobian_tmp2 = StrideArray(undef, RealT,115StaticInt(nnodes(analyzer)),116StaticInt(nnodes(analyzer)), StaticInt(nnodes(dg)))117118return (; u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2, jacobian_local,119jacobian_tmp1, jacobian_tmp2)120end121122function calc_error_norms(func, u, t, analyzer,123mesh::TreeMesh{3}, equations, initial_condition,124dg::DGSEM, cache, cache_analysis)125@unpack vandermonde, weights = analyzer126@unpack node_coordinates = cache.elements127@unpack u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2 = cache_analysis128129# Set up data structures130l2_error = zero(func(get_node_vars(u, equations, dg, 1, 1, 1, 1), equations))131linf_error = copy(l2_error)132133# Iterate over all elements for error calculations134for element in eachelement(dg, cache)135# Interpolate solution and node locations to analysis nodes136multiply_dimensionwise!(u_local, vandermonde, view(u, :, :, :, :, element),137u_tmp1, u_tmp2)138multiply_dimensionwise!(x_local, vandermonde,139view(node_coordinates, :, :, :, :, element), x_tmp1,140x_tmp2)141142# Calculate errors at each analysis node143volume_jacobian_ = volume_jacobian(element, mesh, cache)144145for k in eachnode(analyzer), j in eachnode(analyzer), i in eachnode(analyzer)146u_exact = initial_condition(get_node_coords(x_local, equations, dg, i, j,147k), t, equations)148diff = func(u_exact, equations) -149func(get_node_vars(u_local, equations, dg, i, j, k), equations)150l2_error += diff .^ 2 *151(weights[i] * weights[j] * weights[k] * volume_jacobian_)152linf_error = @. max(linf_error, abs(diff))153end154end155156# For L2 error, divide by total volume157total_volume_ = total_volume(mesh)158l2_error = @. sqrt(l2_error / total_volume_)159160return l2_error, linf_error161end162163function calc_error_norms(func, u, t, analyzer,164mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},165equations, initial_condition,166dg::DGSEM, cache, cache_analysis)167@unpack vandermonde, weights = analyzer168@unpack node_coordinates, inverse_jacobian = cache.elements169@unpack u_local, u_tmp1, u_tmp2, x_local, x_tmp1, x_tmp2, jacobian_local, jacobian_tmp1, jacobian_tmp2 = cache_analysis170171# Set up data structures172l2_error = zero(func(get_node_vars(u, equations, dg, 1, 1, 1, 1), equations))173linf_error = copy(l2_error)174total_volume = zero(real(mesh))175176# Iterate over all elements for error calculations177for element in eachelement(dg, cache)178# Interpolate solution and node locations to analysis nodes179multiply_dimensionwise!(u_local, vandermonde, view(u, :, :, :, :, element),180u_tmp1, u_tmp2)181multiply_dimensionwise!(x_local, vandermonde,182view(node_coordinates, :, :, :, :, element), x_tmp1,183x_tmp2)184multiply_scalar_dimensionwise!(jacobian_local, vandermonde,185inv.(view(inverse_jacobian, :, :, :, element)),186jacobian_tmp1, jacobian_tmp2)187188# Calculate errors at each analysis node189for k in eachnode(analyzer), j in eachnode(analyzer), i in eachnode(analyzer)190u_exact = initial_condition(get_node_coords(x_local, equations, dg, i, j,191k), t, equations)192diff = func(u_exact, equations) -193func(get_node_vars(u_local, equations, dg, i, j, k), equations)194# We take absolute value as we need the Jacobian here for the volume calculation195abs_jacobian_local_ijk = abs(jacobian_local[i, j, k])196197l2_error += diff .^ 2 *198(weights[i] * weights[j] * weights[k] * abs_jacobian_local_ijk)199linf_error = @. max(linf_error, abs(diff))200total_volume += (weights[i] * weights[j] * weights[k] *201abs_jacobian_local_ijk)202end203end204205# For L2 error, divide by total volume206l2_error = @. sqrt(l2_error / total_volume)207208return l2_error, linf_error209end210211function integrate_via_indices(func::Func, u,212mesh::TreeMesh{3}, equations, dg::DGSEM, cache,213args...; normalize = true) where {Func}214@unpack weights = dg.basis215216# Initialize integral with zeros of the right shape217integral = zero(func(u, 1, 1, 1, 1, equations, dg, args...))218219# Use quadrature to numerically integrate over entire domain220@batch reduction=(+, integral) for element in eachelement(dg, cache)221volume_jacobian_ = volume_jacobian(element, mesh, cache)222for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)223integral += volume_jacobian_ * weights[i] * weights[j] * weights[k] *224func(u, i, j, k, element, equations, dg, args...)225end226end227228# Normalize with total volume229if normalize230integral = integral / total_volume(mesh)231end232233return integral234end235236function integrate_via_indices(func::Func, u,237mesh::Union{StructuredMesh{3}, P4estMesh{3},238T8codeMesh{3}},239equations, dg::DGSEM, cache,240args...; normalize = true) where {Func}241@unpack weights = dg.basis242243# Initialize integral with zeros of the right shape244integral = zero(func(u, 1, 1, 1, 1, equations, dg, args...))245total_volume = zero(real(mesh))246247# Use quadrature to numerically integrate over entire domain248@batch reduction=((+, integral), (+, total_volume)) for element in eachelement(dg,249cache)250for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)251volume_jacobian = abs(inv(cache.elements.inverse_jacobian[i, j, k, element]))252integral += volume_jacobian * weights[i] * weights[j] * weights[k] *253func(u, i, j, k, element, equations, dg, args...)254total_volume += volume_jacobian * weights[i] * weights[j] * weights[k]255end256end257258# Normalize with total volume259if normalize260integral = integral / total_volume261end262263return integral264end265266function integrate(func::Func, u,267mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3},268T8codeMesh{3}},269equations, dg::DG, cache; normalize = true) where {Func}270integrate_via_indices(u, mesh, equations, dg, cache;271normalize = normalize) do u, i, j, k, element, equations, dg272u_local = get_node_vars(u, equations, dg, i, j, k, element)273return func(u_local, equations)274end275end276277function integrate(func::Func, u,278mesh::Union{TreeMesh{3}, P4estMesh{3}},279equations, equations_parabolic,280dg::DGSEM,281cache, cache_parabolic; normalize = true) where {Func}282gradients_x, gradients_y, gradients_z = cache_parabolic.viscous_container.gradients283integrate_via_indices(u, mesh, equations, dg, cache;284normalize = normalize) do u, i, j, k, element, equations, dg285u_local = get_node_vars(u, equations, dg, i, j, k, element)286gradients_1_local = get_node_vars(gradients_x, equations_parabolic, dg, i, j, k,287element)288gradients_2_local = get_node_vars(gradients_y, equations_parabolic, dg, i, j, k,289element)290gradients_3_local = get_node_vars(gradients_z, equations_parabolic, dg, i, j, k,291element)292return func(u_local, (gradients_1_local, gradients_2_local, gradients_3_local),293equations_parabolic)294end295end296297function analyze(::typeof(entropy_timederivative), du, u, t,298mesh::Union{TreeMesh{3}, StructuredMesh{3}, P4estMesh{3},299T8codeMesh{3}},300equations, dg::DG, cache)301# Calculate ∫(∂S/∂u ⋅ ∂u/∂t)dΩ302integrate_via_indices(u, mesh, equations, dg, cache,303du) do u, i, j, k, element, equations, dg, du304u_node = get_node_vars(u, equations, dg, i, j, k, element)305du_node = get_node_vars(du, equations, dg, i, j, k, element)306dot(cons2entropy(u_node, equations), du_node)307end308end309310function analyze(::Val{:l2_divb}, du, u, t,311mesh::TreeMesh{3}, equations,312dg::DGSEM, cache)313integrate_via_indices(u, mesh, equations, dg, cache, cache,314dg.basis.derivative_matrix) do u, i, j, k, element, equations,315dg, cache, derivative_matrix316divb = zero(eltype(u))317for l in eachnode(dg)318u_ljk = get_node_vars(u, equations, dg, l, j, k, element)319u_ilk = get_node_vars(u, equations, dg, i, l, k, element)320u_ijl = get_node_vars(u, equations, dg, i, j, l, element)321322B_ljk = magnetic_field(u_ljk, equations)323B_ilk = magnetic_field(u_ilk, equations)324B_ijl = magnetic_field(u_ijl, equations)325326divb += (derivative_matrix[i, l] * B_ljk[1] +327derivative_matrix[j, l] * B_ilk[2] +328derivative_matrix[k, l] * B_ijl[3])329end330divb *= cache.elements.inverse_jacobian[element]331divb^2332end |> sqrt333end334335function analyze(::Val{:l2_divb}, du, u, t,336mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},337equations,338dg::DGSEM, cache)339@unpack contravariant_vectors = cache.elements340integrate_via_indices(u, mesh, equations, dg, cache, cache,341dg.basis.derivative_matrix) do u, i, j, k, element, equations,342dg, cache, derivative_matrix343divb = zero(eltype(u))344# Get the contravariant vectors Ja^1, Ja^2, and Ja^3345Ja11, Ja12, Ja13 = get_contravariant_vector(1, contravariant_vectors,346i, j, k, element)347Ja21, Ja22, Ja23 = get_contravariant_vector(2, contravariant_vectors,348i, j, k, element)349Ja31, Ja32, Ja33 = get_contravariant_vector(3, contravariant_vectors,350i, j, k, element)351# Compute the transformed divergence352for l in eachnode(dg)353u_ljk = get_node_vars(u, equations, dg, l, j, k, element)354u_ilk = get_node_vars(u, equations, dg, i, l, k, element)355u_ijl = get_node_vars(u, equations, dg, i, j, l, element)356357B_ljk = magnetic_field(u_ljk, equations)358B_ilk = magnetic_field(u_ilk, equations)359B_ijl = magnetic_field(u_ijl, equations)360361divb += (derivative_matrix[i, l] *362(Ja11 * B_ljk[1] + Ja12 * B_ljk[2] + Ja13 * B_ljk[3]) +363derivative_matrix[j, l] *364(Ja21 * B_ilk[1] + Ja22 * B_ilk[2] + Ja23 * B_ilk[3]) +365derivative_matrix[k, l] *366(Ja31 * B_ijl[1] + Ja32 * B_ijl[2] + Ja33 * B_ijl[3]))367end368divb *= cache.elements.inverse_jacobian[i, j, k, element]369divb^2370end |> sqrt371end372373function analyze(::Val{:linf_divb}, du, u, t,374mesh::TreeMesh{3}, equations,375dg::DGSEM, cache)376@unpack derivative_matrix, weights = dg.basis377378# integrate over all elements to get the divergence-free condition errors379linf_divb = zero(eltype(u))380@batch reduction=(max, linf_divb) for element in eachelement(dg, cache)381for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)382divb = zero(eltype(u))383for l in eachnode(dg)384u_ljk = get_node_vars(u, equations, dg, l, j, k, element)385u_ilk = get_node_vars(u, equations, dg, i, l, k, element)386u_ijl = get_node_vars(u, equations, dg, i, j, l, element)387388B_ljk = magnetic_field(u_ljk, equations)389B_ilk = magnetic_field(u_ilk, equations)390B_ijl = magnetic_field(u_ijl, equations)391392divb += (derivative_matrix[i, l] * B_ljk[1] +393derivative_matrix[j, l] * B_ilk[2] +394derivative_matrix[k, l] * B_ijl[3])395end396divb *= cache.elements.inverse_jacobian[element]397linf_divb = max(linf_divb, abs(divb))398end399end400401if mpi_isparallel()402# Base.max instead of max needed, see comment in src/auxiliary/math.jl403linf_divb = MPI.Allreduce!(Ref(linf_divb), Base.max, mpi_comm())[]404end405406return linf_divb407end408409function analyze(::Val{:linf_divb}, du, u, t,410mesh::Union{StructuredMesh{3}, P4estMesh{3}, T8codeMesh{3}},411equations,412dg::DGSEM, cache)413@unpack derivative_matrix, weights = dg.basis414@unpack contravariant_vectors = cache.elements415416# integrate over all elements to get the divergence-free condition errors417linf_divb = zero(eltype(u))418@batch reduction=(max, linf_divb) for element in eachelement(dg, cache)419for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)420divb = zero(eltype(u))421# Get the contravariant vectors Ja^1, Ja^2, and Ja^3422Ja11, Ja12, Ja13 = get_contravariant_vector(1, contravariant_vectors,423i, j, k, element)424Ja21, Ja22, Ja23 = get_contravariant_vector(2, contravariant_vectors,425i, j, k, element)426Ja31, Ja32, Ja33 = get_contravariant_vector(3, contravariant_vectors,427i, j, k, element)428# Compute the transformed divergence429for l in eachnode(dg)430u_ljk = get_node_vars(u, equations, dg, l, j, k, element)431u_ilk = get_node_vars(u, equations, dg, i, l, k, element)432u_ijl = get_node_vars(u, equations, dg, i, j, l, element)433434B_ljk = magnetic_field(u_ljk, equations)435B_ilk = magnetic_field(u_ilk, equations)436B_ijl = magnetic_field(u_ijl, equations)437438divb += (derivative_matrix[i, l] * (Ja11 * B_ljk[1] +439Ja12 * B_ljk[2] + Ja13 * B_ljk[3]) +440derivative_matrix[j, l] * (Ja21 * B_ilk[1] +441Ja22 * B_ilk[2] + Ja23 * B_ilk[3]) +442derivative_matrix[k, l] * (Ja31 * B_ijl[1] +443Ja32 * B_ijl[2] + Ja33 * B_ijl[3]))444end445divb *= cache.elements.inverse_jacobian[i, j, k, element]446linf_divb = max(linf_divb, abs(divb))447end448end449450if mpi_isparallel()451# Base.max instead of max needed, see comment in src/auxiliary/math.jl452linf_divb = MPI.Allreduce!(Ref(linf_divb), Base.max, mpi_comm())[]453end454455return linf_divb456end457end # @muladd458459460