Path: blob/main/src/semidiscretization/semidiscretization_hyperbolic.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"""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...22initial_condition::InitialCondition2324boundary_conditions::BoundaryConditions25source_terms::SourceTerms26solver::Solver27cache::Cache28performance_counter::PerformanceCounter29end3031"""32SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;33source_terms=nothing,34boundary_conditions=boundary_condition_periodic,35RealT=real(solver),36uEltype=RealT)3738Construct a semidiscretization of a hyperbolic PDE.39"""40function SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;41source_terms = nothing,42boundary_conditions = boundary_condition_periodic,43# `RealT` is used as real type for node locations etc.44# while `uEltype` is used as element type of solutions etc.45RealT = real(solver), uEltype = RealT)46@assert ndims(mesh) == ndims(equations)4748cache = create_cache(mesh, equations, solver, RealT, uEltype)49_boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver,50cache)5152check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions)5354performance_counter = PerformanceCounter()5556SemidiscretizationHyperbolic{typeof(mesh), typeof(equations),57typeof(initial_condition),58typeof(_boundary_conditions), typeof(source_terms),59typeof(solver), typeof(cache)}(mesh, equations,60initial_condition,61_boundary_conditions,62source_terms, solver,63cache,64performance_counter)65end6667# @eval due to @muladd68@eval Adapt.@adapt_structure(SemidiscretizationHyperbolic)6970# Create a new semidiscretization but change some parameters compared to the input.71# `Base.similar` follows a related concept but would require us to `copy` the `mesh`,72# which would impact the performance. Instead, `SciMLBase.remake` has exactly the73# semantics we want to use here. In particular, it allows us to re-use mutable parts,74# e.g. `remake(semi).mesh === semi.mesh`.75function remake(semi::SemidiscretizationHyperbolic; uEltype = real(semi.solver),76mesh = semi.mesh,77equations = semi.equations,78initial_condition = semi.initial_condition,79solver = semi.solver,80source_terms = semi.source_terms,81boundary_conditions = semi.boundary_conditions)82# TODO: Which parts do we want to `remake`? At least the solver needs some83# special care if shock-capturing volume integrals are used (because of84# the indicators and their own caches...).85SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver;86source_terms, boundary_conditions, uEltype)87end8889# general fallback90function digest_boundary_conditions(boundary_conditions, mesh, solver, cache)91boundary_conditions92end9394# general fallback95function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,96mesh, solver, cache)97boundary_conditions98end99100# resolve ambiguities with definitions below101function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,102mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,103cache)104boundary_conditions105end106107function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,108mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,109cache)110boundary_conditions111end112113function digest_boundary_conditions(boundary_conditions::BoundaryConditionPeriodic,114mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,115cache)116boundary_conditions117end118119# allow passing a single BC that get converted into a tuple of BCs120# on (mapped) hypercube domains121function digest_boundary_conditions(boundary_conditions,122mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,123cache)124(; x_neg = boundary_conditions, x_pos = boundary_conditions)125end126127function digest_boundary_conditions(boundary_conditions,128mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,129cache)130(; x_neg = boundary_conditions, x_pos = boundary_conditions,131y_neg = boundary_conditions, y_pos = boundary_conditions)132end133134function digest_boundary_conditions(boundary_conditions,135mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,136cache)137(; x_neg = boundary_conditions, x_pos = boundary_conditions,138y_neg = boundary_conditions, y_pos = boundary_conditions,139z_neg = boundary_conditions, z_pos = boundary_conditions)140end141142# allow passing a tuple of BCs that get converted into a named tuple to make it143# self-documenting on (mapped) hypercube domains144function digest_boundary_conditions(boundary_conditions::NTuple{2, Any},145mesh::Union{TreeMesh{1}, StructuredMesh{1}}, solver,146cache)147(; x_neg = boundary_conditions[1], x_pos = boundary_conditions[2])148end149150function digest_boundary_conditions(boundary_conditions::NTuple{4, Any},151mesh::Union{TreeMesh{2}, StructuredMesh{2}}, solver,152cache)153(; x_neg = boundary_conditions[1], x_pos = boundary_conditions[2],154y_neg = boundary_conditions[3], y_pos = boundary_conditions[4])155end156157function digest_boundary_conditions(boundary_conditions::NTuple{6, Any},158mesh::Union{TreeMesh{3}, StructuredMesh{3}}, solver,159cache)160(; x_neg = boundary_conditions[1], x_pos = boundary_conditions[2],161y_neg = boundary_conditions[3], y_pos = boundary_conditions[4],162z_neg = boundary_conditions[5], z_pos = boundary_conditions[6])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_conditions171(; 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_conditions178(; 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_conditions185(; x_neg, x_pos, y_neg, y_pos, z_neg, z_pos)186end187188# sort the boundary conditions from a dictionary and into tuples189function digest_boundary_conditions(boundary_conditions::Dict, mesh, solver, cache)190UnstructuredSortedBoundaryTypes(boundary_conditions, cache)191end192193function digest_boundary_conditions(boundary_conditions::AbstractArray, mesh, solver,194cache)195throw(ArgumentError("Please use a (named) tuple instead of an (abstract) array to supply multiple boundary conditions (to improve performance)."))196end197198# No checks for these meshes yet available199function check_periodicity_mesh_boundary_conditions(mesh::Union{P4estMesh,200P4estMeshView,201UnstructuredMesh2D,202T8codeMesh,203DGMultiMesh},204boundary_conditions)205end206207# No actions needed for periodic boundary conditions208function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh,209StructuredMesh,210StructuredMeshView},211boundary_conditions::BoundaryConditionPeriodic)212end213214function check_periodicity_mesh_boundary_conditions_x(mesh, x_neg, x_pos)215if isperiodic(mesh, 1) &&216(x_neg != BoundaryConditionPeriodic() ||217x_pos != BoundaryConditionPeriodic())218@error "For periodic mesh non-periodic boundary conditions in x-direction are supplied."219end220end221222function check_periodicity_mesh_boundary_conditions_y(mesh, y_neg, y_pos)223if isperiodic(mesh, 2) &&224(y_neg != BoundaryConditionPeriodic() ||225y_pos != BoundaryConditionPeriodic())226@error "For periodic mesh non-periodic boundary conditions in y-direction are supplied."227end228end229230function check_periodicity_mesh_boundary_conditions_z(mesh, z_neg, z_pos)231if isperiodic(mesh, 3) &&232(z_neg != BoundaryConditionPeriodic() ||233z_pos != BoundaryConditionPeriodic())234@error "For periodic mesh non-periodic boundary conditions in z-direction are supplied."235end236end237238function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{1},239StructuredMesh{1}},240boundary_conditions::Union{NamedTuple,241Tuple})242check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions[1],243boundary_conditions[2])244end245246function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{2},247StructuredMesh{2},248StructuredMeshView{2}},249boundary_conditions::Union{NamedTuple,250Tuple})251check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions[1],252boundary_conditions[2])253check_periodicity_mesh_boundary_conditions_y(mesh, boundary_conditions[3],254boundary_conditions[4])255end256257function check_periodicity_mesh_boundary_conditions(mesh::Union{TreeMesh{3},258StructuredMesh{3}},259boundary_conditions::Union{NamedTuple,260Tuple})261check_periodicity_mesh_boundary_conditions_x(mesh, boundary_conditions[1],262boundary_conditions[2])263check_periodicity_mesh_boundary_conditions_y(mesh, boundary_conditions[3],264boundary_conditions[4])265check_periodicity_mesh_boundary_conditions_z(mesh, boundary_conditions[5],266boundary_conditions[6])267end268269function Base.show(io::IO, semi::SemidiscretizationHyperbolic)270@nospecialize semi # reduce precompilation time271272print(io, "SemidiscretizationHyperbolic(")273print(io, semi.mesh)274print(io, ", ", semi.equations)275print(io, ", ", semi.initial_condition)276print(io, ", ", semi.boundary_conditions)277print(io, ", ", semi.source_terms)278print(io, ", ", semi.solver)279print(io, ", cache(")280for (idx, key) in enumerate(keys(semi.cache))281idx > 1 && print(io, " ")282print(io, key)283end284print(io, "))")285end286287function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationHyperbolic)288@nospecialize semi # reduce precompilation time289290if get(io, :compact, false)291show(io, semi)292else293summary_header(io, "SemidiscretizationHyperbolic")294summary_line(io, "#spatial dimensions", ndims(semi.equations))295summary_line(io, "mesh", semi.mesh)296summary_line(io, "equations", semi.equations |> typeof |> nameof)297summary_line(io, "initial condition", semi.initial_condition)298299print_boundary_conditions(io, semi)300301summary_line(io, "source terms", semi.source_terms)302summary_line(io, "solver", semi.solver |> typeof |> nameof)303summary_line(io, "total #DOFs per field", ndofsglobal(semi))304summary_footer(io)305end306end307308# type alias for dispatch in printing of boundary conditions309#! format: off310const SemiHypMeshBCSolver{Mesh, BoundaryConditions, Solver} =311SemidiscretizationHyperbolic{Mesh,312Equations,313InitialCondition,314BoundaryConditions,315SourceTerms,316Solver} where {Equations,317InitialCondition,318SourceTerms}319#! format: on320321# generic fallback: print the type of semi.boundary_condition.322function print_boundary_conditions(io, semi::SemiHypMeshBCSolver)323summary_line(io, "boundary conditions", typeof(semi.boundary_conditions))324end325326function print_boundary_conditions(io,327semi::SemiHypMeshBCSolver{<:Any,328<:UnstructuredSortedBoundaryTypes})329@unpack boundary_conditions = semi330@unpack boundary_dictionary = boundary_conditions331summary_line(io, "boundary conditions", length(boundary_dictionary))332for (boundary_name, boundary_condition) in boundary_dictionary333summary_line(increment_indent(io), boundary_name, typeof(boundary_condition))334end335end336337function print_boundary_conditions(io, semi::SemiHypMeshBCSolver{<:Any, <:NamedTuple})338@unpack boundary_conditions = semi339summary_line(io, "boundary conditions", length(boundary_conditions))340bc_names = keys(boundary_conditions)341for (i, bc_name) in enumerate(bc_names)342summary_line(increment_indent(io), String(bc_name),343typeof(boundary_conditions[i]))344end345end346347function print_boundary_conditions(io,348semi::SemiHypMeshBCSolver{<:Union{TreeMesh,349StructuredMesh},350<:Union{Tuple, NamedTuple,351AbstractArray}})352summary_line(io, "boundary conditions", 2 * ndims(semi))353bcs = semi.boundary_conditions354355summary_line(increment_indent(io), "negative x", bcs[1])356summary_line(increment_indent(io), "positive x", bcs[2])357if ndims(semi) > 1358summary_line(increment_indent(io), "negative y", bcs[3])359summary_line(increment_indent(io), "positive y", bcs[4])360end361if ndims(semi) > 2362summary_line(increment_indent(io), "negative z", bcs[5])363summary_line(increment_indent(io), "positive z", bcs[6])364end365end366367@inline Base.ndims(semi::SemidiscretizationHyperbolic) = ndims(semi.mesh)368369@inline nvariables(semi::SemidiscretizationHyperbolic) = nvariables(semi.equations)370371@inline Base.real(semi::SemidiscretizationHyperbolic) = real(semi.solver)372373@inline function mesh_equations_solver_cache(semi::SemidiscretizationHyperbolic)374@unpack mesh, equations, solver, cache = semi375return mesh, equations, solver, cache376end377378function calc_error_norms(func, u_ode, t, analyzer, semi::SemidiscretizationHyperbolic,379cache_analysis)380@unpack mesh, equations, initial_condition, solver, cache = semi381u = wrap_array(u_ode, mesh, equations, solver, cache)382383calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition, solver,384cache, cache_analysis)385end386387function compute_coefficients(t, semi::SemidiscretizationHyperbolic)388# Call `compute_coefficients` in `src/semidiscretization/semidiscretization.jl`389compute_coefficients(semi.initial_condition, t, semi)390end391392function compute_coefficients!(u_ode, t, semi::SemidiscretizationHyperbolic)393compute_coefficients!(u_ode, semi.initial_condition, t, semi)394end395396function rhs!(du_ode, u_ode, semi::SemidiscretizationHyperbolic, t)397@unpack mesh, equations, boundary_conditions, source_terms, solver, cache = semi398399u = wrap_array(u_ode, mesh, equations, solver, cache)400du = wrap_array(du_ode, mesh, equations, solver, cache)401402# TODO: Taal decide, do we need to pass the mesh?403time_start = time_ns()404@trixi_timeit timer() "rhs!" rhs!(du, u, t, mesh, equations,405boundary_conditions, source_terms, solver, cache)406runtime = time_ns() - time_start407put!(semi.performance_counter, runtime)408409return nothing410end411end # @muladd412413414