Mercurial > repos > public > sbplib_julia
changeset 638:08b2c7a2d063 feature/volume_and_boundary_operators
Implement the Quadrature operator as a VolumeOperator. Make DiagonalQuadrature a special case of the general Quadrature operator. Update tests.
author | Vidar Stiernström <vidar.stiernstrom@it.uu.se> |
---|---|
date | Mon, 04 Jan 2021 09:32:11 +0100 |
parents | 4a81812150f4 |
children | bfb1adb2cd7e |
files | src/SbpOperators/volumeops/quadratures/diagonal_quadrature.jl src/SbpOperators/volumeops/quadratures/quadrature.jl test/testSbpOperators.jl |
diffstat | 3 files changed, 150 insertions(+), 211 deletions(-) [+] |
line wrap: on
line diff
--- a/src/SbpOperators/volumeops/quadratures/diagonal_quadrature.jl Sun Jan 03 18:15:14 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -""" -diagonal_quadrature(g,quadrature_closure) - -Constructs the diagonal quadrature operator `H` on a grid of `Dim` dimensions as -a `TensorMapping`. The one-dimensional operator is a `DiagonalQuadrature`, while -the multi-dimensional operator is the outer-product of the -one-dimensional operators in each coordinate direction. -""" -function diagonal_quadrature(g::EquidistantGrid{Dim}, quadrature_closure) where Dim - H = DiagonalQuadrature(restrict(g,1), quadrature_closure) - for i ∈ 2:Dim - H = H⊗DiagonalQuadrature(restrict(g,i), quadrature_closure) - end - return H -end -export diagonal_quadrature - -""" - DiagonalQuadrature{T,M} <: TensorMapping{T,1,1} - -Implements the one-dimensional diagonal quadrature operator as a `TensorMapping` -The quadrature is defined by the quadrature interval length `h`, the quadrature -closure weights `closure` and the number of quadrature intervals `size`. The -interior stencil has the weight 1. -""" -struct DiagonalQuadrature{T,M} <: TensorMapping{T,1,1} - h::T - closure::NTuple{M,T} - size::Tuple{Int} -end -export DiagonalQuadrature - -""" - DiagonalQuadrature(g, quadrature_closure) - -Constructs the `DiagonalQuadrature` on the `EquidistantGrid` `g` with -closure given by `quadrature_closure`. -""" -function DiagonalQuadrature(g::EquidistantGrid{1}, quadrature_closure) - return DiagonalQuadrature(spacing(g)[1], quadrature_closure, size(g)) -end - -""" - range_size(H::DiagonalQuadrature) - -The size of an object in the range of `H` -""" -LazyTensors.range_size(H::DiagonalQuadrature) = H.size - -""" - domain_size(H::DiagonalQuadrature) - -The size of an object in the domain of `H` -""" -LazyTensors.domain_size(H::DiagonalQuadrature) = H.size - -""" - apply(H::DiagonalQuadrature{T}, v::AbstractVector{T}, i) where T -Implements the application `(H*v)[i]` an `Index{R}` where `R` is one of the regions -`Lower`,`Interior`,`Upper`. If `i` is another type of index (e.g an `Int`) it will first -be converted to an `Index{R}`. -""" -function LazyTensors.apply(H::DiagonalQuadrature{T}, v::AbstractVector{T}, i::Index{Lower}) where T - return @inbounds H.h*H.closure[Int(i)]*v[Int(i)] -end - -function LazyTensors.apply(H::DiagonalQuadrature{T},v::AbstractVector{T}, i::Index{Upper}) where T - N = length(v); #TODO: Use dim_size here? - return @inbounds H.h*H.closure[N-Int(i)+1]*v[Int(i)] -end - -function LazyTensors.apply(H::DiagonalQuadrature{T}, v::AbstractVector{T}, i::Index{Interior}) where T - return @inbounds H.h*v[Int(i)] -end - -function LazyTensors.apply(H::DiagonalQuadrature{T}, v::AbstractVector{T}, i) where T - N = length(v); #TODO: Use dim_size here? - r = getregion(i, closure_size(H), N) - return LazyTensors.apply(H, v, Index(i, r)) -end - -""" - apply(H::DiagonalQuadrature{T}, v::AbstractVector{T}, I::Index) where T -Implements the application (H'*v)[I]. The operator is self-adjoint. -""" -LazyTensors.apply_transpose(H::DiagonalQuadrature{T}, v::AbstractVector{T}, i) where T = LazyTensors.apply(H,v,i) - -""" - closure_size(H) -Returns the size of the closure stencil of a DiagonalQuadrature `H`. -""" -closure_size(H::DiagonalQuadrature{T,M}) where {T,M} = M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/SbpOperators/volumeops/quadratures/quadrature.jl Mon Jan 04 09:32:11 2021 +0100 @@ -0,0 +1,36 @@ +""" + Quadrature(grid::EquidistantGrid, inner_stencil, closure_stencils) + +Creates the quadrature operator `H` as a `TensorMapping` + +The quadrature approximates the integral operator on the grid using +`inner_stencil` in the interior and a set of stencils `closure_stencils` +for the points in the closure regions. + +On a one-dimensional `grid`, `H` is a `VolumeOperator`. On a multi-dimensional +`grid`, `H` is the outer product of the 1-dimensional quadrature operators in +each coordinate direction. Also see the documentation of +`SbpOperators.volume_operator(...)` for more details. +""" +function Quadrature(grid::EquidistantGrid{Dim}, inner_stencil, closure_stencils) where Dim + h = spacing(grid) + H = SbpOperators.volume_operator(grid, scale(inner_stencil,h[1]), scale.(closure_stencils,h[1]), even, 1) + for i ∈ 2:Dim + Hᵢ = SbpOperators.volume_operator(grid, scale(inner_stencil,h[i]), scale.(closure_stencils,h[i]), even, i) + H = H∘Hᵢ + end + return H +end +export Quadrature + +""" + DiagonalQuadrature(grid::EquidistantGrid, closure_stencils) + +Creates the quadrature operator with the inner stencil 1/h and 1-element sized +closure stencils (i.e the operator is diagonal) +""" +function DiagonalQuadrature(grid::EquidistantGrid, closure_stencils::NTuple{M,Stencil{T,1}}) where {M,T} + inner_stencil = Stencil(Tuple{T}(1),center=1) + return Quadrature(grid, inner_stencil, closure_stencils) +end +export DiagonalQuadrature
--- a/test/testSbpOperators.jl Sun Jan 03 18:15:14 2021 +0100 +++ b/test/testSbpOperators.jl Mon Jan 04 09:32:11 2021 +0100 @@ -413,47 +413,51 @@ g_2D = EquidistantGrid((77,66), (0.0, 0.0), (Lx,Ly)) integral(H,v) = sum(H*v) @testset "Constructors" begin - # 1D - H_x = DiagonalQuadrature(spacing(g_1D)[1],op.quadratureClosure,size(g_1D)); - @test H_x == DiagonalQuadrature(g_1D,op.quadratureClosure) - @test H_x == diagonal_quadrature(g_1D,op.quadratureClosure) - @test H_x isa TensorMapping{T,1,1} where T - @test H_x' isa TensorMapping{T,1,1} where T - # 2D - H_xy = diagonal_quadrature(g_2D,op.quadratureClosure) - @test H_xy isa TensorMappingComposition - @test H_xy isa TensorMapping{T,2,2} where T - @test H_xy' isa TensorMapping{T,2,2} where T + @testset "1D" begin + H = DiagonalQuadrature(g_1D,op.quadratureClosure) + inner_stencil = Stencil((spacing(g_1D)[1],),center=1) + H == Quadrature(g_1D,inner_stencil,op.quadratureClosure) + @test H isa TensorMapping{T,1,1} where T + end + @testset "1D" begin + H = DiagonalQuadrature(g_2D,op.quadratureClosure) + H_x = DiagonalQuadrature(restrict(g_2D,1),op.quadratureClosure) + H_y = DiagonalQuadrature(restrict(g_2D,2),op.quadratureClosure) + @test H == H_x⊗H_y + @test H isa TensorMapping{T,2,2} where T + end end @testset "Sizes" begin - # 1D - H_x = diagonal_quadrature(g_1D,op.quadratureClosure) - @test domain_size(H_x) == size(g_1D) - @test range_size(H_x) == size(g_1D) - # 2D - H_xy = diagonal_quadrature(g_2D,op.quadratureClosure) - @test domain_size(H_xy) == size(g_2D) - @test range_size(H_xy) == size(g_2D) + @testset "1D" begin + H = DiagonalQuadrature(g_1D,op.quadratureClosure) + @test domain_size(H) == size(g_1D) + @test range_size(H) == size(g_1D) + end + @testset "2D" begin + H = DiagonalQuadrature(g_2D,op.quadratureClosure) + @test domain_size(H) == size(g_2D) + @test range_size(H) == size(g_2D) + end end @testset "Application" begin - # 1D - H_x = diagonal_quadrature(g_1D,op.quadratureClosure) - a = 3.2 - v_1D = a*ones(Float64, size(g_1D)) - u_1D = evalOn(g_1D,x->sin(x)) - @test integral(H_x,v_1D) ≈ a*Lx rtol = 1e-13 - @test integral(H_x,u_1D) ≈ 1. rtol = 1e-8 - @test H_x*v_1D == H_x'*v_1D - # 2D - H_xy = diagonal_quadrature(g_2D,op.quadratureClosure) - b = 2.1 - v_2D = b*ones(Float64, size(g_2D)) - u_2D = evalOn(g_2D,(x,y)->sin(x)+cos(y)) - @test integral(H_xy,v_2D) ≈ b*Lx*Ly rtol = 1e-13 - @test integral(H_xy,u_2D) ≈ π rtol = 1e-8 - @test H_xy*v_2D ≈ H_xy'*v_2D rtol = 1e-16 #Failed for exact equality. Must differ in operation order for some reason? + @testset "1D" begin + H = DiagonalQuadrature(g_1D,op.quadratureClosure) + a = 3.2 + v_1D = a*ones(Float64, size(g_1D)) + u_1D = evalOn(g_1D,x->sin(x)) + @test integral(H,v_1D) ≈ a*Lx rtol = 1e-13 + @test integral(H,u_1D) ≈ 1. rtol = 1e-8 + end + @testset "1D" begin + H = DiagonalQuadrature(g_2D,op.quadratureClosure) + b = 2.1 + v_2D = b*ones(Float64, size(g_2D)) + u_2D = evalOn(g_2D,(x,y)->sin(x)+cos(y)) + @test integral(H,v_2D) ≈ b*Lx*Ly rtol = 1e-13 + @test integral(H,u_2D) ≈ π rtol = 1e-8 + end end @testset "Accuracy" begin @@ -462,96 +466,87 @@ f_i(x) = 1/factorial(i)*x^i v = (v...,evalOn(g_1D,f_i)) end - # TODO: Bug in readOperator for 2nd order - # # 2nd order - # op2 = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=2) - # H2 = diagonal_quadrature(g_1D,op2.quadratureClosure) - # for i = 1:3 - # @test integral(H2,v[i]) ≈ v[i+1] rtol = 1e-14 - # end - # 4th order - op4 = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4) - H4 = diagonal_quadrature(g_1D,op4.quadratureClosure) - for i = 1:4 - @test integral(H4,v[i]) ≈ v[i+1][end] - v[i+1][1] rtol = 1e-14 + @testset "2nd order" begin + op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=2) + H = DiagonalQuadrature(g_1D,op.quadratureClosure) + for i = 1:2 + @test integral(H,v[i]) ≈ v[i+1][end] - v[i+1][1] rtol = 1e-14 + end end - end - @testset "Inferred" begin - H_x = diagonal_quadrature(g_1D,op.quadratureClosure) - H_xy = diagonal_quadrature(g_2D,op.quadratureClosure) - v_1D = ones(Float64, size(g_1D)) - v_2D = ones(Float64, size(g_2D)) - @inferred H_x*v_1D - @inferred H_x'*v_1D - @inferred H_xy*v_2D - @inferred H_xy'*v_2D + @testset "4th order" begin + op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4) + H = DiagonalQuadrature(g_1D,op.quadratureClosure) + for i = 1:4 + @test integral(H,v[i]) ≈ v[i+1][end] - v[i+1][1] rtol = 1e-14 + end + end end end -@testset "InverseDiagonalQuadrature" begin - op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4) - Lx = π/2. - Ly = Float64(π) - g_1D = EquidistantGrid(77, 0.0, Lx) - g_2D = EquidistantGrid((77,66), (0.0, 0.0), (Lx,Ly)) - @testset "Constructors" begin - # 1D - Hi_x = InverseDiagonalQuadrature(inverse_spacing(g_1D)[1], 1. ./ op.quadratureClosure, size(g_1D)); - @test Hi_x == InverseDiagonalQuadrature(g_1D,op.quadratureClosure) - @test Hi_x == inverse_diagonal_quadrature(g_1D,op.quadratureClosure) - @test Hi_x isa TensorMapping{T,1,1} where T - @test Hi_x' isa TensorMapping{T,1,1} where T - - # 2D - Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) - @test Hi_xy isa TensorMappingComposition - @test Hi_xy isa TensorMapping{T,2,2} where T - @test Hi_xy' isa TensorMapping{T,2,2} where T - end - - @testset "Sizes" begin - # 1D - Hi_x = inverse_diagonal_quadrature(g_1D,op.quadratureClosure) - @test domain_size(Hi_x) == size(g_1D) - @test range_size(Hi_x) == size(g_1D) - # 2D - Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) - @test domain_size(Hi_xy) == size(g_2D) - @test range_size(Hi_xy) == size(g_2D) - end - - @testset "Application" begin - # 1D - H_x = diagonal_quadrature(g_1D,op.quadratureClosure) - Hi_x = inverse_diagonal_quadrature(g_1D,op.quadratureClosure) - v_1D = evalOn(g_1D,x->sin(x)) - u_1D = evalOn(g_1D,x->x^3-x^2+1) - @test Hi_x*H_x*v_1D ≈ v_1D rtol = 1e-15 - @test Hi_x*H_x*u_1D ≈ u_1D rtol = 1e-15 - @test Hi_x*v_1D == Hi_x'*v_1D - # 2D - H_xy = diagonal_quadrature(g_2D,op.quadratureClosure) - Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) - v_2D = evalOn(g_2D,(x,y)->sin(x)+cos(y)) - u_2D = evalOn(g_2D,(x,y)->x*y + x^5 - sqrt(y)) - @test Hi_xy*H_xy*v_2D ≈ v_2D rtol = 1e-15 - @test Hi_xy*H_xy*u_2D ≈ u_2D rtol = 1e-15 - @test Hi_xy*v_2D ≈ Hi_xy'*v_2D rtol = 1e-16 #Failed for exact equality. Must differ in operation order for some reason? - end - - @testset "Inferred" begin - Hi_x = inverse_diagonal_quadrature(g_1D,op.quadratureClosure) - Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) - v_1D = ones(Float64, size(g_1D)) - v_2D = ones(Float64, size(g_2D)) - @inferred Hi_x*v_1D - @inferred Hi_x'*v_1D - @inferred Hi_xy*v_2D - @inferred Hi_xy'*v_2D - end -end +# @testset "InverseDiagonalQuadrature" begin +# op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4) +# Lx = π/2. +# Ly = Float64(π) +# g_1D = EquidistantGrid(77, 0.0, Lx) +# g_2D = EquidistantGrid((77,66), (0.0, 0.0), (Lx,Ly)) +# @testset "Constructors" begin +# # 1D +# Hi_x = InverseDiagonalQuadrature(inverse_spacing(g_1D)[1], 1. ./ op.quadratureClosure, size(g_1D)); +# @test Hi_x == InverseDiagonalQuadrature(g_1D,op.quadratureClosure) +# @test Hi_x == inverse_diagonal_quadrature(g_1D,op.quadratureClosure) +# @test Hi_x isa TensorMapping{T,1,1} where T +# @test Hi_x' isa TensorMapping{T,1,1} where T +# +# # 2D +# Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) +# @test Hi_xy isa TensorMappingComposition +# @test Hi_xy isa TensorMapping{T,2,2} where T +# @test Hi_xy' isa TensorMapping{T,2,2} where T +# end +# +# @testset "Sizes" begin +# # 1D +# Hi_x = inverse_diagonal_quadrature(g_1D,op.quadratureClosure) +# @test domain_size(Hi_x) == size(g_1D) +# @test range_size(Hi_x) == size(g_1D) +# # 2D +# Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) +# @test domain_size(Hi_xy) == size(g_2D) +# @test range_size(Hi_xy) == size(g_2D) +# end +# +# @testset "Application" begin +# # 1D +# H_x = diagonal_quadrature(g_1D,op.quadratureClosure) +# Hi_x = inverse_diagonal_quadrature(g_1D,op.quadratureClosure) +# v_1D = evalOn(g_1D,x->sin(x)) +# u_1D = evalOn(g_1D,x->x^3-x^2+1) +# @test Hi_x*H_x*v_1D ≈ v_1D rtol = 1e-15 +# @test Hi_x*H_x*u_1D ≈ u_1D rtol = 1e-15 +# @test Hi_x*v_1D == Hi_x'*v_1D +# # 2D +# H_xy = diagonal_quadrature(g_2D,op.quadratureClosure) +# Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) +# v_2D = evalOn(g_2D,(x,y)->sin(x)+cos(y)) +# u_2D = evalOn(g_2D,(x,y)->x*y + x^5 - sqrt(y)) +# @test Hi_xy*H_xy*v_2D ≈ v_2D rtol = 1e-15 +# @test Hi_xy*H_xy*u_2D ≈ u_2D rtol = 1e-15 +# @test Hi_xy*v_2D ≈ Hi_xy'*v_2D rtol = 1e-16 #Failed for exact equality. Must differ in operation order for some reason? +# end +# +# @testset "Inferred" begin +# Hi_x = inverse_diagonal_quadrature(g_1D,op.quadratureClosure) +# Hi_xy = inverse_diagonal_quadrature(g_2D,op.quadratureClosure) +# v_1D = ones(Float64, size(g_1D)) +# v_2D = ones(Float64, size(g_2D)) +# @inferred Hi_x*v_1D +# @inferred Hi_x'*v_1D +# @inferred Hi_xy*v_2D +# @inferred Hi_xy'*v_2D +# end +# end @testset "BoundaryOperator" begin closure_stencil = Stencil((0,2), (2.,1.,3.))