Path: blob/main/src/semidiscretization/semidiscretization_parabolic.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"""8SemidiscretizationParabolic910A struct containing everything needed to describe a spatial semidiscretization of a purely11parabolic PDE.12"""13mutable struct SemidiscretizationParabolic{Mesh, Equations, InitialCondition,14BoundaryConditions, SourceTerms,15Solver, SolverParabolic,16Cache, CacheParabolic} <:17AbstractSemidiscretization18mesh::Mesh19equations::Equations2021const initial_condition::InitialCondition2223const boundary_conditions::BoundaryConditions24const source_terms::SourceTerms2526const solver::Solver27const solver_parabolic::SolverParabolic2829cache::Cache30cache_parabolic::CacheParabolic3132performance_counter::PerformanceCounter33end3435"""36SemidiscretizationParabolic(mesh, equations, initial_condition, solver;37solver_parabolic=default_parabolic_solver(),38source_terms=nothing,39boundary_conditions,40RealT=real(solver),41uEltype=RealT)4243Construct a semidiscretization of a purely parabolic PDE.4445Boundary conditions must be provided explicitly either as a `NamedTuple` or as a46single boundary condition that gets applied to all boundaries.47"""48function SemidiscretizationParabolic(mesh, equations::AbstractEquationsParabolic,49initial_condition, solver;50solver_parabolic = default_parabolic_solver(),51source_terms = nothing,52boundary_conditions,53# `RealT` is used as real type for node locations etc.54# while `uEltype` is used as element type of solutions etc.55RealT = real(solver), uEltype = RealT)56@assert ndims(mesh) == ndims(equations)5758cache = create_cache(mesh, equations, solver, RealT, uEltype)59_boundary_conditions = digest_boundary_conditions(boundary_conditions, mesh, solver,60cache)61check_periodicity_mesh_boundary_conditions(mesh, _boundary_conditions)6263cache_parabolic = create_cache_parabolic(mesh, equations, solver,64nelements(solver, cache), uEltype)6566performance_counter = PerformanceCounter()6768return SemidiscretizationParabolic{typeof(mesh), typeof(equations),69typeof(initial_condition),70typeof(_boundary_conditions),71typeof(source_terms),72typeof(solver),73typeof(solver_parabolic),74typeof(cache),75typeof(cache_parabolic)}(mesh, equations,76initial_condition,77_boundary_conditions,78source_terms,79solver,80solver_parabolic,81cache,82cache_parabolic,83performance_counter)84end8586# @eval due to @muladd87@eval Adapt.@adapt_structure(SemidiscretizationParabolic)8889# Create a new semidiscretization but change some parameters compared to the input.90function remake(semi::SemidiscretizationParabolic; uEltype = real(semi.solver),91mesh = semi.mesh,92equations = semi.equations,93initial_condition = semi.initial_condition,94solver = semi.solver,95solver_parabolic = semi.solver_parabolic,96source_terms = semi.source_terms,97boundary_conditions = semi.boundary_conditions)98return SemidiscretizationParabolic(mesh, equations, initial_condition, solver;99solver_parabolic = solver_parabolic,100source_terms = source_terms,101boundary_conditions = boundary_conditions,102uEltype = uEltype)103end104105function Base.show(io::IO, semi::SemidiscretizationParabolic)106@nospecialize semi # reduce precompilation time107108print(io, "SemidiscretizationParabolic(")109print(io, semi.mesh)110print(io, ", ", semi.equations)111print(io, ", ", semi.initial_condition)112print(io, ", ", semi.boundary_conditions)113print(io, ", ", semi.source_terms)114print(io, ", ", semi.solver)115print(io, ", ", semi.solver_parabolic)116print(io, ", cache(")117for (idx, key) in enumerate(keys(semi.cache))118idx > 1 && print(io, " ")119print(io, key)120end121print(io, "))")122return nothing123end124125function Base.show(io::IO, ::MIME"text/plain", semi::SemidiscretizationParabolic)126@nospecialize semi # reduce precompilation time127128if get(io, :compact, false)129show(io, semi)130else131summary_header(io, "SemidiscretizationParabolic")132summary_line(io, "#spatial dimensions", ndims(semi.equations))133summary_line(io, "mesh", semi.mesh)134summary_line(io, "equations", semi.equations |> typeof |> nameof)135summary_line(io, "initial condition", semi.initial_condition)136summary_line(io, "source terms", semi.source_terms)137summary_line(io, "solver", semi.solver |> typeof |> nameof)138summary_line(io, "parabolic solver", semi.solver_parabolic |> typeof |> nameof)139summary_line(io, "total #DOFs per field", ndofsglobal(semi))140summary_footer(io)141end142end143144@inline Base.ndims(semi::SemidiscretizationParabolic) = ndims(semi.mesh)145146@inline function nvariables(semi::SemidiscretizationParabolic)147return nvariables(semi.equations)148end149150@inline Base.real(semi::SemidiscretizationParabolic) = real(semi.solver)151152@inline function mesh_equations_solver_cache(semi::SemidiscretizationParabolic)153@unpack mesh, equations, solver, cache = semi154return mesh, equations, solver, cache155end156157function calc_error_norms(func, u_ode, t, analyzer,158semi::SemidiscretizationParabolic, cache_analysis)159@unpack mesh, equations, initial_condition, solver, cache = semi160u = wrap_array(u_ode, mesh, equations, solver, cache)161162return calc_error_norms(func, u, t, analyzer, mesh, equations, initial_condition,163solver, cache, cache_analysis)164end165166function compute_coefficients(t, semi::SemidiscretizationParabolic)167return compute_coefficients(semi.initial_condition, t, semi)168end169170function compute_coefficients!(u_ode, t, semi::SemidiscretizationParabolic)171return compute_coefficients!(u_ode, semi.initial_condition, t, semi)172end173174# Required for storing `extra_node_variables` in the `SaveSolutionCallback`.175# Not to be confused with `get_node_vars` which returns the variables of the simulated equation.176function get_node_variables!(node_variables, u_ode,177semi::SemidiscretizationParabolic)178return get_node_variables!(node_variables, u_ode,179mesh_equations_solver_cache(semi)...,180semi.cache_parabolic)181end182183"""184linear_structure(semi::SemidiscretizationParabolic;185t0 = zero(real(semi)))186187Wraps the right-hand side operator of the parabolic semidiscretization `semi`188at time `t0` as an affine-linear operator given by a linear operator `A`189and a vector `b`:190```math191\\partial_t u(t) = A u(t) - b.192```193Works only for equations for which `have_constant_diffusivity(equations) == True()`.194As in [`linear_structure(semi::AbstractSemidiscretization)`](@ref), returns `A` and `b`,195with `A` represented as a matrix-free `LinearMap`.196"""197function linear_structure(semi::SemidiscretizationParabolic;198t0 = zero(real(semi)))199if have_constant_diffusivity(semi.equations) == False()200throw(ArgumentError("`linear_structure` expects equations with constant diffusive terms."))201end202203apply_rhs! = function (dest, src)204return rhs_parabolic!(dest, src, semi, t0)205end206207return _linear_structure_from_rhs(semi, apply_rhs!)208end209210# For a purely parabolic semidiscretization, the right-hand side is `rhs_parabolic!`211# instead of the default `rhs!`.212@inline default_rhs(::SemidiscretizationParabolic) = rhs_parabolic!213214function rhs_parabolic!(du_ode, u_ode, semi::SemidiscretizationParabolic, t)215@unpack mesh, equations, boundary_conditions, source_terms, solver, solver_parabolic, cache, cache_parabolic = semi216217u = wrap_array(u_ode, mesh, equations, solver, cache)218du = wrap_array(du_ode, mesh, equations, solver, cache)219backend = trixi_backend(u)220221time_start = time_ns()222@trixi_timeit_ext backend timer() "parabolic rhs!" rhs_parabolic!(du, u, t, mesh,223equations,224boundary_conditions,225source_terms,226solver,227solver_parabolic,228cache,229cache_parabolic)230runtime = time_ns() - time_start231put!(semi.performance_counter, runtime)232233return nothing234end235end # @muladd236237238