Mercurial > repos > public > sbplib_julia
changeset 1164:d26aef8a5987 feature/boundary_conditions
Add types for different kinds of boundary data functions to discretize the data on the grid. Add tests
author | Vidar Stiernström <vidar.stiernstrom@it.uu.se> |
---|---|
date | Wed, 07 Dec 2022 21:39:07 +0100 |
parents | c9fdfb1efba8 |
children | fd80e9a0ef99 |
files | src/BoundaryConditions/BoundaryConditions.jl src/BoundaryConditions/boundary_condition.jl test/BoundaryConditions/boundary_condition_test.jl |
diffstat | 3 files changed, 198 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/src/BoundaryConditions/BoundaryConditions.jl Wed Oct 12 10:45:47 2022 +0200 +++ b/src/BoundaryConditions/BoundaryConditions.jl Wed Dec 07 21:39:07 2022 +0100 @@ -1,10 +1,12 @@ module BoundaryConditions -export BoundaryDataType +export BoundaryData export ConstantBoundaryData export SpaceDependentBoundaryData export TimeDependentBoundaryData -export SpaceDependentBoundaryData +export SpaceTimeDependentBoundaryData +export ZeroBoundaryData +export discretize export BoundaryCondition export NeumannCondition @@ -12,6 +14,7 @@ export RobinCondition export data + export sat export sat_tensors
--- a/src/BoundaryConditions/boundary_condition.jl Wed Oct 12 10:45:47 2022 +0200 +++ b/src/BoundaryConditions/boundary_condition.jl Wed Dec 07 21:39:07 2022 +0100 @@ -1,26 +1,97 @@ +# TODO: Should BoundaryData just be used for traits +# of the BoundaryConditions? Seems like one then could move the +# the boundary data value val directly to BoundaryCondition +# Not sure how one would do this tho. """ - BoundaryDataType + BoundaryData A type for storing boundary data, e.g. constant, space-dependent, time-dependent etc. -Subtypes of `BoundaryDataType` should store the boundary data in a field `val`, i.e. -`struct MyBoundaryDataType{T} <: BoundaryDataType val::T end`. +Subtypes of `BoundaryData` should store the boundary data in a field `val`. The exception +to this is ZeroBoundaryData. +""" +abstract type BoundaryData end + +""" + ConstantBoundaryData + +`val` is a scalar value of type T +""" +struct ConstantBoundaryData{T<:Number} <: BoundaryData + val::T +end + """ -abstract type BoundaryDataType end + SpaceDependentBoundaryData -struct ConstantBoundaryData{T} <: BoundaryDataType +`val` is a function of dimensionality equal to the boundary +""" +struct SpaceDependentBoundaryData{T<:Function} <: BoundaryData + val::T +end + +""" + TimeDependentBoundaryData + +`val` is a scalar function val(t) +""" +struct TimeDependentBoundaryData{T<:Function} <: BoundaryData val::T end -struct SpaceDependentBoundaryData{T} <: BoundaryDataType +""" + SpaceTimeDependentBoundaryData + +`val` is a timedependent function returning the spacedependent + boundary data at a specific time. For instance, if f(t,x) + is the function describing the spacetimedependent boundary data then + val(t*) returns the function g(x) = f(t*,x...) +""" +struct SpaceTimeDependentBoundaryData{T<:Function} <: BoundaryData val::T + + function SpaceTimeDependentBoundaryData(f::Function) + val(t) = (args...) -> f(t,args...) + return new{typeof(val)}(val) + end end -struct TimeDependentBoundaryData{T} <: BoundaryDataType - val::T +""" + ZeroBoundaryData +""" +struct ZeroBoundaryData <: BoundaryData end + + +""" + discretize(::BoundaryData, boundary_grid) + +Returns an anonymous time-dependent function f, such that f(t) is +a `LazyArray` holding the `BoundaryData` discretized on `boundary_grid`. +""" +# TODO: Is the return type of discretize really a good interface +# for the boundary data? +# Moreover, instead of explicitly converting to a LazyArray here +# should we defer this to evalOn (and extend evalOn for scalars as well)? +# I.e. if evalOn returns a LazyArray, the boundary data is lazy. Otherwise +# it is preallocated. + +function discretize(bd::ConstantBoundaryData, boundary_grid) + return t -> LazyTensors.LazyConstantArray(bd.val, size(boundary_grid)) end -struct SpaceTimeDependentBoundaryData{T} <: BoundaryDataType - val::T +function discretize(bd::TimeDependentBoundaryData, boundary_grid) + return t -> LazyTensors.LazyConstantArray(bd.val(t), size(boundary_grid)) +end + +function discretize(bd::SpaceDependentBoundaryData, boundary_grid) + return t -> evalOn(boundary_grid, bd.val) +end + +function discretize(bd::SpaceTimeDependentBoundaryData, boundary_grid) + return t -> evalOn(boundary_grid, bd.val(t)) +end + +function discretize(::ZeroBoundaryData, boundary_grid) + return t -> LazyTensors.LazyConstantArray(zero(eltype(boundary_grid)), size(boundary_grid)) end """ @@ -29,24 +100,22 @@ 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. """ -# TODO: Parametrize the boundary id as well? -abstract type BoundaryCondition{T<:BoundaryDataType} end +abstract type BoundaryCondition{T<:BoundaryData} end + +""" + data(::BoundaryCondition) -data(bc::BoundaryCondition) = bc.data.val +Returns the data stored by the `BoundaryCondition`. +""" +data(bc::BoundaryCondition) = bc.data + -struct NeumannCondition{BDT<:BoundaryDataType} <: BoundaryCondition{BDT} - id::BoundaryIdentifier - data::BDT +struct NeumannCondition{BD<:BoundaryData} <: BoundaryCondition{BD} + data::BD + id::BoundaryIdentifier end -struct DirichletCondition{BDT<:BoundaryDataType} <: BoundaryCondition{BDT} +struct DirichletCondition{BD<:BoundaryData} <: BoundaryCondition{BD} + data::BD id::BoundaryIdentifier - data::BDT -end - -struct RobinCondition{BDT<:BoundaryDataType,T<:Real} <: BoundaryCondition{BDT} - id::BoundaryIdentifier - data::BDT - α::T - β::T end \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/BoundaryConditions/boundary_condition_test.jl Wed Dec 07 21:39:07 2022 +0100 @@ -0,0 +1,99 @@ +using Test + +using Sbplib.BoundaryConditions +using Sbplib.Grids + +grid_1D = EquidistantGrid(11, 0.0, 1.0) +grid_2D = EquidistantGrid((11,15), (0.0, 0.0), (1.0,1.0)) +grid_3D = EquidistantGrid((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) + +@testset "BoundaryData" begin + + @testset "ConstantBoundaryData" begin + c = float(pi) + @test ConstantBoundaryData(c) isa BoundaryData + g_1D = discretize(ConstantBoundaryData(c),boundary_grid(grid_1D, id_l)) + g_2D = discretize(ConstantBoundaryData(c),boundary_grid(grid_2D, id_n)) + @test g_1D isa Function + @test g_2D isa Function + @test g_1D(0.) == fill(c) + @test g_2D(2.) == c*ones(11) + @test_throws MethodError g_1D(0.,0.) + @test_throws MethodError g_2D(0.,0.) + end + + @testset "TimeDependentBoundaryData" begin + f(t) = 1. /(t+0.1) + @test TimeDependentBoundaryData(f) isa BoundaryData + g_1D = discretize(TimeDependentBoundaryData(f),boundary_grid(grid_1D, id_l)) + g_2D = discretize(TimeDependentBoundaryData(f),boundary_grid(grid_2D, id_n)) + @test g_1D isa Function + @test g_2D isa Function + @test g_1D(0.) == f(0.)*fill(1) + @test g_2D(2.) == f(2.)*ones(11) + @test_throws MethodError g_1D(0.,0.) + @test_throws MethodError g_2D(0.,0.) + end + + #TBD: Is it reasoanble to have SpaceDependentBoundaryData for 1D-grids? It would then be a constant + # which then may be represented by ConstantBoundaryData. + @testset "SpaceDependentBoundaryData" begin + f0() = 2 + f1(x) = x.^2 + f2(x,y) = x.^2 - y + @test SpaceDependentBoundaryData(f1) isa BoundaryData + g_1D = discretize(SpaceDependentBoundaryData(f0),boundary_grid(grid_1D, id_l)) + g_2D = discretize(SpaceDependentBoundaryData(f1),boundary_grid(grid_2D, id_n)) + g_3D = discretize(SpaceDependentBoundaryData(f2),boundary_grid(grid_3D, id_n)) + @test g_1D isa Function + @test g_2D isa Function + @test g_3D isa Function + @test_broken g_1D(1.) == fill(f0()) # Does not work since evalOn for f0 returns (). + @test g_2D(2.) ≈ f1.(range(0., 1., 11)) rtol=1e-14 + @test g_3D(0.) ≈ evalOn(boundary_grid(grid_3D, id_n),f2) rtol=1e-14 + @test_throws MethodError g_1D(0.,0.) + @test_throws MethodError g_2D(0.,0.) + @test_throws MethodError g_3D(0.,0.) + end + + # TBD: Include tests for 1D-grids? See TBD above + @testset "SpaceTimeDependentBoundaryData" begin + fx1(x) = x.^2 + fx2(x,y) = x.^2 - y + ft(t) = exp(t) + ftx1(t,x) = ft(t)*fx1(x) + ftx2(t,x,y) = ft(t)*fx2(x,y) + @test SpaceTimeDependentBoundaryData(ftx1) isa BoundaryData + g_2D = discretize(SpaceTimeDependentBoundaryData(ftx1),boundary_grid(grid_2D, id_n)) + g_3D = discretize(SpaceTimeDependentBoundaryData(ftx2),boundary_grid(grid_3D, id_b)) + @test g_2D isa Function + @test g_3D isa Function + @test g_2D(2.) ≈ ft(2.)*fx1.(range(0., 1., 11)) rtol=1e-14 + @test g_3D(3.14) ≈ ft(3.14)*evalOn(boundary_grid(grid_3D, id_b),fx2) rtol=1e-14 + @test_throws MethodError g_2D(0.,0.) + @test_throws MethodError g_3D(0.,0.) + end + + @testset "ZeroBoundaryData" begin + @test ZeroBoundaryData() isa BoundaryData + g_2D = discretize(ZeroBoundaryData(), boundary_grid(grid_2D, id_n)) + g_3D = discretize(ZeroBoundaryData(), boundary_grid(grid_3D, id_b)) + @test g_2D isa Function + @test g_3D isa Function + @test g_2D(2.) ≈ 0.0*range(0., 1., 11) rtol=1e-14 + f(x,y) = 0 + @test g_3D(3.14) ≈ 0.0*evalOn(boundary_grid(grid_3D, id_b), f) rtol=1e-14 + @test_throws MethodError g_2D(0.,0.) + @test_throws MethodError g_3D(0.,0.) + end +end + +@testset "BoundaryCondition" begin + g = ConstantBoundaryData(1.0) + NeumannCondition(g,id_n) isa BoundaryCondition{ConstantBoundaryData} + DirichletCondition(g,id_n) isa BoundaryCondition{ConstantBoundaryData} + @test data(NeumannCondition(g,id_n)) == g +end