module TrixiConvexECOSExt
using Convex: MOI, solve!, Variable, minimize, evaluate
using ECOS: Optimizer
using LinearAlgebra: eigvals
using Trixi: Trixi, bisect_stability_polynomial, @muladd
@muladd begin
function undo_normalization!(gamma_opt, num_stage_evals,
num_reduced_coeffs, fac_offset)
for k in 1:(num_stage_evals - num_reduced_coeffs)
gamma_opt[k] /= factorial(k + fac_offset)
end
end
@inline function polynomial_exponential_terms!(pnoms, normalized_powered_eigvals_scaled,
consistency_order)
pnoms .= 1
for k in 1:consistency_order
pnoms .+= view(normalized_powered_eigvals_scaled, :, k)
end
return nothing
end
function stability_polynomials!(pnoms, num_stage_evals,
normalized_powered_eigvals_scaled,
gamma, consistency_order,
num_reduced_unknown; kwargs...)
polynomial_exponential_terms!(pnoms, normalized_powered_eigvals_scaled,
consistency_order)
if consistency_order == 4
cS3 = kwargs[:cS3]
k1 = 0.001055026310046423 / cS3
k2 = 0.03726406530405851 / cS3
pnoms += k1 * view(normalized_powered_eigvals_scaled, :, 5) * factorial(5)
for k in 1:(num_stage_evals - 5)
pnoms += (k2 * view(normalized_powered_eigvals_scaled, :, k + 4) *
gamma[k] +
k1 * view(normalized_powered_eigvals_scaled, :, k + 5) *
gamma[k] * (k + 5))
end
else
for k in (consistency_order + 1):num_stage_evals
pnoms += gamma[k - consistency_order] *
view(normalized_powered_eigvals_scaled, :, k)
end
end
if num_stage_evals - num_reduced_unknown == 0
return maximum(abs.(pnoms))
else
return maximum(abs(pnoms))
end
end
@inline function normalize_power_eigvals!(normalized_powered_eigvals,
eig_vals,
num_stage_evals)
for j in 1:num_stage_evals
@views normalized_powered_eigvals[:, j] = eig_vals[:] .^ j ./ factorial(j)
end
end
function Trixi.bisect_stability_polynomial(consistency_order, num_eig_vals,
num_stage_evals,
dtmax, dteps, eig_vals;
kwargs...)
dtmin = 0.0
dt = -1.0
abs_p = -1.0
if consistency_order == 4
num_reduced_unknown = 5
else
num_reduced_unknown = consistency_order
end
pnoms = ones(Complex{Float64}, num_eig_vals, 1)
gamma = Variable(num_stage_evals - num_reduced_unknown)
normalized_powered_eigvals = zeros(Complex{Float64}, num_eig_vals, num_stage_evals)
normalize_power_eigvals!(normalized_powered_eigvals,
eig_vals,
num_stage_evals)
normalized_powered_eigvals_scaled = similar(normalized_powered_eigvals)
if kwargs[:verbose]
println("Start optimization of stability polynomial \n")
end
while dtmax - dtmin > dteps
dt = 0.5 * (dtmax + dtmin)
for k in 1:num_stage_evals
@views normalized_powered_eigvals_scaled[:, k] = dt^k .*
normalized_powered_eigvals[:,
k]
end
if num_stage_evals - num_reduced_unknown > 0
problem = minimize(stability_polynomials!(pnoms,
num_stage_evals,
normalized_powered_eigvals_scaled,
gamma, consistency_order,
num_reduced_unknown; kwargs...))
solve!(problem,
MOI.OptimizerWithAttributes(Optimizer, "gamma" => 0.99,
"delta" => 2e-7,
"feastol" => 1e-9,
"abstol" => 1e-9,
"reltol" => 1e-9,
"feastol_inacc" => 1e-4,
"abstol_inacc" => 5e-5,
"reltol_inacc" => 5e-5,
"nitref" => 9,
"maxit" => 100,
"verbose" => 3); silent = true)
abs_p = problem.optval
else
abs_p = stability_polynomials!(pnoms,
num_stage_evals,
normalized_powered_eigvals_scaled,
gamma, consistency_order,
num_reduced_unknown; kwargs...)
end
if abs_p < 1
dtmin = dt
else
dtmax = dt
end
end
if kwargs[:verbose]
println("Concluded stability polynomial optimization \n")
end
if num_stage_evals - num_reduced_unknown > 0
gamma_opt = evaluate(gamma)
if isa(gamma_opt, Number)
gamma_opt = [gamma_opt]
end
else
gamma_opt = nothing
end
undo_normalization!(gamma_opt, num_stage_evals,
num_reduced_unknown, consistency_order)
return gamma_opt, dt
end
end
end