Path: blob/main/src/solvers/dgsem_p4est/dg_2d_parabolic_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: noindent6function rhs_parabolic!(du, u, t,7mesh::P4estMeshParallel{2},8equations_parabolic::AbstractEquationsParabolic,9boundary_conditions_parabolic, source_terms_parabolic,10dg::DG, parabolic_scheme, cache, cache_parabolic)11@unpack parabolic_container = cache_parabolic12@unpack u_transformed, gradients, flux_parabolic = parabolic_container1314@trixi_timeit timer() "transform variables" begin15transform_variables!(u_transformed, u, mesh, equations_parabolic,16dg, cache)17end1819### Gradient computation ###2021# Start gradient MPI receive22@trixi_timeit timer() "start MPI receive gradient" begin23start_mpi_receive!(cache.mpi_cache)24end2526# Prolong transformed variables to MPI mortars27@trixi_timeit timer() "prolong2mpimortars gradient" begin28prolong2mpimortars!(cache, u_transformed, mesh, equations_parabolic,29dg.mortar, dg)30end3132# Prolong transformed variables to MPI interfaces33@trixi_timeit timer() "prolong2mpiinterfaces gradient" begin34prolong2mpiinterfaces!(cache, u_transformed, mesh, equations_parabolic,35dg.surface_integral, dg)36end3738# Start gradient MPI send39@trixi_timeit timer() "start MPI send gradient" begin40start_mpi_send!(cache.mpi_cache, mesh, equations_parabolic, dg, cache)41end4243# Local gradient computation44@trixi_timeit timer() "calculate gradient local" begin45calc_gradient_local!(gradients, u_transformed, t, mesh,46equations_parabolic, boundary_conditions_parabolic,47dg, parabolic_scheme, cache)48end4950# Finish gradient MPI receive51@trixi_timeit timer() "finish MPI receive gradient" begin52finish_mpi_receive!(cache.mpi_cache, mesh, equations_parabolic, dg, cache)53end5455# MPI interface fluxes for gradients56@trixi_timeit timer() "MPI interface flux gradient" begin57calc_mpi_interface_flux_gradient!(cache.elements.surface_flux_values,58mesh, equations_parabolic,59dg, parabolic_scheme, cache)60end6162# MPI mortar fluxes for gradients63@trixi_timeit timer() "MPI mortar flux gradient" begin64calc_mpi_mortar_flux_gradient!(cache.elements.surface_flux_values,65mesh, equations_parabolic, dg.mortar,66dg, parabolic_scheme, cache)67end6869# Calculate surface integrals70@trixi_timeit timer() "surface integral" begin71calc_surface_integral_gradient!(gradients, mesh, equations_parabolic,72dg, cache)73end7475# Apply Jacobian from mapping to reference element76@trixi_timeit timer() "Jacobian" begin77apply_jacobian_parabolic!(gradients, mesh, equations_parabolic, dg,78cache)79end8081# Finish gradient MPI send82@trixi_timeit timer() "finish MPI send gradient" begin83finish_mpi_send!(cache.mpi_cache)84end8586# Local parabolic flux construction87@trixi_timeit timer() "calculate parabolic fluxes" begin88calc_parabolic_fluxes!(flux_parabolic, gradients, u_transformed, mesh,89equations_parabolic, dg, cache)90end9192### Divergence of parabolic/gradient fluxes ###9394# Start divergence MPI receive95@trixi_timeit timer() "start MPI receive divergence" begin96start_mpi_receive!(cache.mpi_cache)97end9899# Reset du100@trixi_timeit timer() "reset ∂u/∂t" begin101set_zero!(du, dg, cache)102end103104# Local volume integral105@trixi_timeit timer() "volume integral" begin106calc_volume_integral!(du, flux_parabolic, mesh, equations_parabolic, dg, cache)107end108109# Prolong parabolic fluxes to MPI mortars110@trixi_timeit timer() "prolong2mpimortars divergence" begin111prolong2mpimortars_divergence!(cache, flux_parabolic, mesh, equations_parabolic,112dg.mortar, dg)113end114115# Prolong parabolic fluxes to MPI interfaces116@trixi_timeit timer() "prolong2mpiinterfaces divergence" begin117prolong2mpiinterfaces!(cache, flux_parabolic, mesh, equations_parabolic, dg)118end119120# Start divergence MPI send121@trixi_timeit timer() "start MPI send divergence" begin122start_mpi_send!(cache.mpi_cache, mesh, equations_parabolic, dg, cache)123end124125# Local interface fluxes126@trixi_timeit timer() "prolong2interfaces" begin127prolong2interfaces!(cache, flux_parabolic, mesh, equations_parabolic, dg)128end129130@trixi_timeit timer() "interface flux" begin131calc_interface_flux!(cache.elements.surface_flux_values, mesh,132equations_parabolic, dg, parabolic_scheme, cache)133end134135# Local boundary fluxes136@trixi_timeit timer() "prolong2boundaries" begin137prolong2boundaries!(cache, flux_parabolic, mesh, equations_parabolic, dg)138end139140@trixi_timeit timer() "boundary flux" begin141calc_boundary_flux_divergence!(cache, t,142boundary_conditions_parabolic, mesh,143equations_parabolic,144dg.surface_integral, dg)145end146147# Local mortar fluxes148@trixi_timeit timer() "prolong2mortars" begin149prolong2mortars_divergence!(cache, flux_parabolic, mesh, equations_parabolic,150dg.mortar, dg)151end152153@trixi_timeit timer() "mortar flux" begin154calc_mortar_flux_divergence!(cache.elements.surface_flux_values,155mesh, equations_parabolic, dg.mortar,156dg, parabolic_scheme, cache)157end158159# Finish divergence MPI receive160@trixi_timeit timer() "finish MPI receive divergence" begin161finish_mpi_receive!(cache.mpi_cache, mesh, equations_parabolic, dg, cache)162end163164# MPI interface fluxes for divergence165@trixi_timeit timer() "MPI interface flux divergence" begin166calc_mpi_interface_flux_divergence!(cache.elements.surface_flux_values,167mesh, equations_parabolic,168dg, parabolic_scheme, cache)169end170171# MPI mortar fluxes for divergence172@trixi_timeit timer() "MPI mortar flux divergence" begin173calc_mpi_mortar_flux_divergence!(cache.elements.surface_flux_values,174mesh, equations_parabolic, dg.mortar,175dg, parabolic_scheme, cache)176end177178# Finish divergence MPI send179@trixi_timeit timer() "finish MPI send divergence" begin180finish_mpi_send!(cache.mpi_cache)181end182183@trixi_timeit timer() "surface integral" begin184calc_surface_integral!(nothing, du, u, mesh, equations_parabolic,185dg.surface_integral, dg, cache)186end187188@trixi_timeit timer() "Jacobian" begin189apply_jacobian_parabolic!(du, mesh, equations_parabolic, dg, cache)190end191192@trixi_timeit timer() "source terms parabolic" begin193calc_sources_parabolic!(du, u, gradients, t, source_terms_parabolic,194equations_parabolic, dg, cache)195end196197return nothing198end199200function calc_gradient_local!(gradients, u_transformed, t,201mesh::P4estMeshParallel{2},202equations_parabolic, boundary_conditions_parabolic,203dg::DG, parabolic_scheme, cache)204# Reset gradients205@trixi_timeit timer() "reset gradients" begin206reset_gradients!(gradients, dg, cache)207end208209# Calculate volume integral210@trixi_timeit timer() "volume integral" begin211calc_volume_integral_gradient!(gradients, u_transformed,212mesh, equations_parabolic, dg, cache)213end214215# Prolong solution to interfaces.216# This reuses `prolong2interfaces` for the purely hyperbolic case.217@trixi_timeit timer() "prolong2interfaces" begin218prolong2interfaces!(nothing, cache, u_transformed, mesh,219equations_parabolic, dg)220end221222# Calculate interface fluxes for the gradients223@trixi_timeit timer() "interface flux" begin224@unpack surface_flux_values = cache.elements225calc_interface_flux_gradient!(surface_flux_values, mesh, equations_parabolic,226dg, parabolic_scheme, cache)227end228229# Prolong solution to boundaries.230# This reuses `prolong2boundaries` for the purely hyperbolic case.231@trixi_timeit timer() "prolong2boundaries" begin232prolong2boundaries!(cache, u_transformed, mesh,233equations_parabolic, dg)234end235236# Calculate boundary fluxes237@trixi_timeit timer() "boundary flux" begin238calc_boundary_flux_gradient!(cache, t, boundary_conditions_parabolic,239mesh, equations_parabolic, dg.surface_integral,240dg)241end242243# Prolong solution to mortars.244# This reuses `prolong2mortars` for the purely hyperbolic case.245@trixi_timeit timer() "prolong2mortars" begin246prolong2mortars!(cache, u_transformed, mesh, equations_parabolic,247dg.mortar, dg)248end249250# Calculate mortar fluxes251@trixi_timeit timer() "mortar flux" begin252calc_mortar_flux_gradient!(cache.elements.surface_flux_values,253mesh, equations_parabolic, dg.mortar,254dg, parabolic_scheme, cache)255end256257return nothing258end259260function prolong2mpiinterfaces!(cache, flux_parabolic::Tuple,261mesh::P4estMeshParallel{2},262equations_parabolic, dg::DG)263@unpack local_neighbor_ids, node_indices, local_sides = cache.mpi_interfaces264@unpack contravariant_vectors = cache.elements265index_range = eachnode(dg)266267flux_parabolic_x, flux_parabolic_y = flux_parabolic268269@threaded for interface in eachmpiinterface(dg, cache)270# Copy solution data from the local element using "delayed indexing" with271# a start value and a step size to get the correct face and orientation.272# Note that in the current implementation, the interface will be273# "aligned at the primary element", i.e., the index of the primary side274# will always run forwards.275local_element = local_neighbor_ids[interface]276local_indices = node_indices[interface]277local_direction = indices2direction(local_indices)278local_side = local_sides[interface]279# store the normal flux with respect to the primary normal direction,280# which is the negative of the secondary normal direction281orientation_factor = local_side == 1 ? 1 : -1282283i_start, i_step = index_to_start_step_2d(local_indices[1], index_range)284j_start, j_step = index_to_start_step_2d(local_indices[2], index_range)285286i_elem = i_start287j_elem = j_start288289for i in eachnode(dg)290normal_direction = get_normal_direction(local_direction,291contravariant_vectors,292i_elem, j_elem,293local_element)294295for v in eachvariable(equations_parabolic)296flux_visc = SVector(flux_parabolic_x[v, i_elem, j_elem, local_element],297flux_parabolic_y[v, i_elem, j_elem, local_element])298# Side 1 and 2 must be consistent, i.e., with their outward-pointing normals.299# Thus, the `orientation_factor` changes the logic such that the300# flux which enters side 1 leaves side 2.301cache.mpi_interfaces.u[local_side, v, i, interface] = orientation_factor *302dot(flux_visc,303normal_direction)304end305306i_elem += i_step307j_elem += j_step308end309end310311return nothing312end313314function calc_mpi_interface_flux_gradient!(surface_flux_values,315mesh::P4estMeshParallel{2},316equations_parabolic,317dg::DG, parabolic_scheme, cache)318@unpack local_neighbor_ids, node_indices, local_sides = cache.mpi_interfaces319@unpack contravariant_vectors = cache.elements320@unpack u = cache.mpi_interfaces321index_range = eachnode(dg)322index_end = last(index_range)323324@threaded for interface in eachmpiinterface(dg, cache)325local_element = local_neighbor_ids[interface]326local_indices = node_indices[interface]327local_direction = indices2direction(local_indices)328local_side = local_sides[interface]329330# Create the local i,j indexing on the local element used to pull normal direction information331i_element_start, i_element_step = index_to_start_step_2d(local_indices[1],332index_range)333j_element_start, j_element_step = index_to_start_step_2d(local_indices[2],334index_range)335336i_element = i_element_start337j_element = j_element_start338339# Initiate the node index to be used in the surface for loop,340# the surface flux storage must be indexed in alignment with the local element indexing341if :i_backward in local_indices342surface_node = index_end343surface_node_step = -1344else345surface_node = 1346surface_node_step = 1347end348349for i in eachnode(dg)350normal_direction = get_normal_direction(local_direction,351contravariant_vectors,352i_element, j_element,353local_element)354355u_ll, u_rr = get_surface_node_vars(u, equations_parabolic, dg,356i, interface)357358flux_ = flux_parabolic(u_ll, u_rr, normal_direction, Gradient(),359equations_parabolic, parabolic_scheme)360361for v in eachvariable(equations_parabolic)362surface_flux_values[v, surface_node,363local_direction, local_element] = flux_[v]364end365366# Increment local element indices to pull the normal direction367# from the element data368i_element += i_element_step369j_element += j_element_step370371# Increment the surface node index along the local element372surface_node += surface_node_step373end374end375376return nothing377end378379function calc_mpi_interface_flux_divergence!(surface_flux_values,380mesh::P4estMeshParallel{2},381equations_parabolic,382dg::DG, parabolic_scheme, cache)383@unpack local_neighbor_ids, node_indices, local_sides = cache.mpi_interfaces384@unpack contravariant_vectors = cache.elements385@unpack u = cache.mpi_interfaces386index_range = eachnode(dg)387index_end = last(index_range)388389@threaded for interface in eachmpiinterface(dg, cache)390local_element = local_neighbor_ids[interface]391local_indices = node_indices[interface]392local_direction = indices2direction(local_indices)393local_side = local_sides[interface]394395i_element_start, i_element_step = index_to_start_step_2d(local_indices[1],396index_range)397j_element_start, j_element_step = index_to_start_step_2d(local_indices[2],398index_range)399400i_element = i_element_start401j_element = j_element_start402403# Initiate the node index to be used in the surface for loop,404# the surface flux storage must be indexed in alignment with the local element indexing405if :i_backward in local_indices406surface_node = index_end407surface_node_step = -1408else409surface_node = 1410surface_node_step = 1411end412413for i in eachnode(dg)414normal_direction = get_normal_direction(local_direction,415contravariant_vectors,416i_element, j_element,417local_element)418419parabolic_flux_normal_ll, parabolic_flux_normal_rr = get_surface_node_vars(u,420equations_parabolic,421dg,422i,423interface)424425# Sign flip for `local_side = 2` required for divergence calculation since426# the divergence interface flux involves the normal direction.427# `local_side=2` is thus flipped (opposite of primary side)428orientation_factor = local_side == 1 ? 1 : -1429flux_ = flux_parabolic(parabolic_flux_normal_ll, parabolic_flux_normal_rr,430orientation_factor * normal_direction, Divergence(),431equations_parabolic, parabolic_scheme)432433for v in eachvariable(equations_parabolic)434surface_flux_values[v, surface_node,435local_direction, local_element] = orientation_factor * flux_[v]436end437438i_element += i_element_step439j_element += j_element_step440441surface_node += surface_node_step442end443end444445return nothing446end447448function calc_mpi_mortar_flux_gradient!(surface_flux_values,449mesh::Union{P4estMeshParallel{2},450T8codeMeshParallel{2}},451equations_parabolic,452mortar_l2::LobattoLegendreMortarL2,453dg::DG, parabolic_scheme, cache)454@unpack (fstar_primary_upper_threaded, fstar_primary_lower_threaded,455fstar_secondary_upper_threaded, fstar_secondary_lower_threaded) = cache456@unpack u = cache.mpi_mortars457@threaded for mortar in eachmpimortar(dg, cache)458fstar_primary = (fstar_primary_lower_threaded[Threads.threadid()],459fstar_primary_upper_threaded[Threads.threadid()])460461fstar_secondary = (fstar_secondary_lower_threaded[Threads.threadid()],462fstar_secondary_upper_threaded[Threads.threadid()])463464for position in 1:2465for i in eachnode(dg)466normal_direction = get_normal_direction(cache.mpi_mortars, i,467position, mortar)468469u_ll, u_rr = get_surface_node_vars(u, equations_parabolic, dg,470position, i, mortar)471472flux = flux_parabolic(u_ll, u_rr, normal_direction, Gradient(),473equations_parabolic, parabolic_scheme)474475set_node_vars!(fstar_primary[position], flux, equations_parabolic, dg,476i)477set_node_vars!(fstar_secondary[position], flux, equations_parabolic, dg,478i)479end480end481482u_buffer = cache.u_threaded[Threads.threadid()]483mpi_mortar_fluxes_to_elements_gradient!(surface_flux_values,484mesh, equations_parabolic, mortar_l2,485dg, cache,486mortar, fstar_primary, fstar_secondary,487u_buffer)488end489490return nothing491end492493@inline function mpi_mortar_fluxes_to_elements_gradient!(surface_flux_values,494mesh::Union{P4estMeshParallel{2},495T8codeMeshParallel{2}},496equations_parabolic,497mortar_l2::LobattoLegendreMortarL2,498dg::DGSEM, cache, mortar,499fstar_primary, fstar_secondary,500u_buffer)501@unpack local_neighbor_ids, local_neighbor_positions, node_indices = cache.mpi_mortars502index_range = eachnode(dg)503index_end = last(index_range)504505small_indices = node_indices[1, mortar]506small_direction = indices2direction(small_indices)507508large_indices = node_indices[2, mortar]509large_direction = indices2direction(large_indices)510511for (element, position) in zip(local_neighbor_ids[mortar],512local_neighbor_positions[mortar])513514# In 2D the large side is the third mortar neighbor slot.515if position == 3516# Project the two small-side traces to the large side.517multiply_dimensionwise!(u_buffer,518mortar_l2.reverse_upper,519fstar_secondary[2],520mortar_l2.reverse_lower,521fstar_secondary[1])522523# Gradient stage: no extra sign flip / scale factor524# (same as local 2D parabolic mortar_fluxes_to_elements!)525if :i_backward in large_indices526for i in eachnode(dg)527for v in eachvariable(equations_parabolic)528surface_flux_values[v, index_end + 1 - i,529large_direction, element] = u_buffer[v, i]530end531end532else533for i in eachnode(dg)534for v in eachvariable(equations_parabolic)535surface_flux_values[v, i,536large_direction, element] = u_buffer[v, i]537end538end539end540else541# Small sides copy directly542for i in eachnode(dg)543for v in eachvariable(equations_parabolic)544surface_flux_values[v, i,545small_direction, element] = fstar_primary[position][v, i]546end547end548end549end550551return nothing552end553554function prolong2mpimortars_divergence!(cache, flux_parabolic,555mesh::Union{P4estMeshParallel{2},556T8codeMeshParallel{2}},557equations_parabolic,558mortar_l2::LobattoLegendreMortarL2,559dg::DGSEM)560@unpack node_indices = cache.mpi_mortars561@unpack contravariant_vectors = cache.elements562index_range = eachnode(dg)563564flux_parabolic_x, flux_parabolic_y = flux_parabolic565566@threaded for mortar in eachmpimortar(dg, cache)567local_neighbor_ids = cache.mpi_mortars.local_neighbor_ids[mortar]568local_neighbor_positions = cache.mpi_mortars.local_neighbor_positions[mortar]569570# Small side indexing571small_indices = node_indices[1, mortar]572small_direction = indices2direction(small_indices)573574i_small_start, i_small_step = index_to_start_step_2d(small_indices[1],575index_range)576j_small_start, j_small_step = index_to_start_step_2d(small_indices[2],577index_range)578579# Large side indexing580large_indices = node_indices[2, mortar]581large_direction = indices2direction(large_indices)582583i_large_start, i_large_step = index_to_start_step_2d(large_indices[1],584index_range)585j_large_start, j_large_step = index_to_start_step_2d(large_indices[2],586index_range)587588for (element, position) in zip(local_neighbor_ids, local_neighbor_positions)589if position == 3590# =========================591# LARGE ELEMENT592# =========================593u_buffer = cache.u_threaded[Threads.threadid()]594595i_large = i_large_start596j_large = j_large_start597598for i in eachnode(dg)599normal_direction = get_normal_direction(large_direction,600contravariant_vectors,601i_large, j_large,602element)603604for v in eachvariable(equations_parabolic)605flux_node = SVector(flux_parabolic_x[v, i_large, j_large,606element],607flux_parabolic_y[v, i_large, j_large,608element])609610# Same convention as local 2D code:611# prolong flux dotted with outward normal on the small element.612# The large-element normal is -2x the small-element normal,613# hence the factor -1/2 here.614u_buffer[v, i] = -0.5f0 * dot(flux_node, normal_direction)615end616617i_large += i_large_step618j_large += j_large_step619end620621multiply_dimensionwise!(view(cache.mpi_mortars.u, 2, :, 1, :, mortar),622mortar_l2.forward_lower, u_buffer)623multiply_dimensionwise!(view(cache.mpi_mortars.u, 2, :, 2, :, mortar),624mortar_l2.forward_upper, u_buffer)625626else627# =========================628# SMALL ELEMENT (1–2)629# =========================630i_small = i_small_start631j_small = j_small_start632633for i in eachnode(dg)634normal_direction = get_normal_direction(small_direction,635contravariant_vectors,636i_small, j_small,637element)638639for v in eachvariable(equations_parabolic)640flux_node = SVector(flux_parabolic_x[v, i_small, j_small,641element],642flux_parabolic_y[v, i_small, j_small,643element])644645cache.mpi_mortars.u[1, v, position, i, mortar] = dot(flux_node,646normal_direction)647end648649i_small += i_small_step650j_small += j_small_step651end652end653end654end655656return nothing657end658659function calc_mpi_mortar_flux_divergence!(surface_flux_values,660mesh::Union{P4estMeshParallel{2},661T8codeMeshParallel{2}},662equations_parabolic,663mortar_l2::LobattoLegendreMortarL2,664dg::DG, parabolic_scheme, cache)665@unpack fstar_primary_upper_threaded, fstar_primary_lower_threaded = cache666@unpack u = cache.mpi_mortars667@threaded for mortar in eachmpimortar(dg, cache)668# Match local 2D structure as one tuple669fstar = (fstar_primary_lower_threaded[Threads.threadid()],670fstar_primary_upper_threaded[Threads.threadid()])671672for position in 1:2673for i in eachnode(dg)674normal_direction = get_normal_direction(cache.mpi_mortars, i,675position, mortar)676677for v in eachvariable(equations_parabolic)678viscous_flux_normal_ll = u[1, v, position, i, mortar]679viscous_flux_normal_rr = u[2, v, position, i, mortar]680681flux = flux_parabolic(viscous_flux_normal_ll,682viscous_flux_normal_rr,683normal_direction, Divergence(),684equations_parabolic, parabolic_scheme)685686# Same convention as local 2D: sign flip / scaling already handled in prolongation687fstar[position][v, i] = flux688end689end690end691692u_buffer = cache.u_threaded[Threads.threadid()]693694# Reuse hyperbolic MPI mortar-to-element transfer, same as local 2D695mpi_mortar_fluxes_to_elements!(surface_flux_values, mesh,696equations_parabolic, mortar_l2, dg, cache,697mortar, fstar, fstar, u_buffer)698end699700return nothing701end702end #muladd703704705