# Package extension for adding NLsolve-based features to Trixi.jl1module TrixiNLsolveExt23# Required for finding coefficients in Butcher tableau in the third order of4# PERK scheme integrators5using NLsolve: nlsolve67# We use a random initialization of the nonlinear solver.8# To make the tests reproducible, we need to seed the RNG9using StableRNGs: StableRNG, rand1011# Use functions that are to be extended and additional symbols that are not exported12using Trixi: Trixi, @muladd1314# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).15# Since these FMAs can increase the performance of many numerical algorithms,16# we need to opt-in explicitly.17# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.18@muladd begin19#! format: noindent2021# Compute residuals for nonlinear equations to match a stability polynomial with given coefficients,22# in order to find A-matrix in the Butcher-Tableau23function PairedExplicitRK3_butcher_tableau_objective_function!(c_eq, a_unknown,24num_stages,25num_stage_evals,26monomial_coeffs, c)27c_ts = c # ts = timestep28# For explicit methods, a_{1,1} = 0 and a_{2,1} = c_2 (Butcher's condition)29a_coeff = [0, c_ts[2], a_unknown...]30# Equality constraint array that ensures that the stability polynomial computed from31# the to-be-constructed Butcher-Tableau matches the monomial coefficients of the32# optimized stability polynomial.33# For details, see Chapter 4.3, Proposition 3.2, Equation (3.3) from34# Hairer, Wanner: Solving Ordinary Differential Equations 235# DOI: 10.1007/978-3-662-09947-63637# Lower-order terms: Two summands present38for i in 1:(num_stage_evals - 4)39term1 = a_coeff[num_stage_evals - 1]40term2 = a_coeff[num_stage_evals]41for j in 1:i42term1 *= a_coeff[num_stage_evals - 1 - j]43term2 *= a_coeff[num_stage_evals - j]44end45term1 *= c_ts[num_stages - 2 - i] * 1 / 6 # 1 / 6 = b_{S-1}46term2 *= c_ts[num_stages - 1 - i] * 2 / 3 # 2 / 3 = b_S4748c_eq[i] = monomial_coeffs[i] - (term1 + term2)49end5051# Highest coefficient: Only one term present52i = num_stage_evals - 353term2 = a_coeff[num_stage_evals]54for j in 1:i55term2 *= a_coeff[num_stage_evals - j]56end57term2 *= c_ts[num_stages - 1 - i] * 2 / 3 # 2 / 3 = b_S5859c_eq[i] = monomial_coeffs[i] - term260# Third-order consistency condition (Cf. eq. (27) from https://doi.org/10.1016/j.jcp.2022.11147061c_eq[num_stage_evals - 2] = 1 - 4 * a_coeff[num_stage_evals] -62a_coeff[num_stage_evals - 1]63end6465# Find the values of the a_{i, i-1} in the Butcher tableau matrix A by solving a system of66# non-linear equations that arise from the relation of the stability polynomial to the Butcher tableau.67# For details, see Proposition 3.2, Equation (3.3) from68# Hairer, Wanner: Solving Ordinary Differential Equations 269function Trixi.solve_a_butcher_coeffs_unknown!(a_unknown, num_stages, monomial_coeffs,70c; verbose, max_iter = 100000)7172# Define the objective_function73function objective_function!(c_eq, x)74return PairedExplicitRK3_butcher_tableau_objective_function!(c_eq, x,75num_stages,76num_stages,77monomial_coeffs,78c)79end8081# RealT is determined as the type of the first element in monomial_coeffs to ensure type consistency82RealT = typeof(monomial_coeffs[1])8384# To ensure consistency and reproducibility of results across runs, we use85# a seeded random initial guess.86rng = StableRNG(555)8788for _ in 1:max_iter89# Due to the nature of the nonlinear solver, different initial guesses can lead to90# small numerical differences in the solution.9192x0 = convert(RealT, 0.1) .* rand(rng, RealT, num_stages - 2)9394sol = nlsolve(objective_function!, x0, method = :trust_region,95ftol = 4.0e-16, # Enforce objective up to machine precision96iterations = 10^4, xtol = 1.0e-13, autodiff = :forward)9798a_unknown = sol.zero # Retrieve solution (root = zero)99100# Check if the values a[i, i-1] >= 0.0 (which stem from the nonlinear solver)101# and also c[i] - a[i, i-1] >= 0.0 since all coefficients should be non-negative102# to avoid downwinding of numerical fluxes.103is_sol_valid = all(x -> !isnan(x) && x >= 0, a_unknown) &&104all(!isnan(c[i] - a_unknown[i - 2]) &&105c[i] - a_unknown[i - 2] >= 0 for i in eachindex(c) if i > 2)106107if is_sol_valid108return a_unknown109else110if verbose111println("Solution invalid. Restart the process of solving non-linear system of equations again.")112end113end114end115116error("Maximum number of iterations ($max_iter) reached. Cannot find valid sets of coefficients.")117end118end # @muladd119120end # module TrixiNLsolveExt121122123