changeset 865:545a6c1a0a0e feature/laplace_opset

Merge default again
author Vidar Stiernström <vidar.stiernstrom@it.uu.se>
date Fri, 02 Jul 2021 14:23:33 +0200
parents 4d40d31c35f2 (diff) 80d88bb1c5bd (current diff)
children 1784b1c0af3e 9929c99754fb
files
diffstat 5 files changed, 286 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
diff -r 80d88bb1c5bd -r 545a6c1a0a0e Notes.md
--- a/Notes.md	Fri Jul 02 13:46:39 2021 +0200
+++ b/Notes.md	Fri Jul 02 14:23:33 2021 +0200
@@ -130,6 +130,7 @@
  - [ ] How do we handle mixes of periodic and non-periodic grids? Seems it should be supported on the grid level and on the 1d operator level. Between there it should be transparent.
  - [ ] Can we have a trait to tell if a TensorMapping is transposable?
  - [ ] Is it ok to have "Constructors" for abstract types which create subtypes? For example a Grids() functions that gives different kind of grids based on input?
+ - [ ] Figure out how to treat the borrowing parameters of operators. Include in into the struct? Expose via function dispatched on the operator type and grid?
 
 ## Regions and tensormappings
 - [ ] Use a trait to indicate if a TensorMapping uses indices with regions.
diff -r 80d88bb1c5bd -r 545a6c1a0a0e src/Grids/AbstractGrid.jl
--- a/src/Grids/AbstractGrid.jl	Fri Jul 02 13:46:39 2021 +0200
+++ b/src/Grids/AbstractGrid.jl	Fri Jul 02 14:23:33 2021 +0200
@@ -7,7 +7,7 @@
 
 """
 abstract type AbstractGrid end
-
+export AbstractGrid
 function dimension end
 function points end
 export dimension, points
diff -r 80d88bb1c5bd -r 545a6c1a0a0e src/SbpOperators/SbpOperators.jl
--- a/src/SbpOperators/SbpOperators.jl	Fri Jul 02 13:46:39 2021 +0200
+++ b/src/SbpOperators/SbpOperators.jl	Fri Jul 02 14:23:33 2021 +0200
@@ -3,6 +3,7 @@
 using Sbplib.RegionIndices
 using Sbplib.LazyTensors
 using Sbplib.Grids
+using Sbplib.StaticDicts
 
 include("stencil.jl")
 include("d2.jl")
diff -r 80d88bb1c5bd -r 545a6c1a0a0e src/SbpOperators/volumeops/laplace/laplace.jl
--- a/src/SbpOperators/volumeops/laplace/laplace.jl	Fri Jul 02 13:46:39 2021 +0200
+++ b/src/SbpOperators/volumeops/laplace/laplace.jl	Fri Jul 02 14:23:33 2021 +0200
@@ -1,19 +1,146 @@
 """
-    laplace(grid::EquidistantGrid{Dim}, inner_stencil, closure_stencils)
+    Laplace{T, Dim, TMdiffop} <: TensorMapping{T,Dim,Dim}
+    Laplace(grid::AbstractGrid, fn; order)
+
+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 `TensorMapping`s.
+
+Laplace(grid, fn; order) creates the Laplace operator defined on `grid`,
+where the operators are read from TOML. The differential operator is created
+using `laplace(grid::AbstractGrid,...)`.
+
+Note that all properties of Laplace, excluding the Differential operator `D`, are
+abstract types. For performance reasons, they should therefore be
+accessed via the provided getter functions (e.g `inner_product(::Laplace)`).
+
+"""
+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 # TODO: Boundary inner product?
+end
+export Laplace
+
+function Laplace(grid::AbstractGrid, fn; order)
+    # TODO: Removed once we can construct the volume and
+    # boundary operators by op(grid, fn; order,...).
+    # Read stencils
+    op = read_D2_operator(fn; order)
+    D_inner_stecil = op.innerStencil
+    D_closure_stencils = op.closureStencils
+    H_closure_stencils = op.quadratureClosure
+    e_closure_stencil = op.eClosure
+    d_closure_stencil = op.dClosure
+
+    # Volume operators
+    Δ =  laplace(grid, D_inner_stecil, D_closure_stencils)
+    H =  inner_product(grid, H_closure_stencils)
+    H⁻¹ =  inverse_inner_product(grid, H_closure_stencils)
+
+    # Boundary operator - id pairs
+    ids = boundary_identifiers(grid)
+    n_ids = length(ids)
+    e_pairs = ntuple(i -> ids[i] => boundary_restriction(grid,e_closure_stencil,ids[i]),n_ids)
+    d_pairs = ntuple(i -> ids[i] => normal_derivative(grid,d_closure_stencil,ids[i]),n_ids)
+    Hᵧ_pairs = ntuple(i -> ids[i] => inner_product(boundary_grid(grid,ids[i]),H_closure_stencils),n_ids)
+
+    return Laplace(Δ, H, H⁻¹, StaticDict(e_pairs), StaticDict(d_pairs), StaticDict(Hᵧ_pairs))
+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, ")")
+
+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::Lapalace)
+
+Returns the inner product operator associated with `L`
+
+"""
+inner_product(L::Laplace) = L.H
+export inner_product
+
+
+"""
+    inverse_inner_product(L::Lapalace)
+
+Returns the inverse of the inner product operator associated with `L`
+
+"""
+inverse_inner_product(L::Laplace) = L.H_inv
+export inverse_inner_product
+
+
+"""
+    boundary_restriction(L::Lapalace,id::BoundaryIdentifier)
+    boundary_restriction(L::Lapalace,ids::NTuple{N,BoundaryIdentifier})
+    boundary_restriction(L::Lapalace,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::NTuple{N,BoundaryIdentifier}) where N = ntuple(i->L.e[ids[i]],N)
+boundary_restriction(L::Laplace,ids::Vararg{BoundaryIdentifier,N}) where N = ntuple(i->L.e[ids[i]],N)
+export boundary_restriction
+
+
+"""
+    normal_derivative(L::Lapalace,id::BoundaryIdentifier)
+    normal_derivative(L::Lapalace,ids::NTuple{N,BoundaryIdentifier})
+    normal_derivative(L::Lapalace,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)
+export normal_derivative
+
+
+# TODO: boundary_inner_product?
+"""
+    boundary_quadrature(L::Lapalace,id::BoundaryIdentifier)
+    boundary_quadrature(L::Lapalace,ids::NTuple{N,BoundaryIdentifier})
+    boundary_quadrature(L::Lapalace,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)
+export boundary_quadrature
+
+
+"""
+    laplace(grid, inner_stencil, closure_stencils)
 
 Creates the Laplace operator operator `Δ` as a `TensorMapping`
 
-`Δ` approximates the Laplace operator ∑d²/xᵢ² , i = 1,...,`Dim` on `grid`, using
-the stencil `inner_stencil` in the interior and a set of stencils `closure_stencils`
-for the points in the closure regions.
+`Δ` approximates the Laplace operator ∑d²/xᵢ² , i = 1,...,N on the N-dimensional
+`grid`, using the stencil `inner_stencil` in the interior and a set of stencils
+`closure_stencils` for the points in the closure regions.
 
 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.
 """
-function laplace(grid::EquidistantGrid{Dim}, inner_stencil, closure_stencils) where Dim
+function laplace(grid::AbstractGrid, inner_stencil, closure_stencils)
     Δ = second_derivative(grid, inner_stencil, closure_stencils, 1)
-    for d = 2:Dim
+    for d = 2:dimension(grid)
         Δ += second_derivative(grid, inner_stencil, closure_stencils, d)
     end
     return Δ
diff -r 80d88bb1c5bd -r 545a6c1a0a0e test/SbpOperators/volumeops/laplace/laplace_test.jl
--- a/test/SbpOperators/volumeops/laplace/laplace_test.jl	Fri Jul 02 13:46:39 2021 +0200
+++ b/test/SbpOperators/volumeops/laplace/laplace_test.jl	Fri Jul 02 14:23:33 2021 +0200
@@ -3,12 +3,85 @@
 using Sbplib.SbpOperators
 using Sbplib.Grids
 using Sbplib.LazyTensors
+using Sbplib.RegionIndices
+using Sbplib.StaticDicts
 
 @testset "Laplace" begin
     g_1D = EquidistantGrid(101, 0.0, 1.)
     g_3D = EquidistantGrid((51,101,52), (0.0, -1.0, 0.0), (1., 1., 1.))
+    op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
     @testset "Constructors" begin
-        op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
+
+        @testset "1D" begin
+            # Create all tensor mappings included in Laplace
+            Δ = laplace(g_1D, op.innerStencil, op.closureStencils)
+            H = inner_product(g_1D, op.quadratureClosure)
+            Hi = inverse_inner_product(g_1D, op.quadratureClosure)
+
+            (id_l, id_r) = boundary_identifiers(g_1D)
+
+            e_l = boundary_restriction(g_1D,op.eClosure,id_l)
+            e_r = boundary_restriction(g_1D,op.eClosure,id_r)
+            e_dict = StaticDict(id_l => e_l, id_r => e_r)
+
+            d_l = normal_derivative(g_1D,op.dClosure,id_l)
+            d_r = normal_derivative(g_1D,op.dClosure,id_r)
+            d_dict = StaticDict(id_l => d_l, id_r => d_r)
+
+            H_l = inner_product(boundary_grid(g_1D,id_l),op.quadratureClosure)
+            H_r = inner_product(boundary_grid(g_1D,id_r),op.quadratureClosure)
+            Hb_dict = StaticDict(id_l => H_l, id_r => H_r)
+
+            L = Laplace(g_1D, sbp_operators_path()*"standard_diagonal.toml"; 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)
+        end
+        @testset "3D" begin
+            # Create all tensor mappings included in Laplace
+            Δ = laplace(g_3D, op.innerStencil, op.closureStencils)
+            H = inner_product(g_3D, op.quadratureClosure)
+            Hi = inverse_inner_product(g_3D, op.quadratureClosure)
+
+            (id_l, id_r, id_s, id_n, id_b, id_t) = boundary_identifiers(g_3D)
+            e_l = boundary_restriction(g_3D,op.eClosure,id_l)
+            e_r = boundary_restriction(g_3D,op.eClosure,id_r)
+            e_s = boundary_restriction(g_3D,op.eClosure,id_s)
+            e_n = boundary_restriction(g_3D,op.eClosure,id_n)
+            e_b = boundary_restriction(g_3D,op.eClosure,id_b)
+            e_t = boundary_restriction(g_3D,op.eClosure,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,op.dClosure,id_l)
+            d_r = normal_derivative(g_3D,op.dClosure,id_r)
+            d_s = normal_derivative(g_3D,op.dClosure,id_s)
+            d_n = normal_derivative(g_3D,op.dClosure,id_n)
+            d_b = normal_derivative(g_3D,op.dClosure,id_b)
+            d_t = normal_derivative(g_3D,op.dClosure,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),op.quadratureClosure)
+            H_r = inner_product(boundary_grid(g_3D,id_r),op.quadratureClosure)
+            H_s = inner_product(boundary_grid(g_3D,id_s),op.quadratureClosure)
+            H_n = inner_product(boundary_grid(g_3D,id_n),op.quadratureClosure)
+            H_b = inner_product(boundary_grid(g_3D,id_b),op.quadratureClosure)
+            H_t = inner_product(boundary_grid(g_3D,id_t),op.quadratureClosure)
+            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, sbp_operators_path()*"standard_diagonal.toml"; 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
+
+    @testset "laplace" begin
         @testset "1D" begin
             L = laplace(g_1D, op.innerStencil, op.closureStencils)
             @test L == second_derivative(g_1D, op.innerStencil, op.closureStencils)
@@ -21,9 +94,83 @@
             Dyy = second_derivative(g_3D, op.innerStencil, op.closureStencils,2)
             Dzz = second_derivative(g_3D, op.innerStencil, op.closureStencils,3)
             @test L == Dxx + Dyy + Dzz
+            @test L isa TensorMapping{T,3,3} where T
         end
     end
 
+    @testset "inner_product" begin
+        L = Laplace(g_3D, sbp_operators_path()*"standard_diagonal.toml"; order=4)
+        @test inner_product(L) == inner_product(g_3D,op.quadratureClosure)
+    end
+
+    @testset "inverse_inner_product" begin
+        L = Laplace(g_3D, sbp_operators_path()*"standard_diagonal.toml"; order=4)
+        @test inverse_inner_product(L) == inverse_inner_product(g_3D,op.quadratureClosure)
+    end
+
+    @testset "boundary_restriction" begin
+        L = Laplace(g_3D, sbp_operators_path()*"standard_diagonal.toml"; 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,op.eClosure,id_l)
+        @test boundary_restriction(L,id_r) == boundary_restriction(g_3D,op.eClosure,id_r)
+        @test boundary_restriction(L,id_s) == boundary_restriction(g_3D,op.eClosure,id_s)
+        @test boundary_restriction(L,id_n) == boundary_restriction(g_3D,op.eClosure,id_n)
+        @test boundary_restriction(L,id_b) == boundary_restriction(g_3D,op.eClosure,id_b)
+        @test boundary_restriction(L,id_t) == boundary_restriction(g_3D,op.eClosure,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, sbp_operators_path()*"standard_diagonal.toml"; 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,op.dClosure,id_l)
+        @test normal_derivative(L,id_r) == normal_derivative(g_3D,op.dClosure,id_r)
+        @test normal_derivative(L,id_s) == normal_derivative(g_3D,op.dClosure,id_s)
+        @test normal_derivative(L,id_n) == normal_derivative(g_3D,op.dClosure,id_n)
+        @test normal_derivative(L,id_b) == normal_derivative(g_3D,op.dClosure,id_b)
+        @test normal_derivative(L,id_t) == normal_derivative(g_3D,op.dClosure,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, sbp_operators_path()*"standard_diagonal.toml"; 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),op.quadratureClosure)
+        @test boundary_quadrature(L,id_r) == inner_product(boundary_grid(g_3D,id_r),op.quadratureClosure)
+        @test boundary_quadrature(L,id_s) == inner_product(boundary_grid(g_3D,id_s),op.quadratureClosure)
+        @test boundary_quadrature(L,id_n) == inner_product(boundary_grid(g_3D,id_n),op.quadratureClosure)
+        @test boundary_quadrature(L,id_b) == inner_product(boundary_grid(g_3D,id_b),op.quadratureClosure)
+        @test boundary_quadrature(L,id_t) == inner_product(boundary_grid(g_3D,id_t),op.quadratureClosure)
+
+        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
@@ -40,8 +187,7 @@
         # 2nd order interior stencil, 1st order boundary stencil,
         # implies that L*v should be exact for binomials up to order 2.
         @testset "2nd order" begin
-            op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=2)
-            L = laplace(g_3D,op.innerStencil,op.closureStencils)
+            L = Laplace(g_3D, sbp_operators_path()*"standard_diagonal.toml"; order=2)
             @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
@@ -51,8 +197,7 @@
         # 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
-            op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
-            L = laplace(g_3D,op.innerStencil,op.closureStencils)
+            L = Laplace(g_3D, sbp_operators_path()*"standard_diagonal.toml"; order=4)
             # 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
diff -r 80d88bb1c5bd -r 545a6c1a0a0e test/runtests.jl