Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/ext/TrixiNLsolveExt.jl
2055 views
1
# Package extension for adding NLsolve-based features to Trixi.jl
2
module TrixiNLsolveExt
3
4
# Required for finding coefficients in Butcher tableau in the third order of
5
# PERK scheme integrators
6
using NLsolve: nlsolve
7
8
# We use a random initialization of the nonlinear solver.
9
# To make the tests reproducible, we need to seed the RNG
10
using StableRNGs: StableRNG, rand
11
12
# Use functions that are to be extended and additional symbols that are not exported
13
using Trixi: Trixi, @muladd
14
15
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
16
# Since these FMAs can increase the performance of many numerical algorithms,
17
# we need to opt-in explicitly.
18
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
19
@muladd begin
20
#! format: noindent
21
22
# Compute residuals for nonlinear equations to match a stability polynomial with given coefficients,
23
# in order to find A-matrix in the Butcher-Tableau
24
function PairedExplicitRK3_butcher_tableau_objective_function!(c_eq, a_unknown,
25
num_stages,
26
num_stage_evals,
27
monomial_coeffs, c)
28
c_ts = c # ts = timestep
29
# For explicit methods, a_{1,1} = 0 and a_{2,1} = c_2 (Butcher's condition)
30
a_coeff = [0, c_ts[2], a_unknown...]
31
# Equality constraint array that ensures that the stability polynomial computed from
32
# the to-be-constructed Butcher-Tableau matches the monomial coefficients of the
33
# optimized stability polynomial.
34
# For details, see Chapter 4.3, Proposition 3.2, Equation (3.3) from
35
# Hairer, Wanner: Solving Ordinary Differential Equations 2
36
# DOI: 10.1007/978-3-662-09947-6
37
38
# Lower-order terms: Two summands present
39
for i in 1:(num_stage_evals - 4)
40
term1 = a_coeff[num_stage_evals - 1]
41
term2 = a_coeff[num_stage_evals]
42
for j in 1:i
43
term1 *= a_coeff[num_stage_evals - 1 - j]
44
term2 *= a_coeff[num_stage_evals - j]
45
end
46
term1 *= c_ts[num_stages - 2 - i] * 1 / 6 # 1 / 6 = b_{S-1}
47
term2 *= c_ts[num_stages - 1 - i] * 2 / 3 # 2 / 3 = b_S
48
49
c_eq[i] = monomial_coeffs[i] - (term1 + term2)
50
end
51
52
# Highest coefficient: Only one term present
53
i = num_stage_evals - 3
54
term2 = a_coeff[num_stage_evals]
55
for j in 1:i
56
term2 *= a_coeff[num_stage_evals - j]
57
end
58
term2 *= c_ts[num_stages - 1 - i] * 2 / 3 # 2 / 3 = b_S
59
60
c_eq[i] = monomial_coeffs[i] - term2
61
# Third-order consistency condition (Cf. eq. (27) from https://doi.org/10.1016/j.jcp.2022.111470
62
c_eq[num_stage_evals - 2] = 1 - 4 * a_coeff[num_stage_evals] -
63
a_coeff[num_stage_evals - 1]
64
end
65
66
# Find the values of the a_{i, i-1} in the Butcher tableau matrix A by solving a system of
67
# non-linear equations that arise from the relation of the stability polynomial to the Butcher tableau.
68
# For details, see Proposition 3.2, Equation (3.3) from
69
# Hairer, Wanner: Solving Ordinary Differential Equations 2
70
function Trixi.solve_a_butcher_coeffs_unknown!(a_unknown, num_stages, monomial_coeffs,
71
c; verbose, max_iter = 100000)
72
73
# Define the objective_function
74
function objective_function!(c_eq, x)
75
return PairedExplicitRK3_butcher_tableau_objective_function!(c_eq, x,
76
num_stages,
77
num_stages,
78
monomial_coeffs,
79
c)
80
end
81
82
# RealT is determined as the type of the first element in monomial_coeffs to ensure type consistency
83
RealT = typeof(monomial_coeffs[1])
84
85
# To ensure consistency and reproducibility of results across runs, we use
86
# a seeded random initial guess.
87
rng = StableRNG(555)
88
89
for _ in 1:max_iter
90
# Due to the nature of the nonlinear solver, different initial guesses can lead to
91
# small numerical differences in the solution.
92
93
x0 = convert(RealT, 0.1) .* rand(rng, RealT, num_stages - 2)
94
95
sol = nlsolve(objective_function!, x0, method = :trust_region,
96
ftol = 4.0e-16, # Enforce objective up to machine precision
97
iterations = 10^4, xtol = 1.0e-13, autodiff = :forward)
98
99
a_unknown = sol.zero # Retrieve solution (root = zero)
100
101
# Check if the values a[i, i-1] >= 0.0 (which stem from the nonlinear solver)
102
# and also c[i] - a[i, i-1] >= 0.0 since all coefficients should be non-negative
103
# to avoid downwinding of numerical fluxes.
104
is_sol_valid = all(x -> !isnan(x) && x >= 0, a_unknown) &&
105
all(!isnan(c[i] - a_unknown[i - 2]) &&
106
c[i] - a_unknown[i - 2] >= 0 for i in eachindex(c) if i > 2)
107
108
if is_sol_valid
109
return a_unknown
110
else
111
if verbose
112
println("Solution invalid. Restart the process of solving non-linear system of equations again.")
113
end
114
end
115
end
116
117
error("Maximum number of iterations ($max_iter) reached. Cannot find valid sets of coefficients.")
118
end
119
end # @muladd
120
121
end # module TrixiNLsolveExt
122
123