Path: blob/main/src/solvers/dgsem_p4est/dg_2d_parallel.jl
5616 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 prolong2mpiinterfaces!(cache, u,8mesh::Union{P4estMeshParallel{2},9T8codeMeshParallel{2}},10equations, surface_integral, dg::DG)11@unpack mpi_interfaces = cache12index_range = eachnode(dg)1314@threaded for interface in eachmpiinterface(dg, cache)15# Copy solution data from the local element using "delayed indexing" with16# a start value and a step size to get the correct face and orientation.17# Note that in the current implementation, the interface will be18# "aligned at the primary element", i.e., the index of the primary side19# will always run forwards.20local_side = mpi_interfaces.local_sides[interface]21local_element = mpi_interfaces.local_neighbor_ids[interface]22local_indices = mpi_interfaces.node_indices[interface]2324i_element_start, i_element_step = index_to_start_step_2d(local_indices[1],25index_range)26j_element_start, j_element_step = index_to_start_step_2d(local_indices[2],27index_range)2829i_element = i_element_start30j_element = j_element_start31for i in eachnode(dg)32for v in eachvariable(equations)33mpi_interfaces.u[local_side, v, i, interface] = u[v, i_element,34j_element,35local_element]36end37i_element += i_element_step38j_element += j_element_step39end40end4142return nothing43end4445function calc_mpi_interface_flux!(surface_flux_values,46mesh::Union{P4estMeshParallel{2},47T8codeMeshParallel{2}},48have_nonconservative_terms,49equations, surface_integral, dg::DG, cache)50@unpack local_neighbor_ids, node_indices, local_sides = cache.mpi_interfaces51@unpack contravariant_vectors = cache.elements52index_range = eachnode(dg)53index_end = last(index_range)5455@threaded for interface in eachmpiinterface(dg, cache)56# Get element and side index information on the local element57local_element = local_neighbor_ids[interface]58local_indices = node_indices[interface]59local_direction = indices2direction(local_indices)60local_side = local_sides[interface]6162# Create the local i,j indexing on the local element used to pull normal direction information63i_element_start, i_element_step = index_to_start_step_2d(local_indices[1],64index_range)65j_element_start, j_element_step = index_to_start_step_2d(local_indices[2],66index_range)6768i_element = i_element_start69j_element = j_element_start7071# Initiate the node index to be used in the surface for loop,72# the surface flux storage must be indexed in alignment with the local element indexing73if :i_backward in local_indices74surface_node = index_end75surface_node_step = -176else77surface_node = 178surface_node_step = 179end8081for node in eachnode(dg)82# Get the normal direction on the local element83# Contravariant vectors at interfaces in negative coordinate direction84# are pointing inwards. This is handled by `get_normal_direction`.85normal_direction = get_normal_direction(local_direction,86contravariant_vectors,87i_element, j_element, local_element)8889calc_mpi_interface_flux!(surface_flux_values, mesh,90have_nonconservative_terms,91equations,92surface_integral, dg, cache,93interface, normal_direction,94node, local_side,95surface_node, local_direction, local_element)9697# Increment local element indices to pull the normal direction98i_element += i_element_step99j_element += j_element_step100101# Increment the surface node index along the local element102surface_node += surface_node_step103end104end105106return nothing107end108109# Inlined version of the interface flux computation for conservation laws110@inline function calc_mpi_interface_flux!(surface_flux_values,111mesh::Union{P4estMeshParallel{2},112T8codeMeshParallel{2}},113have_nonconservative_terms::False, equations,114surface_integral, dg::DG, cache,115interface_index, normal_direction,116interface_node_index, local_side,117surface_node_index, local_direction_index,118local_element_index)119@unpack u = cache.mpi_interfaces120@unpack surface_flux = surface_integral121122u_ll, u_rr = get_surface_node_vars(u, equations, dg, interface_node_index,123interface_index)124125if local_side == 1126flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)127else # local_side == 2128flux_ = -surface_flux(u_ll, u_rr, -normal_direction, equations)129end130131for v in eachvariable(equations)132surface_flux_values[v, surface_node_index, local_direction_index, local_element_index] = flux_[v]133end134135return nothing136end137138# Inlined version of the interface flux computation for non-conservative equations139@inline function calc_mpi_interface_flux!(surface_flux_values,140mesh::Union{P4estMeshParallel{2},141T8codeMeshParallel{2}},142have_nonconservative_terms::True, equations,143surface_integral, dg::DG, cache,144interface_index, normal_direction,145interface_node_index, local_side,146surface_node_index, local_direction_index,147local_element_index)148@unpack u = cache.mpi_interfaces149surface_flux, nonconservative_flux = surface_integral.surface_flux150151u_ll, u_rr = get_surface_node_vars(u, equations, dg,152interface_node_index,153interface_index)154155# Compute flux and non-conservative term for this side of the interface156if local_side == 1157flux_ = surface_flux(u_ll, u_rr, normal_direction, equations)158noncons_flux_ = nonconservative_flux(u_ll, u_rr, normal_direction, equations)159else # local_side == 2160flux_ = -surface_flux(u_ll, u_rr, -normal_direction, equations)161noncons_flux_ = -nonconservative_flux(u_rr, u_ll, -normal_direction, equations)162end163164for v in eachvariable(equations)165surface_flux_values[v, surface_node_index,166local_direction_index, local_element_index] = flux_[v] +1670.5f0 * noncons_flux_[v]168end169170return nothing171end172173function prolong2mpimortars!(cache, u,174mesh::Union{P4estMeshParallel{2}, T8codeMeshParallel{2}},175equations,176mortar_l2::LobattoLegendreMortarL2,177dg::DGSEM)178@unpack node_indices = cache.mpi_mortars179index_range = eachnode(dg)180181@threaded for mortar in eachmpimortar(dg, cache)182local_neighbor_ids = cache.mpi_mortars.local_neighbor_ids[mortar]183local_neighbor_positions = cache.mpi_mortars.local_neighbor_positions[mortar]184185# Get start value and step size for indices on both sides to get the correct face186# and orientation187small_indices = node_indices[1, mortar]188i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],189index_range)190j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],191index_range)192193large_indices = node_indices[2, mortar]194i_large_start, i_large_step = index_to_start_step_2d(large_indices[1],195index_range)196j_large_start, j_large_step = index_to_start_step_2d(large_indices[2],197index_range)198199for (element, position) in zip(local_neighbor_ids, local_neighbor_positions)200if position == 3 # -> large element201# Buffer to copy solution values of the large element in the correct orientation202# before interpolating203u_buffer = cache.u_threaded[Threads.threadid()]204i_large = i_large_start205j_large = j_large_start206for i in eachnode(dg)207for v in eachvariable(equations)208u_buffer[v, i] = u[v, i_large, j_large, element]209end210211i_large += i_large_step212j_large += j_large_step213end214215# Interpolate large element face data from buffer to small face locations216multiply_dimensionwise!(view(cache.mpi_mortars.u, 2, :, 1, :, mortar),217mortar_l2.forward_lower, u_buffer)218multiply_dimensionwise!(view(cache.mpi_mortars.u, 2, :, 2, :, mortar),219mortar_l2.forward_upper, u_buffer)220else # position in (1, 2) -> small element221# Copy solution data from the small elements222i_small = i_small_start223j_small = j_small_start224for i in eachnode(dg)225for v in eachvariable(equations)226cache.mpi_mortars.u[1, v, position, i, mortar] = u[v, i_small,227j_small,228element]229end230i_small += i_small_step231j_small += j_small_step232end233end234end235end236237return nothing238end239240function calc_mpi_mortar_flux!(surface_flux_values,241mesh::Union{P4estMeshParallel{2}, T8codeMeshParallel{2}},242have_nonconservative_terms, equations,243mortar_l2::LobattoLegendreMortarL2,244surface_integral, dg::DG, cache)245@unpack local_neighbor_ids, local_neighbor_positions, node_indices = cache.mpi_mortars246@unpack contravariant_vectors = cache.elements247@unpack fstar_primary_upper_threaded, fstar_primary_lower_threaded = cache248@unpack fstar_secondary_upper_threaded, fstar_secondary_lower_threaded = cache249index_range = eachnode(dg)250251@threaded for mortar in eachmpimortar(dg, cache)252# Choose thread-specific pre-allocated container253fstar_primary = (fstar_primary_lower_threaded[Threads.threadid()],254fstar_primary_upper_threaded[Threads.threadid()])255fstar_secondary = (fstar_secondary_lower_threaded[Threads.threadid()],256fstar_secondary_upper_threaded[Threads.threadid()])257258# Get index information on the small elements259small_indices = node_indices[1, mortar]260261i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],262index_range)263j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],264index_range)265266for position in 1:2267i_small = i_small_start268j_small = j_small_start269for node in eachnode(dg)270# Get the normal direction on the small element.271normal_direction = get_normal_direction(cache.mpi_mortars, node,272position, mortar)273274calc_mpi_mortar_flux!(fstar_primary, fstar_secondary, mesh,275have_nonconservative_terms, equations,276surface_integral, dg, cache,277mortar, position, normal_direction, node)278279i_small += i_small_step280j_small += j_small_step281end282end283284# Buffer to interpolate flux values of the large element to before285# copying in the correct orientation286u_buffer = cache.u_threaded[Threads.threadid()]287288mpi_mortar_fluxes_to_elements!(surface_flux_values,289mesh, equations, mortar_l2, dg, cache,290mortar, fstar_primary, fstar_secondary, u_buffer)291end292293return nothing294end295296# Inlined version of the mortar flux computation on small elements for conservation laws297@inline function calc_mpi_mortar_flux!(fstar_primary, fstar_secondary,298mesh::Union{P4estMeshParallel{2},299T8codeMeshParallel{2}},300have_nonconservative_terms::False, equations,301surface_integral, dg::DG, cache,302mortar_index, position_index, normal_direction,303node_index)304@unpack u = cache.mpi_mortars305@unpack surface_flux = surface_integral306307u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index, node_index,308mortar_index)309310flux = surface_flux(u_ll, u_rr, normal_direction, equations)311312# Copy flux to buffer313set_node_vars!(fstar_primary[position_index], flux, equations, dg, node_index)314set_node_vars!(fstar_secondary[position_index], flux, equations, dg, node_index)315316return nothing317end318319# Inlined version of the mortar flux computation on small elements for non-conservative equations320@inline function calc_mpi_mortar_flux!(fstar_primary, fstar_secondary,321mesh::Union{P4estMeshParallel{2},322T8codeMeshParallel{2}},323have_nonconservative_terms::True, equations,324surface_integral, dg::DG, cache,325mortar_index, position_index, normal_direction,326node_index)327@unpack u = cache.mpi_mortars328surface_flux, nonconservative_flux = surface_integral.surface_flux329330u_ll, u_rr = get_surface_node_vars(u, equations, dg, position_index, node_index,331mortar_index)332333flux = surface_flux(u_ll, u_rr, normal_direction, equations)334noncons_flux_primary = nonconservative_flux(u_ll, u_rr, normal_direction, equations)335noncons_flux_secondary = nonconservative_flux(u_rr, u_ll, normal_direction,336equations)337338for v in eachvariable(equations)339fstar_primary[position_index][v, node_index] = flux[v] +3400.5f0 * noncons_flux_primary[v]341fstar_secondary[position_index][v, node_index] = flux[v] +3420.5f0 *343noncons_flux_secondary[v]344end345346return nothing347end348349@inline function mpi_mortar_fluxes_to_elements!(surface_flux_values,350mesh::Union{P4estMeshParallel{2},351T8codeMeshParallel{2}},352equations,353mortar_l2::LobattoLegendreMortarL2,354dg::DGSEM, cache, mortar, fstar_primary,355fstar_secondary,356u_buffer)357@unpack local_neighbor_ids, local_neighbor_positions, node_indices = cache.mpi_mortars358359small_indices = node_indices[1, mortar]360small_direction = indices2direction(small_indices)361large_indices = node_indices[2, mortar]362large_direction = indices2direction(large_indices)363364for (element, position) in zip(local_neighbor_ids[mortar],365local_neighbor_positions[mortar])366if position == 3 # -> large element367# Project small fluxes to large element.368multiply_dimensionwise!(u_buffer,369mortar_l2.reverse_upper, fstar_secondary[2],370mortar_l2.reverse_lower, fstar_secondary[1])371# The flux is calculated in the outward direction of the small elements,372# so the sign must be switched to get the flux in outward direction373# of the large element.374# The contravariant vectors of the large element (and therefore the normal375# vectors of the large element as well) are twice as large as the376# contravariant vectors of the small elements. Therefore, the flux needs377# to be scaled by a factor of 2 to obtain the flux of the large element.378u_buffer .*= -2379# Copy interpolated flux values from buffer to large element face in the380# correct orientation.381# Note that the index of the small sides will always run forward but382# the index of the large side might need to run backwards for flipped sides.383if :i_backward in large_indices384for i in eachnode(dg)385for v in eachvariable(equations)386surface_flux_values[v, end + 1 - i, large_direction, element] = u_buffer[v,387i]388end389end390else391for i in eachnode(dg)392for v in eachvariable(equations)393surface_flux_values[v, i, large_direction, element] = u_buffer[v,394i]395end396end397end398else # position in (1, 2) -> small element399# Copy solution small to small400for i in eachnode(dg)401for v in eachvariable(equations)402surface_flux_values[v, i, small_direction, element] = fstar_primary[position][v,403i]404end405end406end407end408409return nothing410end411end # muladd412413414