Mercurial > repos > public > sbplib_julia
changeset 1591:615eeb6e662e feature/boundary_conditions
Merge with default
author | Vidar Stiernström <vidar.stiernstrom@it.uu.se> |
---|---|
date | Tue, 23 Jan 2024 20:48:25 +0100 |
parents | e96ee7d7ac9c (diff) 8ef207e4bc87 (current diff) |
children | d68d02dd882f |
files | |
diffstat | 11 files changed, 288 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/Notes.md Tue Jan 23 20:39:06 2024 +0100 +++ b/Notes.md Tue Jan 23 20:48:25 2024 +0100 @@ -1,5 +1,23 @@ # Notes +## Boundary Conditions and SATs + +Types for boundary conditions: + + * abstract type `BoundaryData` + * abstract type `BoundaryCondition{T<:BoundaryData}` + * concrete types `ConstantBoundaryData <: BoundaryData` and similar + * concrete types `NeumannCondition{BD<:BoundaryData} <: BoundaryCondition{BD}` and similar +The concrete `BoundaryData` subtypes are "thin types" wrapping the boundary data, and are used to indicate how the boundary data should be used in e.g. sat routines. The concrete `BoundaryCondition{BD}` subtypes are used for assembling the tensors used to construct e.g. a SAT. + +SAT methods: +There are multiple options for what the SAT methods could return. +* (Current) a function which returns a `LazyTensorApplication`, e.g. `f = sat(grid,op,bc)`. The the resulting `LazyTensorApplication` can then be added to scheme i.e. `scheme = op*u + f(u)`. Depdending on the type of data in the BC, e.g. time-depdendent etc one can return f(u,t). +* `LazyTensor`s `closure, penalty = sat(grid,op,bc)` like in the matlab version. Probably the most general one. Up to the user to make use of the returned `LazyTensor`s. One can for example then easily include the closures to the operator and have eg. `D = (op + closure)*u`. +* A `LazyTensor` for closure, and a `LazyArray` for `penalty*data`. Mix of the above. +* Same as first but of the form sat = `sat_op*(L*u-g)`. This is how one typically would write the SAT in the litterature. The function `sat_tensors` would return `sat_op` and `L`. Need to get compositions working before we can implement this approach. + + ## Reading operators Jonatan's suggestion is to add methods to `Laplace`, `SecondDerivative` and
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BoundaryConditions/BoundaryConditions.jl Tue Jan 23 20:48:25 2024 +0100 @@ -0,0 +1,21 @@ +module BoundaryConditions + + +export BoundaryCondition +export discretize_data +export data +export id + +export NeumannCondition +export DirichletCondition + +export sat +export sat_tensors + +using Sbplib.Grids +using Sbplib.LazyTensors + +include("boundary_condition.jl") +include("sat.jl") + +end # module
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BoundaryConditions/boundary_condition.jl Tue Jan 23 20:48:25 2024 +0100 @@ -0,0 +1,47 @@ +""" + BoundaryCondition + +A type for implementing 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 + +""" + id(::BoundaryCondition) + +The boundary identifier of the BoundaryCondition. +Must be implemented by subtypes. +""" +function id end + +""" + data(::BoundaryCondition) + +If implemented, the data associated with the BoundaryCondition +""" +function data end + +""" + discretize(grid, bc::BoundaryCondition) + +The grid function obtained from discretizing the `bc` 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)) +end + +struct DirichletCondition{T1,T2} <: BoundaryCondition{T1,T2} + data::T1 + id::T2 +end +id(bc::DirichletCondition) = bc.id +data(bc::DirichletCondition) = bc.data + +struct NeumannCondition{T1,T2} <: BoundaryCondition{T1,T2} + data::T1 + id::T2 +end +id(bc::NeumannCondition) = bc.id +data(bc::NeumannCondition) = bc.data +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BoundaryConditions/sat.jl Tue Jan 23 20:48:25 2024 +0100 @@ -0,0 +1,24 @@ +""" + sat_tensors(op, grid, bc::BoundaryCondition, params...) + +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` +is the boundary data. +""" +function sat_tensors end + + +""" + sat(op, grid, bc::BoundaryCondition, params...) + +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) +end \ No newline at end of file
--- a/src/LazyTensors/lazy_tensor_operations.jl Tue Jan 23 20:39:06 2024 +0100 +++ b/src/LazyTensors/lazy_tensor_operations.jl Tue Jan 23 20:48:25 2024 +0100 @@ -121,6 +121,7 @@ Base.:*(a::T, tm::LazyTensor{T}) where T = TensorComposition(ScalingTensor{T,range_dim(tm)}(a,range_size(tm)), tm) Base.:*(tm::LazyTensor{T}, a::T) where T = a*tm +Base.:-(tm::LazyTensor{T}) where T = (-one(T))*tm """ InflatedTensor{T,R,D} <: LazyTensor{T,R,D}
--- a/src/SbpOperators/SbpOperators.jl Tue Jan 23 20:39:06 2024 +0100 +++ b/src/SbpOperators/SbpOperators.jl Tue Jan 23 20:48:25 2024 +0100 @@ -26,6 +26,7 @@ using Sbplib.RegionIndices using Sbplib.LazyTensors using Sbplib.Grids +using Sbplib.BoundaryConditions @enum Parity begin odd = -1
--- a/src/SbpOperators/volumeops/laplace/laplace.jl Tue Jan 23 20:39:06 2024 +0100 +++ b/src/SbpOperators/volumeops/laplace/laplace.jl Tue Jan 23 20:48:25 2024 +0100 @@ -52,3 +52,25 @@ return Δ end laplace(g::EquidistantGrid, stencil_set) = second_derivative(g, stencil_set) + +# TODO: Add sat_tensor for Diirichlet condition + +""" +sat_tensors(Δ::Laplace, g::Grid, bc::NeumannCondition) + +The operators required to construct the SAT for imposing Neumann condition + + +See also: [`sat`,`NeumannCondition`](@ref). +""" +function BoundaryConditions.sat_tensors(Δ::Laplace, g::Grid, bc::NeumannCondition) + 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) + + sat_op = H⁻¹∘e'∘Hᵧ + return sat_op, d +end
--- a/src/Sbplib.jl Tue Jan 23 20:39:06 2024 +0100 +++ b/src/Sbplib.jl Tue Jan 23 20:48:25 2024 +0100 @@ -4,6 +4,7 @@ include("RegionIndices/RegionIndices.jl") include("LazyTensors/LazyTensors.jl") include("Grids/Grids.jl") +include("BoundaryConditions/BoundaryConditions.jl") include("SbpOperators/SbpOperators.jl") include("DiffOps/DiffOps.jl") @@ -12,5 +13,6 @@ export Grids export SbpOperators export DiffOps +export BoundaryConditions end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/BoundaryConditions/boundary_condition_test.jl Tue Jan 23 20:48:25 2024 +0100 @@ -0,0 +1,25 @@ +using Test + +using Sbplib.BoundaryConditions +using Sbplib.Grids + +@testset "BoundaryCondition" begin + grid_1d = equidistant_grid(11, 0.0, 1.0) + grid_2d = equidistant_grid((11,15), (0.0, 0.0), (1.0,1.0)) + grid_3d = equidistant_grid((11,15,13), (0.0, 0.0, 0.0), (1.0,1.0, 1.0)) + (id_l,_) = boundary_identifiers(grid_1d) + (_,_,_,id_n) = boundary_identifiers(grid_2d) + (_,_,_,_,id_b,_) = boundary_identifiers(grid_3d) + + 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} + + @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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/BoundaryConditions/sat_test.jl Tue Jan 23 20:48:25 2024 +0100 @@ -0,0 +1,80 @@ +using Test + + +using Sbplib.BoundaryConditions +using Sbplib.Grids +using Sbplib.LazyTensors +using Sbplib.SbpOperators + +stencil_set = read_stencil_set(sbp_operators_path()*"standard_diagonal.toml"; order = 4) + +struct MockOp end + +function BoundaryConditions.sat_tensors(op::MockOp, g::Grid, bc::DirichletCondition) + e = boundary_restriction(g, stencil_set, id(bc)) + L = e + sat_op = e' + return sat_op, L +end + +function BoundaryConditions.sat_tensors(op::MockOp, g::Grid, bc::DirichletCondition, a) + e = boundary_restriction(g, stencil_set, id(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)) + L = d + sat_op = e' + return sat_op, L +end + +@testset "sat" begin + op = MockOp() + @testset "1D" begin + grid = equidistant_grid(11, 0., 1.) + l, r = boundary_identifiers(grid) + u = eval_on(grid, x-> 1. + 2x^2) + dc = DirichletCondition(1.0, l) + g_l = discretize_data(grid, dc) + SAT_l = sat(op, grid, dc) + @test SAT_l(u, g_l) ≈ zeros((size(grid))) atol = 1e-13 + + nc = NeumannCondition(4.0, r) + g_r = discretize_data(grid, nc) + SAT_r = sat(op, grid, nc) + @test SAT_r(u, g_r) ≈ zeros((size(grid))) atol = 1e-13 + end + @testset "2D" begin + grid = equidistant_grid((11,13), (0.,0.), (1.,1.)) + W, E, S, N = boundary_identifiers(grid) + u = eval_on(grid, (x,y) -> x+y^2) + + dc_W = DirichletCondition(1.0, W) + SAT_W = sat(op, grid, dc_W) + g_W = discretize_data(grid, dc_W) + r_W = zeros(size(grid)) + r_W[1,:] .= map(y -> (y^2-1.), range(0., 1., length=13)) + @test SAT_W(u, g_W) ≈ r_W atol = 1e-13 + + dc_E = DirichletCondition(2, E) + SAT_E = sat(op, grid, dc_E, 2.) + g_E = discretize_data(grid, dc_E) + r_E = zeros(size(grid)) + 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) + SAT_S = sat(op, grid, nc_S) + g_S = discretize_data(grid, nc_S) + @test SAT_S(u, g_S) ≈ zeros(size(grid)) atol = 1e-13 + + nc_N = NeumannCondition(2.0, N) + SAT_N = sat(op, grid, nc_N) + g_N = discretize_data(grid, nc_N) + @test SAT_N(u, g_N) ≈ zeros(size(grid)) atol = 1e-13 + end +end
--- a/test/SbpOperators/volumeops/laplace/laplace_test.jl Tue Jan 23 20:39:06 2024 +0100 +++ b/test/SbpOperators/volumeops/laplace/laplace_test.jl Tue Jan 23 20:48:25 2024 +0100 @@ -3,6 +3,7 @@ using Sbplib.SbpOperators using Sbplib.Grids using Sbplib.LazyTensors +using Sbplib.BoundaryConditions @testset "Laplace" begin # Default stencils (4th order) @@ -88,3 +89,49 @@ end end +@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) + + 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)) + + + @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 + end +end +