changeset 1598:19cdec9c21cb feature/boundary_conditions

Implement and test sat_tensors for Dirichlet and Neumann conditions
author Vidar Stiernström <vidar.stiernstrom@gmail.com>
date Sun, 26 May 2024 18:19:02 -0700
parents 330c39505a94
children 37b05221beda
files TODO.md src/SbpOperators/volumeops/laplace/laplace.jl test/SbpOperators/volumeops/laplace/laplace_test.jl
diffstat 3 files changed, 87 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/TODO.md	Sun May 26 18:18:17 2024 -0700
+++ b/TODO.md	Sun May 26 18:19:02 2024 -0700
@@ -21,6 +21,10 @@
        See (https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing)
  - [ ] Samla noggrannhets- och SBP-ness-tester för alla operatorer på ett ställe
  - [ ] Move export statements to top of each module
+ - [ ] Implement apply_transpose for
+      - [ ] ElementwiseTensorOperation
+      - [ ] VolumeOperator
+      - [ ] Laplace
 
 
  - [ ] Gå igenom alla typ parametrar och kolla om de är motiverade. Både i signaturer och typer, tex D i VariableSecondDerivative. Kan vi använda promote istället?
--- a/src/SbpOperators/volumeops/laplace/laplace.jl	Sun May 26 18:18:17 2024 -0700
+++ b/src/SbpOperators/volumeops/laplace/laplace.jl	Sun May 26 18:19:02 2024 -0700
@@ -53,12 +53,31 @@
 end
 laplace(g::EquidistantGrid, stencil_set) = second_derivative(g, stencil_set)
 
-# TODO: Add sat_tensor for Diirichlet condition
+"""
+sat_tensors(Δ::Laplace, g::Grid, bc::DirichletCondition, tuning)
+
+The operators required to construct the SAT for imposing a Dirichlet condition.
+`tuning` specifies the strength of the penalty. See
+
+See also: [`sat`,`DirichletCondition`, `positivity_decomposition`](@ref).
+"""
+function BoundaryConditions.sat_tensors(Δ::Laplace, g::Grid, bc::DirichletCondition, tuning)
+    id = bc.id
+    set  = Δ.stencil_set
+    H⁻¹ = inverse_inner_product(g,set)
+    Hᵧ = inner_product(boundary_grid(g, id), set)
+    e = boundary_restriction(g, set, id)
+    d = normal_derivative(g, set, id)
+    B = positivity_decomposition(Δ, g, bc, tuning)
+    sat_op = H⁻¹∘(d' - B*e')∘Hᵧ
+    return sat_op, e
+end
+BoundaryConditions.sat_tensors(Δ::Laplace, g::Grid, bc::DirichletCondition) = BoundaryConditions.sat_tensors(Δ, g, bc, (1.,1.))
 
 """
 sat_tensors(Δ::Laplace, g::Grid, bc::NeumannCondition)
 
-The operators required to construct the SAT for imposing Neumann condition
+The operators required to construct the SAT for imposing a Neumann condition
 
 
 See also: [`sat`,`NeumannCondition`](@ref).
@@ -71,6 +90,19 @@
     e = boundary_restriction(g, set, id)
     d = normal_derivative(g, set, id)
 
-    sat_op = H⁻¹∘e'∘Hᵧ
+    sat_op = -H⁻¹∘e'∘Hᵧ
     return sat_op, d
 end
+
+function positivity_decomposition(Δ::Laplace, g::Grid, bc::DirichletCondition, tuning)
+    pos_prop = positivity_properties(Δ)
+    h = spacing(orthogonal_grid(g, bc.id))
+    θ_H = pos_prop.theta_H
+    τ_H = tuning[1]*ndims(g)/(h*θ_H)
+    θ_R = pos_prop.theta_R
+    τ_R = tuning[2]/(h*θ_R)
+    B = τ_H + τ_R
+    return B
+end
+
+positivity_properties(Δ::Laplace) = parse_named_tuple(Δ.stencil_set["Positivity"]["D2"])
\ No newline at end of file
--- a/test/SbpOperators/volumeops/laplace/laplace_test.jl	Sun May 26 18:18:17 2024 -0700
+++ b/test/SbpOperators/volumeops/laplace/laplace_test.jl	Sun May 26 18:19:02 2024 -0700
@@ -91,47 +91,58 @@
 
 @testset "sat_tensors" begin
     operator_path = sbp_operators_path()*"standard_diagonal.toml"
-    stencil_set = read_stencil_set(operator_path; order=4)
-    g = equidistant_grid((101,102), (-1.,-1.), (1.,1.))
-    W,E,S,N = boundary_identifiers(g)
+    orders = (2,4)
+    tols = (5e-2,5e-4)
+    sz = (201,401)
+    g = equidistant_grid((0.,0.), (1.,1.), sz...)
     
-    u = eval_on(g, (x,y) -> sin(x+y))
-    uWx = eval_on(boundary_grid(g,W), (x,y) -> -cos(x+y))
-    uEx = eval_on(boundary_grid(g,E), (x,y) -> cos(x+y))
-    uSy = eval_on(boundary_grid(g,S), (x,y) -> -cos(x+y))
-    uNy = eval_on(boundary_grid(g,N), (x,y) -> cos(x+y))
-    
-    
-    v = eval_on(g, (x,y) -> cos(x+y))
-    vW = eval_on(boundary_grid(g,W), (x,y) -> cos(x+y))
-    vE = eval_on(boundary_grid(g,E), (x,y) -> cos(x+y))
-    vS = eval_on(boundary_grid(g,S), (x,y) -> cos(x+y))
-    vN = eval_on(boundary_grid(g,N), (x,y) -> cos(x+y))
+    # Verify implementation of sat_tesnors by testing accuracy and symmetry (TODO) 
+    # of the operator D = Δ + SAT, where SAT is the tensor composition of the 
+    # operators from sat_tensor. Note that SAT*u should approximate 0 for the 
+    # conditions chosen.
 
+    @testset "Dirichlet" begin
+        for (o, tol) ∈ zip(orders,tols)
+            stencil_set = read_stencil_set(operator_path; order=o)
+            Δ = Laplace(g, stencil_set)
+            H = inner_product(g, stencil_set)
+            u = collect(eval_on(g, (x,y) -> sin(π*x)sin(2*π*y)))
+            Δu = collect(eval_on(g, (x,y) -> -5*π^2*sin(π*x)sin(2*π*y)))
+            D = Δ 
+            for id ∈ boundary_identifiers(g)
+                D = D + foldl(∘, sat_tensors(Δ, g, DirichletCondition(0., id)))
+            end
+            e = D*u .- Δu
+            # Accuracy
+            @test sqrt(sum(H*e.^2)) ≈ 0 atol = tol
+            # Symmetry
+            # TODO: # Consider generating the matrices to H and D and test D'H == H'D
+            r = randn(size(u))
+            @test_broken (D'∘H - H∘D)*r .≈ 0 atol = 1e-13 # TODO: Need to implement apply_transpose for D.
+        end
+    end
 
     @testset "Neumann" begin
-        Δ = Laplace(g, stencil_set)
-        H = inner_product(g, stencil_set)
-        HW = inner_product(boundary_grid(g,W), stencil_set)
-        HE = inner_product(boundary_grid(g,E), stencil_set)
-        HS = inner_product(boundary_grid(g,S), stencil_set)
-        HN = inner_product(boundary_grid(g,N), stencil_set)
-        
-        ncW = NeumannCondition(0., W)
-        ncE = NeumannCondition(0., E)
-        ncS = NeumannCondition(0., S)
-        ncN = NeumannCondition(0., N)
-        
-        SATW = foldl(∘,sat_tensors(Δ, g, ncW))
-        SATE = foldl(∘,sat_tensors(Δ, g, ncE))
-        SATS = foldl(∘,sat_tensors(Δ, g, ncS))
-        SATN = foldl(∘,sat_tensors(Δ, g, ncN))
-        
-
-        @test sum((H*SATW*u).*v) ≈ sum((HW*uWx).*vW) rtol = 1e-6
-        @test sum((H*SATE*u).*v) ≈ sum((HE*uEx).*vE) rtol = 1e-6
-        @test sum((H*SATS*u).*v) ≈ sum((HS*uSy).*vS) rtol = 1e-6
-        @test sum((H*SATN*u).*v) ≈ sum((HN*uNy).*vN) rtol = 1e-6
+        @testset "Dirichlet" begin
+            for (o, tol) ∈ zip(orders,tols)
+                stencil_set = read_stencil_set(operator_path; order=o)
+                Δ = Laplace(g, stencil_set)
+                H = inner_product(g, stencil_set)
+                u = collect(eval_on(g, (x,y) -> cos(π*x)cos(2*π*y)))
+                Δu = collect(eval_on(g, (x,y) -> -5*π^2*cos(π*x)cos(2*π*y)))
+                op = Δ 
+                for id ∈ boundary_identifiers(g)
+                    op = op + foldl(∘, sat_tensors(Δ, g, NeumannCondition(0., id)))
+                end
+                e = op*u .- Δu
+                # Accuracy
+                @test sqrt(sum(H*e.^2)) ≈ 0 atol = tol
+                # Symmetry
+                # TODO: # Consider generating the matrices to H and D and test D'H == H'D
+                r = randn(size(u))
+                @test_broken (D'∘H - H∘D)*r .≈ 0 atol = 1e-13 # TODO: Need to implement apply_transpose for D.
+            end
+        end
     end
 end