changeset 1602:3e7438e2a033 feature/boundary_conditions

Address review comments (1 left to be discussed)
author Vidar Stiernström <vidar.stiernstrom@gmail.com>
date Sat, 01 Jun 2024 17:39:54 -0700
parents fad18896d20a
children fca4a01d60c9
files src/BoundaryConditions/BoundaryConditions.jl src/BoundaryConditions/boundary_condition.jl src/BoundaryConditions/sat.jl src/Grids/Grids.jl src/Grids/equidistant_grid.jl src/Grids/grid.jl src/Grids/tensor_grid.jl src/SbpOperators/operators/standard_diagonal.toml src/SbpOperators/volumeops/laplace/laplace.jl test/BoundaryConditions/boundary_condition_test.jl test/BoundaryConditions/sat_test.jl test/Grids/equidistant_grid_test.jl test/Grids/tensor_grid_test.jl
diffstat 13 files changed, 89 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/src/BoundaryConditions/BoundaryConditions.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/BoundaryConditions/BoundaryConditions.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -4,8 +4,8 @@
 
 export BoundaryCondition
 export discretize_data
-export data # REVIEW: Name too generic to export.
-export id # REVIEW: Also to generic. Change to `boundary`?
+export boundary_data
+export boundary
 
 export NeumannCondition
 export DirichletCondition
--- a/src/BoundaryConditions/boundary_condition.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/BoundaryConditions/boundary_condition.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -1,47 +1,48 @@
 """
-    BoundaryCondition
+    BoundaryCondition{BID}
 
-A type for implementing data needed in order to impose a boundary condition.
+A type for implementing boundary_data needed in order to impose a boundary condition.
 Subtypes refer to perticular types of boundary conditions, e.g. Neumann conditions.
 """
-abstract type BoundaryCondition{T1,T2} end # REVIEW: No type parameters needed here.
+abstract type BoundaryCondition{BID} end
 
 """
-    id(::BoundaryCondition)
+    boundary(::BoundaryCondition)
 
 The boundary identifier of the BoundaryCondition.
-Must be implemented by subtypes.
 """
-function id end
+boundary(::BoundaryCondition{BID}) where {BID} = BID()
 
 """
-    data(::BoundaryCondition)
+    boundary_data(::BoundaryCondition)
 
 If implemented, the data associated with the BoundaryCondition
 """
-function data end
+function boundary_data end
 
 """
     discretize(grid, bc::BoundaryCondition)
 
-The grid function obtained from discretizing the `bc` data on the boundary grid
+The grid function obtained from discretizing the `bc` boundary_data on the boundary grid
     specified the by bc `id`.
 """
 function discretize_data(grid, bc::BoundaryCondition)
-    return eval_on(boundary_grid(grid, id(bc)), data(bc))
+    return eval_on(boundary_grid(grid, boundary(bc)), boundary_data(bc))
 end
 
-struct DirichletCondition{T1,T2} <: BoundaryCondition{T1,T2}
+struct DirichletCondition{T1,T2} <: BoundaryCondition{T2}
     data::T1
-    id::T2 # REVIEW: This field not needed since BoundaryId are usually type parameters?
+    function DirichletCondition(data, id)
+        return new{typeof(data),typeof(id)}(data)
+    end
 end
-id(bc::DirichletCondition) = bc.id
-data(bc::DirichletCondition) = bc.data
+boundary_data(bc::DirichletCondition) = bc.data
 
-struct NeumannCondition{T1,T2} <: BoundaryCondition{T1,T2}
+struct NeumannCondition{T1,T2} <: BoundaryCondition{T2}
     data::T1
-    id::T2 # REVIEW: This field not needed since BoundaryId are usually type parameters?
+    function NeumannCondition(data, id)
+        return new{typeof(data),typeof(id)}(data)
+    end
 end
-id(bc::NeumannCondition) = bc.id
-data(bc::NeumannCondition) = bc.data
+boundary_data(bc::NeumannCondition) = bc.data
 
--- a/src/BoundaryConditions/sat.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/BoundaryConditions/sat.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -4,22 +4,21 @@
 The tensor and boundary operator used to construct a simultaneous-approximation-term
 for imposing `bc` related to `op`.
 
-For `sat_op, L  = sat_tensors(...)` then `SAT = sat_op*(L*u - g)`  where `g` 
+For `penalty_tensor, L  = sat_tensors(...)` then `SAT = penalty_tensor*(L*u - g)`  where `g` 
 is the boundary data.
 """
 function sat_tensors end
-# REVIEW: Rename `sat_op` to `penalty_tensor`?
 
 
 """
-    sat(op, grid, bc::BoundaryCondition, params...)
+    sat(op, grid, bc::BoundaryCondition; kwargs...)
 
 Simultaneous-Approximation-Term for a general `bc` to `op`. 
 Returns a function `SAT(u,g)` weakly imposing `bc` when added to `op*u`.
 
 `op` must implement the function `sat_tensors`.
 """
-function sat(op, grid, bc::BoundaryCondition, params...)
-    sat_op, L = sat_tensors(op, grid, bc, params...)
-    return SAT(u, g) = sat_op*(L*u - g)
+function sat(op, grid, bc::BoundaryCondition; kwargs...)
+    penalty_tensor, L = sat_tensors(op, grid, bc; kwargs...)
+    return SAT(u, g) = penalty_tensor*(L*u - g)
 end
--- a/src/Grids/Grids.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/Grids/Grids.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -18,7 +18,6 @@
 export eval_on
 export componentview
 export ArrayComponentView
-export orthogonal_grid
 
 export BoundaryIdentifier
 export TensorGridBoundary
--- a/src/Grids/equidistant_grid.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/Grids/equidistant_grid.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -52,7 +52,6 @@
 boundary_grid(g::EquidistantGrid, id::Upper) = ZeroDimGrid(g[end])
 boundary_indices(g::EquidistantGrid, id::Lower) = (1,)
 boundary_indices(g::EquidistantGrid, id::Upper) = (length(g),)
-orthogonal_grid(g::EquidistantGrid, id::Union{Lower,Upper}) = g
 
 """
     refine(g::EquidistantGrid, r::Int)
--- a/src/Grids/grid.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/Grids/grid.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -127,15 +127,6 @@
 """
 function boundary_indices end
 
-"""
-    orthogonal_grid(g::Grid, id::BoundaryIdentifier)
-
-The grid for the coordinate direction orthogonal to that of the boundary
-with given id
-"""
-function orthogonal_grid end
-
-function boundary_indices end
 
 """
     eval_on(g::Grid, f)
--- a/src/Grids/tensor_grid.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/Grids/tensor_grid.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -95,8 +95,6 @@
     return LazyTensors.concatenate_tuples(b_ind...)
 end
 
-orthogonal_grid(g::TensorGrid, id::BoundaryIdentifier) = g.grids[grid_id(id)] # REVIEW: Seems clearer to me to just inline this and remove the function definition?
-
 
 function combined_coordinate_vector_type(coordinate_types...)
     combined_coord_length = mapreduce(_ncomponents, +, coordinate_types)
--- a/src/SbpOperators/operators/standard_diagonal.toml	Wed May 29 23:28:33 2024 +0200
+++ b/src/SbpOperators/operators/standard_diagonal.toml	Sat Jun 01 17:39:54 2024 -0700
@@ -39,7 +39,7 @@
         {s = [["2", "-1", "0"],["-3", "1",   "0"],["1","0","0"]], c = 1},
 ]
 
-Positivity.D2 = {theta_H = "1/2", theta_M = "0.3636363636", theta_R = "1.000000538455350", m_b = "2"}
+D2.positivity = {theta_M = "0.3636363636", theta_R = "1.000000538455350", m_b = "2"}
 
 [[stencil_set]]
 
@@ -137,7 +137,7 @@
     ]}
 ]
 
-Positivity.D2 = {theta_H = "17/48", theta_M = "0.2505765857", theta_R = "0.577587500088313", m_b = "4"} # REVIEW: Shouldn't the keys be reversed here (D2.positivity instead of Positivity.D2)
+D2.positivity = {theta_M = "0.2505765857", theta_R = "0.577587500088313", m_b = "4"}
 
 [[stencil_set]]
 
@@ -153,4 +153,4 @@
 e.closure = ["1"]
 d1.closure = ["-25/12", "4", "-3", "4/3", "-1/4"]
 
-Positivity.D2 = {theta_H = "13649/43200", theta_M = "0.1878687080", theta_R = "0.3697", m_b = "7"}
+D2.positivity = {theta_M = "0.1878687080", theta_R = "0.3697", m_b = "7"}
--- a/src/SbpOperators/volumeops/laplace/laplace.jl	Wed May 29 23:28:33 2024 +0200
+++ b/src/SbpOperators/volumeops/laplace/laplace.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -53,28 +53,26 @@
 end
 laplace(g::EquidistantGrid, stencil_set) = second_derivative(g, stencil_set)
 
-# REVIEW: I think the handling of tuning parameters below should be through kwargs instead.
 
 """
-sat_tensors(Δ::Laplace, g::Grid, bc::DirichletCondition, tuning)
+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
+function BoundaryConditions.sat_tensors(Δ::Laplace, g::Grid, bc::DirichletCondition; tuning = (1., 1.))
+    id = boundary(bc)
     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
+    penalty_tensor = H⁻¹∘(d' - B*e')∘Hᵧ
+    return penalty_tensor, e
 end
-BoundaryConditions.sat_tensors(Δ::Laplace, g::Grid, bc::DirichletCondition) = BoundaryConditions.sat_tensors(Δ, g, bc, (1.,1.)) # REVIEW: Should be possible to replace this with argument default values.
 
 """
 sat_tensors(Δ::Laplace, g::Grid, bc::NeumannCondition)
@@ -85,21 +83,23 @@
 See also: [`sat`,`NeumannCondition`](@ref).
 """
 function BoundaryConditions.sat_tensors(Δ::Laplace, g::Grid, bc::NeumannCondition)
-    id = bc.id
+    id = boundary(bc)
     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)
 
-    sat_op = -H⁻¹∘e'∘Hᵧ
-    return sat_op, d
+    penalty_tensor = -H⁻¹∘e'∘Hᵧ
+    return penalty_tensor, d
 end
 
-# REVIEW: This function assumes a TensorGrid right? In that case there should probably be a type annotation to get clearer error messages.
-function positivity_decomposition(Δ::Laplace, g::Grid, bc::DirichletCondition, tuning)
+# TODO: We should consider implementing a proper BoundaryIdentifier for EquidistantGrid and then
+# change bc::BoundaryCondition to id::BoundaryIdentifier
+
+function positivity_decomposition(Δ::Laplace, g::EquidistantGrid, bc::BoundaryCondition, tuning)
     pos_prop = positivity_properties(Δ)
-    h = spacing(orthogonal_grid(g, bc.id))
+    h = spacing(g)
     θ_H = pos_prop.theta_H
     τ_H = tuning[1]*ndims(g)/(h*θ_H)
     θ_R = pos_prop.theta_R
@@ -108,4 +108,19 @@
     return B
 end
 
-positivity_properties(Δ::Laplace) = parse_named_tuple(Δ.stencil_set["Positivity"]["D2"]) # REVIEW: Can this function extract theta_H from the inner product instead of storing it twice in the TOML?
+function positivity_decomposition(Δ::Laplace, g::TensorGrid, bc::BoundaryCondition, tuning)
+    pos_prop = positivity_properties(Δ)
+    h = spacing(g.grids[grid_id(boundary(bc))]) # grid spacing of the 1D grid normal to the boundary
+    θ_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
+
+function positivity_properties(Δ::Laplace)
+    D2_pos_prop = parse_named_tuple(Δ.stencil_set["D2"]["positivity"])
+    H_closure = parse_tuple(Δ.stencil_set["H"]["closure"])
+    return merge(D2_pos_prop, (theta_H = H_closure[1],))
+end
--- a/test/BoundaryConditions/boundary_condition_test.jl	Wed May 29 23:28:33 2024 +0200
+++ b/test/BoundaryConditions/boundary_condition_test.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -2,6 +2,7 @@
 
 using Sbplib.BoundaryConditions
 using Sbplib.Grids
+using Sbplib.RegionIndices
 
 @testset "BoundaryCondition" begin
     grid_1d = equidistant_grid(0.0, 1.0, 11)
@@ -13,13 +14,28 @@
 
     g = 3.14
     f(x,y,z) = x^2+y^2+z^2
-    @test DirichletCondition(g,id_l) isa BoundaryCondition{Float64}
-    @test DirichletCondition(g,id_n) isa BoundaryCondition{Float64}
-    @test NeumannCondition(f,id_b) isa BoundaryCondition{<:Function}
+    @testset "Constructors" begin
+        @test DirichletCondition(g,id_l) isa BoundaryCondition{Lower}
+        @test DirichletCondition(g,id_n) isa BoundaryCondition{CartesianBoundary{2,Upper}}
+        @test DirichletCondition(g,id_l) isa DirichletCondition{Float64,Lower}
+        @test NeumannCondition(f,id_b) isa NeumannCondition{<:Function}
+    end
+
+    @testset "boundary" begin
+        @test boundary(DirichletCondition(g,id_l)) == id_l
+        @test boundary(NeumannCondition(f,id_b)) == id_b
+    end
 
-    @test fill(g) ≈ discretize_data(grid_1d,DirichletCondition(g,id_l))
-    @test g*ones(11,1) ≈ discretize_data(grid_2d,DirichletCondition(g,id_n))
-    X = repeat(0:1/10:1, inner = (1,15))
-    Y = repeat(0:1/14:1, outer = (1,11))
-    @test map((x,y)->f(x,y,0), X,Y') ≈ discretize_data(grid_3d,NeumannCondition(f,id_b))
+    @testset "boundary_data" begin
+        @test boundary_data(DirichletCondition(g,id_l)) == g
+        @test boundary_data(NeumannCondition(f,id_b)) == f
+    end
+
+    @testset "discretize_data" begin
+        @test fill(g) ≈ discretize_data(grid_1d,DirichletCondition(g,id_l))
+        @test g*ones(11,1) ≈ discretize_data(grid_2d,DirichletCondition(g,id_n))
+        X = repeat(0:1/10:1, inner = (1,15))
+        Y = repeat(0:1/14:1, outer = (1,11))
+        @test map((x,y)->f(x,y,0), X,Y') ≈ discretize_data(grid_3d,NeumannCondition(f,id_b))
+    end
 end
--- a/test/BoundaryConditions/sat_test.jl	Wed May 29 23:28:33 2024 +0200
+++ b/test/BoundaryConditions/sat_test.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -10,16 +10,16 @@
 
 struct MockOp end
 
-function BoundaryConditions.sat_tensors(op::MockOp, g::Grid, bc::DirichletCondition)
-    e = boundary_restriction(g, stencil_set, id(bc))
-    L = e
+function BoundaryConditions.sat_tensors(op::MockOp, g::Grid, bc::DirichletCondition; a = 1.)
+    e = boundary_restriction(g, stencil_set, boundary(bc))
+    L = a*e
     sat_op = e'
     return sat_op, L
 end
 
 function BoundaryConditions.sat_tensors(op::MockOp, g::Grid, bc::NeumannCondition)
-    e = boundary_restriction(g, stencil_set, id(bc))
-    d = normal_derivative(g, stencil_set, id(bc))
+    e = boundary_restriction(g, stencil_set, boundary(bc))
+    d = normal_derivative(g, stencil_set, boundary(bc))
     L = d
     sat_op = e'
     return sat_op, L
@@ -54,10 +54,10 @@
         @test SAT_W(u, g_W) ≈ r_W atol = 1e-13
 
         dc_E = DirichletCondition(2, E)
-        SAT_E = sat(op, grid, dc_E)
+        SAT_E = sat(op, grid, dc_E; a = 2.)
         g_E = discretize_data(grid, dc_E)
         r_E = zeros(size(grid))
-        r_E[end,:] .= map(y -> ((1. + y^2)-2.), range(0., 1., length=13))
+        r_E[end,:] .= map(y -> (2*(1. + y^2)-2.), range(0., 1., length=13))
         @test SAT_E(u, g_E) ≈ r_E atol = 1e-13
 
         nc_S = NeumannCondition(.0, S)
--- a/test/Grids/equidistant_grid_test.jl	Wed May 29 23:28:33 2024 +0200
+++ b/test/Grids/equidistant_grid_test.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -79,12 +79,6 @@
 
     end
 
-    @testset "orthogonal_grid" begin
-        g = EquidistantGrid(0:0.1:1)
-        @test orthogonal_grid(g, Lower()) == g
-        @test orthogonal_grid(g, Upper()) == g
-    end
-
     @testset "refine" begin
         g = EquidistantGrid(0:0.1:1)
         @test refine(g, 1) == g
--- a/test/Grids/tensor_grid_test.jl	Wed May 29 23:28:33 2024 +0200
+++ b/test/Grids/tensor_grid_test.jl	Sat Jun 01 17:39:54 2024 -0700
@@ -185,16 +185,6 @@
         @test boundary_indices(TensorGrid(g₄,g₁), TensorGridBoundary{2, Upper}()) == (11,)
     end
 
-    @testset "orthogonal_grid" begin
-        g₁ = EquidistantGrid(range(0,1,length=11))
-        g₂ = EquidistantGrid(range(2,3,length=6))
-        g₃ = EquidistantGrid(range(4,5,length=7))
-    
-        @test orthogonal_grid(TensorGrid(g₁, g₂, g₃), TensorGridBoundary{1, Lower}()) == g₁
-        @test orthogonal_grid(TensorGrid(g₁, g₂, g₃), TensorGridBoundary{2, Lower}()) == g₂
-        @test orthogonal_grid(TensorGrid(g₁, g₂, g₃), TensorGridBoundary{3, Upper}()) == g₃
-    end
-
 end
 
 @testset "combined_coordinate_vector_type" begin