Mercurial > repos > public > sbplib_julia
changeset 924:12e8e431b43c feature/laplace_opset
Start restructuring Laplace making it more minimal.
author | Vidar Stiernström <vidar.stiernstrom@it.uu.se> |
---|---|
date | Mon, 21 Feb 2022 13:12:47 +0100 |
parents | 88bf50821cf5 |
children | 6b47a9ee1632 |
files | src/SbpOperators/volumeops/laplace/laplace.jl test/SbpOperators/volumeops/laplace/laplace_test.jl |
diffstat | 2 files changed, 58 insertions(+), 325 deletions(-) [+] |
line wrap: on
line diff
--- a/src/SbpOperators/volumeops/laplace/laplace.jl Mon Feb 21 13:11:17 2022 +0100 +++ b/src/SbpOperators/volumeops/laplace/laplace.jl Mon Feb 21 13:12:47 2022 +0100 @@ -1,157 +1,38 @@ -export Laplace -export laplace -# REVIEW: Makes more sense to me to have the exports at the top of the file. -# Might as well start fixing that. - -# REVIEW: -# Design discussions has led to attempt a restructuring of Laplace to a more -# minimal type, holding the tensor mapping and a stencil set. This allows -# construction of associated tensor mappings, e.g. boundary operators, based on the -# stencil set while keeping the type simpler. - -# REVIEW: The style of name `Laplace` might clash with other concepts. When -# thinking about implementing the variable second derivative I think I will -# have to create it as a full TM for the full dimensional problem instead of -# building it as a 1D operator and then use that with outer products. The -# natural name there would be `VariableSecondDerivative` (or something -# similar). But the similarity of the two names would suggest that `Laplace` -# and `VariableSecondDerivative` are the same kind of thing, which they -# wouldn't be. -# -# How do we distinguish the kind of type we are implementing here and what we -# could potentially do for the variable second derivative? -# -# I see two ways out: -# * Come up with a name for these sets of operators and change `Laplace` accordingly. -# * Come up with a name for the bare operators and change `VariableSecondDerivative` accordingly. - """ - Laplace{T, Dim, TMdiffop} <: TensorMapping{T,Dim,Dim} - Laplace(grid, filename; order) + Laplace{T, DiffOp} <: TensorMapping{T,Dim,Dim} + Laplace(grid::Equidistant, stencil_set) Implements the Laplace operator, approximating ∑d²/xᵢ² , i = 1,...,`Dim` as a -`TensorMapping`. Additionally, `Laplace` stores the inner product and boundary -operators relevant for constructing a SBP finite difference scheme as a `TensorMapping`. - -`Laplace(grid, filename; order)` creates the Laplace operator defined on `grid`, -where the operators are read from TOML. The differential operator is created -using `laplace(grid,...)`. - -Note that all properties of Laplace, excluding the differential operator `Laplace.D`, are -abstract types. For performance reasons, they should therefore be -accessed via the provided getter functions (e.g `inner_product(::Laplace)`). +`TensorMapping`. Additionally `Laplace` stores the stencil set (parsed from TOML) +used to construct the `TensorMapping`. """ -struct Laplace{T, Dim, TMdiffop<:TensorMapping{T,Dim,Dim}} <: TensorMapping{T,Dim,Dim} - D::TMdiffop # Differential operator - H::TensorMapping # Inner product operator - H_inv::TensorMapping # Inverse inner product operator - e::StaticDict{<:BoundaryIdentifier,<:TensorMapping} # Boundary restriction operators. - d::StaticDict{<:BoundaryIdentifier,<:TensorMapping} # Normal derivative operators - H_boundary::StaticDict{<:BoundaryIdentifier,<:TensorMapping} # Boundary quadrature operators +struct Laplace{T, DiffOp<:TensorMapping{T,Dim,Dim}} <: TensorMapping{T,Dim,Dim} + D::DiffOp# Differential operator + stencil_set # Stencil set of the operator end -function Laplace(grid, filename; order) - - # Read stencils - stencil_set = read_stencil_set(filename; order) - # TODO: Removed once we can construct the volume and - # boundary operators by op(grid, read_stencil_set(fn; order,...)). - D_inner_stecil = parse_stencil(stencil_set["D2"]["inner_stencil"]) - D_closure_stencils = parse_stencil.(stencil_set["D2"]["closure_stencils"]) - H_inner_stencils = parse_scalar(stencil_set["H"]["inner"]) - H_closure_stencils = parse_tuple(stencil_set["H"]["closure"]) - e_closure_stencil = parse_stencil(stencil_set["e"]["closure"]) - d_closure_stencil = parse_stencil(stencil_set["d1"]["closure"]) - # REVIEW: Do we add the methods to get rid of this in this branch or a new one? +""" + `Laplace(grid::Equidistant, stencil_set)` - # Volume operators - Δ = laplace(grid, D_inner_stecil, D_closure_stencils) - H = inner_product(grid, H_inner_stencils, H_closure_stencils) - H⁻¹ = inverse_inner_product(grid, H_inner_stencils, H_closure_stencils) - - # Boundary operator - id pairs - ids = boundary_identifiers(grid) - # REVIEW: Change suggestion: Seems more readable to me but pretty subjective so feel free to ignore - e_pairs = map(id -> Pair(id, boundary_restriction(grid, e_closure_stencil, id)), ids) - d_pairs = map(id -> Pair(id, normal_derivative(grid, d_closure_stencil, id)), ids) - Hᵧ_pairs = map(id -> Pair(id, inner_product(boundary_grid(grid, id), H_inner_stencils, H_closure_stencils)), ids) - - return Laplace(Δ, H, H⁻¹, StaticDict(e_pairs), StaticDict(d_pairs), StaticDict(Hᵧ_pairs)) +Creates the `Laplace`` operator `Δ` on `grid` given a parsed TOML +`stencil_set`. See also [`laplace`](@ref). +""" +function Laplace(grid::Equidistant, stencil_set) + inner_stencil = parse_stencil(stencil_set["D2"]["inner_stencil"]) + closure_stencils = parse_stencil.(stencil_set["D2"]["closure_stencils"]) + Δ = laplace(grid, inner_stencil,closure_stencils) + return Laplace(Δ,stencil_set) end -# TODO: Consider pretty printing of the following form -# Base.show(io::IO, L::Laplace{T, Dim}) where {T,Dim,TM} = print(io, "Laplace{$T, $Dim, $TM}(", L.D, L.H, L.H_inv, L.e, L.d, L.H_boundary, ")") -# REVIEW: Should leave a todo here to update this once we have some pretty printing for tensor mappings in general. - LazyTensors.range_size(L::Laplace) = LazyTensors.range_size(L.D) LazyTensors.domain_size(L::Laplace) = LazyTensors.domain_size(L.D) LazyTensors.apply(L::Laplace, v::AbstractArray, I...) = LazyTensors.apply(L.D,v,I...) - -""" - inner_product(L::Laplace) - -Returns the inner product operator associated with `L` -""" -inner_product(L::Laplace) = L.H - - -""" - inverse_inner_product(L::Laplace) - -Returns the inverse of the inner product operator associated with `L` -""" -inverse_inner_product(L::Laplace) = L.H_inv - +# TODO: Implement pretty printing of Laplace once pretty printing of TensorMappings is implemented. +# Base.show(io::IO, L::Laplace) = ... """ - boundary_restriction(L::Laplace, id::BoundaryIdentifier) - boundary_restriction(L::Laplace, ids::Tuple) - boundary_restriction(L::Laplace, ids...) - -Returns boundary restriction operator(s) associated with `L` for the boundary(s) -identified by id(s). -""" -boundary_restriction(L::Laplace, id::BoundaryIdentifier) = L.e[id] -boundary_restriction(L::Laplace, ids::Tuple) = map(id-> L.e[id], ids) -boundary_restriction(L::Laplace, ids...) = boundary_restriction(L, ids) -# REVIEW: I propose changing the following implementations according to the -# above. There are some things we're missing with regards to -# `BoundaryIdentifier`, for example we should be able to handle groups of -# boundaries as a single `BoundaryIdentifier`. I don't know if we can figure -# out the interface for that now or if we save it for the future but either -# way these methods will be affected. - - - -""" - normal_derivative(L::Laplace, id::BoundaryIdentifier) - normal_derivative(L::Laplace, ids::NTuple{N,BoundaryIdentifier}) - normal_derivative(L::Laplace, ids...) - -Returns normal derivative operator(s) associated with `L` for the boundary(s) -identified by id(s). -""" -normal_derivative(L::Laplace, id::BoundaryIdentifier) = L.d[id] -normal_derivative(L::Laplace, ids::NTuple{N,BoundaryIdentifier}) where N = ntuple(i->L.d[ids[i]],N) -normal_derivative(L::Laplace, ids::Vararg{BoundaryIdentifier,N}) where N = ntuple(i->L.d[ids[i]],N) - - -""" - boundary_quadrature(L::Laplace, id::BoundaryIdentifier) - boundary_quadrature(L::Laplace, ids::NTuple{N,BoundaryIdentifier}) - boundary_quadrature(L::Laplace, ids...) - -Returns boundary quadrature operator(s) associated with `L` for the boundary(s) -identified by id(s). -""" -boundary_quadrature(L::Laplace, id::BoundaryIdentifier) = L.H_boundary[id] -boundary_quadrature(L::Laplace, ids::NTuple{N,BoundaryIdentifier}) where N = ntuple(i->L.H_boundary[ids[i]],N) -boundary_quadrature(L::Laplace, ids::Vararg{BoundaryIdentifier,N}) where N = ntuple(i->L.H_boundary[ids[i]],N) - - -""" - laplace(grid::EquidistantGrid{Dim}, inner_stencil, closure_stencils) + laplace(grid::EquidistantGrid, inner_stencil, closure_stencils) Creates the Laplace operator operator `Δ` as a `TensorMapping` @@ -161,12 +42,12 @@ On a one-dimensional `grid`, `Δ` is equivalent to `second_derivative`. On a multi-dimensional `grid`, `Δ` is the sum of multi-dimensional `second_derivative`s -where the sum is carried out lazily. +where the sum is carried out lazily. See also [`second_derivative`](@ref). """ -function laplace(grid::EquidistantGrid, inner_stencil, closure_stencils) - Δ = second_derivative(grid, inner_stencil, closure_stencils, 1) +function laplace(grid::Equidistant, inner_stencil, closure_stencils) + second_derivative(grid, inner_stencil, closure_stencils, 1) for d = 2:dimension(grid) Δ += second_derivative(grid, inner_stencil, closure_stencils, d) end return Δ -end +end \ No newline at end of file
--- a/test/SbpOperators/volumeops/laplace/laplace_test.jl Mon Feb 21 13:11:17 2022 +0100 +++ b/test/SbpOperators/volumeops/laplace/laplace_test.jl Mon Feb 21 13:12:47 2022 +0100 @@ -4,17 +4,12 @@ using Sbplib.Grids using Sbplib.LazyTensors using Sbplib.RegionIndices -using Sbplib.StaticDicts +# Default stencils (4th order) operator_path = sbp_operators_path()*"standard_diagonal.toml" -# Default stencils (4th order) stencil_set = read_stencil_set(operator_path; order=4) inner_stencil = parse_stencil(stencil_set["D2"]["inner_stencil"]) closure_stencils = parse_stencil.(stencil_set["D2"]["closure_stencils"]) -e_closure = parse_stencil(stencil_set["e"]["closure"]) -d_closure = parse_stencil(stencil_set["d1"]["closure"]) -quadrature_interior = parse_scalar(stencil_set["H"]["inner"]) -quadrature_closure = parse_tuple(stencil_set["H"]["closure"]) @testset "Laplace" begin g_1D = EquidistantGrid(101, 0.0, 1.) @@ -23,173 +18,16 @@ @testset "1D" begin Δ = laplace(g_1D, inner_stencil, closure_stencils) - H = inner_product(g_1D, quadrature_interior, quadrature_closure) - Hi = inverse_inner_product(g_1D, quadrature_interior, quadrature_closure) - - (id_l, id_r) = boundary_identifiers(g_1D) - - e_l = boundary_restriction(g_1D, e_closure,id_l) - e_r = boundary_restriction(g_1D, e_closure,id_r) - e_dict = StaticDict(id_l => e_l, id_r => e_r) - - d_l = normal_derivative(g_1D, d_closure,id_l) - d_r = normal_derivative(g_1D, d_closure,id_r) - d_dict = StaticDict(id_l => d_l, id_r => d_r) - - H_l = inner_product(boundary_grid(g_1D,id_l), quadrature_interior, quadrature_closure) - H_r = inner_product(boundary_grid(g_1D,id_r), quadrature_interior, quadrature_closure) - Hb_dict = StaticDict(id_l => H_l, id_r => H_r) - - L = Laplace(g_1D, operator_path; order=4) - @test L == Laplace(Δ, H, Hi, e_dict, d_dict, Hb_dict) - @test L isa TensorMapping{T,1,1} where T - @inferred Laplace(Δ, H, Hi, e_dict, d_dict, Hb_dict) - # REVIEW: The tests above seem very tied to the implementation. Is - # it important that the components of the operator set are stored - # in static dicts? Is something like below better? - # - # ``` - # L = Laplace(g_1D, operator_path; order=4) - # @test L isa TensorMapping{T,1,1} where T - # @test boundary_restriction(L,id_l) == boundary_restriction(g_1D, e_closure,id_l) - # ... - # ``` - # I guess this is more or less simply a reorganization of the test and skipping testing for the struct layout + @test Laplace(g_1D, stencil_set) == Laplace(Δ, stencil_set) + @test Laplace(g_1D, stencil_set) isa TensorMapping{T,1,1} where T end @testset "3D" begin Δ = laplace(g_3D, inner_stencil, closure_stencils) - H = inner_product(g_3D, quadrature_interior, quadrature_closure) - Hi = inverse_inner_product(g_3D, quadrature_interior, quadrature_closure) - - (id_l, id_r, id_s, id_n, id_b, id_t) = boundary_identifiers(g_3D) - e_l = boundary_restriction(g_3D, e_closure,id_l) - e_r = boundary_restriction(g_3D, e_closure,id_r) - e_s = boundary_restriction(g_3D, e_closure,id_s) - e_n = boundary_restriction(g_3D, e_closure,id_n) - e_b = boundary_restriction(g_3D, e_closure,id_b) - e_t = boundary_restriction(g_3D, e_closure,id_t) - e_dict = StaticDict(id_l => e_l, id_r => e_r, - id_s => e_s, id_n => e_n, - id_b => e_b, id_t => e_t) - - d_l = normal_derivative(g_3D, d_closure,id_l) - d_r = normal_derivative(g_3D, d_closure,id_r) - d_s = normal_derivative(g_3D, d_closure,id_s) - d_n = normal_derivative(g_3D, d_closure,id_n) - d_b = normal_derivative(g_3D, d_closure,id_b) - d_t = normal_derivative(g_3D, d_closure,id_t) - d_dict = StaticDict(id_l => d_l, id_r => d_r, - id_s => d_s, id_n => d_n, - id_b => d_b, id_t => d_t) - - H_l = inner_product(boundary_grid(g_3D,id_l), quadrature_interior, quadrature_closure) - H_r = inner_product(boundary_grid(g_3D,id_r), quadrature_interior, quadrature_closure) - H_s = inner_product(boundary_grid(g_3D,id_s), quadrature_interior, quadrature_closure) - H_n = inner_product(boundary_grid(g_3D,id_n), quadrature_interior, quadrature_closure) - H_b = inner_product(boundary_grid(g_3D,id_b), quadrature_interior, quadrature_closure) - H_t = inner_product(boundary_grid(g_3D,id_t), quadrature_interior, quadrature_closure) - Hb_dict = StaticDict(id_l => H_l, id_r => H_r, - id_s => H_s, id_n => H_n, - id_b => H_b, id_t => H_t) - - L = Laplace(g_3D, operator_path; order=4) - @test L == Laplace(Δ,H,Hi,e_dict,d_dict,Hb_dict) - @test L isa TensorMapping{T,3,3} where T - @inferred Laplace(Δ,H,Hi,e_dict,d_dict,Hb_dict) - end - end - - # REVIEW: Is this testset misplaced? Should it really be inside the "Laplace" testset? - @testset "laplace" begin - @testset "1D" begin - L = laplace(g_1D, inner_stencil, closure_stencils) - @test L == second_derivative(g_1D, inner_stencil, closure_stencils) - @test L isa TensorMapping{T,1,1} where T - end - @testset "3D" begin - L = laplace(g_3D, inner_stencil, closure_stencils) - @test L isa TensorMapping{T,3,3} where T - Dxx = second_derivative(g_3D, inner_stencil, closure_stencils, 1) - Dyy = second_derivative(g_3D, inner_stencil, closure_stencils, 2) - Dzz = second_derivative(g_3D, inner_stencil, closure_stencils, 3) - @test L == Dxx + Dyy + Dzz - @test L isa TensorMapping{T,3,3} where T + @test Laplace(g_3D, stencil_set) == Laplace(Δ,stencil_set) + @test Laplace(g_3D, stencil_set) isa TensorMapping{T,3,3} where T end end - @testset "inner_product" begin - L = Laplace(g_3D, operator_path; order=4) - @test inner_product(L) == inner_product(g_3D, quadrature_interior, quadrature_closure) - end - - @testset "inverse_inner_product" begin - L = Laplace(g_3D, operator_path; order=4) - @test inverse_inner_product(L) == inverse_inner_product(g_3D, quadrature_interior, quadrature_closure) - end - - @testset "boundary_restriction" begin - L = Laplace(g_3D, operator_path; order=4) - (id_l, id_r, id_s, id_n, id_b, id_t) = boundary_identifiers(g_3D) - @test boundary_restriction(L, id_l) == boundary_restriction(g_3D, e_closure,id_l) - @test boundary_restriction(L, id_r) == boundary_restriction(g_3D, e_closure,id_r) - @test boundary_restriction(L, id_s) == boundary_restriction(g_3D, e_closure,id_s) - @test boundary_restriction(L, id_n) == boundary_restriction(g_3D, e_closure,id_n) - @test boundary_restriction(L, id_b) == boundary_restriction(g_3D, e_closure,id_b) - @test boundary_restriction(L, id_t) == boundary_restriction(g_3D, e_closure,id_t) - - ids = boundary_identifiers(g_3D) - es = boundary_restriction(L, ids) - @test es == (boundary_restriction(L, id_l), - boundary_restriction(L, id_r), - boundary_restriction(L, id_s), - boundary_restriction(L, id_n), - boundary_restriction(L, id_b), - boundary_restriction(L, id_t)); - @test es == boundary_restriction(L, ids...) - end - - @testset "normal_derivative" begin - L = Laplace(g_3D, operator_path; order=4) - (id_l, id_r, id_s, id_n, id_b, id_t) = boundary_identifiers(g_3D) - @test normal_derivative(L, id_l) == normal_derivative(g_3D, d_closure,id_l) - @test normal_derivative(L, id_r) == normal_derivative(g_3D, d_closure,id_r) - @test normal_derivative(L, id_s) == normal_derivative(g_3D, d_closure,id_s) - @test normal_derivative(L, id_n) == normal_derivative(g_3D, d_closure,id_n) - @test normal_derivative(L, id_b) == normal_derivative(g_3D, d_closure,id_b) - @test normal_derivative(L, id_t) == normal_derivative(g_3D, d_closure,id_t) - - ids = boundary_identifiers(g_3D) - ds = normal_derivative(L, ids) - @test ds == (normal_derivative(L, id_l), - normal_derivative(L, id_r), - normal_derivative(L, id_s), - normal_derivative(L, id_n), - normal_derivative(L, id_b), - normal_derivative(L, id_t)); - @test ds == normal_derivative(L, ids...) - end - - @testset "boundary_quadrature" begin - L = Laplace(g_3D, operator_path; order=4) - (id_l, id_r, id_s, id_n, id_b, id_t) = boundary_identifiers(g_3D) - @test boundary_quadrature(L, id_l) == inner_product(boundary_grid(g_3D, id_l), quadrature_interior, quadrature_closure) - @test boundary_quadrature(L, id_r) == inner_product(boundary_grid(g_3D, id_r), quadrature_interior, quadrature_closure) - @test boundary_quadrature(L, id_s) == inner_product(boundary_grid(g_3D, id_s), quadrature_interior, quadrature_closure) - @test boundary_quadrature(L, id_n) == inner_product(boundary_grid(g_3D, id_n), quadrature_interior, quadrature_closure) - @test boundary_quadrature(L, id_b) == inner_product(boundary_grid(g_3D, id_b), quadrature_interior, quadrature_closure) - @test boundary_quadrature(L, id_t) == inner_product(boundary_grid(g_3D, id_t), quadrature_interior, quadrature_closure) - - ids = boundary_identifiers(g_3D) - H_gammas = boundary_quadrature(L, ids) - @test H_gammas == (boundary_quadrature(L, id_l), - boundary_quadrature(L, id_r), - boundary_quadrature(L, id_s), - boundary_quadrature(L, id_n), - boundary_quadrature(L, id_b), - boundary_quadrature(L, id_t)); - @test H_gammas == boundary_quadrature(L, ids...) - end - # Exact differentiation is measured point-wise. In other cases # the error is measured in the l2-norm. @testset "Accuracy" begin @@ -207,29 +45,43 @@ # implies that L*v should be exact for binomials up to order 2. @testset "2nd order" begin stencil_set = read_stencil_set(operator_path; order=2) - inner_stencil = parse_stencil(stencil_set["D2"]["inner_stencil"]) - closure_stencils = parse_stencil.(stencil_set["D2"]["closure_stencils"]) - L = laplace(g_3D, inner_stencil, closure_stencils) - @test L*polynomials[1] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 - @test L*polynomials[2] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 - @test L*polynomials[3] ≈ polynomials[1] atol = 5e-9 - @test L*v ≈ Δv rtol = 5e-2 norm = l2 + Δ = Laplace(g_3D, stencil_set) + @test Δ*polynomials[1] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 + @test Δ*polynomials[2] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 + @test Δ*polynomials[3] ≈ polynomials[1] atol = 5e-9 + @test Δ*v ≈ Δv rtol = 5e-2 norm = l2 end # 4th order interior stencil, 2nd order boundary stencil, # implies that L*v should be exact for binomials up to order 3. @testset "4th order" begin stencil_set = read_stencil_set(operator_path; order=4) - inner_stencil = parse_stencil(stencil_set["D2"]["inner_stencil"]) - closure_stencils = parse_stencil.(stencil_set["D2"]["closure_stencils"]) - L = laplace(g_3D, inner_stencil, closure_stencils) + Δ = Laplace(g_3D, stencil_set) # NOTE: high tolerances for checking the "exact" differentiation # due to accumulation of round-off errors/cancellation errors? - @test L*polynomials[1] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 - @test L*polynomials[2] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 - @test L*polynomials[3] ≈ polynomials[1] atol = 5e-9 - @test L*polynomials[4] ≈ polynomials[2] atol = 5e-9 - @test L*v ≈ Δv rtol = 5e-4 norm = l2 + @test Δ*polynomials[1] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 + @test Δ*polynomials[2] ≈ zeros(Float64, size(g_3D)...) atol = 5e-9 + @test Δ*polynomials[3] ≈ polynomials[1] atol = 5e-9 + @test Δ*polynomials[4] ≈ polynomials[2] atol = 5e-9 + @test Δ*v ≈ Δv rtol = 5e-4 norm = l2 end end end + +@testset "laplace" begin + @testset "1D" begin + Δ = laplace(g_1D, inner_stencil, closure_stencils) + @test Δ == second_derivative(g_1D, inner_stencil, closure_stencils) + @test Δ isa TensorMapping{T,1,1} where T + end + @testset "3D" begin + Δ = laplace(g_3D, inner_stencil, closure_stencils) + @test Δ isa TensorMapping{T,3,3} where T + Dxx = second_derivative(g_3D, inner_stencil, closure_stencils, 1) + Dyy = second_derivative(g_3D, inner_stencil, closure_stencils, 2) + Dzz = second_derivative(g_3D, inner_stencil, closure_stencils, 3) + @test Δ == Dxx + Dyy + Dzz + @test Δ isa TensorMapping{T,3,3} where T + end +end +