Mercurial > repos > public > sbplib_julia
diff src/Grids/equidistant_grid.jl @ 1858:4a9be96f2569 feature/documenter_logo
Merge default
author | Jonatan Werpers <jonatan@werpers.com> |
---|---|
date | Sun, 12 Jan 2025 21:18:44 +0100 |
parents | 244311761969 |
children | 516eaabf1169 81559cb7b11c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Grids/equidistant_grid.jl Sun Jan 12 21:18:44 2025 +0100 @@ -0,0 +1,166 @@ +""" + EquidistantGrid{T,R<:AbstractRange{T}} <: Grid{T,1} + +A one-dimensional equidistant grid. Most users are expected to use +[`equidistant_grid`](@ref) for constructing equidistant grids. + +See also: [`equidistant_grid`](@ref) + + +## Note +The type of range used for the points can likely impact performance. +""" +struct EquidistantGrid{T,R<:AbstractRange{T}} <: Grid{T,1} + points::R +end + +# Indexing interface +Base.getindex(g::EquidistantGrid, i::Int) = g.points[i] +Base.eachindex(g::EquidistantGrid) = eachindex(g.points) +Base.firstindex(g::EquidistantGrid) = firstindex(g.points) +Base.lastindex(g::EquidistantGrid) = lastindex(g.points) + +Base.axes(g::EquidistantGrid, d) = axes(g.points, d) + +# Iteration interface +Base.iterate(g::EquidistantGrid) = iterate(g.points) +Base.iterate(g::EquidistantGrid, state) = iterate(g.points, state) + +Base.IteratorSize(::Type{<:EquidistantGrid}) = Base.HasShape{1}() +Base.length(g::EquidistantGrid) = length(g.points) +Base.size(g::EquidistantGrid) = size(g.points) +Base.size(g::EquidistantGrid, d) = size(g.points)[d] + + +""" + spacing(grid::EquidistantGrid) + +The spacing between grid points. +""" +spacing(g::EquidistantGrid) = step(g.points) + + +""" + inverse_spacing(grid::EquidistantGrid) + +The reciprocal of the spacing between grid points. +""" +inverse_spacing(g::EquidistantGrid) = 1/step(g.points) + +min_spacing(g::EquidistantGrid) = spacing(g) + +""" + LowerBoundary <: BoundaryIdentifier + +Boundary identifier for the the lower (left) boundary of a one-dimensional grid. + +See also: [`BoundaryIdentifier`](@ref) +""" +struct LowerBoundary <: BoundaryIdentifier end + +""" + UpperBoundary <: BoundaryIdentifier + +Boundary identifier for the the upper (right) boundary of a one-dimensional grid. + +See also: [`BoundaryIdentifier`](@ref) +""" +struct UpperBoundary <: BoundaryIdentifier end + + +boundary_identifiers(::EquidistantGrid) = (LowerBoundary(), UpperBoundary()) +boundary_grid(g::EquidistantGrid, id::LowerBoundary) = ZeroDimGrid(g[begin]) +boundary_grid(g::EquidistantGrid, id::UpperBoundary) = ZeroDimGrid(g[end]) +boundary_indices(g::EquidistantGrid, id::LowerBoundary) = (firstindex(g),) +boundary_indices(g::EquidistantGrid, id::UpperBoundary) = (lastindex(g),) + +""" + refine(g::EquidistantGrid, r::Int) + +The grid where `g` is refined by the factor `r`. The factor is applied to the number of +intervals, i.e., 1 less than the size of `g`. + +See also: [`coarsen`](@ref) +""" +function refine(g::EquidistantGrid, r::Int) + new_sz = (length(g) - 1)*r + 1 + return EquidistantGrid(change_length(g.points, new_sz)) +end + +""" + coarsen(g::EquidistantGrid, r::Int) + +The grid where `g` is coarsened by the factor `r`. The factor is applied to the number of +intervals, i.e., 1 less than the size of `g`. If the number of +intervals are not divisible by `r` an error is raised. + +See also: [`refine`](@ref) +""" +function coarsen(g::EquidistantGrid, r::Int) + if (length(g)-1)%r != 0 + throw(DomainError(r, "Size minus 1 must be divisible by the ratio.")) + end + + new_sz = (length(g) - 1)÷r + 1 + + return EquidistantGrid(change_length(g.points, new_sz)) +end + + +""" + equidistant_grid(limit_lower, limit_upper, dims...) + +Construct an equidistant grid with corners at the coordinates `limit_lower` and +`limit_upper`. + +The length of the domain sides are given by the components of +`limit_upper-limit_lower`. E.g for a 2D grid with `limit_lower=(-1,0)` and +`limit_upper=(1,2)` the domain is defined as `(-1,1)x(0,2)`. The side lengths +of the grid are not allowed to be negative. + +The number of equispaced points in each coordinate direction are given +by the tuple `dims`. + +Note: If `limit_lower` and `limit_upper` are integers and `dims` would allow a +completely integer grid, `equidistant_grid` will still return a floating point +grid. This simplifies the implementation and avoids certain surprise +behaviors. +""" +function equidistant_grid(limit_lower, limit_upper, dims::Vararg{Int}) + if !(length(limit_lower) == length(limit_upper) == length(dims)) + throw(ArgumentError("All arguments must be of the same length")) + end + gs = map(equidistant_grid, limit_lower, limit_upper, dims) + return TensorGrid(gs...) +end + +""" + equidistant_grid(limit_lower::T, limit_upper::T, size::Int) + +Constructs a 1D equidistant grid. +""" +function equidistant_grid(limit_lower::Number, limit_upper::Number, size::Int) + if any(size .<= 0) + throw(DomainError("size must be postive")) + end + + if any(limit_upper.-limit_lower .<= 0) + throw(DomainError("side length must be postive")) + end + return EquidistantGrid(range(limit_lower, limit_upper, length=size)) # TBD: Should it use LinRange instead? +end + +CartesianBoundary{D,BID} = TensorGridBoundary{D,BID} # TBD: What should we do about the naming of this boundary? + + +""" + change_length(r::AbstractRange, n) + +Change the length of `r` to `n`, keeping the same start and stop. +""" +function change_length end + +change_length(r::UnitRange, n) = StepRange{Int,Int}(range(r[begin], r[end], n)) +change_length(r::StepRange, n) = StepRange{Int,Int}(range(r[begin], r[end], n)) +change_length(r::StepRangeLen, n) = range(r[begin], r[end], n) +change_length(r::LinRange, n) = LinRange(r[begin], r[end], n)