diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml new file mode 100644 index 00000000..2293d5ef --- /dev/null +++ b/.github/workflows/FormatCheck.yml @@ -0,0 +1,38 @@ +name: format-check + +on: + push: + branches: + - 'main' + tags: '*' + pull_request: + +jobs: + check-format: + runs-on: ${{ matrix.os }} + strategy: + matrix: + julia-version: [1] + julia-arch: [x86] + os: [ubuntu-latest] + steps: + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.julia-version }} + + - uses: actions/checkout@v4 + - name: Install JuliaFormatter and format + run: | + julia -e 'using Pkg; Pkg.add(PackageSpec(name = "JuliaFormatter", version="1.0.45"))' + julia -e 'using JuliaFormatter; format(".")' + - name: Format check + run: | + julia -e ' + out = Cmd(`git diff --name-only`) |> read |> String + if out == "" + exit(0) + else + @error "Some files have not been formatted !!!" + write(stdout, out) + exit(1) + end' \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index 67899f36..9cdbb129 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,22 +3,22 @@ Pkg.add("Documenter") using Documenter using StableSpectralElements -push!(LOAD_PATH,"../src/") +push!(LOAD_PATH, "../src/") makedocs( sitename = "StableSpectralElements.jl", authors = "Tristan Montoya", pages = [ "Home" => "index.md", - "Reference" => ["`ConservationLaws`" => "ConservationLaws.md", - "`SpatialDiscretizations`" => "SpatialDiscretizations.md"] + "Reference" => [ + "`ConservationLaws`" => "ConservationLaws.md", + "`SpatialDiscretizations`" => "SpatialDiscretizations.md", ], + ], format = Documenter.HTML( prettyurls = get(ENV, "CI", nothing) == "true", assets = ["assets/favicon.ico"], - ansicolor=true - ) + ansicolor = true, + ), ) -deploydocs( - repo="github.com/tristanmontoya/StableSpectralElements.jl.git" -) \ No newline at end of file +deploydocs(repo = "github.com/tristanmontoya/StableSpectralElements.jl.git") diff --git a/src/.JuliaFormatter.toml b/src/.JuliaFormatter.toml new file mode 100644 index 00000000..6862df52 --- /dev/null +++ b/src/.JuliaFormatter.toml @@ -0,0 +1,8 @@ +# Use SciML style: https://github.com/SciML/SciMLStyle +style = "sciml" + +# Python style alignment. See https://github.com/domluna/JuliaFormatter.jl/pull/732. +yas_style_nesting = true + +# Align struct fields for better readability of large struct definitions +align_struct_field = true \ No newline at end of file diff --git a/src/Analysis/Analysis.jl b/src/Analysis/Analysis.jl index a2017d2f..60fe8b8d 100644 --- a/src/Analysis/Analysis.jl +++ b/src/Analysis/Analysis.jl @@ -1,49 +1,94 @@ module Analysis - using LinearMaps: LinearMap, UniformScalingMap - using LinearAlgebra: Diagonal, diag, diagm, mul!, lmul!, dot, norm, eigen, inv, svd, qr, pinv, eigsortby, I, rank, cond - using JLD2: save, load, save_object, load_object - using Plots: plot, savefig, plot!, scatter, text, annotate!, vline!, grid, theme_palette, twinx, @layout - using LaTeXStrings: LaTeXString, latexstring - using SparseArrays: sparse, blockdiag, kron! - using OrdinaryDiffEq: OrdinaryDiffEqAlgorithm, ODESolution, ODEIntegrator, solve, RK4, step!, reinit! - using StartUpDG: MeshData, vandermonde - using RecipesBase - using Printf - using PrettyTables - using Markdown - using TimerOutputs - using BenchmarkTools - - using ..ConservationLaws - using ..SpatialDiscretizations - using ..GridFunctions - using ..Solvers - using ..MatrixFreeOperators - using ..File - using ..Visualize - - export AbstractAnalysis, AbstractAnalysisResults, analyze, analyze_new, save_analysis, tabulate_analysis, tabulate_analysis_for_paper - - abstract type AbstractAnalysis end - abstract type AbstractAnalysisResults end - - function save_analysis(analysis::AbstractAnalysis, - results::AbstractAnalysisResults) - save(string(analysis.analysis_path, "analysis.jld2"), - Dict("analysis" => analysis, - "results" => results)) - end - - export ErrorAnalysis, AbstractNorm, QuadratureL2, QuadratureL2Normalized - include("error.jl") - - export ConservationAnalysis, PrimaryConservationAnalysis, EnergyConservationAnalysis, EntropyConservationAnalysis, ConservationAnalysisResults, ConservationAnalysisResultsWithDerivative, evaluate_conservation, evaluate_conservation_residual - include("conservation.jl") - - export RefinementAnalysis, RefinementErrorAnalysis, RefinementAnalysisResults, RefinementErrorAnalysisResults, run_refinement, get_tickslogscale - include("refinement.jl") - - export scaling_test_euler_2d - include("benchmark.jl") -end \ No newline at end of file +using LinearMaps: LinearMap, UniformScalingMap +using LinearAlgebra: + Diagonal, + diag, + diagm, + mul!, + lmul!, + dot, + norm, + eigen, + inv, + svd, + qr, + pinv, + eigsortby, + I, + rank, + cond +using JLD2: save, load, save_object, load_object +using Plots: + plot, + savefig, + plot!, + scatter, + text, + annotate!, + vline!, + grid, + theme_palette, + twinx, + @layout +using LaTeXStrings: LaTeXString, latexstring +using SparseArrays: sparse, blockdiag, kron! +using OrdinaryDiffEq: + OrdinaryDiffEqAlgorithm, ODESolution, ODEIntegrator, solve, RK4, + step!, reinit! +using StartUpDG: MeshData, vandermonde +using RecipesBase +using Printf +using PrettyTables +using Markdown +using TimerOutputs +using BenchmarkTools + +using ..ConservationLaws +using ..SpatialDiscretizations +using ..GridFunctions +using ..Solvers +using ..MatrixFreeOperators +using ..File +using ..Visualize + +export AbstractAnalysis, + AbstractAnalysisResults, + analyze, + analyze_new, + save_analysis, + tabulate_analysis, + tabulate_analysis_for_paper + +abstract type AbstractAnalysis end +abstract type AbstractAnalysisResults end + +function save_analysis(analysis::AbstractAnalysis, results::AbstractAnalysisResults) + save(string(analysis.analysis_path, "analysis.jld2"), + Dict("analysis" => analysis, "results" => results)) +end + +export ErrorAnalysis, AbstractNorm, QuadratureL2, QuadratureL2Normalized +include("error.jl") + +export ConservationAnalysis, + PrimaryConservationAnalysis, + EnergyConservationAnalysis, + EntropyConservationAnalysis, + ConservationAnalysisResults, + ConservationAnalysisResultsWithDerivative, + evaluate_conservation, + evaluate_conservation_residual +include("conservation.jl") + +export RefinementAnalysis, + RefinementErrorAnalysis, + RefinementAnalysisResults, + RefinementErrorAnalysisResults, + run_refinement, + get_tickslogscale +include("refinement.jl") + +export scaling_test_euler_2d +include("benchmark.jl") +end diff --git a/src/Analysis/benchmark.jl b/src/Analysis/benchmark.jl index 9184735e..faa9f845 100644 --- a/src/Analysis/benchmark.jl +++ b/src/Analysis/benchmark.jl @@ -1,143 +1,167 @@ """This removes the threading and considers just one element. Note that this probably will not work with the Euler equations as the facet states at adjacent elements are left undefined and thus may lead to non-physical states when used to compute the fluxes""" -@views @timeit "semi-disc. residual" function rhs_benchmark!( - dudt::AbstractArray{Float64,3}, u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{d,FirstOrder},<:ReferenceOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, t::Float64=0.0) where {d} - +@views @timeit "semi-disc. residual" function rhs_benchmark!(dudt::AbstractArray{Float64, + 3}, + u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{d, + FirstOrder}, + <:ReferenceOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + t::Float64 = 0.0) where {d} @timeit "unpack" begin (; conservation_law, connectivity, form) = solver (; inviscid_numerical_flux) = form (; f_q, f_f, f_n, u_q, r_q, u_f, temp, CI) = solver.preallocated_arrays (; D, V, R, halfWΛ, halfN, BJf, n_f) = solver.operators end - + k = 1 #just one element - - @timeit "vandermonde" mul!(u_q[:,:,k], V, u[:,:,k]) - - @timeit "extrap solution" mul!(u_f[:,k,:], R, u_q[:,:,k]) - @timeit "phys flux" physical_flux!(f_q[:,:,:,k], - conservation_law, u_q[:,:,k]) + @timeit "vandermonde" mul!(u_q[:, :, k], V, u[:, :, k]) + + @timeit "extrap solution" mul!(u_f[:, k, :], R, u_q[:, :, k]) + + @timeit "phys flux" physical_flux!(f_q[:, :, :, k], conservation_law, u_q[:, :, k]) - @timeit "num flux" numerical_flux!(f_f[:,:,k], - conservation_law, inviscid_numerical_flux, u_f[:,k,:], - u_f[CI[connectivity[:,k]],:], n_f[:,:,k]) + @timeit "num flux" numerical_flux!(f_f[:, :, k], + conservation_law, + inviscid_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + n_f[:, :, k]) - @timeit "fill w zeros" fill!(r_q[:,:,k],0.0) + @timeit "fill w zeros" fill!(r_q[:, :, k], 0.0) @inbounds for n in 1:d @inbounds @timeit "volume operators" for m in 1:d - mul!(temp[:,:,k],halfWΛ[m,n,k],f_q[:,:,n,k]) - mul!(u_q[:,:,k],D[m]',temp[:,:,k]) - r_q[:,:,k] .+= u_q[:,:,k] - mul!(u_q[:,:,k],D[m],f_q[:,:,n,k]) - lmul!(halfWΛ[m,n,k],u_q[:,:,k]) - r_q[:,:,k] .-= u_q[:,:,k] + mul!(temp[:, :, k], halfWΛ[m, n, k], f_q[:, :, n, k]) + mul!(u_q[:, :, k], D[m]', temp[:, :, k]) + r_q[:, :, k] .+= u_q[:, :, k] + mul!(u_q[:, :, k], D[m], f_q[:, :, n, k]) + lmul!(halfWΛ[m, n, k], u_q[:, :, k]) + r_q[:, :, k] .-= u_q[:, :, k] end - + # difference facet flux @timeit "diff flux" begin - mul!(f_n[:,:,k], R, f_q[:,:,n,k]) - lmul!(halfN[n,k], f_n[:,:,k]) - f_f[:,:,k] .-= f_n[:,:,k] + mul!(f_n[:, :, k], R, f_q[:, :, n, k]) + lmul!(halfN[n, k], f_n[:, :, k]) + f_f[:, :, k] .-= f_n[:, :, k] end end # apply facet operators @timeit "facet operators" begin - lmul!(BJf[k], f_f[:,:,k]) - mul!(u_q[:,:,k], R', f_f[:,:,k]) - r_q[:,:,k] .-= u_q[:,:,k] + lmul!(BJf[k], f_f[:, :, k]) + mul!(u_q[:, :, k], R', f_f[:, :, k]) + r_q[:, :, k] .-= u_q[:, :, k] end # solve for time derivative - @timeit "trans. VDM" mul!(dudt[:,:,k], V', r_q[:,:,k]) - @timeit "mass solve" mass_matrix_solve!( - solver.mass_solver, k, dudt[:,:,k], u_q[:,:,k]) + @timeit "trans. VDM" mul!(dudt[:, :, k], V', r_q[:, :, k]) + @timeit "mass solve" mass_matrix_solve!(solver.mass_solver, + k, + dudt[:, :, k], + u_q[:, :, k]) return dudt end -@views @timeit "semi-disc. residual" function rhs_benchmark!( - dudt::AbstractArray{Float64,3}, u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{d,FirstOrder},<:PhysicalOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, t::Float64=0.0) where {d} - +@views @timeit "semi-disc. residual" function rhs_benchmark!(dudt::AbstractArray{Float64, + 3}, + u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{d, + FirstOrder}, + <:PhysicalOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + t::Float64 = 0.0) where {d} @timeit "unpack" begin (; conservation_law, operators, connectivity, form) = solver (; inviscid_numerical_flux) = form (; f_q, f_f, u_q, u_f, temp, CI) = solver.preallocated_arrays end - + k = 1 # just one element - - @timeit "vandermonde" mul!(u_q[:,:,k], operators.V[k], u[:,:,k]) - @timeit "extrap solution" mul!(u_f[:,k,:], operators.R[k], u_q[:,:,k]) - - @timeit "phys flux" physical_flux!(f_q[:,:,:,k], - conservation_law, u_q[:,:,k]) - - @timeit "num flux" numerical_flux!(f_f[:,:,k], - conservation_law, inviscid_numerical_flux, u_f[:,k,:], - u_f[CI[connectivity[:,k]],:], operators.n_f[k]) - - @timeit "fill w zeros" fill!(view(dudt,:,:,k),0.0) - + + @timeit "vandermonde" mul!(u_q[:, :, k], operators.V[k], u[:, :, k]) + @timeit "extrap solution" mul!(u_f[:, k, :], operators.R[k], u_q[:, :, k]) + + @timeit "phys flux" physical_flux!(f_q[:, :, :, k], conservation_law, u_q[:, :, k]) + + @timeit "num flux" numerical_flux!(f_f[:, :, k], + conservation_law, + inviscid_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + operators.n_f[k]) + + @timeit "fill w zeros" fill!(view(dudt, :, :, k), 0.0) + @inbounds for m in 1:d @timeit "volume operators" begin - mul!(view(temp,:,:,k),operators.VOL[k][m],f_q[:,:,m,k]) - dudt[:,:,k] .+= temp[:,:,k] + mul!(view(temp, :, :, k), operators.VOL[k][m], f_q[:, :, m, k]) + dudt[:, :, k] .+= temp[:, :, k] end end - - mul!(view(temp,:,:,k), operators.FAC[k], f_f[:,:,k]) - dudt[:,:,k] .+= temp[:,:,k] + + mul!(view(temp, :, :, k), operators.FAC[k], f_f[:, :, k]) + dudt[:, :, k] .+= temp[:, :, k] return dudt end -function scaling_test_euler_2d(p::Int, M::Int, - path="./results/euler_benchmark/", - parallelism=Threaded()) - path = new_path(path,true,true) +function scaling_test_euler_2d(p::Int, + M::Int, + path = "./results/euler_benchmark/", + parallelism = Threaded()) + path = new_path(path, true, true) mach_number = 0.4 angle = 0.0 L = 1.0 - γ=1.4 - T = L/mach_number # end time - strength = sqrt(2/(γ-1)*(1-0.75^(γ-1))) + γ = 1.4 + T = L / mach_number # end time + strength = sqrt(2 / (γ - 1) * (1 - 0.75^(γ - 1))) conservation_law = EulerEquations{2}(γ) - exact_solution = IsentropicVortex(conservation_law, θ=angle, - Ma=mach_number, β=strength, R=1.0/10.0, x_0=(L/2,L/2)); + exact_solution = IsentropicVortex(conservation_law, + θ = angle, + Ma = mach_number, + β = strength, + R = 1.0 / 10.0, + x_0 = (L / 2, L / 2)) - form = FluxDifferencingForm( - inviscid_numerical_flux=LaxFriedrichsNumericalFlux()) + form = FluxDifferencingForm(inviscid_numerical_flux = LaxFriedrichsNumericalFlux()) println("building reference approximation...") - reference_approximation = ReferenceApproximation(ModalTensor(p), - Tri(), mapping_degree=p, N_plot=25) + reference_approximation = ReferenceApproximation(ModalTensor(p), Tri(), + mapping_degree = p, N_plot = 25) println("generating mesh...") - uniform_mesh = uniform_periodic_mesh( - reference_approximation, ((0.0,L),(0.0,L)), (M,M)) + uniform_mesh = uniform_periodic_mesh(reference_approximation, ((0.0, L), (0.0, L)), + (M, M)) println("warping mesh...") - mesh = warp_mesh(uniform_mesh, reference_approximation, - ChanWarping(1/16, (L,L))) + mesh = warp_mesh(uniform_mesh, reference_approximation, ChanWarping(1 / 16, (L, L))) println("building global discretization...") - spatial_discretization = SpatialDiscretization(mesh, reference_approximation, project_jacobian=true) + spatial_discretization = SpatialDiscretization(mesh, reference_approximation, + project_jacobian = true) println("preprocessing...") - ode = semidiscretize(conservation_law, spatial_discretization, - exact_solution, form, (0.0, T), ReferenceOperator(), GenericTensorProductAlgorithm(), parallelism=parallelism) + ode = semidiscretize(conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + ReferenceOperator(), + GenericTensorProductAlgorithm(), + parallelism = parallelism) dudt = similar(ode.u0) println("solving...") - b = @benchmark semi_discrete_residual!($dudt,$ode.u0,$ode.p,0.0) + b = @benchmark semi_discrete_residual!($dudt, $ode.u0, $ode.p, 0.0) min_time = minimum(b.times) med_time = median(b.times) println("min = ", min_time, " med = ", med_time) @@ -148,10 +172,10 @@ function scaling_test_euler_2d(p::Int, M::Int, else minima = load_object(string(path, "minimum.jld2")) push!(minima, min_time) - save_object(string(path, "minimum.jld2"),min_time) + save_object(string(path, "minimum.jld2"), min_time) medians = load_object(string(path, "median.jld2")) push!(medians, med) - save_object(string(path, "median.jld2"),med_time) + save_object(string(path, "median.jld2"), med_time) end -end \ No newline at end of file +end diff --git a/src/Analysis/conservation.jl b/src/Analysis/conservation.jl index 81440013..16bf46a8 100644 --- a/src/Analysis/conservation.jl +++ b/src/Analysis/conservation.jl @@ -25,8 +25,8 @@ struct EnergyConservationAnalysis{V_type, MassSolver} <: ConservationAnalysis dict_name::String end -struct EntropyConservationAnalysis{V_type, MassSolver, - ConservationLaw} <: ConservationAnalysis +struct EntropyConservationAnalysis{V_type, MassSolver, ConservationLaw} <: + ConservationAnalysis mass_solver::MassSolver WJ::Vector{Diagonal{Float64, Vector{Float64}}} conservation_law::ConservationLaw @@ -37,256 +37,240 @@ struct EntropyConservationAnalysis{V_type, MassSolver, dict_name::String end -struct ConservationAnalysisResults <:AbstractConservationAnalysisResults +struct ConservationAnalysisResults <: AbstractConservationAnalysisResults t::Vector{Float64} E::Matrix{Float64} end -struct ConservationAnalysisResultsWithDerivative <:AbstractConservationAnalysisResults +struct ConservationAnalysisResultsWithDerivative <: AbstractConservationAnalysisResults t::Vector{Float64} E::Matrix{Float64} dEdt::Matrix{Float64} end function PrimaryConservationAnalysis(results_path::String, - conservation_law::AbstractConservationLaw, - spatial_discretization::SpatialDiscretization{d}) where {d} - + conservation_law::AbstractConservationLaw, + spatial_discretization::SpatialDiscretization{d}) where {d} _, N_c, N_e = get_dof(spatial_discretization, conservation_law) - - (; W, V) = spatial_discretization.reference_approximation + + (; W, V) = spatial_discretization.reference_approximation (; geometric_factors, N_e) = spatial_discretization - - WJ = [Diagonal(W .* geometric_factors.J_q[:,k]) for k in 1:N_e] - return PrimaryConservationAnalysis(WJ, N_c, N_e, V, results_path, - "conservation.jld2") + WJ = [Diagonal(W .* geometric_factors.J_q[:, k]) for k in 1:N_e] + + return PrimaryConservationAnalysis(WJ, N_c, N_e, V, results_path, "conservation.jld2") end function EnergyConservationAnalysis(results_path::String, - conservation_law::AbstractConservationLaw, - spatial_discretization::SpatialDiscretization{d}, - mass_solver=WeightAdjustedSolver(spatial_discretization)) where {d} - + conservation_law::AbstractConservationLaw, + spatial_discretization::SpatialDiscretization{d}, + mass_solver = WeightAdjustedSolver(spatial_discretization)) where {d} _, N_c, N_e = get_dof(spatial_discretization, conservation_law) - (; V) = spatial_discretization.reference_approximation + (; V) = spatial_discretization.reference_approximation (; N_e) = spatial_discretization - return EnergyConservationAnalysis( - mass_solver, N_c, N_e, V ,results_path, "energy.jld2") + return EnergyConservationAnalysis(mass_solver, N_c, N_e, V, results_path, "energy.jld2") end -function PrimaryConservationAnalysis( - conservation_law::AbstractConservationLaw, - spatial_discretization::SpatialDiscretization) - return PrimaryConservationAnalysis("./", conservation_law, - spatial_discretization) +function PrimaryConservationAnalysis(conservation_law::AbstractConservationLaw, + spatial_discretization::SpatialDiscretization) + return PrimaryConservationAnalysis("./", conservation_law, spatial_discretization) end -function EnergyConservationAnalysis( - conservation_law::AbstractConservationLaw, - spatial_discretization::SpatialDiscretization, - mass_solver=WeightAdjustedSolver(spatial_discretization)) - return EnergyConservationAnalysis("./", conservation_law, - spatial_discretization, mass_solver) +function EnergyConservationAnalysis(conservation_law::AbstractConservationLaw, + spatial_discretization::SpatialDiscretization, + mass_solver = WeightAdjustedSolver(spatial_discretization)) + return EnergyConservationAnalysis("./", + conservation_law, + spatial_discretization, + mass_solver) end function EntropyConservationAnalysis(results_path::String, - conservation_law::AbstractConservationLaw, - spatial_discretization::SpatialDiscretization{d}, - mass_solver=WeightAdjustedSolver(spatial_discretization)) where {d} - + conservation_law::AbstractConservationLaw, + spatial_discretization::SpatialDiscretization{d}, + mass_solver = WeightAdjustedSolver(spatial_discretization)) where {d} _, N_c, N_e = get_dof(spatial_discretization, conservation_law) - - (; W, V) = spatial_discretization.reference_approximation + + (; W, V) = spatial_discretization.reference_approximation (; geometric_factors, N_e) = spatial_discretization - - WJ = [Diagonal(W .* geometric_factors.J_q[:,k]) for k in 1:N_e] - return EntropyConservationAnalysis(mass_solver, WJ, conservation_law, - N_c, N_e, V, results_path, "entropy.jld2") + WJ = [Diagonal(W .* geometric_factors.J_q[:, k]) for k in 1:N_e] + + return EntropyConservationAnalysis(mass_solver, + WJ, + conservation_law, + N_c, + N_e, + V, + results_path, + "entropy.jld2") end -function evaluate_conservation( - analysis::PrimaryConservationAnalysis, - u::Array{Float64,3}) - (; WJ, N_c, N_e, V) = analysis +function evaluate_conservation(analysis::PrimaryConservationAnalysis, u::Array{Float64, 3}) + (; WJ, N_c, N_e, V) = analysis - return [sum(sum(WJ[k]*V*u[:,e,k]) - for k in 1:N_e) for e in 1:N_c] + return [sum(sum(WJ[k] * V * u[:, e, k]) for k in 1:N_e) for e in 1:N_c] end -function evaluate_conservation( - analysis::EntropyConservationAnalysis, - u::Array{Float64,3}) - (; WJ, conservation_law, N_e, V) = analysis +function evaluate_conservation(analysis::EntropyConservationAnalysis, u::Array{Float64, 3}) + (; WJ, conservation_law, N_e, V) = analysis S = 0.0 @views for k in 1:N_e - u_q = Matrix(V*u[:,:,k]) - for i in axes(u_q,1) - S += WJ[k][i,i]*entropy(conservation_law, u_q[i,:]) + u_q = Matrix(V * u[:, :, k]) + for i in axes(u_q, 1) + S += WJ[k][i, i] * entropy(conservation_law, u_q[i, :]) end end return [S] end -function evaluate_conservation( - analysis::EnergyConservationAnalysis, - u::Array{Float64,3}) - (; mass_solver, N_c, N_e, V) = analysis +function evaluate_conservation(analysis::EnergyConservationAnalysis, u::Array{Float64, 3}) + (; mass_solver, N_c, N_e, V) = analysis E = zeros(N_c) - N_p = size(V,2) + N_p = size(V, 2) M = Matrix{Float64}(undef, N_p, N_p) for k in 1:N_e M .= mass_matrix(mass_solver, k) for e in 1:N_c - E[e] += 0.5*u[:,e,k]'*M*u[:,e,k] + E[e] += 0.5 * u[:, e, k]' * M * u[:, e, k] end end return E end -function evaluate_conservation_residual( - analysis::PrimaryConservationAnalysis, - ::Array{Float64,3}, - dudt::Array{Float64,3}) - (; WJ, N_c, N_e, V) = analysis +function evaluate_conservation_residual(analysis::PrimaryConservationAnalysis, + ::Array{Float64, 3}, + dudt::Array{Float64, 3}) + (; WJ, N_c, N_e, V) = analysis - return [sum(ones(size(WJ[k],1))'*WJ[k]*V*dudt[:,e,k] - for k in 1:N_e) for e in 1:N_c] + return [sum(ones(size(WJ[k], 1))' * WJ[k] * V * dudt[:, e, k] for k in 1:N_e) + for e in 1:N_c] end -function evaluate_conservation_residual( - analysis::EnergyConservationAnalysis, - u::Array{Float64,3}, - dudt::Array{Float64,3}) - (; mass_solver, N_c, N_e) = analysis +function evaluate_conservation_residual(analysis::EnergyConservationAnalysis, + u::Array{Float64, 3}, + dudt::Array{Float64, 3}) + (; mass_solver, N_c, N_e) = analysis dEdt = zeros(N_c) for k in 1:N_e M = mass_matrix(mass_solver, k) for e in 1:N_c - dEdt[e] += u[:,e,k]'*M*dudt[:,e,k] + dEdt[e] += u[:, e, k]' * M * dudt[:, e, k] end end return dEdt end -@inline @views function evaluate_conservation_residual( - analysis::EntropyConservationAnalysis, u::Array{Float64,3}, - dudt::Array{Float64,3}) +@inline @views function evaluate_conservation_residual(analysis::EntropyConservationAnalysis, + u::Array{Float64, 3}, + dudt::Array{Float64, 3}) (; mass_solver, conservation_law, N_c, N_e, V, WJ) = analysis - u_q = Matrix{Float64}(undef,size(V,1),N_c) - w_q = Matrix{Float64}(undef,size(V,1),N_c) + u_q = Matrix{Float64}(undef, size(V, 1), N_c) + w_q = Matrix{Float64}(undef, size(V, 1), N_c) dEdt = 0.0 for k in 1:N_e M = mass_matrix(mass_solver, k) - mul!(u_q,V,u[:,:,k]) - for i in axes(u_q,1) - conservative_to_entropy!(w_q[i,:], conservation_law, u_q[i,:]) + mul!(u_q, V, u[:, :, k]) + for i in axes(u_q, 1) + conservative_to_entropy!(w_q[i, :], conservation_law, u_q[i, :]) end P = mass_matrix_inverse(mass_solver, k) * V' * WJ[k] for e in 1:N_c - dEdt += (P*w_q[:,e])'*M*dudt[:,e,k] + dEdt += (P * w_q[:, e])' * M * dudt[:, e, k] end end return [dEdt] - end function analyze(analysis::EntropyConservationAnalysis, - time_steps::Vector{Int}; normalize=false) - + time_steps::Vector{Int}; + normalize = false,) (; results_path, dict_name) = analysis N_t = length(time_steps) - t = Vector{Float64}(undef,N_t) - E = Matrix{Float64}(undef,N_t, 1) - dEdt = Matrix{Float64}(undef,N_t, 1) + t = Vector{Float64}(undef, N_t) + E = Matrix{Float64}(undef, N_t, 1) + dEdt = Matrix{Float64}(undef, N_t, 1) if normalize u_0, _ = load_solution(results_path, time_steps[1]) - factor = abs.(evaluate_conservation(analysis,u_0)) + factor = abs.(evaluate_conservation(analysis, u_0)) else factor = 1.0 end - + for i in 1:N_t - u, dudt, t[i] = load_solution(results_path, time_steps[i], load_du=true) - E[i,:] .= evaluate_conservation(analysis, u) ./ factor - dEdt[i,:] .= evaluate_conservation_residual(analysis, u, dudt) ./ factor + u, dudt, t[i] = load_solution(results_path, time_steps[i], load_du = true) + E[i, :] .= evaluate_conservation(analysis, u) ./ factor + dEdt[i, :] .= evaluate_conservation_residual(analysis, u, dudt) ./ factor end - results = ConservationAnalysisResults(t,E) + results = ConservationAnalysisResults(t, E) + + save(string(results_path, dict_name), + Dict("conservation_analysis" => analysis, "conservation_results" => results)) - save(string(results_path, dict_name), - Dict("conservation_analysis" => analysis, - "conservation_results" => results)) - return ConservationAnalysisResultsWithDerivative(t, E, dEdt) end - -function analyze(analysis::ConservationAnalysis, - initial_time_step::Union{Int,String}=0, - final_time_step::Union{Int,String}="final") - +function analyze(analysis::ConservationAnalysis, + initial_time_step::Union{Int, String} = 0, + final_time_step::Union{Int, String} = "final") (; results_path) = analysis u_0, _ = load_solution(results_path, initial_time_step) u_f, _ = load_solution(results_path, final_time_step) - initial = evaluate_conservation(analysis,u_0) - final = evaluate_conservation(analysis,u_f) + initial = evaluate_conservation(analysis, u_0) + final = evaluate_conservation(analysis, u_f) difference = final .- initial return initial, final, difference end -function analyze(analysis::ConservationAnalysis, - time_steps::Vector{Int}; normalize=false) - +function analyze(analysis::ConservationAnalysis, time_steps::Vector{Int}; normalize = false) (; results_path, N_c, dict_name) = analysis N_t = length(time_steps) - t = Vector{Float64}(undef,N_t) - E = Matrix{Float64}(undef,N_t, N_c) - dEdt = Matrix{Float64}(undef,N_t, N_c) + t = Vector{Float64}(undef, N_t) + E = Matrix{Float64}(undef, N_t, N_c) + dEdt = Matrix{Float64}(undef, N_t, N_c) if normalize u_0, _ = load_solution(results_path, time_steps[1]) - factor = abs.(evaluate_conservation(analysis,u_0)) + factor = abs.(evaluate_conservation(analysis, u_0)) else factor = 1.0 end - + for i in 1:N_t - u, dudt, t[i] = load_solution(results_path, time_steps[i], load_du=true) - E[i,:] = evaluate_conservation(analysis, u) ./ factor - dEdt[i,:] = evaluate_conservation_residual(analysis, u, dudt) ./ factor + u, dudt, t[i] = load_solution(results_path, time_steps[i], load_du = true) + E[i, :] = evaluate_conservation(analysis, u) ./ factor + dEdt[i, :] = evaluate_conservation_residual(analysis, u, dudt) ./ factor end - results = ConservationAnalysisResults(t,E) + results = ConservationAnalysisResults(t, E) - save(string(results_path, dict_name), - Dict("conservation_analysis" => analysis, - "conservation_results" => results)) + save(string(results_path, dict_name), + Dict("conservation_analysis" => analysis, "conservation_results" => results)) - return ConservationAnalysisResultsWithDerivative(t,E, dEdt) + return ConservationAnalysisResultsWithDerivative(t, E, dEdt) end -@recipe function plot(results::ConservationAnalysisResults, e::Int=1) - +@recipe function plot(results::ConservationAnalysisResults, e::Int = 1) xlabel --> latexstring("t") ylabel --> LaTeXString("Energy") legend --> false - results.t, results.E[:,e] + results.t, results.E[:, e] end @recipe function plot(results::ConservationAnalysisResultsWithDerivative, - e::Int=1; net_change=true, derivative=true) - + e::Int = 1; + net_change = true, + derivative = true,) xlabel --> latexstring("t") labels = [LaTeXString("Net change"), LaTeXString("Time derivative")] fontfamily --> "Computer Modern" @@ -295,14 +279,14 @@ end @series begin linestyle --> :solid label --> labels[1] - results.t, results.E[:,e] .- first(results.E[:,e]) + results.t, results.E[:, e] .- first(results.E[:, e]) end end if derivative @series begin linestyle --> :dot label --> labels[2] - results.t, results.dEdt[:,e] + results.t, results.dEdt[:, e] end end -end \ No newline at end of file +end diff --git a/src/Analysis/error.jl b/src/Analysis/error.jl index 677eb818..48bea541 100644 --- a/src/Analysis/error.jl +++ b/src/Analysis/error.jl @@ -1,4 +1,4 @@ -struct ErrorAnalysis{d,V_err_type,Volq_to_Err_type} <: AbstractAnalysis +struct ErrorAnalysis{d, V_err_type, Volq_to_Err_type} <: AbstractAnalysis N_c::Int N_e::Int V_err::V_err_type @@ -10,20 +10,19 @@ struct ErrorAnalysis{d,V_err_type,Volq_to_Err_type} <: AbstractAnalysis results_path::String end -function ErrorAnalysis(results_path::String, - conservation_law::AbstractConservationLaw, - spatial_discretization::SpatialDiscretization{d}, - error_quadrature_rule=nothing) where {d} - +function ErrorAnalysis(results_path::String, + conservation_law::AbstractConservationLaw, + spatial_discretization::SpatialDiscretization{d}, + error_quadrature_rule = nothing) where {d} (; N_e, reference_approximation) = spatial_discretization (; xyzq) = spatial_discretization.mesh - (; V,reference_element,approx_type) = reference_approximation + (; V, reference_element, approx_type) = reference_approximation (; J_q) = spatial_discretization.geometric_factors if isnothing(error_quadrature_rule) (w_err, V_err) = (diag(reference_approximation.W), V) volq_to_err = diagm(ones(length(w_err))) - else + else # Note: this introduces an additional approximation if the mapping and # Jacobian determinant are over degree p. # Otherwise we have to recompute the Jacobian rather than just @@ -31,7 +30,7 @@ function ErrorAnalysis(results_path::String, (; wq, rstq, element_type) = reference_element error_quad = quadrature(element_type, error_quadrature_rule) r_err = error_quad[1:d] - w_err = error_quad[d+1] + w_err = error_quad[d + 1] V_modes_to_errq = vandermonde(element_type, approx_type.p, r_err...) VDM = vandermonde(element_type, approx_type.p, rstq...) P_volq_to_modes = inv(VDM' * Diagonal(wq) * VDM) * VDM' * Diagonal(wq) @@ -41,35 +40,42 @@ function ErrorAnalysis(results_path::String, end total_volume = 0.0 for k in 1:N_e - total_volume += sum(w_err .* volq_to_err*J_q[:,k]) + total_volume += sum(w_err .* volq_to_err * J_q[:, k]) end _, N_c, N_e = get_dof(spatial_discretization, conservation_law) - - return ErrorAnalysis(N_c, N_e, V_err, w_err, volq_to_err, xyzq, J_q, - total_volume, results_path) -end -function analyze(analysis::ErrorAnalysis{d}, sol::Array{Float64,3}, - exact_solution, t::Float64=0.0; - normalize=false, write_to_file=true) where {d} + return ErrorAnalysis(N_c, + N_e, + V_err, + w_err, + volq_to_err, + xyzq, + J_q, + total_volume, + results_path) +end - (; N_c, N_e, J_q, w_err, V_err, volq_to_err, xyzq, - total_volume, results_path) = analysis +function analyze(analysis::ErrorAnalysis{d}, + sol::Array{Float64, 3}, + exact_solution, + t::Float64 = 0.0; + normalize = false, + write_to_file = true,) where {d} + (; N_c, N_e, J_q, w_err, V_err, volq_to_err, xyzq, total_volume, results_path) = analysis - u_approx = Matrix{Float64}(undef, size(V_err,1), N_c) + u_approx = Matrix{Float64}(undef, size(V_err, 1), N_c) error = zeros(N_c) @inbounds @views for k in 1:N_e - u_exact = evaluate(exact_solution, - Tuple(volq_to_err * xyzq[m][:,k] for m in 1:d), t) - u_approx = V_err * sol[:,:,k] + u_exact = evaluate(exact_solution, Tuple(volq_to_err * xyzq[m][:, k] for m in 1:d), + t) + u_approx = V_err * sol[:, :, k] for e in 1:N_c - error_nodal = u_exact[:,e] .- u_approx[:,e] - error[e] += dot(error_nodal, - (w_err .* volq_to_err*J_q[:,k]) .* error_nodal) + error_nodal = u_exact[:, e] .- u_approx[:, e] + error[e] += dot(error_nodal, (w_err .* volq_to_err * J_q[:, k]) .* error_nodal) end end - + if normalize error = sqrt.(error ./ total_volume) else @@ -77,9 +83,9 @@ function analyze(analysis::ErrorAnalysis{d}, sol::Array{Float64,3}, end if write_to_file - save(string(results_path, "error.jld2"), - Dict("error_analysis" => analysis, "error" => error)) + save(string(results_path, "error.jld2"), + Dict("error_analysis" => analysis, "error" => error)) end return error -end \ No newline at end of file +end diff --git a/src/Analysis/refinement.jl b/src/Analysis/refinement.jl index dd817bb0..501e80ea 100644 --- a/src/Analysis/refinement.jl +++ b/src/Analysis/refinement.jl @@ -13,7 +13,7 @@ end struct RefinementAnalysisResults <: AbstractAnalysisResults error::Matrix{Float64} # columns are solution variables - eoc::Matrix{Union{Float64,Missing}} + eoc::Matrix{Union{Float64, Missing}} dof::Matrix{Int} # columns are N_p, N_e conservation::Matrix{Float64} energy::Matrix{Float64} @@ -21,77 +21,80 @@ end struct RefinementErrorAnalysisResults <: AbstractAnalysisResults error::Matrix{Float64} # columns are solution variables - eoc::Matrix{Union{Float64,Missing}} + eoc::Matrix{Union{Float64, Missing}} dof::Matrix{Int} # columns are N_p, N_e end -function analyze(analysis::RefinementErrorAnalysis, n_grids=100) - +function analyze(analysis::RefinementErrorAnalysis, n_grids = 100) (; sequence_path) = analysis results_path = string(sequence_path, "grid_1/") - if !isfile(string(results_path,"error.jld2")) - error("File not found!") + if !isfile(string(results_path, "error.jld2")) + error("File not found!") end - conservation_law, spatial_discretization = load_project(results_path) + conservation_law, spatial_discretization = load_project(results_path) d = dim(spatial_discretization) (N_p, N_c, N_e) = get_dof(spatial_discretization, conservation_law) dof = [N_p N_e] - error = transpose(load(string(results_path,"error.jld2"))["error"]) - eoc = fill!(Array{Union{Float64, Missing}}(undef,1,N_c), missing) + error = transpose(load(string(results_path, "error.jld2"))["error"]) + eoc = fill!(Array{Union{Float64, Missing}}(undef, 1, N_c), missing) i = 2 grid_exists = true - + while isdir(string(sequence_path, "grid_", i, "/")) && i <= n_grids results_path = string(sequence_path, "grid_", i, "/") - - conservation_law, spatial_discretization = load_project(results_path) + + conservation_law, spatial_discretization = load_project(results_path) (N_p, N_c, N_e) = get_dof(spatial_discretization, conservation_law) dof = [dof; [N_p N_e]] - if !isfile(string(results_path), "error.jld2") + if !isfile(string(results_path), "error.jld2") error = [error; fill(NaN, 1, N_c)] eoc = [eoc; fill(NaN, 1, N_c)] else - error = [error; - transpose(load(string(results_path,"error.jld2"))["error"])] - eoc = [eoc; transpose([ - (log(error[i,e]) - log(error[i-1,e])) / - (log((dof[i,1]*dof[i,2])^(-1.0/d) ) - - log((dof[i-1,1]*dof[i-1,2])^(-1.0/d))) - for e in 1:N_c])] + error = [error + transpose(load(string(results_path, "error.jld2"))["error"])] + eoc = [eoc + transpose([(log(error[i, e]) - log(error[i - 1, e])) / + (log((dof[i, 1] * dof[i, 2])^(-1.0 / d)) - + log((dof[i - 1, 1] * dof[i - 1, 2])^(-1.0 / d))) + for e in 1:N_c])] end - if !isfile(string(sequence_path, "grid_", i+1, "/error.jld2")) + if !isfile(string(sequence_path, "grid_", i + 1, "/error.jld2")) grid_exists = false end - i = i+1 + i = i + 1 end - + return RefinementErrorAnalysisResults(error, eoc, dof) end -function analyze(analysis::RefinementAnalysis, - n_grids=100; max_derivs::Bool=false, - use_weight_adjusted_mass_matrix::Bool=true) - +function analyze(analysis::RefinementAnalysis, + n_grids = 100; + max_derivs::Bool = false, + use_weight_adjusted_mass_matrix::Bool = true,) (; sequence_path, exact_solution) = analysis results_path = string(sequence_path, "grid_1/") - if !isfile(string(results_path,"error.jld2")) error("File not found!") end - conservation_law, spatial_discretization = load_project(results_path) + if !isfile(string(results_path, "error.jld2")) + error("File not found!") + end + conservation_law, spatial_discretization = load_project(results_path) d = dim(spatial_discretization) (N_p, N_c, N_e) = get_dof(spatial_discretization, conservation_law) dof = [N_p N_e] time_steps = load_time_steps(results_path) N_t = last(time_steps) u, _ = load_solution(results_path, N_t) - - error = transpose(analyze(ErrorAnalysis(results_path, conservation_law, - spatial_discretization), u, exact_solution)) + + error = transpose(analyze(ErrorAnalysis(results_path, conservation_law, + spatial_discretization), + u, + exact_solution)) if use_weight_adjusted_mass_matrix mass_solver = WeightAdjustedSolver(spatial_discretization) @@ -100,33 +103,40 @@ function analyze(analysis::RefinementAnalysis, end if max_derivs - conservation_results = analyze( - PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), time_steps) - energy_results = analyze( - EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization, - mass_solver), time_steps) - conservation = [maximum(abs.(conservation_results.dEdt[:,e])) - for e in 1:N_c]' - energy = [maximum((energy_results.dEdt[:,e])) for e in 1:N_c]' + conservation_results = analyze(PrimaryConservationAnalysis(results_path, + conservation_law, + spatial_discretization), + time_steps) + energy_results = analyze(EnergyConservationAnalysis(results_path, + conservation_law, + spatial_discretization, + mass_solver), + time_steps) + conservation = [maximum(abs.(conservation_results.dEdt[:, e])) for e in 1:N_c]' + energy = [maximum((energy_results.dEdt[:, e])) for e in 1:N_c]' else - conservation = transpose( - analyze(PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), 0, N_t)[3]) - energy = transpose(analyze(EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization, mass_solver), 0, N_t)[3]) + conservation = transpose(analyze(PrimaryConservationAnalysis(results_path, + conservation_law, + spatial_discretization), + 0, + N_t)[3]) + energy = transpose(analyze(EnergyConservationAnalysis(results_path, + conservation_law, + spatial_discretization, + mass_solver), + 0, + N_t)[3]) end - eoc = fill!(Array{Union{Float64, Missing}}(undef,1,N_c), missing) + eoc = fill!(Array{Union{Float64, Missing}}(undef, 1, N_c), missing) i = 2 grid_exists = true - + while isdir(string(sequence_path, "grid_", i, "/")) && i <= n_grids results_path = string(sequence_path, "grid_", i, "/") - - conservation_law, spatial_discretization = load_project(results_path) + + conservation_law, spatial_discretization = load_project(results_path) (N_p, N_c, N_e) = get_dof(spatial_discretization, conservation_law) dof = [dof; [N_p N_e]] @@ -134,9 +144,9 @@ function analyze(analysis::RefinementAnalysis, mass_solver = WeightAdjustedSolver(spatial_discretization) else mass_solver = CholeskySolver(spatial_discretization) - end + end - if !isfile(string(results_path), "error.jld2") + if !isfile(string(results_path), "error.jld2") error = [error; fill(NaN, 1, N_c)] eoc = [eoc; fill(NaN, 1, N_c)] conservation = [conservation; fill(NaN, 1, N_c)] @@ -145,65 +155,87 @@ function analyze(analysis::RefinementAnalysis, time_steps = load_time_steps(results_path) N_t = last(time_steps) u, _ = load_solution(results_path, N_t) - error = [error; transpose(analyze(ErrorAnalysis(results_path, - conservation_law, spatial_discretization), u, exact_solution))] - eoc = [eoc; transpose([ - (log(error[i,e]) - log(error[i-1,e])) / - (log((dof[i,1]*dof[i,2])^(-1.0/d) ) - - log((dof[i-1,1]*dof[i-1,2])^(-1.0/d))) - for e in 1:N_c])] + error = [error + transpose(analyze(ErrorAnalysis(results_path, + conservation_law, + spatial_discretization), + u, + exact_solution))] + eoc = [eoc + transpose([(log(error[i, e]) - log(error[i - 1, e])) / + (log((dof[i, 1] * dof[i, 2])^(-1.0 / d)) - + log((dof[i - 1, 1] * dof[i - 1, 2])^(-1.0 / d))) + for e in 1:N_c])] if max_derivs - conservation_results = analyze( - PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), time_steps) - energy_results = analyze( - EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization, - mass_solver), time_steps) - conservation = [conservation; - [maximum(abs.(conservation_results.dEdt[:,e])) for e in 1:N_c]'] - energy = [energy; - [maximum((energy_results.dEdt[:,e])) for e in 1:N_c]'] + conservation_results = analyze(PrimaryConservationAnalysis(results_path, + conservation_law, + spatial_discretization), + time_steps) + energy_results = analyze(EnergyConservationAnalysis(results_path, + conservation_law, + spatial_discretization, + mass_solver), + time_steps) + conservation = [conservation + [maximum(abs.(conservation_results.dEdt[:, e])) + for e in 1:N_c]'] + energy = [energy + [maximum((energy_results.dEdt[:, e])) for e in 1:N_c]'] else - conservation = [conservation; transpose( - analyze(PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), 0, N_t)[3])] - energy = [energy; - transpose(analyze(EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization, - mass_solver), 0, N_t)[3])] + conservation = [conservation + transpose(analyze(PrimaryConservationAnalysis(results_path, + conservation_law, + spatial_discretization), + 0, + N_t)[3])] + energy = [energy + transpose(analyze(EnergyConservationAnalysis(results_path, + conservation_law, + spatial_discretization, + mass_solver), + 0, + N_t)[3])] end end - if !isfile(string(sequence_path, "grid_", i+1, "/error.jld2")) + if !isfile(string(sequence_path, "grid_", i + 1, "/error.jld2")) grid_exists = false end - i = i+1 + i = i + 1 end - + return RefinementAnalysisResults(error, eoc, dof, conservation, energy) end -@recipe function plot( - analysis::Vector{<:Union{RefinementAnalysis,RefinementErrorAnalysis}}, - results::Vector{<:Union{RefinementAnalysisResults,RefinementErrorAnalysisResults}}; - n_grids=nothing, pairs=true, xlims=nothing, reference_line=nothing, e=1) - +@recipe function plot(analysis::Vector{<:Union{RefinementAnalysis, RefinementErrorAnalysis}}, + results::Vector{<:Union{RefinementAnalysisResults, + RefinementErrorAnalysisResults}}; + n_grids = nothing, + pairs = true, + xlims = nothing, + reference_line = nothing, + e = 1,) results_path = string(analysis.sequence_path, "grid_1/") - if !isfile(string(results_path,"error.jld2")) error("File not found!") end - _, spatial_discretization = load_project(results_path) + if !isfile(string(results_path, "error.jld2")) + error("File not found!") + end + _, spatial_discretization = load_project(results_path) d = dim(spatial_discretization) - if d == 1 xlabel --> latexstring("\\mathrm{DOF}") - elseif d == 2 xlabel --> latexstring("\\sqrt{\\mathrm{DOF}}") - else xlabel --> latexstring(string("\\sqrt"),"[", d, "]{\\mathrm{DOF}}") end - + if d == 1 + xlabel --> latexstring("\\mathrm{DOF}") + elseif d == 2 + xlabel --> latexstring("\\sqrt{\\mathrm{DOF}}") + else + xlabel --> latexstring(string("\\sqrt"), "[", d, "]{\\mathrm{DOF}}") + end + ylabel --> LaTeXString("Error Metric") xaxis --> :log10 yaxis --> :log10 markersize --> 5 - windowsize --> (400,400) + windowsize --> (400, 400) legend --> :bottomleft legendfontsize --> 10 xlims --> xlims @@ -223,12 +255,15 @@ end linestyle --> :dash end label --> analysis[i].label - linecolor --> (i-1) ÷ 2 + 1 - markercolor --> (i-1) ÷ 2 + 1 + linecolor --> (i - 1) ÷ 2 + 1 + markercolor --> (i - 1) ÷ 2 + 1 if isnothing(n_grids) - (results[i].dof[:,1].*results[i].dof[:,2]).^(1.0/d), results[i].error[:,e] + (results[i].dof[:, 1] .* results[i].dof[:, 2]) .^ (1.0 / d), + results[i].error[:, e] else - (results[i].dof[1:n_grids[i],1].*results[i].dof[1:n_grids[i],2]).^(1.0/d), results[i].error[1:n_grids[i],e] + (results[i].dof[1:n_grids[i], 1] .* results[i].dof[1:n_grids[i], 2]) .^ + (1.0 / d), + results[i].error[1:n_grids[i], e] end end end @@ -240,44 +275,79 @@ end linestyle --> :solid label --> "" x = [reference_line[i][3], reference_line[i][4]] - x, reference_line[i][2]./(x.^reference_line[i][1]) + x, reference_line[i][2] ./ (x .^ reference_line[i][1]) end end end end -function tabulate_analysis(results::RefinementAnalysisResults; e=1, - print_latex=true) - tab = hcat(results.dof[:,2], results.conservation[:,e], - results.energy[:,e], results.error[:,e], results.eoc[:,e]) +function tabulate_analysis(results::RefinementAnalysisResults; e = 1, print_latex = true) + tab = hcat(results.dof[:, 2], + results.conservation[:, e], + results.energy[:, e], + results.error[:, e], + results.eoc[:, e]) if print_latex - latex_header = ["Elements", "Conservation Metric", "Energy Metric", - "\$L^2\$ Error", "Order"] - pretty_table(tab, header=latex_header, backend = Val(:latex), - formatters = (ft_nomissing, ft_printf("%d", [1,]), ft_printf("%.5e", [2,3,4]), ft_printf("%1.5f", [5,])), tf = tf_latex_booktabs) + latex_header = [ + "Elements", + "Conservation Metric", + "Energy Metric", + "\$L^2\$ Error", + "Order" + ] + pretty_table(tab, + header = latex_header, + backend = Val(:latex), + formatters = (ft_nomissing, + ft_printf("%d", [1]), + ft_printf("%.5e", [2, 3, 4]), + ft_printf("%1.5f", [5])), + tf = tf_latex_booktabs) end - return pretty_table(String, tab, header=["Elements", "Conservation Metric", - "Energy Metric", "L² Error", "Order"], - formatters = (ft_nomissing, ft_printf("%d", [1,]), - ft_printf("%.5e", [2,3,4,]), - ft_printf("%1.5f", [5,])), - tf = tf_unicode_rounded) + return pretty_table(String, + tab, + header = [ + "Elements", + "Conservation Metric", + "Energy Metric", + "L² Error", + "Order" + ], + formatters = (ft_nomissing, + ft_printf("%d", [1]), + ft_printf("%.5e", [2, 3, 4]), + ft_printf("%1.5f", [5])), + tf = tf_unicode_rounded) +end + +function tabulate_analysis_for_paper(results::NTuple{2, RefinementAnalysisResults}; e = 1) + cons = Tuple(results[i].conservation[:, e] for i in 1:2) + ener = Tuple(results[i].energy[:, e] for i in 1:2) + err = Tuple(results[i].error[:, e] for i in 1:2) + eoc = Tuple(results[i].eoc[:, e] for i in 1:2) + + tab = hcat(results[1].dof[:, 2], cons..., ener..., err..., eoc...) + + latex_header = vcat([ + "\$N_e\$", + "Conservation Metric", + "", + "Energy Metric", + "", + "Error Metric", + "", + "Order", + "" + ]) + pretty_table(tab, + header = latex_header, + backend = Val(:latex), + formatters = (ft_nomissing, + ft_printf("& %d", [1]), + ft_printf("%.3e", [2, 3, 4, 5, 6, 7]), + ft_printf("%1.2f", [8, 9]), + (v, i, j) -> (v == "NaN") ? "---" : v), + tf = tf_latex_booktabs) end - -function tabulate_analysis_for_paper(results::NTuple{2,RefinementAnalysisResults}; e=1) - cons = Tuple(results[i].conservation[:,e] for i in 1:2) - ener = Tuple(results[i].energy[:,e] for i in 1:2) - err =Tuple(results[i].error[:,e] for i in 1:2) - eoc =Tuple(results[i].eoc[:,e] for i in 1:2) - - tab = hcat(results[1].dof[:,2], cons..., ener..., err..., eoc...) - - latex_header = vcat(["\$N_e\$", "Conservation Metric", "", "Energy Metric", "", "Error Metric","", "Order",""]) - pretty_table(tab, header=latex_header, backend = Val(:latex), - formatters = (ft_nomissing, ft_printf("& %d", [1,]), ft_printf("%.3e", [2,3,4,5,6,7]), ft_printf("%1.2f", [8,9]), - (v, i, j) -> (v == "NaN") ? "---" : v), - - tf = tf_latex_booktabs) -end \ No newline at end of file diff --git a/src/ConservationLaws/ConservationLaws.jl b/src/ConservationLaws/ConservationLaws.jl index 8d7d2d05..971cd2cb 100644 --- a/src/ConservationLaws/ConservationLaws.jl +++ b/src/ConservationLaws/ConservationLaws.jl @@ -1,153 +1,199 @@ module ConservationLaws - using LinearMaps: LinearMap - using MuladdMacro - using StaticArrays: SVector, SMatrix, MVector - using LinearAlgebra: mul!, I - using ..GridFunctions - - import ..GridFunctions: evaluate - - export physical_flux, physical_flux!, numerical_flux!, entropy, conservative_to_entropy!, entropy_to_conservative!, compute_two_point_flux, wave_speed, logmean, inv_logmean, AbstractConservationLaw, AbstractPDEType, FirstOrder, SecondOrder, AbstractInviscidNumericalFlux, AbstractViscousNumericalFlux, NoInviscidFlux, NoViscousFlux, LaxFriedrichsNumericalFlux, CentralNumericalFlux, BR1, EntropyConservativeNumericalFlux, AbstractTwoPointFlux, ConservativeFlux, EntropyConservativeFlux, NoTwoPointFlux, ExactSolution - - abstract type AbstractConservationLaw{d, PDEType, N_c} end - abstract type AbstractPDEType end - struct FirstOrder <: AbstractPDEType end - struct SecondOrder <: AbstractPDEType end - - """First-order numerical fluxes""" - abstract type AbstractInviscidNumericalFlux end - struct NoInviscidFlux <: AbstractInviscidNumericalFlux end - struct LaxFriedrichsNumericalFlux <: AbstractInviscidNumericalFlux - halfλ::Float64 - function LaxFriedrichsNumericalFlux(λ::Float64) - return new(0.5*λ) - end +using LinearMaps: LinearMap +using MuladdMacro +using StaticArrays: SVector, SMatrix, MVector +using LinearAlgebra: mul!, I +using ..GridFunctions + +import ..GridFunctions: evaluate + +export physical_flux, + physical_flux!, + numerical_flux!, + entropy, + conservative_to_entropy!, + entropy_to_conservative!, + compute_two_point_flux, + wave_speed, + logmean, + inv_logmean, + AbstractConservationLaw, + AbstractPDEType, + FirstOrder, + SecondOrder, + AbstractInviscidNumericalFlux, + AbstractViscousNumericalFlux, + NoInviscidFlux, + NoViscousFlux, + LaxFriedrichsNumericalFlux, + CentralNumericalFlux, + BR1, + EntropyConservativeNumericalFlux, + AbstractTwoPointFlux, + ConservativeFlux, + EntropyConservativeFlux, + NoTwoPointFlux, + ExactSolution + +abstract type AbstractConservationLaw{d, PDEType, N_c} end +abstract type AbstractPDEType end +struct FirstOrder <: AbstractPDEType end +struct SecondOrder <: AbstractPDEType end + +"""First-order numerical fluxes""" +abstract type AbstractInviscidNumericalFlux end +struct NoInviscidFlux <: AbstractInviscidNumericalFlux end +struct LaxFriedrichsNumericalFlux <: AbstractInviscidNumericalFlux + halfλ::Float64 + function LaxFriedrichsNumericalFlux(λ::Float64) + return new(0.5 * λ) end - struct EntropyConservativeNumericalFlux <: AbstractInviscidNumericalFlux end - struct CentralNumericalFlux <: AbstractInviscidNumericalFlux end - LaxFriedrichsNumericalFlux() = LaxFriedrichsNumericalFlux(1.0) - - """Second-order numerical fluxes""" - abstract type AbstractViscousNumericalFlux end - struct BR1 <: AbstractViscousNumericalFlux end - struct NoViscousFlux <: AbstractViscousNumericalFlux end - - """Two-point fluxes (for split forms and entropy-stable schemes)""" - abstract type AbstractTwoPointFlux end - struct ConservativeFlux <: AbstractTwoPointFlux end - struct EntropyConservativeFlux <: AbstractTwoPointFlux end - struct NoTwoPointFlux <: AbstractTwoPointFlux end - - @inline @views function numerical_flux!( - f_star::AbstractMatrix{Float64}, - conservation_law::AbstractConservationLaw{d,PDEType,N_c}, - numerical_flux::LaxFriedrichsNumericalFlux, - u_in::AbstractMatrix{Float64}, u_out::AbstractMatrix{Float64}, - n_f::AbstractMatrix{Float64}, - two_point_flux=ConservativeFlux()) where {d, PDEType, N_c} - - @inbounds for i in axes(u_in, 1) - f_s = compute_two_point_flux(conservation_law, two_point_flux, - u_in[i,:], u_out[i,:]) - a = numerical_flux.halfλ*wave_speed(conservation_law, - u_in[i,:], u_out[i,:], n_f[:,i]) - for e in 1:N_c - f_n_avg = 0.0 - for m in 1:d - @muladd f_n_avg = f_n_avg + f_s[e,m]*n_f[m,i] - end - @muladd f_star[i,e] = f_n_avg + a * (u_in[i,e] - u_out[i,e]) +end +struct EntropyConservativeNumericalFlux <: AbstractInviscidNumericalFlux end +struct CentralNumericalFlux <: AbstractInviscidNumericalFlux end +LaxFriedrichsNumericalFlux() = LaxFriedrichsNumericalFlux(1.0) + +"""Second-order numerical fluxes""" +abstract type AbstractViscousNumericalFlux end +struct BR1 <: AbstractViscousNumericalFlux end +struct NoViscousFlux <: AbstractViscousNumericalFlux end + +"""Two-point fluxes (for split forms and entropy-stable schemes)""" +abstract type AbstractTwoPointFlux end +struct ConservativeFlux <: AbstractTwoPointFlux end +struct EntropyConservativeFlux <: AbstractTwoPointFlux end +struct NoTwoPointFlux <: AbstractTwoPointFlux end + +@inline @views function numerical_flux!(f_star::AbstractMatrix{Float64}, + conservation_law::AbstractConservationLaw{d, + PDEType, + N_c}, + numerical_flux::LaxFriedrichsNumericalFlux, + u_in::AbstractMatrix{Float64}, + u_out::AbstractMatrix{Float64}, + n_f::AbstractMatrix{Float64}, + two_point_flux = ConservativeFlux()) where {d, + PDEType, + N_c} + @inbounds for i in axes(u_in, 1) + f_s = compute_two_point_flux(conservation_law, + two_point_flux, + u_in[i, :], + u_out[i, :]) + a = numerical_flux.halfλ * + wave_speed(conservation_law, u_in[i, :], u_out[i, :], n_f[:, i]) + for e in 1:N_c + f_n_avg = 0.0 + for m in 1:d + @muladd f_n_avg = f_n_avg + f_s[e, m] * n_f[m, i] end + @muladd f_star[i, e] = f_n_avg + a * (u_in[i, e] - u_out[i, e]) end end - - @inline @views function numerical_flux!( - f_star::AbstractMatrix{Float64}, - conservation_law::AbstractConservationLaw{d,PDEType,N_c}, - ::Union{CentralNumericalFlux,EntropyConservativeNumericalFlux}, - u_in::AbstractMatrix{Float64}, u_out::AbstractMatrix{Float64}, - n_f::AbstractMatrix{Float64}, - two_point_flux=ConservativeFlux()) where {d,PDEType,N_c} - - @inbounds for i in axes(u_in, 1) - f_s = compute_two_point_flux(conservation_law, two_point_flux, - u_in[i,:], u_out[i,:]) - for e in 1:N_c - temp = 0.0 - for m in 1:d - @muladd temp = temp + f_s[e,m]*n_f[m,i] - end - f_star[i,e] = temp +end + +@inline @views function numerical_flux!(f_star::AbstractMatrix{Float64}, + conservation_law::AbstractConservationLaw{d, + PDEType, + N_c}, + ::Union{CentralNumericalFlux, + EntropyConservativeNumericalFlux}, + u_in::AbstractMatrix{Float64}, + u_out::AbstractMatrix{Float64}, + n_f::AbstractMatrix{Float64}, + two_point_flux = ConservativeFlux()) where {d, + PDEType, + N_c} + @inbounds for i in axes(u_in, 1) + f_s = compute_two_point_flux(conservation_law, + two_point_flux, + u_in[i, :], + u_out[i, :]) + for e in 1:N_c + temp = 0.0 + for m in 1:d + @muladd temp = temp + f_s[e, m] * n_f[m, i] end + f_star[i, e] = temp end end - - """ - Algorithm based on the Taylor series trick from Ismail and Roe (2009). There are further optimizations that could be made, but let's leave it like this for now. - """ - @inline function logmean(x::Float64, y::Float64) - # f = (y/x - 1) / (y/x + 1) - # = (y - x) / (x + y) - # rearrange to avoid divisions using trick from https://trixi-framework.github.io/Trixi.jl/stable/reference-trixi/#Trixi.ln_mean - f² = (x * (x - 2 * y) + y * y) / (x * (x + 2 * y) + y * y) - if f² < 1.0e-4 - return (x + y) * 105 / (210 + f² * (70 + f² * (42 + f² * 30))) - # faster factorized way to compute - # (x + y) / (2 + 2/3 * f^2 + 2/5 * f^4 + 2/7 * f^6) - else - return (y - x) / log(y/x) - end - end - - @inline function inv_logmean(x::Float64, y::Float64) - f² = (x * (x - 2 * y) + y * y) / (x * (x + 2 * y) + y * y) - if f² < 1.0e-4 - return (210 + f² * (70 + f² * (42 + f² * 30))) / ((x + y) * 105) - # faster factorized way to compute - # (x + y) / (2 + 2/3 * f^2 + 2/5 * f^4 + 2/7 * f^6) - else - return log(y/x)/(y - x) - end - end - - """Generic structure for exact solution to PDE (may be deprecated in future versions)""" - struct ExactSolution{d,ConservationLaw,InitialData,SourceTerm} <: AbstractGridFunction{d} - conservation_law::ConservationLaw - initial_data::InitialData - periodic::Bool - N_c::Int - - function ExactSolution( - conservation_law::AbstractConservationLaw{d,PDEType,N_c}, - initial_data::AbstractGridFunction{d}; - periodic::Bool=false) where {d, PDEType, N_c} - - return new{d,typeof(conservation_law),typeof(initial_data), - typeof(conservation_law.source_term)}( - conservation_law, initial_data, periodic, N_c) - end +end + +""" +Algorithm based on the Taylor series trick from Ismail and Roe (2009). There are further optimizations that could be made, but let's leave it like this for now. +""" +@inline function logmean(x::Float64, y::Float64) + # f = (y/x - 1) / (y/x + 1) + # = (y - x) / (x + y) + # rearrange to avoid divisions using trick from https://trixi-framework.github.io/Trixi.jl/stable/reference-trixi/#Trixi.ln_mean + f² = (x * (x - 2 * y) + y * y) / (x * (x + 2 * y) + y * y) + if f² < 1.0e-4 + return (x + y) * 105 / (210 + f² * (70 + f² * (42 + f² * 30))) + # faster factorized way to compute + # (x + y) / (2 + 2/3 * f^2 + 2/5 * f^4 + 2/7 * f^6) + else + return (y - x) / log(y / x) end - - @inbounds function entropy_to_conservative!(u::AbstractVector{Float64}, - ::AbstractConservationLaw, w::AbstractVector{Float64}) - copyto!(u,w) - return +end + +@inline function inv_logmean(x::Float64, y::Float64) + f² = (x * (x - 2 * y) + y * y) / (x * (x + 2 * y) + y * y) + if f² < 1.0e-4 + return (210 + f² * (70 + f² * (42 + f² * 30))) / ((x + y) * 105) + # faster factorized way to compute + # (x + y) / (2 + 2/3 * f^2 + 2/5 * f^4 + 2/7 * f^6) + else + return log(y / x) / (y - x) end - - @inbounds function conservative_to_entropy(w::AbstractVector{Float64}, - ::AbstractConservationLaw, u::AbstractVector{Float64}) - copyto!(w,u) - return +end + +"""Generic structure for exact solution to PDE (may be deprecated in future versions)""" +struct ExactSolution{d, ConservationLaw, InitialData, SourceTerm} <: AbstractGridFunction{d} + conservation_law::ConservationLaw + initial_data::InitialData + periodic::Bool + N_c::Int + + function ExactSolution(conservation_law::AbstractConservationLaw{d, PDEType, N_c}, + initial_data::AbstractGridFunction{d}; + periodic::Bool = false,) where {d, PDEType, N_c} + return new{d, + typeof(conservation_law), + typeof(initial_data), + typeof(conservation_law.source_term)}(conservation_law, + initial_data, + periodic, + N_c) end - - export LinearAdvectionEquation, LinearAdvectionDiffusionEquation - include("linear_advection_diffusion.jl") - - export InviscidBurgersEquation, ViscousBurgersEquation - include("burgers.jl") - - export EulerEquations, NavierStokesEquations, EulerPeriodicTest, TaylorGreenVortex, IsentropicVortex, KelvinHelmholtzInstability - include("euler_navierstokes.jl") - -end \ No newline at end of file +end + +@inbounds function entropy_to_conservative!(u::AbstractVector{Float64}, + ::AbstractConservationLaw, + w::AbstractVector{Float64}) + copyto!(u, w) + return +end + +@inbounds function conservative_to_entropy(w::AbstractVector{Float64}, + ::AbstractConservationLaw, + u::AbstractVector{Float64}) + copyto!(w, u) + return +end + +export LinearAdvectionEquation, LinearAdvectionDiffusionEquation +include("linear_advection_diffusion.jl") + +export InviscidBurgersEquation, ViscousBurgersEquation +include("burgers.jl") + +export EulerEquations, + NavierStokesEquations, + EulerPeriodicTest, + TaylorGreenVortex, + IsentropicVortex, + KelvinHelmholtzInstability +include("euler_navierstokes.jl") + +end diff --git a/src/ConservationLaws/burgers.jl b/src/ConservationLaws/burgers.jl index ee24dfd4..5b773630 100644 --- a/src/ConservationLaws/burgers.jl +++ b/src/ConservationLaws/burgers.jl @@ -7,12 +7,12 @@ Define an inviscid Burgers' equation of the form ``` where $\bm{a} \in \R^d$. A specialized constructor `InviscidBurgersEquation()` is provided for the one-dimensional case with `a = (1.0,)`. """ -struct InviscidBurgersEquation{d} <: AbstractConservationLaw{d,FirstOrder,1} - a::NTuple{d,Float64} +struct InviscidBurgersEquation{d} <: AbstractConservationLaw{d, FirstOrder, 1} + a::NTuple{d, Float64} source_term::AbstractGridFunction{d} - function InviscidBurgersEquation(a::NTuple{d,Float64}, - source_term::AbstractGridFunction{d}=NoSourceTerm{d}()) where {d} + function InviscidBurgersEquation(a::NTuple{d, Float64}, + source_term::AbstractGridFunction{d} = NoSourceTerm{d}()) where {d} return new{d}(a, source_term) end end @@ -26,13 +26,14 @@ Define a viscous Burgers' equation of the form ``` where $\bm{a} \in \R^d$ and $b \in \R^+$. A specialized constructor `ViscousBurgersEquation(b::Float64)` is provided for the one-dimensional case with `a = (1.0,)`. """ -struct ViscousBurgersEquation{d} <: AbstractConservationLaw{d,SecondOrder,1} - a::NTuple{d,Float64} +struct ViscousBurgersEquation{d} <: AbstractConservationLaw{d, SecondOrder, 1} + a::NTuple{d, Float64} b::Float64 source_term::AbstractGridFunction{d} - function ViscousBurgersEquation(a::NTuple{d,Float64}, b::Float64, - source_term::AbstractGridFunction{d}=NoSourceTerm{d}()) where {d} + function ViscousBurgersEquation(a::NTuple{d, Float64}, + b::Float64, + source_term::AbstractGridFunction{d} = NoSourceTerm{d}()) where {d} return new{d}(a, b, source_term) end end @@ -47,11 +48,11 @@ Evaluate the flux for the inviscid Burgers' equation `F(u) = a ½u^2` """ -function physical_flux!(f::AbstractArray{Float64,3}, - conservation_law::InviscidBurgersEquation{d}, - u::AbstractMatrix{Float64}) where {d} +function physical_flux!(f::AbstractArray{Float64, 3}, + conservation_law::InviscidBurgersEquation{d}, + u::AbstractMatrix{Float64}) where {d} @inbounds for m in 1:d - f[:,:,m] .= 0.5* conservation_law.a[m] * u.^2 + f[:, :, m] .= 0.5 * conservation_law.a[m] * u .^ 2 end end @@ -60,12 +61,13 @@ Evaluate the flux for the viscous Burgers' equation `F(u,q) = a ½u^2 - bq` """ -function physical_flux!(f::AbstractArray{Float64,3}, - conservation_law::ViscousBurgersEquation{d}, - u::AbstractMatrix{Float64}, q::AbstractArray{Float64,3}) where {d} +function physical_flux!(f::AbstractArray{Float64, 3}, + conservation_law::ViscousBurgersEquation{d}, + u::AbstractMatrix{Float64}, + q::AbstractArray{Float64, 3}) where {d} @inbounds for m in 1:d - f[:,:,m] .= 0.5*conservation_law.a[m] * u.^2 .- - conservation_law.b * q[:,:,m] + f[:, :, m] .= 0.5 * conservation_law.a[m] * u .^ 2 .- + conservation_law.b * q[:, :, m] end end @@ -74,15 +76,16 @@ Evaluate the interface normal solution for the viscous Burgers' equation using t `U*(u⁻, u⁺, n) = ½(u⁻ + u⁺)n` """ -@inline function numerical_flux!(u_nstar::AbstractArray{Float64,3}, - ::ViscousBurgersEquation{d}, - ::BR1, u_in::AbstractMatrix{Float64}, u_out::AbstractMatrix{Float64}, - n_f::AbstractMatrix{Float64}) where {d} - - u_avg = 0.5*(u_in .+ u_out) +@inline function numerical_flux!(u_nstar::AbstractArray{Float64, 3}, + ::ViscousBurgersEquation{d}, + ::BR1, + u_in::AbstractMatrix{Float64}, + u_out::AbstractMatrix{Float64}, + n_f::AbstractMatrix{Float64}) where {d} + u_avg = 0.5 * (u_in .+ u_out) @inbounds for m in 1:d - u_nstar[:,:,m] .= u_avg.*n_f[m,:] + u_nstar[:, :, m] .= u_avg .* n_f[m, :] end end @@ -92,61 +95,71 @@ Evaluate the numerical flux for the viscous Burgers' equation using the BR1 appr F*(u⁻, u⁺, q⁻, q⁺, n) = ½(F²(u⁻,q⁻) + F²(u⁺, q⁺))⋅n """ @inline function numerical_flux!(f_star::AbstractMatrix{Float64}, - conservation_law::ViscousBurgersEquation{d}, - ::BR1, u_in::AbstractMatrix{Float64}, u_out::AbstractMatrix{Float64}, - q_in::AbstractArray{Float64,3}, q_out::AbstractArray{Float64,3}, - n_f::AbstractMatrix{Float64}) where {d} - - minus_q_avg = -0.5*(q_in .+ q_out) - f_star .+= sum(conservation_law.b * minus_q_avg[:,:,m] .* n_f[m,:] - for m in 1:d) + conservation_law::ViscousBurgersEquation{d}, + ::BR1, + u_in::AbstractMatrix{Float64}, + u_out::AbstractMatrix{Float64}, + q_in::AbstractArray{Float64, 3}, + q_out::AbstractArray{Float64, 3}, + n_f::AbstractMatrix{Float64}) where {d} + minus_q_avg = -0.5 * (q_in .+ q_out) + f_star .+= sum(conservation_law.b * minus_q_avg[:, :, m] .* n_f[m, :] for m in 1:d) end -@inline entropy(::BurgersType, u) = 0.5*u[1]^2 +@inline entropy(::BurgersType, u) = 0.5 * u[1]^2 @inline function wave_speed(conservation_law::BurgersType{d}, - u_in::AbstractVector{Float64}, u_out::AbstractVector{Float64}, - n_f) where {d} - a_n = sum(conservation_law.a[m]*n_f[m] for m in 1:d) - return max.(abs.(a_n*u_in),abs.(a_n*u_out)) + u_in::AbstractVector{Float64}, + u_out::AbstractVector{Float64}, + n_f) where {d} + a_n = sum(conservation_law.a[m] * n_f[m] for m in 1:d) + return max.(abs.(a_n * u_in), abs.(a_n * u_out)) end -@inline function compute_two_point_flux(conservation_law::BurgersType{d}, - ::EntropyConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}) where {d} - flux_1d = (u_L[1]^2 + u_L[1]*u_R[1] + u_R[1]^2)/6 - return SMatrix{1,d}(conservation_law.a[m]*flux_1d for m in 1:d) +@inline function compute_two_point_flux(conservation_law::BurgersType{d}, + ::EntropyConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}) where {d} + flux_1d = (u_L[1]^2 + u_L[1] * u_R[1] + u_R[1]^2) / 6 + return SMatrix{1, d}(conservation_law.a[m] * flux_1d for m in 1:d) end -@inline function compute_two_point_flux(conservation_law::BurgersType{d}, - ::EntropyConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}, n::NTuple{d,Float64}) where {d} - flux_1d = (u_L[1]^2 + u_L[1]*u_R[1] + u_R[1]^2)/6 - return SVector{1}(n[m]*conservation_law.a[m]*flux_1d for m in 1:d) +@inline function compute_two_point_flux(conservation_law::BurgersType{d}, + ::EntropyConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}, + n::NTuple{d, Float64}) where {d} + flux_1d = (u_L[1]^2 + u_L[1] * u_R[1] + u_R[1]^2) / 6 + return SVector{1}(n[m] * conservation_law.a[m] * flux_1d for m in 1:d) end -@inline function compute_two_point_flux(conservation_law::BurgersType{d}, - ::ConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}) where {d} - flux_1d = (u_L[1]^2 + u_R[1]^2)*0.25 - return SMatrix{1,d}(conservation_law.a[m]*flux_1d for m in 1:d) +@inline function compute_two_point_flux(conservation_law::BurgersType{d}, + ::ConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}) where {d} + flux_1d = (u_L[1]^2 + u_R[1]^2) * 0.25 + return SMatrix{1, d}(conservation_law.a[m] * flux_1d for m in 1:d) end -@inline function compute_two_point_flux(conservation_law::BurgersType{d}, - ::ConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}, n::NTuple{d,Float64}) where {d} - flux_1d = (u_L[1]^2 + u_R[1]^2)*0.25 - return SVector{1}(n[m]*conservation_law.a[m]*flux_1d for m in 1:d) +@inline function compute_two_point_flux(conservation_law::BurgersType{d}, + ::ConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}, + n::NTuple{d, Float64}) where {d} + flux_1d = (u_L[1]^2 + u_R[1]^2) * 0.25 + return SVector{1}(n[m] * conservation_law.a[m] * flux_1d for m in 1:d) end -function evaluate( - exact_solution::ExactSolution{1,InviscidBurgersEquation{1},InitialDataGassner,SourceTermGassner}, - x::NTuple{1,Float64},t::Float64=0.0) +function evaluate(exact_solution::ExactSolution{1, + InviscidBurgersEquation{1}, + InitialDataGassner, + SourceTermGassner}, + x::NTuple{1, Float64}, + t::Float64 = 0.0) if !exact_solution.periodic z = x[1] - t else z = x[1] end - return [sin(exact_solution.initial_data.k*z) + - exact_solution.initial_data.ϵ] -end \ No newline at end of file + return [sin(exact_solution.initial_data.k * z) + exact_solution.initial_data.ϵ] +end diff --git a/src/ConservationLaws/euler_navierstokes.jl b/src/ConservationLaws/euler_navierstokes.jl index 442bafd9..1b6db252 100644 --- a/src/ConservationLaws/euler_navierstokes.jl +++ b/src/ConservationLaws/euler_navierstokes.jl @@ -23,28 +23,28 @@ P(\bm{x},t) = (\gamma - 1)\Big(E(\bm{x},t) - \frac{1}{2}\rho(\bm{x},t) \lVert \b ``` The specific heat ratio is specified as a parameter `γ::Float64`, which must be greater than unity. """ -struct EulerEquations{d,N_c} <: AbstractConservationLaw{d,FirstOrder,N_c} +struct EulerEquations{d, N_c} <: AbstractConservationLaw{d, FirstOrder, N_c} γ::Float64 γ_minus_1::Float64 inv_γ_minus_1::Float64 source_term::AbstractGridFunction{d} - function EulerEquations{d}(γ::Float64=1.4, - source_term::AbstractGridFunction{d}=NoSourceTerm{d}()) where {d} - return new{d,d+2}(γ,γ-1,1/(γ-1),source_term) + function EulerEquations{d}(γ::Float64 = 1.4, + source_term::AbstractGridFunction{d} = NoSourceTerm{d}()) where {d} + return new{d, d + 2}(γ, γ - 1, 1 / (γ - 1), source_term) end end -struct NavierStokesEquations{d,N_c} <: AbstractConservationLaw{d,SecondOrder,N_c} +struct NavierStokesEquations{d, N_c} <: AbstractConservationLaw{d, SecondOrder, N_c} γ::Float64 γ_minus_1::Float64 inv_γ_minus_1::Float64 source_term::AbstractGridFunction{d} - function NavierStokesEquations{d}(γ::Float64=1.4, - source_term::AbstractGridFunction{d}=NoSourceTerm{d}()) where {d} + function NavierStokesEquations{d}(γ::Float64 = 1.4, + source_term::AbstractGridFunction{d} = NoSourceTerm{d}()) where {d} @error "Navier-Stokes not implemented." - return new{d,N_c}(γ,γ-1,1/(γ-1),source_term) + return new{d, N_c}(γ, γ - 1, 1 / (γ - 1), source_term) end end @@ -53,202 +53,207 @@ const EulerType{d} = Union{EulerEquations{d}, NavierStokesEquations{d}} """ Evaluate the flux for the Euler equations """ -@inline function physical_flux(conservation_law::EulerType{d}, - u::AbstractVector{Float64}) where {d} - +@inline function physical_flux(conservation_law::EulerType{d}, + u::AbstractVector{Float64}) where {d} (; γ_minus_1) = conservation_law - V = SVector{d}(u[m+1] / u[1] for m in 1:d) - p = γ_minus_1 * (u[end] - 0.5*(sum(u[m+1]*V[m] for m in 1:d))) + V = SVector{d}(u[m + 1] / u[1] for m in 1:d) + p = γ_minus_1 * (u[end] - 0.5 * (sum(u[m + 1] * V[m] for m in 1:d))) h_t = u[end] + p - return vcat(SMatrix{1,d}(u[n+1] for n in 1:d), - SMatrix{d,d}(u[m+1]*V[n] + I[m,n]*p for m in 1:d, n in 1:d), - SMatrix{1,d}(h_t*V[n] for n in 1:d)) + return vcat(SMatrix{1, d}(u[n + 1] for n in 1:d), + SMatrix{d, d}(u[m + 1] * V[n] + I[m, n] * p for m in 1:d, n in 1:d), + SMatrix{1, d}(h_t * V[n] for n in 1:d)) end -@inline function physical_flux(conservation_law::EulerType{d}, - u::AbstractVector{Float64}, n_f::AbstractVector{Float64}) where {d} - +@inline function physical_flux(conservation_law::EulerType{d}, + u::AbstractVector{Float64}, + n_f::AbstractVector{Float64}) where {d} (; γ_minus_1) = conservation_law - V = SVector{d}(u[m+1] / u[1] for m in 1:d) - Vₙ = sum(V[m]*n_f[m] for m in 1:d) + V = SVector{d}(u[m + 1] / u[1] for m in 1:d) + Vₙ = sum(V[m] * n_f[m] for m in 1:d) - p = γ_minus_1 * (u[end] - 0.5*(sum(u[m+1]*V[m] for m in 1:d))) + p = γ_minus_1 * (u[end] - 0.5 * (sum(u[m + 1] * V[m] for m in 1:d))) - return SVector{d+2}( - u[1]*Vₙ, (u[m+1]*Vₙ + p*n_f[m] for m in 1:d)..., (u[end] + p)*Vₙ) + return SVector{d + 2}(u[1] * Vₙ, + (u[m + 1] * Vₙ + p * n_f[m] for m in 1:d)..., + (u[end] + p) * Vₙ) end -@inline @views function physical_flux!(f::AbstractArray{Float64,3}, - conservation_law::EulerType{d}, - u::AbstractMatrix{Float64}) where {d} - - @inbounds for i in axes(u,1) - f[i,:,:] .= physical_flux(conservation_law, u[i,:]) +@inline @views function physical_flux!(f::AbstractArray{Float64, 3}, + conservation_law::EulerType{d}, + u::AbstractMatrix{Float64}) where {d} + @inbounds for i in axes(u, 1) + f[i, :, :] .= physical_flux(conservation_law, u[i, :]) end end -@inline function entropy(conservation_law::EulerType{d}, - u::AbstractVector{Float64}) where {d} +@inline function entropy(conservation_law::EulerType{d}, + u::AbstractVector{Float64}) where {d} (; γ, γ_minus_1, inv_γ_minus_1) = conservation_law - p = γ_minus_1 * (u[end] - (0.5/u[1]) * (sum(u[m+1]^2 for m in 1:d))) - return -u[1]*log(p/(u[1]^γ))*inv_γ_minus_1 + p = γ_minus_1 * (u[end] - (0.5 / u[1]) * (sum(u[m + 1]^2 for m in 1:d))) + return -u[1] * log(p / (u[1]^γ)) * inv_γ_minus_1 end -@inline function conservative_to_entropy!( - w::AbstractVector{Float64}, conservation_law::EulerType{d}, - u::AbstractVector{Float64}) where {d} +@inline function conservative_to_entropy!(w::AbstractVector{Float64}, + conservation_law::EulerType{d}, + u::AbstractVector{Float64}) where {d} (; γ, γ_minus_1, inv_γ_minus_1) = conservation_law - k = (0.5/u[1]) * (sum(u[m+1]^2 for m in 1:d)) + k = (0.5 / u[1]) * (sum(u[m + 1]^2 for m in 1:d)) p = γ_minus_1 * (u[end] - k) - inv_p = 1.0/p - w[1] = inv_γ_minus_1*(γ-log(p/(u[1]^γ))) - k*inv_p - @inbounds for m in 1:d w[m+1] = u[m+1]*inv_p end - w[d+2] = -u[1]*inv_p + inv_p = 1.0 / p + w[1] = inv_γ_minus_1 * (γ - log(p / (u[1]^γ))) - k * inv_p + @inbounds for m in 1:d + w[m + 1] = u[m + 1] * inv_p + end + w[d + 2] = -u[1] * inv_p return end -@inline function entropy_to_conservative!( - u::AbstractVector{Float64}, - conservation_law::EulerType{d}, - w::AbstractVector{Float64}) where {d} - +@inline function entropy_to_conservative!(u::AbstractVector{Float64}, + conservation_law::EulerType{d}, + w::AbstractVector{Float64}) where {d} (; γ, γ_minus_1, inv_γ_minus_1) = conservation_law - + w = w * γ_minus_1 - k = sum(w[m+1]^2 for m in 1:d)/(2*w[d+2]) + k = sum(w[m + 1]^2 for m in 1:d) / (2 * w[d + 2]) s = γ - w[1] + k - ρe = (γ_minus_1/((-w[d+2])^γ))^inv_γ_minus_1*exp(-s*inv_γ_minus_1) - - u[1] = -w[d+2]*ρe - @inbounds for m in 1:d u[m+1] = w[m+1] * ρe end - u[d+2] = ρe*(1-k) + ρe = (γ_minus_1 / ((-w[d + 2])^γ))^inv_γ_minus_1 * exp(-s * inv_γ_minus_1) + + u[1] = -w[d + 2] * ρe + @inbounds for m in 1:d + u[m + 1] = w[m + 1] * ρe + end + u[d + 2] = ρe * (1 - k) return end @inline function wave_speed(conservation_law::EulerType{d}, - u_in::AbstractVector{Float64}, u_out::AbstractVector{Float64}, - n_f) where {d} - + u_in::AbstractVector{Float64}, + u_out::AbstractVector{Float64}, + n_f) where {d} (; γ, γ_minus_1) = conservation_law - V_in = SVector{d}(u_in[m+1] / u_in[1] for m in 1:d) - p_in = γ_minus_1 * (u_in[end] - (0.5/u_in[1]) * - (sum(u_in[m+1]^2 for m in 1:d))) - V_out = SVector{d}(u_out[m+1] / u_out[1] for m in 1:d) - p_out = γ_minus_1 * (u_out[end] - (0.5/u_out[1]) * - (sum(u_out[m+1]^2 for m in 1:d))) - Vn_in = sum(V_in[m] * n_f[m] for m in 1:d) + V_in = SVector{d}(u_in[m + 1] / u_in[1] for m in 1:d) + p_in = γ_minus_1 * (u_in[end] - (0.5 / u_in[1]) * (sum(u_in[m + 1]^2 for m in 1:d))) + V_out = SVector{d}(u_out[m + 1] / u_out[1] for m in 1:d) + p_out = γ_minus_1 * (u_out[end] - (0.5 / u_out[1]) * (sum(u_out[m + 1]^2 for m in 1:d))) + Vn_in = sum(V_in[m] * n_f[m] for m in 1:d) Vn_out = sum(V_out[m] * n_f[m] for m in 1:d) - - c_in = sqrt(γ*p_in / u_in[1]) - c_out = sqrt(γ*p_out / u_out[1]) + + c_in = sqrt(γ * p_in / u_in[1]) + c_out = sqrt(γ * p_out / u_out[1]) return max(abs(Vn_in), abs(Vn_out)) + max(c_in, c_out) end -@inline function compute_two_point_flux(conservation_law::EulerType{d}, - ::ConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}) where {d} - - return 0.5*(physical_flux(conservation_law, u_L) .+ - physical_flux(conservation_law, u_R)) +@inline function compute_two_point_flux(conservation_law::EulerType{d}, + ::ConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}) where {d} + return 0.5 * + (physical_flux(conservation_law, u_L) .+ physical_flux(conservation_law, u_R)) end -@inline function compute_two_point_flux(conservation_law::EulerType{d}, - ::ConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}, n_f::AbstractVector{Float64}) where {d} - - return 0.5*(physical_flux(conservation_law, u_L, n_f) .+ - physical_flux(conservation_law, u_R, n_f)) +@inline function compute_two_point_flux(conservation_law::EulerType{d}, + ::ConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}, + n_f::AbstractVector{Float64}) where {d} + return 0.5 * (physical_flux(conservation_law, u_L, n_f) .+ + physical_flux(conservation_law, u_R, n_f)) end """ Entropy-conservative, kinetic-energy-preserving, and pressure-equilibrium-preserving numerical flux from Ranocha (see his 2018 PhD thesis) """ -@inline function compute_two_point_flux(conservation_law::EulerType{d}, - ::EntropyConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}) where {d} - +@inline function compute_two_point_flux(conservation_law::EulerType{d}, + ::EntropyConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}) where {d} (; γ_minus_1, inv_γ_minus_1) = conservation_law # velocities and pressures - V_L = SVector{d}(u_L[m+1] / u_L[1] for m in 1:d) - V_R = SVector{d}(u_R[m+1] / u_R[1] for m in 1:d) - p_L = γ_minus_1 * (u_L[end] - 0.5*u_L[1]*(sum(V_L[m]^2 for m in 1:d))) - p_R = γ_minus_1 * (u_R[end] - 0.5*u_R[1]*(sum(V_R[m]^2 for m in 1:d))) + V_L = SVector{d}(u_L[m + 1] / u_L[1] for m in 1:d) + V_R = SVector{d}(u_R[m + 1] / u_R[1] for m in 1:d) + p_L = γ_minus_1 * (u_L[end] - 0.5 * u_L[1] * (sum(V_L[m]^2 for m in 1:d))) + p_R = γ_minus_1 * (u_R[end] - 0.5 * u_R[1] * (sum(V_R[m]^2 for m in 1:d))) # mean quantities ρ_avg = logmean(u_L[1], u_R[1]) - V_avg = 0.5*(V_L + V_R) - p_avg = 0.5*(p_L + p_R) - C = 0.5*sum(V_L[m]*V_R[m] for m in 1:d) + - inv_γ_minus_1*inv_logmean(u_L[1]/p_L, u_R[1]/p_R) + V_avg = 0.5 * (V_L + V_R) + p_avg = 0.5 * (p_L + p_R) + C = 0.5 * sum(V_L[m] * V_R[m] for m in 1:d) + + inv_γ_minus_1 * inv_logmean(u_L[1] / p_L, u_R[1] / p_R) # flux tensor - f_ρ = SMatrix{1,d}(ρ_avg*V_avg[n] for n in 1:d) - f_ρV = SMatrix{d,d}(f_ρ[m]*V_avg[n] + I[m,n]*p_avg for m in 1:d, n in 1:d) - f_E = SMatrix{1,d}(f_ρ[n]*C + 0.5*(p_L*V_R[n] + p_R*V_L[n]) for n in 1:d) + f_ρ = SMatrix{1, d}(ρ_avg * V_avg[n] for n in 1:d) + f_ρV = SMatrix{d, d}(f_ρ[m] * V_avg[n] + I[m, n] * p_avg for m in 1:d, n in 1:d) + f_E = SMatrix{1, d}(f_ρ[n] * C + 0.5 * (p_L * V_R[n] + p_R * V_L[n]) for n in 1:d) return vcat(f_ρ, f_ρV, f_E) end -@inline function compute_two_point_flux(conservation_law::EulerType{d}, - ::EntropyConservativeFlux, u_L::AbstractVector{Float64}, - u_R::AbstractVector{Float64}, n::AbstractVector{Float64}) where {d} - +@inline function compute_two_point_flux(conservation_law::EulerType{d}, + ::EntropyConservativeFlux, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}, + n::AbstractVector{Float64}) where {d} (; γ_minus_1, inv_γ_minus_1) = conservation_law # velocities and pressures - V_L = SVector{d}(u_L[m+1] / u_L[1] for m in 1:d) - V_R = SVector{d}(u_R[m+1] / u_R[1] for m in 1:d) - Vn_L = sum(V_L[m]*n[m] for m in 1:d) - Vn_R = sum(V_R[m]*n[m] for m in 1:d) - p_L = γ_minus_1 * (u_L[end] - 0.5*u_L[1]*(sum(V_L[m]^2 for m in 1:d))) - p_R = γ_minus_1 * (u_R[end] - 0.5*u_R[1]*(sum(V_R[m]^2 for m in 1:d))) + V_L = SVector{d}(u_L[m + 1] / u_L[1] for m in 1:d) + V_R = SVector{d}(u_R[m + 1] / u_R[1] for m in 1:d) + Vn_L = sum(V_L[m] * n[m] for m in 1:d) + Vn_R = sum(V_R[m] * n[m] for m in 1:d) + p_L = γ_minus_1 * (u_L[end] - 0.5 * u_L[1] * (sum(V_L[m]^2 for m in 1:d))) + p_R = γ_minus_1 * (u_R[end] - 0.5 * u_R[1] * (sum(V_R[m]^2 for m in 1:d))) # mean quantities ρ_avg = logmean(u_L[1], u_R[1]) - V_avg = 0.5*(V_L + V_R) - Vn_avg = 0.5*(Vn_L + Vn_R) - p_avg = 0.5*(p_L + p_R) - C = 0.5*sum(V_L[m]*V_R[m] for m in 1:d) + - inv_γ_minus_1*inv_logmean(u_L[1]/p_L, u_R[1]/p_R) + V_avg = 0.5 * (V_L + V_R) + Vn_avg = 0.5 * (Vn_L + Vn_R) + p_avg = 0.5 * (p_L + p_R) + C = 0.5 * sum(V_L[m] * V_R[m] for m in 1:d) + + inv_γ_minus_1 * inv_logmean(u_L[1] / p_L, u_R[1] / p_R) # flux vector - f_ρ = ρ_avg*Vn_avg - return SVector{d+2}(f_ρ, (f_ρ*V_avg[m] + p_avg*n[m] for m in 1:d)..., - f_ρ*C + 0.5*(p_L*Vn_R + p_R*Vn_L)) + f_ρ = ρ_avg * Vn_avg + return SVector{d + 2}(f_ρ, + (f_ρ * V_avg[m] + p_avg * n[m] for m in 1:d)..., + f_ρ * C + 0.5 * (p_L * Vn_R + p_R * Vn_L)) end -struct IsentropicVortex <: AbstractGridFunction{2} +struct IsentropicVortex <: AbstractGridFunction{2} γ::Float64 - Ma::Float64 + Ma::Float64 θ::Float64 R::Float64 β::Float64 σ²::Float64 - x_0::NTuple{2,Float64} + x_0::NTuple{2, Float64} N_c::Int - - function IsentropicVortex(conservation_law::EulerEquations{2}; - Ma::Float64=0.4, θ::Float64=π/4, R::Float64=1.0, - β::Float64=1.0, σ::Float64=1.0, x_0::NTuple{2,Float64}=(0.0,0.0)) - return new(conservation_law.γ,Ma, θ, R, β, σ^2, x_0, 4) + + function IsentropicVortex(conservation_law::EulerEquations{2}; + Ma::Float64 = 0.4, + θ::Float64 = π / 4, + R::Float64 = 1.0, + β::Float64 = 1.0, + σ::Float64 = 1.0, + x_0::NTuple{2, Float64} = (0.0, 0.0),) + return new(conservation_law.γ, Ma, θ, R, β, σ^2, x_0, 4) end end -@inline function evaluate(f::IsentropicVortex, x::NTuple{2,Float64}, - t::Float64=0.0) +@inline function evaluate(f::IsentropicVortex, x::NTuple{2, Float64}, t::Float64 = 0.0) (; γ, Ma, θ, R, β, σ², x_0) = f - x_rel = ((x[1] - x_0[1])/R, (x[2] - x_0[2])/R) - Ω = β*exp(-0.5/σ² * (x_rel[1]^2 + x_rel[2]^2)) - dv = (-x_rel[2]*Ω, x_rel[1]*Ω) - dT = -0.5*(γ-1)*Ω^2 - ρ = (1+dT)^(1/(γ-1)) - v = (Ma*cos(θ) + dv[1], Ma*sin(θ) + dv[2]) - p = (ρ^γ)/γ - E = p/(γ-1) + 0.5*ρ*(v[1]^2 + v[2]^2) - return SVector{4}(ρ, ρ*v[1], ρ*v[2], E) + x_rel = ((x[1] - x_0[1]) / R, (x[2] - x_0[2]) / R) + Ω = β * exp(-0.5 / σ² * (x_rel[1]^2 + x_rel[2]^2)) + dv = (-x_rel[2] * Ω, x_rel[1] * Ω) + dT = -0.5 * (γ - 1) * Ω^2 + ρ = (1 + dT)^(1 / (γ - 1)) + v = (Ma * cos(θ) + dv[1], Ma * sin(θ) + dv[2]) + p = (ρ^γ) / γ + E = p / (γ - 1) + 0.5 * ρ * (v[1]^2 + v[2]^2) + return SVector{4}(ρ, ρ * v[1], ρ * v[2], E) end """ @@ -259,46 +264,48 @@ Periodic wave test case used in the following papers: Domain should be [0,2]ᵈ. """ -struct EulerPeriodicTest{d} <: AbstractGridFunction{d} +struct EulerPeriodicTest{d} <: AbstractGridFunction{d} γ::Float64 strength::Float64 L::Float64 N_c::Int - function EulerPeriodicTest(conservation_law::EulerEquations{d}, - strength::Float64=0.2, L::Float64=2.0) where {d} - return new{d}(conservation_law.γ,strength,L,d+2) + function EulerPeriodicTest(conservation_law::EulerEquations{d}, + strength::Float64 = 0.2, + L::Float64 = 2.0) where {d} + return new{d}(conservation_law.γ, strength, L, d + 2) end end -@inline function evaluate(f::EulerPeriodicTest{d}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - - ρ = 1.0 + f.strength*sin(2π*sum(x[m] for m in 1:d)/f.L) - return SVector{d+2}(ρ, fill(ρ,d)..., 1.0/(f.γ-1.0) + 0.5*ρ*d) +@inline function evaluate(f::EulerPeriodicTest{d}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} + ρ = 1.0 + f.strength * sin(2π * sum(x[m] for m in 1:d) / f.L) + return SVector{d + 2}(ρ, fill(ρ, d)..., 1.0 / (f.γ - 1.0) + 0.5 * ρ * d) end """Inviscid 3D Taylor-Green vortex, I think I got this version of it from Shadpey and Zingg, "Entropy-Stable Multidimensional Summation-by-Parts Discretizations on hp-Adaptive Curvilinear Grids for Hyperbolic Conservation Laws," JSC 2020. Domain should be [0,2π]³. """ -struct TaylorGreenVortex <: AbstractGridFunction{3} +struct TaylorGreenVortex <: AbstractGridFunction{3} γ::Float64 Ma::Float64 N_c::Int - function TaylorGreenVortex(conservation_law::EulerEquations{3}, - Ma::Float64=0.1) - return new(conservation_law.γ,Ma,5) + function TaylorGreenVortex(conservation_law::EulerEquations{3}, Ma::Float64 = 0.1) + return new(conservation_law.γ, Ma, 5) end end -@inline function evaluate(f::TaylorGreenVortex, - x::NTuple{3,Float64}, t::Float64=0.0) - p = (1/(f.Ma^2*f.γ)) + 0.0625*(2*cos(2*x[1]) + 2*cos(2*x[2]) + - cos(2*x[1])*cos(2*x[3]) + cos(2*x[2])*cos(2*x[3])) - u = sin(x[1])*cos(x[2])*cos(x[3]) - v = -cos(x[1])*sin(x[2])*cos(x[3]) - return SVector{5}(1.0, u, v, 0.0, p/(f.γ-1) + 0.5*(u^2 + v^2)) +@inline function evaluate(f::TaylorGreenVortex, x::NTuple{3, Float64}, t::Float64 = 0.0) + p = (1 / (f.Ma^2 * f.γ)) + + 0.0625 * (2 * cos(2 * x[1]) + + 2 * cos(2 * x[2]) + + cos(2 * x[1]) * cos(2 * x[3]) + + cos(2 * x[2]) * cos(2 * x[3])) + u = sin(x[1]) * cos(x[2]) * cos(x[3]) + v = -cos(x[1]) * sin(x[2]) * cos(x[3]) + return SVector{5}(1.0, u, v, 0.0, p / (f.γ - 1) + 0.5 * (u^2 + v^2)) end """ @@ -310,18 +317,19 @@ struct KelvinHelmholtzInstability <: AbstractGridFunction{2} ρ_0::Float64 N_c::Int function KelvinHelmholtzInstability(conservation_law::EulerEquations{2}, - ρ_0::Float64=0.5) - return new(conservation_law.γ,ρ_0,4) + ρ_0::Float64 = 0.5) + return new(conservation_law.γ, ρ_0, 4) end end -@inline function evaluate(f::KelvinHelmholtzInstability, - x::NTuple{2,Float64}, t::Float64=0.0) - x_rel = (x[1]-1, x[2]-1) - B = tanh(15*x_rel[2] + 7.5) - tanh(15*x_rel[2] - 7.5) - ρ = f.ρ_0 + 0.75*B +@inline function evaluate(f::KelvinHelmholtzInstability, + x::NTuple{2, Float64}, + t::Float64 = 0.0) + x_rel = (x[1] - 1, x[2] - 1) + B = tanh(15 * x_rel[2] + 7.5) - tanh(15 * x_rel[2] - 7.5) + ρ = f.ρ_0 + 0.75 * B p = 1.0 - u = 0.5*(B-1) - v = 0.1*sin(2π*x_rel[1]) - return SVector{4}(ρ,ρ*u,ρ*v,p/(f.γ-1) + 0.5*ρ*(u^2 + v^2)) -end \ No newline at end of file + u = 0.5 * (B - 1) + v = 0.1 * sin(2π * x_rel[1]) + return SVector{4}(ρ, ρ * u, ρ * v, p / (f.γ - 1) + 0.5 * ρ * (u^2 + v^2)) +end diff --git a/src/ConservationLaws/linear_advection_diffusion.jl b/src/ConservationLaws/linear_advection_diffusion.jl index 43148147..3f55a8d9 100644 --- a/src/ConservationLaws/linear_advection_diffusion.jl +++ b/src/ConservationLaws/linear_advection_diffusion.jl @@ -7,11 +7,11 @@ Define a linear advection equation of the form ``` with a constant advection velocity $\bm{a} \in \R^d$. A specialized constructor `LinearAdvectionEquation(a::Float64)` is provided for the one-dimensional case. """ -struct LinearAdvectionEquation{d} <: AbstractConservationLaw{d,FirstOrder,1} - a::NTuple{d,Float64} +struct LinearAdvectionEquation{d} <: AbstractConservationLaw{d, FirstOrder, 1} + a::NTuple{d, Float64} source_term::AbstractGridFunction{d} - function LinearAdvectionEquation(a::NTuple{d,Float64}, - source_term::AbstractGridFunction{d}=NoSourceTerm{d}()) where {d} + function LinearAdvectionEquation(a::NTuple{d, Float64}, + source_term::AbstractGridFunction{d} = NoSourceTerm{d}()) where {d} return new{d}(a, source_term) end end @@ -25,36 +25,37 @@ Define a linear advection-diffusion equation of the form ``` with a constant advection velocity $\bm{a} \in \R^d$ and diffusion coefficient $b \in \R^+$. A specialized constructor `LinearAdvectionDiffusionEquation(a::Float64, b::Float64)` is provided for the one-dimensional case. """ -struct LinearAdvectionDiffusionEquation{d} <: AbstractConservationLaw{d,SecondOrder,1} - a::NTuple{d,Float64} +struct LinearAdvectionDiffusionEquation{d} <: AbstractConservationLaw{d, SecondOrder, 1} + a::NTuple{d, Float64} b::Float64 source_term::AbstractGridFunction{d} - function LinearAdvectionDiffusionEquation( - a::NTuple{d,Float64}, b::Float64, - source_term::AbstractGridFunction{d}=NoSourceTerm{d}()) where {d} + function LinearAdvectionDiffusionEquation(a::NTuple{d, Float64}, + b::Float64, + source_term::AbstractGridFunction{d} = NoSourceTerm{d}()) where {d} return new{d}(a, b, source_term) end end -const AdvectionType{d} = Union{LinearAdvectionEquation{d}, LinearAdvectionDiffusionEquation{d}} +const AdvectionType{d} = Union{LinearAdvectionEquation{d}, + LinearAdvectionDiffusionEquation{d}} LinearAdvectionEquation(a::Float64) = LinearAdvectionEquation((a,)) -LinearAdvectionDiffusionEquation(a::Float64, b::Float64) = LinearAdvectionDiffusionEquation((a,),b) +function LinearAdvectionDiffusionEquation(a::Float64, b::Float64) + LinearAdvectionDiffusionEquation((a,), b) +end """ Evaluate the flux for the linear advection equation `F(u) = au` """ -function physical_flux!( - f::AbstractArray{Float64,3}, - conservation_law::LinearAdvectionEquation{d}, - u::AbstractMatrix{Float64}) where {d} - +function physical_flux!(f::AbstractArray{Float64, 3}, + conservation_law::LinearAdvectionEquation{d}, + u::AbstractMatrix{Float64}) where {d} @inbounds for m in 1:d - f[:,:,m] .= conservation_law.a[m] .* u + f[:, :, m] .= conservation_law.a[m] .* u end return f end @@ -64,12 +65,12 @@ Evaluate the flux for the linear advection-diffusion equation `F(u,q) = au - bq` """ -function physical_flux!(f::AbstractArray{Float64,3}, - conservation_law::LinearAdvectionDiffusionEquation{d}, - u::AbstractMatrix{Float64}, q::AbstractArray{Float64,3}) where {d} - +function physical_flux!(f::AbstractArray{Float64, 3}, + conservation_law::LinearAdvectionDiffusionEquation{d}, + u::AbstractMatrix{Float64}, + q::AbstractArray{Float64, 3}) where {d} @inbounds for m in 1:d - f[:,:,m] .= conservation_law.a[m] .* u .- conservation_law.b .* q[:,:,m] + f[:, :, m] .= conservation_law.a[m] .* u .- conservation_law.b .* q[:, :, m] end end @@ -78,15 +79,16 @@ Evaluate the interface normal solution for the (advection-)diffusion equation us `U*(u⁻, u⁺, n) = ½(u⁻ + u⁺)n` """ -function numerical_flux!(u_nstar::AbstractArray{Float64,3}, - ::LinearAdvectionDiffusionEquation{d}, - ::BR1, u_in::AbstractMatrix{Float64}, u_out::AbstractMatrix{Float64}, - n_f::AbstractMatrix{Float64}) where {d} - - u_avg = 0.5*(u_in .+ u_out) +function numerical_flux!(u_nstar::AbstractArray{Float64, 3}, + ::LinearAdvectionDiffusionEquation{d}, + ::BR1, + u_in::AbstractMatrix{Float64}, + u_out::AbstractMatrix{Float64}, + n_f::AbstractMatrix{Float64}) where {d} + u_avg = 0.5 * (u_in .+ u_out) @inbounds for m in 1:d - u_nstar[:,:,m] .= u_avg.*n_f[m,:] + u_nstar[:, :, m] .= u_avg .* n_f[m, :] end end @@ -96,79 +98,96 @@ Evaluate the numerical flux for the (advection-)diffusion equation using the BR1 `F*(u⁻, u⁺, q⁻, q⁺, n) = ½(F²(u⁻,q⁻) + F²(u⁺, q⁺))⋅n` """ function numerical_flux!(f_star::AbstractMatrix{Float64}, - conservation_law::LinearAdvectionDiffusionEquation{d}, - ::BR1, u_in::AbstractMatrix{Float64}, u_out::AbstractMatrix{Float64}, - q_in::AbstractArray{Float64,3}, q_out::AbstractArray{Float64,3}, - n_f::AbstractMatrix{Float64}) where {d} + conservation_law::LinearAdvectionDiffusionEquation{d}, + ::BR1, + u_in::AbstractMatrix{Float64}, + u_out::AbstractMatrix{Float64}, + q_in::AbstractArray{Float64, 3}, + q_out::AbstractArray{Float64, 3}, + n_f::AbstractMatrix{Float64}) where {d} # average both sides - minus_q_avg = -0.5*(q_in .+ q_out) - f_star .+= sum(conservation_law.b * minus_q_avg[:,:,m] .* n_f[m,:] - for m in 1:d) + minus_q_avg = -0.5 * (q_in .+ q_out) + f_star .+= sum(conservation_law.b * minus_q_avg[:, :, m] .* n_f[m, :] for m in 1:d) end -@inline entropy(::AdvectionType, u) = 0.5*u[1]^2 +@inline entropy(::AdvectionType, u) = 0.5 * u[1]^2 @inline function wave_speed(conservation_law::AdvectionType{d}, - ::AbstractVector{Float64}, ::AbstractVector{Float64}, n_f) where {d} - return abs(sum(conservation_law.a[m]*n_f[m] for m in 1:d)) + ::AbstractVector{Float64}, + ::AbstractVector{Float64}, + n_f) where {d} + return abs(sum(conservation_law.a[m] * n_f[m] for m in 1:d)) end -@inline function compute_two_point_flux(conservation_law::AdvectionType{d}, - ::Union{EntropyConservativeFlux,ConservativeFlux}, - u_L::AbstractVector{Float64}, u_R::AbstractVector{Float64}) where {d} - flux_1d = 0.5*(u_L[1] + u_R[1]) - return SMatrix{1,d}(conservation_law.a[m]*flux_1d for m in 1:d) +@inline function compute_two_point_flux(conservation_law::AdvectionType{d}, + ::Union{EntropyConservativeFlux, ConservativeFlux}, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}) where {d} + flux_1d = 0.5 * (u_L[1] + u_R[1]) + return SMatrix{1, d}(conservation_law.a[m] * flux_1d for m in 1:d) end -@inline function compute_two_point_flux(conservation_law::AdvectionType{d}, - ::Union{EntropyConservativeFlux,ConservativeFlux}, - u_L::AbstractVector{Float64}, u_R::AbstractVector{Float64}, - n::NTuple{d,Float64}) where {d} - flux_1d = 0.5*(u_L[1] + u_R[1]) - return SVector{1}(sum(n[m]*conservation_law.a[m]*flux_1d for m in 1:d)) +@inline function compute_two_point_flux(conservation_law::AdvectionType{d}, + ::Union{EntropyConservativeFlux, ConservativeFlux}, + u_L::AbstractVector{Float64}, + u_R::AbstractVector{Float64}, + n::NTuple{d, Float64}) where {d} + flux_1d = 0.5 * (u_L[1] + u_R[1]) + return SVector{1}(sum(n[m] * conservation_law.a[m] * flux_1d for m in 1:d)) end -function evaluate( - exact_solution::ExactSolution{d,LinearAdvectionEquation{d}, <:AbstractGridFunction{d},NoSourceTerm{d}}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - +function evaluate(exact_solution::ExactSolution{d, + LinearAdvectionEquation{d}, + <:AbstractGridFunction{d}, + NoSourceTerm{d}}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} (; initial_data, conservation_law) = exact_solution - - if !exact_solution.periodic - z = Tuple(x[m] - conservation_law.a[m]*t for m in 1:d) - else - z = x + + if !exact_solution.periodic + z = Tuple(x[m] - conservation_law.a[m] * t for m in 1:d) + else + z = x end - return evaluate(initial_data,z) + return evaluate(initial_data, z) end -function evaluate( - exact_solution::ExactSolution{d,LinearAdvectionDiffusionEquation{d}, InitialDataGaussian{d},NoSourceTerm{d}}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - +function evaluate(exact_solution::ExactSolution{d, + LinearAdvectionDiffusionEquation{d}, + InitialDataGaussian{d}, + NoSourceTerm{d}}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} (; A, σ, x₀) = exact_solution.initial_data (; a, b) = exact_solution.conservation_law - if !exact_solution.periodic z = Tuple(x[m] - a[m]*t for m in 1:d) - else z = x end + if !exact_solution.periodic + z = Tuple(x[m] - a[m] * t for m in 1:d) + else + z = x + end - r² = sum((z[m] - x₀[m]).^2 for m in 1:d) - t₀ = σ^2/(2.0*b) - return SVector{1}(A*(t₀/(t+t₀))^(0.5*d)*exp.(-r²/(4.0*b*(t₀ + t)))) + r² = sum((z[m] - x₀[m]) .^ 2 for m in 1:d) + t₀ = σ^2 / (2.0 * b) + return SVector{1}(A * (t₀ / (t + t₀))^(0.5 * d) * exp.(-r² / (4.0 * b * (t₀ + t)))) end -function evaluate( - exact_solution::ExactSolution{d,LinearAdvectionDiffusionEquation{d}, InitialDataSine{d},NoSourceTerm{d}}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - +function evaluate(exact_solution::ExactSolution{d, + LinearAdvectionDiffusionEquation{d}, + InitialDataSine{d}, + NoSourceTerm{d}}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} (; k) = exact_solution.initial_data (; a, b) = exact_solution.conservation_law - if !exact_solution.periodic z = Tuple(x[m] - a[m]*t for m in 1:d) - else z = x end + if !exact_solution.periodic + z = Tuple(x[m] - a[m] * t for m in 1:d) + else + z = x + end - return evaluate(exact_solution.initial_data,z) * - exp(-b*sum(k[m]^2 for m in 1:d)*t) + return evaluate(exact_solution.initial_data, z) * exp(-b * sum(k[m]^2 for m in 1:d) * t) end diff --git a/src/File/File.jl b/src/File/File.jl index e6a42a05..a36fe808 100644 --- a/src/File/File.jl +++ b/src/File/File.jl @@ -1,18 +1,25 @@ module File - using JLD2: save, load, save_object, load_object - using OrdinaryDiffEq: ODEIntegrator, ODEProblem, ODESolution, DiscreteCallback, CallbackSet, get_du - using DiffEqCallbacks: PresetTimeCallback +using JLD2: save, load, save_object, load_object +using OrdinaryDiffEq: + ODEIntegrator, ODEProblem, ODESolution, DiscreteCallback, CallbackSet, + get_du +using DiffEqCallbacks: PresetTimeCallback - using ..ConservationLaws: AbstractConservationLaw - using ..SpatialDiscretizations: SpatialDiscretization - using ..GridFunctions: AbstractGridFunction - using ..Solvers: AbstractResidualForm, Solver, get_dof, semi_discrete_residual! +using ..ConservationLaws: AbstractConservationLaw +using ..SpatialDiscretizations: SpatialDiscretization +using ..GridFunctions: AbstractGridFunction +using ..Solvers: AbstractResidualForm, Solver, get_dof, semi_discrete_residual! - export new_path, save_callback, save_project, save_solution - include("save.jl") - - export load_solution, load_project, load_time_steps, load_snapshots, load_snapshots_with_derivatives, load_solver - include("load.jl") +export new_path, save_callback, save_project, save_solution +include("save.jl") + +export load_solution, + load_project, + load_time_steps, + load_snapshots, + load_snapshots_with_derivatives, + load_solver +include("load.jl") end diff --git a/src/File/load.jl b/src/File/load.jl index 4114c7c8..91141f8a 100644 --- a/src/File/load.jl +++ b/src/File/load.jl @@ -1,72 +1,69 @@ -function load_solution(results_path::String, time_step::Union{Int,String}=0; - load_du=false) +function load_solution(results_path::String, + time_step::Union{Int, String} = 0; + load_du = false,) dict = load(string(results_path, "sol_", time_step, ".jld2")) if load_du return dict["u"], dict["du"], dict["t"] else - return dict["u"], dict["t"] + return dict["u"], dict["t"] end end function load_project(results_path::String) - dict = load(string(results_path,"project.jld2")) - return (dict["conservation_law"], - dict["spatial_discretization"], - dict["initial_data"], - dict["form"], - dict["tspan"]) + dict = load(string(results_path, "project.jld2")) + return (dict["conservation_law"], + dict["spatial_discretization"], + dict["initial_data"], + dict["form"], + dict["tspan"]) end function load_solver(results_path::String) - dict = load(string(results_path,"project.jld2")) - return Solver(dict["conservation_law"],dict["spatial_discretization"], - dict["form"],dict["strategy"]) + dict = load(string(results_path, "project.jld2")) + return Solver(dict["conservation_law"], + dict["spatial_discretization"], + dict["form"], + dict["strategy"]) end function load_time_steps(results_path::String) return load_object(string(results_path, "time_steps.jld2")) end -function load_snapshots(results_path::String, time_steps::Vector{Int}, du=false) - - dict = load(string(results_path,"project.jld2")) - N_p, N_c, N_e = get_dof(dict["spatial_discretization"], - dict["conservation_law"]) - N_dof = N_p*N_c*N_e +function load_snapshots(results_path::String, time_steps::Vector{Int}, du = false) + dict = load(string(results_path, "project.jld2")) + N_p, N_c, N_e = get_dof(dict["spatial_discretization"], dict["conservation_law"]) + N_dof = N_p * N_c * N_e N_t = length(time_steps) X = Matrix{Float64}(undef, N_dof, N_t) - times = Vector{Float64}(undef,N_t) + times = Vector{Float64}(undef, N_t) for i in 1:N_t u, times[i] = load_solution(results_path, time_steps[i]) - X[:,i] = vec(u) + X[:, i] = vec(u) end - t_s = (times[N_t] - times[1])/(N_t - 1.0) - + t_s = (times[N_t] - times[1]) / (N_t - 1.0) + return X, t_s end -function load_snapshots_with_derivatives(results_path::String, - time_steps::Vector{Int}) - - dict = load(string(results_path,"project.jld2")) - N_p, N_c, N_e = get_dof(dict["spatial_discretization"], - dict["conservation_law"]) - N_dof = N_p*N_c*N_e +function load_snapshots_with_derivatives(results_path::String, time_steps::Vector{Int}) + dict = load(string(results_path, "project.jld2")) + N_p, N_c, N_e = get_dof(dict["spatial_discretization"], dict["conservation_law"]) + N_dof = N_p * N_c * N_e N_t = length(time_steps) U = Matrix{Float64}(undef, N_dof, N_t) dU = Matrix{Float64}(undef, N_dof, N_t) - times = Vector{Float64}(undef,N_t) + times = Vector{Float64}(undef, N_t) for i in 1:N_t - u, du, times[i] = load_solution(results_path, time_steps[i], - load_du=true) - U[:,i] = vec(u) - dU[:,i] = vec(du) + u, du, times[i] = load_solution(results_path, time_steps[i], load_du = true) + U[:, i] = vec(u) + dU[:, i] = vec(du) end - t_s = (times[N_t] - times[1])/(N_t - 1.0) - + t_s = (times[N_t] - times[1]) / (N_t - 1.0) + return U, dU, t_s -end \ No newline at end of file +end diff --git a/src/File/save.jl b/src/File/save.jl index 27dddab0..3ca095ed 100644 --- a/src/File/save.jl +++ b/src/File/save.jl @@ -1,12 +1,10 @@ -function new_path(results_path::String, - overwrite::Bool=false, clear::Bool=false) - +function new_path(results_path::String, overwrite::Bool = false, clear::Bool = false) if !isdir(results_path) path = results_path elseif overwrite - if clear - rm(results_path, force=true, recursive=true) - end + if clear + rm(results_path, force = true, recursive = true) + end path = results_path else dir_exists = true @@ -16,76 +14,76 @@ function new_path(results_path::String, dir_exists = isdir(path) suffix = suffix + 1 end - end + end mkpath(path) return path end -function save_project( - @nospecialize(conservation_law::AbstractConservationLaw), - @nospecialize(spatial_discretization::SpatialDiscretization), - initial_data, @nospecialize(form::AbstractResidualForm), - tspan::NTuple{2,Float64}, - results_path::String; - overwrite=false, - clear=false) - +function save_project(@nospecialize(conservation_law::AbstractConservationLaw), + @nospecialize(spatial_discretization::SpatialDiscretization), + initial_data, + @nospecialize(form::AbstractResidualForm), + tspan::NTuple{2, Float64}, + results_path::String; + overwrite = false, + clear = false,) results_path = new_path(results_path, overwrite, clear) - save(string(results_path, "project.jld2"), - Dict("conservation_law" => conservation_law, - "spatial_discretization" => spatial_discretization, - "initial_data" => initial_data, - "form" => form, - "tspan" => tspan)) - + save(string(results_path, "project.jld2"), + Dict("conservation_law" => conservation_law, + "spatial_discretization" => spatial_discretization, + "initial_data" => initial_data, + "form" => form, + "tspan" => tspan)) + save_object(string(results_path, "time_steps.jld2"), Int64[]) return results_path end -function save_solution(integrator::ODEIntegrator, - results_path::String, restart_step::Int=0) - +function save_solution(integrator::ODEIntegrator, + results_path::String, + restart_step::Int = 0) time_step = integrator.iter + restart_step file_name = string(results_path, "sol_", time_step, ".jld2") if !isfile(file_name) - open(string(results_path,"screen.txt"), "a") do io + open(string(results_path, "screen.txt"), "a") do io println(io, "writing time step ", time_step, " t = ", integrator.t) end - save(file_name, Dict( - "u" => integrator.u, - "t" => integrator.t, - "du" => semi_discrete_residual!(similar(integrator.u), - integrator.u, integrator.p, integrator.t))) + save(file_name, + Dict("u" => integrator.u, + "t" => integrator.t, + "du" => semi_discrete_residual!(similar(integrator.u), + integrator.u, + integrator.p, + integrator.t))) time_steps = load_object(string(results_path, "time_steps.jld2")) - save_object(string(results_path, "time_steps.jld2"), - push!(time_steps, time_step)) + save_object(string(results_path, "time_steps.jld2"), push!(time_steps, time_step)) end end -function save_solution(u::Array{Float64,3}, t::Float64, results_path::String, - time_step::Union{Int,String}=0) - save(string(results_path, "sol_", time_step, ".jld2"), - Dict("u" => u, "t" => t)) +function save_solution(u::Array{Float64, 3}, + t::Float64, + results_path::String, + time_step::Union{Int, String} = 0) + save(string(results_path, "sol_", time_step, ".jld2"), Dict("u" => u, "t" => t)) if time_step isa Int - time_steps=load_object(string(results_path, "time_steps.jld2")) - save_object(string(results_path, "time_steps.jld2"), - push!(time_steps, time_step)) + time_steps = load_object(string(results_path, "time_steps.jld2")) + save_object(string(results_path, "time_steps.jld2"), push!(time_steps, time_step)) end end -function save_callback(results_path::String, - tspan::NTuple{2,Float64}, interval::Int=1, restart_step::Int=0) - - condition(u,t,integrator) = ((integrator.iter + restart_step) % interval) == 0 +function save_callback(results_path::String, + tspan::NTuple{2, Float64}, + interval::Int = 1, + restart_step::Int = 0) + condition(u, t, integrator) = ((integrator.iter + restart_step) % interval) == 0 affect!(integrator) = save_solution(integrator, results_path, restart_step) - return CallbackSet( - DiscreteCallback(condition, affect!, save_positions=(true,false)), - PresetTimeCallback([tspan[1],tspan[2]],affect!, - save_positions = (true,false))) + return CallbackSet(DiscreteCallback(condition, affect!, save_positions = (true, false)), + PresetTimeCallback([tspan[1], tspan[2]], affect!, + save_positions = (true, false))) end diff --git a/src/GridFunctions/GridFunctions.jl b/src/GridFunctions/GridFunctions.jl index cd2824fc..b6355e7f 100644 --- a/src/GridFunctions/GridFunctions.jl +++ b/src/GridFunctions/GridFunctions.jl @@ -1,207 +1,216 @@ module GridFunctions - using StaticArrays - - export AbstractGridFunction, ConstantFunction, InitialDataSine, InitialDataCosine, InitialDataGaussian, InitialDataSinCos, DerivativeSinCos, InitialDataGassner, BurgersSolution, SourceTermGassner, GaussianNoise, NoSourceTerm, evaluate - - abstract type AbstractGridFunction{d} end - - struct ConstantFunction{d} <: AbstractGridFunction{d} - c::Float64 - N_c::Int - - function ConstantFunction{d}(c) where {d} - return new{d}(c,1) - end +using StaticArrays + +export AbstractGridFunction, + ConstantFunction, + InitialDataSine, + InitialDataCosine, + InitialDataGaussian, + InitialDataSinCos, + DerivativeSinCos, + InitialDataGassner, + BurgersSolution, + SourceTermGassner, + GaussianNoise, + NoSourceTerm, + evaluate + +abstract type AbstractGridFunction{d} end + +struct ConstantFunction{d} <: AbstractGridFunction{d} + c::Float64 + N_c::Int + + function ConstantFunction{d}(c) where {d} + return new{d}(c, 1) end +end - struct InitialDataSine{d} <: AbstractGridFunction{d} - A::Float64 # amplitude - k::NTuple{d,Float64} # wave number in each direction - N_c::Int +struct InitialDataSine{d} <: AbstractGridFunction{d} + A::Float64 # amplitude + k::NTuple{d, Float64} # wave number in each direction + N_c::Int - function InitialDataSine(A::Float64, - k::NTuple{d,Float64}) where {d} - return new{d}(A,k,1) - end + function InitialDataSine(A::Float64, k::NTuple{d, Float64}) where {d} + return new{d}(A, k, 1) end +end - struct InitialDataCosine{d} <: AbstractGridFunction{d} - A::Float64 # amplitude - k::NTuple{d,Float64} # wave number in each direction - N_c::Int +struct InitialDataCosine{d} <: AbstractGridFunction{d} + A::Float64 # amplitude + k::NTuple{d, Float64} # wave number in each direction + N_c::Int - function InitialDataCosine(A::Float64, - k::NTuple{d,Float64}) where {d} - return new{d}(A,k,1) - end + function InitialDataCosine(A::Float64, k::NTuple{d, Float64}) where {d} + return new{d}(A, k, 1) end +end - struct InitialDataGaussian{d} <: AbstractGridFunction{d} - A::Float64 # amplitude - σ::Float64 # width - x₀::NTuple{d,Float64} - N_c::Int - function InitialDataGaussian(A::Float64,σ::Float64, - x₀::NTuple{d,Float64}) where {d} - return new{d}(A,σ,x₀,1) - end +struct InitialDataGaussian{d} <: AbstractGridFunction{d} + A::Float64 # amplitude + σ::Float64 # width + x₀::NTuple{d, Float64} + N_c::Int + function InitialDataGaussian(A::Float64, σ::Float64, x₀::NTuple{d, Float64}) where {d} + return new{d}(A, σ, x₀, 1) end +end - struct InitialDataGassner <: AbstractGridFunction{1} - k::Float64 - ϵ::Float64 - N_c::Int +struct InitialDataGassner <: AbstractGridFunction{1} + k::Float64 + ϵ::Float64 + N_c::Int - function InitialDataGassner(k,ϵ) - return new(k,ϵ,1) - end + function InitialDataGassner(k, ϵ) + return new(k, ϵ, 1) end +end + +struct SourceTermGassner <: AbstractGridFunction{1} + k::Float64 + ϵ::Float64 + N_c::Int - struct SourceTermGassner <: AbstractGridFunction{1} - k::Float64 - ϵ::Float64 - N_c::Int - - function SourceTermGassner(k,ϵ) - return new(k,ϵ,1) - end + function SourceTermGassner(k, ϵ) + return new(k, ϵ, 1) end +end +struct InitialDataSinCos <: AbstractGridFunction{2} + A::Float64 # amplitude + k::NTuple{2, Float64} # wave number in each direction + N_c::Int - struct InitialDataSinCos <: AbstractGridFunction{2} - A::Float64 # amplitude - k::NTuple{2,Float64} # wave number in each direction - N_c::Int + function InitialDataSinCos(A::Float64, k::NTuple{2, Float64}) + return new(A, k, 1) + end +end - function InitialDataSinCos(A::Float64, - k::NTuple{2,Float64}) - return new(A,k,1) - end +struct DerivativeSinCos <: AbstractGridFunction{2} + A::Float64 # amplitude + k::NTuple{2, Float64} # wave number in each direction + N_c::Int + + function DerivativeSinCos(A::Float64, k::NTuple{2, Float64}) + return new(A, k, 1) end +end +struct GaussianNoise{d} <: AbstractGridFunction{d} + σ::Float64 + N_c::Int +end - struct DerivativeSinCos <: AbstractGridFunction{2} - A::Float64 # amplitude - k::NTuple{2,Float64} # wave number in each direction - N_c::Int +struct NoSourceTerm{d} <: AbstractGridFunction{d} end - function DerivativeSinCos(A::Float64, - k::NTuple{2,Float64}) - return new(A,k,1) - end +@inline function InitialDataSine(A::Float64, k::Float64) + return InitialDataSine(A, (k,)) end - struct GaussianNoise{d} <: AbstractGridFunction{d} - σ::Float64 - N_c::Int - end - - struct NoSourceTerm{d} <: AbstractGridFunction{d} end - @inline function InitialDataSine(A::Float64, k::Float64) - return InitialDataSine(A,(k,)) - end +@inline function InitialDataCosine(A::Float64, k::Float64) + return InitialDataCosine(A, (k,)) +end - @inline function InitialDataCosine(A::Float64, k::Float64) - return InitialDataCosine(A,(k,)) - end +@inline function InitialDataGaussian(A::Float64, σ::Float64, x₀::Float64) + return InitialDataGaussian(A, σ, (x₀,)) +end - @inline function InitialDataGaussian(A::Float64, σ::Float64, x₀::Float64) - return InitialDataGaussian(A,σ,(x₀,)) - end +@inline function evaluate(f::ConstantFunction{d}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} + return fill(f.c, f.N_c) +end - @inline function evaluate(f::ConstantFunction{d}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - return fill(f.c, f.N_c) - end +@inline function evaluate(f::InitialDataSine{d}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} + return fill(f.A * prod(Tuple(sin(f.k[m] * x[m]) for m in 1:d)), f.N_c) +end - @inline function evaluate(f::InitialDataSine{d}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - return fill(f.A*prod(Tuple(sin(f.k[m]*x[m]) for m in 1:d)), f.N_c) - end +@inline function evaluate(f::InitialDataCosine{d}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} + return fill(f.A * prod(Tuple(cos(f.k[m] * x[m]) for m in 1:d)), f.N_c) +end - @inline function evaluate(f::InitialDataCosine{d}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - return fill(f.A*prod(Tuple(cos(f.k[m]*x[m]) for m in 1:d)), f.N_c) - end +@inline function evaluate(f::InitialDataGaussian{d}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} + (; A, σ, x₀, N_c) = f + r² = sum((x[m] - x₀[m]) .^ 2 for m in 1:d) + return fill(A * exp.(-r² / (2.0 * σ^2)), N_c) +end - @inline function evaluate(f::InitialDataGaussian{d}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - (; A, σ, x₀, N_c) = f - r² = sum((x[m] - x₀[m]).^2 for m in 1:d) - return fill(A*exp.(-r²/(2.0*σ^2)),N_c) - end +@inline function evaluate(f::InitialDataGassner, x::NTuple{1, Float64}, t::Float64 = 0.0) + return [sin(f.k * x[1]) + f.ϵ] +end - @inline function evaluate(f::InitialDataGassner, - x::NTuple{1,Float64},t::Float64=0.0) - return [sin(f.k*x[1])+f.ϵ] - end +@inline function evaluate(f::SourceTermGassner, x::NTuple{1, Float64}, t::Float64 = 0.0) + return SVector{1}(f.k .* cos(f.k * (x[1] - t)) * (-1.0 + f.ϵ + sin(f.k * (x[1] - t)))) +end - @inline function evaluate(f::SourceTermGassner, - x::NTuple{1,Float64},t::Float64=0.0) - return SVector{1}( - f.k .* cos(f.k*(x[1]-t))*(-1.0 + f.ϵ + sin(f.k*(x[1]-t)))) - end +@inline function evaluate(f::GaussianNoise{d}, + x::NTuple{d, Float64}, + t::Float64 = 0.0) where {d} + return [f.σ * randn() for e in 1:(f.N_c)] +end - @inline function evaluate(f::GaussianNoise{d}, - x::NTuple{d,Float64},t::Float64=0.0) where {d} - return [f.σ*randn() for e in 1:f.N_c] +@inline function evaluate(f::AbstractGridFunction{d}, + x::NTuple{d, AbstractVector{Float64}}, + t::Float64 = 0.0) where {d} + N = length(x[1]) + u0 = Matrix{Float64}(undef, N, f.N_c) + @inbounds for i in 1:N + u0[i, :] .= evaluate(f, Tuple(x[m][i] for m in 1:d), t) end + return u0 +end - @inline function evaluate(f::AbstractGridFunction{d}, - x::NTuple{d,AbstractVector{Float64}}, t::Float64=0.0) where {d} - N = length(x[1]) - u0 = Matrix{Float64}(undef, N, f.N_c) - @inbounds for i in 1:N - u0[i,:] .= evaluate(f, Tuple(x[m][i] for m in 1:d),t) - end - return u0 +function evaluate(f::AbstractGridFunction{d}, + x::NTuple{d, AbstractMatrix{Float64}}, + t::Float64 = 0.0) where {d} + N, N_e = size(x[1]) + u0 = Array{Float64}(undef, N, f.N_c, N_e) + @inbounds for k in 1:N_e + u0[:, :, k] .= evaluate(f, Tuple(x[m][:, k] for m in 1:d), t) end + return u0 +end - function evaluate(f::AbstractGridFunction{d}, - x::NTuple{d,AbstractMatrix{Float64}}, - t::Float64=0.0) where {d} - N, N_e = size(x[1]) - u0 = Array{Float64}(undef, N, f.N_c, N_e) - @inbounds for k in 1:N_e - u0[:,:,k] .= evaluate(f, Tuple(x[m][:,k] for m in 1:d),t) - end - return u0 - end +function evaluate(f::Function, + x::NTuple{d, AbstractMatrix{Float64}}, + t::Float64 = 0.0) where {d} + u0 = Array{Float64}(undef, + size(x[1], 1), + length(f(Tuple(0.0 for m in 1:d)..., t)), + size(x[1], 2)) + @inbounds for k in axes(x[1], 2), i in axes(x[1], 1) + u0[i, :, k] .= f(Tuple(x[m][i, k] for m in 1:d)..., t) + end + return u0 +end - function evaluate(f::Function, - x::NTuple{d,AbstractMatrix{Float64}}, - t::Float64=0.0) where {d} - u0 = Array{Float64}(undef, size(x[1],1), - length(f(Tuple(0.0 for m in 1:d)...,t)), size(x[1],2)) - @inbounds for k in axes(x[1],2), i in axes(x[1],1) - u0[i,:,k] .= f(Tuple(x[m][i,k] for m in 1:d)...,t) - end - return u0 +function evaluate(f::Function, + x::NTuple{d, AbstractVector{Float64}}, + t::Float64 = 0.0) where {d} + u0 = Matrix{Float64}(undef, length(x[1]), length(f(Tuple(0.0 for m in 1:d)..., t))) + @inbounds for i in eachindex(x[1]) + u0[i, :] .= f(Tuple(x[m][i] for m in 1:d)..., t) end + return u0 +end - function evaluate(f::Function, - x::NTuple{d,AbstractVector{Float64}}, - t::Float64=0.0) where {d} - u0 = Matrix{Float64}(undef, length(x[1]), - length(f(Tuple(0.0 for m in 1:d)...,t))) - @inbounds for i in eachindex(x[1]) - u0[i,:] .= f(Tuple(x[m][i] for m in 1:d)...,t) - end - return u0 - end +@inline function evaluate(::NoSourceTerm{d}, + ::NTuple{d, Vector{Float64}}, + ::Float64) where {d} + return nothing +end - @inline function evaluate(::NoSourceTerm{d}, ::NTuple{d,Vector{Float64}}, - ::Float64) where {d} - return nothing - end +@inline function evaluate(f::InitialDataSinCos, x::NTuple{2, Float64}, t::Float64 = 0.0) + return f.A * sin(f.k[1] * x[1]) * cos(f.k[2] * x[2]) +end - @inline function evaluate(f::InitialDataSinCos, - x::NTuple{2,Float64},t::Float64=0.0) - return f.A*sin(f.k[1]*x[1])*cos(f.k[2]*x[2]) - end - - @inline function evaluate(f::DerivativeSinCos, - x::NTuple{2,Float64},t::Float64=0.0) - return f.A*f.k[1]*cos(f.k[1]*x[1])*cos(f.k[2]*x[2]) - end -end \ No newline at end of file +@inline function evaluate(f::DerivativeSinCos, x::NTuple{2, Float64}, t::Float64 = 0.0) + return f.A * f.k[1] * cos(f.k[1] * x[1]) * cos(f.k[2] * x[2]) +end +end diff --git a/src/MatrixFreeOperators/MatrixFreeOperators.jl b/src/MatrixFreeOperators/MatrixFreeOperators.jl index 0e73b396..517750dc 100644 --- a/src/MatrixFreeOperators/MatrixFreeOperators.jl +++ b/src/MatrixFreeOperators/MatrixFreeOperators.jl @@ -1,72 +1,83 @@ module MatrixFreeOperators - using LinearAlgebra, LinearMaps, MuladdMacro, StaticArrays, Octavian, TimerOutputs - export AbstractOperatorAlgorithm, BLASAlgorithm, GenericMatrixAlgorithm, GenericTensorProductAlgorithm, DefaultOperatorAlgorithm, SelectionMap, make_operator - - abstract type AbstractOperatorAlgorithm end - struct DefaultOperatorAlgorithm <: AbstractOperatorAlgorithm end - struct BLASAlgorithm <: AbstractOperatorAlgorithm end - struct GenericMatrixAlgorithm <: AbstractOperatorAlgorithm end - struct GenericTensorProductAlgorithm <: AbstractOperatorAlgorithm end +using LinearAlgebra, LinearMaps, MuladdMacro, StaticArrays, Octavian, TimerOutputs +export AbstractOperatorAlgorithm, + BLASAlgorithm, + GenericMatrixAlgorithm, + GenericTensorProductAlgorithm, + DefaultOperatorAlgorithm, + SelectionMap, + make_operator - export WarpedTensorProductMap2D, WarpedTensorProductMap3D - include("warped_product_2d.jl") - include("warped_product_3d.jl") +abstract type AbstractOperatorAlgorithm end +struct DefaultOperatorAlgorithm <: AbstractOperatorAlgorithm end +struct BLASAlgorithm <: AbstractOperatorAlgorithm end +struct GenericMatrixAlgorithm <: AbstractOperatorAlgorithm end +struct GenericTensorProductAlgorithm <: AbstractOperatorAlgorithm end - export SelectionMap - include("selection.jl") +export WarpedTensorProductMap2D, WarpedTensorProductMap3D +include("warped_product_2d.jl") +include("warped_product_3d.jl") - export GenericMatrixMap - include("generic.jl") +export SelectionMap +include("selection.jl") - export OctavianMap - include("octavian.jl") +export GenericMatrixMap +include("generic.jl") - export ZeroMap - include("zero.jl") +export OctavianMap +include("octavian.jl") - # union of all types that don't actually do any floating-point operations - const NoOp = Union{SelectionMap, ZeroMap, LinearMaps.UniformScalingMap, - LinearMaps.TransposeMap{Float64, <:SelectionMap}} +export ZeroMap +include("zero.jl") - # default fallbacks - make_operator(matrix::Diagonal, # specialize diagonal matrices - ::DefaultOperatorAlgorithm) = LinearMap(matrix) - make_operator(matrix::AbstractMatrix, # gemm/gemv using Octavian.jl - ::AbstractOperatorAlgorithm) = OctavianMap(Matrix(matrix)) - make_operator(map::UniformScaling, # specialize scalar/identity matrices - ::AbstractOperatorAlgorithm) = LinearMap(map,size(map,1)) - make_operator(map::LinearMap, # keep predefined maps as is - ::AbstractOperatorAlgorithm) = map +# union of all types that don't actually do any floating-point operations +const NoOp = Union{SelectionMap, + ZeroMap, + LinearMaps.UniformScalingMap, + LinearMaps.TransposeMap{Float64, <:SelectionMap}} - # BLAS algorithm (OpenBLAS) - make_operator(matrix::AbstractMatrix, - ::BLASAlgorithm) = LinearMaps.WrappedMap(matrix) - make_operator(map::NoOp, ::BLASAlgorithm) = map - make_operator(map::LinearMap, - ::BLASAlgorithm) = LinearMaps.WrappedMap(Matrix(map)) +# default fallbacks +make_operator(matrix::Diagonal, # specialize diagonal matrices +::DefaultOperatorAlgorithm) = LinearMap(matrix) +function make_operator(matrix::AbstractMatrix, # gemm/gemv using Octavian.jl + ::AbstractOperatorAlgorithm) + OctavianMap(Matrix(matrix)) +end +function make_operator(map::UniformScaling, # specialize scalar/identity matrices + ::AbstractOperatorAlgorithm) + LinearMap(map, size(map, 1)) +end +make_operator(map::LinearMap, # keep predefined maps as is +::AbstractOperatorAlgorithm) = map - # Hand-coded matrix-vector multiplication - make_operator(matrix::Diagonal, - ::GenericMatrixAlgorithm) = LinearMap(matrix) - make_operator(matrix::AbstractMatrix, - ::GenericMatrixAlgorithm) = GenericMatrixMap(Matrix(matrix)) - make_operator(map::NoOp, ::GenericMatrixAlgorithm) = map - make_operator(map::LinearMap, - ::GenericMatrixAlgorithm) = GenericMatrixMap(map) +# BLAS algorithm (OpenBLAS) +make_operator(matrix::AbstractMatrix, ::BLASAlgorithm) = LinearMaps.WrappedMap(matrix) +make_operator(map::NoOp, ::BLASAlgorithm) = map +make_operator(map::LinearMap, ::BLASAlgorithm) = LinearMaps.WrappedMap(Matrix(map)) - # Hand-coded Kronecker products - make_operator(map::LinearMaps.BlockMap, - alg::GenericTensorProductAlgorithm) = vcat( - [make_operator(block, alg) for block in map.maps]...) - make_operator(map::LinearMaps.KroneckerMap{Float64, <:NTuple{2,LinearMap}}, - ::GenericTensorProductAlgorithm) = - make_operator(map.maps[1], GenericMatrixAlgorithm()) ⊗ - make_operator(map.maps[2], GenericMatrixAlgorithm()) - make_operator(map::LinearMaps.KroneckerMap{Float64, <:NTuple{3,LinearMap}}, - ::GenericTensorProductAlgorithm) = - make_operator(map.maps[1], GenericMatrixAlgorithm()) ⊗ - make_operator(map.maps[2], GenericMatrixAlgorithm()) ⊗ - make_operator(map.maps[3], GenericMatrixAlgorithm()) +# Hand-coded matrix-vector multiplication +make_operator(matrix::Diagonal, ::GenericMatrixAlgorithm) = LinearMap(matrix) +function make_operator(matrix::AbstractMatrix, ::GenericMatrixAlgorithm) + GenericMatrixMap(Matrix(matrix)) +end +make_operator(map::NoOp, ::GenericMatrixAlgorithm) = map +make_operator(map::LinearMap, ::GenericMatrixAlgorithm) = GenericMatrixMap(map) -end \ No newline at end of file +# Hand-coded Kronecker products +function make_operator(map::LinearMaps.BlockMap, alg::GenericTensorProductAlgorithm) + vcat([make_operator(block, alg) for block in map.maps]...) +end +function make_operator(map::LinearMaps.KroneckerMap{Float64, <:NTuple{2, LinearMap}}, + ::GenericTensorProductAlgorithm) + make_operator(map.maps[1], GenericMatrixAlgorithm()) ⊗ + make_operator(map.maps[2], GenericMatrixAlgorithm()) +end +function make_operator(map::LinearMaps.KroneckerMap{Float64, <:NTuple{3, LinearMap}}, + ::GenericTensorProductAlgorithm) + make_operator(map.maps[1], GenericMatrixAlgorithm()) ⊗ + make_operator(map.maps[2], GenericMatrixAlgorithm()) ⊗ + make_operator(map.maps[3], GenericMatrixAlgorithm()) +end + +end diff --git a/src/MatrixFreeOperators/generic.jl b/src/MatrixFreeOperators/generic.jl index d531f0a8..6bb6dc58 100644 --- a/src/MatrixFreeOperators/generic.jl +++ b/src/MatrixFreeOperators/generic.jl @@ -12,16 +12,16 @@ function LinearAlgebra.transpose(L::GenericMatrixMap) end @inline function LinearAlgebra.mul!(y::AbstractVector{Float64}, - L::GenericMatrixMap, x::AbstractVector{Float64}) - + L::GenericMatrixMap, + x::AbstractVector{Float64}) LinearMaps.check_dim_mul(y, L, x) @inbounds for i in eachindex(y) temp = 0.0 @inbounds @simd for j in eachindex(x) - @muladd temp = temp + L.lmap[i,j]*x[j] + @muladd temp = temp + L.lmap[i, j] * x[j] end y[i] = temp end - + return y -end \ No newline at end of file +end diff --git a/src/MatrixFreeOperators/octavian.jl b/src/MatrixFreeOperators/octavian.jl index adb906f9..d42be7e4 100644 --- a/src/MatrixFreeOperators/octavian.jl +++ b/src/MatrixFreeOperators/octavian.jl @@ -13,8 +13,9 @@ function LinearAlgebra.transpose(L::OctavianMap) end @inline function LinearAlgebra.mul!(y::AbstractVector{Float64}, - L::OctavianMap, x::AbstractVector{Float64}) + L::OctavianMap, + x::AbstractVector{Float64}) LinearMaps.check_dim_mul(y, L, x) matmul_serial!(y, L.lmap, x) return y -end \ No newline at end of file +end diff --git a/src/MatrixFreeOperators/selection.jl b/src/MatrixFreeOperators/selection.jl index 20b71284..b40e9c60 100644 --- a/src/MatrixFreeOperators/selection.jl +++ b/src/MatrixFreeOperators/selection.jl @@ -7,29 +7,29 @@ struct SelectionMap <: LinearMaps.LinearMap{Float64} end function SelectionMap(facet_ids::Vector{Int}, N_vol::Int) - return SelectionMap(facet_ids, - Tuple(findall(j->j==i, facet_ids) for i in 1:N_vol)) + return SelectionMap(facet_ids, Tuple(findall(j -> j == i, facet_ids) for i in 1:N_vol)) end -Base.size(R::SelectionMap) = (length(R.facet_ids),length(R.volume_ids)) +Base.size(R::SelectionMap) = (length(R.facet_ids), length(R.volume_ids)) -@inline function LinearAlgebra.mul!(y::AbstractVector, - R::SelectionMap, x::AbstractVector) +@inline function LinearAlgebra.mul!(y::AbstractVector, R::SelectionMap, x::AbstractVector) LinearMaps.check_dim_mul(y, R, x) y[:] = x[R.facet_ids] return y end -@inline function LinearMaps._unsafe_mul!(y::AbstractVector, - transR::LinearMaps.TransposeMap{Float64, <:SelectionMap}, - x::AbstractVector) - +@inline function LinearMaps._unsafe_mul!(y::AbstractVector, + transR::LinearMaps.TransposeMap{Float64, + <:SelectionMap}, + x::AbstractVector) LinearMaps.check_dim_mul(y, transR, x) (; volume_ids) = transR.lmap for i in eachindex(volume_ids) y[i] = 0.0 - for j in volume_ids[i] @muladd y[i] = y[i] + x[j] end + for j in volume_ids[i] + @muladd y[i] = y[i] + x[j] + end end return y -end \ No newline at end of file +end diff --git a/src/MatrixFreeOperators/warped_product_2d.jl b/src/MatrixFreeOperators/warped_product_2d.jl index 9495ec97..842bc5d4 100644 --- a/src/MatrixFreeOperators/warped_product_2d.jl +++ b/src/MatrixFreeOperators/warped_product_2d.jl @@ -1,20 +1,26 @@ """ Warped tensor-product operator (e.g. for Dubiner-type bases) -""" -struct WarpedTensorProductMap2D{A_type,B_type, - σᵢ_type,σₒ_type} <: LinearMaps.LinearMap{Float64} +""" +struct WarpedTensorProductMap2D{A_type, B_type, σᵢ_type, σₒ_type} <: + LinearMaps.LinearMap{Float64} A::A_type B::B_type - σᵢ::σᵢ_type + σᵢ::σᵢ_type σₒ::σₒ_type N2::Vector{Int} - size::NTuple{2,Int} + size::NTuple{2, Int} - function WarpedTensorProductMap2D(A::A_type,B::B_type, σᵢ::σᵢ_type, - σₒ::σₒ_type) where {A_type,B_type,σᵢ_type,σₒ_type} - N2 = [count(a -> a>0, σᵢ[β1,:]) for β1 in axes(σᵢ,1)] - return new{A_type,B_type,σᵢ_type,σₒ_type}(A,B,σᵢ,σₒ, - N2, (size(A,1)*size(B,1), sum(N2))) + function WarpedTensorProductMap2D(A::A_type, + B::B_type, + σᵢ::σᵢ_type, + σₒ::σₒ_type) where {A_type, B_type, σᵢ_type, σₒ_type} + N2 = [count(a -> a > 0, σᵢ[β1, :]) for β1 in axes(σᵢ, 1)] + return new{A_type, B_type, σᵢ_type, σₒ_type}(A, + B, + σᵢ, + σₒ, + N2, + (size(A, 1) * size(B, 1), sum(N2))) end end @@ -27,28 +33,28 @@ Evaluate the matrix-vector product = ∑_{β1} A[α1,β1] (∑_{β2} B[α2,β1,β2] x[σᵢ[β1,β2]]) = ∑_{β1} A[α1,β1] Z[β1,α2] """ -@inline function LinearAlgebra.mul!(y::AbstractVector, - L::WarpedTensorProductMap2D, x::AbstractVector) - +@inline function LinearAlgebra.mul!(y::AbstractVector, + L::WarpedTensorProductMap2D, + x::AbstractVector) LinearMaps.check_dim_mul(y, L, x) (; A, B, σᵢ, σₒ, N2) = L - - Z = MMatrix{size(σᵢ,1), size(σₒ,2),Float64}(undef) # stack allocate - @inbounds for α2 in axes(σₒ,2), β1 in axes(σᵢ,1) + Z = MMatrix{size(σᵢ, 1), size(σₒ, 2), Float64}(undef) # stack allocate + + @inbounds for α2 in axes(σₒ, 2), β1 in axes(σᵢ, 1) temp = 0.0 @simd for β2 in 1:N2[β1] - @muladd temp = temp + B[α2,β1,β2] * x[σᵢ[β1,β2]] + @muladd temp = temp + B[α2, β1, β2] * x[σᵢ[β1, β2]] end - Z[β1,α2] = temp + Z[β1, α2] = temp end - @inbounds for α1 in axes(σₒ,1), α2 in axes(σₒ,2) + @inbounds for α1 in axes(σₒ, 1), α2 in axes(σₒ, 2) temp = 0.0 - @simd for β1 in axes(σᵢ,1) - @muladd temp = temp + A[α1,β1] * Z[β1,α2] + @simd for β1 in axes(σᵢ, 1) + @muladd temp = temp + A[α1, β1] * Z[β1, α2] end - y[σₒ[α1,α2]] = temp + y[σₒ[α1, α2]] = temp end return y end @@ -60,33 +66,32 @@ Evaluate the matrix-vector product = ∑_{α2} B[α2,β1,β2] (∑_{α1} A[α1,β1] x[σₒ[α1,α2]] ) = ∑_{α2} B[α2,β1,β2] Z[β1,α2]) """ -@inline function LinearMaps._unsafe_mul!( - y::AbstractVector, - L::LinearMaps.TransposeMap{Float64, <:WarpedTensorProductMap2D}, - x::AbstractVector) - +@inline function LinearMaps._unsafe_mul!(y::AbstractVector, + L::LinearMaps.TransposeMap{Float64, + <:WarpedTensorProductMap2D}, + x::AbstractVector) LinearMaps.check_dim_mul(y, L, x) (; A, B, σᵢ, σₒ, N2) = L.lmap - Z = MMatrix{size(σᵢ,1), size(σₒ,2),Float64}(undef) # stack allocate + Z = MMatrix{size(σᵢ, 1), size(σₒ, 2), Float64}(undef) # stack allocate - @inbounds for β1 in axes(σᵢ,1), α2 in axes(σₒ,2) + @inbounds for β1 in axes(σᵢ, 1), α2 in axes(σₒ, 2) temp = 0.0 - @simd for α1 in axes(σₒ,1) - @muladd temp = temp + A[α1,β1] * x[σₒ[α1,α2]] + @simd for α1 in axes(σₒ, 1) + @muladd temp = temp + A[α1, β1] * x[σₒ[α1, α2]] end - Z[β1,α2] = temp + Z[β1, α2] = temp end - @inbounds for β1 in axes(σᵢ,1) + @inbounds for β1 in axes(σᵢ, 1) for β2 in 1:N2[β1] temp = 0.0 - @simd for α2 in axes(σₒ,2) - @muladd temp = temp + B[α2,β1,β2] * Z[β1,α2] + @simd for α2 in axes(σₒ, 2) + @muladd temp = temp + B[α2, β1, β2] * Z[β1, α2] end - y[σᵢ[β1,β2]] = temp + y[σᵢ[β1, β2]] = temp end end return y -end \ No newline at end of file +end diff --git a/src/MatrixFreeOperators/warped_product_3d.jl b/src/MatrixFreeOperators/warped_product_3d.jl index b3e64699..69a2a4fa 100644 --- a/src/MatrixFreeOperators/warped_product_3d.jl +++ b/src/MatrixFreeOperators/warped_product_3d.jl @@ -1,8 +1,9 @@ """ Warped tensor-product operator (e.g. for Dubiner-type bases) -""" +""" -struct WarpedTensorProductMap3D{A_type,B_type,C_type,σᵢ_type,σₒ_type} <: LinearMaps.LinearMap{Float64} +struct WarpedTensorProductMap3D{A_type, B_type, C_type, σᵢ_type, σₒ_type} <: + LinearMaps.LinearMap{Float64} A::A_type B::B_type C::C_type @@ -10,16 +11,29 @@ struct WarpedTensorProductMap3D{A_type,B_type,C_type,σᵢ_type,σₒ_type} <: L σₒ::σₒ_type N2::Vector{Int} N3::Matrix{Int} - size::NTuple{2,Int} - - function WarpedTensorProductMap3D( - A::A_type,B::B_type,C::C_type,σᵢ::σᵢ_type, - σₒ::σₒ_type) where {A_type,B_type,C_type,σᵢ_type,σₒ_type} - return new{A_type,B_type,C_type,σᵢ_type,σₒ_type}(A, B, C, σᵢ, σₒ, - [count(a -> a>0, σᵢ[β1,1,:]) for β1 in axes(σᵢ,1)], - [count(a -> a>0, σᵢ[β1,β2,:]) for β1 in axes(σᵢ,1), - β2 in axes(σᵢ,2)], (size(A,1)*size(B,1)*size(C,1), - count(a->a>0,σᵢ))) + size::NTuple{2, Int} + + function WarpedTensorProductMap3D(A::A_type, + B::B_type, + C::C_type, + σᵢ::σᵢ_type, + σₒ::σₒ_type) where {A_type, B_type, C_type, σᵢ_type, + σₒ_type} + return new{A_type, B_type, C_type, σᵢ_type, σₒ_type}(A, + B, + C, + σᵢ, + σₒ, + [count(a -> a > 0, + σᵢ[β1, 1, :]) + for β1 in axes(σᵢ, 1)], + [count(a -> a > 0, + σᵢ[β1, β2, :]) + for β1 in axes(σᵢ, 1), + β2 in axes(σᵢ, 2)], + (size(A, 1) * size(B, 1) * + size(C, 1), + count(a -> a > 0, σᵢ))) end end @@ -60,46 +74,45 @@ Evaluate the matrix-vector product = ∑_{β1} A[α1,β1] (∑_{β2} B[α2,β1,β2] Z[β1,β2,α3]) = ∑_{β1} A[α1,β1] W[β1,α2,α3] """ -@inline function LinearAlgebra.mul!(y::AbstractVector, - L::WarpedTensorProductMap3D, x::AbstractVector) - +@inline function LinearAlgebra.mul!(y::AbstractVector, + L::WarpedTensorProductMap3D, + x::AbstractVector) LinearMaps.check_dim_mul(y, L, x) (; A, B, C, σᵢ, σₒ, N2, N3) = L # these will be stack allocated - Z = MArray{Tuple{size(σᵢ,1), size(σᵢ,2), size(σₒ,3)},Float64}(undef) - W = MArray{Tuple{size(σᵢ,1), size(σₒ,2), size(σₒ,3)},Float64}(undef) + Z = MArray{Tuple{size(σᵢ, 1), size(σᵢ, 2), size(σₒ, 3)}, Float64}(undef) + W = MArray{Tuple{size(σᵢ, 1), size(σₒ, 2), size(σₒ, 3)}, Float64}(undef) - @inbounds for β1 in axes(σᵢ,1) - for β2 in 1:N2[β1], α3 in axes(σₒ,3) + @inbounds for β1 in axes(σᵢ, 1) + for β2 in 1:N2[β1], α3 in axes(σₒ, 3) temp = 0.0 - @simd for β3 in 1:N3[β1,β2] - @muladd temp = temp + C[α3,β1,β2,β3] * x[σᵢ[β1,β2,β3]] + @simd for β3 in 1:N3[β1, β2] + @muladd temp = temp + C[α3, β1, β2, β3] * x[σᵢ[β1, β2, β3]] end - Z[β1,β2,α3] = temp + Z[β1, β2, α3] = temp end end - @inbounds for β1 in axes(σᵢ,1), α2 in axes(σₒ,2), α3 in axes(σₒ,3) + @inbounds for β1 in axes(σᵢ, 1), α2 in axes(σₒ, 2), α3 in axes(σₒ, 3) temp = 0.0 @simd for β2 in 1:N2[β1] - @muladd temp = temp + B[α2,β1,β2] * Z[β1,β2,α3] + @muladd temp = temp + B[α2, β1, β2] * Z[β1, β2, α3] end - W[β1,α2,α3] = temp + W[β1, α2, α3] = temp end - @inbounds for α1 in axes(σₒ,1), α2 in axes(σₒ,2), α3 in axes(σₒ,3) + @inbounds for α1 in axes(σₒ, 1), α2 in axes(σₒ, 2), α3 in axes(σₒ, 3) temp = 0.0 - @simd for β1 in axes(σᵢ,1) - @muladd temp = temp + A[α1,β1] * W[β1,α2,α3] + @simd for β1 in axes(σᵢ, 1) + @muladd temp = temp + A[α1, β1] * W[β1, α2, α3] end - y[σₒ[α1,α2,α3]] = temp + y[σₒ[α1, α2, α3]] = temp end return y end - """ Evaluate the matrix-vector product @@ -111,46 +124,46 @@ Evaluate the matrix-vector product = ∑_{α3} C[α3,β1,β2,β3] (∑_{α2} B[α2,β1,β2] W[β1,α2,α3]) = ∑_{α3} C[α3,β1,β2,β3] Z[β1,β2,α3] """ -@inline function LinearMaps._unsafe_mul!(y::AbstractVector, - L::LinearMaps.TransposeMap{Float64, <:WarpedTensorProductMap3D}, - x::AbstractVector) - +@inline function LinearMaps._unsafe_mul!(y::AbstractVector, + L::LinearMaps.TransposeMap{Float64, + <:WarpedTensorProductMap3D}, + x::AbstractVector) LinearMaps.check_dim_mul(y, L, x) (; A, B, C, σᵢ, σₒ, N2, N3) = L.lmap # these will be stack allocated - Z = MArray{Tuple{size(σᵢ,1), size(σᵢ,2), size(σₒ,3)},Float64}(undef) - W = MArray{Tuple{size(σᵢ,1), size(σₒ,2), size(σₒ,3)},Float64}(undef) - - @inbounds for β1 in axes(σᵢ,1), α2 in axes(σₒ,2), α3 in axes(σₒ,3) + Z = MArray{Tuple{size(σᵢ, 1), size(σᵢ, 2), size(σₒ, 3)}, Float64}(undef) + W = MArray{Tuple{size(σᵢ, 1), size(σₒ, 2), size(σₒ, 3)}, Float64}(undef) + + @inbounds for β1 in axes(σᵢ, 1), α2 in axes(σₒ, 2), α3 in axes(σₒ, 3) temp = 0.0 - @simd for α1 in axes(σₒ,1) - @muladd temp = temp + A[α1,β1] * x[σₒ[α1,α2,α3]] + @simd for α1 in axes(σₒ, 1) + @muladd temp = temp + A[α1, β1] * x[σₒ[α1, α2, α3]] end - W[β1,α2,α3] = temp + W[β1, α2, α3] = temp end - @inbounds for β1 in axes(σᵢ,1) - for β2 in 1:N2[β1], α3 in axes(σₒ,3) + @inbounds for β1 in axes(σᵢ, 1) + for β2 in 1:N2[β1], α3 in axes(σₒ, 3) temp = 0.0 - @simd for α2 in axes(σₒ,2) - @muladd temp = temp + B[α2,β1,β2] * W[β1,α2,α3] + @simd for α2 in axes(σₒ, 2) + @muladd temp = temp + B[α2, β1, β2] * W[β1, α2, α3] end - Z[β1,β2,α3] = temp + Z[β1, β2, α3] = temp end end - @inbounds for β1 in axes(σᵢ,1) + @inbounds for β1 in axes(σᵢ, 1) for β2 in 1:N2[β1] - for β3 in 1:N3[β1,β2] + for β3 in 1:N3[β1, β2] temp = 0.0 - @simd for α3 in axes(σₒ,3) - @muladd temp = temp + C[α3,β1,β2,β3] * Z[β1,β2,α3] + @simd for α3 in axes(σₒ, 3) + @muladd temp = temp + C[α3, β1, β2, β3] * Z[β1, β2, α3] end - y[σᵢ[β1,β2,β3]] = temp + y[σᵢ[β1, β2, β3]] = temp end end end return y -end \ No newline at end of file +end diff --git a/src/MatrixFreeOperators/zero.jl b/src/MatrixFreeOperators/zero.jl index 1411c08d..f3c591ea 100644 --- a/src/MatrixFreeOperators/zero.jl +++ b/src/MatrixFreeOperators/zero.jl @@ -3,15 +3,16 @@ struct ZeroMap <: LinearMaps.LinearMap{Float64} N::Int end -@inline Base.size(L::ZeroMap) = (L.M,L.N) +@inline Base.size(L::ZeroMap) = (L.M, L.N) function LinearAlgebra.transpose(L::ZeroMap) - return ZeroMap(L.N,L.M) + return ZeroMap(L.N, L.M) end -function LinearAlgebra.mul!(y::AbstractVector{Float64}, - L::ZeroMap, x::AbstractVector{Float64}) +function LinearAlgebra.mul!(y::AbstractVector{Float64}, + L::ZeroMap, + x::AbstractVector{Float64}) LinearMaps.check_dim_mul(y, L, x) - fill!(y,0.0) + fill!(y, 0.0) return y -end \ No newline at end of file +end diff --git a/src/Solvers/Solvers.jl b/src/Solvers/Solvers.jl index 5e5f69b4..8449d89c 100644 --- a/src/Solvers/Solvers.jl +++ b/src/Solvers/Solvers.jl @@ -1,398 +1,501 @@ module Solvers - - import LinearAlgebra - using StaticArrays - using MuladdMacro - using SparseArrays - using LinearAlgebra: Diagonal, eigvals, inv, mul!, lmul!, diag, diagm, factorize, cholesky, ldiv!, Factorization, Cholesky, Symmetric, I, UniformScaling - using TimerOutputs - using LinearMaps: LinearMap, UniformScalingMap, TransposeMap - using OrdinaryDiffEq: ODEProblem, OrdinaryDiffEqAlgorithm, solve - using StartUpDG: num_faces - - using ..MatrixFreeOperators - using ..ConservationLaws - using ..SpatialDiscretizations - using ..GridFunctions - - export AbstractResidualForm, StandardForm, FluxDifferencingForm, AbstractMappingForm, AbstractStrategy, AbstractDiscretizationOperators, AbstractMassMatrixSolver, AbstractParallelism, ReferenceOperators, PhysicalOperators, FluxDifferencingOperators, AbstractPreAllocatedArrays, PreAllocatedArraysFirstOrder, PreAllocatedArraysSecondOrder,PhysicalOperator, ReferenceOperator, Solver, StandardMapping, SkewSymmetricMapping, Serial, Threaded, get_dof, semi_discrete_residual!, auxiliary_variable!, make_operators, entropy_projection!, facet_correction!, nodal_values!, time_derivative!, project_function!,flux_differencing_operators, initialize, semidiscretize - - abstract type AbstractResidualForm end - abstract type AbstractMappingForm end - abstract type AbstractStrategy end - abstract type AbstractDiscretizationOperators end - abstract type AbstractMassMatrixSolver end - abstract type AbstractParallelism end - abstract type AbstractPreAllocatedArrays end - - struct StandardMapping <: AbstractMappingForm end - struct SkewSymmetricMapping <: AbstractMappingForm end - struct PhysicalOperator <: AbstractStrategy end - struct ReferenceOperator <: AbstractStrategy end - struct Serial <: AbstractParallelism end - struct Threaded <: AbstractParallelism end - - Base.@kwdef struct StandardForm{MappingForm,InviscidNumericalFlux, - ViscousNumericalFlux} <: AbstractResidualForm - mapping_form::MappingForm = SkewSymmetricMapping() - inviscid_numerical_flux::InviscidNumericalFlux = - LaxFriedrichsNumericalFlux() - viscous_numerical_flux::ViscousNumericalFlux = BR1() - end - Base.@kwdef struct FluxDifferencingForm{MappingForm, - InviscidNumericalFlux, ViscousNumericalFlux, - TwoPointFlux} <: AbstractResidualForm - mapping_form::MappingForm = SkewSymmetricMapping() - inviscid_numerical_flux::InviscidNumericalFlux = - LaxFriedrichsNumericalFlux() - viscous_numerical_flux::ViscousNumericalFlux = BR1() - two_point_flux::TwoPointFlux = EntropyConservativeFlux() +import LinearAlgebra +using StaticArrays +using MuladdMacro +using SparseArrays +using LinearAlgebra: + Diagonal, + eigvals, + inv, + mul!, + lmul!, + diag, + diagm, + factorize, + cholesky, + ldiv!, + Factorization, + Cholesky, + Symmetric, + I, + UniformScaling +using TimerOutputs +using LinearMaps: LinearMap, UniformScalingMap, TransposeMap +using OrdinaryDiffEq: ODEProblem, OrdinaryDiffEqAlgorithm, solve +using StartUpDG: num_faces + +using ..MatrixFreeOperators +using ..ConservationLaws +using ..SpatialDiscretizations +using ..GridFunctions + +export AbstractResidualForm, + StandardForm, + FluxDifferencingForm, + AbstractMappingForm, + AbstractStrategy, + AbstractDiscretizationOperators, + AbstractMassMatrixSolver, + AbstractParallelism, + ReferenceOperators, + PhysicalOperators, + FluxDifferencingOperators, + AbstractPreAllocatedArrays, + PreAllocatedArraysFirstOrder, + PreAllocatedArraysSecondOrder, + PhysicalOperator, + ReferenceOperator, + Solver, + StandardMapping, + SkewSymmetricMapping, + Serial, + Threaded, + get_dof, + semi_discrete_residual!, + auxiliary_variable!, + make_operators, + entropy_projection!, + facet_correction!, + nodal_values!, + time_derivative!, + project_function!, + flux_differencing_operators, + initialize, + semidiscretize + +abstract type AbstractResidualForm end +abstract type AbstractMappingForm end +abstract type AbstractStrategy end +abstract type AbstractDiscretizationOperators end +abstract type AbstractMassMatrixSolver end +abstract type AbstractParallelism end +abstract type AbstractPreAllocatedArrays end + +struct StandardMapping <: AbstractMappingForm end +struct SkewSymmetricMapping <: AbstractMappingForm end +struct PhysicalOperator <: AbstractStrategy end +struct ReferenceOperator <: AbstractStrategy end +struct Serial <: AbstractParallelism end +struct Threaded <: AbstractParallelism end + +Base.@kwdef struct StandardForm{MappingForm, InviscidNumericalFlux, ViscousNumericalFlux} <: + AbstractResidualForm + mapping_form::MappingForm = SkewSymmetricMapping() + inviscid_numerical_flux::InviscidNumericalFlux = LaxFriedrichsNumericalFlux() + viscous_numerical_flux::ViscousNumericalFlux = BR1() +end + +Base.@kwdef struct FluxDifferencingForm{MappingForm, + InviscidNumericalFlux, + ViscousNumericalFlux, + TwoPointFlux} <: AbstractResidualForm + mapping_form::MappingForm = SkewSymmetricMapping() + inviscid_numerical_flux::InviscidNumericalFlux = LaxFriedrichsNumericalFlux() + viscous_numerical_flux::ViscousNumericalFlux = BR1() + two_point_flux::TwoPointFlux = EntropyConservativeFlux() +end + +struct ReferenceOperators{D_type, Dt_type, V_type, Vt_type, R_type, Rt_type} <: + AbstractDiscretizationOperators + D::D_type + Dᵀ::Dt_type + V::V_type + Vᵀ::Vt_type + R::R_type + Rᵀ::Rt_type + W::Diagonal{Float64, Vector{Float64}} + B::Diagonal{Float64, Vector{Float64}} + halfWΛ::Array{Diagonal{Float64, Vector{Float64}}, 3} # d x d x N_e + halfN::Matrix{Diagonal{Float64, Vector{Float64}}} # d x N_e + BJf::Vector{Diagonal{Float64, Vector{Float64}}} # N_e + n_f::Array{Float64, 3} # d x N_f x N_e +end + +struct PhysicalOperators{d, VOL_type, FAC_type, V_type, R_type} <: + AbstractDiscretizationOperators + VOL::Vector{NTuple{d, VOL_type}} + FAC::Vector{FAC_type} + V::V_type + R::R_type + n_f::Array{Float64, 3} # d x N_f x N_e +end + +struct FluxDifferencingOperators{S_type, C_type, V_type, Vt_type, R_type, Rt_type} <: + AbstractDiscretizationOperators + S::S_type + C::C_type + V::V_type + Vᵀ::Vt_type + R::R_type + Rᵀ::Rt_type + W::Diagonal{Float64, Vector{Float64}} + B::Diagonal{Float64, Vector{Float64}} + WJ::Vector{Diagonal{Float64, Vector{Float64}}} + Λ_q::Array{Float64, 4} # N_q x d x d x N_e + BJf::Vector{Diagonal{Float64, Vector{Float64}}} # N_e + n_f::Array{Float64, 3} # d x N_f x N_e + halfnJf::Array{Float64, 3} # d x N_f x N_e + halfnJq::Array{Float64, 4} # d x N_q x num_faces x N_e + nodes_per_face::Int64 +end + +struct PreAllocatedArraysFirstOrder <: AbstractPreAllocatedArrays + f_q::Array{Float64, 4} + f_f::Array{Float64, 3} + f_n::Array{Float64, 3} + u_q::Array{Float64, 3} + r_q::Array{Float64, 3} + u_f::Array{Float64, 3} + temp::Array{Float64, 3} + CI::CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}} + + function PreAllocatedArraysFirstOrder(d, + N_q, + N_f, + N_c, + N_e, + temp_size = N_q, + N_r = Threads.nthreads()) + return new(Array{Float64}(undef, N_q, N_c, d, N_r), + Array{Float64}(undef, N_f, N_c, N_r), + Array{Float64}(undef, N_f, N_c, N_r), + Array{Float64}(undef, N_q, N_c, N_e), + Array{Float64}(undef, N_q, N_c, N_r), + Array{Float64}(undef, N_f, N_e, N_c), #note switched order + Array{Float64}(undef, temp_size, N_c, N_r), + CartesianIndices((N_f, N_e))) end - - struct ReferenceOperators{D_type, Dt_type, V_type, Vt_type, - R_type, Rt_type} <: AbstractDiscretizationOperators - D::D_type - Dᵀ::Dt_type - V::V_type - Vᵀ::Vt_type - R::R_type - Rᵀ::Rt_type - W::Diagonal{Float64, Vector{Float64}} - B::Diagonal{Float64, Vector{Float64}} - halfWΛ::Array{Diagonal{Float64, Vector{Float64}},3} # d x d x N_e - halfN::Matrix{Diagonal{Float64, Vector{Float64}}} # d x N_e - BJf::Vector{Diagonal{Float64, Vector{Float64}}} # N_e - n_f::Array{Float64,3} # d x N_f x N_e +end + +struct PreAllocatedArraysSecondOrder <: AbstractPreAllocatedArrays + f_q::Array{Float64, 4} + f_f::Array{Float64, 3} + f_n::Array{Float64, 3} + u_q::Array{Float64, 3} + r_q::Array{Float64, 3} + u_f::Array{Float64, 3} + temp::Array{Float64, 3} + u_n::Array{Float64, 4} + q_q::Array{Float64, 4} + q_f::Array{Float64, 4} + CI::CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}} + + function PreAllocatedArraysSecondOrder(d, + N_q, + N_f, + N_c, + N_e, + temp_size = N_q, + N_r = Threads.nthreads()) + return new(Array{Float64}(undef, N_q, N_c, d, N_r), + Array{Float64}(undef, N_f, N_c, N_r), + Array{Float64}(undef, N_f, N_c, N_r), + Array{Float64}(undef, N_q, N_c, N_e), + Array{Float64}(undef, N_q, N_c, N_r), + Array{Float64}(undef, N_f, N_e, N_c), #note switched order + Array{Float64}(undef, temp_size, N_c, N_r), + Array{Float64}(undef, N_f, N_c, d, N_r), + Array{Float64}(undef, N_q, N_c, d, N_e), + Array{Float64}(undef, N_f, N_e, N_c, d), #note switched order + CartesianIndices((N_f, N_e))) end - - struct PhysicalOperators{d, VOL_type, FAC_type, V_type, - R_type} <: AbstractDiscretizationOperators - VOL::Vector{NTuple{d,VOL_type}} - FAC::Vector{FAC_type} - V::V_type - R::R_type - n_f::Array{Float64,3} # d x N_f x N_e +end + +struct Solver{ConservationLaw, + Operators, + MassSolver, + ResidualForm, + Parallelism, + PreAllocatedArrays} + conservation_law::ConservationLaw + operators::Operators + mass_solver::MassSolver + connectivity::Matrix{Int} + form::ResidualForm + parallelism::Parallelism + preallocated_arrays::PreAllocatedArrays +end + +function Base.size(solver::Solver{<:AbstractConservationLaw{d, <:AbstractPDEType, N_c}}) where { + d, + N_c + } + N_p = size(solver.operators.V, 2) + N_e = size(solver.preallocated_arrays.CI, 2) + return (N_p, N_c, N_e) +end + +function Solver(conservation_law::AbstractConservationLaw{d, FirstOrder, N_c}, + spatial_discretization::SpatialDiscretization{d}, + form::StandardForm, + ::ReferenceOperator, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver, + parallelism::AbstractParallelism) where {d, N_c} + (; reference_approximation, geometric_factors, N_e, mesh) = spatial_discretization + (; N_q, N_f, reference_mapping) = reference_approximation + + lumped_geometric_factors = apply_reference_mapping(geometric_factors, reference_mapping) + (; Λ_q, nJf, J_f) = lumped_geometric_factors + + operators = ReferenceOperators(reference_approximation, alg, Λ_q, nJf, J_f) + + return Solver(conservation_law, + operators, + mass_solver, + mesh.mapP, + form, + parallelism, + PreAllocatedArraysFirstOrder(d, N_q, N_f, N_c, N_e, N_q)) +end + +function Solver(conservation_law::AbstractConservationLaw{d, FirstOrder, N_c}, + spatial_discretization::SpatialDiscretization{d}, + form::FluxDifferencingForm, + ::ReferenceOperator, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver, + parallelism::AbstractParallelism) where {d, N_c} + (; reference_approximation, N_e, mesh) = spatial_discretization + (; N_p, N_q, N_f) = reference_approximation + (; J_q, Λ_q, nJf, nJq, J_f) = spatial_discretization.geometric_factors + (; reference_approximation, N_e, mesh) = spatial_discretization + + operators = FluxDifferencingOperators(reference_approximation, alg, J_q, Λ_q, nJq, nJf, + J_f) + + return Solver(conservation_law, + operators, + mass_solver, + mesh.mapP, + form, + parallelism, + PreAllocatedArraysFirstOrder(d, N_q, N_f, N_c, N_e, N_p)) +end + +function Solver(conservation_law::AbstractConservationLaw{d, FirstOrder, N_c}, + spatial_discretization::SpatialDiscretization{d}, + form::StandardForm, + ::PhysicalOperator, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver, + parallelism::AbstractParallelism) where {d, N_c} + (; N_e) = spatial_discretization + (; N_p, N_q, N_f) = spatial_discretization.reference_approximation + + operators = PhysicalOperators(spatial_discretization, form, alg, mass_solver) + + return Solver(conservation_law, + operators, + mass_solver, + spatial_discretization.mesh.mapP, + form, + parallelism, + PreAllocatedArraysFirstOrder(d, N_q, N_f, N_c, N_e, N_p, + Threads.nthreads())) +end + +function Solver(conservation_law::AbstractConservationLaw{d, SecondOrder, N_c}, + spatial_discretization::SpatialDiscretization{d}, + form::StandardForm, + ::AbstractStrategy, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver, + parallelism::AbstractParallelism) where {d, N_c} + (; N_e) = spatial_discretization + (; N_p, N_q, N_f) = spatial_discretization.reference_approximation + + operators = PhysicalOperators(spatial_discretization, form, alg, mass_solver) + + return Solver(conservation_law, + operators, + mass_solver, + spatial_discretization.mesh.mapP, + form, + parallelism, + PreAllocatedArraysSecondOrder(d, N_q, N_f, N_c, N_e, N_p)) +end + +@inline function get_dof(spatial_discretization::SpatialDiscretization{d}, + ::AbstractConservationLaw{d, PDEType, N_c}) where {d, PDEType, N_c} + return (spatial_discretization.reference_approximation.N_p, + N_c, + spatial_discretization.N_e) +end + +@inline function project_function(initial_data::AbstractGridFunction{d}, + ::UniformScalingMap, + W::Diagonal, + J_q::Matrix{Float64}, + x::NTuple{d, Matrix{Float64}}) where {d} + return evaluate(initial_data, x, 0.0) +end + +@inline function project_function(initial_data, + V::LinearMap, + W::Diagonal, + J_q::Matrix{Float64}, + x::NTuple{d, Matrix{Float64}}) where {d} + N_p = size(V, 2) + N_e = size(J_q, 2) + + u_q = evaluate(initial_data, x, 0.0) + u0 = Array{Float64}(undef, N_p, size(u_q, 2), N_e) + VDM = Matrix(V) + + @inbounds @views for k in 1:N_e + WJ = Diagonal(W .* J_q[:, k]) + + # this will throw if M is not SPD + M = cholesky(Symmetric(VDM' * WJ * VDM)) + lmul!(WJ, u_q[:, :, k]) + mul!(u0[:, :, k], VDM', u_q[:, :, k]) + ldiv!(M, u0[:, :, k]) end - - struct FluxDifferencingOperators{S_type, - C_type, V_type, Vt_type, R_type, - Rt_type} <: AbstractDiscretizationOperators - S::S_type - C::C_type - V::V_type - Vᵀ::Vt_type - R::R_type - Rᵀ::Rt_type - W::Diagonal{Float64, Vector{Float64}} - B::Diagonal{Float64, Vector{Float64}} - WJ::Vector{Diagonal{Float64, Vector{Float64}}} - Λ_q::Array{Float64,4} # N_q x d x d x N_e - BJf::Vector{Diagonal{Float64, Vector{Float64}}} # N_e - n_f::Array{Float64,3} # d x N_f x N_e - halfnJf::Array{Float64,3} # d x N_f x N_e - halfnJq::Array{Float64,4} # d x N_q x num_faces x N_e - nodes_per_face::Int64 + return u0 +end + +"""Returns an array of initial data as nodal or modal DOF""" +@inline function initialize(initial_data, spatial_discretization::SpatialDiscretization) + (; J_q) = spatial_discretization.geometric_factors + (; V, W) = spatial_discretization.reference_approximation + (; xyzq) = spatial_discretization.mesh + + return project_function(initial_data, V, W, J_q, xyzq) +end + +@inline function semidiscretize(conservation_law::AbstractConservationLaw{d, PDEType}, + spatial_discretization::SpatialDiscretization{d}, + initial_data, + form::AbstractResidualForm, + tspan::NTuple{2, Float64}, + strategy::AbstractStrategy = ReferenceOperator(), + alg::AbstractOperatorAlgorithm = DefaultOperatorAlgorithm(); + mass_matrix_solver::AbstractMassMatrixSolver = default_mass_matrix_solver(spatial_discretization, + alg), + parallelism::AbstractParallelism = Threaded(),) where {d, + PDEType + } + u0 = initialize(initial_data, spatial_discretization) + + solver = Solver(conservation_law, + spatial_discretization, + form, + strategy, + alg, + mass_matrix_solver, + parallelism) + + return ODEProblem(semi_discrete_residual!, u0, tspan, solver) +end + +@timeit "semi-disc. residual" function semi_discrete_residual!(dudt::AbstractArray{Float64, + 3}, + u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{<:Any, + FirstOrder}, + <:AbstractDiscretizationOperators, + <:AbstractMassMatrixSolver, + <:AbstractResidualForm, + Serial}, + t::Float64 = 0.0) + @inbounds for k in axes(u, 3) + @timeit "nodal values" nodal_values!(u, solver, k) end - struct PreAllocatedArraysFirstOrder <: AbstractPreAllocatedArrays - f_q::Array{Float64,4} - f_f::Array{Float64,3} - f_n::Array{Float64,3} - u_q::Array{Float64,3} - r_q::Array{Float64,3} - u_f::Array{Float64,3} - temp::Array{Float64,3} - CI::CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}} - - function PreAllocatedArraysFirstOrder(d,N_q,N_f,N_c,N_e, - temp_size=N_q, N_r=Threads.nthreads()) - return new(Array{Float64}(undef,N_q, N_c, d, N_r), - Array{Float64}(undef, N_f, N_c, N_r), - Array{Float64}(undef, N_f, N_c, N_r), - Array{Float64}(undef, N_q, N_c, N_e), - Array{Float64}(undef, N_q, N_c, N_r), - Array{Float64}(undef, N_f, N_e, N_c), #note switched order - Array{Float64}(undef, temp_size, N_c, N_r), - CartesianIndices((N_f,N_e))) - end - end - - struct PreAllocatedArraysSecondOrder <: AbstractPreAllocatedArrays - f_q::Array{Float64,4} - f_f::Array{Float64,3} - f_n::Array{Float64,3} - u_q::Array{Float64,3} - r_q::Array{Float64,3} - u_f::Array{Float64,3} - temp::Array{Float64,3} - u_n::Array{Float64,4} - q_q::Array{Float64,4} - q_f::Array{Float64,4} - CI::CartesianIndices{2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}} - - function PreAllocatedArraysSecondOrder(d,N_q,N_f,N_c,N_e, - temp_size=N_q, N_r=Threads.nthreads()) - return new(Array{Float64}(undef,N_q, N_c, d, N_r), - Array{Float64}(undef,N_f, N_c, N_r), - Array{Float64}(undef,N_f, N_c, N_r), - Array{Float64}(undef,N_q, N_c, N_e), - Array{Float64}(undef,N_q, N_c, N_r), - Array{Float64}(undef,N_f, N_e, N_c), #note switched order - Array{Float64}(undef,temp_size, N_c, N_r), - Array{Float64}(undef,N_f, N_c, d, N_r), - Array{Float64}(undef,N_q, N_c, d, N_e), - Array{Float64}(undef,N_f, N_e, N_c, d), #note switched order - CartesianIndices((N_f,N_e))) - end + @inbounds for k in axes(u, 3) + @timeit "time deriv." time_derivative!(dudt, solver, k) end - struct Solver{ConservationLaw, Operators, MassSolver, ResidualForm, - Parallelism, PreAllocatedArrays} - conservation_law::ConservationLaw - operators::Operators - mass_solver::MassSolver - connectivity::Matrix{Int} - form::ResidualForm - parallelism::Parallelism - preallocated_arrays::PreAllocatedArrays + return dudt +end + +@timeit "semi-disc. residual" function semi_discrete_residual!(dudt::AbstractArray{Float64, + 3}, + u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{<:Any, + FirstOrder}, + <:AbstractDiscretizationOperators, + <:AbstractMassMatrixSolver, + <:AbstractResidualForm, + Threaded}, + t::Float64 = 0.0) + Threads.@threads for k in axes(u, 3) + nodal_values!(u, solver, k) end - function Base.size(solver::Solver{<:AbstractConservationLaw{d, - <:AbstractPDEType,N_c}}) where {d, N_c} - N_p = size(solver.operators.V,2) - N_e = size(solver.preallocated_arrays.CI,2) - return (N_p, N_c, N_e) + Threads.@threads for k in axes(u, 3) + time_derivative!(dudt, solver, k) end - function Solver( - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - spatial_discretization::SpatialDiscretization{d}, - form::StandardForm, ::ReferenceOperator, - alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver, - parallelism::AbstractParallelism) where {d, N_c} - - (; reference_approximation, geometric_factors, - N_e, mesh) = spatial_discretization - (; N_q, N_f, reference_mapping) = reference_approximation - - lumped_geometric_factors = apply_reference_mapping( - geometric_factors, reference_mapping) - (; Λ_q, nJf, J_f) = lumped_geometric_factors - - operators = ReferenceOperators( - reference_approximation, alg, Λ_q, nJf, J_f) - - return Solver(conservation_law, operators, mass_solver, mesh.mapP, form, - parallelism, PreAllocatedArraysFirstOrder(d,N_q,N_f,N_c,N_e,N_q)) + return dudt +end + +@timeit "semi-disc. residual" function semi_discrete_residual!(dudt::AbstractArray{Float64, + 3}, + u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{<:Any, + SecondOrder}, + <:AbstractDiscretizationOperators, + <:AbstractMassMatrixSolver, + <:AbstractResidualForm, + Serial}, + t::Float64 = 0.0) + @inbounds for k in axes(u, 3) + nodal_values!(u, solver, k) end - function Solver( - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - spatial_discretization::SpatialDiscretization{d}, - form::FluxDifferencingForm, ::ReferenceOperator, - alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver, - parallelism::AbstractParallelism) where {d, N_c} - - (; reference_approximation, N_e, mesh) = spatial_discretization - (; N_p, N_q, N_f) = reference_approximation - (; J_q, Λ_q, nJf, nJq, J_f) = spatial_discretization.geometric_factors - (; reference_approximation, N_e, mesh) = spatial_discretization - - operators = FluxDifferencingOperators( - reference_approximation, alg, J_q, Λ_q, nJq, nJf, J_f) - - return Solver(conservation_law, operators, mass_solver, mesh.mapP, form, - parallelism, PreAllocatedArraysFirstOrder(d,N_q,N_f,N_c,N_e,N_p)) - end - - function Solver( - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - spatial_discretization::SpatialDiscretization{d}, - form::StandardForm, ::PhysicalOperator, alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver, - parallelism::AbstractParallelism) where {d, N_c} - - (; N_e) = spatial_discretization - (; N_p, N_q, N_f) = spatial_discretization.reference_approximation - - operators = PhysicalOperators( - spatial_discretization, form, alg, mass_solver) - - return Solver(conservation_law, operators, mass_solver, - spatial_discretization.mesh.mapP, form, parallelism, - PreAllocatedArraysFirstOrder(d,N_q,N_f,N_c,N_e,N_p, - Threads.nthreads())) + @inbounds for k in axes(u, 3) + auxiliary_variable!(dudt, solver, k) end - function Solver( - conservation_law::AbstractConservationLaw{d,SecondOrder,N_c}, - spatial_discretization::SpatialDiscretization{d}, - form::StandardForm, ::AbstractStrategy, alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver, - parallelism::AbstractParallelism) where {d, N_c} - - (; N_e) = spatial_discretization - (; N_p, N_q, N_f) = spatial_discretization.reference_approximation - - operators = PhysicalOperators(spatial_discretization, form, - alg, mass_solver) - - return Solver(conservation_law, operators, mass_solver, - spatial_discretization.mesh.mapP, form, - parallelism, PreAllocatedArraysSecondOrder(d,N_q,N_f,N_c,N_e,N_p)) + @inbounds for k in axes(u, 3) + time_derivative!(dudt, solver, k) end - @inline function get_dof(spatial_discretization::SpatialDiscretization{d}, - ::AbstractConservationLaw{d,PDEType,N_c}) where {d, PDEType,N_c} - return (spatial_discretization.reference_approximation.N_p, N_c, - spatial_discretization.N_e) + return dudt +end + +@timeit "semi-disc. residual" function semi_discrete_residual!(dudt::AbstractArray{Float64, + 3}, + u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{<:Any, + SecondOrder}, + <:AbstractDiscretizationOperators, + <:AbstractMassMatrixSolver, + <:AbstractResidualForm, + Threaded}, + t::Float64 = 0.0) + @inbounds Threads.@threads for k in axes(u, 3) + nodal_values!(u, solver, k) end - @inline function project_function(initial_data::AbstractGridFunction{d}, - ::UniformScalingMap, W::Diagonal, J_q::Matrix{Float64}, - x::NTuple{d,Matrix{Float64}}) where {d} - return evaluate(initial_data,x,0.0) - end - - @inline function project_function( - initial_data, V::LinearMap, W::Diagonal, J_q::Matrix{Float64}, x::NTuple{d,Matrix{Float64}}) where {d} - - N_p = size(V,2) - N_e = size(J_q,2) - - u_q = evaluate(initial_data,x,0.0) - u0 = Array{Float64}(undef, N_p, size(u_q,2), N_e) - VDM = Matrix(V) - - @inbounds @views for k in 1:N_e - WJ = Diagonal(W .* J_q[:,k]) - - # this will throw if M is not SPD - M = cholesky(Symmetric(VDM' * WJ * VDM)) - lmul!(WJ, u_q[:,:,k]) - mul!(u0[:,:,k], VDM', u_q[:,:,k]) - ldiv!(M, u0[:,:,k]) - end - return u0 - end - - """Returns an array of initial data as nodal or modal DOF""" - @inline function initialize(initial_data, - spatial_discretization::SpatialDiscretization) - - (; J_q) = spatial_discretization.geometric_factors - (; V, W) = spatial_discretization.reference_approximation - (; xyzq) = spatial_discretization.mesh - - return project_function(initial_data, V, W, J_q, xyzq) + @inbounds Threads.@threads for k in axes(u, 3) + auxiliary_variable!(dudt, solver, k) end - - @inline function semidiscretize( - conservation_law::AbstractConservationLaw{d,PDEType},spatial_discretization::SpatialDiscretization{d}, - initial_data, - form::AbstractResidualForm, - tspan::NTuple{2,Float64}, - strategy::AbstractStrategy=ReferenceOperator(), - alg::AbstractOperatorAlgorithm=DefaultOperatorAlgorithm(); - mass_matrix_solver::AbstractMassMatrixSolver=default_mass_matrix_solver( - spatial_discretization,alg), - parallelism::AbstractParallelism=Threaded()) where {d, PDEType} - - u0 = initialize(initial_data, spatial_discretization) - - solver = Solver(conservation_law,spatial_discretization, form, - strategy, alg, mass_matrix_solver, parallelism) - - return ODEProblem(semi_discrete_residual!, u0, tspan, solver) - end - - @timeit "semi-disc. residual" function semi_discrete_residual!( - dudt::AbstractArray{Float64,3}, u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{<:Any,FirstOrder}, - <:AbstractDiscretizationOperators,<:AbstractMassMatrixSolver, - <:AbstractResidualForm,Serial}, t::Float64=0.0) - @inbounds for k in axes(u,3) - @timeit "nodal values" nodal_values!(u, solver, k) - end - - @inbounds for k in axes(u,3) - @timeit "time deriv." time_derivative!(dudt, solver, k) - end - - return dudt - end - - @timeit "semi-disc. residual" function semi_discrete_residual!( - dudt::AbstractArray{Float64,3}, u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{<:Any,FirstOrder}, - <:AbstractDiscretizationOperators,<:AbstractMassMatrixSolver, - <:AbstractResidualForm,Threaded}, t::Float64=0.0) - - Threads.@threads for k in axes(u,3) - nodal_values!(u, solver, k) - end - - Threads.@threads for k in axes(u,3) - time_derivative!(dudt, solver, k) - end - - return dudt - end - - @timeit "semi-disc. residual" function semi_discrete_residual!( - dudt::AbstractArray{Float64,3}, u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{<:Any,SecondOrder}, - <:AbstractDiscretizationOperators,<:AbstractMassMatrixSolver, - <:AbstractResidualForm,Serial}, t::Float64=0.0) - - @inbounds for k in axes(u,3) - nodal_values!(u, solver, k) - end - - @inbounds for k in axes(u,3) - auxiliary_variable!(dudt, solver, k) - end - - @inbounds for k in axes(u,3) - time_derivative!(dudt, solver, k) - end - - return dudt - end - - @timeit "semi-disc. residual" function semi_discrete_residual!( - dudt::AbstractArray{Float64,3}, u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{<:Any,SecondOrder}, - <:AbstractDiscretizationOperators,<:AbstractMassMatrixSolver, - <:AbstractResidualForm,Threaded}, t::Float64=0.0) - - @inbounds Threads.@threads for k in axes(u,3) - nodal_values!(u, solver, k) - end - - @inbounds Threads.@threads for k in axes(u,3) - auxiliary_variable!(dudt, solver, k) - end - - @inbounds Threads.@threads for k in axes(u,3) - time_derivative!(dudt, solver, k) - end - - return dudt + @inbounds Threads.@threads for k in axes(u, 3) + time_derivative!(dudt, solver, k) end - export CholeskySolver, WeightAdjustedSolver, DiagonalSolver, mass_matrix, mass_matrix_inverse, mass_matrix_solve!, default_mass_matrix_solver - include("mass_matrix.jl") - - include("operators.jl") - include("standard_form_first_order.jl") - include("standard_form_second_order.jl") - include("flux_differencing_form.jl") - - export LinearResidual - include("linear.jl") -end \ No newline at end of file + return dudt +end + +export CholeskySolver, + WeightAdjustedSolver, + DiagonalSolver, + mass_matrix, + mass_matrix_inverse, + mass_matrix_solve!, + default_mass_matrix_solver +include("mass_matrix.jl") + +include("operators.jl") +include("standard_form_first_order.jl") +include("standard_form_second_order.jl") +include("flux_differencing_form.jl") + +export LinearResidual +include("linear.jl") +end diff --git a/src/Solvers/flux_differencing_form.jl b/src/Solvers/flux_differencing_form.jl index 2a122373..2493ad48 100644 --- a/src/Solvers/flux_differencing_form.jl +++ b/src/Solvers/flux_differencing_form.jl @@ -1,18 +1,20 @@ -@inline @views function flux_difference!( - r_q::AbstractMatrix{Float64}, # N_q x N_c - S::NTuple{d,Matrix{Float64}}, # N_q x N_q - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - two_point_flux::AbstractTwoPointFlux, - Λ_q::AbstractArray{Float64,3}, # N_q x d x d - u_q::AbstractMatrix{Float64}) where {d,N_c} - +@inline @views function flux_difference!(r_q::AbstractMatrix{Float64}, # N_q x N_c + S::NTuple{d, Matrix{Float64}}, # N_q x N_q + conservation_law::AbstractConservationLaw{d, + FirstOrder, + N_c}, + two_point_flux::AbstractTwoPointFlux, + Λ_q::AbstractArray{Float64, 3}, # N_q x d x d + u_q::AbstractMatrix{Float64}) where {d, N_c} fill!(r_q, 0.0) - @inbounds for i in axes(u_q,1) - for j in (i+1):size(u_q,1) + @inbounds for i in axes(u_q, 1) + for j in (i + 1):size(u_q, 1) # evaluate two-point flux (must be symmetric) F_ij = compute_two_point_flux(conservation_law, - two_point_flux, u_q[i,:], u_q[j,:]) + two_point_flux, + u_q[i, :], + u_q[j, :]) # apply flux-differencing operator to flux tensor for e in 1:N_c @@ -20,49 +22,51 @@ for m in 1:d Fm_ij = 0.0 for n in 1:d - Λ_ij = Λ_q[i,m,n] + Λ_q[j,m,n] - @muladd Fm_ij = Fm_ij + Λ_ij * F_ij[e,n] + Λ_ij = Λ_q[i, m, n] + Λ_q[j, m, n] + @muladd Fm_ij = Fm_ij + Λ_ij * F_ij[e, n] end - @muladd diff_ij = diff_ij + S[m][i,j] * Fm_ij + @muladd diff_ij = diff_ij + S[m][i, j] * Fm_ij end - r_q[i,e] -= diff_ij - r_q[j,e] += diff_ij + r_q[i, e] -= diff_ij + r_q[j, e] += diff_ij end end end end -@inline @views function flux_difference!( - r_q::AbstractMatrix{Float64}, # N_q x N_c - S::NTuple{d,SparseMatrixCSC{Float64}}, # N_q x N_q - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - two_point_flux::AbstractTwoPointFlux, - Λ_q::AbstractArray{Float64,3}, # N_q x d x d - u_q::AbstractMatrix{Float64}) where {d,N_c} - +@inline @views function flux_difference!(r_q::AbstractMatrix{Float64}, # N_q x N_c + S::NTuple{d, SparseMatrixCSC{Float64}}, # N_q x N_q + conservation_law::AbstractConservationLaw{d, + FirstOrder, + N_c}, + two_point_flux::AbstractTwoPointFlux, + Λ_q::AbstractArray{Float64, 3}, # N_q x d x d + u_q::AbstractMatrix{Float64}) where {d, N_c} fill!(r_q, 0.0) @inbounds for m in 1:d Sm_nz = nonzeros(S[m]) row_index = rowvals(S[m]) - for j in axes(u_q,1) - for ii in nzrange(S[m],j) + for j in axes(u_q, 1) + for ii in nzrange(S[m], j) i = row_index[ii] - if i < j + if i < j # evaluate two-point flux F_ij = compute_two_point_flux(conservation_law, - two_point_flux, u_q[i,:], u_q[j,:]) + two_point_flux, + u_q[i, :], + u_q[j, :]) Sm_ij = Sm_nz[ii] - + # apply flux-differencing operator to flux tensor for e in 1:N_c Fm_ij = 0.0 for n in 1:d - Λ_ij = Λ_q[i,m,n] + Λ_q[j,m,n] - @muladd Fm_ij = Fm_ij + Λ_ij * F_ij[e,n] + Λ_ij = Λ_q[i, m, n] + Λ_q[j, m, n] + @muladd Fm_ij = Fm_ij + Λ_ij * F_ij[e, n] end diff_ij = Sm_ij * Fm_ij - r_q[i,e] -= diff_ij - r_q[j,e] += diff_ij + r_q[i, e] -= diff_ij + r_q[j, e] += diff_ij end end end @@ -71,162 +75,160 @@ end end # no-op for LGL/diagonal-E operators -@inline function facet_correction!( - ::AbstractMatrix{Float64}, - ::AbstractMatrix{Float64}, - ::Nothing, # no facet correction operator - ::AbstractConservationLaw{d}, - ::AbstractTwoPointFlux, - ::AbstractMatrix{Float64}, - ::AbstractArray{Float64,3}, - ::AbstractMatrix{Float64}, - ::AbstractMatrix{Float64}, - ::Int) where {d} - +@inline function facet_correction!(::AbstractMatrix{Float64}, + ::AbstractMatrix{Float64}, + ::Nothing, # no facet correction operator + ::AbstractConservationLaw{d}, + ::AbstractTwoPointFlux, + ::AbstractMatrix{Float64}, + ::AbstractArray{Float64, 3}, + ::AbstractMatrix{Float64}, + ::AbstractMatrix{Float64}, + ::Int) where {d} return end -@inline @views function facet_correction!( - r_q::AbstractMatrix{Float64}, # N_q x N_c - f_f::AbstractMatrix{Float64}, # N_f x N_c - C::Matrix{Float64}, # N_f x N_q - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - two_point_flux::AbstractTwoPointFlux, - halfnJf::AbstractMatrix{Float64}, - halfnJq::AbstractArray{Float64,3}, - u_q::AbstractMatrix{Float64}, - u_f::AbstractMatrix{Float64}, - nodes_per_face::Int) where {d, N_c} - - @inbounds for i in axes(u_q,1) - for j in axes(u_f,1) - F_ij = compute_two_point_flux(conservation_law, - two_point_flux, u_q[i,:], u_f[j,:]) - - f = (j-1)÷nodes_per_face + 1 +@inline @views function facet_correction!(r_q::AbstractMatrix{Float64}, # N_q x N_c + f_f::AbstractMatrix{Float64}, # N_f x N_c + C::Matrix{Float64}, # N_f x N_q + conservation_law::AbstractConservationLaw{d, + FirstOrder, + N_c}, + two_point_flux::AbstractTwoPointFlux, + halfnJf::AbstractMatrix{Float64}, + halfnJq::AbstractArray{Float64, 3}, + u_q::AbstractMatrix{Float64}, + u_f::AbstractMatrix{Float64}, + nodes_per_face::Int) where {d, N_c} + @inbounds for i in axes(u_q, 1) + for j in axes(u_f, 1) + F_ij = compute_two_point_flux(conservation_law, + two_point_flux, + u_q[i, :], + u_f[j, :]) + + f = (j - 1) ÷ nodes_per_face + 1 for e in 1:N_c F_dot_n_ij = 0.0 for m in 1:d - nJ_ij = halfnJf[m,j] + halfnJq[m,f,i] - @muladd F_dot_n_ij = F_dot_n_ij + nJ_ij * F_ij[e,m] + nJ_ij = halfnJf[m, j] + halfnJq[m, f, i] + @muladd F_dot_n_ij = F_dot_n_ij + nJ_ij * F_ij[e, m] end - diff_ij = C[i,j] * F_dot_n_ij - r_q[i,e] -= diff_ij - f_f[j,e] -= diff_ij + diff_ij = C[i, j] * F_dot_n_ij + r_q[i, e] -= diff_ij + f_f[j, e] -= diff_ij end end end end -@inline @views function facet_correction!( - r_q::AbstractMatrix{Float64}, # N_q x N_c - f_f::AbstractMatrix{Float64}, # N_f x N_c - C::SparseMatrixCSC{Float64}, # N_f x N_q - conservation_law::AbstractConservationLaw{d,FirstOrder,N_c}, - two_point_flux::AbstractTwoPointFlux, - halfnJf::AbstractMatrix{Float64}, - halfnJq::AbstractArray{Float64,3}, - u_q::AbstractMatrix{Float64}, - u_f::AbstractMatrix{Float64}, - nodes_per_face::Int) where {d, N_c} - +@inline @views function facet_correction!(r_q::AbstractMatrix{Float64}, # N_q x N_c + f_f::AbstractMatrix{Float64}, # N_f x N_c + C::SparseMatrixCSC{Float64}, # N_f x N_q + conservation_law::AbstractConservationLaw{d, + FirstOrder, + N_c}, + two_point_flux::AbstractTwoPointFlux, + halfnJf::AbstractMatrix{Float64}, + halfnJq::AbstractArray{Float64, 3}, + u_q::AbstractMatrix{Float64}, + u_f::AbstractMatrix{Float64}, + nodes_per_face::Int) where {d, N_c} C_nz = nonzeros(C) row_index = rowvals(C) - - @inbounds for j in axes(u_f,1) - for ii in nzrange(C,j) + + @inbounds for j in axes(u_f, 1) + for ii in nzrange(C, j) i = row_index[ii] # evaluate two-point flux - F_ij = compute_two_point_flux(conservation_law, - two_point_flux, u_q[i,:], u_f[j,:]) + F_ij = compute_two_point_flux(conservation_law, + two_point_flux, + u_q[i, :], + u_f[j, :]) C_ij = C_nz[ii] - + # get facet index # (note this won't work if different number of nodes per facet) - f = (j-1)÷nodes_per_face + 1 + f = (j - 1) ÷ nodes_per_face + 1 for e in 1:N_c F_dot_n_ij = 0.0 for m in 1:d - nJ_ij = halfnJf[m,j] + halfnJq[m,f,i] - @muladd F_dot_n_ij = F_dot_n_ij + nJ_ij * F_ij[e,m] + nJ_ij = halfnJf[m, j] + halfnJq[m, f, i] + @muladd F_dot_n_ij = F_dot_n_ij + nJ_ij * F_ij[e, m] end diff_ij = C_ij * F_dot_n_ij - r_q[i,e] -= diff_ij - f_f[j,e] -= diff_ij + r_q[i, e] -= diff_ij + f_f[j, e] -= diff_ij end end end end # specialize for LGL/Diag-E nodal operators (no entropy projection) -@inline function entropy_projection!( - ::AbstractMassMatrixSolver, - conservation_law::AbstractConservationLaw, - u_q::AbstractMatrix, - u_f::AbstractMatrix, - ::AbstractMatrix, - ::AbstractMatrix, - ::AbstractMatrix, - V::UniformScalingMap, # nodal - ::LinearMap, - R::SelectionMap, # diag-E - ::Diagonal, - u::AbstractMatrix, ::Int) - +@inline function entropy_projection!(::AbstractMassMatrixSolver, + conservation_law::AbstractConservationLaw, + u_q::AbstractMatrix, + u_f::AbstractMatrix, + ::AbstractMatrix, + ::AbstractMatrix, + ::AbstractMatrix, + V::UniformScalingMap, # nodal + ::LinearMap, + R::SelectionMap, # diag-E + ::Diagonal, + u::AbstractMatrix, + ::Int) mul!(u_q, V, u) mul!(u_f, R, u_q) return end # specialized for nodal schemes (not necessarily diagonal-E) -@inline @views function entropy_projection!( - ::AbstractMassMatrixSolver, - conservation_law::AbstractConservationLaw, - u_q::AbstractMatrix, - u_f::AbstractMatrix, - w_q::AbstractMatrix, - w_f::AbstractMatrix, - w::AbstractMatrix, - V::UniformScalingMap, # nodal - ::LinearMap, - R::LinearMap, # not just SelectionMap - ::Diagonal, - u::AbstractMatrix, - ::Int) - +@inline @views function entropy_projection!(::AbstractMassMatrixSolver, + conservation_law::AbstractConservationLaw, + u_q::AbstractMatrix, + u_f::AbstractMatrix, + w_q::AbstractMatrix, + w_f::AbstractMatrix, + w::AbstractMatrix, + V::UniformScalingMap, # nodal + ::LinearMap, + R::LinearMap, # not just SelectionMap + ::Diagonal, + u::AbstractMatrix, + ::Int) mul!(u_q, V, u) @inbounds for i in axes(u, 1) - conservative_to_entropy!(w_q[i,:], conservation_law, u_q[i,:]) + conservative_to_entropy!(w_q[i, :], conservation_law, u_q[i, :]) end mul!(w_f, R, w_q) @inbounds for i in axes(u_f, 1) - entropy_to_conservative!(u_f[i,:], conservation_law, w_f[i,:]) + entropy_to_conservative!(u_f[i, :], conservation_law, w_f[i, :]) end end # most general (i.e. suitable for modal) approach -@inline @views function entropy_projection!( - mass_solver::AbstractMassMatrixSolver, - conservation_law::AbstractConservationLaw, - u_q::AbstractMatrix, - u_f::AbstractMatrix, - w_q::AbstractMatrix, - w_f::AbstractMatrix, - w::AbstractMatrix, - V::LinearMap, # not just UniformScalingMap - Vᵀ::LinearMap, - R::LinearMap, # not just SelectionMap - WJ::Diagonal, - u::AbstractMatrix, - k::Int) - +@inline @views function entropy_projection!(mass_solver::AbstractMassMatrixSolver, + conservation_law::AbstractConservationLaw, + u_q::AbstractMatrix, + u_f::AbstractMatrix, + w_q::AbstractMatrix, + w_f::AbstractMatrix, + w::AbstractMatrix, + V::LinearMap, # not just UniformScalingMap + Vᵀ::LinearMap, + R::LinearMap, # not just SelectionMap + WJ::Diagonal, + u::AbstractMatrix, + k::Int) + # evaluate entropy variables in terms of nodal conservative variables mul!(u_q, V, u) @inbounds for i in axes(u_q, 1) - conservative_to_entropy!(w_q[i,:], conservation_law, u_q[i,:]) + conservative_to_entropy!(w_q[i, :], conservation_law, u_q[i, :]) end # project entropy variables and store modal coeffs in w @@ -240,76 +242,106 @@ end # convert back to conservative variables @inbounds for i in axes(u_q, 1) - entropy_to_conservative!(u_q[i,:], conservation_law, w_q[i,:]) + entropy_to_conservative!(u_q[i, :], conservation_law, w_q[i, :]) end @inbounds for i in axes(u_f, 1) - entropy_to_conservative!(u_f[i,:], conservation_law, w_f[i,:]) + entropy_to_conservative!(u_f[i, :], conservation_law, w_f[i, :]) end end # for scalar equations, no entropy projection -@inline @views function nodal_values!(u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{<:Any,1}, - <:FluxDifferencingOperators, <:AbstractMassMatrixSolver, - <:FluxDifferencingForm}, k::Int) - +@inline @views function nodal_values!(u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{<:Any, 1}, + <:FluxDifferencingOperators, + <:AbstractMassMatrixSolver, + <:FluxDifferencingForm}, + k::Int) (; u_q, u_f) = solver.preallocated_arrays (; V, R) = solver.operators - mul!(u_q[:,:,k], V, u[:,:,k]) - mul!(u_f[:,k,:], R, u_q[:,:,k]) + mul!(u_q[:, :, k], V, u[:, :, k]) + mul!(u_f[:, k, :], R, u_q[:, :, k]) return end # for systems, dispatch entropy projection on V and R -@inline @views function nodal_values!(u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw,<:FluxDifferencingOperators, - <:AbstractMassMatrixSolver,<:FluxDifferencingForm}, k::Int) - +@inline @views function nodal_values!(u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw, + <:FluxDifferencingOperators, + <:AbstractMassMatrixSolver, + <:FluxDifferencingForm}, + k::Int) (; conservation_law, preallocated_arrays, mass_solver) = solver (; f_f, u_q, r_q, u_f, temp) = preallocated_arrays (; V, Vᵀ, R, WJ) = solver.operators id = Threads.threadid() - entropy_projection!(mass_solver, conservation_law, u_q[:,:,k], - u_f[:,k,:], r_q[:,:,id], f_f[:,:,id], temp[:,:,id], - V, Vᵀ, R, WJ[k], u[:,:,k], k) + entropy_projection!(mass_solver, + conservation_law, + u_q[:, :, k], + u_f[:, k, :], + r_q[:, :, id], + f_f[:, :, id], + temp[:, :, id], + V, + Vᵀ, + R, + WJ[k], + u[:, :, k], + k) end -@inline @views function time_derivative!(dudt::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw,<:FluxDifferencingOperators, - <:AbstractMassMatrixSolver,<:FluxDifferencingForm}, k::Int) - - (; conservation_law, connectivity, form, mass_solver) = solver - (; inviscid_numerical_flux, two_point_flux) = form - (; f_f, u_q, r_q, u_f, CI) = solver.preallocated_arrays - (; S, C, Vᵀ, Rᵀ, Λ_q, BJf, halfnJq, halfnJf, n_f, - nodes_per_face) = solver.operators +@inline @views function time_derivative!(dudt::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw, + <:FluxDifferencingOperators, + <:AbstractMassMatrixSolver, + <:FluxDifferencingForm}, + k::Int) + (; conservation_law, connectivity, form, mass_solver) = solver + (; inviscid_numerical_flux, two_point_flux) = form + (; f_f, u_q, r_q, u_f, CI) = solver.preallocated_arrays + (; S, C, Vᵀ, Rᵀ, Λ_q, BJf, halfnJq, halfnJf, n_f, nodes_per_face) = solver.operators # get thread id for temporary register id = Threads.threadid() # evaluate interface numerical flux - numerical_flux!(f_f[:,:,id], conservation_law, inviscid_numerical_flux, - u_f[:,k,:], u_f[CI[connectivity[:,k]],:], n_f[:,:,k], two_point_flux) - + numerical_flux!(f_f[:, :, id], + conservation_law, + inviscid_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + n_f[:, :, k], + two_point_flux) + # scale numerical flux by quadrature weights - lmul!(BJf[k], f_f[:,:,id]) + lmul!(BJf[k], f_f[:, :, id]) # volume flux differencing term - flux_difference!(r_q[:,:,id], S, conservation_law, - two_point_flux, Λ_q[:,:,:,k], u_q[:,:,k]) - + flux_difference!(r_q[:, :, id], + S, + conservation_law, + two_point_flux, + Λ_q[:, :, :, k], + u_q[:, :, k]) + # apply facet correction term (if C is not nothing) - facet_correction!(r_q[:,:,id], f_f[:,:,id], C, conservation_law, - two_point_flux, halfnJf[:,:,k], halfnJq[:,:,:,k], - u_q[:,:,k], u_f[:,k,:], nodes_per_face) + facet_correction!(r_q[:, :, id], + f_f[:, :, id], + C, + conservation_law, + two_point_flux, + halfnJf[:, :, k], + halfnJq[:, :, :, k], + u_q[:, :, k], + u_f[:, k, :], + nodes_per_face) # apply facet operators - mul!(u_q[:,:,k], Rᵀ, f_f[:,:,id]) - r_q[:,:,id] .-= u_q[:,:,k] + mul!(u_q[:, :, k], Rᵀ, f_f[:, :, id]) + r_q[:, :, id] .-= u_q[:, :, k] # solve for time derivative - mul!(dudt[:,:,k], Vᵀ, r_q[:,:,id]) - mass_matrix_solve!(mass_solver, k, dudt[:,:,k], u_q[:,:,k]) -end \ No newline at end of file + mul!(dudt[:, :, k], Vᵀ, r_q[:, :, id]) + mass_matrix_solve!(mass_solver, k, dudt[:, :, k], u_q[:, :, k]) +end diff --git a/src/Solvers/linear.jl b/src/Solvers/linear.jl index 42f2541f..c601b234 100644 --- a/src/Solvers/linear.jl +++ b/src/Solvers/linear.jl @@ -3,17 +3,16 @@ struct LinearResidual{SolverType} <: LinearMap{Float64} solver::SolverType end -function Base.size(L::LinearResidual) +function Base.size(L::LinearResidual) (N_p, N_c, N_e) = size(L.solver) - return (N_p*N_c*N_e, N_p*N_c*N_e) + return (N_p * N_c * N_e, N_p * N_c * N_e) end -function LinearAlgebra.mul!(y::AbstractVector, L::LinearResidual, - x::AbstractVector) +function LinearAlgebra.mul!(y::AbstractVector, L::LinearResidual, x::AbstractVector) (N_p, N_c, N_e) = size(L.solver) - u = reshape(x,(N_p,N_c,N_e)) + u = reshape(x, (N_p, N_c, N_e)) dudt = similar(u) - semi_discrete_residual!(dudt,u,L.solver,0.0) + semi_discrete_residual!(dudt, u, L.solver, 0.0) y[:] = vec(dudt) return y -end \ No newline at end of file +end diff --git a/src/Solvers/mass_matrix.jl b/src/Solvers/mass_matrix.jl index dd143e0e..369c35d2 100644 --- a/src/Solvers/mass_matrix.jl +++ b/src/Solvers/mass_matrix.jl @@ -1,31 +1,29 @@ -struct CholeskySolver{V_type<:LinearMap} <: AbstractMassMatrixSolver +struct CholeskySolver{V_type <: LinearMap} <: AbstractMassMatrixSolver M::Vector{Cholesky{Float64, Matrix{Float64}}} WJ::Vector{Diagonal{Float64, Vector{Float64}}} V::V_type end -struct DiagonalSolver <: AbstractMassMatrixSolver +struct DiagonalSolver <: AbstractMassMatrixSolver WJ⁻¹::Vector{Diagonal{Float64, Vector{Float64}}} end -struct WeightAdjustedSolver{Minv_type, V_type<:LinearMap, - Vt_type<:LinearMap} <: AbstractMassMatrixSolver +struct WeightAdjustedSolver{Minv_type, V_type <: LinearMap, Vt_type <: LinearMap} <: + AbstractMassMatrixSolver M⁻¹::Minv_type J⁻¹W::Vector{Diagonal{Float64, Vector{Float64}}} V::V_type Vᵀ::Vt_type end -function default_mass_matrix_solver( - spatial_discretization::SpatialDiscretization, alg::AbstractOperatorAlgorithm=DefaultOperatorAlgorithm()) +function default_mass_matrix_solver(spatial_discretization::SpatialDiscretization, + alg::AbstractOperatorAlgorithm = DefaultOperatorAlgorithm()) (; V, W) = spatial_discretization.reference_approximation (; J_q) = spatial_discretization.geometric_factors - return WeightAdjustedSolver(J_q, V, W, alg, - Val(true), Float64(0.0)) + return WeightAdjustedSolver(J_q, V, W, alg, Val(true), Float64(0.0)) end -function CholeskySolver(J_q::Matrix{Float64}, V::UniformScalingMap, - W::Diagonal) +function CholeskySolver(J_q::Matrix{Float64}, V::UniformScalingMap, W::Diagonal) return DiagonalSolver(J_q, V, W) end @@ -34,37 +32,54 @@ function CholeskySolver(J_q::Matrix{Float64}, V::LinearMap, W::Diagonal) WJ = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) M = Vector{Cholesky{Float64, Matrix{Float64}}}(undef, N_e) @inbounds for k in 1:N_e - WJ[k] = Diagonal(W .* J_q[:,k]) + WJ[k] = Diagonal(W .* J_q[:, k]) M[k] = cholesky(Symmetric(Matrix(V' * WJ[k] * V))) end return CholeskySolver(M, WJ, V) end -function WeightAdjustedSolver(J_q::Matrix{Float64}, V::UniformScalingMap, - W::Diagonal, ::AbstractOperatorAlgorithm, ::Val{true}, ::Float64) +function WeightAdjustedSolver(J_q::Matrix{Float64}, + V::UniformScalingMap, + W::Diagonal, + ::AbstractOperatorAlgorithm, + ::Val{true}, + ::Float64) return DiagonalSolver(J_q, V, W) end -function WeightAdjustedSolver(J_q::Matrix{Float64}, V::UniformScalingMap, - W::Diagonal, ::AbstractOperatorAlgorithm, ::Val{false}, ::Float64) +function WeightAdjustedSolver(J_q::Matrix{Float64}, + V::UniformScalingMap, + W::Diagonal, + ::AbstractOperatorAlgorithm, + ::Val{false}, + ::Float64) return DiagonalSolver(J_q, V, W) end -function WeightAdjustedSolver(J_q::Matrix{Float64}, V::LinearMap, W::Diagonal, - operator_algorithm::AbstractOperatorAlgorithm, ::Val{true}, ::Float64) +function WeightAdjustedSolver(J_q::Matrix{Float64}, + V::LinearMap, + W::Diagonal, + operator_algorithm::AbstractOperatorAlgorithm, + ::Val{true}, + ::Float64) N_e = size(J_q, 2) J⁻¹W = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) @inbounds for k in 1:N_e - J⁻¹W[k] = Diagonal(W ./ J_q[:,k]) + J⁻¹W[k] = Diagonal(W ./ J_q[:, k]) end - return WeightAdjustedSolver(I, J⁻¹W, make_operator(V, operator_algorithm), - make_operator(V', operator_algorithm)) + return WeightAdjustedSolver(I, + J⁻¹W, + make_operator(V, operator_algorithm), + make_operator(V', operator_algorithm)) end -function WeightAdjustedSolver(J_q::Matrix{Float64}, V::LinearMap, W::Diagonal, - operator_algorithm::AbstractOperatorAlgorithm, ::Val{false}, tol::Float64) - +function WeightAdjustedSolver(J_q::Matrix{Float64}, + V::LinearMap, + W::Diagonal, + operator_algorithm::AbstractOperatorAlgorithm, + ::Val{false}, + tol::Float64) N_e = size(J_q, 2) VDM = Matrix(V) M = VDM' * W * VDM @@ -81,19 +96,20 @@ function WeightAdjustedSolver(J_q::Matrix{Float64}, V::LinearMap, W::Diagonal, J⁻¹W = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) for k in 1:N_e - J⁻¹W[k] = Diagonal(W ./ J_q[:,k]) + J⁻¹W[k] = Diagonal(W ./ J_q[:, k]) end - return WeightAdjustedSolver(M⁻¹, J⁻¹W, make_operator(V, operator_algorithm), - make_operator(V', operator_algorithm)) + return WeightAdjustedSolver(M⁻¹, + J⁻¹W, + make_operator(V, operator_algorithm), + make_operator(V', operator_algorithm)) end -function DiagonalSolver(J_q::Matrix{Float64}, - ::UniformScalingMap, W::Diagonal) +function DiagonalSolver(J_q::Matrix{Float64}, ::UniformScalingMap, W::Diagonal) N_e = size(J_q, 2) WJ⁻¹ = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) @inbounds for k in 1:N_e - WJ⁻¹[k] = inv(Diagonal(W .* J_q[:,k])) + WJ⁻¹[k] = inv(Diagonal(W .* J_q[:, k])) end return DiagonalSolver(WJ⁻¹) end @@ -101,7 +117,7 @@ end function CholeskySolver(spatial_discretization::SpatialDiscretization) (; V, W) = spatial_discretization.reference_approximation (; J_q) = spatial_discretization.geometric_factors - + return CholeskySolver(J_q, V, W) end @@ -111,16 +127,14 @@ function DiagonalSolver(spatial_discretization::SpatialDiscretization) return DiagonalSolver(J_q, V, W) end -function WeightAdjustedSolver(spatial_discretization::SpatialDiscretization, - operator_algorithm=DefaultOperatorAlgorithm(); - assume_orthonormal::Bool=true, - tol=1.0e-13) - +function WeightAdjustedSolver(spatial_discretization::SpatialDiscretization, + operator_algorithm = DefaultOperatorAlgorithm(); + assume_orthonormal::Bool = true, + tol = 1.0e-13,) (; V, W) = spatial_discretization.reference_approximation (; J_q) = spatial_discretization.geometric_factors - return WeightAdjustedSolver(J_q,V,W,operator_algorithm, - Val(assume_orthonormal), tol) + return WeightAdjustedSolver(J_q, V, W, operator_algorithm, Val(assume_orthonormal), tol) end @inline function mass_matrix(mass_solver::CholeskySolver, k::Int) @@ -134,13 +148,13 @@ end end @inline function mass_matrix(mass_solver::WeightAdjustedSolver, k::Int) - (; M⁻¹,J⁻¹W, V, Vᵀ) = mass_solver - return inv(Matrix(M⁻¹*Vᵀ*J⁻¹W[k]*V*M⁻¹)) + (; M⁻¹, J⁻¹W, V, Vᵀ) = mass_solver + return inv(Matrix(M⁻¹ * Vᵀ * J⁻¹W[k] * V * M⁻¹)) end @inline function mass_matrix_inverse(mass_solver::CholeskySolver, k::Int) (; WJ, V) = mass_solver - return inv(Matrix(V'*WJ[k]*V)) + return inv(Matrix(V' * WJ[k] * V)) end @inline function mass_matrix_inverse(mass_solver::DiagonalSolver, k::Int) @@ -149,23 +163,29 @@ end @inline function mass_matrix_inverse(mass_solver::WeightAdjustedSolver, k::Int) (; M⁻¹, Vᵀ, J⁻¹W, V) = mass_solver - return Matrix(M⁻¹*Vᵀ*J⁻¹W[k]*V*M⁻¹) + return Matrix(M⁻¹ * Vᵀ * J⁻¹W[k] * V * M⁻¹) end -@inline function mass_matrix_solve!(mass_solver::CholeskySolver, k::Int, - rhs::AbstractMatrix, ::AbstractMatrix) +@inline function mass_matrix_solve!(mass_solver::CholeskySolver, + k::Int, + rhs::AbstractMatrix, + ::AbstractMatrix) ldiv!(mass_solver.M[k], rhs) return rhs end -@inline function mass_matrix_solve!(mass_solver::DiagonalSolver, k::Int, - rhs::AbstractMatrix, ::AbstractMatrix) +@inline function mass_matrix_solve!(mass_solver::DiagonalSolver, + k::Int, + rhs::AbstractMatrix, + ::AbstractMatrix) lmul!(mass_solver.WJ⁻¹[k], rhs) return rhs end -@inline function mass_matrix_solve!(mass_solver::WeightAdjustedSolver, k::Int, - rhs::AbstractMatrix, temp::AbstractMatrix) +@inline function mass_matrix_solve!(mass_solver::WeightAdjustedSolver, + k::Int, + rhs::AbstractMatrix, + temp::AbstractMatrix) (; M⁻¹, Vᵀ, J⁻¹W, V) = mass_solver lmul!(M⁻¹, rhs) mul!(temp, V, rhs) @@ -173,4 +193,4 @@ end mul!(rhs, Vᵀ, temp) lmul!(M⁻¹, rhs) return rhs -end \ No newline at end of file +end diff --git a/src/Solvers/operators.jl b/src/Solvers/operators.jl index f925f7e5..770e2590 100644 --- a/src/Solvers/operators.jl +++ b/src/Solvers/operators.jl @@ -1,196 +1,216 @@ -function ReferenceOperators( - reference_approximation::ReferenceApproximation{<:RefElemData{d}}, - alg::AbstractOperatorAlgorithm, Λ_q::Array{Float64,4}, - nJf::Array{Float64,3}, J_f::Matrix{Float64}) where {d} - +function ReferenceOperators(reference_approximation::ReferenceApproximation{<:RefElemData{d}}, + alg::AbstractOperatorAlgorithm, + Λ_q::Array{Float64, 4}, + nJf::Array{Float64, 3}, + J_f::Matrix{Float64}) where {d} (; D, W, V, R, B) = reference_approximation (N_f, N_e) = size(J_f) - halfWΛ = Array{Diagonal{Float64, Vector{Float64}},3}(undef, d, d, N_e) + halfWΛ = Array{Diagonal{Float64, Vector{Float64}}, 3}(undef, d, d, N_e) halfN = Matrix{Diagonal{Float64, Vector{Float64}}}(undef, d, N_e) BJf = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) - n_f = Array{Float64,3}(undef, d, N_f, N_e) + n_f = Array{Float64, 3}(undef, d, N_f, N_e) @inbounds for k in 1:N_e for m in 1:d - halfWΛ[m,:,k] .= [Diagonal(0.5 * W * Λ_q[:,m,n,k]) for n in 1:d] - n_f[m,:,k] .= nJf[m,:,k] ./ J_f[:,k] - halfN[m,k] = Diagonal(0.5 * n_f[m,:,k]) + halfWΛ[m, :, k] .= [Diagonal(0.5 * W * Λ_q[:, m, n, k]) for n in 1:d] + n_f[m, :, k] .= nJf[m, :, k] ./ J_f[:, k] + halfN[m, k] = Diagonal(0.5 * n_f[m, :, k]) end - BJf[k] = Diagonal(B .* J_f[:,k]) + BJf[k] = Diagonal(B .* J_f[:, k]) end - return ReferenceOperators(Tuple(make_operator(D[m], alg) for m in 1:d), - Tuple(transpose(make_operator(D[m], alg)) for m in 1:d), - make_operator(V, alg), transpose(make_operator(V, alg)), - make_operator(R, alg), transpose(make_operator(R, alg)), - W, B, halfWΛ, halfN, BJf, n_f) + return ReferenceOperators(Tuple(make_operator(D[m], alg) for m in 1:d), + Tuple(transpose(make_operator(D[m], alg)) for m in 1:d), + make_operator(V, alg), + transpose(make_operator(V, alg)), + make_operator(R, alg), + transpose(make_operator(R, alg)), + W, + B, + halfWΛ, + halfN, + BJf, + n_f) end -function FluxDifferencingOperators( - reference_approximation::ReferenceApproximation{<:RefElemData{d}}, - alg::AbstractOperatorAlgorithm, J_q::Matrix{Float64}, - Λ_q::Array{Float64,4}, nJq::Array{Float64,4}, nJf::Array{Float64,3}, - J_f::Matrix{Float64}) where {d} - - (; D, W, V, R, B, approx_type, reference_element, - reference_mapping) = reference_approximation +function FluxDifferencingOperators(reference_approximation::ReferenceApproximation{<:RefElemData{d}}, + alg::AbstractOperatorAlgorithm, + J_q::Matrix{Float64}, + Λ_q::Array{Float64, 4}, + nJq::Array{Float64, 4}, + nJf::Array{Float64, 3}, + J_f::Matrix{Float64}) where {d} + (; D, W, V, R, B, approx_type, reference_element, reference_mapping) = reference_approximation (N_f, N_e) = size(J_f) WJ = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) BJf = Vector{Diagonal{Float64, Vector{Float64}}}(undef, N_e) - n_f = Array{Float64,3}(undef, d, N_f, N_e) - + n_f = Array{Float64, 3}(undef, d, N_f, N_e) + @inbounds for k in 1:N_e - WJ[k] = Diagonal(W .* J_q[:,k]) - BJf[k] = Diagonal(B .* J_f[:,k]) + WJ[k] = Diagonal(W .* J_q[:, k]) + BJf[k] = Diagonal(B .* J_f[:, k]) @inbounds for m in 1:d - n_f[m,:,k] = nJf[m,:,k] ./ J_f[:,k] + n_f[m, :, k] = nJf[m, :, k] ./ J_f[:, k] end end - + D_ξ = reference_derivative_operators(D, reference_mapping) S, C = flux_differencing_operators(approx_type, D_ξ, W, R, B) - return FluxDifferencingOperators(S, C, make_operator(V, alg), - transpose(make_operator(V, alg)), make_operator(R, alg), - transpose(make_operator(R, alg)), W, B, WJ, Λ_q, BJf, n_f, 0.5*nJf, - 0.5*nJq, N_f÷num_faces(reference_element.element_type)) + return FluxDifferencingOperators(S, + C, + make_operator(V, alg), + transpose(make_operator(V, alg)), + make_operator(R, alg), + transpose(make_operator(R, alg)), + W, + B, + WJ, + Λ_q, + BJf, + n_f, + 0.5 * nJf, + 0.5 * nJq, + N_f ÷ num_faces(reference_element.element_type)) end -function PhysicalOperators(spatial_discretization::SpatialDiscretization{1}, - ::StandardForm{StandardMapping}, alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver) - +function PhysicalOperators(spatial_discretization::SpatialDiscretization{1}, + ::StandardForm{StandardMapping}, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver) (; N_e, reference_approximation) = spatial_discretization (; D, V, R, W, B) = reference_approximation (; nJf) = spatial_discretization.geometric_factors - VOL = Vector{NTuple{1,LinearMap}}(undef,N_e) - FAC = Vector{LinearMap}(undef,N_e) + VOL = Vector{NTuple{1, LinearMap}}(undef, N_e) + FAC = Vector{LinearMap}(undef, N_e) @inbounds for k in 1:N_e M⁻¹ = mass_matrix_inverse(mass_solver, k) VOL[k] = (make_operator(M⁻¹ * Matrix(V' * D[1]' * W), alg),) FAC[k] = make_operator(-M⁻¹ * Matrix(V' * R' * B), alg) end - return PhysicalOperators( - VOL, FAC, make_operator(V, alg), make_operator(R, alg), nJf) + return PhysicalOperators(VOL, FAC, make_operator(V, alg), make_operator(R, alg), nJf) end -function PhysicalOperators(spatial_discretization::SpatialDiscretization{d}, - ::StandardForm{StandardMapping}, alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver) where {d} - +function PhysicalOperators(spatial_discretization::SpatialDiscretization{d}, + ::StandardForm{StandardMapping}, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver) where {d} (; N_e, reference_approximation, geometric_factors) = spatial_discretization (; V, R, W, B, D, N_f) = reference_approximation (; Λ_q, nJf, J_f) = apply_reference_mapping(geometric_factors, - reference_approximation.reference_mapping) - - VOL = Vector{NTuple{d,LinearMap}}(undef,N_e) - FAC = Vector{LinearMap}(undef,N_e) - n_f = Array{Float64,3}(undef, d, N_f, N_e) + reference_approximation.reference_mapping) + + VOL = Vector{NTuple{d, LinearMap}}(undef, N_e) + FAC = Vector{LinearMap}(undef, N_e) + n_f = Array{Float64, 3}(undef, d, N_f, N_e) - @inbounds for k in 1:N_e + @inbounds for k in 1:N_e M⁻¹ = mass_matrix_inverse(mass_solver, k) - VOL[k] = Tuple(make_operator(M⁻¹ * Matrix(V' * - sum(D[m]' * Diagonal(W * Λ_q[:,m,n,k]) for m in 1:d)), alg) - for n in 1:d) - FAC[k] = make_operator(-M⁻¹ * - Matrix(V' * R' * Diagonal(B * J_f[:,k])), alg) + VOL[k] = Tuple(make_operator(M⁻¹ * + Matrix(V' * sum(D[m]' * Diagonal(W * Λ_q[:, m, n, k]) + for m in 1:d)), + alg) for n in 1:d) + FAC[k] = make_operator(-M⁻¹ * Matrix(V' * R' * Diagonal(B * J_f[:, k])), alg) @inbounds for m in 1:d - n_f[m,:,k] = nJf[m,:,k] ./ J_f[:,k] + n_f[m, :, k] = nJf[m, :, k] ./ J_f[:, k] end end - return PhysicalOperators( - VOL, FAC, make_operator(V, alg), make_operator(R, alg), n_f) + return PhysicalOperators(VOL, FAC, make_operator(V, alg), make_operator(R, alg), n_f) end -function PhysicalOperators(spatial_discretization::SpatialDiscretization{d}, - ::StandardForm{SkewSymmetricMapping}, - alg::AbstractOperatorAlgorithm, - mass_solver::AbstractMassMatrixSolver) where {d} - +function PhysicalOperators(spatial_discretization::SpatialDiscretization{d}, + ::StandardForm{SkewSymmetricMapping}, + alg::AbstractOperatorAlgorithm, + mass_solver::AbstractMassMatrixSolver) where {d} (; N_e, reference_approximation, geometric_factors) = spatial_discretization (; V, R, W, B, D, N_f) = reference_approximation (; Λ_q, nJf, J_f) = apply_reference_mapping(geometric_factors, - reference_approximation.reference_mapping) - - VOL = Vector{NTuple{d,LinearMap}}(undef,N_e) - FAC = Vector{LinearMap}(undef,N_e) - n_f = Array{Float64,3}(undef, d, N_f, N_e) + reference_approximation.reference_mapping) + + VOL = Vector{NTuple{d, LinearMap}}(undef, N_e) + FAC = Vector{LinearMap}(undef, N_e) + n_f = Array{Float64, 3}(undef, d, N_f, N_e) @inbounds for k in 1:N_e M⁻¹ = mass_matrix_inverse(mass_solver, k) - VOL[k] = Tuple(make_operator(M⁻¹ * Matrix(V' * - (sum(D[m]' * Diagonal(0.5 * W * Λ_q[:,m,n,k]) - - Diagonal(0.5 * W * Λ_q[:,m,n,k]) * D[m] for m in 1:d) + - R' * Diagonal(0.5 * B * nJf[n,:,k]) * R)), alg) for n in 1:d) - FAC[k] = make_operator(-M⁻¹ * - Matrix(V' * R' * Diagonal(B * J_f[:,k])), alg) + VOL[k] = Tuple(make_operator(M⁻¹ * Matrix(V' * (sum(D[m]' * + Diagonal(0.5 * W * Λ_q[:, m, n, k]) - + Diagonal(0.5 * W * Λ_q[:, m, n, k]) * D[m] + for m in 1:d) + + R' * Diagonal(0.5 * B * nJf[n, :, k]) * R)), + alg) for n in 1:d) + FAC[k] = make_operator(-M⁻¹ * Matrix(V' * R' * Diagonal(B * J_f[:, k])), alg) @inbounds for m in 1:d - n_f[m,:,k] = nJf[m,:,k] ./ J_f[:,k] + n_f[m, :, k] = nJf[m, :, k] ./ J_f[:, k] end end - return PhysicalOperators( - VOL, FAC, make_operator(V, alg), make_operator(R, alg), n_f) + return PhysicalOperators(VOL, FAC, make_operator(V, alg), make_operator(R, alg), n_f) end # ensure one-dimensional schemes don't use sparse operators unnecessarily -function flux_differencing_operators( - ::AbstractTensorProduct, D_ξ::NTuple{1,LinearMap}, - W::Diagonal, ::SelectionMap, ::Diagonal) - - S = (0.5*Matrix(W*D_ξ[1] - D_ξ[1]'*W),) +function flux_differencing_operators(::AbstractTensorProduct, + D_ξ::NTuple{1, LinearMap}, + W::Diagonal, + ::SelectionMap, + ::Diagonal) + S = (0.5 * Matrix(W * D_ξ[1] - D_ξ[1]' * W),) return S, nothing end -function flux_differencing_operators( - ::AbstractTensorProduct, D_ξ::NTuple{1,LinearMap}, - W::Diagonal, R::LinearMap, B::Diagonal) - - S = (0.5*Matrix(W*D_ξ[1] - D_ξ[1]'*W),) - return S, Matrix(R')*Matrix(B) +function flux_differencing_operators(::AbstractTensorProduct, + D_ξ::NTuple{1, LinearMap}, + W::Diagonal, + R::LinearMap, + B::Diagonal) + S = (0.5 * Matrix(W * D_ξ[1] - D_ξ[1]' * W),) + return S, Matrix(R') * Matrix(B) end - # sparse version for tensor-product operators -function flux_differencing_operators( - ::AbstractTensorProduct, D_ξ::NTuple{d,LinearMap}, - W::Diagonal, ::SelectionMap, ::Diagonal) where {d} +function flux_differencing_operators(::AbstractTensorProduct, + D_ξ::NTuple{d, LinearMap}, + W::Diagonal, + ::SelectionMap, + ::Diagonal) where {d} + S = Tuple(0.5 * Matrix(W * D_ξ[m] - D_ξ[m]' * W) for m in 1:d) - S = Tuple(0.5*Matrix(W*D_ξ[m] - D_ξ[m]'*W) for m in 1:d) - return Tuple(sparse(S[m]) for m in 1:d), nothing end -function flux_differencing_operators( - ::AbstractTensorProduct, D_ξ::NTuple{d,LinearMap}, - W::Diagonal, R::LinearMap, B::Diagonal) where {d} +function flux_differencing_operators(::AbstractTensorProduct, + D_ξ::NTuple{d, LinearMap}, + W::Diagonal, + R::LinearMap, + B::Diagonal) where {d} + S = Tuple(0.5 * Matrix(W * D_ξ[m] - D_ξ[m]' * W) for m in 1:d) - S = Tuple(0.5*Matrix(W*D_ξ[m] - D_ξ[m]'*W) for m in 1:d) - - return Tuple(sparse(S[m]) for m in 1:d), sparse(Matrix(R')*Matrix(B)) + return Tuple(sparse(S[m]) for m in 1:d), sparse(Matrix(R') * Matrix(B)) end # dense version -function flux_differencing_operators( - ::AbstractMultidimensional, D_ξ::NTuple{d,LinearMap}, - W::Diagonal, ::SelectionMap, ::Diagonal) where {d} +function flux_differencing_operators(::AbstractMultidimensional, + D_ξ::NTuple{d, LinearMap}, + W::Diagonal, + ::SelectionMap, + ::Diagonal) where {d} + S = Tuple(0.5 * Matrix(W * D_ξ[m] - D_ξ[m]' * W) for m in 1:d) - S = Tuple(0.5*Matrix(W*D_ξ[m] - D_ξ[m]'*W) for m in 1:d) - return S, nothing end -function flux_differencing_operators( - ::AbstractMultidimensional, D_ξ::NTuple{d,LinearMap}, - W::Diagonal, R::LinearMap, B::Diagonal) where {d} +function flux_differencing_operators(::AbstractMultidimensional, + D_ξ::NTuple{d, LinearMap}, + W::Diagonal, + R::LinearMap, + B::Diagonal) where {d} + S = Tuple(0.5 * Matrix(W * D_ξ[m] - D_ξ[m]' * W) for m in 1:d) - S = Tuple(0.5*Matrix(W*D_ξ[m] - D_ξ[m]'*W) for m in 1:d) - - return S, Matrix(R')*Matrix(B) -end \ No newline at end of file + return S, Matrix(R') * Matrix(B) +end diff --git a/src/Solvers/standard_form_first_order.jl b/src/Solvers/standard_form_first_order.jl index 9d9f977c..42d82963 100644 --- a/src/Solvers/standard_form_first_order.jl +++ b/src/Solvers/standard_form_first_order.jl @@ -1,78 +1,94 @@ -@inline @views function nodal_values!(u::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw,<:AbstractDiscretizationOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, k::Int) - +@inline @views function nodal_values!(u::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw, + <:AbstractDiscretizationOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + k::Int) (; u_q, u_f) = solver.preallocated_arrays (; V, R) = solver.operators - mul!(u_q[:,:,k], V, u[:,:,k]) - mul!(u_f[:,k,:], R, u_q[:,:,k]) - + mul!(u_q[:, :, k], V, u[:, :, k]) + mul!(u_f[:, k, :], R, u_q[:, :, k]) + return end -@inline @views function time_derivative!(dudt::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{d,FirstOrder},<:ReferenceOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, k::Int) where {d} - +@inline @views function time_derivative!(dudt::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{d, + FirstOrder}, + <:ReferenceOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + k::Int) where {d} (; conservation_law, connectivity, form) = solver (; inviscid_numerical_flux) = form (; f_q, f_f, f_n, u_q, r_q, u_f, temp, CI) = solver.preallocated_arrays (; D, Dᵀ, Vᵀ, R, Rᵀ, halfWΛ, halfN, BJf, n_f) = solver.operators id = Threads.threadid() - physical_flux!(f_q[:,:,:,id], conservation_law, u_q[:,:,k]) - numerical_flux!(f_f[:,:,id], conservation_law, inviscid_numerical_flux, - u_f[:,k,:], u_f[CI[connectivity[:,k]],:], n_f[:,:,k]) + physical_flux!(f_q[:, :, :, id], conservation_law, u_q[:, :, k]) + numerical_flux!(f_f[:, :, id], + conservation_law, + inviscid_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + n_f[:, :, k]) - fill!(r_q[:,:,id], 0.0) + fill!(r_q[:, :, id], 0.0) @inbounds for n in 1:d # apply volume operators @inbounds for m in 1:d - mul!(temp[:,:,id],halfWΛ[m,n,k],f_q[:,:,n,id]) - mul!(u_q[:,:,k],Dᵀ[m],temp[:,:,id]) - r_q[:,:,id] .+= u_q[:,:,k] - mul!(u_q[:,:,k],D[m],f_q[:,:,n,id]) - lmul!(halfWΛ[m,n,k],u_q[:,:,k]) - r_q[:,:,id] .-= u_q[:,:,k] + mul!(temp[:, :, id], halfWΛ[m, n, k], f_q[:, :, n, id]) + mul!(u_q[:, :, k], Dᵀ[m], temp[:, :, id]) + r_q[:, :, id] .+= u_q[:, :, k] + mul!(u_q[:, :, k], D[m], f_q[:, :, n, id]) + lmul!(halfWΛ[m, n, k], u_q[:, :, k]) + r_q[:, :, id] .-= u_q[:, :, k] end # difference facet flux - mul!(f_n[:,:,id], R, f_q[:,:,n,id]) - lmul!(halfN[n,k], f_n[:,:,id]) - f_f[:,:,id] .-= f_n[:,:,id] + mul!(f_n[:, :, id], R, f_q[:, :, n, id]) + lmul!(halfN[n, k], f_n[:, :, id]) + f_f[:, :, id] .-= f_n[:, :, id] end # apply facet operators - lmul!(BJf[k], f_f[:,:,id]) - mul!(temp[:,:,id], Rᵀ, f_f[:,:,id]) - r_q[:,:,id] .-= temp[:,:,id] + lmul!(BJf[k], f_f[:, :, id]) + mul!(temp[:, :, id], Rᵀ, f_f[:, :, id]) + r_q[:, :, id] .-= temp[:, :, id] # solve for time derivative - mul!(dudt[:,:,k], Vᵀ, r_q[:,:,id]) - mass_matrix_solve!(solver.mass_solver, k, dudt[:,:,k], temp[:,:,id]) + mul!(dudt[:, :, k], Vᵀ, r_q[:, :, id]) + mass_matrix_solve!(solver.mass_solver, k, dudt[:, :, k], temp[:, :, id]) end -@inline @views function time_derivative!(dudt::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{d,FirstOrder},<:PhysicalOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, k::Int) where {d} - +@inline @views function time_derivative!(dudt::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{d, + FirstOrder}, + <:PhysicalOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + k::Int) where {d} (; conservation_law, operators, connectivity, form) = solver (; VOL, FAC, n_f) = operators (; inviscid_numerical_flux) = form (; f_q, f_f, u_q, u_f, temp, CI) = solver.preallocated_arrays id = Threads.threadid() - physical_flux!(f_q[:,:,:,id], conservation_law, u_q[:,:,k]) - numerical_flux!(f_f[:,:,id], conservation_law, inviscid_numerical_flux, - u_f[:,k,:], u_f[CI[connectivity[:,k]],:], n_f[:,:,k]) - - fill!(dudt[:,:,k],0.0) + physical_flux!(f_q[:, :, :, id], conservation_law, u_q[:, :, k]) + numerical_flux!(f_f[:, :, id], + conservation_law, + inviscid_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + n_f[:, :, k]) + + fill!(dudt[:, :, k], 0.0) @inbounds for m in 1:d - mul!(temp[:,:,id], VOL[k][m], f_q[:,:,m,id]) - dudt[:,:,k] .+= temp[:,:,id] + mul!(temp[:, :, id], VOL[k][m], f_q[:, :, m, id]) + dudt[:, :, k] .+= temp[:, :, id] end - mul!(temp[:,:,id], FAC[k], f_f[:,:,id]) - dudt[:,:,k] .+= temp[:,:,id] -end \ No newline at end of file + mul!(temp[:, :, id], FAC[k], f_f[:, :, id]) + dudt[:, :, k] .+= temp[:, :, id] +end diff --git a/src/Solvers/standard_form_second_order.jl b/src/Solvers/standard_form_second_order.jl index a3ac6d4d..c8a947c6 100644 --- a/src/Solvers/standard_form_second_order.jl +++ b/src/Solvers/standard_form_second_order.jl @@ -1,52 +1,71 @@ -@inline @views function auxiliary_variable!(dudt::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{d,SecondOrder},<:PhysicalOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, k::Int) where {d} - +@inline @views function auxiliary_variable!(dudt::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{d, + SecondOrder}, + <:PhysicalOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + k::Int) where {d} (; conservation_law, operators, connectivity, form) = solver (; V, R, VOL, FAC, n_f) = operators (; viscous_numerical_flux) = form (; u_q, u_f, u_n, temp, CI, q_q, q_f) = solver.preallocated_arrays id = Threads.threadid() - numerical_flux!(u_n[:,:,:,id], conservation_law, viscous_numerical_flux, - u_f[:,k,:], u_f[CI[connectivity[:,k]],:], n_f[:,:,k]) - + numerical_flux!(u_n[:, :, :, id], + conservation_law, + viscous_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + n_f[:, :, k]) + @inbounds for m in 1:d - fill!(dudt[:,:,k],0.0) - mul!(temp[:,:,id], VOL[k][m], u_q[:,:,k]) - dudt[:,:,k] .-= temp[:,:,id] + fill!(dudt[:, :, k], 0.0) + mul!(temp[:, :, id], VOL[k][m], u_q[:, :, k]) + dudt[:, :, k] .-= temp[:, :, id] - mul!(temp[:,:,id], FAC[k], u_n[:,:,m,id]) - dudt[:,:,k] .-= temp[:,:,id] + mul!(temp[:, :, id], FAC[k], u_n[:, :, m, id]) + dudt[:, :, k] .-= temp[:, :, id] - mul!(q_q[:,:,m,k], V, dudt[:,:,k]) - mul!(q_f[:,k,:,m], R, q_q[:,:,m,k]) + mul!(q_q[:, :, m, k], V, dudt[:, :, k]) + mul!(q_f[:, k, :, m], R, q_q[:, :, m, k]) end end -@inline @views function time_derivative!(dudt::AbstractArray{Float64,3}, - solver::Solver{<:AbstractConservationLaw{d,SecondOrder},<:PhysicalOperators, - <:AbstractMassMatrixSolver,<:StandardForm}, k::Int) where {d} - +@inline @views function time_derivative!(dudt::AbstractArray{Float64, 3}, + solver::Solver{<:AbstractConservationLaw{d, + SecondOrder}, + <:PhysicalOperators, + <:AbstractMassMatrixSolver, + <:StandardForm}, + k::Int) where {d} (; conservation_law, operators, connectivity, form) = solver (; VOL, FAC, n_f) = operators (; inviscid_numerical_flux, viscous_numerical_flux) = form (; f_q, f_f, u_q, u_f, temp, CI, q_q, q_f) = solver.preallocated_arrays id = Threads.threadid() - physical_flux!(f_q[:,:,:,id], conservation_law, u_q[:,:,k], q_q[:,:,:,k]) - numerical_flux!(f_f[:,:,id],conservation_law, inviscid_numerical_flux, - u_f[:,k,:], u_f[CI[connectivity[:,k]],:], n_f[:,:,k]) - numerical_flux!(f_f[:,:,id], conservation_law, viscous_numerical_flux, - u_f[:,k,:], u_f[CI[connectivity[:,k]],:], q_f[:,k,:,:], - q_f[CI[connectivity[:,k]],:,:], n_f[:,:,k]) - - fill!(dudt[:,:,k],0.0) + physical_flux!(f_q[:, :, :, id], conservation_law, u_q[:, :, k], q_q[:, :, :, k]) + numerical_flux!(f_f[:, :, id], + conservation_law, + inviscid_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + n_f[:, :, k]) + numerical_flux!(f_f[:, :, id], + conservation_law, + viscous_numerical_flux, + u_f[:, k, :], + u_f[CI[connectivity[:, k]], :], + q_f[:, k, :, :], + q_f[CI[connectivity[:, k]], :, :], + n_f[:, :, k]) + + fill!(dudt[:, :, k], 0.0) @inbounds for m in 1:d - mul!(temp[:,:,id], VOL[k][m], f_q[:,:,m,id]) - dudt[:,:,k] .+= temp[:,:,id] + mul!(temp[:, :, id], VOL[k][m], f_q[:, :, m, id]) + dudt[:, :, k] .+= temp[:, :, id] end - mul!(temp[:,:,id], FAC[k], f_f[:,:,id]) - dudt[:,:,k] .+= temp[:,:,id] -end \ No newline at end of file + mul!(temp[:, :, id], FAC[k], f_f[:, :, id]) + dudt[:, :, k] .+= temp[:, :, id] +end diff --git a/src/SpatialDiscretizations/SpatialDiscretizations.jl b/src/SpatialDiscretizations/SpatialDiscretizations.jl index 5d91bf30..09f1109a 100644 --- a/src/SpatialDiscretizations/SpatialDiscretizations.jl +++ b/src/SpatialDiscretizations/SpatialDiscretizations.jl @@ -1,307 +1,422 @@ module SpatialDiscretizations - using LinearAlgebra: I, inv, Diagonal, diagm, kron, transpose, det, eigvals, mul! - using Random: rand, shuffle - using LinearMaps: LinearMap, ⊗ - using StartUpDG: MeshData, basis, vandermonde, grad_vandermonde, diagE_sbp_nodes, quad_nodes, NodesAndModes.quad_nodes_tri, NodesAndModes.quad_nodes_tet, face_vertices, nodes, num_faces, find_face_nodes, init_face_data, equi_nodes, face_type, Polynomial, jacobiP, match_coordinate_vectors,uniform_mesh, make_periodic, jaskowiec_sukumar_quad_nodes, Hicken, geometric_factors, MultidimensionalQuadrature - using Jacobi: zgrjm, wgrjm, zgj, wgj, zglj, wglj - - using ..MatrixFreeOperators - - using Reexport - @reexport using StartUpDG: RefElemData, AbstractElemShape, Line, Quad, Tri, Tet, Hex, SBP - @reexport using StaticArrays: SArray, SMatrix, SVector - - export AbstractApproximationType, AbstractTensorProduct, AbstractMultidimensional, - NodalTensor, ModalTensor, ModalMulti, NodalMulti, ModalMultiDiagE, NodalMultiDiagE, AbstractReferenceMapping, AbstractMetrics, ExactMetrics, ConservativeCurlMetrics, ChanWilcoxMetrics, NoMapping, ReferenceApproximation, GeometricFactors, SpatialDiscretization, apply_reference_mapping, reference_derivative_operators, check_normals, check_facet_nodes, check_sbp_property, centroids, trace_constant, dim, χ, warped_product - - abstract type AbstractApproximationType end - abstract type AbstractTensorProduct <: AbstractApproximationType end - abstract type AbstractMultidimensional <: AbstractApproximationType end - - """Nodal approximation using tensor-product operators""" - struct NodalTensor <: AbstractTensorProduct - p::Int +using LinearAlgebra: I, inv, Diagonal, diagm, kron, transpose, det, eigvals, mul! +using Random: rand, shuffle +using LinearMaps: LinearMap, ⊗ +using StartUpDG: + MeshData, + basis, + vandermonde, + grad_vandermonde, + diagE_sbp_nodes, + quad_nodes, + NodesAndModes.quad_nodes_tri, + NodesAndModes.quad_nodes_tet, + face_vertices, + nodes, + num_faces, + find_face_nodes, + init_face_data, + equi_nodes, + face_type, + Polynomial, + jacobiP, + match_coordinate_vectors, + uniform_mesh, + make_periodic, + jaskowiec_sukumar_quad_nodes, + Hicken, + geometric_factors, + MultidimensionalQuadrature +using Jacobi: zgrjm, wgrjm, zgj, wgj, zglj, wglj + +using ..MatrixFreeOperators + +using Reexport +@reexport using StartUpDG: RefElemData, AbstractElemShape, Line, Quad, Tri, Tet, Hex, SBP +@reexport using StaticArrays: SArray, SMatrix, SVector + +export AbstractApproximationType, + AbstractTensorProduct, + AbstractMultidimensional, + NodalTensor, + ModalTensor, + ModalMulti, + NodalMulti, + ModalMultiDiagE, + NodalMultiDiagE, + AbstractReferenceMapping, + AbstractMetrics, + ExactMetrics, + ConservativeCurlMetrics, + ChanWilcoxMetrics, + NoMapping, + ReferenceApproximation, + GeometricFactors, + SpatialDiscretization, + apply_reference_mapping, + reference_derivative_operators, + check_normals, + check_facet_nodes, + check_sbp_property, + centroids, + trace_constant, + dim, + χ, + warped_product + +abstract type AbstractApproximationType end +abstract type AbstractTensorProduct <: AbstractApproximationType end +abstract type AbstractMultidimensional <: AbstractApproximationType end + +"""Nodal approximation using tensor-product operators""" +struct NodalTensor <: AbstractTensorProduct + p::Int +end + +"""Modal approximation using tensor-product operators""" +struct ModalTensor <: AbstractTensorProduct + p::Int +end + +"""Modal approximation using multidimensional operators""" +struct ModalMulti <: AbstractMultidimensional + p::Int +end + +"""Nodal approximation using multidimensional operators""" +struct NodalMulti <: AbstractMultidimensional + p::Int +end + +"""Modal approximation using diagonal-E SBP operators""" +struct ModalMultiDiagE <: AbstractMultidimensional + p::Int +end + +"""Nodal approximation using diagonal-E SBP operators""" +struct NodalMultiDiagE <: AbstractMultidimensional + p::Int +end + +"""Collapsed coordinate mapping χ: [-1,1]ᵈ → Ωᵣ""" +abstract type AbstractReferenceMapping end +struct NoMapping <: AbstractReferenceMapping end +struct ReferenceMapping <: AbstractReferenceMapping + J_ref::Vector{Float64} + Λ_ref::Array{Float64, 3} +end + +abstract type AbstractMetrics end +struct ExactMetrics <: AbstractMetrics end +struct ConservativeCurlMetrics <: AbstractMetrics end +const ChanWilcoxMetrics = ConservativeCurlMetrics + +"""Operators for local approximation on reference element""" +struct ReferenceApproximation{RefElemType, + ApproxType, + D_type, + V_type, + Vf_type, + R_type, + V_plot_type, + ReferenceMappingType} + approx_type::ApproxType + N_p::Int + N_q::Int + N_f::Int + reference_element::RefElemType + D::D_type + V::V_type + Vf::Vf_type + R::R_type + W::Diagonal{Float64, Vector{Float64}} + B::Diagonal{Float64, Vector{Float64}} + V_plot::V_plot_type + reference_mapping::ReferenceMappingType + + function ReferenceApproximation(approx_type::ApproxType, + reference_element::RefElemType, + D::D_type, + V::V_type, + Vf::Vf_type, + R::R_type, + V_plot::V_plot_type, + reference_mapping::ReferenceMappingType = NoMapping()) where { + RefElemType, + ApproxType, + D_type, + V_type, + Vf_type, + R_type, + V_plot_type, + ReferenceMappingType + } + return new{RefElemType, + ApproxType, + D_type, + V_type, + Vf_type, + R_type, + V_plot_type, + ReferenceMappingType}(approx_type, + size(V, 2), + size(V, 1), + size(R, 1), + reference_element, + D, + V, + Vf, + R, + Diagonal(reference_element.wq), + Diagonal(reference_element.wf), + V_plot, + reference_mapping) end - - """Modal approximation using tensor-product operators""" - struct ModalTensor <: AbstractTensorProduct - p::Int - end - - """Modal approximation using multidimensional operators""" - struct ModalMulti <: AbstractMultidimensional - p::Int - end - - """Nodal approximation using multidimensional operators""" - struct NodalMulti <: AbstractMultidimensional - p::Int - end - - """Modal approximation using diagonal-E SBP operators""" - struct ModalMultiDiagE <: AbstractMultidimensional - p::Int - end - - """Nodal approximation using diagonal-E SBP operators""" - struct NodalMultiDiagE <: AbstractMultidimensional - p::Int - end - - """Collapsed coordinate mapping χ: [-1,1]ᵈ → Ωᵣ""" - abstract type AbstractReferenceMapping end - struct NoMapping <: AbstractReferenceMapping end - struct ReferenceMapping <: AbstractReferenceMapping - J_ref::Vector{Float64} - Λ_ref::Array{Float64, 3} - end - - abstract type AbstractMetrics end - struct ExactMetrics <: AbstractMetrics end - struct ConservativeCurlMetrics <: AbstractMetrics end - const ChanWilcoxMetrics = ConservativeCurlMetrics - - """Operators for local approximation on reference element""" - struct ReferenceApproximation{RefElemType, ApproxType, D_type, V_type, - Vf_type, R_type, V_plot_type, ReferenceMappingType} - approx_type::ApproxType - N_p::Int - N_q::Int - N_f::Int - reference_element::RefElemType - D::D_type - V::V_type - Vf::Vf_type - R::R_type - W::Diagonal{Float64, Vector{Float64}} - B::Diagonal{Float64, Vector{Float64}} - V_plot::V_plot_type - reference_mapping::ReferenceMappingType - - function ReferenceApproximation(approx_type::ApproxType, - reference_element::RefElemType, D::D_type, V::V_type, - Vf::Vf_type, R::R_type, V_plot::V_plot_type, - reference_mapping::ReferenceMappingType = NoMapping() - ) where {RefElemType, ApproxType, D_type, V_type, Vf_type, R_type, - V_plot_type, ReferenceMappingType} - - return new{RefElemType, ApproxType, D_type, V_type, - Vf_type, R_type, V_plot_type, ReferenceMappingType}( - approx_type, size(V,2), size(V,1), size(R,1), reference_element, D, V, Vf, R, - Diagonal(reference_element.wq), Diagonal(reference_element.wf), - V_plot, reference_mapping) - end - end - - struct GeometricFactors - J_q::Matrix{Float64} # N_q x N_e - Λ_q::Array{Float64,4} # N_q x d x d x N_e - J_f::Matrix{Float64} # N_f x N_e - nJf::Array{Float64,3} # d x N_f x N_e - nJq::Array{Float64,4} # d x num_faces x N_q x N_e +end + +struct GeometricFactors + J_q::Matrix{Float64} # N_q x N_e + Λ_q::Array{Float64, 4} # N_q x d x d x N_e + J_f::Matrix{Float64} # N_f x N_e + nJf::Array{Float64, 3} # d x N_f x N_e + nJq::Array{Float64, 4} # d x num_faces x N_q x N_e +end + +"""Data for constructing the global spatial discretization""" +struct SpatialDiscretization{d, MeshType, ReferenceApproximationType} + mesh::MeshType + N_e::Int + reference_approximation::ReferenceApproximationType + geometric_factors::GeometricFactors + M::Vector{Matrix{Float64}} + x_plot::NTuple{d, Matrix{Float64}} +end + +dim(::SpatialDiscretization{d}) where {d} = d + +function project_jacobian!(J_q::Matrix{Float64}, V::LinearMap, W::Diagonal, ::Val{true}) + VDM = Matrix(V) + proj = VDM * inv(VDM' * W * VDM) * VDM' * W + @inbounds for k in axes(J_q, 2) + J_qk = copy(J_q[:, k]) + mul!(view(J_q, :, k), proj, J_qk) end - - """Data for constructing the global spatial discretization""" - struct SpatialDiscretization{d,MeshType,ReferenceApproximationType} - mesh::MeshType - N_e::Int - reference_approximation::ReferenceApproximationType - geometric_factors::GeometricFactors - M::Vector{Matrix{Float64}} - x_plot::NTuple{d, Matrix{Float64}} +end + +function project_jacobian!(::Matrix{Float64}, ::LinearMap, ::Diagonal, ::Val{false}) + return +end + +function physical_mass_matrix(J_q::Matrix{Float64}, V::LinearMap, W::Diagonal) + N_e = size(J_q, 2) + VDM = Matrix(V) + M = Vector{Matrix{Float64}}(undef, N_e) + @inbounds for k in 1:N_e + M[k] = VDM' * W * Diagonal(J_q[:, k]) * VDM end - - dim(::SpatialDiscretization{d}) where {d} = d - - function project_jacobian!(J_q::Matrix{Float64}, V::LinearMap, - W::Diagonal, ::Val{true}) - VDM = Matrix(V) - proj = VDM * inv(VDM'*W*VDM) * VDM' * W - @inbounds for k in axes(J_q,2) - J_qk = copy(J_q[:,k]) - mul!(view(J_q,:,k),proj,J_qk) - end - end - - function project_jacobian!(::Matrix{Float64}, ::LinearMap, - ::Diagonal, ::Val{false}) - return - end - - function physical_mass_matrix(J_q::Matrix{Float64}, - V::LinearMap, W::Diagonal) - N_e = size(J_q,2) - VDM = Matrix(V) - M = Vector{Matrix{Float64}}(undef, N_e) - @inbounds for k in 1:N_e - M[k] = VDM' * W * Diagonal(J_q[:,k]) * VDM - end - return M - end - - function SpatialDiscretization(mesh::MeshType, - reference_approximation::ReferenceApproximationType, - metric_type::ExactMetrics=ExactMetrics(); - project_jacobian::Bool=true) where {d,MeshType<:MeshData{d},ReferenceApproximationType<:ReferenceApproximation{<:RefElemData{d}}} - - (; reference_element, W, V) = reference_approximation - - geometric_factors = GeometricFactors(mesh, - reference_element, metric_type) - (; J_q, Λ_q, J_f, nJf, nJq) = geometric_factors - - project_jacobian!(J_q, V, W, Val(project_jacobian)) - - return SpatialDiscretization{d,MeshType,ReferenceApproximationType}( - mesh, size(J_q,2), reference_approximation, - GeometricFactors(J_q, Λ_q, J_f, nJf, nJq), - physical_mass_matrix(J_q, V, W), - Tuple(reference_element.Vp * mesh.xyz[m] for m in 1:d)) - end - - function SpatialDiscretization(mesh::MeshType, - reference_approximation::ReferenceApproximationType, - metric_type::ChanWilcoxMetrics) where {d,MeshType<:MeshData{d},ReferenceApproximationType<:ReferenceApproximation{<:RefElemData{d}}} - - (; reference_element, W, V) = reference_approximation - (; J_q, Λ_q, J_f, nJf, nJq) = GeometricFactors(mesh, - reference_element, metric_type) - - return SpatialDiscretization{d,MeshType,ReferenceApproximationType}( - mesh, size(J_q,2), reference_approximation, - GeometricFactors(J_q, Λ_q, J_f, nJf, nJq), - physical_mass_matrix(J_q, V, W), - Tuple(reference_element.Vp * mesh.xyz[m] for m in 1:d)) + return M +end + +function SpatialDiscretization(mesh::MeshType, + reference_approximation::ReferenceApproximationType, + metric_type::ExactMetrics = ExactMetrics(); + project_jacobian::Bool = true,) where { + d, + MeshType <: + MeshData{d}, + ReferenceApproximationType <: + ReferenceApproximation{<:RefElemData{d}} + } + (; reference_element, W, V) = reference_approximation + + geometric_factors = GeometricFactors(mesh, reference_element, metric_type) + (; J_q, Λ_q, J_f, nJf, nJq) = geometric_factors + + project_jacobian!(J_q, V, W, Val(project_jacobian)) + + return SpatialDiscretization{d, MeshType, ReferenceApproximationType}(mesh, + size(J_q, 2), + reference_approximation, + GeometricFactors(J_q, + Λ_q, + J_f, + nJf, + nJq), + physical_mass_matrix(J_q, + V, + W), + Tuple(reference_element.Vp * + mesh.xyz[m] + for m in 1:d)) +end + +function SpatialDiscretization(mesh::MeshType, + reference_approximation::ReferenceApproximationType, + metric_type::ChanWilcoxMetrics) where { + d, + MeshType <: + MeshData{d}, + ReferenceApproximationType <: + ReferenceApproximation{<:RefElemData{d}} + } + (; reference_element, W, V) = reference_approximation + (; J_q, Λ_q, J_f, nJf, nJq) = GeometricFactors(mesh, reference_element, metric_type) + + return SpatialDiscretization{d, MeshType, ReferenceApproximationType}(mesh, + size(J_q, 2), + reference_approximation, + GeometricFactors(J_q, + Λ_q, + J_f, + nJf, + nJq), + physical_mass_matrix(J_q, + V, + W), + Tuple(reference_element.Vp * + mesh.xyz[m] + for m in 1:d)) +end + +"""Use this when there are no collapsed coordinates""" +@inline apply_reference_mapping(geometric_factors::GeometricFactors, ::NoMapping) = geometric_factors + +"""Express all metric terms in terms of collapsed coordinates""" +function apply_reference_mapping(geometric_factors::GeometricFactors, + reference_mapping::ReferenceMapping) + (; J_q, Λ_q, J_f, nJf, nJq) = geometric_factors + (; J_ref, Λ_ref) = reference_mapping + (N_q, N_e) = size(J_q) + d = size(Λ_q, 2) + Λ_η = similar(Λ_q) + + @inbounds for k in 1:N_e, i in 1:N_q, m in 1:d, n in 1:d + Λ_η[i, m, n, k] = sum(Λ_ref[i, m, l] * Λ_q[i, l, n, k] / J_ref[i] for l in 1:d) end - """Use this when there are no collapsed coordinates""" - @inline apply_reference_mapping(geometric_factors::GeometricFactors, ::NoMapping) = geometric_factors - - """Express all metric terms in terms of collapsed coordinates""" - function apply_reference_mapping(geometric_factors::GeometricFactors, - reference_mapping::ReferenceMapping) - - (; J_q, Λ_q, J_f, nJf, nJq) = geometric_factors - (; J_ref, Λ_ref) = reference_mapping - (N_q, N_e) = size(J_q) - d = size(Λ_q, 2) - Λ_η = similar(Λ_q) - - @inbounds for k in 1:N_e, i in 1:N_q, m in 1:d, n in 1:d - Λ_η[i,m,n,k] = sum(Λ_ref[i,m,l] * Λ_q[i,l,n,k] / J_ref[i] - for l in 1:d) - end - - return GeometricFactors(J_q, Λ_η, J_f, nJf, nJq) - end - - """ - Get derivative operators in reference coordinates from collapsed coordinates - """ - function reference_derivative_operators(D_η::NTuple{d, LinearMap}, - reference_mapping::ReferenceMapping) where {d} - (; Λ_ref, J_ref) = reference_mapping - return Tuple(sum(Diagonal(Λ_ref[:,l,m]./J_ref) * D_η[l] - for l in 1:d) for m in 1:d) - end - - function reference_derivative_operators(D_η::NTuple{d, LinearMap}, - ::NoMapping) where {d} - return D_η - end - - """ - Check if the normals are equal and opposite under the mapping - """ - function check_normals( - spatial_discretization::SpatialDiscretization{d}) where {d} - (; geometric_factors, mesh, N_e) = spatial_discretization - return Tuple([maximum(abs.(geometric_factors.nJf[m,:,k] + - geometric_factors.nJf[m,:,:][mesh.mapP[:,k]])) for k in 1:N_e] - for m in 1:d) - end - - """ - Check if the facet nodes are conforming - """ - function check_facet_nodes( - spatial_discretization::SpatialDiscretization{d}) where {d} - (; mesh, N_e) = spatial_discretization - return Tuple([maximum(abs.(mesh.xyzf[m][:,k] - - mesh.xyzf[m][mesh.mapP[:,k]])) for k in 1:N_e] for m in 1:d) - end - - """ - Check if the SBP property is satisfied on the reference element - """ - function check_sbp_property( - reference_approximation::ReferenceApproximation{<:RefElemData{d}}) where {d} - (; W, D, R, B) = reference_approximation - (; reference_mapping) = reference_approximation - (; nrstJ) = reference_approximation.reference_element - - D_ξ = reference_derivative_operators(D, reference_mapping) - - return Tuple(maximum(abs.(Matrix(W*D_ξ[m] + D_ξ[m]'*W - - R'*B*Diagonal(nrstJ[m])*R))) for m in 1:d) - end - - """ - Check if the SBP property is satisfied on the physical element - """ - function check_sbp_property( - spatial_discretization::SpatialDiscretization{d}, k::Int=1) where {d} - - (; W, D, R, B) = spatial_discretization.reference_approximation - (; Λ_q, nJf) = spatial_discretization.geometric_factors - - Q = Tuple((sum( 0.5 * D[m]' * W * Diagonal(Λ_q[:,m,n,k]) - - 0.5 * Diagonal(Λ_q[:,m,n,k]) * W * D[m] for m in 1:d) + - 0.5 * R' * B * Diagonal(nJf[n,:,k]) * R) for n in 1:d) - - E = Tuple(R' * B * Diagonal(nJf[n,:,k]) * R for n in 1:d) - - return Tuple(maximum(abs.(convert(Matrix, Q[n] + Q[n]' - E[n]))) - for n in 1:d) - end - - """ - Average of vertex positions (not necessarily actual centroid). - Use only for plotting. - """ - function centroids( - spatial_discretization::SpatialDiscretization{d}) where {d} - (; xyz) = spatial_discretization.mesh - return [Tuple(sum(xyz[m][:,k])/length(xyz[m][:,k]) - for m in 1:d) for k in 1:spatial_discretization.N_e] - end - - """ - Trace inequality constant from Chan et al. (2016) - """ - function trace_constant(reference_approximation::ReferenceApproximation) - (; B, Vf, W, V) = reference_approximation - return maximum(eigvals(Matrix(Vf' * B * Vf), Matrix(V' * W * V))) - end - - @inline dim(::Line) = 1 - @inline dim(::Union{Tri,Quad}) = 2 - @inline dim(::Union{Tet,Hex}) = 3 - - export AbstractQuadratureRule, DefaultQuadrature, LGLQuadrature, LGQuadrature, LGRQuadrature, GaussLobattoQuadrature, GaussRadauQuadrature, GaussQuadrature, XiaoGimbutasQuadrature, JaskowiecSukumarQuadrature, quadrature - include("quadrature_rules.jl") - - # new constructors for RefElemData from StartUpDG - include("ref_elem_data.jl") - - include("multidimensional.jl") - include("tensor_cartesian.jl") - - export reference_geometric_factors, operators_1d - include("tensor_simplex.jl") - - export GeometricFactors, metrics, uniform_periodic_mesh, warp_mesh, cartesian_mesh, Uniform, ZigZag, DelReyWarping, ChanWarping - include("mesh.jl") - -end \ No newline at end of file + return GeometricFactors(J_q, Λ_η, J_f, nJf, nJq) +end + +""" +Get derivative operators in reference coordinates from collapsed coordinates +""" +function reference_derivative_operators(D_η::NTuple{d, LinearMap}, + reference_mapping::ReferenceMapping) where {d} + (; Λ_ref, J_ref) = reference_mapping + return Tuple(sum(Diagonal(Λ_ref[:, l, m] ./ J_ref) * D_η[l] for l in 1:d) for m in 1:d) +end + +function reference_derivative_operators(D_η::NTuple{d, LinearMap}, ::NoMapping) where {d} + return D_η +end + +""" +Check if the normals are equal and opposite under the mapping +""" +function check_normals(spatial_discretization::SpatialDiscretization{d}) where {d} + (; geometric_factors, mesh, N_e) = spatial_discretization + return Tuple([maximum(abs.(geometric_factors.nJf[m, :, k] + + geometric_factors.nJf[m, :, :][mesh.mapP[:, k]])) + for k in 1:N_e] for m in 1:d) +end + +""" +Check if the facet nodes are conforming +""" +function check_facet_nodes(spatial_discretization::SpatialDiscretization{d}) where {d} + (; mesh, N_e) = spatial_discretization + return Tuple([maximum(abs.(mesh.xyzf[m][:, k] - mesh.xyzf[m][mesh.mapP[:, k]])) + for k in 1:N_e] + for m in 1:d) +end + +""" +Check if the SBP property is satisfied on the reference element +""" +function check_sbp_property(reference_approximation::ReferenceApproximation{<:RefElemData{d}}) where {d} + (; W, D, R, B) = reference_approximation + (; reference_mapping) = reference_approximation + (; nrstJ) = reference_approximation.reference_element + + D_ξ = reference_derivative_operators(D, reference_mapping) + + return Tuple(maximum(abs.(Matrix(W * D_ξ[m] + D_ξ[m]' * W - + R' * B * Diagonal(nrstJ[m]) * R))) + for m in 1:d) +end + +""" +Check if the SBP property is satisfied on the physical element +""" +function check_sbp_property(spatial_discretization::SpatialDiscretization{d}, + k::Int = 1) where {d} + (; W, D, R, B) = spatial_discretization.reference_approximation + (; Λ_q, nJf) = spatial_discretization.geometric_factors + + Q = Tuple((sum(0.5 * D[m]' * W * Diagonal(Λ_q[:, m, n, k]) - + 0.5 * Diagonal(Λ_q[:, m, n, k]) * W * D[m] for m in 1:d) + + 0.5 * R' * B * Diagonal(nJf[n, :, k]) * R) for n in 1:d) + + E = Tuple(R' * B * Diagonal(nJf[n, :, k]) * R for n in 1:d) + + return Tuple(maximum(abs.(convert(Matrix, Q[n] + Q[n]' - E[n]))) for n in 1:d) +end + +""" +Average of vertex positions (not necessarily actual centroid). +Use only for plotting. +""" +function centroids(spatial_discretization::SpatialDiscretization{d}) where {d} + (; xyz) = spatial_discretization.mesh + return [Tuple(sum(xyz[m][:, k]) / length(xyz[m][:, k]) for m in 1:d) + for + k in 1:(spatial_discretization.N_e)] +end + +""" +Trace inequality constant from Chan et al. (2016) +""" +function trace_constant(reference_approximation::ReferenceApproximation) + (; B, Vf, W, V) = reference_approximation + return maximum(eigvals(Matrix(Vf' * B * Vf), Matrix(V' * W * V))) +end + +@inline dim(::Line) = 1 +@inline dim(::Union{Tri, Quad}) = 2 +@inline dim(::Union{Tet, Hex}) = 3 + +export AbstractQuadratureRule, + DefaultQuadrature, + LGLQuadrature, + LGQuadrature, + LGRQuadrature, + GaussLobattoQuadrature, + GaussRadauQuadrature, + GaussQuadrature, + XiaoGimbutasQuadrature, + JaskowiecSukumarQuadrature, + quadrature +include("quadrature_rules.jl") + +# new constructors for RefElemData from StartUpDG +include("ref_elem_data.jl") + +include("multidimensional.jl") +include("tensor_cartesian.jl") + +export reference_geometric_factors, operators_1d +include("tensor_simplex.jl") + +export GeometricFactors, + metrics, + uniform_periodic_mesh, + warp_mesh, + cartesian_mesh, + Uniform, + ZigZag, + DelReyWarping, + ChanWarping +include("mesh.jl") + +end diff --git a/src/SpatialDiscretizations/mesh.jl b/src/SpatialDiscretizations/mesh.jl index a1d98fec..bd114dc4 100644 --- a/src/SpatialDiscretizations/mesh.jl +++ b/src/SpatialDiscretizations/mesh.jl @@ -5,231 +5,236 @@ struct ZigZag <: AbstractMeshGenStrategy end abstract type AbstractMeshWarping{d} end struct DelReyWarping{d} <: AbstractMeshWarping{d} factor::Float64 - L::NTuple{d,Float64} + L::NTuple{d, Float64} end -struct ChanWarping{d} <: AbstractMeshWarping{d} +struct ChanWarping{d} <: AbstractMeshWarping{d} # Chan, Del Rey Fernandez, Carpenter (2018) # Rueda-Ramirez, Hindenlang, Chan, Gassner (2023) factor::Float64 - L::NTuple{d,Float64} + L::NTuple{d, Float64} end -struct UniformWarping{d} <: AbstractMeshWarping{d} +struct UniformWarping{d} <: AbstractMeshWarping{d} # Chan, Del Rey Fernandez, Carpenter (2019) or Shadpey and Zingg (2020) factor::Float64 - L::NTuple{d,Float64} + L::NTuple{d, Float64} end -function warp_mesh(mesh::MeshData{d}, - reference_element::RefElemData{d}, - factor::Float64=0.2, L::Float64=1.0) where {d} - return warp_mesh(mesh,reference_element, - DelReyWarping(factor, Tuple(L for m in 1:d))) +function warp_mesh(mesh::MeshData{d}, + reference_element::RefElemData{d}, + factor::Float64 = 0.2, + L::Float64 = 1.0) where {d} + return warp_mesh(mesh, reference_element, DelReyWarping(factor, Tuple(L for m in 1:d))) end -function warp_mesh(mesh::MeshData{2}, - reference_element::RefElemData{2}, - mesh_warping::DelReyWarping{2}) - +function warp_mesh(mesh::MeshData{2}, + reference_element::RefElemData{2}, + mesh_warping::DelReyWarping{2}) (; x, y) = mesh (; factor, L) = mesh_warping - x_new = x .+ L[1]*factor*sin.(π*x./L[1]).*sin.(π*y/L[2]) - y_new = y .+ L[2]*factor*exp.(1.0.-y/L[2]).*sin.(π*x/L[1]).* - sin.(π*y/L[2]) + x_new = x .+ L[1] * factor * sin.(π * x ./ L[1]) .* sin.(π * y / L[2]) + y_new = y .+ + L[2] * factor * exp.(1.0 .- y / L[2]) .* sin.(π * x / L[1]) .* + sin.(π * y / L[2]) return MeshData(reference_element, mesh, x_new, y_new) end -function warp_mesh(mesh::MeshData{2}, - reference_element::RefElemData{2}, - mesh_warping::ChanWarping{2}) - +function warp_mesh(mesh::MeshData{2}, + reference_element::RefElemData{2}, + mesh_warping::ChanWarping{2}) (; x, y) = mesh (; factor, L) = mesh_warping - - x_new = x .+ L[1]*factor*cos.(π/L[1]*(x.-0.5*L[1])) .* - cos.(3π/L[2]*(y.-0.5*L[2])) - y_new = y .+ L[2]*factor*sin.(4π/L[1]*(x_new.-0.5*L[1])) .* - cos.(π/L[2]*(y.-0.5*L[2])) + + x_new = x .+ + L[1] * factor * cos.(π / L[1] * (x .- 0.5 * L[1])) .* + cos.(3π / L[2] * (y .- 0.5 * L[2])) + y_new = y .+ + L[2] * factor * sin.(4π / L[1] * (x_new .- 0.5 * L[1])) .* + cos.(π / L[2] * (y .- 0.5 * L[2])) return MeshData(reference_element, mesh, x_new, y_new) end -function warp_mesh(mesh::MeshData{3}, - reference_element::RefElemData{3}, - mesh_warping::ChanWarping{3}) - +function warp_mesh(mesh::MeshData{3}, + reference_element::RefElemData{3}, + mesh_warping::ChanWarping{3}) (; x, y, z) = mesh (; factor, L) = mesh_warping - - y_new = y .+ L[2]*factor*cos.(3π/L[1]*(x.-0.5*L[1])) .* - cos.(π/L[2]*(y.-0.5*L[2])) .* cos.(π/L[3]*(z.-0.5*L[3])) - x_new = x .+ L[1]*factor*cos.(π/L[1]*(x.-0.5*L[1])) .* - sin.(4π/L[2]*(y_new.-0.5*L[2])) .* cos.(π/L[3]*(z.-0.5*L[3])) - z_new = z .+ L[3]*factor*cos.(π/L[1]*(x_new.-0.5*L[1])) .* - cos.(2π/L[2]*(y_new.-0.5*L[2])) .* cos.(π/L[3]*(z.-0.5*L[3])) + + y_new = y .+ + L[2] * factor * cos.(3π / L[1] * (x .- 0.5 * L[1])) .* + cos.(π / L[2] * (y .- 0.5 * L[2])) .* cos.(π / L[3] * (z .- 0.5 * L[3])) + x_new = x .+ + L[1] * factor * cos.(π / L[1] * (x .- 0.5 * L[1])) .* + sin.(4π / L[2] * (y_new .- 0.5 * L[2])) .* cos.(π / L[3] * (z .- 0.5 * L[3])) + z_new = z .+ + L[3] * factor * cos.(π / L[1] * (x_new .- 0.5 * L[1])) .* + cos.(2π / L[2] * (y_new .- 0.5 * L[2])) .* cos.(π / L[3] * (z .- 0.5 * L[3])) return MeshData(reference_element, mesh, x_new, y_new, z_new) end -function warp_mesh(mesh::MeshData{2}, - reference_element::RefElemData{2}, - mesh_warping::UniformWarping{2}) - +function warp_mesh(mesh::MeshData{2}, + reference_element::RefElemData{2}, + mesh_warping::UniformWarping{2}) (; x, y) = mesh (; factor, L) = mesh_warping - eps = factor * sin.(2π*(x.-L[1]/2)/L[1]) .* sin.(2π*(y.-L[2]/2)/L[2]) - x_new = x .+ L[1]*eps - y_new = y .+ L[2]*eps + eps = factor * sin.(2π * (x .- L[1] / 2) / L[1]) .* sin.(2π * (y .- L[2] / 2) / L[2]) + x_new = x .+ L[1] * eps + y_new = y .+ L[2] * eps return MeshData(reference_element, mesh, x_new, y_new) end -function warp_mesh(mesh::MeshData{3}, - reference_element::RefElemData{3}, - mesh_warping::UniformWarping{3}) - +function warp_mesh(mesh::MeshData{3}, + reference_element::RefElemData{3}, + mesh_warping::UniformWarping{3}) (; x, y, z) = mesh (; factor, L) = mesh_warping - eps = factor * sin.(2π*(x.-L[1]/2)/L[1]) .* sin.(2π*(y.-L[2]/2)/L[2]) .* - sin.(2π*(z.-L[3]/2)/L[3]) - x_new = x .+ L[1]*eps - y_new = y .+ L[2]*eps - z_new = z .+ L[3]*eps + eps = factor * sin.(2π * (x .- L[1] / 2) / L[1]) .* sin.(2π * (y .- L[2] / 2) / L[2]) .* + sin.(2π * (z .- L[3] / 2) / L[3]) + x_new = x .+ L[1] * eps + y_new = y .+ L[2] * eps + z_new = z .+ L[3] * eps return MeshData(reference_element, mesh, x_new, y_new, z_new) end -function warp_mesh(mesh::MeshData{3}, reference_element::RefElemData{3}, - mesh_warping::DelReyWarping{3}) - +function warp_mesh(mesh::MeshData{3}, + reference_element::RefElemData{3}, + mesh_warping::DelReyWarping{3}) (; x, y, z) = mesh (; factor, L) = mesh_warping - x_new = x .+ L[1]*factor*sin.(π*x/L[1]).*sin.(π*y/L[2]) - y_new = y .+ L[2]* - factor*exp.((1.0.-y)/L[2]).*sin.(π*x/L[1]).*sin.(π*y/L[2]) - z_new = z .+ 0.25*L[3]* - factor*(sin.(2π*x/L[1]).*sin.(2π*y/L[2])).*sin.(2π*z/L[3]) + x_new = x .+ L[1] * factor * sin.(π * x / L[1]) .* sin.(π * y / L[2]) + y_new = y .+ + L[2] * factor * exp.((1.0 .- y) / L[2]) .* sin.(π * x / L[1]) .* + sin.(π * y / L[2]) + z_new = z .+ + 0.25 * L[3] * factor * (sin.(2π * x / L[1]) .* sin.(2π * y / L[2])) .* + sin.(2π * z / L[3]) return MeshData(reference_element, mesh, x_new, y_new, z_new) end -function uniform_periodic_mesh(reference_element::RefElemData{1}, - limits::NTuple{2,Float64}, M::Int) - +function uniform_periodic_mesh(reference_element::RefElemData{1}, + limits::NTuple{2, Float64}, + M::Int) VX, EtoV = uniform_mesh(reference_element.element_type, M) - mesh = MeshData(limits[1] .+ 0.5*(limits[2]-limits[1])*(VX[1] .+ 1.0), - EtoV,reference_element) + mesh = MeshData(limits[1] .+ 0.5 * (limits[2] - limits[1]) * (VX[1] .+ 1.0), + EtoV, + reference_element) return make_periodic(mesh) end -function uniform_periodic_mesh( - reference_element::RefElemData{d}, - limits::NTuple{d,NTuple{2,Float64}}, - M::NTuple{d,Int}; - random_rotate::Bool=false, - collapsed_orientation::Bool=false, - strategy::AbstractMeshGenStrategy=Uniform(), - tol::Float64=1.0e-12) where {d} - - VXY, EtoV = cartesian_mesh(reference_element.element_type, - M, strategy) - N_e = size(EtoV,1) - +function uniform_periodic_mesh(reference_element::RefElemData{d}, + limits::NTuple{d, NTuple{2, Float64}}, + M::NTuple{d, Int}; + random_rotate::Bool = false, + collapsed_orientation::Bool = false, + strategy::AbstractMeshGenStrategy = Uniform(), + tol::Float64 = 1.0e-12,) where {d} + VXY, EtoV = cartesian_mesh(reference_element.element_type, M, strategy) + N_e = size(EtoV, 1) + if random_rotate for k in 1:N_e - len = size(EtoV,2) - step = rand(0:len-1) - row = EtoV[k,:] - EtoV[k,:] = vcat(row[end-step+1:end], row[1:end-step]) + len = size(EtoV, 2) + step = rand(0:(len - 1)) + row = EtoV[k, :] + EtoV[k, :] = vcat(row[(end - step + 1):end], row[1:(end - step)]) end elseif reference_element.element_type isa Tet && collapsed_orientation # Second algorithm from Warburton's PhD thesis - EtoV_new = Vector{Float64}(undef,4) + EtoV_new = Vector{Float64}(undef, 4) @inbounds for k in 1:N_e - EtoV_new = sort(EtoV[k,:], rev=true) - X = hcat([[VXY[1][EtoV_new[m]] - VXY[1][EtoV_new[1]]; - VXY[2][EtoV_new[m]] - VXY[2][EtoV_new[1]]; - VXY[3][EtoV_new[m]] - VXY[3][EtoV_new[1]]] for m in 2:4]...) - - if det(X) < 0 - EtoV[k,:] = [EtoV_new[2]; - EtoV_new[1]; EtoV_new[3]; EtoV_new[4]] - else - EtoV[k,:] = EtoV_new + EtoV_new = sort(EtoV[k, :], rev = true) + X = hcat([[VXY[1][EtoV_new[m]] - VXY[1][EtoV_new[1]] + VXY[2][EtoV_new[m]] - VXY[2][EtoV_new[1]] + VXY[3][EtoV_new[m]] - VXY[3][EtoV_new[1]]] for m in 2:4]...) + + if det(X) < 0 + EtoV[k, :] = [EtoV_new[2] + EtoV_new[1] + EtoV_new[3] + EtoV_new[4]] + else + EtoV[k, :] = EtoV_new end end end - return make_periodic(MeshData([limits[m][1] .+ - 0.5*(limits[m][2]-limits[m][1])*(VXY[m] .+ 1.0) for m in 1:d]..., - EtoV, reference_element),tol=tol) + return make_periodic(MeshData([limits[m][1] .+ + 0.5 * (limits[m][2] - limits[m][1]) * (VXY[m] .+ 1.0) + for + m in 1:d]..., + EtoV, + reference_element), + tol = tol) end -function cartesian_mesh(element_type::AbstractElemShape, - M::NTuple{d,Int}, ::AbstractMeshGenStrategy) where {d} +function cartesian_mesh(element_type::AbstractElemShape, + M::NTuple{d, Int}, + ::AbstractMeshGenStrategy) where {d} return uniform_mesh(element_type, [M[m] for m in 1:d]...) end -function cartesian_mesh(::Tri, M::NTuple{2,Int}, ::ZigZag) - +function cartesian_mesh(::Tri, M::NTuple{2, Int}, ::ZigZag) if !(iseven(M[1]) && iseven(M[2])) @error "ERROR: ZigZag mesh must have even number of elements in each direction" end - (VX,VY), _ = uniform_mesh(Quad(), M[1], M[2]) + (VX, VY), _ = uniform_mesh(Quad(), M[1], M[2]) EtoV = Matrix{Int64}(undef, 0, 3) - for i in 1:2:(M[1]-1), j in 1:2:(M[2]-1) - bot_left = (j-1)*(M[2]+1) + i - bot_mid = j*(M[2]+1) + i - bot_right = (j+1)*(M[2]+1) + i - EtoV =vcat(EtoV,[ - bot_mid bot_left bot_left+1 ; - bot_left+1 bot_mid+1 bot_mid ; - bot_right+1 bot_right bot_mid ; - bot_mid bot_mid+1 bot_right+1 ; - bot_mid+2 bot_mid+1 bot_left+1 ; - bot_left+1 bot_left+2 bot_mid+2 ; - bot_right+1 bot_mid+1 bot_mid+2 ; - bot_mid+2 bot_right+2 bot_right+1]) + for i in 1:2:(M[1] - 1), j in 1:2:(M[2] - 1) + bot_left = (j - 1) * (M[2] + 1) + i + bot_mid = j * (M[2] + 1) + i + bot_right = (j + 1) * (M[2] + 1) + i + EtoV = vcat(EtoV, + [bot_mid bot_left bot_left+1 + bot_left+1 bot_mid+1 bot_mid + bot_right+1 bot_right bot_mid + bot_mid bot_mid+1 bot_right+1 + bot_mid+2 bot_mid+1 bot_left+1 + bot_left+1 bot_left+2 bot_mid+2 + bot_right+1 bot_mid+1 bot_mid+2 + bot_mid+2 bot_right+2 bot_right+1]) end return (VX, VY), EtoV end - -function metrics(dxdr::SMatrix{1,1}) - J = dxdr[1,1] - Λ = SMatrix{1,1}(1.0) +function metrics(dxdr::SMatrix{1, 1}) + J = dxdr[1, 1] + Λ = SMatrix{1, 1}(1.0) return J, Λ end -function metrics(dxdr::SMatrix{2,2}) - J = dxdr[1,1]*dxdr[2,2] - dxdr[1,2]*dxdr[2,1] - Λ = SMatrix{2,2}([dxdr[2,2] -dxdr[1,2]; -dxdr[2,1] dxdr[1,1]]) +function metrics(dxdr::SMatrix{2, 2}) + J = dxdr[1, 1] * dxdr[2, 2] - dxdr[1, 2] * dxdr[2, 1] + Λ = SMatrix{2, 2}([dxdr[2, 2] -dxdr[1, 2]; -dxdr[2, 1] dxdr[1, 1]]) return J, Λ end -function metrics(dxdr::SMatrix{3,3}) +function metrics(dxdr::SMatrix{3, 3}) J = det(dxdr) - Λ = J*inv(dxdr) + Λ = J * inv(dxdr) return J, Λ end -function GeometricFactors(mesh::MeshData{d}, - reference_element::RefElemData{d}, - metric_type::ExactMetrics=ExactMetrics()) where {d} - +function GeometricFactors(mesh::MeshData{d}, + reference_element::RefElemData{d}, + metric_type::ExactMetrics = ExactMetrics()) where {d} (; nrstJ) = reference_element # note, here we assume that mesh is same N_q, N_f every element - N_q = size(mesh.xyzq[1],1) - N_f = size(mesh.xyzf[1],1) - N_e = size(mesh.xyzq[1],2) + N_q = size(mesh.xyzq[1], 1) + N_f = size(mesh.xyzf[1], 1) + N_e = size(mesh.xyzq[1], 2) # here we assume same number of nodes per face N_fac = num_faces(reference_element.element_type) @@ -241,51 +246,51 @@ function GeometricFactors(mesh::MeshData{d}, nJq = Array{Float64, 4}(undef, d, N_fac, N_q, N_e) dxdr_q = Array{Float64, 4}(undef, N_q, d, d, N_e) Λ_q = Array{Float64, 4}(undef, N_q, d, d, N_e) - dxdr_f = Array{Float64, 4}(undef, N_f, d, d, N_e) + dxdr_f = Array{Float64, 4}(undef, N_f, d, d, N_e) @inbounds for k in 1:N_e for m in 1:d, n in 1:d # evaluate metric at mapping nodes - dxdr = reference_element.Drst[n]*mesh.xyz[m][:,k] + dxdr = reference_element.Drst[n] * mesh.xyz[m][:, k] # use mapping basis to interpolate to quadrature nodes (exact) - dxdr_q[:,m,n,k] = reference_element.Vq*dxdr - dxdr_f[:,m,n,k] = reference_element.Vf*dxdr + dxdr_q[:, m, n, k] = reference_element.Vq * dxdr + dxdr_f[:, m, n, k] = reference_element.Vf * dxdr end # loops over slower indices for i in 1:N_q - J_q[i,k], Λ_q[i,:,:,k] = metrics(SMatrix{d,d}(dxdr_q[i,:,:,k])) + J_q[i, k], Λ_q[i, :, :, k] = metrics(SMatrix{d, d}(dxdr_q[i, :, :, k])) for f in 1:N_fac - n_ref = Tuple(nrstJ[m][nodes_per_face*(f-1)+1] for m in 1:d) + n_ref = Tuple(nrstJ[m][nodes_per_face * (f - 1) + 1] for m in 1:d) for n in 1:d - nJq[n,f,i,k] = sum(Λ_q[i,m,n,k]*n_ref[m] for m in 1:d) + nJq[n, f, i, k] = sum(Λ_q[i, m, n, k] * n_ref[m] for m in 1:d) end end end # get scaled normal vectors - this includes scaling for ref. quadrature weights on long side of right-angled triangle. @inbounds for i in 1:N_f - _, Jdrdx_f = metrics(SMatrix{d,d}(dxdr_f[i,:,:,k])) + _, Jdrdx_f = metrics(SMatrix{d, d}(dxdr_f[i, :, :, k])) @inbounds for m in 1:d - nJf[m,i,k] = sum(Jdrdx_f[n,m]*nrstJ[n][i] for n in 1:d) + nJf[m, i, k] = sum(Jdrdx_f[n, m] * nrstJ[n][i] for n in 1:d) end - J_f[i,k] = sqrt(sum(nJf[m,i,k]^2 for m in 1:d)) + J_f[i, k] = sqrt(sum(nJf[m, i, k]^2 for m in 1:d)) end end return GeometricFactors(J_q, Λ_q, J_f, nJf, nJq) end -function GeometricFactors(mesh::MeshData{2}, reference_element::RefElemData{2}, - ::ConservativeCurlMetrics) - +function GeometricFactors(mesh::MeshData{2}, + reference_element::RefElemData{2}, + ::ConservativeCurlMetrics) (; x, y) = mesh (; nrstJ, Dr, Ds, Vq, Vf) = reference_element # note, here we assume that mesh is same N_q, N_f every element - N_q = size(mesh.xyzq[1],1) - N_f = size(mesh.xyzf[1],1) - N_e = size(mesh.xyzq[1],2) + N_q = size(mesh.xyzq[1], 1) + N_f = size(mesh.xyzf[1], 1) + N_e = size(mesh.xyzq[1], 2) # here we assume same number of nodes per face N_fac = num_faces(reference_element.element_type) @@ -298,27 +303,26 @@ function GeometricFactors(mesh::MeshData{2}, reference_element::RefElemData{2}, Λ_q = Array{Float64, 4}(undef, N_q, 2, 2, N_e) @inbounds @views for k in 1:N_e + rxJ, sxJ, ryJ, syJ, J = geometric_factors(x[:, k], y[:, k], Dr, Ds) - rxJ, sxJ, ryJ, syJ, J = geometric_factors(x[:,k], y[:,k], Dr, Ds) + mul!(Λ_q[:, 1, 1, k], Vq, rxJ) + mul!(Λ_q[:, 2, 1, k], Vq, sxJ) + mul!(Λ_q[:, 1, 2, k], Vq, ryJ) + mul!(Λ_q[:, 2, 2, k], Vq, syJ) + mul!(J_q[:, k], Vq, J) - mul!(Λ_q[:,1,1,k], Vq, rxJ) - mul!(Λ_q[:,2,1,k], Vq, sxJ) - mul!(Λ_q[:,1,2,k], Vq, ryJ) - mul!(Λ_q[:,2,2,k], Vq, syJ) - mul!(J_q[:,k], Vq, J) - Λ_f = Array{Float64, 3}(undef, N_f, 2, 2) - mul!(Λ_f[:,1,1], Vf, rxJ) - mul!(Λ_f[:,2,1], Vf, sxJ) - mul!(Λ_f[:,1,2], Vf, ryJ) - mul!(Λ_f[:,2,2], Vf, syJ) + mul!(Λ_f[:, 1, 1], Vf, rxJ) + mul!(Λ_f[:, 2, 1], Vf, sxJ) + mul!(Λ_f[:, 1, 2], Vf, ryJ) + mul!(Λ_f[:, 2, 2], Vf, syJ) # loops over slower indices @inbounds for i in 1:N_q for f in 1:N_fac - n_ref = Tuple(nrstJ[m][nodes_per_face*(f-1)+1] for m in 1:2) + n_ref = Tuple(nrstJ[m][nodes_per_face * (f - 1) + 1] for m in 1:2) for n in 1:2 - nJq[n,f,i,k] = sum(Λ_q[i,m,n,k]*n_ref[m] for m in 1:2) + nJq[n, f, i, k] = sum(Λ_q[i, m, n, k] * n_ref[m] for m in 1:2) end end end @@ -326,24 +330,24 @@ function GeometricFactors(mesh::MeshData{2}, reference_element::RefElemData{2}, # get scaled normal vectors - this includes scaling for ref. quadrature weights on long side of right-angled triangle. @inbounds for i in 1:N_f @inbounds for m in 1:2 - nJf[m,i,k] = sum(Λ_f[i,n,m]*nrstJ[n][i] for n in 1:2) + nJf[m, i, k] = sum(Λ_f[i, n, m] * nrstJ[n][i] for n in 1:2) end - J_f[i,k] = sqrt(sum(nJf[m,i,k]^2 for m in 1:2)) + J_f[i, k] = sqrt(sum(nJf[m, i, k]^2 for m in 1:2)) end end return GeometricFactors(J_q, Λ_q, J_f, nJf, nJq) end -function GeometricFactors(mesh::MeshData{3}, - reference_element::RefElemData{3,Hex}, ::ConservativeCurlMetrics) - +function GeometricFactors(mesh::MeshData{3}, + reference_element::RefElemData{3, Hex}, + ::ConservativeCurlMetrics) (; x, y, z) = mesh (; nrstJ, Dr, Ds, Dt, Vq, Vf) = reference_element # note, here we assume that mesh is same N_q, N_f every element - N_q = size(mesh.xyzq[1],1) - N_f = size(mesh.xyzf[1],1) - N_e = size(mesh.xyzq[1],2) + N_q = size(mesh.xyzq[1], 1) + N_f = size(mesh.xyzf[1], 1) + N_e = size(mesh.xyzq[1], 2) # here we assume same number of nodes per face N_fac = num_faces(reference_element.element_type) @@ -356,38 +360,38 @@ function GeometricFactors(mesh::MeshData{3}, Λ_q = Array{Float64, 4}(undef, N_q, 3, 3, N_e) @inbounds @views for k in 1:N_e + rxJ, sxJ, txJ, ryJ, syJ, tyJ, rzJ, szJ, tzJ, J = geometric_factors(x[:, k], y[:, k], + z[:, k], Dr, Ds, + Dt) + + mul!(Λ_q[:, 1, 1, k], Vq, rxJ) + mul!(Λ_q[:, 2, 1, k], Vq, sxJ) + mul!(Λ_q[:, 3, 1, k], Vq, txJ) + mul!(Λ_q[:, 1, 2, k], Vq, ryJ) + mul!(Λ_q[:, 2, 2, k], Vq, syJ) + mul!(Λ_q[:, 3, 2, k], Vq, tyJ) + mul!(Λ_q[:, 1, 3, k], Vq, rzJ) + mul!(Λ_q[:, 2, 3, k], Vq, szJ) + mul!(Λ_q[:, 3, 3, k], Vq, tzJ) + mul!(J_q[:, k], Vq, J) - rxJ, sxJ, txJ, ryJ, syJ, tyJ, rzJ, szJ, tzJ, J = geometric_factors( - x[:,k], y[:,k], z[:,k], Dr, Ds, Dt) - - mul!(Λ_q[:,1,1,k], Vq, rxJ) - mul!(Λ_q[:,2,1,k], Vq, sxJ) - mul!(Λ_q[:,3,1,k], Vq, txJ) - mul!(Λ_q[:,1,2,k], Vq, ryJ) - mul!(Λ_q[:,2,2,k], Vq, syJ) - mul!(Λ_q[:,3,2,k], Vq, tyJ) - mul!(Λ_q[:,1,3,k], Vq, rzJ) - mul!(Λ_q[:,2,3,k], Vq, szJ) - mul!(Λ_q[:,3,3,k], Vq, tzJ) - mul!(J_q[:,k], Vq, J) - Λ_f = Array{Float64, 3}(undef, N_f, 3, 3) - mul!(Λ_f[:,1,1], Vf, rxJ) - mul!(Λ_f[:,2,1], Vf, sxJ) - mul!(Λ_f[:,3,1], Vf, txJ) - mul!(Λ_f[:,1,2], Vf, ryJ) - mul!(Λ_f[:,2,2], Vf, syJ) - mul!(Λ_f[:,3,2], Vf, tyJ) - mul!(Λ_f[:,1,3], Vf, rzJ) - mul!(Λ_f[:,2,3], Vf, szJ) - mul!(Λ_f[:,3,3], Vf, tzJ) + mul!(Λ_f[:, 1, 1], Vf, rxJ) + mul!(Λ_f[:, 2, 1], Vf, sxJ) + mul!(Λ_f[:, 3, 1], Vf, txJ) + mul!(Λ_f[:, 1, 2], Vf, ryJ) + mul!(Λ_f[:, 2, 2], Vf, syJ) + mul!(Λ_f[:, 3, 2], Vf, tyJ) + mul!(Λ_f[:, 1, 3], Vf, rzJ) + mul!(Λ_f[:, 2, 3], Vf, szJ) + mul!(Λ_f[:, 3, 3], Vf, tzJ) # loops over slower indices @inbounds for i in 1:N_q for f in 1:N_fac - n_ref = Tuple(nrstJ[m][nodes_per_face*(f-1)+1] for m in 1:3) + n_ref = Tuple(nrstJ[m][nodes_per_face * (f - 1) + 1] for m in 1:3) for n in 1:3 - nJq[n,f,i,k] = sum(Λ_q[i,m,n,k]*n_ref[m] for m in 1:3) + nJq[n, f, i, k] = sum(Λ_q[i, m, n, k] * n_ref[m] for m in 1:3) end end end @@ -395,30 +399,32 @@ function GeometricFactors(mesh::MeshData{3}, # get scaled normal vectors - this includes scaling for ref. quadrature weights on long side of right-angled triangle. @inbounds for i in 1:N_f for m in 1:3 - nJf[m,i,k] = sum(Λ_f[i,n,m]*nrstJ[n][i] for n in 1:3) + nJf[m, i, k] = sum(Λ_f[i, n, m] * nrstJ[n][i] for n in 1:3) end - J_f[i,k] = sqrt(sum(nJf[m,i,k]^2 for m in 1:3)) + J_f[i, k] = sqrt(sum(nJf[m, i, k]^2 for m in 1:3)) end end return GeometricFactors(J_q, Λ_q, J_f, nJf, nJq) end -@views function GeometricFactors(mesh::MeshData{3}, - reference_element::RefElemData{3,Tet}, ::ConservativeCurlMetrics) - +@views function GeometricFactors(mesh::MeshData{3}, + reference_element::RefElemData{3, Tet}, + ::ConservativeCurlMetrics) (; x, y, z) = mesh (; N, nrstJ) = reference_element # need a degree N+1 element on which to compute the argument of the curl # operator, which is approximated as a degree N+1 polynomial - r_Nplus1, s_Nplus1, t_Nplus1 = nodes(Tet(), N+1) - V_Nplus1, Vr_Nplus1, Vs_Nplus1, Vt_Nplus1 = basis(Tet(), N+1, r_Nplus1, - s_Nplus1, t_Nplus1) - N_to_Nplus1 = vandermonde(Tet(), N, - r_Nplus1, s_Nplus1, t_Nplus1) / reference_element.VDM - Nplus1_to_N = vandermonde(Tet(), N+1, - reference_element.r, reference_element.s, - reference_element.t) / V_Nplus1 + r_Nplus1, s_Nplus1, t_Nplus1 = nodes(Tet(), N + 1) + V_Nplus1, Vr_Nplus1, Vs_Nplus1, Vt_Nplus1 = basis(Tet(), N + 1, r_Nplus1, s_Nplus1, + t_Nplus1) + N_to_Nplus1 = vandermonde(Tet(), N, r_Nplus1, s_Nplus1, t_Nplus1) / + reference_element.VDM + Nplus1_to_N = vandermonde(Tet(), + N + 1, + reference_element.r, + reference_element.s, + reference_element.t) / V_Nplus1 # before evaluating metrics at quadrature nodes, they are brought # back to degree N interpolation nodes -- this is exact since they @@ -427,9 +433,9 @@ end Vf = reference_element.Vf * Nplus1_to_N # note, here we assume that mesh is same N_q, N_f every element - N_q = size(mesh.xyzq[1],1) - N_f = size(mesh.xyzf[1],1) - N_e = size(mesh.xyzq[1],2) + N_q = size(mesh.xyzq[1], 1) + N_f = size(mesh.xyzf[1], 1) + N_e = size(mesh.xyzq[1], 2) # here we assume same number of nodes per face N_fac = num_faces(reference_element.element_type) @@ -443,96 +449,109 @@ end Λ_f = Array{Float64, 4}(undef, N_f, 3, 3, N_e) # Jacobian as degree N polynomial represented in degree N nodal basis - _, _, _, _, _, _, _, _, _, J = geometric_factors(x, y, z, - reference_element.Dr, reference_element.Ds, reference_element.Dt) + _, _, _, _, _, _, _, _, _, J = geometric_factors(x, + y, + z, + reference_element.Dr, + reference_element.Ds, + reference_element.Dt) mul!(J_q, reference_element.Vq, J) # Metric terms as degree N polynomials represented in degree N+1 nodal basis - rxJ, sxJ, txJ, ryJ, syJ, tyJ, rzJ, szJ, tzJ, _ = geometric_factors( - N_to_Nplus1*x, N_to_Nplus1*y, N_to_Nplus1*z, - Vr_Nplus1 / V_Nplus1, Vs_Nplus1 / V_Nplus1, Vt_Nplus1 / V_Nplus1) + rxJ, sxJ, txJ, ryJ, syJ, tyJ, rzJ, szJ, tzJ, _ = geometric_factors(N_to_Nplus1 * x, + N_to_Nplus1 * y, + N_to_Nplus1 * z, + Vr_Nplus1 / V_Nplus1, + Vs_Nplus1 / V_Nplus1, + Vt_Nplus1 / V_Nplus1) # Evaluate metric at volume quadrature nodes - mul!(Λ_q[:,1,1,:], Vq, rxJ) - mul!(Λ_q[:,2,1,:], Vq, sxJ) - mul!(Λ_q[:,3,1,:], Vq, txJ) - mul!(Λ_q[:,1,2,:], Vq, ryJ) - mul!(Λ_q[:,2,2,:], Vq, syJ) - mul!(Λ_q[:,3,2,:], Vq, tyJ) - mul!(Λ_q[:,1,3,:], Vq, rzJ) - mul!(Λ_q[:,2,3,:], Vq, szJ) - mul!(Λ_q[:,3,3,:], Vq, tzJ) + mul!(Λ_q[:, 1, 1, :], Vq, rxJ) + mul!(Λ_q[:, 2, 1, :], Vq, sxJ) + mul!(Λ_q[:, 3, 1, :], Vq, txJ) + mul!(Λ_q[:, 1, 2, :], Vq, ryJ) + mul!(Λ_q[:, 2, 2, :], Vq, syJ) + mul!(Λ_q[:, 3, 2, :], Vq, tyJ) + mul!(Λ_q[:, 1, 3, :], Vq, rzJ) + mul!(Λ_q[:, 2, 3, :], Vq, szJ) + mul!(Λ_q[:, 3, 3, :], Vq, tzJ) # Evaluate metric at volume facet quadrature nodes - mul!(Λ_f[:,1,1,:], Vf, rxJ) - mul!(Λ_f[:,2,1,:], Vf, sxJ) - mul!(Λ_f[:,3,1,:], Vf, txJ) - mul!(Λ_f[:,1,2,:], Vf, ryJ) - mul!(Λ_f[:,2,2,:], Vf, syJ) - mul!(Λ_f[:,3,2,:], Vf, tyJ) - mul!(Λ_f[:,1,3,:], Vf, rzJ) - mul!(Λ_f[:,2,3,:], Vf, szJ) - mul!(Λ_f[:,3,3,:], Vf, tzJ) + mul!(Λ_f[:, 1, 1, :], Vf, rxJ) + mul!(Λ_f[:, 2, 1, :], Vf, sxJ) + mul!(Λ_f[:, 3, 1, :], Vf, txJ) + mul!(Λ_f[:, 1, 2, :], Vf, ryJ) + mul!(Λ_f[:, 2, 2, :], Vf, syJ) + mul!(Λ_f[:, 3, 2, :], Vf, tyJ) + mul!(Λ_f[:, 1, 3, :], Vf, rzJ) + mul!(Λ_f[:, 2, 3, :], Vf, szJ) + mul!(Λ_f[:, 3, 3, :], Vf, tzJ) # compute normals - @inbounds for k in 1:N_e + @inbounds for k in 1:N_e for i in 1:N_q, f in 1:N_fac - n_ref = Tuple(nrstJ[m][nodes_per_face*(f-1)+1] for m in 1:3) + n_ref = Tuple(nrstJ[m][nodes_per_face * (f - 1) + 1] for m in 1:3) for n in 1:3 - nJq[n,f,i,k] = sum(Λ_q[i,m,n,k]*n_ref[m] for m in 1:3) + nJq[n, f, i, k] = sum(Λ_q[i, m, n, k] * n_ref[m] for m in 1:3) end end for i in 1:N_f for m in 1:3 - nJf[m,i,k] = sum(Λ_f[i,n,m,k]*nrstJ[n][i] for n in 1:3) + nJf[m, i, k] = sum(Λ_f[i, n, m, k] * nrstJ[n][i] for n in 1:3) end - J_f[i,k] = sqrt(sum(nJf[m,i,k]^2 for m in 1:3)) + J_f[i, k] = sqrt(sum(nJf[m, i, k]^2 for m in 1:3)) end end return GeometricFactors(J_q, Λ_q, J_f, nJf, nJq) end -function uniform_periodic_mesh( - reference_approximation::ReferenceApproximation{<:RefElemData{3, Tet}, - <:Union{NodalTensor,ModalTensor}}, - limits::NTuple{3,NTuple{2,Float64}}, - M::NTuple{3,Int}; - random_rotate::Bool=false, - strategy::AbstractMeshGenStrategy=Uniform(), - tol=1e-10) - - return uniform_periodic_mesh( - reference_approximation.reference_element, limits, M, - random_rotate=random_rotate, - collapsed_orientation=true, - strategy=strategy, tol=tol) +function uniform_periodic_mesh(reference_approximation::ReferenceApproximation{<:RefElemData{3, + Tet}, + <:Union{NodalTensor, + ModalTensor}}, + limits::NTuple{3, NTuple{2, Float64}}, + M::NTuple{3, Int}; + random_rotate::Bool = false, + strategy::AbstractMeshGenStrategy = Uniform(), + tol = 1e-10,) + return uniform_periodic_mesh(reference_approximation.reference_element, + limits, + M, + random_rotate = random_rotate, + collapsed_orientation = true, + strategy = strategy, + tol = tol) end -function uniform_periodic_mesh( - reference_approximation::ReferenceApproximation{<:RefElemData{d}}, - limits::NTuple{d,NTuple{2,Float64}}, - M::NTuple{d,Int}; - random_rotate::Bool=false, - strategy::AbstractMeshGenStrategy=Uniform(), tol=1.0e-10) where {d} - - return uniform_periodic_mesh( - reference_approximation.reference_element, limits, M, - random_rotate=random_rotate, - strategy=strategy, tol=tol) +function uniform_periodic_mesh(reference_approximation::ReferenceApproximation{<:RefElemData{d}}, + limits::NTuple{d, NTuple{2, Float64}}, + M::NTuple{d, Int}; + random_rotate::Bool = false, + strategy::AbstractMeshGenStrategy = Uniform(), + tol = 1.0e-10,) where {d} + return uniform_periodic_mesh(reference_approximation.reference_element, + limits, + M, + random_rotate = random_rotate, + strategy = strategy, + tol = tol) end -@inline uniform_periodic_mesh( - reference_approximation::ReferenceApproximation{<:RefElemData{1}}, - limits::NTuple{2,Float64}, M::Int) = uniform_periodic_mesh( - reference_approximation.reference_element, limits, M) +@inline uniform_periodic_mesh(reference_approximation::ReferenceApproximation{<:RefElemData{1}}, +limits::NTuple{2, Float64}, +M::Int) = uniform_periodic_mesh(reference_approximation.reference_element, limits, M) function warp_mesh(mesh::MeshData{d}, - reference_approximation::ReferenceApproximation{<:RefElemData{d}}, - factor::Float64=0.2, L::Float64=1.0) where {d} - return warp_mesh(mesh,reference_approximation.reference_element, - DelReyWarping(factor, Tuple(L for m in 1:d))) + reference_approximation::ReferenceApproximation{<:RefElemData{d}}, + factor::Float64 = 0.2, + L::Float64 = 1.0) where {d} + return warp_mesh(mesh, + reference_approximation.reference_element, + DelReyWarping(factor, Tuple(L for m in 1:d))) end -function warp_mesh(mesh::MeshData{d}, reference_approximation::ReferenceApproximation{<:RefElemData{d}}, mesh_warping::AbstractMeshWarping{d}) where {d} +function warp_mesh(mesh::MeshData{d}, + reference_approximation::ReferenceApproximation{<:RefElemData{d}}, + mesh_warping::AbstractMeshWarping{d}) where {d} return warp_mesh(mesh, reference_approximation.reference_element, mesh_warping) -end \ No newline at end of file +end diff --git a/src/SpatialDiscretizations/multidimensional.jl b/src/SpatialDiscretizations/multidimensional.jl index f9ebd76d..1d513d2c 100644 --- a/src/SpatialDiscretizations/multidimensional.jl +++ b/src/SpatialDiscretizations/multidimensional.jl @@ -1,113 +1,147 @@ -function ReferenceApproximation( - approx_type::ModalMulti, element_type::AbstractElemShape; - mapping_degree::Int=1, N_plot::Int=10, volume_quadrature_rule=DefaultQuadrature(2*approx_type.p), - facet_quadrature_rule=DefaultQuadrature(2*approx_type.p)) - +function ReferenceApproximation(approx_type::ModalMulti, + element_type::AbstractElemShape; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = DefaultQuadrature(2 * + approx_type.p), + facet_quadrature_rule = DefaultQuadrature(2 * approx_type.p),) d = dim(element_type) (; p) = approx_type - + if d > 1 - reference_element = RefElemData(element_type, mapping_degree, - quad_rule_vol=quadrature(element_type, volume_quadrature_rule), - quad_rule_face=quadrature(face_type(element_type), - facet_quadrature_rule), Nplot=N_plot) + reference_element = RefElemData(element_type, + mapping_degree, + quad_rule_vol = quadrature(element_type, + volume_quadrature_rule), + quad_rule_face = quadrature(face_type(element_type), + facet_quadrature_rule), + Nplot = N_plot) else - reference_element = RefElemData(element_type, mapping_degree, - quad_rule_vol=quadrature(element_type, volume_quadrature_rule), Nplot=N_plot) + reference_element = RefElemData(element_type, + mapping_degree, + quad_rule_vol = quadrature(element_type, + volume_quadrature_rule), + Nplot = N_plot) end - (; rstq, rstf, rstp, wq,) = reference_element + (; rstq, rstf, rstp, wq) = reference_element VDM, ∇VDM... = basis(element_type, p, rstq...) Vf = vandermonde(element_type, p, rstf...) V_plot = vandermonde(element_type, p, rstp...) P = Matrix(inv(VDM' * Diagonal(wq) * VDM) * VDM' * Diagonal(wq)) - return ReferenceApproximation(approx_type, reference_element, - Tuple(OctavianMap(∇VDM[m] * P) for m in 1:d), OctavianMap(VDM), - OctavianMap(Vf), OctavianMap(Vf * P), OctavianMap(V_plot)) + return ReferenceApproximation(approx_type, + reference_element, + Tuple(OctavianMap(∇VDM[m] * P) for m in 1:d), + OctavianMap(VDM), + OctavianMap(Vf), + OctavianMap(Vf * P), + OctavianMap(V_plot)) end -function ReferenceApproximation( - approx_type::NodalMulti, element_type::AbstractElemShape; - mapping_degree::Int=1, N_plot::Int=10, volume_quadrature_rule=DefaultQuadrature(2*approx_type.p), - facet_quadrature_rule=DefaultQuadrature(2*approx_type.p)) - +function ReferenceApproximation(approx_type::NodalMulti, + element_type::AbstractElemShape; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = DefaultQuadrature(2 * + approx_type.p), + facet_quadrature_rule = DefaultQuadrature(2 * approx_type.p),) d = dim(element_type) (; p) = approx_type - reference_element = RefElemData(element_type, mapping_degree, - quad_rule_vol=quadrature(element_type, volume_quadrature_rule), - quad_rule_face=quadrature(face_type(element_type), - facet_quadrature_rule), Nplot=N_plot) + reference_element = RefElemData(element_type, + mapping_degree, + quad_rule_vol = quadrature(element_type, + volume_quadrature_rule), + quad_rule_face = quadrature(face_type(element_type), + facet_quadrature_rule), + Nplot = N_plot) (; rstq, rstf, rstp, wq) = reference_element - VDM, ∇VDM... = basis(element_type, p, rstq...) + VDM, ∇VDM... = basis(element_type, p, rstq...) Vf = vandermonde(element_type, p, rstf...) V_plot = vandermonde(element_type, p, rstp...) P = Matrix(inv(VDM' * Diagonal(wq) * VDM) * VDM' * Diagonal(wq)) - - return ReferenceApproximation(approx_type, reference_element, - Tuple(OctavianMap(∇VDM[m] * P) for m in 1:d), LinearMap(I, length(wq)), - OctavianMap(Vf * P), OctavianMap(Vf * P), OctavianMap(V_plot*P)) -end -function ReferenceApproximation( - approx_type::ModalMultiDiagE, - element_type::AbstractElemShape; - sbp_type::SBP=SBP{Hicken}(), - mapping_degree::Int=1, N_plot::Int=10) + return ReferenceApproximation(approx_type, + reference_element, + Tuple(OctavianMap(∇VDM[m] * P) for m in 1:d), + LinearMap(I, length(wq)), + OctavianMap(Vf * P), + OctavianMap(Vf * P), + OctavianMap(V_plot * P)) +end +function ReferenceApproximation(approx_type::ModalMultiDiagE, + element_type::AbstractElemShape; + sbp_type::SBP = SBP{Hicken}(), + mapping_degree::Int = 1, + N_plot::Int = 10,) d = dim(element_type) - volume_quadrature_rule, facet_quadrature_rule = diagE_sbp_nodes( - element_type, sbp_type, approx_type.p) - - reference_element = RefElemData(element_type, mapping_degree, - quad_rule_vol=volume_quadrature_rule, - quad_rule_face=facet_quadrature_rule, Nplot=N_plot) + volume_quadrature_rule, facet_quadrature_rule = diagE_sbp_nodes(element_type, sbp_type, + approx_type.p) + + reference_element = RefElemData(element_type, + mapping_degree, + quad_rule_vol = volume_quadrature_rule, + quad_rule_face = facet_quadrature_rule, + Nplot = N_plot) sbp_element = RefElemData(element_type, sbp_type, approx_type.p) (; rstq, rstf, rstp, wq) = reference_element V = OctavianMap(vandermonde(element_type, approx_type.p, rstq...)) - V_plot = OctavianMap(vandermonde(element_type, approx_type.p, rstp...)) + V_plot = OctavianMap(vandermonde(element_type, approx_type.p, rstp...)) #R = LinearMap(sbp_element.Vf) # SparseMatrixCSC R = SelectionMap(match_coordinate_vectors(rstf, rstq), length(wq)) - return ReferenceApproximation(approx_type, reference_element, - Tuple(OctavianMap(sbp_element.Drst[m]) for m in 1:d), V, - OctavianMap(Matrix(R*V)), R, V_plot) + return ReferenceApproximation(approx_type, + reference_element, + Tuple(OctavianMap(sbp_element.Drst[m]) for m in 1:d), + V, + OctavianMap(Matrix(R * V)), + R, + V_plot) end -function ReferenceApproximation( - approx_type::NodalMultiDiagE, - element_type::AbstractElemShape; - sbp_type::SBP=SBP{Hicken}(), - mapping_degree::Int=1, N_plot::Int=10) - +function ReferenceApproximation(approx_type::NodalMultiDiagE, + element_type::AbstractElemShape; + sbp_type::SBP = SBP{Hicken}(), + mapping_degree::Int = 1, + N_plot::Int = 10,) d = dim(element_type) - volume_quadrature_rule, facet_quadrature_rule = diagE_sbp_nodes( - element_type, sbp_type, approx_type.p) - - reference_element = RefElemData(element_type, mapping_degree, - quad_rule_vol=volume_quadrature_rule, - quad_rule_face=facet_quadrature_rule, Nplot=N_plot) + volume_quadrature_rule, facet_quadrature_rule = diagE_sbp_nodes(element_type, sbp_type, + approx_type.p) + + reference_element = RefElemData(element_type, + mapping_degree, + quad_rule_vol = volume_quadrature_rule, + quad_rule_face = facet_quadrature_rule, + Nplot = N_plot) sbp_element = RefElemData(element_type, sbp_type, approx_type.p) (; rstq, rstf, rstp, wq) = reference_element - VDM = vandermonde(element_type, approx_type.p, rstq...) + VDM = vandermonde(element_type, approx_type.p, rstq...) V = LinearMap(I, length(wq)) V_plot = OctavianMap(vandermonde(element_type, approx_type.p, rstp...) * - inv(VDM' * Diagonal(wq) * VDM) * VDM' * Diagonal(wq)) + inv(VDM' * Diagonal(wq) * VDM) * + VDM' * + Diagonal(wq)) #R = LinearMap(sbp_element.Vf) # SparseMatrixCSC R = SelectionMap(match_coordinate_vectors(rstf, rstq), length(wq)) - return ReferenceApproximation(approx_type, reference_element, - Tuple(OctavianMap(sbp_element.Drst[m]) for m in 1:d), V, R, R, V_plot) -end \ No newline at end of file + return ReferenceApproximation(approx_type, + reference_element, + Tuple(OctavianMap(sbp_element.Drst[m]) for m in 1:d), + V, + R, + R, + V_plot) +end diff --git a/src/SpatialDiscretizations/quadrature_rules.jl b/src/SpatialDiscretizations/quadrature_rules.jl index b474beac..692e9f57 100644 --- a/src/SpatialDiscretizations/quadrature_rules.jl +++ b/src/SpatialDiscretizations/quadrature_rules.jl @@ -30,24 +30,23 @@ struct DefaultQuadrature <: AbstractQuadratureRule degree::Int end -@inline LGLQuadrature(q::Int) = GaussLobattoQuadrature(q,0,0) -@inline LGQuadrature(q::Int) = GaussQuadrature(q,0,0) -@inline LGRQuadrature(q::Int) = GaussRadauQuadrature(q,0,0) +@inline LGLQuadrature(q::Int) = GaussLobattoQuadrature(q, 0, 0) +@inline LGQuadrature(q::Int) = GaussQuadrature(q, 0, 0) +@inline LGRQuadrature(q::Int) = GaussRadauQuadrature(q, 0, 0) function meshgrid(x::Vector{Float64}, y::Vector{Float64}) return ([x[j] for i in 1:length(y), j in 1:length(x)], - [y[i] for i in 1:length(y), j in 1:length(x)]) + [y[i] for i in 1:length(y), j in 1:length(x)]) end function meshgrid(x::Vector{Float64}, y::Vector{Float64}, z::Vector{Float64}) return ([x[k] for i in 1:length(z), j in 1:length(y), k in 1:length(y)], - [y[j] for i in 1:length(z), j in 1:length(y), k in 1:length(y)], - [z[i] for i in 1:length(z), j in 1:length(y), k in 1:length(y)]) + [y[j] for i in 1:length(z), j in 1:length(y), k in 1:length(y)], + [z[i] for i in 1:length(z), j in 1:length(y), k in 1:length(y)]) end function quadrature(::Line, quadrature_rule::DefaultQuadrature) - return quadrature(Line(), - LGQuadrature(ceil(Int, (quadrature_rule.degree-1)/2))) + return quadrature(Line(), LGQuadrature(ceil(Int, (quadrature_rule.degree - 1) / 2))) end function quadrature(::Tri, quadrature_rule::DefaultQuadrature) @@ -75,49 +74,47 @@ function quadrature(::Tet, quadrature_rule::DefaultQuadrature) end function quadrature(::Line, quadrature_rule::GaussLobattoQuadrature) - (; q,a,b) = quadrature_rule - z = zglj(q+1, Float64(a), Float64(b)) + (; q, a, b) = quadrature_rule + z = zglj(q + 1, Float64(a), Float64(b)) return z, wglj(z, Float64(a), Float64(b)) end function quadrature(::Line, quadrature_rule::GaussQuadrature) - (; q,a,b) = quadrature_rule - z = zgj(q+1, Float64(a), Float64(b)) + (; q, a, b) = quadrature_rule + z = zgj(q + 1, Float64(a), Float64(b)) return z, wgj(z, Float64(a), Float64(b)) end function quadrature(::Line, quadrature_rule::GaussRadauQuadrature) - (; q,a,b) = quadrature_rule - z = zgrjm(q+1, Float64(a), Float64(b)) + (; q, a, b) = quadrature_rule + z = zgrjm(q + 1, Float64(a), Float64(b)) return z, wgrjm(z, Float64(a), Float64(b)) end function quadrature(::Quad, quadrature_rule::AbstractQuadratureRule) r1d, w1d = quadrature(Line(), quadrature_rule) - w_grid = meshgrid(w1d,w1d) - r_grid = meshgrid(r1d,r1d) - w2d = w_grid[1] .* w_grid[2] + w_grid = meshgrid(w1d, w1d) + r_grid = meshgrid(r1d, r1d) + w2d = w_grid[1] .* w_grid[2] return r_grid[1][:], r_grid[2][:], w2d[:] end -function quadrature(::Quad, - quadrature_rule::NTuple{2,AbstractQuadratureRule}) +function quadrature(::Quad, quadrature_rule::NTuple{2, AbstractQuadratureRule}) r1d_1, w1d_1 = quadrature(Line(), quadrature_rule[1]) r1d_2, w1d_2 = quadrature(Line(), quadrature_rule[2]) - w_grid = meshgrid(w1d_1,w1d_2) - r_grid = meshgrid(r1d_1,r1d_2) - w2d = w_grid[1] .* w_grid[2] + w_grid = meshgrid(w1d_1, w1d_2) + r_grid = meshgrid(r1d_1, r1d_2) + w2d = w_grid[1] .* w_grid[2] return r_grid[1][:], r_grid[2][:], w2d[:] end -function quadrature(::Hex, - quadrature_rule::NTuple{3,AbstractQuadratureRule}) +function quadrature(::Hex, quadrature_rule::NTuple{3, AbstractQuadratureRule}) r1d_1, w1d_1 = quadrature(Line(), quadrature_rule[1]) r1d_2, w1d_2 = quadrature(Line(), quadrature_rule[2]) r1d_3, w1d_3 = quadrature(Line(), quadrature_rule[3]) - w_grid = meshgrid(w1d_1,w1d_2,w1d_3) - r_grid = meshgrid(r1d_1,r1d_2,r1d_3) - w2d = w_grid[1] .* w_grid[2] .* w_grid[3] + w_grid = meshgrid(w1d_1, w1d_2, w1d_3) + r_grid = meshgrid(r1d_1, r1d_2, r1d_3) + w2d = w_grid[1] .* w_grid[2] .* w_grid[3] return r_grid[1][:], r_grid[2][:], r_grid[3][:], w2d[:] end @@ -125,43 +122,45 @@ function quadrature(::Hex, quadrature_rule::AbstractQuadratureRule) return quadrature(Hex(), Tuple(quadrature_rule for m in 1:3)) end -function quadrature(::Tri, quadrature_rule::NTuple{2,AbstractQuadratureRule}) +function quadrature(::Tri, quadrature_rule::NTuple{2, AbstractQuadratureRule}) r1d_1, w1d_1 = quadrature(Line(), quadrature_rule[1]) r1d_2, w1d_2 = quadrature(Line(), quadrature_rule[2]) w_grid = meshgrid(w1d_1, w1d_2) - r_grid = meshgrid(r1d_1,r1d_2) - w2d = w_grid[1] .* w_grid[2] - if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (0,0)) - return χ(Tri(), (r_grid[1][:], r_grid[2][:]))..., - 0.5*(η -> (1-η)).(r_grid[2][:]) .* w2d[:] - elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (1,0)) - return χ(Tri(), (r_grid[1][:], r_grid[2][:]))..., 0.5*w2d[:] - else - @error "Chosen Jacobi weight not supported." + r_grid = meshgrid(r1d_1, r1d_2) + w2d = w_grid[1] .* w_grid[2] + if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (0, 0)) + return χ(Tri(), (r_grid[1][:], r_grid[2][:]))..., + 0.5 * (η -> (1 - η)).(r_grid[2][:]) .* w2d[:] + elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (1, 0)) + return χ(Tri(), (r_grid[1][:], r_grid[2][:]))..., 0.5 * w2d[:] + else + @error "Chosen Jacobi weight not supported." end end -function quadrature(::Tet, quadrature_rule::NTuple{3,AbstractQuadratureRule}) +function quadrature(::Tet, quadrature_rule::NTuple{3, AbstractQuadratureRule}) r1d_1, w1d_1 = quadrature(Line(), quadrature_rule[1]) r1d_2, w1d_2 = quadrature(Line(), quadrature_rule[2]) r1d_3, w1d_3 = quadrature(Line(), quadrature_rule[3]) - w_grid = meshgrid(w1d_1,w1d_2,w1d_3) - r_grid = meshgrid(r1d_1,r1d_2,r1d_3) - w2d = w_grid[1] .* w_grid[2] .* w_grid[3] + w_grid = meshgrid(w1d_1, w1d_2, w1d_3) + r_grid = meshgrid(r1d_1, r1d_2, r1d_3) + w2d = w_grid[1] .* w_grid[2] .* w_grid[3] - if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (0,0) && - (quadrature_rule[3].a, quadrature_rule[3].b) == (0,0)) + if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (0, 0) && + (quadrature_rule[3].a, quadrature_rule[3].b) == (0, 0)) return χ(Tet(), (r_grid[1][:], r_grid[2][:], r_grid[3][:]))..., - 0.125 * (η -> (1-η)).(r_grid[2][:]) .* (η -> ((1-η))^2).(r_grid[3][:]) .* w2d[:] - elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (0,0) && - (quadrature_rule[3].a, quadrature_rule[3].b) == (1,0)) + 0.125 * (η -> (1 - η)).(r_grid[2][:]) .* (η -> ((1 - η))^2).(r_grid[3][:]) .* + w2d[:] + elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (0, 0) && + (quadrature_rule[3].a, quadrature_rule[3].b) == (1, 0)) return χ(Tet(), (r_grid[1][:], r_grid[2][:], r_grid[3][:]))..., - 0.125 * (η -> (1-η)).(r_grid[2][:]) .* (η -> (1-η)).(r_grid[3][:]) .* w2d[:] - else - @error "Chosen Jacobi weight not supported" + 0.125 * (η -> (1 - η)).(r_grid[2][:]) .* (η -> (1 - η)).(r_grid[3][:]) .* + w2d[:] + else + @error "Chosen Jacobi weight not supported" end -end \ No newline at end of file +end diff --git a/src/SpatialDiscretizations/ref_elem_data.jl b/src/SpatialDiscretizations/ref_elem_data.jl index 1746c9c1..d75c4a3c 100644 --- a/src/SpatialDiscretizations/ref_elem_data.jl +++ b/src/SpatialDiscretizations/ref_elem_data.jl @@ -1,12 +1,14 @@ -function RefElemData(elem::Tri, approx_type::Union{ModalTensor,NodalTensor}, N; - volume_quadrature_rule=(LGQuadrature(approx_type.p), - LGQuadrature(approx_type.p)), - facet_quadrature_rule=LGQuadrature(approx_type.p), Nplot=10) - +function RefElemData(elem::Tri, + approx_type::Union{ModalTensor, NodalTensor}, + N; + volume_quadrature_rule = (LGQuadrature(approx_type.p), + LGQuadrature(approx_type.p)), + facet_quadrature_rule = LGQuadrature(approx_type.p), + Nplot = 10,) fv = face_vertices(elem) # set faces for triangle # Construct matrices on reference elements - r,s = nodes(elem, N) + r, s = nodes(elem, N) Fmask = hcat(find_face_nodes(elem, r, s)...) VDM, Vr, Vs = basis(elem, N, r, s) @@ -18,45 +20,62 @@ function RefElemData(elem::Tri, approx_type::Union{ModalTensor,NodalTensor}, N; V1 = vandermonde(elem, 1, r, s) / vandermonde(elem, 1, r1, s1) r_1d, w_1d = quadrature(Line(), facet_quadrature_rule) - + wf = [w_1d; w_1d; w_1d] - (rf, sf) = ([r_1d; -r_1d; -ones(size(r_1d))], - [-ones(size(r_1d)); r_1d; r_1d]) + (rf, sf) = ([r_1d; -r_1d; -ones(size(r_1d))], [-ones(size(r_1d)); r_1d; r_1d]) nrJ = [zeros(size(r_1d)); ones(size(r_1d)); -ones(size(r_1d))] nsJ = [-ones(size(r_1d)); ones(size(r_1d)); zeros(size(r_1d))] - - rq, sq, wq = quadrature(elem, volume_quadrature_rule) + + rq, sq, wq = quadrature(elem, volume_quadrature_rule) Vq = vandermonde(elem, N, rq, sq) / VDM M = Vq' * diagm(wq) * Vq Pq = M \ (Vq' * diagm(wq)) Vf = vandermonde(elem, N, rf, sf) / VDM # - LIFT = M \ (Vf' * diagm(wf)) + LIFT = M \ (Vf' * diagm(wf)) # plotting nodes - rp, sp = χ(Tri(),equi_nodes(Quad(),Nplot)) + rp, sp = χ(Tri(), equi_nodes(Quad(), Nplot)) Vp = vandermonde(elem, N, rp, sp) / VDM - return RefElemData(elem, Polynomial(), N, fv, V1, - tuple(r, s), VDM, vec(Fmask), - tuple(rp, sp), Vp, - tuple(rq, sq), wq, Vq, - tuple(rf, sf), wf, Vf, tuple(nrJ, nsJ), - M, Pq, (Dr, Ds), LIFT) + return RefElemData(elem, + Polynomial(), + N, + fv, + V1, + tuple(r, s), + VDM, + vec(Fmask), + tuple(rp, sp), + Vp, + tuple(rq, sq), + wq, + Vq, + tuple(rf, sf), + wf, + Vf, + tuple(nrJ, nsJ), + M, + Pq, + (Dr, Ds), + LIFT) end -function RefElemData(elem::Tet, approx_type::Union{ModalTensor,NodalTensor}, N; - volume_quadrature_rule=(LGQuadrature(approx_type.p), - LGQuadrature(approx_type.p), LGQuadrature(approx_type.p)), - facet_quadrature_rule=(LGQuadrature(approx_type.p), - LGQuadrature(approx_type.p)), Nplot=10) - - fv = face_vertices(elem) +function RefElemData(elem::Tet, + approx_type::Union{ModalTensor, NodalTensor}, + N; + volume_quadrature_rule = (LGQuadrature(approx_type.p), + LGQuadrature(approx_type.p), + LGQuadrature(approx_type.p)), + facet_quadrature_rule = (LGQuadrature(approx_type.p), + LGQuadrature(approx_type.p)), + Nplot = 10,) + fv = face_vertices(elem) # Construct matrices on reference elements r, s, t = nodes(elem, N) - face_nodes = find_face_nodes(elem, r, s, t, 100*eps()) + face_nodes = find_face_nodes(elem, r, s, t, 100 * eps()) Fmask = hcat(face_nodes...) VDM, Vr, Vs, Vt = basis(elem, N, r, s, t) Dr, Ds, Dt = (A -> A / VDM).((Vr, Vs, Vt)) @@ -65,15 +84,15 @@ function RefElemData(elem::Tet, approx_type::Union{ModalTensor,NodalTensor}, N; r1, s1, t1 = nodes(elem, 1) V1 = vandermonde(elem, 1, r, s, t) / vandermonde(elem, 1, r1, s1, t1) - r_2d, s_2d, w_2d = quadrature(Tri(), - (facet_quadrature_rule[1], facet_quadrature_rule[2])) + r_2d, s_2d, w_2d = quadrature(Tri(), + (facet_quadrature_rule[1], facet_quadrature_rule[2])) (ee, zz) = (ones(size(r_2d)), zeros(size(r_2d))) rf = [r_2d; -(ee + r_2d + s_2d); -ee; r_2d] sf = [-ee; r_2d; r_2d; s_2d] tf = [s_2d; s_2d; s_2d; -ee] - + wf = [w_2d; w_2d; w_2d; w_2d] nrJ = [zz; ee; -ee; zz] nsJ = [-ee; ee; zz; zz] @@ -91,10 +110,25 @@ function RefElemData(elem::Tet, approx_type::Union{ModalTensor,NodalTensor}, N; rp, sp, tp = equi_nodes(elem, Nplot) Vp = vandermonde(elem, N, rp, sp, tp) / VDM - return RefElemData(elem, Polynomial(), N, fv, V1, - tuple(r, s, t), VDM, vec(Fmask), - tuple(rp, sp, tp), Vp, - tuple(rq, sq, tq), wq, Vq, - tuple(rf, sf, tf), wf, Vf, tuple(nrJ, nsJ, ntJ), - M, Pq, (Dr, Ds, Dt), LIFT) -end \ No newline at end of file + return RefElemData(elem, + Polynomial(), + N, + fv, + V1, + tuple(r, s, t), + VDM, + vec(Fmask), + tuple(rp, sp, tp), + Vp, + tuple(rq, sq, tq), + wq, + Vq, + tuple(rf, sf, tf), + wf, + Vf, + tuple(nrJ, nsJ, ntJ), + M, + Pq, + (Dr, Ds, Dt), + LIFT) +end diff --git a/src/SpatialDiscretizations/tensor_cartesian.jl b/src/SpatialDiscretizations/tensor_cartesian.jl index 61e0f7cc..f5eafe40 100644 --- a/src/SpatialDiscretizations/tensor_cartesian.jl +++ b/src/SpatialDiscretizations/tensor_cartesian.jl @@ -1,102 +1,131 @@ -function ReferenceApproximation( - approx_type::NodalTensor, element_type::Line; - mapping_degree::Int=1, N_plot::Int=10, - volume_quadrature_rule=LGLQuadrature(approx_type.p)) - - reference_element = RefElemData(Line(), - Polynomial{MultidimensionalQuadrature}(), mapping_degree, - quad_rule_vol=quadrature(Line(), volume_quadrature_rule), Nplot=N_plot) +function ReferenceApproximation(approx_type::NodalTensor, + element_type::Line; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = LGLQuadrature(approx_type.p),) + reference_element = RefElemData(Line(), + Polynomial{MultidimensionalQuadrature}(), + mapping_degree, + quad_rule_vol = quadrature(Line(), + volume_quadrature_rule), + Nplot = N_plot) (; rp, rq, rstq, rf, rstf) = reference_element - q = length(rq)-1 + q = length(rq) - 1 VDM, ∇VDM = basis(Line(), q, rq) if volume_quadrature_rule isa GaussLobattoQuadrature - R = SelectionMap(match_coordinate_vectors(rstf, rstq), q+1) - else + R = SelectionMap(match_coordinate_vectors(rstf, rstq), q + 1) + else R = OctavianMap(vandermonde(element_type, q, rf) / VDM) end V_plot = OctavianMap(vandermonde(element_type, q, rp) / VDM) - return ReferenceApproximation(NodalTensor(q), reference_element, - (OctavianMap(∇VDM / VDM),), LinearMap(I, q+1), R, R, V_plot) + return ReferenceApproximation(NodalTensor(q), + reference_element, + (OctavianMap(∇VDM / VDM),), + LinearMap(I, q + 1), + R, + R, + V_plot) end -function ReferenceApproximation(approx_type::NodalTensor, - element_type::Quad; mapping_degree::Int=1, N_plot::Int=10, - volume_quadrature_rule=LGLQuadrature(approx_type.p), - facet_quadrature_rule=LGLQuadrature(approx_type.p)) +function ReferenceApproximation(approx_type::NodalTensor, + element_type::Quad; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = LGLQuadrature(approx_type.p), + facet_quadrature_rule = LGLQuadrature(approx_type.p),) # one-dimensional operators - nodes_1D = quadrature(Line(),volume_quadrature_rule)[1] - q = length(nodes_1D)-1 + nodes_1D = quadrature(Line(), volume_quadrature_rule)[1] + q = length(nodes_1D) - 1 VDM_1D, ∇VDM_1D = basis(Line(), q, nodes_1D) D_1D = OctavianMap(∇VDM_1D / VDM_1D) - I_1D = LinearMap(I, q+1) + I_1D = LinearMap(I, q + 1) R_L = OctavianMap(vandermonde(Line(), q, [-1.0]) / VDM_1D) R_R = OctavianMap(vandermonde(Line(), q, [1.0]) / VDM_1D) # reference element data reference_element = RefElemData(element_type, - Polynomial{MultidimensionalQuadrature}(), mapping_degree, - quad_rule_vol=quadrature(Quad(), volume_quadrature_rule), - quad_rule_face=quadrature(Line(), facet_quadrature_rule), Nplot=N_plot) + Polynomial{MultidimensionalQuadrature}(), + mapping_degree, + quad_rule_vol = quadrature(Quad(), + volume_quadrature_rule), + quad_rule_face = quadrature(Line(), + facet_quadrature_rule), + Nplot = N_plot) (; rstp, rstq, rstf) = reference_element # extrapolation operators if volume_quadrature_rule == facet_quadrature_rule if volume_quadrature_rule isa GaussLobattoQuadrature - R = SelectionMap(match_coordinate_vectors(rstf, rstq), (q+1)^2) + R = SelectionMap(match_coordinate_vectors(rstf, rstq), (q + 1)^2) else R = [R_L ⊗ I_1D; R_R ⊗ I_1D; I_1D ⊗ R_L; I_1D ⊗ R_R] end else - R = OctavianMap(vandermonde(element_type,q,rstf...) / - vandermonde(element_type,q,rstq...)) + R = OctavianMap(vandermonde(element_type, q, rstf...) / + vandermonde(element_type, q, rstq...)) end - - V_plot = OctavianMap(vandermonde(element_type, q, rstp...) / - vandermonde(element_type, q, rstq...)) - return ReferenceApproximation(NodalTensor(q), reference_element, - (D_1D ⊗ I_1D, I_1D ⊗ D_1D), LinearMap(I, (q+1)^2), R, R, V_plot) + V_plot = OctavianMap(vandermonde(element_type, q, rstp...) / + vandermonde(element_type, q, rstq...)) + + return ReferenceApproximation(NodalTensor(q), + reference_element, + (D_1D ⊗ I_1D, I_1D ⊗ D_1D), + LinearMap(I, (q + 1)^2), + R, + R, + V_plot) end -function ReferenceApproximation(approx_type::NodalTensor, ::Hex; - mapping_degree::Int=1, N_plot::Int=10, - volume_quadrature_rule=LGLQuadrature(approx_type.p), facet_quadrature_rule=LGLQuadrature(approx_type.p)) +function ReferenceApproximation(approx_type::NodalTensor, + ::Hex; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = LGLQuadrature(approx_type.p), + facet_quadrature_rule = LGLQuadrature(approx_type.p),) # one-dimensional operators - nodes_1D = quadrature(Line(),volume_quadrature_rule)[1] - q = length(nodes_1D)-1 + nodes_1D = quadrature(Line(), volume_quadrature_rule)[1] + q = length(nodes_1D) - 1 VDM_1D, ∇VDM_1D = basis(Line(), q, nodes_1D) D_1D = OctavianMap(∇VDM_1D / VDM_1D) - I_1D = LinearMap(I, q+1) + I_1D = LinearMap(I, q + 1) # reference element data - reference_element = RefElemData(Hex(), - Polynomial{MultidimensionalQuadrature}(), mapping_degree, - quad_rule_vol=quadrature(Hex(), volume_quadrature_rule), - quad_rule_face=quadrature(Quad(), facet_quadrature_rule), Nplot=N_plot) + reference_element = RefElemData(Hex(), + Polynomial{MultidimensionalQuadrature}(), + mapping_degree, + quad_rule_vol = quadrature(Hex(), + volume_quadrature_rule), + quad_rule_face = quadrature(Quad(), + facet_quadrature_rule), + Nplot = N_plot) (; rstp, rstq, rstf) = reference_element # extrapolation operators if (volume_quadrature_rule == facet_quadrature_rule) && - (volume_quadrature_rule isa GaussLobattoQuadrature) - R = SelectionMap(match_coordinate_vectors(rstf,rstq),(q+1)^3) - else - R = OctavianMap(vandermonde(Hex(),q,rstf...) / - vandermonde(Hex(),q,rstq...)) + (volume_quadrature_rule isa GaussLobattoQuadrature) + R = SelectionMap(match_coordinate_vectors(rstf, rstq), (q + 1)^3) + else + R = OctavianMap(vandermonde(Hex(), q, rstf...) / vandermonde(Hex(), q, rstq...)) end - - # mapping from solution/quadrature to plotting nodes - V_plot = OctavianMap(vandermonde(Hex(), q, rstp...) / - vandermonde(Hex(), q, rstq...)) - return ReferenceApproximation(NodalTensor(q), reference_element, - (D_1D ⊗ I_1D ⊗ I_1D, I_1D ⊗ D_1D ⊗ I_1D, I_1D ⊗ I_1D ⊗ D_1D), - LinearMap(I, (q+1)^3), R, R, V_plot) -end \ No newline at end of file + # mapping from solution/quadrature to plotting nodes + V_plot = OctavianMap(vandermonde(Hex(), q, rstp...) / vandermonde(Hex(), q, rstq...)) + + return ReferenceApproximation(NodalTensor(q), + reference_element, + (D_1D ⊗ I_1D ⊗ I_1D, I_1D ⊗ D_1D ⊗ I_1D, + I_1D ⊗ I_1D ⊗ D_1D), + LinearMap(I, (q + 1)^3), + R, + R, + V_plot) +end diff --git a/src/SpatialDiscretizations/tensor_simplex.jl b/src/SpatialDiscretizations/tensor_simplex.jl index a1525a73..49c3a948 100644 --- a/src/SpatialDiscretizations/tensor_simplex.jl +++ b/src/SpatialDiscretizations/tensor_simplex.jl @@ -1,251 +1,241 @@ """Duffy transform from the square to triangle""" -@inline function χ(::Tri, η::Union{NTuple{2,Float64},NTuple{2,Vector{Float64}}}) - return (0.5.*(1.0 .+ η[1]).*(1.0 .- η[2]) .- 1.0, η[2]) +@inline function χ(::Tri, η::Union{NTuple{2, Float64}, NTuple{2, Vector{Float64}}}) + return (0.5 .* (1.0 .+ η[1]) .* (1.0 .- η[2]) .- 1.0, η[2]) end """Duffy transform from the cube to tetrahedron""" -@inline function χ(::Tet, η::Union{NTuple{3,Float64},NTuple{3,Vector{Float64}}}) - ξ_pri = (0.5.*(1.0 .+ η[1]).*(1.0 .- η[3]) .- 1.0, η[2], η[3]) - ξ_pyr = (ξ_pri[1], 0.5.*(1.0 .+ η[2]).*(1.0 .- η[3]) .- 1.0, ξ_pri[3]) - return (0.5.*(1.0 .+ ξ_pri[1]).*(1.0 .- η[2]) .- 1.0, ξ_pyr[2] , ξ_pyr[3]) +@inline function χ(::Tet, η::Union{NTuple{3, Float64}, NTuple{3, Vector{Float64}}}) + ξ_pri = (0.5 .* (1.0 .+ η[1]) .* (1.0 .- η[3]) .- 1.0, η[2], η[3]) + ξ_pyr = (ξ_pri[1], 0.5 .* (1.0 .+ η[2]) .* (1.0 .- η[3]) .- 1.0, ξ_pri[3]) + return (0.5 .* (1.0 .+ ξ_pri[1]) .* (1.0 .- η[2]) .- 1.0, ξ_pyr[2], ξ_pyr[3]) end -function reference_geometric_factors(::Tri, - quadrature_rule::NTuple{2,AbstractQuadratureRule}) - - η = quadrature(Quad(),quadrature_rule) - N = size(η[1],1) +function reference_geometric_factors(::Tri, + quadrature_rule::NTuple{2, AbstractQuadratureRule}) + η = quadrature(Quad(), quadrature_rule) + N = size(η[1], 1) Λ_ref = Array{Float64, 3}(undef, N, 2, 2) - - if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (0,0)) - - J_ref = (x->0.5*(1.0-x)).(η[2]) - Λ_ref[:,1,1] = ones(N) # Jdη1/dξ1 - Λ_ref[:,1,2] = (x->0.5*(1.0+x)).(η[1]) # Jdη1/dξ2 - Λ_ref[:,2,1] = zeros(N) # Jdη2/dξ1 - Λ_ref[:,2,2] = (x->0.5*(1.0-x)).(η[2]) # Jdη2/dξ2 - - elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (1,0)) - - J_ref = 0.5*ones(N) - Λ_ref[:,1,1] = (x->1.0/(1.0-x)).(η[2]) # Jdη1/dξ1 - Λ_ref[:,1,2] = (x->0.5*(1.0+x)).(η[1]) .* - (x->1.0/(1.0-x)).(η[2]) # Jdη1/dξ2 - Λ_ref[:,2,1] = zeros(N) # Jdη2/dξ1 - Λ_ref[:,2,2] = 0.5*ones(N) # Jdη2/dξ2 - - else - @error "Chosen Jacobi weight not supported" + + if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (0, 0)) + J_ref = (x -> 0.5 * (1.0 - x)).(η[2]) + Λ_ref[:, 1, 1] = ones(N) # Jdη1/dξ1 + Λ_ref[:, 1, 2] = (x -> 0.5 * (1.0 + x)).(η[1]) # Jdη1/dξ2 + Λ_ref[:, 2, 1] = zeros(N) # Jdη2/dξ1 + Λ_ref[:, 2, 2] = (x -> 0.5 * (1.0 - x)).(η[2]) # Jdη2/dξ2 + + elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (1, 0)) + J_ref = 0.5 * ones(N) + Λ_ref[:, 1, 1] = (x -> 1.0 / (1.0 - x)).(η[2]) # Jdη1/dξ1 + Λ_ref[:, 1, 2] = (x -> 0.5 * (1.0 + x)).(η[1]) .* (x -> 1.0 / (1.0 - x)).(η[2]) # Jdη1/dξ2 + Λ_ref[:, 2, 1] = zeros(N) # Jdη2/dξ1 + Λ_ref[:, 2, 2] = 0.5 * ones(N) # Jdη2/dξ2 + + else + @error "Chosen Jacobi weight not supported" end return J_ref, Λ_ref end function reference_geometric_factors(::Tet, - quadrature_rule::NTuple{3,AbstractQuadratureRule}) - - η = quadrature(Hex(),quadrature_rule) - N = size(η[1],1) + quadrature_rule::NTuple{3, AbstractQuadratureRule}) + η = quadrature(Hex(), quadrature_rule) + N = size(η[1], 1) Λ_ref = Array{Float64, 3}(undef, N, 3, 3) - if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (0,0) && - (quadrature_rule[3].a, quadrature_rule[3].b) == (0,0)) - - J_ref = (x->0.5*(1.0-x)).(η[2]) .* (x->(0.5*(1.0-x))^2).(η[3]) - Λ_ref[:,1,1] = (x->0.5*(1.0-x)).(η[3]) # Jdη1/dξ1 - Λ_ref[:,1,2] = (x->0.5*(1.0+x)).(η[1]) .* - (x->0.5*(1.0-x)).(η[3]) # Jdη1/dξ2 - Λ_ref[:,1,3] = (x->0.5*(1.0+x)).(η[1]) .* - (x->0.5*(1.0-x)).(η[3]) # Jdη1/dξ3 - Λ_ref[:,2,1] = zeros(N) # Jdη2/dξ1 - Λ_ref[:,2,2] = (x->0.5*(1.0-x)).(η[2]) .* - (x->0.5*(1.0-x)).(η[3]) # Jdη2/dξ2 - Λ_ref[:,2,3] = (x->0.5*(1.0+x)).(η[2]) .* - (x->0.5*(1.0-x)).(η[2]) .* - (x->0.5*(1.0-x)).(η[3]) # Jdη2/dξ3 - Λ_ref[:,3,1] = zeros(N) # Jdη3/dξ1 - Λ_ref[:,3,2] = zeros(N) # Jdη3/dξ2 - Λ_ref[:,3,3] = (x->0.5*(1.0-x)).(η[2]) .* - (x->(0.5*(1.0-x))^2).(η[3])# Jdη3/dξ3 - - elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0,0) && - (quadrature_rule[2].a, quadrature_rule[2].b) == (0,0) && - (quadrature_rule[3].a, quadrature_rule[3].b) == (1,0)) - - J_ref = 0.125*(x->(1.0-x)).(η[2]) .* (x->(1.0-x)).(η[3]) - Λ_ref[:,1,1] = 0.5*ones(N) # Jdη1/dξ1 - Λ_ref[:,1,2] =0.25*(x->(1.0+x)).(η[1]) # Jdη1/dξ2 - Λ_ref[:,1,3] = 0.25*(x->(1.0+x)).(η[1]) # Jdη1/dξ3 - Λ_ref[:,2,1] = zeros(N) # Jdη2/dξ1 - Λ_ref[:,2,2] = 0.25*(x->(1.0-x)).(η[2]) # Jdη2/dξ2 - Λ_ref[:,2,3] = 0.125*(x->(1.0+x)).(η[2]) .* - (x->(1.0-x)).(η[2]) # Jdη2/dξ3 - Λ_ref[:,3,1] = zeros(N) # Jdη3/dξ1 - Λ_ref[:,3,2] = zeros(N) # Jdη3/dξ2 - Λ_ref[:,3,3] = 0.125*(x->(1.0-x)).(η[2]) .* - (x->(1.0-x)).(η[3])# Jdη3/dξ3 - - else - @error "Chosen Jacobi weight not supported" + if ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (0, 0) && + (quadrature_rule[3].a, quadrature_rule[3].b) == (0, 0)) + J_ref = (x -> 0.5 * (1.0 - x)).(η[2]) .* (x -> (0.5 * (1.0 - x))^2).(η[3]) + Λ_ref[:, 1, 1] = (x -> 0.5 * (1.0 - x)).(η[3]) # Jdη1/dξ1 + Λ_ref[:, 1, 2] = (x -> 0.5 * (1.0 + x)).(η[1]) .* (x -> 0.5 * (1.0 - x)).(η[3]) # Jdη1/dξ2 + Λ_ref[:, 1, 3] = (x -> 0.5 * (1.0 + x)).(η[1]) .* (x -> 0.5 * (1.0 - x)).(η[3]) # Jdη1/dξ3 + Λ_ref[:, 2, 1] = zeros(N) # Jdη2/dξ1 + Λ_ref[:, 2, 2] = (x -> 0.5 * (1.0 - x)).(η[2]) .* (x -> 0.5 * (1.0 - x)).(η[3]) # Jdη2/dξ2 + Λ_ref[:, 2, 3] = (x -> 0.5 * (1.0 + x)).(η[2]) .* (x -> 0.5 * (1.0 - x)).(η[2]) .* + (x -> 0.5 * (1.0 - x)).(η[3]) # Jdη2/dξ3 + Λ_ref[:, 3, 1] = zeros(N) # Jdη3/dξ1 + Λ_ref[:, 3, 2] = zeros(N) # Jdη3/dξ2 + Λ_ref[:, 3, 3] = (x -> 0.5 * (1.0 - x)).(η[2]) .* (x -> (0.5 * (1.0 - x))^2).(η[3])# Jdη3/dξ3 + + elseif ((quadrature_rule[1].a, quadrature_rule[1].b) == (0, 0) && + (quadrature_rule[2].a, quadrature_rule[2].b) == (0, 0) && + (quadrature_rule[3].a, quadrature_rule[3].b) == (1, 0)) + J_ref = 0.125 * (x -> (1.0 - x)).(η[2]) .* (x -> (1.0 - x)).(η[3]) + Λ_ref[:, 1, 1] = 0.5 * ones(N) # Jdη1/dξ1 + Λ_ref[:, 1, 2] = 0.25 * (x -> (1.0 + x)).(η[1]) # Jdη1/dξ2 + Λ_ref[:, 1, 3] = 0.25 * (x -> (1.0 + x)).(η[1]) # Jdη1/dξ3 + Λ_ref[:, 2, 1] = zeros(N) # Jdη2/dξ1 + Λ_ref[:, 2, 2] = 0.25 * (x -> (1.0 - x)).(η[2]) # Jdη2/dξ2 + Λ_ref[:, 2, 3] = 0.125 * (x -> (1.0 + x)).(η[2]) .* (x -> (1.0 - x)).(η[2]) # Jdη2/dξ3 + Λ_ref[:, 3, 1] = zeros(N) # Jdη3/dξ1 + Λ_ref[:, 3, 2] = zeros(N) # Jdη3/dξ2 + Λ_ref[:, 3, 3] = 0.125 * (x -> (1.0 - x)).(η[2]) .* (x -> (1.0 - x)).(η[3])# Jdη3/dξ3 + + else + @error "Chosen Jacobi weight not supported" end - + return J_ref, Λ_ref end -function warped_product(::Tri, p::Int, η1D::NTuple{2,Vector{Float64}}) - +function warped_product(::Tri, p::Int, η1D::NTuple{2, Vector{Float64}}) (M1, M2) = (length(η1D[1]), length(η1D[2])) - σₒ = [M2*(i-1) + j for i in 1:M1, j in 1:M2] - σᵢ = zeros(Int,p+1,p+1) - A = zeros(M1, p+1) - B = zeros(M2, p+1, p+1) + σₒ = [M2 * (i - 1) + j for i in 1:M1, j in 1:M2] + σᵢ = zeros(Int, p + 1, p + 1) + A = zeros(M1, p + 1) + B = zeros(M2, p + 1, p + 1) k = 1 - @inbounds for i = 0:p - for j = 0:p-i - σᵢ[i+1,j+1] = k + @inbounds for i in 0:p + for j in 0:(p - i) + σᵢ[i + 1, j + 1] = k k = k + 1 for α1 in 1:M1, α2 in 1:M2 - A[α1,i+1] = sqrt(2) * jacobiP(η1D[1][α1],0,0,i) - B[α2,i+1,j+1] = (1-η1D[2][α2])^i * jacobiP(η1D[2][α2],2i+1,0,j) + A[α1, i + 1] = sqrt(2) * jacobiP(η1D[1][α1], 0, 0, i) + B[α2, i + 1, j + 1] = (1 - η1D[2][α2])^i * jacobiP(η1D[2][α2], 2i + 1, 0, j) end end end - return WarpedTensorProductMap2D( - SArray{Tuple{M1,p+1}}(A), - SArray{Tuple{M1,p+1,p+1}}(B), - SArray{Tuple{M1,M2}}(σᵢ), - SArray{Tuple{p+1,p+1}}(σₒ)) + return WarpedTensorProductMap2D(SArray{Tuple{M1, p + 1}}(A), + SArray{Tuple{M1, p + 1, p + 1}}(B), + SArray{Tuple{M1, M2}}(σᵢ), + SArray{Tuple{p + 1, p + 1}}(σₒ)) end -function warped_product(::Tet, p::Int, η1D::NTuple{3,Vector{Float64}}) - +function warped_product(::Tet, p::Int, η1D::NTuple{3, Vector{Float64}}) (M1, M2, M3) = (length(η1D[1]), length(η1D[2]), length(η1D[3])) - σₒ = [M2*M3*(i-1) + M3*(j-1) + k for i in 1:M1, j in 1:M2, k in 1:M3] - σᵢ = zeros(Int,p+1,p+1,p+1) - A = zeros(M1, p+1) - B = zeros(M2, p+1, p+1) - C = zeros(M3, p+1, p+1, p+1) + σₒ = [M2 * M3 * (i - 1) + M3 * (j - 1) + k for i in 1:M1, j in 1:M2, k in 1:M3] + σᵢ = zeros(Int, p + 1, p + 1, p + 1) + A = zeros(M1, p + 1) + B = zeros(M2, p + 1, p + 1) + C = zeros(M3, p + 1, p + 1, p + 1) l = 1 - @inbounds for i = 0:p - for j = 0:p-i - for k = 0:p-i-j - σᵢ[i+1,j+1,k+1] = l + @inbounds for i in 0:p + for j in 0:(p - i) + for k in 0:(p - i - j) + σᵢ[i + 1, j + 1, k + 1] = l l = l + 1 for α1 in 1:M1, α2 in 1:M2, α3 in 1:M3 - A[α1,i+1] = sqrt(2) * jacobiP(η1D[1][α1],0,0,i) - B[α2,i+1,j+1] = (1-η1D[2][α2])^i * - jacobiP(η1D[2][α2],2i+1,0,j) - C[α3,i+1,j+1,k+1] = 2*(1-η1D[3][α3])^(i+j) * - jacobiP(η1D[3][α3],2i+2j+2,0,k) + A[α1, i + 1] = sqrt(2) * jacobiP(η1D[1][α1], 0, 0, i) + B[α2, i + 1, j + 1] = (1 - η1D[2][α2])^i * + jacobiP(η1D[2][α2], 2i + 1, 0, j) + C[α3, i + 1, j + 1, k + 1] = 2 * + (1 - η1D[3][α3])^(i + j) * + jacobiP(η1D[3][α3], 2i + 2j + 2, 0, k) end end end end - return WarpedTensorProductMap3D( - SArray{Tuple{M1,p+1}}(A), - SArray{Tuple{M1,p+1,p+1}}(B), - SArray{Tuple{M1,p+1,p+1,p+1}}(C), - SArray{Tuple{M1,M2,M3}}(σᵢ), - SArray{Tuple{p+1,p+1,p+1}}(σₒ)) + return WarpedTensorProductMap3D(SArray{Tuple{M1, p + 1}}(A), + SArray{Tuple{M1, p + 1, p + 1}}(B), + SArray{Tuple{M1, p + 1, p + 1, p + 1}}(C), + SArray{Tuple{M1, M2, M3}}(σᵢ), + SArray{Tuple{p + 1, p + 1, p + 1}}(σₒ)) end -function operators_1d( - quadrature_rule::NTuple{d,AbstractQuadratureRule}) where {d} - η_1D, q, V_1D, D_1D, I_1D, R_L, R_R = fill((),7) +function operators_1d(quadrature_rule::NTuple{d, AbstractQuadratureRule}) where {d} + η_1D, q, V_1D, D_1D, I_1D, R_L, R_R = fill((), 7) for m in 1:d - η, _ = quadrature(Line(),quadrature_rule[m]) + η, _ = quadrature(Line(), quadrature_rule[m]) η_1D = (η_1D..., η) q = (q..., length(η_1D[m]) - 1) - V_1D = (V_1D..., vandermonde(Line(),q[m],η_1D[m])) - D_1D = (D_1D..., OctavianMap( - grad_vandermonde(Line(),q[m], η_1D[m]) / V_1D[m])) - I_1D = (I_1D..., LinearMap(I,q[m]+1)) - R_L = (R_L..., OctavianMap(vandermonde(Line(),q[m],[-1.0]) / V_1D[m])) - R_R = (R_R...,OctavianMap(vandermonde(Line(),q[m],[1.0]) / V_1D[m])) + V_1D = (V_1D..., vandermonde(Line(), q[m], η_1D[m])) + D_1D = (D_1D..., OctavianMap(grad_vandermonde(Line(), q[m], η_1D[m]) / V_1D[m])) + I_1D = (I_1D..., LinearMap(I, q[m] + 1)) + R_L = (R_L..., OctavianMap(vandermonde(Line(), q[m], [-1.0]) / V_1D[m])) + R_R = (R_R..., OctavianMap(vandermonde(Line(), q[m], [1.0]) / V_1D[m])) end - + return η_1D, q, V_1D, D_1D, I_1D, R_L, R_R end -function ReferenceApproximation( - approx_type::AbstractTensorProduct, - ::Tri; mapping_degree::Int=1, N_plot::Int=10, - volume_quadrature_rule=(LGQuadrature(approx_type.p), - LGQuadrature(approx_type.p)), - facet_quadrature_rule=LGQuadrature(approx_type.p), - sum_factorize_vandermonde=true) +function ReferenceApproximation(approx_type::AbstractTensorProduct, + ::Tri; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = (LGQuadrature(approx_type.p), + LGQuadrature(approx_type.p)), + facet_quadrature_rule = LGQuadrature(approx_type.p), + sum_factorize_vandermonde = true,) # one-dimensional operators - η_1D, q, V_1D, D_1D, I_1D, R_L, R_R = operators_1d( - volume_quadrature_rule) + η_1D, q, V_1D, D_1D, I_1D, R_L, R_R = operators_1d(volume_quadrature_rule) # geometric factors for collapsed coordinate transformation - J_ref, Λ_ref = reference_geometric_factors(Tri(),volume_quadrature_rule) + J_ref, Λ_ref = reference_geometric_factors(Tri(), volume_quadrature_rule) # one-dimensional facet quadrature rule η_f, _ = quadrature(Line(), facet_quadrature_rule) # interpolation/extrapolation operators if volume_quadrature_rule[1] == facet_quadrature_rule - η1_to_ηf = LinearMap(I, q[1]+1) + η1_to_ηf = LinearMap(I, q[1] + 1) else - η1_to_ηf = OctavianMap(vandermonde(Line(),q[1],η_f) / V_1D[1]) + η1_to_ηf = OctavianMap(vandermonde(Line(), q[1], η_f) / V_1D[1]) end if volume_quadrature_rule[2] == facet_quadrature_rule - η2_to_ηf = LinearMap(I, q[2]+1) + η2_to_ηf = LinearMap(I, q[2] + 1) else - η2_to_ηf = OctavianMap(vandermonde(Line(),q[2],η_f) / V_1D[2]) + η2_to_ηf = OctavianMap(vandermonde(Line(), q[2], η_f) / V_1D[2]) end R = [η1_to_ηf ⊗ R_L[2]; R_R[1] ⊗ η2_to_ηf; R_L[1] ⊗ η2_to_ηf] - + # reference element data (mainly used for mapping, normals, etc.) - reference_element = RefElemData(Tri(), approx_type, mapping_degree, - volume_quadrature_rule=volume_quadrature_rule, - facet_quadrature_rule=facet_quadrature_rule, Nplot=N_plot) + reference_element = RefElemData(Tri(), + approx_type, + mapping_degree, + volume_quadrature_rule = volume_quadrature_rule, + facet_quadrature_rule = facet_quadrature_rule, + Nplot = N_plot) # construct nodal or modal scheme (different Vandermonde matrix) if approx_type isa ModalTensor if sum_factorize_vandermonde - V = warped_product(Tri(),approx_type.p, η_1D) + V = warped_product(Tri(), approx_type.p, η_1D) else - V = OctavianMap(vandermonde(Tri(), - approx_type.p, reference_element.rstq...)) + V = OctavianMap(vandermonde(Tri(), approx_type.p, reference_element.rstq...)) end - V_plot = OctavianMap(vandermonde(Tri(), - approx_type.p, reference_element.rstp...)) + V_plot = OctavianMap(vandermonde(Tri(), approx_type.p, reference_element.rstp...)) else - V = LinearMap(I, (q[1]+1)*(q[2]+1)) - V_plot = (vandermonde(Line(),q[1],equi_nodes(Line(),N_plot))/V_1D[1]) ⊗ - (vandermonde(Line(),q[2],equi_nodes(Line(),N_plot))/V_1D[2]) + V = LinearMap(I, (q[1] + 1) * (q[2] + 1)) + V_plot = (vandermonde(Line(), q[1], equi_nodes(Line(), N_plot)) / V_1D[1]) ⊗ + (vandermonde(Line(), q[2], equi_nodes(Line(), N_plot)) / V_1D[2]) end - - return ReferenceApproximation(approx_type, reference_element, - (D_1D[1] ⊗ I_1D[2], I_1D[1] ⊗ D_1D[2]), V, R * V, R, - V_plot, ReferenceMapping(J_ref, Λ_ref)) + + return ReferenceApproximation(approx_type, + reference_element, + (D_1D[1] ⊗ I_1D[2], I_1D[1] ⊗ D_1D[2]), + V, + R * V, + R, + V_plot, + ReferenceMapping(J_ref, Λ_ref)) end -function ReferenceApproximation( - approx_type::AbstractTensorProduct, - ::Tet; mapping_degree::Int=1, N_plot::Int=10, - volume_quadrature_rule=(LGQuadrature(approx_type.p), - LGQuadrature(approx_type.p), GaussQuadrature(approx_type.p,1,0)), - facet_quadrature_rule=(LGQuadrature(approx_type.p), - GaussQuadrature(approx_type.p,1,0)), - sum_factorize_vandermonde=true) +function ReferenceApproximation(approx_type::AbstractTensorProduct, + ::Tet; + mapping_degree::Int = 1, + N_plot::Int = 10, + volume_quadrature_rule = (LGQuadrature(approx_type.p), + LGQuadrature(approx_type.p), + GaussQuadrature(approx_type.p, 1, + 0)), + facet_quadrature_rule = (LGQuadrature(approx_type.p), + GaussQuadrature(approx_type.p, 1, + 0)), + sum_factorize_vandermonde = true,) # one-dimensional operators - η_1D, q, V_1D, D_1D, I_1D, R_L, R_R = operators_1d( - volume_quadrature_rule) + η_1D, q, V_1D, D_1D, I_1D, R_L, R_R = operators_1d(volume_quadrature_rule) # reference geometric factors for cube-to-tetrahedron mapping - J_ref, Λ_ref = reference_geometric_factors(Tet(),volume_quadrature_rule) + J_ref, Λ_ref = reference_geometric_factors(Tet(), volume_quadrature_rule) # two-dimensional facet quadrature nodes and weights η_f1, _ = quadrature(Line(), facet_quadrature_rule[1]) @@ -253,55 +243,64 @@ function ReferenceApproximation( # interpolation/extrapolation operators if volume_quadrature_rule[1] == facet_quadrature_rule[1] - η1_to_ηf1 = LinearMap(I,q[1]+1) + η1_to_ηf1 = LinearMap(I, q[1] + 1) else - η1_to_ηf1 = OctavianMap(vandermonde(Line(),q[1],η_f1) / V_1D[1]) + η1_to_ηf1 = OctavianMap(vandermonde(Line(), q[1], η_f1) / V_1D[1]) end if volume_quadrature_rule[2] == facet_quadrature_rule[1] - η2_to_ηf1 = LinearMap(I,q[2]+1) + η2_to_ηf1 = LinearMap(I, q[2] + 1) else - η2_to_ηf1 = OctavianMap(vandermonde(Line(),q[2],η_f1) / V_1D[2]) + η2_to_ηf1 = OctavianMap(vandermonde(Line(), q[2], η_f1) / V_1D[2]) end if volume_quadrature_rule[2] == facet_quadrature_rule[2] - η2_to_ηf2 = LinearMap(I,q[2]+1) + η2_to_ηf2 = LinearMap(I, q[2] + 1) else - η2_to_ηf2 = OctavianMap(vandermonde(Line(),q[2],η_f2) / V_1D[2]) + η2_to_ηf2 = OctavianMap(vandermonde(Line(), q[2], η_f2) / V_1D[2]) end if volume_quadrature_rule[3] == facet_quadrature_rule[2] - η3_to_ηf2 = LinearMap(I,q[3]+1) + η3_to_ηf2 = LinearMap(I, q[3] + 1) else - η3_to_ηf2 = OctavianMap(vandermonde(Line(),q[3],η_f2) / V_1D[3]) + η3_to_ηf2 = OctavianMap(vandermonde(Line(), q[3], η_f2) / V_1D[3]) end - R = [η1_to_ηf1 ⊗ R_L[2] ⊗ η3_to_ηf2; R_R[1] ⊗ η2_to_ηf1 ⊗ η3_to_ηf2; - R_L[1] ⊗ η2_to_ηf1 ⊗ η3_to_ηf2; η1_to_ηf1 ⊗ η2_to_ηf2 ⊗ R_L[3]] - + R = [η1_to_ηf1 ⊗ R_L[2] ⊗ η3_to_ηf2 + R_R[1] ⊗ η2_to_ηf1 ⊗ η3_to_ηf2 + R_L[1] ⊗ η2_to_ηf1 ⊗ η3_to_ηf2 + η1_to_ηf1 ⊗ η2_to_ηf2 ⊗ R_L[3]] + # reference element data (mainly used for mapping, normals, etc.) - reference_element = RefElemData(Tet(), approx_type, mapping_degree, - volume_quadrature_rule=volume_quadrature_rule, - facet_quadrature_rule=facet_quadrature_rule, Nplot=N_plot) + reference_element = RefElemData(Tet(), + approx_type, + mapping_degree, + volume_quadrature_rule = volume_quadrature_rule, + facet_quadrature_rule = facet_quadrature_rule, + Nplot = N_plot) # construct nodal or modal scheme if approx_type isa ModalTensor if sum_factorize_vandermonde - V = warped_product(Tet(),approx_type.p, η_1D) + V = warped_product(Tet(), approx_type.p, η_1D) else - V = OctavianMap(vandermonde(Tet(), - approx_type.p, reference_element.rstq...)) + V = OctavianMap(vandermonde(Tet(), approx_type.p, reference_element.rstq...)) end - V_plot = OctavianMap(vandermonde(Tet(), approx_type.p, - reference_element.rstp...)) + V_plot = OctavianMap(vandermonde(Tet(), approx_type.p, reference_element.rstp...)) else - V = LinearMap(I, (q[1]+1)*(q[2]+1)*(q[3]+1)) - V_plot = OctavianMap( - vandermonde(Line(),q[1],equi_nodes(Line(),N_plot))/V_1D[1]) ⊗ - OctavianMap( - vandermonde(Line(),q[2],equi_nodes(Line(),N_plot))/V_1D[2]) ⊗ - OctavianMap( - vandermonde(Line(),q[3],equi_nodes(Line(),N_plot))/V_1D[3]) + V = LinearMap(I, (q[1] + 1) * (q[2] + 1) * (q[3] + 1)) + V_plot = OctavianMap(vandermonde(Line(), q[1], equi_nodes(Line(), N_plot)) / + V_1D[1]) ⊗ + OctavianMap(vandermonde(Line(), q[2], equi_nodes(Line(), N_plot)) / + V_1D[2]) ⊗ + OctavianMap(vandermonde(Line(), q[3], equi_nodes(Line(), N_plot)) / + V_1D[3]) end - + return ReferenceApproximation(approx_type, - reference_element, (D_1D[1] ⊗ I_1D[2] ⊗ I_1D[3], - I_1D[1] ⊗ D_1D[2] ⊗ I_1D[3], I_1D[1] ⊗ I_1D[2] ⊗ D_1D[3]), - V, R * V, R, V_plot, ReferenceMapping(J_ref, Λ_ref)) -end \ No newline at end of file + reference_element, + (D_1D[1] ⊗ I_1D[2] ⊗ I_1D[3], + I_1D[1] ⊗ D_1D[2] ⊗ I_1D[3], + I_1D[1] ⊗ I_1D[2] ⊗ D_1D[3]), + V, + R * V, + R, + V_plot, + ReferenceMapping(J_ref, Λ_ref)) +end diff --git a/src/StableSpectralElements.jl b/src/StableSpectralElements.jl index ea500e46..d7ea59d2 100644 --- a/src/StableSpectralElements.jl +++ b/src/StableSpectralElements.jl @@ -1,32 +1,32 @@ module StableSpectralElements - if VERSION < v"1.10" - error("StableSpectralElements.jl requires Julia v1.10 or newer.") - end +if VERSION < v"1.10" + error("StableSpectralElements.jl requires Julia v1.10 or newer.") +end - using Reexport +using Reexport - include("GridFunctions/GridFunctions.jl") - @reexport using .GridFunctions +include("GridFunctions/GridFunctions.jl") +@reexport using .GridFunctions - include("ConservationLaws/ConservationLaws.jl") - @reexport using .ConservationLaws +include("ConservationLaws/ConservationLaws.jl") +@reexport using .ConservationLaws - include("MatrixFreeOperators/MatrixFreeOperators.jl") - @reexport using .MatrixFreeOperators +include("MatrixFreeOperators/MatrixFreeOperators.jl") +@reexport using .MatrixFreeOperators - include("SpatialDiscretizations/SpatialDiscretizations.jl") - @reexport using .SpatialDiscretizations +include("SpatialDiscretizations/SpatialDiscretizations.jl") +@reexport using .SpatialDiscretizations - include("Solvers/Solvers.jl") - @reexport using .Solvers +include("Solvers/Solvers.jl") +@reexport using .Solvers - include("File/File.jl") - @reexport using .File +include("File/File.jl") +@reexport using .File - include("Visualize/Visualize.jl") - @reexport using .Visualize +include("Visualize/Visualize.jl") +@reexport using .Visualize - include("Analysis/Analysis.jl") - @reexport using .Analysis +include("Analysis/Analysis.jl") +@reexport using .Analysis end diff --git a/src/Visualize/Visualize.jl b/src/Visualize/Visualize.jl index 17bf7c30..1a3b833c 100644 --- a/src/Visualize/Visualize.jl +++ b/src/Visualize/Visualize.jl @@ -1,25 +1,44 @@ module Visualize - using LaTeXStrings: latexstring - using Plots.PlotMeasures - using Plots: plot, plot!, savefig - using StartUpDG: RefElemData, MeshData, AbstractElemShape, Line, Tri, Quad, Tet, Hex, map_face_nodes, find_face_nodes, nodes, vandermonde - using WriteVTK - using LinearMaps: LinearMap - using LinearAlgebra: diagm - using RecipesBase - using Triangulate - using TetGen +using LaTeXStrings: latexstring +using Plots.PlotMeasures +using Plots: plot, plot!, savefig +using StartUpDG: + RefElemData, + MeshData, + AbstractElemShape, + Line, + Tri, + Quad, + Tet, + Hex, + map_face_nodes, + find_face_nodes, + nodes, + vandermonde +using WriteVTK +using LinearMaps: LinearMap +using LinearAlgebra: diagm +using RecipesBase +using Triangulate +using TetGen - using ..SpatialDiscretizations: SpatialDiscretization, ReferenceApproximation, AbstractApproximationType, NodalMulti, ModalMulti, NodalTensor, ModalTensor - using ..GridFunctions: AbstractGridFunction, evaluate - using ..File: new_path +using ..SpatialDiscretizations: + SpatialDiscretization, + ReferenceApproximation, + AbstractApproximationType, + NodalMulti, + ModalMulti, + NodalTensor, + ModalTensor +using ..GridFunctions: AbstractGridFunction, evaluate +using ..File: new_path - export visualize, Plotter - include("plot_1d.jl") +export visualize, Plotter +include("plot_1d.jl") - export low_order_subdivision, postprocess_vtk, outline_element, plot_ref_elem - include("plot_2d.jl") - include("plot_3d.jl") -end \ No newline at end of file +export low_order_subdivision, postprocess_vtk, outline_element, plot_ref_elem +include("plot_2d.jl") +include("plot_3d.jl") +end diff --git a/src/Visualize/plot_1d.jl b/src/Visualize/plot_1d.jl index 6fae42a5..bb3ed793 100644 --- a/src/Visualize/plot_1d.jl +++ b/src/Visualize/plot_1d.jl @@ -1,60 +1,62 @@ struct Plotter{d} - x_plot::NTuple{d,Matrix{Float64}} + x_plot::NTuple{d, Matrix{Float64}} V_plot::LinearMap N_e::Int directory_name::String end -function Plotter(spatial_discretization::SpatialDiscretization{d},directory_name::String) where {d} +function Plotter(spatial_discretization::SpatialDiscretization{d}, + directory_name::String) where {d} path = new_path(directory_name, true, false) - return Plotter{d}(spatial_discretization.x_plot, - spatial_discretization.reference_approximation.V_plot, - spatial_discretization.N_e, path) + return Plotter{d}(spatial_discretization.x_plot, + spatial_discretization.reference_approximation.V_plot, + spatial_discretization.N_e, + path) end -@recipe function plot( - spatial_discretization::SpatialDiscretization{1}, - sol::Array{Float64,3}; e=1, - exact_solution=nothing, time=0.0) - +@recipe function plot(spatial_discretization::SpatialDiscretization{1}, + sol::Array{Float64, 3}; + e = 1, + exact_solution = nothing, + time = 0.0,) (; x_plot, N_e, reference_approximation) = spatial_discretization xlabel --> "\$x\$" label --> ["\$U^h(x,t)\$" "\$U(x,t)\$"] @series begin - vec(vcat(x_plot[1],fill(NaN,1,N_e))), vec( - vcat(convert(Matrix, - reference_approximation.V_plot * sol[:,e,:]),fill(NaN,1,N_e))) + vec(vcat(x_plot[1], fill(NaN, 1, N_e))), + vec(vcat(convert(Matrix, reference_approximation.V_plot * sol[:, e, :]), + fill(NaN, 1, N_e))) end if !isnothing(exact_solution) @series begin - vec(x_plot[1]), vec(evaluate(exact_solution,x_plot,time)[:,e,:]) + vec(x_plot[1]), vec(evaluate(exact_solution, x_plot, time)[:, e, :]) end end end @recipe function plot(spatial_discretization::SpatialDiscretization{1}, - sol::Vector{Array{Float64,3}}; e=1, - exact_solution=nothing, t=0.0) - + sol::Vector{Array{Float64, 3}}; + e = 1, + exact_solution = nothing, + t = 0.0,) (; x_plot, N_e, reference_approximation) = spatial_discretization xlabel --> "\$x\$" label --> "" for k in eachindex(sol) @series begin - vec(vcat(x_plot[1],fill(NaN,1,N_e))), vec( - vcat(convert(Matrix, - reference_approximation.V_plot * sol[k][:,e,:]), - fill(NaN,1,N_e))) + vec(vcat(x_plot[1], fill(NaN, 1, N_e))), + vec(vcat(convert(Matrix, reference_approximation.V_plot * sol[k][:, e, :]), + fill(NaN, 1, N_e))) end end if !isnothing(exact_solution) @series begin - vec(x_plot[1]), vec(evaluate(exact_solution,x_plot,t)[:,e,:]) + vec(x_plot[1]), vec(evaluate(exact_solution, x_plot, t)[:, e, :]) end end -end \ No newline at end of file +end diff --git a/src/Visualize/plot_2d.jl b/src/Visualize/plot_2d.jl index 6bfd703d..454d9144 100644 --- a/src/Visualize/plot_2d.jl +++ b/src/Visualize/plot_2d.jl @@ -3,86 +3,86 @@ Subdivide to low-order mesh for plotting (thanks to Yimin Lin for sharing this trick) https://github.com/yiminllin/ESDG-PosLimit """ -function low_order_subdivision(reference_nodes::NTuple{2,Vector{Float64}}, - physical_nodes::NTuple{2,Matrix{Float64}}) - +function low_order_subdivision(reference_nodes::NTuple{2, Vector{Float64}}, + physical_nodes::NTuple{2, Matrix{Float64}}) tri_in = Triangulate.TriangulateIO() tri_in.pointlist = permutedims(hcat(reference_nodes...)) tri_out, _ = Triangulate.triangulate("Q", tri_in) connectivity = permutedims(tri_out.trianglelist) points = permutedims(hcat(vec(physical_nodes[1]), vec(physical_nodes[2]))) - N_sub = size(connectivity,1) - (N_p,N_e) = size(physical_nodes[1]) + N_sub = size(connectivity, 1) + (N_p, N_e) = size(physical_nodes[1]) - cells = [MeshCell(VTKCellTypes.VTK_TRIANGLE, - connectivity[mod1(i,N_sub),:] .+ N_p*div(i-1,N_sub)) - for i in 1:N_sub*N_e] + cells = [MeshCell(VTKCellTypes.VTK_TRIANGLE, + connectivity[mod1(i, N_sub), :] .+ N_p * div(i - 1, N_sub)) + for i in 1:(N_sub * N_e)] return points, cells end -function postprocess_vtk( - spatial_discretization::SpatialDiscretization{2}, - filename::String, u::Array{Float64,3}; e=1, variable_name="u") - +function postprocess_vtk(spatial_discretization::SpatialDiscretization{2}, + filename::String, + u::Array{Float64, 3}; + e = 1, + variable_name = "u",) (; V_plot, reference_element) = spatial_discretization.reference_approximation (; x_plot) = spatial_discretization (; rstp) = reference_element points, cells = low_order_subdivision(rstp, x_plot) - u_nodal = vec(Matrix(V_plot * u[:,e,:])) + u_nodal = vec(Matrix(V_plot * u[:, e, :])) vtk_grid(filename, points, cells) do vtk vtk[variable_name] = u_nodal end end -function postprocess_vtk_high_order( - spatial_discretization::SpatialDiscretization{2}, - filename::String, u::Array{Float64,3}; e=1, variable_name="u") - +function postprocess_vtk_high_order(spatial_discretization::SpatialDiscretization{2}, + filename::String, + u::Array{Float64, 3}; + e = 1, + variable_name = "u",) (; V_plot, reference_element) = spatial_discretization.reference_approximation (; x_plot, N_e) = spatial_discretization (; rstp) = reference_element points = permutedims(hcat(vec(x_plot[1]), vec(x_plot[2]))) - N_plot = size(V_plot,1) - - cells = [MeshCell(VTKCellTypes.VTK_LAGRANGE_TRIANGLE, - collect((k-1)*N_plot+1:k*N_plot)) for k in 1:N_e] + N_plot = size(V_plot, 1) - u_nodal = vec(Matrix(V_plot * u[:,e,:])) + cells = [MeshCell(VTKCellTypes.VTK_LAGRANGE_TRIANGLE, + collect(((k - 1) * N_plot + 1):(k * N_plot))) + for k in 1:N_e] + + u_nodal = vec(Matrix(V_plot * u[:, e, :])) vtk_grid(filename, points, cells) do vtk vtk[variable_name] = u_nodal end end -@recipe function plot( - obj::Union{SpatialDiscretization{2}, - ReferenceApproximation{<:RefElemData{2}}}; - volume_quadrature=true, - facet_quadrature=true, - mapping_nodes=false, - grid_connect=false, - volume_quadrature_connect = false, - mapping_nodes_connect = nothing, - sketch=false, - stride = nothing, - elems = nothing, - node_color = 1, - facet_node_color=2, - mapping_node_color=3, - grid_line_width = 2.0, - edge_line_width = 3.0) - +@recipe function plot(obj::Union{SpatialDiscretization{2}, + ReferenceApproximation{<:RefElemData{2}}}; + volume_quadrature = true, + facet_quadrature = true, + mapping_nodes = false, + grid_connect = false, + volume_quadrature_connect = false, + mapping_nodes_connect = nothing, + sketch = false, + stride = nothing, + elems = nothing, + node_color = 1, + facet_node_color = 2, + mapping_node_color = 3, + grid_line_width = 2.0, + edge_line_width = 3.0,) aspect_ratio --> 1.0 legend --> false grid --> false xlabelfontsize --> 15 ylabelfontsize --> 15 - windowsize --> (400,400) + windowsize --> (400, 400) if obj isa SpatialDiscretization (; N_e) = obj @@ -112,60 +112,61 @@ end for k in elems if obj isa SpatialDiscretization - X = function(ξ1,ξ2) + X = function (ξ1, ξ2) V = vandermonde(reference_element.element_type, - reference_element.N,ξ1,ξ2) / reference_element.VDM - return (sum(mesh.x[j,k]*V[:,j] for j in axes(mesh.x,1)), - sum(mesh.y[j,k]*V[:,j] for j in axes(mesh.y,1))) + reference_element.N, + ξ1, + ξ2) / reference_element.VDM + return (sum(mesh.x[j, k] * V[:, j] for j in axes(mesh.x, 1)), + sum(mesh.y[j, k] * V[:, j] for j in axes(mesh.y, 1))) end else - X = (x,y) -> (x,y) + X = (x, y) -> (x, y) end (; element_type, r, s, rq, sq, rf, sf) = reference_approximation.reference_element if element_type isa Tri - ref_edge_nodes = map_face_nodes(element_type, - collect(LinRange(-1.0,1.0, 40))) + ref_edge_nodes = map_face_nodes(element_type, collect(LinRange(-1.0, 1.0, 40))) edges = find_face_nodes(element_type, ref_edge_nodes...) - - for edge ∈ edges + + for edge in edges @series begin linewidth --> edge_line_width linecolor --> :black - X(ref_edge_nodes[1][edge][1:end-1], ref_edge_nodes[2][edge][1:end-1]) + X(ref_edge_nodes[1][edge][1:(end - 1)], + ref_edge_nodes[2][edge][1:(end - 1)]) end end elseif element_type isa Quad N = 40 - range = collect(LinRange(-1.0,1.0, N)) + range = collect(LinRange(-1.0, 1.0, N)) @series begin linewidth --> edge_line_width linecolor --> :black - X(fill(-1.0,N),range) + X(fill(-1.0, N), range) end @series begin linewidth --> edge_line_width linecolor --> :black - X(fill(1.0,N),range) + X(fill(1.0, N), range) end @series begin linewidth --> edge_line_width linecolor --> :black - X(range,fill(-1.0,N)) + X(range, fill(-1.0, N)) end @series begin linewidth --> edge_line_width linecolor --> :black - X(range,fill(1.0,N)) + X(range, fill(1.0, N)) end end if volume_quadrature if grid_connect && - (reference_approximation.approx_type isa Union{NodalTensor, ModalTensor}) - + (reference_approximation.approx_type isa Union{NodalTensor, ModalTensor}) if isnothing(stride) stride = Int(sqrt(reference_approximation.N_q)) end @@ -174,12 +175,11 @@ end N2 = reference_approximation.N_q ÷ stride if element_type isa Tri - for i in 1:N1 @series begin color --> node_color linewidth --> grid_line_width - X(rq[i:N2:(N2*(N1-1) + i)], sq[i:N2:(N2*(N1-1) + i)]) + X(rq[i:N2:(N2 * (N1 - 1) + i)], sq[i:N2:(N2 * (N1 - 1) + i)]) end end @@ -187,17 +187,17 @@ end @series begin color --> node_color linewidth --> grid_line_width - X(rq[(i-1)*N1+1:i*N1], sq[(i-1)*N1+1:i*N1]) + X(rq[((i - 1) * N1 + 1):(i * N1)], + sq[((i - 1) * N1 + 1):(i * N1)]) end end elseif element_type isa Quad - for i in 1:N1 @series begin color --> node_color linewidth --> grid_line_width - X(rq[i:N2:(N2*(N1-1) + i)], sq[i:N2:(N2*(N1-1) + i)]) + X(rq[i:N2:(N2 * (N1 - 1) + i)], sq[i:N2:(N2 * (N1 - 1) + i)]) end end @@ -205,12 +205,13 @@ end @series begin color --> node_color linewidth --> grid_line_width - X(rq[(i-1)*N1+1:i*N1], sq[(i-1)*N1+1:i*N1]) + X(rq[((i - 1) * N1 + 1):(i * N1)], + sq[((i - 1) * N1 + 1):(i * N1)]) end end end else - @series begin + @series begin seriestype --> :scatter markerstrokewidth --> 0.0 markersize --> 5 @@ -218,14 +219,15 @@ end X(rq, sq) end if volume_quadrature_connect - j = argmin([sqrt((rq[i] + 1.0/3.0)^2 + (sq[i] + 1.0/3.0)^2) - for i in 1:reference_approximation.N_q]) - for i in 1:reference_approximation.N_q - @series begin + j = argmin([sqrt((rq[i] + 1.0 / 3.0)^2 + (sq[i] + 1.0 / 3.0)^2) + for + i in 1:(reference_approximation.N_q)]) + for i in 1:(reference_approximation.N_q) + @series begin linewidth --> grid_line_width linecolor --> node_color - x,y = [rq[j], rq[i]], [sq[j],sq[i]] - X(x,y) + x, y = [rq[j], rq[i]], [sq[j], sq[i]] + X(x, y) end end end @@ -233,7 +235,7 @@ end end if facet_quadrature - @series begin + @series begin seriestype --> :scatter markershape --> :circle markercolor --> facet_node_color @@ -243,7 +245,7 @@ end end end if mapping_nodes - @series begin + @series begin seriestype --> :scatter markerstrokewidth --> 0.0 markersize --> 4 @@ -254,15 +256,15 @@ end if !isnothing(mapping_nodes_connect) N_p = length(r) for i in 1:N_p - @series begin + @series begin linewidth --> grid_line_width linecolor --> node_color - x,y = [r[mapping_nodes_connect], r[i]], [s[mapping_nodes_connect],s[i]] - X(x,y) + x, y = [r[mapping_nodes_connect], r[i]], + [s[mapping_nodes_connect], s[i]] + X(x, y) end end end end - end -end \ No newline at end of file +end diff --git a/src/Visualize/plot_3d.jl b/src/Visualize/plot_3d.jl index 48a6a5a6..a14d4471 100644 --- a/src/Visualize/plot_3d.jl +++ b/src/Visualize/plot_3d.jl @@ -1,80 +1,78 @@ -function low_order_subdivision(reference_nodes::NTuple{3,Vector{Float64}}, - physical_nodes::NTuple{3,Matrix{Float64}}) - - tet_in=TetGen.RawTetGenIO{Cdouble}( - pointlist=permutedims(hcat(reference_nodes[1], - reference_nodes[2], reference_nodes[3]))) - tet_out = tetrahedralize(tet_in,"Q") +function low_order_subdivision(reference_nodes::NTuple{3, Vector{Float64}}, + physical_nodes::NTuple{3, Matrix{Float64}}) + tet_in = TetGen.RawTetGenIO{Cdouble}(pointlist = permutedims(hcat(reference_nodes[1], + reference_nodes[2], + reference_nodes[3]))) + tet_out = tetrahedralize(tet_in, "Q") connectivity = permutedims(tet_out.tetrahedronlist) - points = permutedims(hcat(vec(physical_nodes[1]), - vec(physical_nodes[2]), - vec(physical_nodes[3]))) - N_sub = size(connectivity,1) - (N_p,N_e) = size(physical_nodes[1]) + points = permutedims(hcat(vec(physical_nodes[1]), vec(physical_nodes[2]), + vec(physical_nodes[3]))) + N_sub = size(connectivity, 1) + (N_p, N_e) = size(physical_nodes[1]) - cells = [MeshCell(VTKCellTypes.VTK_TETRA, - connectivity[mod1(i,N_sub),:] .+ N_p*div(i-1,N_sub)) - for i in 1:N_sub*N_e] + cells = [MeshCell(VTKCellTypes.VTK_TETRA, + connectivity[mod1(i, N_sub), :] .+ N_p * div(i - 1, N_sub)) + for i in 1:(N_sub * N_e)] - return points, cells + return points, cells end -function low_order_subdivision(p_vis::Int, p_map::Int, - reference_element::RefElemData{3}, - mesh::MeshData{3}, res=0.01) - - (; rq,sq,tq, wq, VDM, element_type) = reference_element - (; x,y,z) = mesh +function low_order_subdivision(p_vis::Int, + p_map::Int, + reference_element::RefElemData{3}, + mesh::MeshData{3}, + res = 0.01) + (; rq, sq, tq, wq, VDM, element_type) = reference_element + (; x, y, z) = mesh - r1,s1,t1 = nodes(element_type,1) + r1, s1, t1 = nodes(element_type, 1) facet_list = hcat(find_face_nodes(element_type, r1, s1, t1)...) - tet_in=TetGen.RawTetGenIO{Cdouble}( - pointlist=permutedims(hcat(r1,s1,t1))) + tet_in = TetGen.RawTetGenIO{Cdouble}(pointlist = permutedims(hcat(r1, s1, t1))) TetGen.facetlist!(tet_in, facet_list) - params = string("Qpq1.1a",res) - tet_out = tetrahedralize(tet_in,params) + params = string("Qpq1.1a", res) + tet_out = tetrahedralize(tet_in, params) connectivity = permutedims(tet_out.tetrahedronlist) - N_sub = size(connectivity,1) - rp = tet_out.pointlist[1,:] - sp = tet_out.pointlist[2,:] - tp = tet_out.pointlist[3,:] + N_sub = size(connectivity, 1) + rp = tet_out.pointlist[1, :] + sp = tet_out.pointlist[2, :] + tp = tet_out.pointlist[3, :] V_map_to_plot = vandermonde(element_type, p_map, rp, sp, tp) / VDM Vp = vandermonde(element_type, p_vis, rp, sp, tp) Vq = vandermonde(element_type, p_vis, rq, sq, tq) V_quad_to_plot = Vp * inv(Vq' * diagm(wq) * Vq) * Vq' * diagm(wq) - + xp, yp, zp = (x -> V_map_to_plot * x).((x, y, z)) - points = permutedims(hcat(vec(xp), - vec(yp), - vec(zp))) - (N_p,N_e) = size(xp) + points = permutedims(hcat(vec(xp), vec(yp), vec(zp))) + (N_p, N_e) = size(xp) - cells = [MeshCell(VTKCellTypes.VTK_TETRA, - connectivity[mod1(i,N_sub),:] .+ N_p*div(i-1,N_sub)) - for i in 1:N_sub*N_e] + cells = [MeshCell(VTKCellTypes.VTK_TETRA, + connectivity[mod1(i, N_sub), :] .+ N_p * div(i - 1, N_sub)) + for i in 1:(N_sub * N_e)] - return points, cells, V_quad_to_plot + return points, cells, V_quad_to_plot end -function postprocess_vtk( - spatial_discretization::SpatialDiscretization{3}, - filename::String, u::Array{Float64,3}; e=1, p_vis=nothing, p_map=nothing, - variable_name="u") - +function postprocess_vtk(spatial_discretization::SpatialDiscretization{3}, + filename::String, + u::Array{Float64, 3}; + e = 1, + p_vis = nothing, + p_map = nothing, + variable_name = "u",) (; reference_element, V, V_plot) = spatial_discretization.reference_approximation (; mesh, x_plot) = spatial_discretization (; rstp) = reference_element if isnothing(p_vis) || isnothing(p_map) points, cells = low_order_subdivision(rstp, x_plot) - u_nodal = vec(Matrix(V_plot * u[:,e,:])) + u_nodal = vec(Matrix(V_plot * u[:, e, :])) else - points, cells, V_quad_to_plot = low_order_subdivision( - p_vis, p_map, reference_element,mesh) - u_nodal = vec(Matrix(V_quad_to_plot * V * u[:,e,:])) + points, cells, V_quad_to_plot = low_order_subdivision(p_vis, p_map, + reference_element, mesh) + u_nodal = vec(Matrix(V_quad_to_plot * V * u[:, e, :])) end vtk_grid(filename, points, cells) do vtk @@ -82,30 +80,28 @@ function postprocess_vtk( end end -@recipe function plot( - obj::Union{SpatialDiscretization{3}, - ReferenceApproximation{<:RefElemData{3}}}; - volume_quadrature=true, - facet_quadrature=true, - mapping_nodes=false, - edges=true, - redraw_edge=true, - sketch=false, - volume_connect=false, - facet_connect=false, - node_color = 1, - facet_node_color=2, - mapping_node_color=3, - edge_line_width = 3.0, - grid_line_width = 2.0, - qf=nothing, - q=nothing, - facet_color_inds=nothing, - facet_inds=nothing, - element_inds=nothing, - mark_vertices=false, - outline_facets=false) - +@recipe function plot(obj::Union{SpatialDiscretization{3}, + ReferenceApproximation{<:RefElemData{3}}}; + volume_quadrature = true, + facet_quadrature = true, + mapping_nodes = false, + edges = true, + redraw_edge = true, + sketch = false, + volume_connect = false, + facet_connect = false, + node_color = 1, + facet_node_color = 2, + mapping_node_color = 3, + edge_line_width = 3.0, + grid_line_width = 2.0, + qf = nothing, + q = nothing, + facet_color_inds = nothing, + facet_inds = nothing, + element_inds = nothing, + mark_vertices = false, + outline_facets = false,) aspect_ratio --> :equal legend --> false grid --> false @@ -114,9 +110,8 @@ end zlabelfontsize --> 15 left_margin --> -10mm - top_margin --> -20mm, - bottom_margin --> -10mm - windowsize --> (400,400) + top_margin --> -20mm, bottom_margin --> -10mm + windowsize --> (400, 400) if sketch xlabel --> "" @@ -145,117 +140,121 @@ end for k in 1:N_e if element_inds isa Vector{Int} - if !(k in element_inds) continue end + if !(k in element_inds) + continue + end end if obj isa SpatialDiscretization - X = function(ξ1,ξ2,ξ3) + X = function (ξ1, ξ2, ξ3) V = vandermonde(reference_element.element_type, - reference_element.N,ξ1,ξ2,ξ3) / reference_element.VDM - return (sum(mesh.x[j,k]*V[:,j] for j in axes(mesh.x,1)), - sum(mesh.y[j,k]*V[:,j] for j in axes(mesh.y,1)), - sum(mesh.z[j,k]*V[:,j] for j in axes(mesh.z,1))) + reference_element.N, + ξ1, + ξ2, + ξ3) / reference_element.VDM + return (sum(mesh.x[j, k] * V[:, j] for j in axes(mesh.x, 1)), + sum(mesh.y[j, k] * V[:, j] for j in axes(mesh.y, 1)), + sum(mesh.z[j, k] * V[:, j] for j in axes(mesh.z, 1))) end else - X = (x,y,z) -> (x,y,z) + X = (x, y, z) -> (x, y, z) end (; element_type, r, s, t, rq, sq, tq, rf, sf, tf) = reference_approximation.reference_element if edges && (element_type isa Tet) - up = collect(LinRange(-1.0,1.0, 40)) + up = collect(LinRange(-1.0, 1.0, 40)) down = up[end:-1:1] e = ones(40) @series begin linewidth --> edge_line_width linecolor --> :black - X([up; down; -e],[-e; -e; -e], [-e; up; down]) + X([up; down; -e], [-e; -e; -e], [-e; up; down]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([up; down; -e],[-e; up; down], [down; -e; up]) + X([up; down; -e], [-e; up; down], [down; -e; up]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([-e; -e; -e],[up; down; -e], [-e; up; down]) + X([-e; -e; -e], [up; down; -e], [-e; up; down]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([up; down; -e],[-e; up; down], [-e; -e; -e]) + X([up; down; -e], [-e; up; down], [-e; -e; -e]) end if outline_facets @series begin - linewidth --> edge_line_width*1.25 + linewidth --> edge_line_width * 1.25 linecolor --> 1 - X(0.97*[up; down; -e],[-e; -e; -e], 0.97*[-e; up; down]) + X(0.97 * [up; down; -e], [-e; -e; -e], 0.97 * [-e; up; down]) end @series begin - linewidth --> edge_line_width*1.25 + linewidth --> edge_line_width * 1.25 linecolor --> 3 - X([-e; -e; -e],0.97*[up; down; -e],0.97*[-e; up; down]) + X([-e; -e; -e], 0.97 * [up; down; -e], 0.97 * [-e; up; down]) end @series begin - linewidth --> edge_line_width*1.25 + linewidth --> edge_line_width * 1.25 linecolor --> 4 - X(0.97*[up; down; -e],0.97*[-e; up; down],[-e; -e; -e]) + X(0.97 * [up; down; -e], 0.97 * [-e; up; down], [-e; -e; -e]) end @series begin - linewidth --> edge_line_width*1.25 + linewidth --> edge_line_width * 1.25 linecolor --> 2 - X(0.97*[up; down; -e],0.97*[-e; up; down],0.97*[down; -e; up]) + X(0.97 * [up; down; -e], 0.97 * [-e; up; down], 0.97 * [down; -e; up]) end - end if mark_vertices @series begin - seriestype--> :scatter + seriestype --> :scatter markersize --> 5 markerstrokewidth --> 0.0 color --> :red markershape --> :utriangle - X([-1.0],[-1.0],[1.0]) + X([-1.0], [-1.0], [1.0]) end @series begin - seriestype--> :scatter + seriestype --> :scatter markersize --> 5 markerstrokewidth --> 0.0 color --> :green markershape --> :utriangle - X([-1.0],[1.0],[-1.0]) + X([-1.0], [1.0], [-1.0]) end end elseif edges && (element_type isa Hex) - up = collect(LinRange(-1.0,1.0, 40)) + up = collect(LinRange(-1.0, 1.0, 40)) down = up[end:-1:1] e = ones(40) @series begin linewidth --> edge_line_width linecolor --> :black - X([-e; -e; -e; -e],[up; e; down; -e], [-e; up; e; down]) + X([-e; -e; -e; -e], [up; e; down; -e], [-e; up; e; down]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([e; e; e; e],[up; e; down; -e], [-e; up; e; down]) + X([e; e; e; e], [up; e; down; -e], [-e; up; e; down]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([up; e; down; -e],[-e; -e; -e; -e], [-e; up; e; down]) + X([up; e; down; -e], [-e; -e; -e; -e], [-e; up; e; down]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([up; e; down; -e;],[e; e; e; e],[-e; up; e; down]) + X([up; e; down; -e], [e; e; e; e], [-e; up; e; down]) end @series begin linewidth --> edge_line_width @@ -265,12 +264,11 @@ end @series begin linewidth --> edge_line_width linecolor --> :black - X([up; e; down; -e],[-e; up; e; down],[e; e; e; e]) + X([up; e; down; -e], [-e; up; e; down], [e; e; e; e]) end end if facet_quadrature - if facet_connect if element_type isa Tet nodes_per_facet = reference_approximation.N_f ÷ 4 @@ -279,12 +277,12 @@ end end if isnothing(qf) - (N1,N2) = (round(Int,sqrt(nodes_per_facet)), - round(Int,sqrt(nodes_per_facet))) + (N1, N2) = (round(Int, sqrt(nodes_per_facet)), + round(Int, sqrt(nodes_per_facet))) else - (N1,N2) = qf + (N1, N2) = qf end - + if isnothing(facet_color_inds) facet_color_inds = facet_inds end @@ -293,23 +291,25 @@ end @series begin color --> facet_color_inds[z] linewidth --> grid_line_width - start = i + nodes_per_facet*(facet_inds[z]-1) - X(rf[start:N2:(N2*(N1-1) + start)], - sf[start:N2:(N2*(N1-1) + start)], - tf[start:N2:(N2*(N1-1) + start)]) + start = i + nodes_per_facet * (facet_inds[z] - 1) + X(rf[start:N2:(N2 * (N1 - 1) + start)], + sf[start:N2:(N2 * (N1 - 1) + start)], + tf[start:N2:(N2 * (N1 - 1) + start)]) end end - + for i in 1:N2 @series begin color --> facet_color_inds[z] linewidth --> grid_line_width - X(rf[(i-1)*N1+1+nodes_per_facet*(facet_inds[z]-1):i*N1+ nodes_per_facet*(facet_inds[z]-1)], sf[(i-1)*N1+1+nodes_per_facet*(facet_inds[z]-1):i*N1+ nodes_per_facet*(facet_inds[z]-1)], tf[(i-1)*N1+1+nodes_per_facet*(facet_inds[z]-1):i*N1+ nodes_per_facet*(facet_inds[z]-1)]) + X(rf[((i - 1) * N1 + 1 + nodes_per_facet * (facet_inds[z] - 1)):(i * N1 + nodes_per_facet * (facet_inds[z] - 1))], + sf[((i - 1) * N1 + 1 + nodes_per_facet * (facet_inds[z] - 1)):(i * N1 + nodes_per_facet * (facet_inds[z] - 1))], + tf[((i - 1) * N1 + 1 + nodes_per_facet * (facet_inds[z] - 1)):(i * N1 + nodes_per_facet * (facet_inds[z] - 1))]) end end end else - @series begin + @series begin seriestype --> :scatter markershape --> :square markercolor --> facet_node_color @@ -322,11 +322,10 @@ end if volume_quadrature if volume_connect - if isnothing(q) - q = (round(Int,reference_approximation.N_q^(1/3)), - round(Int,reference_approximation.N_q^(1/3)), - round(Int,reference_approximation.N_q^(1/3))) + q = (round(Int, reference_approximation.N_q^(1 / 3)), + round(Int, reference_approximation.N_q^(1 / 3)), + round(Int, reference_approximation.N_q^(1 / 3))) end (N1, N2, N3) = q @@ -335,21 +334,21 @@ end @series begin color --> l + 2 linewidth --> grid_line_width - line = [(i-1)*N2*N3 + (j-1)*N3 + l for i in 1:N1] - X(rq[line], sq[line],tq[line]) + line = [(i - 1) * N2 * N3 + (j - 1) * N3 + l for i in 1:N1] + X(rq[line], sq[line], tq[line]) end end for i in 1:N1 @series begin - color --> l + 2 + color --> l + 2 linewidth --> grid_line_width - line = [(i-1)*N2*N3 + (j-1)*N3 + l for j in 1:N1] - X(rq[line], sq[line],tq[line]) + line = [(i - 1) * N2 * N3 + (j - 1) * N3 + l for j in 1:N1] + X(rq[line], sq[line], tq[line]) end end end else - @series begin + @series begin seriestype --> :scatter markerstrokewidth --> 0.0 markersize --> 5 @@ -360,7 +359,7 @@ end end if mapping_nodes - @series begin + @series begin seriestype --> :scatter markerstrokewidth --> 0.0 markersize --> 4 @@ -370,38 +369,56 @@ end end if redraw_edge - up = collect(LinRange(-1.0,1.0, 40)) + up = collect(LinRange(-1.0, 1.0, 40)) down = up[end:-1:1] e = ones(40) @series begin linewidth --> edge_line_width linecolor --> :black - X([up; down; -e],[-e; -e; -e], [-e; up; down]) + X([up; down; -e], [-e; -e; -e], [-e; up; down]) end @series begin linewidth --> edge_line_width linecolor --> :black - X([up; down; -e],[-e; up; down], [down; -e; up]) + X([up; down; -e], [-e; up; down], [down; -e; up]) end - end end - end -function plot_ref_elem( - reference_approximation::ReferenceApproximation{<:RefElemData{3,Tet}, - <:Union{NodalTensor,ModalTensor}}, title::String) +function plot_ref_elem(reference_approximation::ReferenceApproximation{<:RefElemData{3, + Tet}, + <:Union{NodalTensor, + ModalTensor}}, + title::String) (; p) = reference_approximation.approx_type - vol_nodes = plot(reference_approximation, volume_connect=true, - facet_connect=true, facet_quadrature=false, - volume_quadrature=true, mapping_nodes=false, markersize=4, - camera=(115,30), sketch=true, facet_inds=[1,3,4,2], q=(p+1,p+1,p+1), linewidth=3, mapping_node_color=:red) - fac_nodes = plot(reference_approximation, volume_connect=true, - facet_connect=true, facet_quadrature=true, volume_quadrature=false, mapping_nodes=false, markersize=4, camera=(115,30), - sketch=true, facet_inds=[1,3,4,2], q=(p+1,p+1,p+1), linewidth=3) - plt = plot(vol_nodes, fac_nodes, size=(600,300)) + vol_nodes = plot(reference_approximation, + volume_connect = true, + facet_connect = true, + facet_quadrature = false, + volume_quadrature = true, + mapping_nodes = false, + markersize = 4, + camera = (115, 30), + sketch = true, + facet_inds = [1, 3, 4, 2], + q = (p + 1, p + 1, p + 1), + linewidth = 3, + mapping_node_color = :red) + fac_nodes = plot(reference_approximation, + volume_connect = true, + facet_connect = true, + facet_quadrature = true, + volume_quadrature = false, + mapping_nodes = false, + markersize = 4, + camera = (115, 30), + sketch = true, + facet_inds = [1, 3, 4, 2], + q = (p + 1, p + 1, p + 1), + linewidth = 3) + plt = plot(vol_nodes, fac_nodes, size = (600, 300)) savefig(plt, title) run(`pdfcrop $title $title`) -end \ No newline at end of file +end diff --git a/test/advection_3d.jl b/test/advection_3d.jl index 02521ef4..f2d8a3e3 100644 --- a/test/advection_3d.jl +++ b/test/advection_3d.jl @@ -1,54 +1,92 @@ function advection_3d() - a = (1.0,1.0,1.0) # advection velocity + a = (1.0, 1.0, 1.0) # advection velocity L = 1.0 # domain length T = 1.0 # end time - + conservation_law = LinearAdvectionEquation(a) - exact_solution = InitialDataCosine(1.0,(2π/L, 2π/L, 2π/L)); - + exact_solution = InitialDataCosine(1.0, (2π / L, 2π / L, 2π / L)) + M = 2 p = 4 - - reference_approximation = ReferenceApproximation(ModalTensor(p), Tet(), - mapping_degree=4, sum_factorize_vandermonde=false) - - form = StandardForm(mapping_form=SkewSymmetricMapping(), - inviscid_numerical_flux=CentralNumericalFlux()) - - uniform_mesh = uniform_periodic_mesh(reference_approximation, - ((0.0,L),(0.0,L),(0.0,L)), (M,M,M)) - - mesh = warp_mesh(uniform_mesh, reference_approximation, 0.1, L) - - spatial_discretization = SpatialDiscretization(mesh, - reference_approximation, ChanWilcoxMetrics()) - - results_path = save_project(conservation_law, - spatial_discretization, exact_solution, form, (0.0, T), - "results/advection_3d/", overwrite=true, clear=true) + + reference_approximation = ReferenceApproximation( + ModalTensor(p), + Tet(), + mapping_degree = 4, + sum_factorize_vandermonde = false, + ) + + form = StandardForm( + mapping_form = SkewSymmetricMapping(), + inviscid_numerical_flux = CentralNumericalFlux(), + ) + + uniform_mesh = uniform_periodic_mesh( + reference_approximation, + ((0.0, L), (0.0, L), (0.0, L)), + (M, M, M), + ) + + mesh = warp_mesh(uniform_mesh, reference_approximation, 0.1, L) + + spatial_discretization = + SpatialDiscretization(mesh, reference_approximation, ChanWilcoxMetrics()) + + results_path = save_project( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + "results/advection_3d/", + overwrite = true, + clear = true, + ) CFL = 0.1 - h = L/(reference_approximation.N_p * spatial_discretization.N_e)^(1/3) + h = L / (reference_approximation.N_p * spatial_discretization.N_e)^(1 / 3) dt = CFL * h / sqrt(a[1]^2 + a[2]^2 + a[3]^2) - - ode_problem = semidiscretize(conservation_law, spatial_discretization, - exact_solution, form, (0.0, T), ReferenceOperator(), BLASAlgorithm()); - - sol = solve(ode_problem, CarpenterKennedy2N54(), adaptive=false, dt=dt, - save_everystep=false, callback=save_callback(results_path, (0.0,T), - floor(Int, T/(dt*50)))) - - error_analysis = ErrorAnalysis(results_path, conservation_law, - spatial_discretization, JaskowiecSukumarQuadrature(2p+3)) + + ode_problem = semidiscretize( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + ReferenceOperator(), + BLASAlgorithm(), + ) + + sol = solve( + ode_problem, + CarpenterKennedy2N54(), + adaptive = false, + dt = dt, + save_everystep = false, + callback = save_callback(results_path, (0.0, T), floor(Int, T / (dt * 50))), + ) + + error_analysis = ErrorAnalysis( + results_path, + conservation_law, + spatial_discretization, + JaskowiecSukumarQuadrature(2p + 3), + ) error_results = analyze(error_analysis, last(sol.u), exact_solution, T) time_steps = load_time_steps(results_path) - conservation_results = analyze(PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), time_steps) - energy_results = analyze(EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization), time_steps) - - return error_results[1], conservation_results.E[end,1] - conservation_results.E[1,1], maximum(abs.(energy_results.dEdt[:,1])) - -end \ No newline at end of file + conservation_results = analyze( + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization), + time_steps, + ) + energy_results = analyze( + EnergyConservationAnalysis(results_path, conservation_law, spatial_discretization), + time_steps, + ) + + return error_results[1], + conservation_results.E[end, 1] - conservation_results.E[1, 1], + maximum(abs.(energy_results.dEdt[:, 1])) + +end diff --git a/test/burgers_fluxdiff_1d.jl b/test/burgers_fluxdiff_1d.jl index ad54c453..dafc7155 100644 --- a/test/burgers_fluxdiff_1d.jl +++ b/test/burgers_fluxdiff_1d.jl @@ -3,45 +3,60 @@ function burgers_fluxdiff_1d() T = 0.3 # end time for one period conservation_law = InviscidBurgersEquation() - initial_data = InitialDataGassner(π,0.01); + initial_data = InitialDataGassner(π, 0.01) M = 20 p = 7 - - form = FluxDifferencingForm( - inviscid_numerical_flux=EntropyConservativeNumericalFlux()) - reference_approximation = ReferenceApproximation(NodalTensor(p), Line()) - - mesh = uniform_periodic_mesh(reference_approximation, (0.0,L), M) + form = + FluxDifferencingForm(inviscid_numerical_flux = EntropyConservativeNumericalFlux()) - spatial_discretization = SpatialDiscretization(mesh, - reference_approximation); - - results_path = save_project(conservation_law, - spatial_discretization, initial_data, form, (0.0, T), - "results/burgers_1d/", clear=true, overwrite=true); + reference_approximation = ReferenceApproximation(NodalTensor(p), Line()) - ode_problem = semidiscretize(conservation_law, spatial_discretization, - initial_data, form, (0.0, T)) + mesh = uniform_periodic_mesh(reference_approximation, (0.0, L), M) + + spatial_discretization = SpatialDiscretization(mesh, reference_approximation) + + results_path = save_project( + conservation_law, + spatial_discretization, + initial_data, + form, + (0.0, T), + "results/burgers_1d/", + clear = true, + overwrite = true, + ) + + ode_problem = semidiscretize( + conservation_law, + spatial_discretization, + initial_data, + form, + (0.0, T), + ) CFL = 0.1 h = L / (reference_approximation.N_p * spatial_discretization.N_e) dt = CFL * h / 1.0 - solve(ode_problem, CarpenterKennedy2N54(), adaptive=false, dt=dt, - save_everystep=false, callback=save_callback(results_path, (0.0,T), - floor(Int, T/(dt*50)))) - - conservation_analysis = PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization) - conservation_results = analyze(conservation_analysis, - load_time_steps(results_path)) - - energy_analysis = EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization) - energy_results = analyze(energy_analysis, - load_time_steps(results_path)) - - return maximum(abs.(conservation_results.dEdt[:,1])), maximum(abs.(energy_results.dEdt[:,1])) + solve( + ode_problem, + CarpenterKennedy2N54(), + adaptive = false, + dt = dt, + save_everystep = false, + callback = save_callback(results_path, (0.0, T), floor(Int, T / (dt * 50))), + ) + + conservation_analysis = + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization) + conservation_results = analyze(conservation_analysis, load_time_steps(results_path)) + + energy_analysis = + EnergyConservationAnalysis(results_path, conservation_law, spatial_discretization) + energy_results = analyze(energy_analysis, load_time_steps(results_path)) + + return maximum(abs.(conservation_results.dEdt[:, 1])), + maximum(abs.(energy_results.dEdt[:, 1])) end diff --git a/test/euler_1d_gauss.jl b/test/euler_1d_gauss.jl index 6ff9530a..b48071bb 100644 --- a/test/euler_1d_gauss.jl +++ b/test/euler_1d_gauss.jl @@ -1,55 +1,80 @@ function euler_1d_gauss() T = 2.0 L = 2.0 - + conservation_law = EulerEquations{1}(1.4) - function exact_sol(x,t) + function exact_sol(x, t) γ = 1.4 - ρ = 1.0 + 0.2sin(π*x) + ρ = 1.0 + 0.2sin(π * x) v = 1.0 - E = 1.0/(γ-1) + 0.5*ρ - return SVector{3}(ρ, ρ*v, E) + E = 1.0 / (γ - 1) + 0.5 * ρ + return SVector{3}(ρ, ρ * v, E) end p = 5 M = 4 reference_approximation = ReferenceApproximation( - NodalTensor(p), Line(), volume_quadrature_rule=LGQuadrature(p)) - + NodalTensor(p), + Line(), + volume_quadrature_rule = LGQuadrature(p), + ) + mesh = uniform_periodic_mesh(reference_approximation, (0.0, L), M) - spatial_discretization = SpatialDiscretization(mesh, - reference_approximation) + spatial_discretization = SpatialDiscretization(mesh, reference_approximation) - form = FluxDifferencingForm( - inviscid_numerical_flux=EntropyConservativeNumericalFlux()) + form = + FluxDifferencingForm(inviscid_numerical_flux = EntropyConservativeNumericalFlux()) - ode = semidiscretize(conservation_law, spatial_discretization, exact_sol, - form, (0.0, T), ReferenceOperator(), BLASAlgorithm()) + ode = semidiscretize( + conservation_law, + spatial_discretization, + exact_sol, + form, + (0.0, T), + ReferenceOperator(), + BLASAlgorithm(), + ) - results_path = save_project(conservation_law, - spatial_discretization, exact_sol, form, (0.0, T), - "results/euler_1d/", overwrite=true, clear=true) + results_path = save_project( + conservation_law, + spatial_discretization, + exact_sol, + form, + (0.0, T), + "results/euler_1d/", + overwrite = true, + clear = true, + ) - dt=T/1000; + dt = T / 1000 - sol = solve(ode, CarpenterKennedy2N54(), dt=dt, adaptive=false, - save_everystep=false, - callback=save_callback(results_path, (0.0,T), - floor(Int, T/(dt*50)))) + sol = solve( + ode, + CarpenterKennedy2N54(), + dt = dt, + adaptive = false, + save_everystep = false, + callback = save_callback(results_path, (0.0, T), floor(Int, T / (dt * 50))), + ) - error_analysis = ErrorAnalysis(results_path, conservation_law, - spatial_discretization) + error_analysis = ErrorAnalysis(results_path, conservation_law, spatial_discretization) error_results = analyze(error_analysis, last(sol.u), exact_sol, T) time_steps = load_time_steps(results_path) - conservation_results = analyze(PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), time_steps) - entropy_results = analyze(EntropyConservationAnalysis(results_path, - conservation_law, spatial_discretization), time_steps) + conservation_results = analyze( + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization), + time_steps, + ) + entropy_results = analyze( + EntropyConservationAnalysis(results_path, conservation_law, spatial_discretization), + time_steps, + ) - return error_results, conservation_results.E[end,:] - conservation_results.E[1,:], maximum(abs.(entropy_results.dEdt[:,1])) + return error_results, + conservation_results.E[end, :] - conservation_results.E[1, :], + maximum(abs.(entropy_results.dEdt[:, 1])) end diff --git a/test/euler_3d.jl b/test/euler_3d.jl index dad13a9b..f0cafcfd 100644 --- a/test/euler_3d.jl +++ b/test/euler_3d.jl @@ -1,65 +1,90 @@ -function euler_3d(M::Int=2) +function euler_3d(M::Int = 2) L = 2.0 T = L - Ω = ((0.0,L),(0.0,L),(0.0,L)) + Ω = ((0.0, L), (0.0, L), (0.0, L)) conservation_law = EulerEquations{3}(1.4) - exact_solution = EulerPeriodicTest(conservation_law); + exact_solution = EulerPeriodicTest(conservation_law) p = 4 - form = FluxDifferencingForm( - inviscid_numerical_flux=EntropyConservativeNumericalFlux()) + form = + FluxDifferencingForm(inviscid_numerical_flux = EntropyConservativeNumericalFlux()) + + reference_approximation = + ReferenceApproximation(NodalTensor(p), Hex(), mapping_degree = p) + + uniform_mesh = uniform_periodic_mesh(reference_approximation, Ω, (M, M, M)) + + mesh = warp_mesh(uniform_mesh, reference_approximation, ChanWarping(1 / 16, (L, L, L))) + + spatial_discretization = + SpatialDiscretization(mesh, reference_approximation, ConservativeCurlMetrics()) + + results_path = save_project( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + "results/euler_periodic_3d/", + overwrite = true, + clear = true, + ) + + ode = semidiscretize( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + parallelism = Serial(), + ) + + N_t = 25 * M * (p + 1) + sol = solve( + ode, + DP8(), + adaptive = false, + dt = T / N_t, + save_everystep = false, + callback = save_callback(results_path, (0.0, T), floor(Int, N_t / 50)), + ) + + error_analysis = ErrorAnalysis(results_path, conservation_law, spatial_discretization) - reference_approximation = ReferenceApproximation(NodalTensor(p), Hex(), - mapping_degree=p) - - uniform_mesh = uniform_periodic_mesh(reference_approximation, Ω, (M,M,M)) - - mesh = warp_mesh(uniform_mesh, reference_approximation, - ChanWarping(1/16, (L,L,L))) - - spatial_discretization = SpatialDiscretization(mesh, - reference_approximation, ConservativeCurlMetrics()) - - results_path = save_project(conservation_law, - spatial_discretization, exact_solution, form, (0.0, T), - "results/euler_periodic_3d/", overwrite=true, clear=true) - - ode = semidiscretize(conservation_law, spatial_discretization, - exact_solution, form, (0.0, T), parallelism=Serial()) - - N_t = 25*M*(p+1) - sol = solve(ode, DP8(), adaptive=false, dt=T/N_t, save_everystep=false, - callback=save_callback(results_path, (0.0,T), floor(Int, N_t/50))) - - error_analysis = ErrorAnalysis(results_path, conservation_law, - spatial_discretization) - error = analyze(error_analysis, last(sol.u), exact_solution, T) - conservation = analyze(PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), - load_time_steps(results_path), normalize=false) - entropy_analysis = EntropyConservationAnalysis(results_path, - conservation_law, spatial_discretization) + conservation = analyze( + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization), + load_time_steps(results_path), + normalize = false, + ) + entropy_analysis = + EntropyConservationAnalysis(results_path, conservation_law, spatial_discretization) entropy_results = analyze(entropy_analysis, load_time_steps(results_path)) - mass = plot(conservation, ylabel="Mass", 1) - xmom = plot(conservation, ylabel="Momentum (\$x_1\$)", 2) - ymom = plot(conservation, ylabel="Momentum (\$x_2\$)", 3) - zmom = plot(conservation, ylabel="Momentum (\$x_3\$)", 4) - energy = plot(conservation, ylabel="Energy", 5) - entropy = plot(entropy_results, ylabel="Entropy") + mass = plot(conservation, ylabel = "Mass", 1) + xmom = plot(conservation, ylabel = "Momentum (\$x_1\$)", 2) + ymom = plot(conservation, ylabel = "Momentum (\$x_2\$)", 3) + zmom = plot(conservation, ylabel = "Momentum (\$x_3\$)", 4) + energy = plot(conservation, ylabel = "Energy", 5) + entropy = plot(entropy_results, ylabel = "Entropy") - plt = plot(mass, energy, xmom, ymom, zmom, entropy, size=(800,400)) + plt = plot(mass, energy, xmom, ymom, zmom, entropy, size = (800, 400)) savefig(plt, string(results_path, "conservation_metrics.pdf")) for i in eachindex(sol.u) - postprocess_vtk(spatial_discretization, string(results_path, - "solution_",i,".vtu"), sol.u[i], variable_name="Density") + postprocess_vtk( + spatial_discretization, + string(results_path, "solution_", i, ".vtu"), + sol.u[i], + variable_name = "Density", + ) end - return error, conservation.E[end,:] - conservation.E[1,:], maximum(abs.(entropy_results.dEdt[:,1])) -end \ No newline at end of file + return error, + conservation.E[end, :] - conservation.E[1, :], + maximum(abs.(entropy_results.dEdt[:, 1])) +end diff --git a/test/euler_vortex_2d_modal.jl b/test/euler_vortex_2d_modal.jl index e01639f5..74fc8750 100644 --- a/test/euler_vortex_2d_modal.jl +++ b/test/euler_vortex_2d_modal.jl @@ -1,53 +1,73 @@ -function euler_vortex_2d_modal(M::Int=4) +function euler_vortex_2d_modal(M::Int = 4) mach_number = 0.4 angle = 0.0 L = 1.0 - γ=1.4 - T = L/mach_number # end time - strength = sqrt(2/(γ-1)*(1-0.75^(γ-1))) # for central value of ρ=0.75 - + γ = 1.4 + T = L / mach_number # end time + strength = sqrt(2 / (γ - 1) * (1 - 0.75^(γ - 1))) # for central value of ρ=0.75 + conservation_law = EulerEquations{2}(γ) - exact_solution = IsentropicVortex(conservation_law, θ=angle, - Ma=mach_number, β=strength, R=1.0/10.0, x_0=(L/2,L/2)) - + exact_solution = IsentropicVortex( + conservation_law, + θ = angle, + Ma = mach_number, + β = strength, + R = 1.0 / 10.0, + x_0 = (L / 2, L / 2), + ) + p = 3 - form = FluxDifferencingForm( - inviscid_numerical_flux=LaxFriedrichsNumericalFlux()) + form = FluxDifferencingForm(inviscid_numerical_flux = LaxFriedrichsNumericalFlux()) - reference_approximation = ReferenceApproximation(ModalTensor(p), - Tri(), mapping_degree=p) + reference_approximation = + ReferenceApproximation(ModalTensor(p), Tri(), mapping_degree = p) - uniform_mesh = uniform_periodic_mesh(reference_approximation, - ((0.0,L),(0.0,L)), (M,M)) + uniform_mesh = + uniform_periodic_mesh(reference_approximation, ((0.0, L), (0.0, L)), (M, M)) - mesh = warp_mesh(uniform_mesh, reference_approximation, - ChanWarping(1.0/16.0, (L,L))) + mesh = warp_mesh(uniform_mesh, reference_approximation, ChanWarping(1.0 / 16.0, (L, L))) - spatial_discretization = SpatialDiscretization(mesh, - reference_approximation, ChanWilcoxMetrics()) + spatial_discretization = + SpatialDiscretization(mesh, reference_approximation, ChanWilcoxMetrics()) - results_path = save_project(conservation_law, - spatial_discretization, exact_solution, form, (0.0, T), - "results/euler_vortex_2d_modal/", overwrite=true, clear=true); + results_path = save_project( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + "results/euler_vortex_2d_modal/", + overwrite = true, + clear = true, + ) - dt = T/1000 + dt = T / 1000 - ode = semidiscretize(conservation_law, - spatial_discretization, exact_solution, form, (0.0, T), ReferenceOperator()) + ode = semidiscretize( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + ReferenceOperator(), + ) - sol = solve(ode, CarpenterKennedy2N54(), - dt=dt, adaptive=false, save_everystep=false, - callback=save_callback(results_path, (0.0,T), floor(Int, T/(dt*50)))) - - error_analysis = ErrorAnalysis(results_path, conservation_law, - spatial_discretization) + sol = solve( + ode, + CarpenterKennedy2N54(), + dt = dt, + adaptive = false, + save_everystep = false, + callback = save_callback(results_path, (0.0, T), floor(Int, T / (dt * 50))), + ) + + error_analysis = ErrorAnalysis(results_path, conservation_law, spatial_discretization) error_results = analyze(error_analysis, last(sol.u), exact_solution, T) - conservation_analysis = PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization) - conservation_results = analyze(conservation_analysis, - load_time_steps(results_path)) - - return error_results, conservation_results.E[end,:] - conservation_results.E[1,:] -end \ No newline at end of file + conservation_analysis = + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization) + conservation_results = analyze(conservation_analysis, load_time_steps(results_path)) + + return error_results, conservation_results.E[end, :] - conservation_results.E[1, :] +end diff --git a/test/euler_vortex_2d_nodal_diage.jl b/test/euler_vortex_2d_nodal_diage.jl index 1c61d5a4..6c422705 100644 --- a/test/euler_vortex_2d_nodal_diage.jl +++ b/test/euler_vortex_2d_nodal_diage.jl @@ -1,65 +1,94 @@ -function euler_vortex_2d_diage(M::Int=4) +function euler_vortex_2d_diage(M::Int = 4) mach_number = 0.4 angle = 0.0 L = 1.0 - γ=1.4 - T = L/mach_number # end time - strength = sqrt(2/(γ-1)*(1-0.75^(γ-1))) # for central value of ρ=0.75 - + γ = 1.4 + T = L / mach_number # end time + strength = sqrt(2 / (γ - 1) * (1 - 0.75^(γ - 1))) # for central value of ρ=0.75 + conservation_law = EulerEquations{2}(γ) - exact_solution = IsentropicVortex(conservation_law, θ=angle, - Ma=mach_number, β=strength, R=1.0/10.0, x_0=(L/2,L/2)) - + exact_solution = IsentropicVortex( + conservation_law, + θ = angle, + Ma = mach_number, + β = strength, + R = 1.0 / 10.0, + x_0 = (L / 2, L / 2), + ) + p = 3 - form = FluxDifferencingForm( - inviscid_numerical_flux=EntropyConservativeNumericalFlux()) + form = + FluxDifferencingForm(inviscid_numerical_flux = EntropyConservativeNumericalFlux()) - reference_approximation = ReferenceApproximation(NodalMultiDiagE(p), - Tri(), mapping_degree=p) + reference_approximation = + ReferenceApproximation(NodalMultiDiagE(p), Tri(), mapping_degree = p) - uniform_mesh = uniform_periodic_mesh(reference_approximation, - ((0.0,L),(0.0,L)), (M,M)) + uniform_mesh = + uniform_periodic_mesh(reference_approximation, ((0.0, L), (0.0, L)), (M, M)) - mesh = warp_mesh(uniform_mesh, reference_approximation, - ChanWarping(1.0/16.0, (L,L))) + mesh = warp_mesh(uniform_mesh, reference_approximation, ChanWarping(1.0 / 16.0, (L, L))) - spatial_discretization = SpatialDiscretization(mesh, - reference_approximation) + spatial_discretization = SpatialDiscretization(mesh, reference_approximation) - results_path = save_project(conservation_law, - spatial_discretization, exact_solution, form, (0.0, T), - "results/euler_vortex_2d/", overwrite=true, clear=true) + results_path = save_project( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + "results/euler_vortex_2d/", + overwrite = true, + clear = true, + ) mass_solver = DiagonalSolver(spatial_discretization) - ode = semidiscretize(conservation_law, spatial_discretization, - exact_solution, form, (0.0, T), ReferenceOperator(), - mass_matrix_solver=mass_solver); + ode = semidiscretize( + conservation_law, + spatial_discretization, + exact_solution, + form, + (0.0, T), + ReferenceOperator(), + mass_matrix_solver = mass_solver, + ) - dt = T/1000 - sol = solve(ode, CarpenterKennedy2N54(), - dt=dt, adaptive=false, save_everystep=false, - callback=save_callback(results_path, (0.0,T), - floor(Int, T/(dt*50)))) + dt = T / 1000 + sol = solve( + ode, + CarpenterKennedy2N54(), + dt = dt, + adaptive = false, + save_everystep = false, + callback = save_callback(results_path, (0.0, T), floor(Int, T / (dt * 50))), + ) - error_analysis = ErrorAnalysis(results_path, conservation_law, - spatial_discretization) + error_analysis = ErrorAnalysis(results_path, conservation_law, spatial_discretization) error_results = analyze(error_analysis, last(sol.u), exact_solution, T) - conservation_analysis = PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization) - conservation_results = analyze(conservation_analysis, - load_time_steps(results_path)) - entropy_analysis = EntropyConservationAnalysis(results_path, - conservation_law, spatial_discretization, mass_solver) + conservation_analysis = + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization) + conservation_results = analyze(conservation_analysis, load_time_steps(results_path)) + entropy_analysis = EntropyConservationAnalysis( + results_path, + conservation_law, + spatial_discretization, + mass_solver, + ) entropy_results = analyze(entropy_analysis, load_time_steps(results_path)) for i in eachindex(sol.u) - postprocess_vtk(spatial_discretization, string(results_path, - "solution_",i,".vtu"), sol.u[i], variable_name="Density") + postprocess_vtk( + spatial_discretization, + string(results_path, "solution_", i, ".vtu"), + sol.u[i], + variable_name = "Density", + ) end - return error_results, conservation_results.E[end,:] - conservation_results.E[1,:], maximum(abs.(entropy_results.dEdt[:,1])) -end \ No newline at end of file + return error_results, + conservation_results.E[end, :] - conservation_results.E[1, :], + maximum(abs.(entropy_results.dEdt[:, 1])) +end diff --git a/test/runtests.jl b/test/runtests.jl index b2be4952..d107fc39 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -push!(LOAD_PATH,"../") +push!(LOAD_PATH, "../") using Test, StableSpectralElements, OrdinaryDiffEq, TimerOutputs, StaticArrays, Plots include("test_driver.jl") @@ -13,95 +13,132 @@ const tol = 1.0e-10 const p = 4 @testset "Advection-Diffusion 1D ModalMulti" begin (l2, conservation, _) = test_driver( - ReferenceApproximation(ModalMulti(p), Line()), - LinearAdvectionDiffusionEquation(1.0,5.0e-2), - InitialDataSine(1.0,2π), + ReferenceApproximation(ModalMulti(p), Line()), + LinearAdvectionDiffusionEquation(1.0, 5.0e-2), + InitialDataSine(1.0, 2π), StandardForm( - mapping_form=StandardMapping(), - inviscid_numerical_flux=LaxFriedrichsNumericalFlux(), - viscous_numerical_flux=BR1()), - PhysicalOperator(), DefaultOperatorAlgorithm(), - 1.0, 4, 1.0, 1.0/100.0, 0.1, - "test_advection_diffusion_1d_dgmulti") - - @test l2 ≈ 6.988216111882884e-6 atol=tol - @test conservation ≈ 0.0 atol=tol + mapping_form = StandardMapping(), + inviscid_numerical_flux = LaxFriedrichsNumericalFlux(), + viscous_numerical_flux = BR1(), + ), + PhysicalOperator(), + DefaultOperatorAlgorithm(), + 1.0, + 4, + 1.0, + 1.0 / 100.0, + 0.1, + "test_advection_diffusion_1d_dgmulti", + ) + + @test l2 ≈ 6.988216111882884e-6 atol = tol + @test conservation ≈ 0.0 atol = tol end @testset "Advection 2D Energy-Conservative ModalTensor Tri" begin (l2, conservation, energy) = test_driver( - ReferenceApproximation(ModalTensor(p), Tri(),mapping_degree=p), - LinearAdvectionEquation((1.0,1.0)), - InitialDataSine(1.0,(2*π, 2*π)), - StandardForm(mapping_form=SkewSymmetricMapping(), - inviscid_numerical_flux=LaxFriedrichsNumericalFlux(0.0)), - ReferenceOperator(), BLASAlgorithm(), 1.0, 2, 1.0, 1.0/100.0, 0.1, - "test_advection_2d_collapsed_econ") - - @test l2 ≈ 0.2660013939427627 atol=tol - @test conservation ≈ 0.0 atol=tol - @test energy ≈ 0.0 atol=tol + ReferenceApproximation(ModalTensor(p), Tri(), mapping_degree = p), + LinearAdvectionEquation((1.0, 1.0)), + InitialDataSine(1.0, (2 * π, 2 * π)), + StandardForm( + mapping_form = SkewSymmetricMapping(), + inviscid_numerical_flux = LaxFriedrichsNumericalFlux(0.0), + ), + ReferenceOperator(), + BLASAlgorithm(), + 1.0, + 2, + 1.0, + 1.0 / 100.0, + 0.1, + "test_advection_2d_collapsed_econ", + ) + + @test l2 ≈ 0.2660013939427627 atol = tol + @test conservation ≈ 0.0 atol = tol + @test energy ≈ 0.0 atol = tol end @testset "Advection 2D FluxDiff NodalTensor Quad" begin (l2, conservation, _) = test_driver( - ReferenceApproximation(NodalTensor(p), Quad(), mapping_degree=p), - LinearAdvectionEquation((1.0,1.0)), - InitialDataSine(1.0,(2*π, 2*π)), + ReferenceApproximation(NodalTensor(p), Quad(), mapping_degree = p), + LinearAdvectionEquation((1.0, 1.0)), + InitialDataSine(1.0, (2 * π, 2 * π)), FluxDifferencingForm(), - ReferenceOperator(), GenericTensorProductAlgorithm(), - 1.0, 2, 1.0, 1.0/100.0, 0.1, - "test_advection_2d_dgsem_fluxdiff") - - @test l2 ≈ 0.04790536605026519 atol=tol - @test conservation ≈ 0.0 atol=tol + ReferenceOperator(), + GenericTensorProductAlgorithm(), + 1.0, + 2, + 1.0, + 1.0 / 100.0, + 0.1, + "test_advection_2d_dgsem_fluxdiff", + ) + + @test l2 ≈ 0.04790536605026519 atol = tol + @test conservation ≈ 0.0 atol = tol end @testset "Inviscid Burgers NodalTensor FluxDiff 1D" begin (conservation, energy) = burgers_fluxdiff_1d() - @test conservation ≈ 0.0 atol=tol - @test energy ≈ 0.0 atol=tol + @test conservation ≈ 0.0 atol = tol + @test energy ≈ 0.0 atol = tol end @testset "Euler 1D Gauss collocation 1D" begin (l2, conservation, entropy) = euler_1d_gauss() - @test l2 ≈ [3.5808560177567635e-5, 5.2129828619609155e-5, 0.00012637647535378534] atol=tol - @test conservation ≈ [0.0, 0.0, 0.0] atol=tol - @test entropy ≈ 0.0 atol=tol + @test l2 ≈ [3.5808560177567635e-5, 5.2129828619609155e-5, 0.00012637647535378534] atol = + tol + @test conservation ≈ [0.0, 0.0, 0.0] atol = tol + @test entropy ≈ 0.0 atol = tol end @testset "Isentropic Euler vortex FluxDiff NodalMultiDiagE Tri 2D" begin (l2, conservation, entropy) = euler_vortex_2d_diage() - @test l2 ≈ [0.03590766990615721, 0.06742249914084399, - 0.06824665400591982, 0.08389828379848713] atol=tol - @test conservation ≈ [0.0, 0.0, 0.0, 0.0] atol=tol - @test entropy ≈ 0.0 atol=tol + @test l2 ≈ [ + 0.03590766990615721, + 0.06742249914084399, + 0.06824665400591982, + 0.08389828379848713, + ] atol = tol + @test conservation ≈ [0.0, 0.0, 0.0, 0.0] atol = tol + @test entropy ≈ 0.0 atol = tol end @testset "Isentropic Euler vortex FluxDiff ModalTensor Tri 2D LF" begin (l2, conservation) = euler_vortex_2d_modal() - @test l2 ≈ [0.015568197027072704, 0.040539693811761104, 0.04060141777050208, 0.043960971468832745] atol=tol - @test conservation ≈ [0.0, 0.0, 0.0, 0.0] atol=tol + @test l2 ≈ [ + 0.015568197027072704, + 0.040539693811761104, + 0.04060141777050208, + 0.043960971468832745, + ] atol = tol + @test conservation ≈ [0.0, 0.0, 0.0, 0.0] atol = tol end @testset "Advection 3D Energy-Conservative ModalTensor Tet" begin (l2, conservation, energy) = advection_3d() - @test l2 ≈ 0.1876141674772107 atol=tol - @test conservation ≈ 0.0 atol=tol - @test energy ≈ 0.0 atol=tol + @test l2 ≈ 0.1876141674772107 atol = tol + @test conservation ≈ 0.0 atol = tol + @test energy ≈ 0.0 atol = tol end @testset "Euler 3D NodalTensor Hex" begin (l2, conservation, entropy) = euler_3d() - @test l2 ≈ [0.18342164491797003, 0.1834216449179776, - 0.18342164491796725, 0.18342164491796784, 0.2751324673769553] atol=tol - @test conservation ≈ [0.0, 0.0, 0.0, 0.0, 0.0] atol=tol - @test entropy ≈ 0.0 atol=tol + @test l2 ≈ [ + 0.18342164491797003, + 0.1834216449179776, + 0.18342164491796725, + 0.18342164491796784, + 0.2751324673769553, + ] atol = tol + @test conservation ≈ [0.0, 0.0, 0.0, 0.0, 0.0] atol = tol + @test entropy ≈ 0.0 atol = tol -end \ No newline at end of file +end diff --git a/test/test_driver.jl b/test/test_driver.jl index 71da52a1..2f5929d5 100644 --- a/test/test_driver.jl +++ b/test/test_driver.jl @@ -1,18 +1,36 @@ -function test_discretization(L::Float64,M::Int, +function test_discretization( + L::Float64, + M::Int, reference_approximation::ReferenceApproximation{<:RefElemData{1}}, - ::Float64) - - return SpatialDiscretization(uniform_periodic_mesh(reference_approximation, - (0.0,L),M), reference_approximation) + ::Float64, +) + + return SpatialDiscretization( + uniform_periodic_mesh(reference_approximation, (0.0, L), M), + reference_approximation, + ) end -function test_discretization(L::Float64,M::Int, - reference_approximation::ReferenceApproximation{<:RefElemData{d}}, - mesh_perturb::Float64) where {d} +function test_discretization( + L::Float64, + M::Int, + reference_approximation::ReferenceApproximation{<:RefElemData{d}}, + mesh_perturb::Float64, +) where {d} - return SpatialDiscretization(warp_mesh(uniform_periodic_mesh( - reference_approximation, Tuple((0.0,L) for m in 1:d), - Tuple(M for m in 1:d)), reference_approximation, mesh_perturb), reference_approximation, project_jacobian=true) + return SpatialDiscretization( + warp_mesh( + uniform_periodic_mesh( + reference_approximation, + Tuple((0.0, L) for m = 1:d), + Tuple(M for m = 1:d), + ), + reference_approximation, + mesh_perturb, + ), + reference_approximation, + project_jacobian = true, + ) end function test_driver( @@ -27,32 +45,66 @@ function test_driver( T::Float64, dt::Float64, mesh_perturb::Float64, - test_name::String) + test_name::String, +) - spatial_discretization = test_discretization(L,M, - reference_approximation,mesh_perturb) + spatial_discretization = + test_discretization(L, M, reference_approximation, mesh_perturb) - exact_solution = ExactSolution(conservation_law,initial_data) + exact_solution = ExactSolution(conservation_law, initial_data) - results_path = save_project(conservation_law, - spatial_discretization, initial_data, form, (0.0, T), - string("results/", test_name,"/"), overwrite=true, clear=true) + results_path = save_project( + conservation_law, + spatial_discretization, + initial_data, + form, + (0.0, T), + string("results/", test_name, "/"), + overwrite = true, + clear = true, + ) - ode_problem = semidiscretize(conservation_law, - spatial_discretization, initial_data, form, (0.0, T), strategy, alg) + ode_problem = semidiscretize( + conservation_law, + spatial_discretization, + initial_data, + form, + (0.0, T), + strategy, + alg, + ) - sol = solve(ode_problem, CarpenterKennedy2N54(), adaptive=false, dt=dt, - callback=save_callback(results_path, (0.0,T), floor(Int, 1.0/(dt*50)))) + sol = solve( + ode_problem, + CarpenterKennedy2N54(), + adaptive = false, + dt = dt, + callback = save_callback(results_path, (0.0, T), floor(Int, 1.0 / (dt * 50))), + ) - error = analyze(ErrorAnalysis(results_path, conservation_law, - spatial_discretization), last(sol.u), exact_solution, 1.0) - conservation = analyze(PrimaryConservationAnalysis(results_path, - conservation_law, spatial_discretization), - load_time_steps(results_path)) - energy = analyze(EnergyConservationAnalysis(results_path, - conservation_law, spatial_discretization, ode_problem.p.mass_solver), - load_time_steps(results_path)) + error = analyze( + ErrorAnalysis(results_path, conservation_law, spatial_discretization), + last(sol.u), + exact_solution, + 1.0, + ) + conservation = analyze( + PrimaryConservationAnalysis(results_path, conservation_law, spatial_discretization), + load_time_steps(results_path), + ) + energy = analyze( + EnergyConservationAnalysis( + results_path, + conservation_law, + spatial_discretization, + ode_problem.p.mass_solver, + ), + load_time_steps(results_path), + ) - return (error..., maximum(abs.(conservation.dEdt[:,1])), - maximum(abs.(energy.dEdt[:,1]))) -end \ No newline at end of file + return ( + error..., + maximum(abs.(conservation.dEdt[:, 1])), + maximum(abs.(energy.dEdt[:, 1])), + ) +end