changeset 780:3b29b2ff1f0e operator_storage_array_of_table

Merge in refactor/sbp_operators_method_signatures
author Jonatan Werpers <jonatan@werpers.com>
date Mon, 19 Jul 2021 08:47:33 +0200
parents bea2feebbeca (diff) fd84ba4f8742 (current diff)
children d2f4ac2be47f
files
diffstat 6 files changed, 190 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/src/SbpOperators/d2.jl	Sat Jul 17 18:14:48 2021 +0200
+++ b/src/SbpOperators/d2.jl	Mon Jul 19 08:47:33 2021 +0200
@@ -5,6 +5,8 @@
     even = 1
 end
 
+
+# TBD: Can this be deleted when this branch is finished?
 struct D2{T,M}
     innerStencil::Stencil{T}
     closureStencils::NTuple{M,Stencil{T}}
--- a/src/SbpOperators/operators/standard_diagonal.toml	Sat Jul 17 18:14:48 2021 +0200
+++ b/src/SbpOperators/operators/standard_diagonal.toml	Mon Jul 19 08:47:33 2021 +0200
@@ -1,36 +1,42 @@
 [meta]
 authors = "Ken Mattson"
-descripion = "Standard operators for equidistant grids"
+description = "Standard operators for equidistant grids"
 type = "equidistant"
+cite = "A paper a long time ago in a galaxy far far away."
 
-[order2]
+[[stencil_set]]
+
+order = 2
+
 H.inner = ["1"]
 H.closure = ["1/2"]
 
 D1.inner_stencil = ["-1/2", "0", "1/2"]
 D1.closure_stencils = [
-    ["-1", "1"],
+    {s = ["-1", "1"], c = 1},
 ]
 
 D2.inner_stencil = ["1", "-2", "1"]
 D2.closure_stencils = [
-    ["1", "-2", "1"],
+    {s = ["1", "-2", "1"], c = 1},
 ]
 
 e.closure = ["1"]
-d1.closure = ["-3/2", "2", "-1/2"]
+d1.closure = {s = ["-3/2", "2", "-1/2"], c = 1}
 
-[order4]
+[[stencil_set]]
+
+order = 4
 H.inner = ["1"]
 H.closure = ["17/48", "59/48", "43/48", "49/48"]
 
 D2.inner_stencil = ["-1/12","4/3","-5/2","4/3","-1/12"]
 D2.closure_stencils = [
-    [     "2",    "-5",      "4",       "-1",     "0",     "0"],
-    [     "1",    "-2",      "1",        "0",     "0",     "0"],
-    [ "-4/43", "59/43", "-110/43",   "59/43", "-4/43",     "0"],
-    [ "-1/49",     "0",   "59/49", "-118/49", "64/49", "-4/49"],
+    {s = [     "2",    "-5",      "4",       "-1",     "0",     "0"], c = 1},
+    {s = [     "1",    "-2",      "1",        "0",     "0",     "0"], c = 2},
+    {s = [ "-4/43", "59/43", "-110/43",   "59/43", "-4/43",     "0"], c = 3},
+    {s = [ "-1/49",     "0",   "59/49", "-118/49", "64/49", "-4/49"], c = 4},
 ]
 
 e.closure = ["1"]
-d1.closure = ["-11/6", "3", "-3/2", "1/3"]
+d1.closure = {s = ["-11/6", "3", "-3/2", "1/3"], c = 1}
--- a/src/SbpOperators/readoperator.jl	Sat Jul 17 18:14:48 2021 +0200
+++ b/src/SbpOperators/readoperator.jl	Mon Jul 19 08:47:33 2021 +0200
@@ -1,141 +1,100 @@
 using TOML
 
-export read_D2_operator
-export read_stencil
-export read_stencils
-export read_tuple
-
-export get_stencil
-export get_stencils
-export get_tuple
+export read_stencil_set
+export get_stencil_set
 
-function read_D2_operator(fn; order)
-    operators = TOML.parsefile(fn)["order$order"]
-    D2 = operators["D2"]
-    H = operators["H"]
-    e = operators["e"]
-    d1 = operators["d1"]
-
-    # Create inner stencil
-    innerStencil = get_stencil(operators, "D2", "inner_stencil")
+export parse_stencil
 
-    # Create boundary stencils
-    boundarySize = length(D2["closure_stencils"])
-    closureStencils = Vector{typeof(innerStencil)}() # TBD: is the the right way to get the correct type?
-    for i ∈ 1:boundarySize
-        closureStencils = (closureStencils..., get_stencil(operators, "D2", "closure_stencils", i; center=i))
-    end
-    # TODO: Get rid of the padding here. Any padding should be handled by the consturctor accepting the stencils.
-    eClosure = Stencil(pad_tuple(toml_string_array_to_tuple(Float64, e["closure"]), boundarySize)..., center=1)
-    dClosure = Stencil(pad_tuple(toml_string_array_to_tuple(Float64, d1["closure"]), boundarySize)..., center=1)
+# The read_stencil_set and get_stencil_set functions return the freshly parsed
+# toml. The generic code in these functions can't be expected to know anyhting
+# about how to read different stencil sets as they may contain many different
+# kinds of stecils. We should how ever add read_ and get_ functions for all
+# the types of stencils we know about.
+#
+# After getting a stencil set the user can use parse functions to parse what
+# they want from the TOML dict. I.e no more "paths".
+# Functions needed:
+#   * parse stencil
+#   * parse rational
+#
+# maybe there is a better name than parse?
+# Would be nice to be able to control the type in the stencil
 
-    q_tuple = pad_tuple(toml_string_array_to_tuple(Float64, H["closure"]), boundarySize)
-    quadratureClosure = Vector{typeof(innerStencil)}()
-    for i ∈ 1:boundarySize
-        quadratureClosure = (quadratureClosure..., Stencil(q_tuple[i], center=1))
-    end
-
-    d2 = SbpOperators.D2(
-        innerStencil,
-        closureStencils,
-        eClosure,
-        dClosure,
-        quadratureClosure,
-        even
-    )
-
-    return d2
-end
-
+# TODO: Control type for the stencil
+# TODO: Think about naming and terminology around freshly parsed TOML.
 
 """
-    read_stencil(fn, path...; [center])
+    read_stencil_set(fn; filters)
 
-Read a stencil at `path` from the file with name `fn`.
-If a center is specified the given element of the stecil is set as the center.
-
-See also: [`read_stencils`](@ref), [`read_tuple`](@ref), [`get_stencil`](@ref).
+Picks out a stencil set from the given toml file based on some filters.
+If more than one set matches the filters an error is raised.
 
-# Examples
-```
-read_stencil(sbp_operators_path()*"standard_diagonal.toml", "order2", "D2", "inner_stencil")
-read_stencil(sbp_operators_path()*"standard_diagonal.toml", "order2", "d1", "closure"; center=1)
-```
-"""
-read_stencil(fn, path...; center=nothing) = get_stencil(TOML.parsefile(fn), path...; center=center)
-
+The stencil set is not parsed beyond the inital toml parse. To get usable
+stencils use the `parse_stencil` functions on the fields of the stencil set.
 """
-    read_stencils(fn, path...; centers)
-
-Read stencils at `path` from the file `fn`.
-Centers of the stencils are specified as a tuple or array in `centers`.
-
-See also: [`read_stencil`](@ref), [`read_tuple`](@ref), [`get_stencils`](@ref).
-"""
-read_stencils(fn, path...; centers) = get_stencils(TOML.parsefile(fn), path...; centers=centers)
+read_stencil_set(fn; filters...) = get_stencil_set(TOML.parsefile(fn); filters...)
 
 """
-    read_tuple(fn, path...)
-
-Read tuple at `path` from the file `fn`.
-
-See also: [`read_stencil`](@ref), [`read_stencils`](@ref), [`get_tuple`](@ref).
-"""
-read_tuple(fn, path...) = get_tuple(TOML.parsefile(fn), path...)
-
-"""
-    get_stencil(parsed_toml, path...; center=nothing)
+    get_stencil_set(parsed_toml; filters...)
 
-Same as [`read_stencil`](@ref)) but takes already parsed toml.
+Same as `read_stencil_set` but works on already parsed TOML.
 """
-get_stencil(parsed_toml, path...; center=nothing) = get_stencil(parsed_toml[path[1]], path[2:end]...; center=center)
-function get_stencil(parsed_toml; center=nothing)
-    @assert parsed_toml isa Vector{String}
-    stencil_weights = Float64.(parse_rational.(parsed_toml))
+function get_stencil_set(parsed_toml; filters...)
+    matches = findall(parsed_toml["stencil_set"]) do set
+        for (key, val) ∈ filters
+            if set[string(key)] != val
+                return false
+            end
+        end
 
-    width = length(stencil_weights)
-
-    if isnothing(center)
-        center = div(width,2)+1
+        return true
     end
 
-    return Stencil(stencil_weights..., center=center)
+    if length(matches) != 1
+        throw(ArgumentError("filters must pick out a single stencil set"))
+    end
+
+    i = matches[1]
+    return parsed_toml["stencil_set"][i]
 end
 
 """
-    get_stencils(parsed_toml, path...; centers)
+    parse_stencil(toml)
 
-Same as [`read_stencils`](@ref)) but takes already parsed toml.
+Accepts parsed toml and reads it as a stencil
 """
-get_stencils(parsed_toml, path...; centers) = get_stencils(parsed_toml[path[1]], path[2:end]...; centers=centers)
-function get_stencils(parsed_toml; centers)
-    @assert parsed_toml isa Vector{Vector{String}}
-    @assert length(centers) == length(parsed_toml)
+function parse_stencil(toml)
+    check_stencil_toml(toml)
 
-    stencils = ()
-    for i ∈ 1:length(parsed_toml)
-        stencil = get_stencil(parsed_toml[i], center = centers[i])
-        stencils = (stencils..., stencil)
+    if toml isa Array
+        weights = Float64.(parse_rational.(toml))
+        return CenteredStencil(weights...)
     end
 
-    return stencils
+    weights = Float64.(parse_rational.(toml["s"]))
+    return Stencil(weights..., center = toml["c"])
 end
 
-"""
-    get_tuple(parsed_toml, path...)
+function check_stencil_toml(toml)
+    if !(toml isa Dict || toml isa Vector{String})
+        throw(ArgumentError("the TOML for a stecil must be a vector of strings or a table."))
+    end
+
+    if toml isa Vector{String}
+        return
+    end
 
-Same as [`read_tuple`](@ref)) but takes already parsed toml.
-"""
-get_tuple(parsed_toml, path...) = get_tuple(parsed_toml[path[1]], path[2:end]...)
-function get_tuple(parsed_toml)
-    @assert parsed_toml isa Vector{String}
-    t = Tuple(Float64.(parse_rational.(parsed_toml)))
-    return t
-end
+    if !(haskey(toml, "s") && haskey(toml, "c"))
+        throw(ArgumentError("the table form of a stencil must have fields `s` and `c`."))
+    end
 
-# TODO: Probably should be deleted once we have gotten rid of read_D2_operator()
-function toml_string_array_to_tuple(::Type{T}, arr::AbstractVector{String}) where T
-    return Tuple(T.(parse_rational.(arr)))
+    if !(toml["s"] isa Vector{String})
+        throw(ArgumentError("a stencil must be specified as a vector of strings."))
+    end
+
+    if !(toml["c"] isa Int)
+        throw(ArgumentError("the center of a stencil must be specified as an integer."))
+    end
 end
 
 function parse_rational(str)
--- a/test/SbpOperators/boundaryops/boundary_restriction_test.jl	Sat Jul 17 18:14:48 2021 +0200
+++ b/test/SbpOperators/boundaryops/boundary_restriction_test.jl	Mon Jul 19 08:47:33 2021 +0200
@@ -8,27 +8,28 @@
 import Sbplib.SbpOperators.BoundaryOperator
 
 @testset "boundary_restriction" begin
-    op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
+	stencil_set = read_stencil_set(sbp_operators_path()*"standard_diagonal.toml"; order = 4)
+	e_closure = parse_stencil(stencil_set["e"]["closure"])
     g_1D = EquidistantGrid(11, 0.0, 1.0)
     g_2D = EquidistantGrid((11,15), (0.0, 0.0), (1.0,1.0))
 
     @testset "boundary_restriction" begin
         @testset "1D" begin
-            e_l = boundary_restriction(g_1D,op.eClosure,Lower())
-            @test e_l == boundary_restriction(g_1D,op.eClosure,CartesianBoundary{1,Lower}())
-            @test e_l == BoundaryOperator(g_1D,op.eClosure,Lower())
+            e_l = boundary_restriction(g_1D,e_closure,Lower())
+            @test e_l == boundary_restriction(g_1D,e_closure,CartesianBoundary{1,Lower}())
+            @test e_l == BoundaryOperator(g_1D,e_closure,Lower())
             @test e_l isa BoundaryOperator{T,Lower} where T
             @test e_l isa TensorMapping{T,0,1} where T
 
-            e_r = boundary_restriction(g_1D,op.eClosure,Upper())
-            @test e_r == boundary_restriction(g_1D,op.eClosure,CartesianBoundary{1,Upper}())
-            @test e_r == BoundaryOperator(g_1D,op.eClosure,Upper())
+            e_r = boundary_restriction(g_1D,e_closure,Upper())
+            @test e_r == boundary_restriction(g_1D,e_closure,CartesianBoundary{1,Upper}())
+            @test e_r == BoundaryOperator(g_1D,e_closure,Upper())
             @test e_r isa BoundaryOperator{T,Upper} where T
             @test e_r isa TensorMapping{T,0,1} where T
         end
 
         @testset "2D" begin
-            e_w = boundary_restriction(g_2D,op.eClosure,CartesianBoundary{1,Upper}())
+            e_w = boundary_restriction(g_2D,e_closure,CartesianBoundary{1,Upper}())
             @test e_w isa InflatedTensorMapping
             @test e_w isa TensorMapping{T,1,2} where T
         end
@@ -36,8 +37,8 @@
 
     @testset "Application" begin
         @testset "1D" begin
-            e_l = boundary_restriction(g_1D, op.eClosure, CartesianBoundary{1,Lower}())
-            e_r = boundary_restriction(g_1D, op.eClosure, CartesianBoundary{1,Upper}())
+            e_l = boundary_restriction(g_1D, e_closure, CartesianBoundary{1,Lower}())
+            e_r = boundary_restriction(g_1D, e_closure, CartesianBoundary{1,Upper}())
 
             v = evalOn(g_1D,x->1+x^2)
             u = fill(3.124)
@@ -48,10 +49,10 @@
         end
 
         @testset "2D" begin
-            e_w = boundary_restriction(g_2D, op.eClosure, CartesianBoundary{1,Lower}())
-            e_e = boundary_restriction(g_2D, op.eClosure, CartesianBoundary{1,Upper}())
-            e_s = boundary_restriction(g_2D, op.eClosure, CartesianBoundary{2,Lower}())
-            e_n = boundary_restriction(g_2D, op.eClosure, CartesianBoundary{2,Upper}())
+            e_w = boundary_restriction(g_2D, e_closure, CartesianBoundary{1,Lower}())
+            e_e = boundary_restriction(g_2D, e_closure, CartesianBoundary{1,Upper}())
+            e_s = boundary_restriction(g_2D, e_closure, CartesianBoundary{2,Lower}())
+            e_n = boundary_restriction(g_2D, e_closure, CartesianBoundary{2,Upper}())
 
             v = rand(11, 15)
             u = fill(3.124)
--- a/test/SbpOperators/boundaryops/normal_derivative_test.jl	Sat Jul 17 18:14:48 2021 +0200
+++ b/test/SbpOperators/boundaryops/normal_derivative_test.jl	Mon Jul 19 08:47:33 2021 +0200
@@ -11,21 +11,21 @@
     g_1D = EquidistantGrid(11, 0.0, 1.0)
     g_2D = EquidistantGrid((11,12), (0.0, 0.0), (1.0,1.0))
     @testset "normal_derivative" begin
-        op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
+    	stencil_set = read_stencil_set(sbp_operators_path()*"standard_diagonal.toml"; order=4)
+    	d_closure = parse_stencil(stencil_set["d1"]["closure"])
         @testset "1D" begin
-            d_l = normal_derivative(g_1D, op.dClosure, Lower())
-            @test d_l == normal_derivative(g_1D, op.dClosure, CartesianBoundary{1,Lower}())
+            d_l = normal_derivative(g_1D, d_closure, Lower())
+            @test d_l == normal_derivative(g_1D, d_closure, CartesianBoundary{1,Lower}())
             @test d_l isa BoundaryOperator{T,Lower} where T
             @test d_l isa TensorMapping{T,0,1} where T
         end
         @testset "2D" begin
-            op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
-            d_w = normal_derivative(g_2D, op.dClosure, CartesianBoundary{1,Lower}())
-            d_n = normal_derivative(g_2D, op.dClosure, CartesianBoundary{2,Upper}())
+            d_w = normal_derivative(g_2D, d_closure, CartesianBoundary{1,Lower}())
+            d_n = normal_derivative(g_2D, d_closure, CartesianBoundary{2,Upper}())
             Ix = IdentityMapping{Float64}((size(g_2D)[1],))
             Iy = IdentityMapping{Float64}((size(g_2D)[2],))
-            d_l = normal_derivative(restrict(g_2D,1),op.dClosure,Lower())
-            d_r = normal_derivative(restrict(g_2D,2),op.dClosure,Upper())
+            d_l = normal_derivative(restrict(g_2D,1),d_closure,Lower())
+            d_r = normal_derivative(restrict(g_2D,2),d_closure,Upper())
             @test d_w ==  d_l⊗Iy
             @test d_n ==  Ix⊗d_r
             @test d_w isa TensorMapping{T,1,2} where T
@@ -38,11 +38,12 @@
         v∂y = evalOn(g_2D, (x,y)-> 2*(y-1) + x)
         # TODO: Test for higher order polynomials?
         @testset "2nd order" begin
-            op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=2)
-            d_w = normal_derivative(g_2D, op.dClosure, CartesianBoundary{1,Lower}())
-            d_e = normal_derivative(g_2D, op.dClosure, CartesianBoundary{1,Upper}())
-            d_s = normal_derivative(g_2D, op.dClosure, CartesianBoundary{2,Lower}())
-            d_n = normal_derivative(g_2D, op.dClosure, CartesianBoundary{2,Upper}())
+        	stencil_set = read_stencil_set(sbp_operators_path()*"standard_diagonal.toml"; order=2)
+        	d_closure = parse_stencil(stencil_set["d1"]["closure"])
+            d_w = normal_derivative(g_2D, d_closure, CartesianBoundary{1,Lower}())
+            d_e = normal_derivative(g_2D, d_closure, CartesianBoundary{1,Upper}())
+            d_s = normal_derivative(g_2D, d_closure, CartesianBoundary{2,Lower}())
+            d_n = normal_derivative(g_2D, d_closure, CartesianBoundary{2,Upper}())
 
             @test d_w*v ≈ v∂x[1,:] atol = 1e-13
             @test d_e*v ≈ -v∂x[end,:] atol = 1e-13
@@ -51,11 +52,12 @@
         end
 
         @testset "4th order" begin
-            op = read_D2_operator(sbp_operators_path()*"standard_diagonal.toml"; order=4)
-            d_w = normal_derivative(g_2D, op.dClosure, CartesianBoundary{1,Lower}())
-            d_e = normal_derivative(g_2D, op.dClosure, CartesianBoundary{1,Upper}())
-            d_s = normal_derivative(g_2D, op.dClosure, CartesianBoundary{2,Lower}())
-            d_n = normal_derivative(g_2D, op.dClosure, CartesianBoundary{2,Upper}())
+            stencil_set = read_stencil_set(sbp_operators_path()*"standard_diagonal.toml"; order=2)
+        	d_closure = parse_stencil(stencil_set["d1"]["closure"])
+            d_w = normal_derivative(g_2D, d_closure, CartesianBoundary{1,Lower}())
+            d_e = normal_derivative(g_2D, d_closure, CartesianBoundary{1,Upper}())
+            d_s = normal_derivative(g_2D, d_closure, CartesianBoundary{2,Lower}())
+            d_n = normal_derivative(g_2D, d_closure, CartesianBoundary{2,Upper}())
 
             @test d_w*v ≈ v∂x[1,:] atol = 1e-13
             @test d_e*v ≈ -v∂x[end,:] atol = 1e-13
--- a/test/SbpOperators/readoperator_test.jl	Sat Jul 17 18:14:48 2021 +0200
+++ b/test/SbpOperators/readoperator_test.jl	Mon Jul 19 08:47:33 2021 +0200
@@ -18,76 +18,98 @@
 @testset "readoperator" begin
     toml_str = """
         [meta]
+        authors = "Ken Mattson"
+        description = "Standard operators for equidistant grids"
         type = "equidistant"
+        cite = "A paper a long time ago in a galaxy far far away."
 
-        [order2]
+        [[stencil_set]]
+
+        order = 2
+        test = 2
+
         H.inner = ["1"]
+        H.closure = ["1/2"]
 
         D1.inner_stencil = ["-1/2", "0", "1/2"]
         D1.closure_stencils = [
-            ["-1", "1"],
+            {s = ["-1", "1"], c = 1},
+        ]
+
+        D2.inner_stencil = ["1", "-2", "1"]
+        D2.closure_stencils = [
+            {s = ["1", "-2", "1"], c = 1},
         ]
 
-        d1.closure = ["-3/2", "2", "-1/2"]
+        e.closure = ["1"]
+        d1.closure = {s = ["-3/2", "2", "-1/2"], c = 1}
 
-        [order4]
+        [[stencil_set]]
+
+        order = 4
+        test = 1
+        H.inner = ["1"]
         H.closure = ["17/48", "59/48", "43/48", "49/48"]
 
         D2.inner_stencil = ["-1/12","4/3","-5/2","4/3","-1/12"]
         D2.closure_stencils = [
-            [     "2",    "-5",      "4",       "-1",     "0",     "0"],
-            [     "1",    "-2",      "1",        "0",     "0",     "0"],
-            [ "-4/43", "59/43", "-110/43",   "59/43", "-4/43",     "0"],
-            [ "-1/49",     "0",   "59/49", "-118/49", "64/49", "-4/49"],
+            {s = [     "2",    "-5",      "4",       "-1",     "0",     "0"], c = 1},
+            {s = [     "1",    "-2",      "1",        "0",     "0",     "0"], c = 2},
+            {s = [ "-4/43", "59/43", "-110/43",   "59/43", "-4/43",     "0"], c = 3},
+            {s = [ "-1/49",     "0",   "59/49", "-118/49", "64/49", "-4/49"], c = 4},
         ]
+
+        e.closure = ["1"]
+        d1.closure = {s = ["-11/6", "3", "-3/2", "1/3"], c = 1}
+
+        [[stencil_set]]
+        order = 4
+        test = 2
+
+        H.closure = ["-1/49", "0", "59/49", "-118/49", "64/49", "-4/49"]
     """
 
     parsed_toml = TOML.parse(toml_str)
-    @testset "get_stencil" begin
-        @test get_stencil(parsed_toml, "order2", "D1", "inner_stencil") == Stencil(-1/2, 0., 1/2, center=2)
-        @test get_stencil(parsed_toml, "order2", "D1", "inner_stencil", center=1) == Stencil(-1/2, 0., 1/2; center=1)
-        @test get_stencil(parsed_toml, "order2", "D1", "inner_stencil", center=3) == Stencil(-1/2, 0., 1/2; center=3)
 
-        @test get_stencil(parsed_toml, "order2", "H", "inner") == Stencil(1.; center=1)
+    @testset "get_stencil_set" begin
+        @test get_stencil_set(parsed_toml; order = 2) isa Dict
+        @test get_stencil_set(parsed_toml; order = 2) == parsed_toml["stencil_set"][1]
+        @test get_stencil_set(parsed_toml; test = 1) == parsed_toml["stencil_set"][2]
+        @test get_stencil_set(parsed_toml; order = 4, test = 2) == parsed_toml["stencil_set"][3]
 
-        @test_throws AssertionError get_stencil(parsed_toml, "meta", "type")
-        @test_throws AssertionError get_stencil(parsed_toml, "order2", "D1", "closure_stencils")
+        @test_throws ArgumentError get_stencil_set(parsed_toml; test = 2)
+        @test_throws ArgumentError get_stencil_set(parsed_toml; order = 4)
     end
 
-    @testset "get_stencils" begin
-        @test get_stencils(parsed_toml, "order2", "D1", "closure_stencils", centers=(1,)) == (Stencil(-1., 1., center=1),)
-        @test get_stencils(parsed_toml, "order2", "D1", "closure_stencils", centers=(2,)) == (Stencil(-1., 1., center=2),)
-        @test get_stencils(parsed_toml, "order2", "D1", "closure_stencils", centers=[2]) == (Stencil(-1., 1., center=2),)
-
-        @test get_stencils(parsed_toml, "order4", "D2", "closure_stencils",centers=[1,1,1,1]) == (
-            Stencil(    2.,    -5.,      4.,     -1.,    0.,    0., center=1),
-            Stencil(    1.,    -2.,      1.,      0.,    0.,    0., center=1),
-            Stencil( -4/43,  59/43, -110/43,   59/43, -4/43,    0., center=1),
-            Stencil( -1/49,     0.,   59/49, -118/49, 64/49, -4/49, center=1),
-        )
+    @testset "parse_stencil" begin
+        toml = """
+            s1 = ["-1/12","4/3","-5/2","4/3","-1/12"]
+            s2 = {s = ["2", "-5", "4", "-1", "0", "0"], c = 1}
+            s3 = {s = ["1", "-2", "1", "0", "0", "0"], c = 2}
+            s4 = "not a stencil"
+            s5 = [-1, 4, 3]
+            s6 = {k = ["1", "-2", "1", "0", "0", "0"], c = 2}
+            s7 = {s = [-1, 4, 3], c = 2}
+            s8 = {s = ["1", "-2", "1", "0", "0", "0"], c = [2,2]}
+        """
 
-        @test get_stencils(parsed_toml, "order4", "D2", "closure_stencils",centers=(4,2,3,1)) == (
-            Stencil(    2.,    -5.,      4.,     -1.,    0.,    0., center=4),
-            Stencil(    1.,    -2.,      1.,      0.,    0.,    0., center=2),
-            Stencil( -4/43,  59/43, -110/43,   59/43, -4/43,    0., center=3),
-            Stencil( -1/49,     0.,   59/49, -118/49, 64/49, -4/49, center=1),
-        )
+        @test parse_stencil(TOML.parse(toml)["s1"]) == CenteredStencil(-1/12, 4/3, -5/2, 4/3, -1/12)
+        @test parse_stencil(TOML.parse(toml)["s2"]) == Stencil(2., -5., 4., -1., 0., 0.; center=1)
+        @test parse_stencil(TOML.parse(toml)["s3"]) == Stencil(1., -2., 1., 0., 0., 0.; center=2)
 
-        @test get_stencils(parsed_toml, "order4", "D2", "closure_stencils",centers=1:4) == (
-            Stencil(    2.,    -5.,      4.,     -1.,    0.,    0., center=1),
-            Stencil(    1.,    -2.,      1.,      0.,    0.,    0., center=2),
-            Stencil( -4/43,  59/43, -110/43,   59/43, -4/43,    0., center=3),
-            Stencil( -1/49,     0.,   59/49, -118/49, 64/49, -4/49, center=4),
-        )
+        @test_throws ArgumentError parse_stencil(TOML.parse(toml)["s4"])
+        @test_throws ArgumentError parse_stencil(TOML.parse(toml)["s5"])
+        @test_throws ArgumentError parse_stencil(TOML.parse(toml)["s6"])
+        @test_throws ArgumentError parse_stencil(TOML.parse(toml)["s7"])
+        @test_throws ArgumentError parse_stencil(TOML.parse(toml)["s8"])
 
-        @test_throws AssertionError get_stencils(parsed_toml, "order4", "D2", "closure_stencils",centers=(1,2,3))
-        @test_throws AssertionError get_stencils(parsed_toml, "order4", "D2", "closure_stencils",centers=(1,2,3,5,4))
-        @test_throws AssertionError get_stencils(parsed_toml, "order4", "D2", "inner_stencil",centers=(1,2))
-    end
+        stencil_set = get_stencil_set(parsed_toml; order = 4, test = 1)
 
-    @testset "get_tuple" begin
-        @test get_tuple(parsed_toml, "order2", "d1", "closure") == (-3/2, 2, -1/2)
-
-        @test_throws AssertionError get_tuple(parsed_toml, "meta", "type")
+        @test parse_stencil.(stencil_set["D2"]["closure_stencils"]) == [
+            Stencil(    2.,    -5.,      4.,       -1.,     0.,     0.; center=1),
+            Stencil(    1.,    -2.,      1.,        0.,     0.,     0.; center=2),
+            Stencil(-4/43, 59/43, -110/43,   59/43, -4/43,     0.; center=3),
+            Stencil(-1/49,     0.,   59/49, -118/49, 64/49, -4/49; center=4),
+        ]
     end
 end