Path: blob/main/src/time_integration/relaxation_methods/entropy_relaxation.jl
5591 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@inline function integrate_w_dot_stage(stage, u_stage,8mesh::Union{TreeMesh{1}, StructuredMesh{1}},9equations, dg::Union{DGSEM, FDSBP}, cache)10@trixi_timeit timer() "Integrate w ⋅ k" begin11# Calculate ∫(∂S/∂u ⋅ k)dΩ = ∫(w ⋅ k)dΩ12integrate_via_indices(u_stage, mesh, equations, dg, cache,13stage) do u_stage, i, element, equations, dg, stage14w_node = cons2entropy(get_node_vars(u_stage, equations, dg,15i, element),16equations)17stage_node = get_node_vars(stage, equations, dg, i, element)18return dot(w_node, stage_node)19end20end21end2223@inline function integrate_w_dot_stage(stage, u_stage,24mesh::Union{TreeMesh{2}, StructuredMesh{2},25UnstructuredMesh2D, P4estMesh{2},26T8codeMesh{2}},27equations, dg::Union{DGSEM, FDSBP}, cache)28@trixi_timeit timer() "Integrate w ⋅ k" begin29# Calculate ∫(∂S/∂u ⋅ k)dΩ = ∫(w ⋅ k)dΩ30integrate_via_indices(u_stage, mesh, equations, dg, cache,31stage) do u_stage, i, j, element, equations, dg, stage32w_node = cons2entropy(get_node_vars(u_stage, equations, dg,33i, j, element),34equations)35stage_node = get_node_vars(stage, equations, dg, i, j, element)36return dot(w_node, stage_node)37end38end39end4041@inline function integrate_w_dot_stage(stage, u_stage,42mesh::Union{TreeMesh{3}, StructuredMesh{3},43P4estMesh{3}, T8codeMesh{3}},44equations, dg::Union{DGSEM, FDSBP}, cache)45@trixi_timeit timer() "Integrate w ⋅ k" begin46# Calculate ∫(∂S/∂u ⋅ k)dΩ = ∫(w ⋅ k)dΩ47integrate_via_indices(u_stage, mesh, equations, dg, cache,48stage) do u_stage, i, j, k, element, equations, dg, stage49w_node = cons2entropy(get_node_vars(u_stage, equations, dg,50i, j, k, element),51equations)52stage_node = get_node_vars(stage, equations, dg, i, j, k, element)53return dot(w_node, stage_node)54end55end56end5758@inline function entropy_difference(gamma, S_old, dS, u_gamma_dir, mesh,59equations, dg::DG, cache)60return integrate(entropy, u_gamma_dir, mesh, equations, dg, cache) -61S_old - gamma * dS # `dS` is true entropy change computed from stages62end6364@inline function add_direction!(u_tmp_wrap, u_wrap, dir_wrap, gamma,65dg::DG, cache)66@threaded for element in eachelement(dg, cache)67@views @. u_tmp_wrap[.., element] = u_wrap[.., element] +68gamma * dir_wrap[.., element]69end70end7172"""73AbstractRelaxationSolver7475Abstract type for relaxation solvers used to compute the relaxation parameter `` \\gamma``76in the entropy relaxation time integration methods77[`SubDiagonalRelaxationAlgorithm`](@ref) and [`vanderHouwenRelaxationAlgorithm`](@ref).78Implemented methods are [`RelaxationSolverBisection`](@ref) and [`RelaxationSolverNewton`](@ref).79"""80abstract type AbstractRelaxationSolver end8182@doc raw"""83RelaxationSolverBisection(; max_iterations = 25,84root_tol = 1e-15, gamma_tol = 1e-13,85gamma_min = 0.1, gamma_max = 1.2)8687Solve the relaxation equation88```math89H \big(\boldsymbol U_{n+1}(\gamma_n) \big) =90H \left( \boldsymbol U_n + \Delta t \gamma_n \sum_{i=1}^Sb_i \boldsymbol K_i \right) \overset{!}{=}91H(\boldsymbol U_n) + \gamma_n \Delta H (\boldsymbol U_n)92```93with true entropy change94```math95\Delta H \coloneqq96\Delta t \sum_{i=1}^S b_i97\left \langle \frac{\partial H(\boldsymbol U_{n,i})}{\partial \boldsymbol U_{n,i}},98\boldsymbol K_i99\right \rangle100```101for the relaxation parameter ``\gamma_n`` using a bisection method.102Supposed to be supplied to a relaxation Runge-Kutta method such as [`SubDiagonalAlgorithm`](@ref) or [`vanderHouwenRelaxationAlgorithm`](@ref).103104# Arguments105- `max_iterations::Int`: Maximum number of bisection iterations.106- `root_tol::RealT`: Function-tolerance for the relaxation equation, i.e.,107the absolute defect of the left and right-hand side of the equation above, i.e.,108the solver stops if109``\left|H_{n+1} - \left(H_n + \gamma_n \Delta H( \boldsymbol U_n) \right) \right| \leq \text{root\_tol}``.110- `gamma_tol::RealT`: Absolute tolerance for the bracketing interval length, i.e., the bisection stops if111``|\gamma_{\text{max}} - \gamma_{\text{min}}| \leq \text{gamma\_tol}``.112- `gamma_min::RealT`: Lower bound of the initial bracketing interval.113- `gamma_max::RealT`: Upper bound of the initial bracketing interval.114"""115struct RelaxationSolverBisection{RealT <: Real} <: AbstractRelaxationSolver116# General parameters117max_iterations::Int # Maximum number of bisection iterations118root_tol::RealT # Function-tolerance for the relaxation equation119gamma_tol::RealT # Absolute tolerance for the bracketing interval length120# Method-specific parameters121gamma_min::RealT # Lower bound of the initial bracketing interval122gamma_max::RealT # Upper bound of the initial bracketing interval123end124125function RelaxationSolverBisection(; max_iterations = 25,126root_tol = 1e-15, gamma_tol = 1e-13,127gamma_min = 0.1, gamma_max = 1.2)128return RelaxationSolverBisection(max_iterations, root_tol, gamma_tol,129gamma_min, gamma_max)130end131132function Base.show(io::IO, relaxation_solver::RelaxationSolverBisection)133print(io, "RelaxationSolverBisection(max_iterations=",134relaxation_solver.max_iterations,135", root_tol=", relaxation_solver.root_tol,136", gamma_tol=", relaxation_solver.gamma_tol,137", gamma_min=", relaxation_solver.gamma_min,138", gamma_max=", relaxation_solver.gamma_max, ")")139return nothing140end141function Base.show(io::IO, ::MIME"text/plain",142relaxation_solver::RelaxationSolverBisection)143if get(io, :compact, false)144show(io, relaxation_solver)145else146setup = [147"max_iterations" => relaxation_solver.max_iterations,148"root_tol" => relaxation_solver.root_tol,149"gamma_tol" => relaxation_solver.gamma_tol,150"gamma_min" => relaxation_solver.gamma_min,151"gamma_max" => relaxation_solver.gamma_max152]153summary_box(io, "RelaxationSolverBisection", setup)154end155end156157function relaxation_solver!(integrator, u_tmp_wrap, u_wrap, dir_wrap, dS,158mesh, equations, dg::DG, cache,159relaxation_solver::RelaxationSolverBisection)160@unpack max_iterations, root_tol, gamma_tol, gamma_min, gamma_max = relaxation_solver161162add_direction!(u_tmp_wrap, u_wrap, dir_wrap, gamma_max, dg, cache)163@trixi_timeit timer() "ΔH" r_max=entropy_difference(gamma_max, integrator.S_old, dS,164u_tmp_wrap, mesh,165equations, dg, cache)166167add_direction!(u_tmp_wrap, u_wrap, dir_wrap, gamma_min, dg, cache)168@trixi_timeit timer() "ΔH" r_min=entropy_difference(gamma_min, integrator.S_old, dS,169u_tmp_wrap, mesh,170equations, dg, cache)171172entropy_residual = 0173# Check if there exists a root for `r` in the interval [gamma_min, gamma_max]174if r_max > 0 && r_min < 0175iterations = 0176while gamma_max - gamma_min > gamma_tol && iterations < max_iterations177integrator.gamma = (gamma_max + gamma_min) / 2178179add_direction!(u_tmp_wrap, u_wrap, dir_wrap, integrator.gamma, dg, cache)180@trixi_timeit timer() "ΔH" entropy_residual=entropy_difference(integrator.gamma,181integrator.S_old,182dS,183u_tmp_wrap,184mesh,185equations,186dg, cache)187if abs(entropy_residual) <= root_tol # Sufficiently close at root188break189end190191# Bisect interval192if entropy_residual < 0193gamma_min = integrator.gamma194else195gamma_max = integrator.gamma196end197iterations += 1198end199else # No proper bracketing interval found200integrator.gamma = 1201add_direction!(u_tmp_wrap, u_wrap, dir_wrap, integrator.gamma, dg, cache)202@trixi_timeit timer() "ΔH" entropy_residual=entropy_difference(integrator.gamma,203integrator.S_old,204dS, u_tmp_wrap,205mesh, equations,206dg, cache)207end208# Update old entropy209integrator.S_old += integrator.gamma * dS + entropy_residual210211return nothing212end213214@doc raw"""215RelaxationSolverNewton(; max_iterations = 5,216root_tol = 1e-15, gamma_tol = 1e-13,217gamma_min = 1e-13, step_scaling = 1.0)218219Solve the relaxation equation220```math221H \big(\boldsymbol U_{n+1}(\gamma_n) \big) =222H \left( \boldsymbol U_n + \Delta t \gamma_n \sum_{i=1}^Sb_i \boldsymbol K_i \right) \overset{!}{=}223H(\boldsymbol U_n) + \gamma_n \Delta H (\boldsymbol U_n)224```225with true entropy change226```math227\Delta H \coloneqq228\Delta t \sum_{i=1}^S b_i229\left \langle \frac{\partial H(\boldsymbol U_{n,i})}{\partial \boldsymbol U_{n,i}},230\boldsymbol K_i231\right \rangle232```233for the relaxation parameter ``\gamma_n`` using Newton's method.234The derivative of the relaxation function is known and can be directly computed.235Supposed to be supplied to a relaxation Runge-Kutta method such as [`SubDiagonalAlgorithm`](@ref) or [`vanderHouwenRelaxationAlgorithm`](@ref).236237# Arguments238- `max_iterations::Int`: Maximum number of Newton iterations.239- `root_tol::RealT`: Function-tolerance for the relaxation equation, i.e.,240the absolute defect of the left and right-hand side of the equation above, i.e.,241the solver stops if242``|H_{n+1} - (H_n + \gamma_n \Delta H( \boldsymbol U_n))| \leq \text{root\_tol}``.243- `gamma_tol::RealT`: Absolute tolerance for the Newton update step size, i.e., the solver stops if244``|\gamma_{\text{new}} - \gamma_{\text{old}}| \leq \text{gamma\_tol}``.245- `gamma_min::RealT`: Minimum relaxation parameter. If the Newton iteration results a value smaller than this,246the relaxation parameter is set to 1.247- `step_scaling::RealT`: Scaling factor for the Newton step. For `step_scaling > 1` the Newton procedure is accelerated, while for `step_scaling < 1` it is damped.248"""249struct RelaxationSolverNewton{RealT <: Real} <: AbstractRelaxationSolver250# General parameters251max_iterations::Int # Maximum number of Newton iterations252root_tol::RealT # Function-tolerance for the relaxation equation253gamma_tol::RealT # Absolute tolerance for the Newton update step size254# Method-specific parameters255# Minimum relaxation parameter. If the Newton iteration computes a value smaller than this,256# the relaxation parameter is set to 1.257gamma_min::RealT258step_scaling::RealT # Scaling factor for the Newton step259end260function RelaxationSolverNewton(; max_iterations = 5,261root_tol = 1e-15, gamma_tol = 1e-13,262gamma_min = 1e-13, step_scaling = 1.0)263return RelaxationSolverNewton(max_iterations, root_tol, gamma_tol,264gamma_min, step_scaling)265end266267function Base.show(io::IO,268relaxation_solver::RelaxationSolverNewton)269print(io, "RelaxationSolverNewton(max_iterations=",270relaxation_solver.max_iterations,271", root_tol=", relaxation_solver.root_tol,272", gamma_tol=", relaxation_solver.gamma_tol,273", gamma_min=", relaxation_solver.gamma_min,274", step_scaling=", relaxation_solver.step_scaling, ")")275return nothing276end277278function Base.show(io::IO, ::MIME"text/plain",279relaxation_solver::RelaxationSolverNewton)280if get(io, :compact, false)281show(io, relaxation_solver)282else283setup = [284"max_iterations" => relaxation_solver.max_iterations,285"root_tol" => relaxation_solver.root_tol,286"gamma_tol" => relaxation_solver.gamma_tol,287"gamma_min" => relaxation_solver.gamma_min,288"step_scaling" => relaxation_solver.step_scaling289]290summary_box(io, "RelaxationSolverNewton", setup)291end292end293294function relaxation_solver!(integrator,295u_tmp_wrap, u_wrap, dir_wrap, dS,296mesh, equations, dg::DG, cache,297relaxation_solver::RelaxationSolverNewton)298@unpack max_iterations, root_tol, gamma_tol, gamma_min, step_scaling = relaxation_solver299300iterations = 0301entropy_residual = 0302while iterations < max_iterations303add_direction!(u_tmp_wrap, u_wrap, dir_wrap, integrator.gamma, dg, cache)304@trixi_timeit timer() "ΔH" entropy_residual=entropy_difference(integrator.gamma,305integrator.S_old,306dS, u_tmp_wrap,307mesh, equations,308dg, cache)309310if abs(entropy_residual) <= root_tol # Sufficiently close at root311break312end313314# Derivative of object relaxation function `r` with respect to `gamma`315dr = integrate_w_dot_stage(dir_wrap, u_tmp_wrap, mesh, equations, dg, cache) -316dS317318step = step_scaling * entropy_residual / dr # Newton-Raphson update step319if abs(step) <= gamma_tol # Prevent unnecessary small steps320break321end322323integrator.gamma -= step # Perform Newton-Raphson update324iterations += 1325end326327# Catch Newton failures328if integrator.gamma < gamma_min || isnan(integrator.gamma) ||329isinf(integrator.gamma)330integrator.gamma = 1331entropy_residual = 0 # May be very large, avoid using this in `S_old`332end333# Update old entropy334integrator.S_old += integrator.gamma * dS + entropy_residual335336return nothing337end338end # @muladd339340341