Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/time_integration/relaxation_methods/entropy_relaxation.jl
5591 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
@inline function integrate_w_dot_stage(stage, u_stage,
9
mesh::Union{TreeMesh{1}, StructuredMesh{1}},
10
equations, dg::Union{DGSEM, FDSBP}, cache)
11
@trixi_timeit timer() "Integrate w ⋅ k" begin
12
# Calculate ∫(∂S/∂u ⋅ k)dΩ = ∫(w ⋅ k)dΩ
13
integrate_via_indices(u_stage, mesh, equations, dg, cache,
14
stage) do u_stage, i, element, equations, dg, stage
15
w_node = cons2entropy(get_node_vars(u_stage, equations, dg,
16
i, element),
17
equations)
18
stage_node = get_node_vars(stage, equations, dg, i, element)
19
return dot(w_node, stage_node)
20
end
21
end
22
end
23
24
@inline function integrate_w_dot_stage(stage, u_stage,
25
mesh::Union{TreeMesh{2}, StructuredMesh{2},
26
UnstructuredMesh2D, P4estMesh{2},
27
T8codeMesh{2}},
28
equations, dg::Union{DGSEM, FDSBP}, cache)
29
@trixi_timeit timer() "Integrate w ⋅ k" begin
30
# Calculate ∫(∂S/∂u ⋅ k)dΩ = ∫(w ⋅ k)dΩ
31
integrate_via_indices(u_stage, mesh, equations, dg, cache,
32
stage) do u_stage, i, j, element, equations, dg, stage
33
w_node = cons2entropy(get_node_vars(u_stage, equations, dg,
34
i, j, element),
35
equations)
36
stage_node = get_node_vars(stage, equations, dg, i, j, element)
37
return dot(w_node, stage_node)
38
end
39
end
40
end
41
42
@inline function integrate_w_dot_stage(stage, u_stage,
43
mesh::Union{TreeMesh{3}, StructuredMesh{3},
44
P4estMesh{3}, T8codeMesh{3}},
45
equations, dg::Union{DGSEM, FDSBP}, cache)
46
@trixi_timeit timer() "Integrate w ⋅ k" begin
47
# Calculate ∫(∂S/∂u ⋅ k)dΩ = ∫(w ⋅ k)dΩ
48
integrate_via_indices(u_stage, mesh, equations, dg, cache,
49
stage) do u_stage, i, j, k, element, equations, dg, stage
50
w_node = cons2entropy(get_node_vars(u_stage, equations, dg,
51
i, j, k, element),
52
equations)
53
stage_node = get_node_vars(stage, equations, dg, i, j, k, element)
54
return dot(w_node, stage_node)
55
end
56
end
57
end
58
59
@inline function entropy_difference(gamma, S_old, dS, u_gamma_dir, mesh,
60
equations, dg::DG, cache)
61
return integrate(entropy, u_gamma_dir, mesh, equations, dg, cache) -
62
S_old - gamma * dS # `dS` is true entropy change computed from stages
63
end
64
65
@inline function add_direction!(u_tmp_wrap, u_wrap, dir_wrap, gamma,
66
dg::DG, cache)
67
@threaded for element in eachelement(dg, cache)
68
@views @. u_tmp_wrap[.., element] = u_wrap[.., element] +
69
gamma * dir_wrap[.., element]
70
end
71
end
72
73
"""
74
AbstractRelaxationSolver
75
76
Abstract type for relaxation solvers used to compute the relaxation parameter `` \\gamma``
77
in the entropy relaxation time integration methods
78
[`SubDiagonalRelaxationAlgorithm`](@ref) and [`vanderHouwenRelaxationAlgorithm`](@ref).
79
Implemented methods are [`RelaxationSolverBisection`](@ref) and [`RelaxationSolverNewton`](@ref).
80
"""
81
abstract type AbstractRelaxationSolver end
82
83
@doc raw"""
84
RelaxationSolverBisection(; max_iterations = 25,
85
root_tol = 1e-15, gamma_tol = 1e-13,
86
gamma_min = 0.1, gamma_max = 1.2)
87
88
Solve the relaxation equation
89
```math
90
H \big(\boldsymbol U_{n+1}(\gamma_n) \big) =
91
H \left( \boldsymbol U_n + \Delta t \gamma_n \sum_{i=1}^Sb_i \boldsymbol K_i \right) \overset{!}{=}
92
H(\boldsymbol U_n) + \gamma_n \Delta H (\boldsymbol U_n)
93
```
94
with true entropy change
95
```math
96
\Delta H \coloneqq
97
\Delta t \sum_{i=1}^S b_i
98
\left \langle \frac{\partial H(\boldsymbol U_{n,i})}{\partial \boldsymbol U_{n,i}},
99
\boldsymbol K_i
100
\right \rangle
101
```
102
for the relaxation parameter ``\gamma_n`` using a bisection method.
103
Supposed to be supplied to a relaxation Runge-Kutta method such as [`SubDiagonalAlgorithm`](@ref) or [`vanderHouwenRelaxationAlgorithm`](@ref).
104
105
# Arguments
106
- `max_iterations::Int`: Maximum number of bisection iterations.
107
- `root_tol::RealT`: Function-tolerance for the relaxation equation, i.e.,
108
the absolute defect of the left and right-hand side of the equation above, i.e.,
109
the solver stops if
110
``\left|H_{n+1} - \left(H_n + \gamma_n \Delta H( \boldsymbol U_n) \right) \right| \leq \text{root\_tol}``.
111
- `gamma_tol::RealT`: Absolute tolerance for the bracketing interval length, i.e., the bisection stops if
112
``|\gamma_{\text{max}} - \gamma_{\text{min}}| \leq \text{gamma\_tol}``.
113
- `gamma_min::RealT`: Lower bound of the initial bracketing interval.
114
- `gamma_max::RealT`: Upper bound of the initial bracketing interval.
115
"""
116
struct RelaxationSolverBisection{RealT <: Real} <: AbstractRelaxationSolver
117
# General parameters
118
max_iterations::Int # Maximum number of bisection iterations
119
root_tol::RealT # Function-tolerance for the relaxation equation
120
gamma_tol::RealT # Absolute tolerance for the bracketing interval length
121
# Method-specific parameters
122
gamma_min::RealT # Lower bound of the initial bracketing interval
123
gamma_max::RealT # Upper bound of the initial bracketing interval
124
end
125
126
function RelaxationSolverBisection(; max_iterations = 25,
127
root_tol = 1e-15, gamma_tol = 1e-13,
128
gamma_min = 0.1, gamma_max = 1.2)
129
return RelaxationSolverBisection(max_iterations, root_tol, gamma_tol,
130
gamma_min, gamma_max)
131
end
132
133
function Base.show(io::IO, relaxation_solver::RelaxationSolverBisection)
134
print(io, "RelaxationSolverBisection(max_iterations=",
135
relaxation_solver.max_iterations,
136
", root_tol=", relaxation_solver.root_tol,
137
", gamma_tol=", relaxation_solver.gamma_tol,
138
", gamma_min=", relaxation_solver.gamma_min,
139
", gamma_max=", relaxation_solver.gamma_max, ")")
140
return nothing
141
end
142
function Base.show(io::IO, ::MIME"text/plain",
143
relaxation_solver::RelaxationSolverBisection)
144
if get(io, :compact, false)
145
show(io, relaxation_solver)
146
else
147
setup = [
148
"max_iterations" => relaxation_solver.max_iterations,
149
"root_tol" => relaxation_solver.root_tol,
150
"gamma_tol" => relaxation_solver.gamma_tol,
151
"gamma_min" => relaxation_solver.gamma_min,
152
"gamma_max" => relaxation_solver.gamma_max
153
]
154
summary_box(io, "RelaxationSolverBisection", setup)
155
end
156
end
157
158
function relaxation_solver!(integrator, u_tmp_wrap, u_wrap, dir_wrap, dS,
159
mesh, equations, dg::DG, cache,
160
relaxation_solver::RelaxationSolverBisection)
161
@unpack max_iterations, root_tol, gamma_tol, gamma_min, gamma_max = relaxation_solver
162
163
add_direction!(u_tmp_wrap, u_wrap, dir_wrap, gamma_max, dg, cache)
164
@trixi_timeit timer() "ΔH" r_max=entropy_difference(gamma_max, integrator.S_old, dS,
165
u_tmp_wrap, mesh,
166
equations, dg, cache)
167
168
add_direction!(u_tmp_wrap, u_wrap, dir_wrap, gamma_min, dg, cache)
169
@trixi_timeit timer() "ΔH" r_min=entropy_difference(gamma_min, integrator.S_old, dS,
170
u_tmp_wrap, mesh,
171
equations, dg, cache)
172
173
entropy_residual = 0
174
# Check if there exists a root for `r` in the interval [gamma_min, gamma_max]
175
if r_max > 0 && r_min < 0
176
iterations = 0
177
while gamma_max - gamma_min > gamma_tol && iterations < max_iterations
178
integrator.gamma = (gamma_max + gamma_min) / 2
179
180
add_direction!(u_tmp_wrap, u_wrap, dir_wrap, integrator.gamma, dg, cache)
181
@trixi_timeit timer() "ΔH" entropy_residual=entropy_difference(integrator.gamma,
182
integrator.S_old,
183
dS,
184
u_tmp_wrap,
185
mesh,
186
equations,
187
dg, cache)
188
if abs(entropy_residual) <= root_tol # Sufficiently close at root
189
break
190
end
191
192
# Bisect interval
193
if entropy_residual < 0
194
gamma_min = integrator.gamma
195
else
196
gamma_max = integrator.gamma
197
end
198
iterations += 1
199
end
200
else # No proper bracketing interval found
201
integrator.gamma = 1
202
add_direction!(u_tmp_wrap, u_wrap, dir_wrap, integrator.gamma, dg, cache)
203
@trixi_timeit timer() "ΔH" entropy_residual=entropy_difference(integrator.gamma,
204
integrator.S_old,
205
dS, u_tmp_wrap,
206
mesh, equations,
207
dg, cache)
208
end
209
# Update old entropy
210
integrator.S_old += integrator.gamma * dS + entropy_residual
211
212
return nothing
213
end
214
215
@doc raw"""
216
RelaxationSolverNewton(; max_iterations = 5,
217
root_tol = 1e-15, gamma_tol = 1e-13,
218
gamma_min = 1e-13, step_scaling = 1.0)
219
220
Solve the relaxation equation
221
```math
222
H \big(\boldsymbol U_{n+1}(\gamma_n) \big) =
223
H \left( \boldsymbol U_n + \Delta t \gamma_n \sum_{i=1}^Sb_i \boldsymbol K_i \right) \overset{!}{=}
224
H(\boldsymbol U_n) + \gamma_n \Delta H (\boldsymbol U_n)
225
```
226
with true entropy change
227
```math
228
\Delta H \coloneqq
229
\Delta t \sum_{i=1}^S b_i
230
\left \langle \frac{\partial H(\boldsymbol U_{n,i})}{\partial \boldsymbol U_{n,i}},
231
\boldsymbol K_i
232
\right \rangle
233
```
234
for the relaxation parameter ``\gamma_n`` using Newton's method.
235
The derivative of the relaxation function is known and can be directly computed.
236
Supposed to be supplied to a relaxation Runge-Kutta method such as [`SubDiagonalAlgorithm`](@ref) or [`vanderHouwenRelaxationAlgorithm`](@ref).
237
238
# Arguments
239
- `max_iterations::Int`: Maximum number of Newton iterations.
240
- `root_tol::RealT`: Function-tolerance for the relaxation equation, i.e.,
241
the absolute defect of the left and right-hand side of the equation above, i.e.,
242
the solver stops if
243
``|H_{n+1} - (H_n + \gamma_n \Delta H( \boldsymbol U_n))| \leq \text{root\_tol}``.
244
- `gamma_tol::RealT`: Absolute tolerance for the Newton update step size, i.e., the solver stops if
245
``|\gamma_{\text{new}} - \gamma_{\text{old}}| \leq \text{gamma\_tol}``.
246
- `gamma_min::RealT`: Minimum relaxation parameter. If the Newton iteration results a value smaller than this,
247
the relaxation parameter is set to 1.
248
- `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.
249
"""
250
struct RelaxationSolverNewton{RealT <: Real} <: AbstractRelaxationSolver
251
# General parameters
252
max_iterations::Int # Maximum number of Newton iterations
253
root_tol::RealT # Function-tolerance for the relaxation equation
254
gamma_tol::RealT # Absolute tolerance for the Newton update step size
255
# Method-specific parameters
256
# Minimum relaxation parameter. If the Newton iteration computes a value smaller than this,
257
# the relaxation parameter is set to 1.
258
gamma_min::RealT
259
step_scaling::RealT # Scaling factor for the Newton step
260
end
261
function RelaxationSolverNewton(; max_iterations = 5,
262
root_tol = 1e-15, gamma_tol = 1e-13,
263
gamma_min = 1e-13, step_scaling = 1.0)
264
return RelaxationSolverNewton(max_iterations, root_tol, gamma_tol,
265
gamma_min, step_scaling)
266
end
267
268
function Base.show(io::IO,
269
relaxation_solver::RelaxationSolverNewton)
270
print(io, "RelaxationSolverNewton(max_iterations=",
271
relaxation_solver.max_iterations,
272
", root_tol=", relaxation_solver.root_tol,
273
", gamma_tol=", relaxation_solver.gamma_tol,
274
", gamma_min=", relaxation_solver.gamma_min,
275
", step_scaling=", relaxation_solver.step_scaling, ")")
276
return nothing
277
end
278
279
function Base.show(io::IO, ::MIME"text/plain",
280
relaxation_solver::RelaxationSolverNewton)
281
if get(io, :compact, false)
282
show(io, relaxation_solver)
283
else
284
setup = [
285
"max_iterations" => relaxation_solver.max_iterations,
286
"root_tol" => relaxation_solver.root_tol,
287
"gamma_tol" => relaxation_solver.gamma_tol,
288
"gamma_min" => relaxation_solver.gamma_min,
289
"step_scaling" => relaxation_solver.step_scaling
290
]
291
summary_box(io, "RelaxationSolverNewton", setup)
292
end
293
end
294
295
function relaxation_solver!(integrator,
296
u_tmp_wrap, u_wrap, dir_wrap, dS,
297
mesh, equations, dg::DG, cache,
298
relaxation_solver::RelaxationSolverNewton)
299
@unpack max_iterations, root_tol, gamma_tol, gamma_min, step_scaling = relaxation_solver
300
301
iterations = 0
302
entropy_residual = 0
303
while iterations < max_iterations
304
add_direction!(u_tmp_wrap, u_wrap, dir_wrap, integrator.gamma, dg, cache)
305
@trixi_timeit timer() "ΔH" entropy_residual=entropy_difference(integrator.gamma,
306
integrator.S_old,
307
dS, u_tmp_wrap,
308
mesh, equations,
309
dg, cache)
310
311
if abs(entropy_residual) <= root_tol # Sufficiently close at root
312
break
313
end
314
315
# Derivative of object relaxation function `r` with respect to `gamma`
316
dr = integrate_w_dot_stage(dir_wrap, u_tmp_wrap, mesh, equations, dg, cache) -
317
dS
318
319
step = step_scaling * entropy_residual / dr # Newton-Raphson update step
320
if abs(step) <= gamma_tol # Prevent unnecessary small steps
321
break
322
end
323
324
integrator.gamma -= step # Perform Newton-Raphson update
325
iterations += 1
326
end
327
328
# Catch Newton failures
329
if integrator.gamma < gamma_min || isnan(integrator.gamma) ||
330
isinf(integrator.gamma)
331
integrator.gamma = 1
332
entropy_residual = 0 # May be very large, avoid using this in `S_old`
333
end
334
# Update old entropy
335
integrator.S_old += integrator.gamma * dS + entropy_residual
336
337
return nothing
338
end
339
end # @muladd
340
341