module EulerNacaSteady #hide
println("Running euler_naca_steady example...") #hide
# # Solve Euler equation around a NACA0012 airfoil
using Bcube
using BcubeGmsh
using BcubeVTK
using WriteVTK # for write_vtk_bnd_discontinuous (VTKPointData, VTKCellData)
using LinearAlgebra
using StaticArrays
using BenchmarkTools
using Roots
using SparseArrays
using Profile
using InteractiveUtils
using DifferentialEquations
using Symbolics
using SparseDiffTools
const dir = string(@__DIR__, "/")
function compute_residual(qdof, Q, V, params)
q = (FEFunction(Q, qdof)...,)
# alias on measures
dΓ = params.dΓ
dΩ = params.dΩ
dΓ_wall = params.dΓ_wall
dΓ_farfield = params.dΓ_farfield
# Allocate rhs vectors
b_vol = zero(qdof)
b_fac = zero(qdof)
# compute volume residuals
l_vol(v) = ∫(flux_Ω(q, v))dΩ
assemble_linear!(b_vol, l_vol, V)
# face normals for each face domain (lazy, no computation at this step)
nΓ = get_face_normals(dΓ)
nΓ_wall = get_face_normals(dΓ_wall)
nΓ_farfield = get_face_normals(dΓ_farfield)
# flux residuals from interior faces for all variables
l_Γ(v) = ∫(flux_Γ(q, v, nΓ))dΓ
assemble_linear!(b_fac, l_Γ, V)
# flux residuals from bc faces for all variables
l_Γ_wall(v) = ∫(flux_Γ_wall(q, v, nΓ_wall))dΓ_wall
l_Γ_farfield(v) = ∫(flux_Γ_farfield(q, v, nΓ_farfield))dΓ_farfield
assemble_linear!(b_fac, l_Γ_wall, V)
assemble_linear!(b_fac, l_Γ_farfield, V)
dQ = b_vol .- b_fac
return dQ
end
"""
flux_Ω(q, v)
Compute volume residual using the lazy-operators approach
"""
flux_Ω(q, v) = _flux_Ω ∘ (q, map(∇, v))
function _flux_Ω(q, ∇v)
ρ, ρu, ρE = q
∇λ_ρ, ∇λ_ρu, ∇λ_ρE = ∇v
γ = stateInit.γ
vel = ρu ./ ρ
ρuu = ρu * transpose(vel)
p = pressure(ρ, ρu, ρE, γ)
flux_ρ = ρu
flux_ρu = ρuu + p * I
flux_ρE = (ρE + p) .* vel
return return ∇λ_ρ ⋅ flux_ρ + ∇λ_ρu ⊡ flux_ρu + ∇λ_ρE ⋅ flux_ρE
end
"""
flux_Γ(q, v, n)
Flux at the interface is defined by a composition of two functions:
* the input states at face sides which are needed for the riemann flux
* `flux_roe` defines the Riemann flux (as usual)
"""
flux_Γ(q, v, n) = flux_roe ∘ (side⁻(q), side⁺(q), jump(v), side⁻(n))
"""
flux_roe(q⁻, q⁺, δv, n)
"""
function flux_roe(q⁻, q⁺, δv, n)
γ = stateInit.γ
nx, ny = n
ρ1, (ρu1, ρv1), ρE1 = q⁻
ρ2, (ρu2, ρv2), ρE2 = q⁺
δλ_ρ1, δλ_ρu1, δλ_ρE1 = δv
ρ1 = max(eps(ρ1), ρ1)
ρ2 = max(eps(ρ2), ρ2)
# Closure
u1 = ρu1 / ρ1
v1 = ρv1 / ρ1
u2 = ρu2 / ρ2
v2 = ρv2 / ρ2
p1 = pressure(ρ1, SA[ρu1, ρv1], ρE1, γ)
p2 = pressure(ρ2, SA[ρu2, ρv2], ρE2, γ)
H2 = (γ / (γ - 1)) * p2 / ρ2 + (u2 * u2 + v2 * v2) / 2.0
H1 = (γ / (γ - 1)) * p1 / ρ1 + (u1 * u1 + v1 * v1) / 2.0
R = √(ρ1 / ρ2)
invR1 = 1.0 / (R + 1)
uAv = (R * u1 + u2) * invR1
vAv = (R * v1 + v2) * invR1
Hav = (R * H1 + H2) * invR1
cAv = √(abs((γ - 1) * (Hav - (uAv * uAv + vAv * vAv) / 2.0)))
ecAv = (uAv * uAv + vAv * vAv) / 2.0
λ1 = nx * uAv + ny * vAv
λ3 = λ1 + cAv
λ4 = λ1 - cAv
d1 = ρ1 - ρ2
d2 = ρ1 * u1 - ρ2 * u2
d3 = ρ1 * v1 - ρ2 * v2
d4 = ρE1 - ρE2
# computation of the centered part of the flux
flux_ρ = nx * ρ2 * u2 + ny * ρ2 * v2
flux_ρu = nx * p2 + flux_ρ * u2
flux_ρv = ny * p2 + flux_ρ * v2
flux_ρE = H2 * flux_ρ
# Temp variables
rc1 = (γ - 1) / cAv
rc2 = (γ - 1) / cAv / cAv
uq41 = ecAv / cAv + cAv / (γ - 1)
uq42 = nx * uAv + ny * vAv
fdc1 = max(λ1, 0.0) * (d1 + rc2 * (-ecAv * d1 + uAv * d2 + vAv * d3 - d4))
fdc2 = max(λ1, 0.0) * ((nx * vAv - ny * uAv) * d1 + ny * d2 - nx * d3)
fdc3 =
max(λ3, 0.0) * (
(-uq42 * d1 + nx * d2 + ny * d3) / 2.0 +
rc1 * (ecAv * d1 - uAv * d2 - vAv * d3 + d4) / 2.0
)
fdc4 =
max(λ4, 0.0) * (
(uq42 * d1 - nx * d2 - ny * d3) / 2.0 +
rc1 * (ecAv * d1 - uAv * d2 - vAv * d3 + d4) / 2.0
)
duv1 = fdc1 + (fdc3 + fdc4) / cAv
duv2 = uAv * fdc1 + ny * fdc2 + (uAv / cAv + nx) * fdc3 + (uAv / cAv - nx) * fdc4
duv3 = vAv * fdc1 - nx * fdc2 + (vAv / cAv + ny) * fdc3 + (vAv / cAv - ny) * fdc4
duv4 =
ecAv * fdc1 +
(ny * uAv - nx * vAv) * fdc2 +
(uq41 + uq42) * fdc3 +
(uq41 - uq42) * fdc4
flux_ρ += duv1
flux_ρu += duv2
flux_ρv += duv3
flux_ρE += duv4
return (δλ_ρ1 ⋅ flux_ρ + δλ_ρu1 ⋅ SA[flux_ρu, flux_ρv] + δλ_ρE1 ⋅ flux_ρE)
end
"""
flux_Γ_farfield(q, v, n)
Compute `Roe` flux on boundary face by imposing
`stateBcFarfield.u_in` on `side_p`
"""
flux_Γ_farfield(q, v, n) = flux_roe ∘ (side⁻(q), stateBcFarfield.u_inf, side⁻(v), side⁻(n))
"""
flux_Γ_wall(q, v, n)
"""
flux_Γ_wall(q, v, n) = _flux_Γ_wall ∘ (side⁻(q), side⁻(v), side⁻(n))
function _flux_Γ_wall(q⁻, v⁻, n)
γ = stateInit.γ
ρ1, ρu1, ρE1 = q⁻
λ_ρ1, λ_ρu1, λ_ρE1 = v⁻
p1 = pressure(ρ1, ρu1, ρE1, γ)
flux_ρ = zero(ρ1)
flux_ρu = p1 * n
flux_ρE = zero(ρE1)
return (λ_ρ1 ⋅ flux_ρ + λ_ρu1 ⋅ flux_ρu + λ_ρE1 ⋅ flux_ρE)
end
function sparse2vtk(
a::AbstractSparseMatrix,
name::String = string(@__DIR__, "../../../myout/sparse"),
)
BcubeVTK.vtk_write_array(name, Array(a), "my_property_name")
end
mutable struct VtkHandler
basename::String
basename_residual::String
ite::Int
VtkHandler(basename) = new(basename, basename * "_residual", 0)
end
"""
Write solution (at cell centers) to vtk
Wrapper for `write_vtk`
"""
function append_vtk(vtk, mesh, vars, t, params; res = nothing)
ρ, ρu, ρE = vars
# Mean cell values
# name2val_mean = (;zip(get_name.(vars), mean_values.(vars, degquad))...)
# p_mean = pressure.(name2val_mean[:ρ], name2val_mean[:ρu], name2val_mean[:ρE], params.stateInit.γ)
vtk_degree = maximum(x -> get_degree(Bcube.get_function_space(get_fespace(x))), vars)
vtk_degree = max(1, mesh_degree, vtk_degree)
Cp = pressure_coefficient ∘ (ρ, ρu, ρE)
Ma = mach ∘ (ρ, ρu, ρE)
dict_vars_dg = Dict(
"rho" => ρ,
"rhou" => ρu,
"rhoE" => ρE,
"Cp" => Cp,
"Mach" => Ma,
"rho_mean" => cell_mean(ρ, params.dΩ),
"rhou_mean" => cell_mean(ρu, params.dΩ),
"rhoE_mean" => cell_mean(ρE, params.dΩ),
"lim_rho" => params.limρ,
"lim_all" => params.limAll,
)
write_file(
vtk.basename * "_DG.pvd",
mesh,
dict_vars_dg,
vtk.ite,
t;
discontinuous = true,
mesh_degree = vtk_degree,
collection_append = vtk.ite > 0,
)
_ρ_wall = var_on_bnd_nodes_discontinuous(ρ, params.Γ_wall, vtk_degree)
_ρu_wall = var_on_bnd_nodes_discontinuous(ρu, params.Γ_wall, vtk_degree)
_ρE_wall = var_on_bnd_nodes_discontinuous(ρE, params.Γ_wall, vtk_degree)
Cp_wall = pressure_coefficient.(_ρ_wall, _ρu_wall, _ρE_wall)
Ma_wall = pressure_coefficient.(_ρ_wall, _ρu_wall, _ρE_wall)
dict_vars_wall = Dict(
"rho" => (_ρ_wall, VTKPointData()),
"rhou" => (_ρu_wall, VTKPointData()),
"rhoE" => (_ρE_wall, VTKPointData()),
"Cp" => (Cp_wall, VTKPointData()),
"Mach" => (Ma_wall, VTKPointData()),
)
BcubeVTK.write_vtk_bnd_discontinuous(
vtk.basename * "_bnd_DG",
1,
0.0,
params.Γ_wall,
dict_vars_wall,
vtk_degree;
append = false,
)
#residual:
if !isa(res, Nothing)
vtkfile = vtk_grid(vtk.basename_residual, Float64.(res.iter), [0.0, 1.0])
for (k, valₖ) in enumerate(res.val)
vtkfile["res_" * string(k), VTKPointData()] = [valₖ valₖ]
end
vtk_save(vtkfile)
end
# Update counter
vtk.ite += 1
return nothing
end
function init!(q, dΩ, initstate)
AoA = initstate.AoA
Minf = initstate.M_inf
Pinf = initstate.P_inf
Tinf = initstate.T_inf
r = initstate.r_gas
γ = initstate.γ
ρinf = Pinf / r / Tinf
ainf = √(γ * r * Tinf)
Vinf = Minf * ainf
ρVxinf = ρinf * Vinf * cos(AoA)
ρVyinf = ρinf * Vinf * sin(AoA)
ρEinf = Pinf / (γ - 1) + 0.5 * ρinf * Vinf^2
ρ0 = PhysicalFunction(x -> ρinf)
ρu0 = PhysicalFunction(x -> SA[ρVxinf, ρVyinf])
ρE0 = PhysicalFunction(x -> ρEinf)
projection_l2!(q, (ρ0, ρu0, ρE0), dΩ)
return nothing
end
function main(stateInit, stateBcFarfield, degree)
@show degree, degquad
mesh = read_mesh(dir * "../../../input/mesh/naca0012_o" * string(mesh_degree) * ".msh")
scale!(mesh, 1.0 / 0.5334)
dimcar = compute_dimcar(mesh)
DMPrelax = DMPcurv₀ .* dimcar .^ 2
# Then we create a `NamedTuple` to hold the simulation parameters.
params = (
degquad = degquad,
stateInit = stateInit,
stateBcFarfield = stateBcFarfield,
DMPrelax = DMPrelax,
)
# Define measures for cell and interior face integrations
dΩ = Measure(CellDomain(mesh), degquad)
dΓ = Measure(InteriorFaceDomain(mesh), degquad)
# Declare boundary conditions and
# create associated domains and measures
Γ_wall = BoundaryFaceDomain(mesh, ("NACA",))
Γ_farfield = BoundaryFaceDomain(mesh, ("FARFIELD",))
dΓ_wall = Measure(Γ_wall, degquad)
dΓ_farfield = Measure(Γ_farfield, degquad)
params = (
params...,
Γ_wall = Γ_wall,
dΓ = dΓ,
dΩ = dΩ,
dΓ_wall = dΓ_wall,
dΓ_farfield = dΓ_farfield,
)
qLowOrder = nothing
for deg in 0:degree
params = (params..., degree = deg)
fs = FunctionSpace(fspace, deg)
Q_sca = TrialFESpace(fs, mesh, :discontinuous; size = 1) # DG, scalar
Q_vec = TrialFESpace(fs, mesh, :discontinuous; size = 2) # DG, vectoriel
V_sca = TestFESpace(Q_sca)
V_vec = TestFESpace(Q_vec)
Q = MultiFESpace(Q_sca, Q_vec, Q_sca)
V = MultiFESpace(V_sca, V_vec, V_sca)
q = FEFunction(Q)
# select an initial configurations:
if deg == 0
init!(q, mesh, stateInit)
else
println("Start projection")
projection_l2!(q, qLowOrder, dΩ)
println("End projection")
end
# create CellData to store limiter values
limρ = Bcube.MeshCellData(ones(ncells(mesh)))
limAll = Bcube.MeshCellData(ones(ncells(mesh)))
params = (params..., limρ = limρ, limAll = limAll)
# Init vtk handler
mkpath(outputpath)
vtk = VtkHandler(
outputpath * "euler_naca_mdeg" * string(mesh_degree) * "_deg" * string(deg),
)
# Init time
time = 0.0
# Save initial solution
append_vtk(vtk, mesh, q, time, params)
# Build the cache and store everything you want to compute only once (such as the mass matrice inverse...)
cache = ()
# Allocate buffer for compute_residual
b_vol = zeros(Bcube.get_ndofs(Q))
b_fac = zeros(Bcube.get_ndofs(Q))
cache = (cache..., b_vol = b_vol, b_fac = b_fac)
cache = (
cache...,
cacheCellMean = Bcube.build_cell_mean_cache(q, dΩ),
mass = factorize(Bcube.build_mass_matrix(Q, V, dΩ)),
mass_sca = factorize(Bcube.build_mass_matrix(Q_sca, V_sca, dΩ)),
mass_vec = factorize(Bcube.build_mass_matrix(Q_vec, V_vec, dΩ)),
)
time, q = steady_solve!(Q, V, q, mesh, params, cache, vtk, deg)
append_vtk(vtk, mesh, q, time, params)
println("end steady_solve for deg=", deg, " !")
deg < degree && (qLowOrder = deepcopy(q))
end
return nothing
end
function steady_solve!(Q, V, q, mesh, params, cache, vtk, deg)
counter = [0]
q0 = deepcopy(get_dof_values(q))
ode_params =
(Q = Q, V = V, params = params, cache = cache, counter = counter, vtk = vtk)
rhs!(dq, q, p, t) = dq .= compute_residual(q, p.Q, p.V, p.params)
# compute sparsity pattern and coloring
println("computing jacobian cache...")
if withbench
_rhs!(dq, q) = rhs!(dq, q, ode_params, 0.0)
@btime $_rhs!(similar($q0), $q0)
q_bench = FEFunction(Q, q0)
@btime $apply_limitation!($q_bench, $ode_params)
@show length(q0)
end
#sparsity_pattern = Symbolics.jacobian_sparsity(_rhs!, similar(Q0), Q0)
#tjac = @elapsed Symbolics.jacobian_sparsity(_rhs!, similar(Q0), Q0)
#@show tjac
sparsity_pattern = Bcube.build_jacobian_sparsity_pattern(Q, mesh)
println("sparsity pattern computed !")
display(sparsity_pattern)
colors = matrix_colors(sparsity_pattern)
println("coloring done!")
@show maximum(colors)
ode = ODEFunction(
rhs!;
mass_matrix = Bcube.build_mass_matrix(Q, V, params.dΩ),
jac_prototype = sparsity_pattern,
colorvec = colors,
)
Tfinal = Inf
problem = ODEProblem(ode, q0, (0.0, Tfinal), ode_params)
timestepper = ImplicitEuler(; nlsolve = NLNewton(; max_iter = 20))
cb_cache = DiscreteCallback(always_true, update_cache!; save_positions = (false, false))
cb_vtk = DiscreteCallback(always_true, output_vtk; save_positions = (false, false))
cb_steady = TerminateSteadyState(1e-6, 1e-6, condition_steadystate)
error = 1e-1
sol = solve(
problem,
timestepper;
initializealg = NoInit(),
adaptive = true,
abstol = error,
reltol = error,
progress = false,
progress_steps = 1000,
save_everystep = false,
save_start = false,
save_end = false,
isoutofdomain = isoutofdomain,
callback = CallbackSet(cb_cache, cb_vtk, cb_steady),
)
set_dof_values!(q, sol.u[end])
return sol.t[end], q
end
always_true(args...) = true
function isoutofdomain(dof, p, t)
any(isnan, dof) && return true
q = FEFunction(p.Q, dof)
q_mean = map(get_values, cell_mean(q, p.cache.cacheCellMean))
p_mean = pressure.(q_mean..., stateInit.γ)
negative_ρ = any(x -> x < 0, q_mean[1])
negative_p = any(x -> x < 0, p_mean)
isout = negative_ρ || negative_p
isout && @show negative_ρ, negative_p
return isout
end
function update_cache!(integrator)
Q = integrator.p.Q
Q1, = Q
deg = get_degree(Bcube.get_function_space(Q1))
println(
"deg=",
deg,
" update_cache! : iter=",
integrator.p.counter[1],
" dt=",
integrator.dt,
)
q = FEFunction(integrator.p.Q, integrator.u)
limiter_projection && apply_limitation!(q, integrator.p)
return nothing
end
function output_vtk(integrator)
u_modified!(integrator, false)
mesh = get_mesh(get_domain(integrator.p.params.dΩ))
q = FEFunction(integrator.p.Q, integrator.u)
counter = integrator.p.counter
counter .+= 1
if (counter[1] % nout == 0)
println("output_vtk ", counter[1])
append_vtk(integrator.p.vtk, mesh, q, integrator.t, integrator.p.params)
end
return nothing
end
function condition_steadystate(integrator, abstol, reltol, min_t)
u_modified!(integrator, false)
if DiffEqBase.isinplace(integrator.sol.prob)
testval = first(get_tmp_cache(integrator))
@. testval = (integrator.u - integrator.uprev) / (integrator.t - integrator.tprev)
else
testval = (integrator.u - integrator.uprev) / (integrator.t - integrator.tprev)
end
if typeof(integrator.u) <: Array
any(
abs(d) > abstol && abs(d) > reltol * abs(u) for (d, abstol, reltol, u) in
zip(testval, Iterators.cycle(abstol), Iterators.cycle(reltol), integrator.u)
) && (return false)
else
any((abs.(testval) .> abstol) .& (abs.(testval) .> reltol .* abs.(integrator.u))) &&
(return false)
end
if min_t === nothing
return true
else
return integrator.t >= min_t
end
end
"""
Compute the characteristic dimension of each cell of `mesh`:
dimcar = (cell volume) / (cell surface)
# TODO :
to be moved to Bcube
"""
function compute_dimcar(mesh)
fs = FunctionSpace(:Lagrange, 0)
V = TestFESpace(fs, mesh; size = 1, isContinuous = false)
# Define measures for cell and interior face integrations
dΩ = Measure(CellDomain(mesh), degquad)
dΓ = Measure(InteriorFaceDomain(mesh), degquad)
dΓ_bc = Measure(BoundaryFaceDomain(mesh), degquad)
f1 = PhysicalFunction(x -> 1.0)
l(v) = ∫(f1 ⋅ v)dΩ
l_face(v, dω) = ∫(side⁻(f1) ⋅ side⁻(v) + side⁺(f1) ⋅ side⁺(v))dω
vol = assemble_linear(l, V)
surf = assemble_linear(Base.Fix2(l_face, dΓ), V)
surf += assemble_linear(Base.Fix2(l_face, dΓ_bc), V)
return vol ./ surf
end
"""
References:
* Xiangxiong Zhang, Chi-Wang Shu, On positivity-preserving high order discontinuous
Galerkin schemes for compressible Euler equations on rectangular meshes,
Journal of Computational Physics, Volume 229, Issue 23, 2010.
https://doi.org/10.1016/j.jcp.2010.08.016
* Zhang, X., Xia, Y. & Shu, CW. Maximum-Principle-Satisfying and Positivity-Preserving
High Order Discontinuous Galerkin Schemes for Conservation Laws on Triangular Meshes.
J Sci Comput 50, 29–62 (2012). https://doi.org/10.1007/s10915-011-9472-8
"""
function apply_limitation!(q::Bcube.AbstractFEFunction, ode_params)
params = ode_params.params
cache = ode_params.cache
mesh = get_mesh(get_domain(params.dΩ))
ρ, ρu, ρE = q
ρ_mean, ρu_mean, ρE_mean = cell_mean(q, cache.cacheCellMean)
_limρ, ρ_proj = linear_scaling_limiter(
ρ,
params.dΩ;
bounds = (ρmin₀, ρmax₀),
DMPrelax = params.DMPrelax,
mass = cache.mass_sca,
)
op_t = limiter_param_p ∘ (ρ_proj, ρu, ρE, ρ_mean, ρu_mean, ρE_mean)
t = Bcube._minmax_cells(op_t, mesh, Val(params.degquad))
tmin = Bcube.MeshCellData(getindex.(t, 1))
if eltype(_limρ) == eltype(params.limρ) # skip Dual number case
set_values!(params.limρ, get_values(_limρ))
set_values!(params.limAll, get_values(tmin))
end
limited_var(u, ū, lim_u) = ū + lim_u * (u - ū)
projection_l2!(ρ, limited_var(ρ_proj, ρ_mean, tmin), params.dΩ; mass = cache.mass_sca)
projection_l2!(ρu, limited_var(ρu, ρu_mean, tmin), params.dΩ; mass = cache.mass_vec)
projection_l2!(ρE, limited_var(ρE, ρE_mean, tmin), params.dΩ; mass = cache.mass_sca)
return nothing
end
function limiter_param_p(ρ̂, ρu, ρE, ρ_mean, ρu_mean, ρE_mean)
γ = stateInit.γ
p = pressure(ρ̂, ρu, ρE, γ)
if p ≥ pmin₀
t = 1.0
else
@show p, ρ̂, ρu, ρE
@show ρ_mean, ρu_mean, ρE_mean
@show pressure(ρ_mean, ρu_mean, ρE_mean, γ)
if pressure(ρ_mean, ρu_mean, ρE_mean, γ) > pmin₀
fₜ =
t ->
pressure(
t * ρ̂ + (1 - t) * ρ_mean,
t * ρu + (1 - t) * ρu_mean,
t * ρE + (1 - t) * ρE_mean,
γ,
) - pmin₀
bounds = (0.0, 1.0)
t = find_zero(fₜ, bounds, Bisection())
else
t = NaN
println("t = NaN")
end
end
return t
end
function pressure(ρ::Number, ρu::AbstractVector, ρE::Number, γ)
vel = ρu ./ ρ
ρuu = ρu * transpose(vel)
p = (γ - 1) * (ρE - tr(ρuu) / 2)
return p
end
compute_Pᵢ(P, γ, M) = P * (1 + 0.5 * (γ - 1) * M^2)^(γ / (γ - 1))
compute_Tᵢ(T, γ, M) = T * (1 + 0.5 * (γ - 1) * M^2)
function bc_state_farfield(AoA, M, P, T, r, γ)
a = √(γ * r * T)
vn = M * a
ρ = P / r / T
ρu = SA[ρ * vn * cos(AoA), ρ * vn * sin(AoA)]
ρE = P / (γ - 1) + 0.5 * ρ * vn^2
return (ρ, ρu, ρE)
end
function pressure_coefficient(ρ, ρu, ρE)
(pressure(ρ, ρu, ρE, stateInit.γ) - stateInit.P_inf) /
(stateBcFarfield.Pᵢ_inf - stateInit.P_inf)
end
function mach(ρ, ρu, ρE)
norm(ρu ./ ρ) / √(stateInit.γ * max(0.0, pressure(ρ, ρu, ρE, stateInit.γ) / ρ))
end
const degreemax = 2 # Function-space degree
const mesh_degree = 2
const fspace = :Lagrange
const limiter_projection = true
const ρmin₀ = 1.0e-8
const ρmax₀ = 1.0e+10
const pmin₀ = 1.0e-8
const pmax₀ = 1.0e+10
const DMPcurv₀ = 10.0e3
const withbench = false
const stateInit = (
AoA = deg2rad(1.25),
M_inf = 0.8,
P_inf = 101325.0,
T_inf = 275.0,
r_gas = 287.0,
γ = 1.4,
)
const nite_max = 300 #300000 # Number of time iteration(s)
const nout = 1 # number of step between two vtk outputs
const mass_matrix_in_solve = true
const degquad = 6
const outputpath = joinpath(@__DIR__, "../../../myout/euler_naca_steady/")
const stateBcFarfield = (
AoA = stateInit.AoA,
M_inf = stateInit.M_inf,
Pᵢ_inf = compute_Pᵢ(stateInit.P_inf, stateInit.γ, stateInit.M_inf),
Tᵢ_inf = compute_Tᵢ(stateInit.T_inf, stateInit.γ, stateInit.M_inf),
u_inf = bc_state_farfield(
stateInit.AoA,
stateInit.M_inf,
stateInit.P_inf,
stateInit.T_inf,
stateInit.r_gas,
stateInit.γ,
),
r_gas = stateInit.r_gas,
γ = stateInit.γ,
)
main(stateInit, stateBcFarfield, degreemax)
end #hide