"""
DGMulti(approximation_type::AbstractDerivativeOperator;
element_type::AbstractElemShape,
surface_flux=flux_central,
surface_integral=SurfaceIntegralWeakForm(surface_flux),
volume_integral=VolumeIntegralWeakForm(),
kwargs...)
Create a summation by parts (SBP) discretization on the given `element_type`
using a tensor product structure based on the 1D SBP derivative operator
passed as `approximation_type`.
For more info, see the documentations of
[StartUpDG.jl](https://jlchan.github.io/StartUpDG.jl/dev/)
and
[SummationByPartsOperators.jl](https://ranocha.de/SummationByPartsOperators.jl/stable/).
"""
function DGMulti(approximation_type::AbstractDerivativeOperator;
element_type::AbstractElemShape,
surface_flux = flux_central,
surface_integral = SurfaceIntegralWeakForm(surface_flux),
volume_integral = VolumeIntegralWeakForm(),
kwargs...)
rd = RefElemData(element_type, approximation_type; kwargs...)
return DG(rd, nothing, surface_integral, volume_integral)
end
function DGMulti(element_type::AbstractElemShape,
approximation_type::AbstractDerivativeOperator,
volume_integral,
surface_integral;
kwargs...)
return DGMulti(approximation_type, element_type = element_type,
surface_integral = surface_integral, volume_integral = volume_integral)
end
const DGMultiPeriodicFDSBP{NDIMS, ApproxType, ElemType} = DGMulti{NDIMS, ElemType,
ApproxType,
SurfaceIntegral,
VolumeIntegral} where {
NDIMS,
ElemType,
ApproxType <:
SummationByPartsOperators.AbstractPeriodicDerivativeOperator,
SurfaceIntegral,
VolumeIntegral
}
const DGMultiFluxDiffPeriodicFDSBP{NDIMS, ApproxType, ElemType} = DGMulti{NDIMS, ElemType,
ApproxType,
SurfaceIntegral,
VolumeIntegral} where {
NDIMS,
ElemType,
ApproxType <:
SummationByPartsOperators.AbstractPeriodicDerivativeOperator,
SurfaceIntegral <:
SurfaceIntegralWeakForm,
VolumeIntegral <:
VolumeIntegralFluxDifferencing
}
"""
DGMultiMesh(dg::DGMulti)
Constructs a single-element [`DGMultiMesh`](@ref) for a single periodic element given
a DGMulti with `approximation_type` set to a periodic (finite difference) SBP operator from
SummationByPartsOperators.jl.
"""
function DGMultiMesh(dg::DGMultiPeriodicFDSBP{NDIMS};
coordinates_min = ntuple(_ -> -one(real(dg)), NDIMS),
coordinates_max = ntuple(_ -> one(real(dg)), NDIMS)) where {NDIMS}
rd = dg.basis
e = Ones{eltype(rd.r)}(size(rd.r))
z = Zeros{eltype(rd.r)}(size(rd.r))
VXYZ = ntuple(_ -> [], NDIMS)
EToV = NaN
FToF = []
xyz = xyzq = map(copy, rd.rst)
for dim in 1:NDIMS
factor = (coordinates_max[dim] - coordinates_min[dim]) / 2
@. xyz[dim] = factor * (xyz[dim] + 1) + coordinates_min[dim]
end
xyzf = ntuple(_ -> [], NDIMS)
wJq = diag(rd.M)
mapM = mapP = mapB = []
coord_diffs = coordinates_max .- coordinates_min
J_scalar = prod(coord_diffs) / 2^NDIMS
J = e * J_scalar
if NDIMS == 1
rxJ = J_scalar * 2 / coord_diffs[1]
rstxyzJ = @SMatrix [rxJ * e]
elseif NDIMS == 2
rxJ = J_scalar * 2 / coord_diffs[1]
syJ = J_scalar * 2 / coord_diffs[2]
rstxyzJ = @SMatrix [rxJ*e z; z syJ*e]
elseif NDIMS == 3
rxJ = J_scalar * 2 / coord_diffs[1]
syJ = J_scalar * 2 / coord_diffs[2]
tzJ = J_scalar * 2 / coord_diffs[3]
rstxyzJ = @SMatrix [rxJ*e z z; z syJ*e z; z z tzJ*e]
end
nxyzJ = ntuple(_ -> [], NDIMS)
Jf = []
periodicity = ntuple(_ -> true, NDIMS)
if NDIMS == 1
mesh_type = Line()
elseif NDIMS == 2
mesh_type = Quad()
elseif NDIMS == 3
mesh_type = Hex()
end
md = MeshData(StartUpDG.VertexMappedMesh(mesh_type, VXYZ, EToV), FToF, xyz, xyzf, xyzq,
wJq,
mapM, mapP, mapB, rstxyzJ, J, nxyzJ, Jf,
periodicity)
boundary_faces = []
return DGMultiMesh{NDIMS, rd.element_type, typeof(md),
typeof(boundary_faces)}(md, boundary_faces)
end
@muladd begin
function estimate_dt(mesh::DGMultiMesh, dg::DGMultiPeriodicFDSBP)
rd = dg.basis
return StartUpDG.estimate_h(rd, mesh.md)
end
function prolong2interfaces!(cache, u,
mesh::DGMultiMesh, equations, dg::DGMultiPeriodicFDSBP)
@assert nelements(mesh, dg, cache) == 1
return nothing
end
function calc_interface_flux!(cache, surface_integral::SurfaceIntegralWeakForm,
mesh::DGMultiMesh,
have_nonconservative_terms::False, equations,
dg::DGMultiPeriodicFDSBP)
@assert nelements(mesh, dg, cache) == 1
return nothing
end
function calc_surface_integral!(du, u, mesh::DGMultiMesh, equations,
surface_integral::SurfaceIntegralWeakForm,
dg::DGMultiPeriodicFDSBP, cache)
@assert nelements(mesh, dg, cache) == 1
return nothing
end
function create_cache(mesh::DGMultiMesh, equations,
dg::DGMultiFluxDiffPeriodicFDSBP, RealT, uEltype)
md = mesh.md
solution_container = initialize_dgmulti_solution_container(mesh, equations, dg,
uEltype)
Qrst = map(A -> dg.basis.M * A, dg.basis.Drst)
for dim in eachdim(mesh)
Q = Qrst[dim]
@assert isapprox(Q, -Q', atol = 1e-12, rtol = 0.0)
end
return (; solution_container, Qrst, invM = inv(dg.basis.M), invJ = inv.(md.J))
end
function calc_volume_integral!(du, u, mesh::DGMultiMesh,
have_nonconservative_terms::False, equations,
volume_integral::VolumeIntegralFluxDifferencing,
dg::DGMultiFluxDiffPeriodicFDSBP, cache)
@unpack volume_flux = volume_integral
if Threads.nthreads() > 1
for dim in eachdim(mesh)
normal_direction = get_contravariant_vector(1, dim, mesh, cache)
A = cache.Qrst[dim]
A_base, row_ids, cols, vals = sparse_operator_data(A)
@threaded for i in row_ids
u_i = u[i]
du_i = du[i]
@inbounds for id in nzrange(A_base, i)
j = cols[id]
u_j = u[j]
A_ij = -vals[id]
AF_ij = 2 * A_ij *
volume_flux(u_i, u_j, normal_direction, equations)
du_i = du_i + AF_ij
end
du[i] = du_i
end
end
@inbounds for i in eachindex(du)
du[i] = du[i] * cache.invM.diag[i]
end
else
for dim in eachdim(mesh)
normal_direction = get_contravariant_vector(1, dim, mesh, cache)
A = cache.Qrst[dim]
A_base, row_ids, cols, vals = sparse_operator_data(A)
@inbounds for i in row_ids
u_i = u[i]
du_i = du[i]
for id in nzrange(A_base, i)
j = cols[id]
if j > i
A_ij = -vals[id]
u_j = u[j]
AF_ij = 2 * A_ij *
volume_flux(u_i, u_j, normal_direction, equations)
du_i = du_i + AF_ij
du[j] = du[j] - AF_ij
end
end
du[i] = du_i
end
end
@inbounds for i in eachindex(du)
du[i] = du[i] * cache.invM.diag[i]
end
end
return nothing
end
end