diff src/SbpOperators/readoperator.jl @ 866:1784b1c0af3e feature/laplace_opset

Merge with default
author Vidar Stiernström <vidar.stiernstrom@it.uu.se>
date Wed, 19 Jan 2022 14:44:24 +0100
parents 568058183791
children 06c510d40ebb 7bf3121c6864
line wrap: on
line diff
--- a/src/SbpOperators/readoperator.jl	Fri Jul 02 14:23:33 2021 +0200
+++ b/src/SbpOperators/readoperator.jl	Wed Jan 19 14:44:24 2022 +0100
@@ -1,155 +1,160 @@
 using TOML
 
-export read_D2_operator
-export read_stencil
-export read_stencils
-export read_tuple
+export read_stencil_set
+export get_stencil_set
+
+export parse_stencil
+export parse_scalar
+export parse_tuple
+
+export sbp_operators_path
+
 
-export get_stencil
-export get_stencils
-export get_tuple
+"""
+    read_stencil_set(filename; filters)
 
-function read_D2_operator(fn; order)
-    operators = TOML.parsefile(fn)["order$order"]
-    D2 = operators["D2"]
-    H = operators["H"]
-    e = operators["e"]
-    d1 = operators["d1"]
+Picks out a stencil set from a TOML file based on some key-value
+filters. If more than one set matches the filters an error is raised. The
+returned stencil set contains parsed TOML intended for functions like
+`parse_scalar` and `parse_stencil`.
+
+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.
+
+The reason for this is that since stencil sets are intended to be very
+general, and currently do not include any way to specify how to parse a given
+section, the exact parsing is left to the user.
 
-    # Create inner stencil
-    innerStencil = get_stencil(operators, "D2", "inner_stencil")
+For more information see [Operator file format](@ref) in the documentation.
+
+See also [`sbp_operators_path`](@ref), [`get_stencil_set`](@ref), [`parse_stencil`](@ref), [`parse_scalar`](@ref), [`parse_tuple`](@ref),.
+"""
+read_stencil_set(filename; filters...) = get_stencil_set(TOML.parsefile(filename); filters...)
+
+"""
+    get_stencil_set(parsed_toml; filters...)
+
+Picks out a stencil set from an already parsed TOML based on some key-value
+filters.
 
-    # 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))
+See also [`read_stencil_set`](@ref).
+"""
+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
+
+        return true
     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)
 
-    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))
+    if length(matches) != 1
+        throw(ArgumentError("filters must pick out a single stencil set"))
     end
 
-    d2 = SbpOperators.D2(
-        innerStencil,
-        closureStencils,
-        eClosure,
-        dClosure,
-        quadratureClosure,
-        even
-    )
+    i = matches[1]
+    return parsed_toml["stencil_set"][i]
+end
+
+"""
+    parse_stencil(parsed_toml)
+
+Accepts parsed TOML and reads it as a stencil.
+
+See also [`read_stencil_set`](@ref), [`parse_scalar`](@ref), [`parse_tuple`](@ref).
+"""
+function parse_stencil(parsed_toml)
+    check_stencil_toml(parsed_toml)
+
+    if parsed_toml isa Array
+        weights = parse_rational.(parsed_toml)
+        return CenteredStencil(weights...)
+    end
+
+    weights = parse_rational.(parsed_toml["s"])
+    return Stencil(weights..., center = parsed_toml["c"])
+end
+
+"""
+    parse_stencil(T, parsed_toml)
+
+Parses the input as a stencil with element type `T`.
+"""
+parse_stencil(T, parsed_toml) = Stencil{T}(parse_stencil(parsed_toml))
+
+function check_stencil_toml(parsed_toml)
+    if !(parsed_toml isa Dict || parsed_toml isa Vector{String})
+        throw(ArgumentError("the TOML for a stencil must be a vector of strings or a table."))
+    end
+
+    if parsed_toml isa Vector{String}
+        return
+    end
 
-    return d2
+    if !(haskey(parsed_toml, "s") && haskey(parsed_toml, "c"))
+        throw(ArgumentError("the table form of a stencil must have fields `s` and `c`."))
+    end
+
+    if !(parsed_toml["s"] isa Vector{String})
+        throw(ArgumentError("a stencil must be specified as a vector of strings."))
+    end
+
+    if !(parsed_toml["c"] isa Int)
+        throw(ArgumentError("the center of a stencil must be specified as an integer."))
+    end
+end
+
+"""
+    parse_scalar(parsed_toml)
+
+Parse a scalar, represented as a string or a number in the TOML, and return it as a `Rational`
+
+See also [`read_stencil_set`](@ref), [`parse_stencil`](@ref) [`parse_tuple`](@ref).
+"""
+function parse_scalar(parsed_toml)
+    try
+        return parse_rational(parsed_toml)
+    catch e
+        throw(ArgumentError("must be a number or a string representing a number."))
+    end
+end
+
+"""
+    parse_tuple(parsed_toml)
+
+Parse an array as a tuple of scalars.
+
+See also [`read_stencil_set`](@ref), [`parse_stencil`](@ref), [`parse_scalar`](@ref).
+"""
+function parse_tuple(parsed_toml)
+    if !(parsed_toml isa Array)
+        throw(ArgumentError("argument must be an array"))
+    end
+    return Tuple(parse_scalar.(parsed_toml))
 end
 
 
 """
-    read_stencil(fn, path...; [center])
-
-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).
+    parse_rational(parsed_toml)
 
-# 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)
-
-"""
-    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)
-
+Parse a string or a number as a rational.
 """
-    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)
-
-Same as [`read_stencil`](@ref)) but takes 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))
-
-    width = length(stencil_weights)
-
-    if isnothing(center)
-        center = div(width,2)+1
+function parse_rational(parsed_toml)
+    if parsed_toml isa String
+        expr = Meta.parse(replace(parsed_toml, "/"=>"//"))
+        return eval(:(Rational($expr)))
+    else
+        return Rational(parsed_toml)
     end
-
-    return Stencil(stencil_weights..., center=center)
 end
 
 """
-    get_stencils(parsed_toml, path...; centers)
-
-Same as [`read_stencils`](@ref)) but takes already parsed toml.
-"""
-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)
+    sbp_operators_path()
 
-    stencils = ()
-    for i ∈ 1:length(parsed_toml)
-        stencil = get_stencil(parsed_toml[i], center = centers[i])
-        stencils = (stencils..., stencil)
-    end
+Calculate the path for the operators folder with included stencil sets.
 
-    return stencils
-end
-
-"""
-    get_tuple(parsed_toml, path...)
-
-Same as [`read_tuple`](@ref)) but takes already parsed toml.
+See also [`read_stencil_set`](@ref)
 """
-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
-
-# 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)))
-end
-
-function parse_rational(str)
-    expr = Meta.parse(replace(str, "/"=>"//"))
-    return eval(:(Rational($expr)))
-end
-
-function pad_tuple(t::NTuple{N, T}, n::Integer) where {N,T}
-    if N >= n
-        return t
-    else
-        return pad_tuple((t..., zero(T)), n)
-    end
-end
-
 sbp_operators_path() = (@__DIR__) * "/operators/"
-export sbp_operators_path