Path: blob/main/src/callbacks_step/amr_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: noindent67# Refine elements in the DG solver based on a list of cell_ids that should be refined.8# On `P4estMesh`, if an element refines the solution scaled by the Jacobian `J*u` is interpolated9# from the parent element into the eight children elements. The solution on each child10# element is then recovered by dividing by the new element Jacobians.11function refine!(u_ode::AbstractVector, adaptor, mesh::Union{TreeMesh{3}, P4estMesh{3}},12equations, dg::DGSEM, cache, elements_to_refine)13# Return early if there is nothing to do14if isempty(elements_to_refine)15if mpi_isparallel()16# MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do17# locally (there still might be other MPI ranks that have refined elements)18reinitialize_containers!(mesh, equations, dg, cache)19end20return21end2223# Determine for each existing element whether it needs to be refined24needs_refinement = falses(nelements(dg, cache))25needs_refinement[elements_to_refine] .= true2627# Retain current solution data28old_n_elements = nelements(dg, cache)29old_u_ode = copy(u_ode)30old_inverse_jacobian = copy(cache.elements.inverse_jacobian)31# OBS! If we don't GC.@preserve old_u_ode and old_inverse_jacobian, they might be GC'ed32GC.@preserve old_u_ode old_inverse_jacobian begin33old_u = wrap_array(old_u_ode, mesh, equations, dg, cache)3435if mesh isa P4estMesh36# Loop over all elements in old container and scale the old solution by the Jacobian37# prior to projection38for old_element_id in 1:old_n_elements39for v in eachvariable(equations)40old_u[v, .., old_element_id] .= (old_u[v, .., old_element_id] ./41old_inverse_jacobian[..,42old_element_id])43end44end45end4647reinitialize_containers!(mesh, equations, dg, cache)4849resize!(u_ode,50nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache))51u = wrap_array(u_ode, mesh, equations, dg, cache)5253# Loop over all elements in old container and either copy them or refine them54u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg),55nnodes(dg), nnodes(dg))56u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg),57nnodes(dg), nnodes(dg))58element_id = 159for old_element_id in 1:old_n_elements60if needs_refinement[old_element_id]61# Refine element and store solution directly in new data structure62refine_element!(u, element_id, old_u, old_element_id, adaptor,63equations, dg, u_tmp1, u_tmp2)6465if mesh isa P4estMesh66# Before `element_id` is incremented, divide by the new Jacobians on each67# child element and save the result68for m in 0:7 # loop over the children69for v in eachvariable(equations)70u[v, .., element_id + m] .*= (0.125f0 .*71cache.elements.inverse_jacobian[..,72element_id + m])73end74end75end7677# Increment `element_id` on the refined mesh with the number78# of children, i.e., 8 in 3D79element_id += 2^ndims(mesh)80else81if mesh isa P4estMesh82# Copy old element data to new element container and remove Jacobian scaling83for v in eachvariable(equations)84u[v, .., element_id] .= (old_u[v, .., old_element_id] .*85old_inverse_jacobian[..,86old_element_id])87end88else # isa TreeMesh89@views u[:, .., element_id] .= old_u[:, .., old_element_id]90end91# No refinement occurred, so increment `element_id` on the new mesh by one92element_id += 193end94end95# If everything is correct, we should have processed all elements.96# Depending on whether the last element processed above had to be refined or not,97# the counter `element_id` can have two different values at the end.98@assert element_id ==99nelements(dg, cache) +1001||element_id == nelements(dg, cache) + 2^ndims(mesh) "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))"101end # GC.@preserve old_u_ode old_inverse_jacobian102103# Sanity check104if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0105@assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements")106end107108return nothing109end110111# TODO: Taal compare performance of different implementations112# Refine solution data u for an element, using L2 projection (interpolation)113function refine_element!(u::AbstractArray{<:Any, 5}, element_id,114old_u, old_element_id,115adaptor::LobattoLegendreAdaptorL2, equations, dg,116u_tmp1, u_tmp2)117@unpack forward_upper, forward_lower = adaptor118119# Store new element ids120bottom_lower_left_id = element_id121bottom_lower_right_id = element_id + 1122bottom_upper_left_id = element_id + 2123bottom_upper_right_id = element_id + 3124top_lower_left_id = element_id + 4125top_lower_right_id = element_id + 5126top_upper_left_id = element_id + 6127top_upper_right_id = element_id + 7128129@boundscheck begin130@assert old_element_id >= 1131@assert size(old_u, 1) == nvariables(equations)132@assert size(old_u, 2) == nnodes(dg)133@assert size(old_u, 3) == nnodes(dg)134@assert size(old_u, 4) == nnodes(dg)135@assert size(old_u, 5) >= old_element_id136@assert element_id >= 1137@assert size(u, 1) == nvariables(equations)138@assert size(u, 2) == nnodes(dg)139@assert size(u, 3) == nnodes(dg)140@assert size(u, 4) == nnodes(dg)141@assert size(u, 5) >= element_id + 7142end143144# Interpolate to bottom lower left element145multiply_dimensionwise!(view(u, :, :, :, :, bottom_lower_left_id), forward_lower,146forward_lower, forward_lower,147view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)148149# Interpolate to bottom lower right element150multiply_dimensionwise!(view(u, :, :, :, :, bottom_lower_right_id), forward_upper,151forward_lower, forward_lower,152view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)153154# Interpolate to bottom upper left element155multiply_dimensionwise!(view(u, :, :, :, :, bottom_upper_left_id), forward_lower,156forward_upper, forward_lower,157view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)158159# Interpolate to bottom upper right element160multiply_dimensionwise!(view(u, :, :, :, :, bottom_upper_right_id), forward_upper,161forward_upper, forward_lower,162view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)163164# Interpolate to top lower left element165multiply_dimensionwise!(view(u, :, :, :, :, top_lower_left_id), forward_lower,166forward_lower, forward_upper,167view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)168169# Interpolate to top lower right element170multiply_dimensionwise!(view(u, :, :, :, :, top_lower_right_id), forward_upper,171forward_lower, forward_upper,172view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)173174# Interpolate to top upper left element175multiply_dimensionwise!(view(u, :, :, :, :, top_upper_left_id), forward_lower,176forward_upper, forward_upper,177view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)178179# Interpolate to top upper right element180multiply_dimensionwise!(view(u, :, :, :, :, top_upper_right_id), forward_upper,181forward_upper, forward_upper,182view(old_u, :, :, :, :, old_element_id), u_tmp1, u_tmp2)183184return nothing185end186187# Coarsen elements in the DG solver based on a list of cell_ids that should be removed.188# On `P4estMesh`, if an element coarsens the solution scaled by the Jacobian `J*u` is projected189# from the eight children elements back onto the parent element. The solution on the parent190# element is then recovered by dividing by the new element Jacobian.191function coarsen!(u_ode::AbstractVector, adaptor,192mesh::Union{TreeMesh{3}, P4estMesh{3}},193equations, dg::DGSEM, cache, elements_to_remove)194# Return early if there is nothing to do195if isempty(elements_to_remove)196if mpi_isparallel()197# MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do198# locally (there still might be other MPI ranks that have coarsened elements)199reinitialize_containers!(mesh, equations, dg, cache)200end201return202end203204# Determine for each old element whether it needs to be removed205to_be_removed = falses(nelements(dg, cache))206to_be_removed[elements_to_remove] .= true207208# Retain current solution data and Jacobians209old_n_elements = nelements(dg, cache)210old_u_ode = copy(u_ode)211old_inverse_jacobian = copy(cache.elements.inverse_jacobian)212# OBS! If we don't GC.@preserve old_u_ode and old_inverse_jacobian, they might be GC'ed213GC.@preserve old_u_ode old_inverse_jacobian begin214old_u = wrap_array(old_u_ode, mesh, equations, dg, cache)215216if mesh isa P4estMesh217# Loop over all elements in old container and scale the old solution by the Jacobian218# prior to projection219for old_element_id in 1:old_n_elements220for v in eachvariable(equations)221old_u[v, .., old_element_id] .= (old_u[v, .., old_element_id] ./222old_inverse_jacobian[..,223old_element_id])224end225end226end227228reinitialize_containers!(mesh, equations, dg, cache)229230resize!(u_ode,231nvariables(equations) * nnodes(dg)^ndims(mesh) * nelements(dg, cache))232u = wrap_array(u_ode, mesh, equations, dg, cache)233234# Loop over all elements in old container and either copy them or coarsen them235u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg),236nnodes(dg), nnodes(dg))237u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg),238nnodes(dg), nnodes(dg))239skip = 0240element_id = 1241for old_element_id in 1:old_n_elements242# If skip is non-zero, we just coarsened 2^ndims elements and need to omit the following elements243if skip > 0244skip -= 1245continue246end247248if to_be_removed[old_element_id]249# If an element is to be removed, sanity check if the following elements250# are also marked - otherwise there would be an error in the way the251# cells/elements are sorted252@assert all(to_be_removed[old_element_id:(old_element_id + 2^ndims(mesh) - 1)]) "bad cell/element order"253254# Coarsen elements and store solution directly in new data structure255coarsen_elements!(u, element_id, old_u, old_element_id, adaptor,256equations, dg, u_tmp1, u_tmp2)257258if mesh isa P4estMesh259# Before `element_id` is incremented, divide by the new Jacobian and save260# the result in the parent element261for v in eachvariable(equations)262u[v, .., element_id] .*= (8 .*263cache.elements.inverse_jacobian[..,264element_id])265end266end267268# Increment `element_id` on the coarsened mesh by one and `skip` = 7 in 3D269# because 8 children elements become 1 parent element270element_id += 1271skip = 2^ndims(mesh) - 1272else273if mesh isa P4estMesh274# Copy old element data to new element container and remove Jacobian scaling275for v in eachvariable(equations)276u[v, .., element_id] .= (old_u[v, .., old_element_id] .*277old_inverse_jacobian[..,278old_element_id])279end280else # isa TreeMesh281@views u[:, .., element_id] .= old_u[:, .., old_element_id]282end283# No coarsening occurred, so increment `element_id` on the new mesh by one284element_id += 1285end286end287# If everything is correct, we should have processed all elements.288@assert element_id==nelements(dg, cache) + 1 "element_id = $element_id, nelements(dg, cache) = $(nelements(dg, cache))"289end # GC.@preserve old_u_ode old_inverse_jacobian290291# Sanity check292if mesh isa TreeMesh && isperiodic(mesh.tree) && nmortars(cache.mortars) == 0293@assert ninterfaces(cache.interfaces)==ndims(mesh) * nelements(dg, cache) ("For $(ndims(mesh))D and periodic domains and conforming elements, the number of interfaces must be $(ndims(mesh)) times the number of elements")294end295296return nothing297end298299# TODO: Taal compare performance of different implementations300# Coarsen solution data u for four elements, using L2 projection301function coarsen_elements!(u::AbstractArray{<:Any, 5}, element_id,302old_u, old_element_id,303adaptor::LobattoLegendreAdaptorL2, equations, dg,304u_tmp1, u_tmp2)305@unpack reverse_upper, reverse_lower = adaptor306307# Store old element ids308bottom_lower_left_id = old_element_id309bottom_lower_right_id = old_element_id + 1310bottom_upper_left_id = old_element_id + 2311bottom_upper_right_id = old_element_id + 3312top_lower_left_id = old_element_id + 4313top_lower_right_id = old_element_id + 5314top_upper_left_id = old_element_id + 6315top_upper_right_id = old_element_id + 7316317@boundscheck begin318@assert old_element_id >= 1319@assert size(old_u, 1) == nvariables(equations)320@assert size(old_u, 2) == nnodes(dg)321@assert size(old_u, 3) == nnodes(dg)322@assert size(old_u, 4) == nnodes(dg)323@assert size(old_u, 5) >= old_element_id + 7324@assert element_id >= 1325@assert size(u, 1) == nvariables(equations)326@assert size(u, 2) == nnodes(dg)327@assert size(u, 3) == nnodes(dg)328@assert size(u, 4) == nnodes(dg)329@assert size(u, 5) >= element_id330end331332# Project from bottom lower left element333multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_lower,334reverse_lower, reverse_lower,335view(old_u, :, :, :, :, bottom_lower_left_id), u_tmp1,336u_tmp2)337338# Project from bottom lower right element_variables339add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_upper,340reverse_lower, reverse_lower,341view(old_u, :, :, :, :, bottom_lower_right_id), u_tmp1,342u_tmp2)343344# Project from bottom upper left element345add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_lower,346reverse_upper, reverse_lower,347view(old_u, :, :, :, :, bottom_upper_left_id), u_tmp1,348u_tmp2)349350# Project from bottom upper right element351add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_upper,352reverse_upper, reverse_lower,353view(old_u, :, :, :, :, bottom_upper_right_id), u_tmp1,354u_tmp2)355356# Project from top lower left element357add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_lower,358reverse_lower, reverse_upper,359view(old_u, :, :, :, :, top_lower_left_id), u_tmp1,360u_tmp2)361362# Project from top lower right element363add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_upper,364reverse_lower, reverse_upper,365view(old_u, :, :, :, :, top_lower_right_id), u_tmp1,366u_tmp2)367368# Project from top upper left element369add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_lower,370reverse_upper, reverse_upper,371view(old_u, :, :, :, :, top_upper_left_id), u_tmp1,372u_tmp2)373374# Project from top upper right element375add_multiply_dimensionwise!(view(u, :, :, :, :, element_id), reverse_upper,376reverse_upper, reverse_upper,377view(old_u, :, :, :, :, top_upper_right_id), u_tmp1,378u_tmp2)379380return nothing381end382383# Coarsen and refine elements in the DG solver based on a difference list.384function adapt!(u_ode::AbstractVector, adaptor, mesh::T8codeMesh{3}, equations,385dg::DGSEM, cache, difference)386387# Return early if there is nothing to do.388if !any(difference .!= 0)389if mpi_isparallel()390# MPICache init uses all-to-all communication -> reinitialize even if there is nothing to do391# locally (there still might be other MPI ranks that have refined elements)392reinitialize_containers!(mesh, equations, dg, cache)393end394return395end396397# Number of (local) cells/elements.398old_nelems = nelements(dg, cache)399new_nelems = ncells(mesh)400401# Local element indices.402old_index = 1403new_index = 1404405# Note: This is only true for `hexs`.406T8_CHILDREN = 8407408# Retain current solution and inverse Jacobian data.409old_u_ode = copy(u_ode)410old_inverse_jacobian = copy(cache.elements.inverse_jacobian)411412# OBS! If we don't GC.@preserve old_u_ode and old_inverse_jacobian, they might be GC'ed413GC.@preserve old_u_ode begin414old_u = wrap_array(old_u_ode, mesh, equations, dg, cache)415416# Loop over all elements in old container and scale the old solution by the Jacobian417# prior to interpolation or projection418for old_element_id in 1:old_nelems419for v in eachvariable(equations)420old_u[v, .., old_element_id] .= (old_u[v, .., old_element_id] ./421old_inverse_jacobian[..,422old_element_id])423end424end425426reinitialize_containers!(mesh, equations, dg, cache)427428resize!(u_ode, nvariables(equations) * ndofs(mesh, dg, cache))429u = wrap_array(u_ode, mesh, equations, dg, cache)430431u_tmp1 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg),432nnodes(dg), nnodes(dg))433u_tmp2 = Array{eltype(u), 4}(undef, nvariables(equations), nnodes(dg),434nnodes(dg), nnodes(dg))435436while old_index <= old_nelems && new_index <= new_nelems437if difference[old_index] > 0 # Refine.438439# Refine element and store solution directly in new data structure.440refine_element!(u, new_index, old_u, old_index, adaptor, equations, dg,441u_tmp1, u_tmp2)442443# Before indices are incremented divide by the new Jacobians on each444# child element and save the result445for m in 0:7 # loop over the children446for v in eachvariable(equations)447u[v, .., new_index + m] .*= (0.125f0 .*448cache.elements.inverse_jacobian[..,449new_index + m])450end451end452453# Increment `old_index` on the original mesh and the `new_index`454# on the refined mesh with the number of children, i.e., T8_CHILDREN = 8455old_index += 1456new_index += T8_CHILDREN457458elseif difference[old_index] < 0 # Coarsen.459460# If an element is to be removed, sanity check if the following elements461# are also marked - otherwise there would be an error in the way the462# cells/elements are sorted.463@assert all(difference[old_index:(old_index + T8_CHILDREN - 1)] .< 0) "bad cell/element order"464465# Coarsen elements and store solution directly in new data structure.466coarsen_elements!(u, new_index, old_u, old_index, adaptor, equations,467dg, u_tmp1, u_tmp2)468469# Before the indices are incremented divide by the new Jacobian and save470# the result again in the parent element471for v in eachvariable(equations)472u[v, .., new_index] .*= (8 .* cache.elements.inverse_jacobian[..,473new_index])474end475476# Increment `old_index` on the original mesh with the number of children477# (T8_CHILDREN = 8 in 3D) and the `new_index` by one for the single478# coarsened element479old_index += T8_CHILDREN480new_index += 1481482else # No changes.483484# Copy old element data to new element container and remove Jacobian scaling485for v in eachvariable(equations)486u[v, .., new_index] .= (old_u[v, .., old_index] .*487old_inverse_jacobian[.., old_index])488end489490# No refinement / coarsening occurred, so increment element index491# on each mesh by one492old_index += 1493new_index += 1494end495end # while496end # GC.@preserve old_u_ode old_inverse_jacobian497498return nothing499end500end # @muladd501502503