Path: blob/main/src/semidiscretization/semidiscretization_hyperbolic.jl
5586 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"""8SemidiscretizationHyperbolic910A struct containing everything needed to describe a spatial semidiscretization11of a hyperbolic conservation law.12"""13mutable struct SemidiscretizationHyperbolic{Mesh, Equations, InitialCondition,14BoundaryConditions,15SourceTerms, Solver, Cache} <:16AbstractSemidiscretization17mesh::Mesh18equations::Equations1920# This guy is a bit messy since we abuse it as some kind of "exact solution"21# although this doesn't really exist...22const initial_condition::InitialCondition2324const boundary_conditions::BoundaryConditions25const source_terms::SourceTerms26const solver::Solver27cache::Cache28performance_counter::PerformanceCounter29end30# We assume some properties of the fields of the semidiscretization, e.g.,31# the `equations` and the `mesh` should have the same dimension. We check these32# properties in the outer constructor defined below. While we could ensure33# them even better in an inner constructor, we do not use this approach to34# simplify the integration with Adapt.jl for GPU usage, see35# https://github.com/trixi-framework/Trixi.jl/pull/2677#issuecomment-35917899213637"""38SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;39source_terms=nothing,40boundary_conditions,41RealT=real(solver),42uEltype=RealT)4344Construct a semidiscretization of a hyperbolic PDE.4546Boundary conditions must be provided explicitly either as a `NamedTuple` or as a47single boundary condition that gets applied to all boundaries.48"""49function SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;50source_terms = nothing,51boundary_conditions,52# `RealT` is used as real type for node locations etc.53# while `uEltype` is used as element type of solutions etc.54RealT = real(solver), uEltype = RealT)55@assert ndims(mesh) == ndims(equations)5657cache = create_cache(mesh, equations, solver, RealT, uEltype)58_boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver,59cache)6061check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions)6263performance_counter = PerformanceCounter()6465return SemidiscretizationHyperbolic{typeof(mesh), typeof(equations),66typeof(initial_condition),67typeof(_boundary_conditions),68typeof(source_terms),69typeof(solver), typeof(cache)}(mesh, equations,70initial_condition,71_boundary_conditions,72source_terms,73solver, cache,74performance_counter)75end7677# @eval due to @muladd78@eval Adapt.@adapt_structure(SemidiscretizationHyperbolic)7980# Create a new semidiscretization but change some parameters compared to the input.81# `Base.similar` follows a related concept but would require us to `copy` the `mesh`,82# which would impact the performance. Instead, `SciMLBase.remake` has exactly the83# semantics we want to use here. In particular, it allows us to re-use mutable parts,84# e.g. `remake(semi).mesh === semi.mesh`.85function remake(semi::SemidiscretizationHyperbolic; uEltype = real(semi.solver),86mesh = semi.mesh,87equations = semi.equations,88initial_condition = semi.initial_condition,89solver = semi.solver,90source_terms = semi.source_terms,91boundary_conditions = semi.boundary_conditions)92# TODO: Which parts do we want to `remake`? At least the solver needs some93# special care if shock-capturing volume integrals are used (because of94# the indicators and their own caches...).95return SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;96source_terms, boundary_conditions, uEltype)97end9899# general fallback100function digest_boundary_conditions(boundary_conditions, mesh, solver, cache)101return boundary_conditions102end103104# general fallback105function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,106mesh, solver, cache)107return boundary_conditions108end109110# resolve ambiguities with definitions below111function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,112mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,113cache)114return boundary_conditions115end116117function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,118mesh::Union{TreeMesh{2}, StructuredMesh{2}},119solver, cache)120return boundary_conditions121end122123function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,124mesh::Union{TreeMesh{3}, StructuredMesh{3}},125solver, cache)126return boundary_conditions127end128129function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,130mesh::Union{P4estMesh{2}, UnstructuredMesh2D,131T8codeMesh{2}},132solver, cache)133return boundary_conditions134end135136function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,137mesh::Union{P4estMesh{3}, T8codeMesh{3}},138solver, cache)139return boundary_conditions140end141142# allow passing a single BC that get converted into a tuple of BCs143# on (mapped) hypercube domains144function digest_boundary_conditions(boundary_conditions,145mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,146cache)147return (; x_neg = boundary_conditions, x_pos = boundary_conditions)148end149150function digest_boundary_conditions(boundary_conditions,151mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,152cache)153return (; x_neg = boundary_conditions, x_pos = boundary_conditions,154y_neg = boundary_conditions, y_pos = boundary_conditions)155end156157function digest_boundary_conditions(boundary_conditions,158mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,159cache)160return (; x_neg = boundary_conditions, x_pos = boundary_conditions,161y_neg = boundary_conditions, y_pos = boundary_conditions,162z_neg = boundary_conditions, z_pos = boundary_conditions)163end164165# allow passing named tuples of BCs constructed in an arbitrary order166# on (mapped) hypercube domains167function digest_boundary_conditions(boundary_conditions::NamedTuple{Keys, ValueTypes},168mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,169cache) where {Keys, ValueTypes <: NTuple{2, Any}}170@unpack x_neg, x_pos = boundary_conditions171return (; x_neg, x_pos)172end173174function digest_boundary_conditions(boundary_conditions::NamedTuple{Keys, ValueTypes},175mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,176cache) where {Keys, ValueTypes <: NTuple{4, Any}}177@unpack x_neg, x_pos, y_neg, y_pos = boundary_conditions178return (; x_neg, x_pos, y_neg, y_pos)179end180181function digest_boundary_conditions(boundary_conditions::NamedTuple{Keys, ValueTypes},182mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,183cache) where {Keys, ValueTypes <: NTuple{6, Any}}184@unpack x_neg, x_pos, y_neg, y_pos, z_neg, z_pos = boundary_conditions185return (; x_neg, x_pos, y_neg, y_pos, z_neg, z_pos)186end187188# If a NamedTuple is passed with not the same number of BCs, ensure that the keys are correct.189# For periodic boundary parts, the keys can be missing and get filled with `boundary_condition_periodic`.190function digest_boundary_conditions(boundary_conditions::NamedTuple,191mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,192cache)193x_neg, x_pos = get_periodicity_boundary_conditions_x(boundary_conditions, mesh)194return (; x_neg, x_pos)195end196197function digest_boundary_conditions(boundary_conditions::NamedTuple,198mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,199cache)200x_neg, x_pos = get_periodicity_boundary_conditions_x(boundary_conditions, mesh)201y_neg, y_pos = get_periodicity_boundary_conditions_y(boundary_conditions, mesh)202return (; x_neg, x_pos, y_neg, y_pos)203end204205function digest_boundary_conditions(boundary_conditions::NamedTuple,206mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,207cache)208x_neg, x_pos = get_periodicity_boundary_conditions_x(boundary_conditions, mesh)209y_neg, y_pos = get_periodicity_boundary_conditions_y(boundary_conditions, mesh)210z_neg, z_pos = get_periodicity_boundary_conditions_z(boundary_conditions, mesh)211return (; x_neg, x_pos, y_neg, y_pos, z_neg, z_pos)212end213214# Allow NamedTuple for P4estMesh, UnstructuredMesh2D, and T8codeMesh215# define in two functions to resolve ambiguities216function digest_boundary_conditions(boundary_conditions::NamedTuple,217mesh::Union{P4estMesh{2}, P4estMeshView{2},218UnstructuredMesh2D,219T8codeMesh{2}},220solver, cache)221return UnstructuredSortedBoundaryTypes(boundary_conditions, cache)222end223224function digest_boundary_conditions(boundary_conditions::NamedTuple,225mesh::Union{P4estMesh{3}, T8codeMesh{3}},226solver, cache)227return UnstructuredSortedBoundaryTypes(boundary_conditions, cache)228end229230function digest_boundary_conditions(boundary_conditions::UnstructuredSortedBoundaryTypes,231mesh::Union{P4estMesh{2}, P4estMeshView{2},232UnstructuredMesh2D,233T8codeMesh{2}},234solver, cache)235return boundary_conditions236end237238function digest_boundary_conditions(boundary_conditions::UnstructuredSortedBoundaryTypes,239mesh::Union{P4estMesh{3}, T8codeMesh{3}},240solver, cache)241return boundary_conditions242end243244# allow passing a single BC that get converted into a named tuple of BCs245# on (mapped) hypercube domains246function digest_boundary_conditions(boundary_conditions,247mesh::Union{P4estMesh{2}, P4estMeshView{2},248UnstructuredMesh2D,249T8codeMesh{2}},250solver, cache)251bcs = (; x_neg = boundary_conditions, x_pos = boundary_conditions,252y_neg = boundary_conditions, y_pos = boundary_conditions)253return UnstructuredSortedBoundaryTypes(bcs, cache)254end255256function digest_boundary_conditions(boundary_conditions,257mesh::Union{P4estMesh{3}, T8codeMesh{3}},258solver, cache)259bcs = (; x_neg = boundary_conditions, x_pos = boundary_conditions,260y_neg = boundary_conditions, y_pos = boundary_conditions,261z_neg = boundary_conditions, z_pos = boundary_conditions)262return UnstructuredSortedBoundaryTypes(bcs, cache)263end264265# add methods for every dimension to resolve ambiguities266function digest_boundary_conditions(boundary_conditions::AbstractArray,267mesh::Union{TreeMesh{1}, StructuredMesh{1}},268solver, cache)269throw(ArgumentError("Please use a named tuple instead of an (abstract) array to supply multiple boundary conditions (to improve performance)."))270end271272function digest_boundary_conditions(boundary_conditions::AbstractArray,273mesh::Union{TreeMesh{2}, StructuredMesh{2}},274solver, cache)275throw(ArgumentError("Please use a named tuple instead of an (abstract) array to supply multiple boundary conditions (to improve performance)."))276end277278function digest_boundary_conditions(boundary_conditions::AbstractArray,279mesh::Union{TreeMesh{3}, StructuredMesh{3}},280solver, cache)281throw(ArgumentError("Please use a named tuple instead of an (abstract) array to supply multiple boundary conditions (to improve performance)."))282end283284function digest_boundary_conditions(boundary_conditions::Tuple,285mesh::Union{TreeMesh{1}, StructuredMesh{1}},286solver, cache)287throw(ArgumentError("Please use a named tuple instead of a tuple to supply multiple boundary conditions."))288end289290function digest_boundary_conditions(boundary_conditions::Tuple,291mesh::Union{TreeMesh{2}, StructuredMesh{2}},292solver, cache)293throw(ArgumentError("Please use a named tuple instead of a tuple to supply multiple boundary conditions."))294end295296function digest_boundary_conditions(boundary_conditions::Tuple,297mesh::Union{TreeMesh{3}, StructuredMesh{3}},298solver, cache)299throw(ArgumentError("Please use a named tuple instead of a tuple to supply multiple boundary conditions."))300end301302function get_periodicity_boundary_conditions_x(boundary_conditions, mesh)303if isperiodic(mesh, 1)304if :x_neg in keys(boundary_conditions) &&305boundary_conditions.x_neg != boundary_condition_periodic ||306:x_pos in keys(boundary_conditions) &&307boundary_conditions.x_pos != boundary_condition_periodic308throw(ArgumentError("For periodic mesh non-periodic boundary conditions in x-direction are supplied."))309end310x_neg = x_pos = boundary_condition_periodic311else312required = (:x_neg, :x_pos)313if !all(in(keys(boundary_conditions)), required)314throw(ArgumentError("NamedTuple of boundary conditions for 1-dimensional (non-periodic) mesh must have keys $(required), got $(keys(boundary_conditions))"))315end316@unpack x_neg, x_pos = boundary_conditions317end318return x_neg, x_pos319end320321function get_periodicity_boundary_conditions_y(boundary_conditions, mesh)322if isperiodic(mesh, 2)323if :y_neg in keys(boundary_conditions) &&324boundary_conditions.y_neg != boundary_condition_periodic ||325:y_pos in keys(boundary_conditions) &&326boundary_conditions.y_pos != boundary_condition_periodic327throw(ArgumentError("For periodic mesh non-periodic boundary conditions in y-direction are supplied."))328end329y_neg = y_pos = boundary_condition_periodic330else331required = (:y_neg, :y_pos)332if !all(in(keys(boundary_conditions)), required)333throw(ArgumentError("NamedTuple of boundary conditions for 2-dimensional (non-periodic) mesh must have keys $(required), got $(keys(boundary_conditions))"))334end335@unpack y_neg, y_pos = boundary_conditions336end337return y_neg, y_pos338end339340function get_periodicity_boundary_conditions_z(boundary_conditions, mesh)341if isperiodic(mesh, 3)342if :z_neg in keys(boundary_conditions) &&343boundary_conditions.z_neg != boundary_condition_periodic ||344:z_pos in keys(boundary_conditions) &&345boundary_conditions.z_pos != boundary_condition_periodic346throw(ArgumentError("For periodic mesh non-periodic boundary conditions in z-direction are supplied."))347end348z_neg = z_pos = boundary_condition_periodic349else350required = (:z_neg, :z_pos)351if !all(in(keys(boundary_conditions)), required)352throw(ArgumentError("NamedTuple of boundary conditions for 3-dimensional (non-periodic) mesh must have keys $(required), got $(keys(boundary_conditions))"))353end354@unpack z_neg, z_pos = boundary_conditions355end356return z_neg, z_pos357end358359# No checks for these meshes yet available360function check_periodicity_mesh_boundary_conditions(mesh::Union{P4estMesh,361P4estMeshView,362UnstructuredMesh2D,363T8codeMesh,364DGMultiMesh},365boundary_conditions)366return nothing367end368369function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh,370StructuredMesh,371StructuredMeshView},372boundary_conditions::BoundaryConditionPeriodic)373if !isperiodic(mesh)374throw(ArgumentError("Periodic boundary condition supplied for non-periodic mesh."))375end376return nothing377end378379function check_periodicity_mesh_boundary_conditions_x(mesh, x_neg, x_pos)380if isperiodic(mesh, 1) &&381(x_neg != boundary_condition_periodic ||382x_pos != boundary_condition_periodic)383throw(ArgumentError("For periodic mesh non-periodic boundary conditions in x-direction are supplied."))384end385if !isperiodic(mesh, 1) &&386(x_neg == boundary_condition_periodic ||387x_pos == boundary_condition_periodic)388throw(ArgumentError("For non-periodic mesh periodic boundary conditions in x-direction are supplied."))389end390return nothing391end392393function check_periodicity_mesh_boundary_conditions_y(mesh, y_neg, y_pos)394if isperiodic(mesh, 2) &&395(y_neg != boundary_condition_periodic ||396y_pos != boundary_condition_periodic)397throw(ArgumentError("For periodic mesh non-periodic boundary conditions in y-direction are supplied."))398end399if !isperiodic(mesh, 2) &&400(y_neg == boundary_condition_periodic ||401y_pos == boundary_condition_periodic)402throw(ArgumentError("For non-periodic mesh periodic boundary conditions in y-direction are supplied."))403end404return nothing405end406407function check_periodicity_mesh_boundary_conditions_z(mesh, z_neg, z_pos)408if isperiodic(mesh, 3) &&409(z_neg != boundary_condition_periodic ||410z_pos != boundary_condition_periodic)411throw(ArgumentError("For periodic mesh non-periodic boundary conditions in z-direction are supplied."))412end413if !isperiodic(mesh, 3) &&414(z_neg == boundary_condition_periodic ||415z_pos == boundary_condition_periodic)416throw(ArgumentError("For non-periodic mesh periodic boundary conditions in z-direction are supplied."))417end418return nothing419end420421function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{1},422StructuredMesh{1}},423boundary_conditions::NamedTuple)424return check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions.x_neg,425boundary_conditions.x_pos)426end427428function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{2},429StructuredMesh{2},430StructuredMeshView{2}},431boundary_conditions::NamedTuple)432check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions.x_neg,433boundary_conditions.x_pos)434return check_periodicity_mesh_boundary_conditions_y(mesh, boundary_conditions.y_neg,435boundary_conditions.y_pos)436end437438function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{3},439StructuredMesh{3}},440boundary_conditions::NamedTuple)441check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions.x_neg,442boundary_conditions.x_pos)443check_periodicity_mesh_boundary_conditions_y(mesh, boundary_conditions.y_neg,444boundary_conditions.y_pos)445return check_periodicity_mesh_boundary_conditions_z(mesh, boundary_conditions.z_neg,446boundary_conditions.z_pos)447end448449function Base.show(io::IO, semi::SemidiscretizationHyperbolic)450@nospecialize semi # reduce precompilation time451452print(io, "SemidiscretizationHyperbolic(")453print(io, semi.mesh)454print(io, ", ", semi.equations)455print(io, ", ", semi.initial_condition)456print(io, ", ", semi.boundary_conditions)457print(io, ", ", semi.source_terms)458print(io, ", ", semi.solver)459print(io, ", cache(")460for (idx, key) in enumerate(keys(semi.cache))461idx > 1 && print(io, " ")462print(io, key)463end464print(io, "))")465return nothing466end467468function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationHyperbolic)469@nospecialize semi # reduce precompilation time470471if get(io, :compact, false)472show(io, semi)473else474summary_header(io, "SemidiscretizationHyperbolic")475summary_line(io, "#spatial dimensions", ndims(semi.equations))476summary_line(io, "mesh", semi.mesh)477summary_line(io, "equations", semi.equations |> typeof |> nameof)478summary_line(io, "initial condition", semi.initial_condition)479480print_boundary_conditions(io, semi)481482summary_line(io, "source terms", semi.source_terms)483summary_line(io, "solver", semi.solver |> typeof |> nameof)484summary_line(io, "total #DOFs per field", ndofsglobal(semi))485summary_footer(io)486end487end488489# type alias for dispatch in printing of boundary conditions490#! format: off491const SemiHypMeshBCSolver{Mesh, BoundaryConditions, Solver} =492SemidiscretizationHyperbolic{Mesh,493Equations,494InitialCondition,495BoundaryConditions,496SourceTerms,497Solver} where {Equations,498InitialCondition,499SourceTerms}500#! format: on501502# generic fallback: print the type of semi.boundary_condition.503function print_boundary_conditions(io, semi::SemiHypMeshBCSolver)504return summary_line(io, "boundary conditions", typeof(semi.boundary_conditions))505end506507function print_boundary_conditions(io,508semi::SemiHypMeshBCSolver{<:Any,509<:UnstructuredSortedBoundaryTypes})510@unpack boundary_conditions = semi.boundary_conditions511summary_line(io, "boundary conditions", length(boundary_conditions))512for (boundary_name, boundary_condition) in pairs(boundary_conditions)513summary_line(increment_indent(io), boundary_name, typeof(boundary_condition))514end515end516517function print_boundary_conditions(io, semi::SemiHypMeshBCSolver{<:Any, <:NamedTuple})518@unpack boundary_conditions = semi519summary_line(io, "boundary conditions", length(boundary_conditions))520bc_names = keys(boundary_conditions)521for (i, bc_name) in enumerate(bc_names)522summary_line(increment_indent(io), String(bc_name),523typeof(boundary_conditions[i]))524end525end526527function print_boundary_conditions(io,528semi::SemiHypMeshBCSolver{<:Union{TreeMesh,529StructuredMesh},530<:Union{NamedTuple,531AbstractArray}})532summary_line(io, "boundary conditions", 2 * ndims(semi))533bcs = semi.boundary_conditions534535summary_line(increment_indent(io), "negative x", bcs.x_neg)536summary_line(increment_indent(io), "positive x", bcs.x_pos)537if ndims(semi) > 1538summary_line(increment_indent(io), "negative y", bcs.y_neg)539summary_line(increment_indent(io), "positive y", bcs.y_pos)540end541if ndims(semi) > 2542summary_line(increment_indent(io), "negative z", bcs.z_neg)543summary_line(increment_indent(io), "positive z", bcs.z_pos)544end545end546547@inline Base.ndims(semi::SemidiscretizationHyperbolic) = ndims(semi.mesh)548549@inline nvariables(semi::SemidiscretizationHyperbolic) = nvariables(semi.equations)550551@inline Base.real(semi::SemidiscretizationHyperbolic) = real(semi.solver)552553@inline function mesh_equations_solver_cache(semi::SemidiscretizationHyperbolic)554@unpack mesh, equations, solver, cache = semi555return mesh, equations, solver, cache556end557558function calc_error_norms(func, u_ode, t, analyzer, semi::SemidiscretizationHyperbolic,559cache_analysis)560@unpack mesh, equations, initial_condition, solver, cache = semi561u = wrap_array(u_ode, mesh, equations, solver, cache)562563return calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition,564solver,565cache, cache_analysis)566end567568function compute_coefficients(t, semi::SemidiscretizationHyperbolic)569# Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl`570return compute_coefficients(semi.initial_condition, t, semi)571end572573function compute_coefficients!(u_ode, t, semi::SemidiscretizationHyperbolic)574return compute_coefficients!(u_ode, semi.initial_condition, t, semi)575end576577function rhs!(du_ode, u_ode, semi::SemidiscretizationHyperbolic, t)578@unpack mesh, equations, boundary_conditions, source_terms, solver, cache = semi579580u = wrap_array(u_ode, mesh, equations, solver, cache)581du = wrap_array(du_ode, mesh, equations, solver, cache)582backend = trixi_backend(u)583584# TODO: Taal decide, do we need to pass the mesh?585time_start = time_ns()586@trixi_timeit_ext backend timer() "rhs!" rhs!(du, u, t, mesh, equations,587boundary_conditions, source_terms,588solver, cache)589runtime = time_ns() - time_start590put!(semi.performance_counter, runtime)591592return nothing593end594end # @muladd595596597