Skip to content

Commit 9f7bf16

Browse files
committed
Put the GridType into the BoundaryCondition (fixes #228)
1 parent aaf9ca0 commit 9f7bf16

File tree

13 files changed

+241
-130
lines changed

13 files changed

+241
-130
lines changed

src/Interpolations.jl

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,37 +44,52 @@ struct OnCell <: GridType end
4444

4545
const DimSpec{T} = Union{T,Tuple{Vararg{Union{T,NoInterp}}},NoInterp}
4646

47-
abstract type AbstractInterpolation{T,N,IT<:DimSpec{InterpolationType},GT<:DimSpec{GridType}} <: AbstractArray{T,N} end
48-
abstract type AbstractInterpolationWrapper{T,N,ITPT,IT,GT} <: AbstractInterpolation{T,N,IT,GT} end
49-
abstract type AbstractExtrapolation{T,N,ITPT,IT,GT} <: AbstractInterpolationWrapper{T,N,ITPT,IT,GT} end
47+
abstract type AbstractInterpolation{T,N,IT<:DimSpec{InterpolationType}} <: AbstractArray{T,N} end
48+
abstract type AbstractInterpolationWrapper{T,N,ITPT,IT} <: AbstractInterpolation{T,N,IT} end
49+
abstract type AbstractExtrapolation{T,N,ITPT,IT} <: AbstractInterpolationWrapper{T,N,ITPT,IT} end
5050

5151
"""
5252
BoundaryCondition
5353
5454
An abstract type with one of the following values (see the help for each for details):
5555
56-
- `Throw()`
57-
- `Flat()`
58-
- `Line()`
59-
- `Free()`
60-
- `Periodic()`
61-
- `Reflect()`
62-
- `InPlace()`
63-
- `InPlaceQ()`
56+
- `Throw(gt)`
57+
- `Flat(gt)`
58+
- `Line(gt)`
59+
- `Free(gt)`
60+
- `Periodic(gt)`
61+
- `Reflect(gt)`
62+
- `InPlace(gt)`
63+
- `InPlaceQ(gt)`
64+
65+
where `gt` is the grid type, e.g., `OnGrid()` or `OnCell()`. `OnGrid` means that the boundary
66+
condition "activates" at the first and/or last integer location within the interpolation region,
67+
`OnCell` means the interpolation extends a half-integer beyond the edge before
68+
activating the boundary condition.
6469
"""
6570
abstract type BoundaryCondition <: Flag end
66-
struct Throw <: BoundaryCondition end
67-
struct Flat <: BoundaryCondition end
68-
struct Line <: BoundaryCondition end
69-
struct Free <: BoundaryCondition end
70-
struct Periodic <: BoundaryCondition end
71-
struct Reflect <: BoundaryCondition end
72-
struct InPlace <: BoundaryCondition end
71+
# Put the gridtype into the boundary condition, since that's all it affects (see issue #228)
72+
# Nothing is used for extrapolation
73+
struct Throw{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
74+
struct Flat{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
75+
struct Line{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
76+
struct Free{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
77+
struct Periodic{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
78+
struct Reflect{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
79+
struct InPlace{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
7380
# InPlaceQ is exact for an underlying quadratic. This is nice for ground-truth testing
7481
# of in-place (unpadded) interpolation.
75-
struct InPlaceQ <: BoundaryCondition end
82+
struct InPlaceQ{GT<:Union{GridType,Nothing}} <: BoundaryCondition gt::GT end
7683
const Natural = Line
7784

85+
(::Type{BC})() where BC<:BoundaryCondition = BC(nothing)
86+
function Base.show(io::IO, bc::BoundaryCondition)
87+
print(io, nameof(typeof(bc)), '(')
88+
bc.gt === nothing || show(io, bc.gt)
89+
print(io, ')')
90+
end
91+
92+
7893
Base.IndexStyle(::Type{<:AbstractInterpolation}) = IndexCartesian()
7994

8095
size(exp::AbstractExtrapolation) = size(exp.itp)
@@ -85,16 +100,16 @@ twotuple(x, y) = (x, y)
85100
bounds(itp::AbstractInterpolation) = map(twotuple, lbounds(itp), ubounds(itp))
86101
bounds(itp::AbstractInterpolation, d) = bounds(itp)[d]
87102

88-
itptype(::Type{AbstractInterpolation{T,N,IT,GT}}) where {T,N,IT<:DimSpec{InterpolationType},GT<:DimSpec{GridType}} = IT
103+
itptype(::Type{AbstractInterpolation{T,N,IT}}) where {T,N,IT<:DimSpec{InterpolationType}} = IT
89104
itptype(::Type{ITP}) where {ITP<:AbstractInterpolation} = itptype(supertype(ITP))
90105
itptype(itp::AbstractInterpolation) = itptype(typeof(itp))
91-
gridtype(::Type{AbstractInterpolation{T,N,IT,GT}}) where {T,N,IT<:DimSpec{InterpolationType},GT<:DimSpec{GridType}} = GT
106+
gridtype(::Type{AbstractInterpolation{T,N,IT}}) where {T,N,IT<:DimSpec{InterpolationType}} = GT
92107
gridtype(::Type{ITP}) where {ITP<:AbstractInterpolation} = gridtype(supertype(ITP))
93108
gridtype(itp::AbstractInterpolation) = gridtype(typeof(itp))
94-
ndims(::Type{AbstractInterpolation{T,N,IT,GT}}) where {T,N,IT<:DimSpec{InterpolationType},GT<:DimSpec{GridType}} = N
109+
ndims(::Type{AbstractInterpolation{T,N,IT}}) where {T,N,IT<:DimSpec{InterpolationType}} = N
95110
ndims(::Type{ITP}) where {ITP<:AbstractInterpolation} = ndims(supertype(ITP))
96111
ndims(itp::AbstractInterpolation) = ndims(typeof(itp))
97-
eltype(::Type{AbstractInterpolation{T,N,IT,GT}}) where {T,N,IT<:DimSpec{InterpolationType},GT<:DimSpec{GridType}} = T
112+
eltype(::Type{AbstractInterpolation{T,N,IT}}) where {T,N,IT<:DimSpec{InterpolationType}} = T
98113
eltype(::Type{ITP}) where {ITP<:AbstractInterpolation} = eltype(supertype(ITP))
99114
eltype(itp::AbstractInterpolation) = eltype(typeof(itp))
100115

@@ -209,9 +224,6 @@ import Base: getindex
209224
itp(i)
210225
end
211226

212-
# deprecate getindex for other numeric indices
213-
@deprecate getindex(itp::AbstractInterpolation{T,N}, i::Vararg{Number,N}) where {T,N} itp(i...)
214-
215227
include("nointerp/nointerp.jl")
216228
include("b-splines/b-splines.jl")
217229
# include("gridded/gridded.jl")
@@ -220,5 +232,6 @@ include("scaling/scaling.jl")
220232
include("utils.jl")
221233
include("io.jl")
222234
include("convenience-constructors.jl")
235+
include("deprecations.jl")
223236

224237
end # module

src/b-splines/b-splines.jl

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,61 +8,89 @@ export
88
Cubic
99

1010
abstract type Degree{N} <: Flag end
11+
abstract type DegreeBC{N} <: Degree{N} end # degree type supporting a BoundaryCondition
1112

12-
struct BSpline{D<:Degree} <: InterpolationType end
13-
BSpline(::D) where {D<:Degree} = BSpline{D}()
13+
struct BSpline{D<:Degree} <: InterpolationType
14+
degree::D
15+
end
1416

1517
bsplinetype(::Type{BSpline{D}}) where {D<:Degree} = D
1618
bsplinetype(::BS) where {BS<:BSpline} = bsplinetype(BS)
1719

18-
degree(::BSpline{D}) where D<:Degree = D()
20+
degree(mode::BSpline) = mode.degree
1921
degree(::NoInterp) = NoInterp()
2022

21-
struct BSplineInterpolation{T,N,TCoefs<:AbstractArray,IT<:DimSpec{BSpline},GT<:DimSpec{GridType},Axs<:Tuple{Vararg{AbstractUnitRange,N}}} <: AbstractInterpolation{T,N,IT,GT}
23+
iscomplete(mode::BSpline) = iscomplete(degree(mode))
24+
iscomplete(deg::DegreeBC) = _iscomplete(deg.bc.gt)
25+
iscomplete(deg::Degree) = true
26+
_iscomplete(::Nothing) = false
27+
_iscomplete(::GridType) = true
28+
29+
function Base.show(io::IO, bs::BSpline)
30+
print(io, "BSpline(")
31+
show(io, degree(bs))
32+
print(io, ')')
33+
end
34+
35+
function Base.show(io::IO, deg::DegreeBC)
36+
print(io, nameof(typeof(deg)), '(')
37+
show(io, deg.bc)
38+
print(io, ')')
39+
end
40+
41+
struct BSplineInterpolation{T,N,TCoefs<:AbstractArray,IT<:DimSpec{BSpline},Axs<:Tuple{Vararg{AbstractUnitRange,N}}} <: AbstractInterpolation{T,N,IT}
2242
coefs::TCoefs
2343
parentaxes::Axs
2444
it::IT
25-
gt::GT
2645
end
27-
function BSplineInterpolation(::Type{TWeights}, A::AbstractArray{Tel,N}, it::IT, gt::GT, axs) where {N,Tel,TWeights<:Real,IT<:DimSpec{BSpline},GT<:DimSpec{GridType}}
46+
function BSplineInterpolation(::Type{TWeights}, A::AbstractArray{Tel,N}, it::IT, axs) where {N,Tel,TWeights<:Real,IT<:DimSpec{BSpline}}
2847
# String interpolation causes allocation, noinline avoids that unless they get called
2948
@noinline err_concrete(IT) = error("The b-spline type must be a concrete type (was $IT)")
3049
@noinline warn_concrete(A) = @warn("For performance reasons, consider using an array of a concrete type (typeof(A) == $(typeof(A)))")
50+
@noinline err_incomplete(it) = error("OnGrid/OnCell is not supplied for some of the interpolation modes in $it")
3151

3252
isconcretetype(IT) || err_concrete(IT)
3353
isconcretetype(typeof(A)) || warn_concrete(A)
54+
iscomplete(it) || err_incomplete(it)
3455

3556
# Compute the output element type when positions have type TWeights
3657
if isempty(A)
3758
T = Base.promote_op(*, TWeights, eltype(A))
3859
else
3960
T = typeof(zero(TWeights) * first(A))
4061
end
41-
BSplineInterpolation{T,N,typeof(A),IT,GT,typeof(axs)}(A, fix_axis.(axs), it, gt)
62+
BSplineInterpolation{T,N,typeof(A),IT,typeof(axs)}(A, fix_axis.(axs), it)
4263
end
4364

65+
iscomplete(its::Tuple) = all(iscomplete, its)
66+
4467
coefficients(itp::BSplineInterpolation) = itp.coefs
4568
interpdegree(itp::BSplineInterpolation) = interpdegree(itpflag(itp))
4669
interpdegree(::BSpline{T}) where T = T()
4770
interpdegree(it::Tuple{Vararg{Union{BSpline,NoInterp},N}}) where N = interpdegree.(it)
4871
itpflag(itp::BSplineInterpolation) = itp.it
49-
gridflag(itp::BSplineInterpolation) = itp.gt
5072

5173
size(itp::BSplineInterpolation) = map(length, itp.parentaxes)
5274
axes(itp::BSplineInterpolation) = itp.parentaxes
5375

54-
lbounds(itp::BSplineInterpolation) = _lbounds(itp.parentaxes, itpflag(itp), gridflag(itp))
55-
ubounds(itp::BSplineInterpolation) = _ubounds(itp.parentaxes, itpflag(itp), gridflag(itp))
56-
_lbounds(axs, itp, gt) = (lbound(axs[1], getfirst(itp), getfirst(gt)), _lbounds(Base.tail(axs), getrest(itp), getrest(gt))...)
57-
_ubounds(axs, itp, gt) = (ubound(axs[1], getfirst(itp), getfirst(gt)), _ubounds(Base.tail(axs), getrest(itp), getrest(gt))...)
58-
_lbounds(::Tuple{}, itp, gt) = ()
59-
_ubounds(::Tuple{}, itp, gt) = ()
60-
61-
# The unpadded defaults
62-
lbound(ax::AbstractUnitRange, ::BSpline, ::OnCell) = first(ax) - 0.5
63-
ubound(ax::AbstractUnitRange, ::BSpline, ::OnCell) = last(ax) + 0.5
64-
lbound(ax::AbstractUnitRange, ::BSpline, ::OnGrid) = first(ax)
65-
ubound(ax::AbstractUnitRange, ::BSpline, ::OnGrid) = last(ax)
76+
lbounds(itp::BSplineInterpolation) = _lbounds(itp.parentaxes, itpflag(itp))
77+
ubounds(itp::BSplineInterpolation) = _ubounds(itp.parentaxes, itpflag(itp))
78+
_lbounds(axs, itp) = (lbound(axs[1], getfirst(itp)), _lbounds(Base.tail(axs), getrest(itp))...)
79+
_ubounds(axs, itp) = (ubound(axs[1], getfirst(itp)), _ubounds(Base.tail(axs), getrest(itp))...)
80+
_lbounds(::Tuple{}, itp) = ()
81+
_ubounds(::Tuple{}, itp) = ()
82+
83+
lbound(ax::AbstractUnitRange, bs::BSpline) = lbound(ax, degree(bs))
84+
lbound(ax::AbstractUnitRange, deg::Degree) = first(ax)
85+
lbound(ax::AbstractUnitRange, deg::DegreeBC) = lbound(ax, deg, deg.bc.gt)
86+
ubound(ax::AbstractUnitRange, bs::BSpline) = ubound(ax, degree(bs))
87+
ubound(ax::AbstractUnitRange, deg::Degree) = last(ax)
88+
ubound(ax::AbstractUnitRange, deg::DegreeBC) = ubound(ax, deg, deg.bc.gt)
89+
90+
lbound(ax::AbstractUnitRange, ::DegreeBC, ::OnCell) = first(ax) - 0.5
91+
ubound(ax::AbstractUnitRange, ::DegreeBC, ::OnCell) = last(ax) + 0.5
92+
lbound(ax::AbstractUnitRange, ::DegreeBC, ::OnGrid) = first(ax)
93+
ubound(ax::AbstractUnitRange, ::DegreeBC, ::OnGrid) = last(ax)
6694

6795
fix_axis(r::Base.OneTo) = r
6896
fix_axis(r::Base.Slice) = r
@@ -71,9 +99,9 @@ fix_axis(r::AbstractUnitRange) = fix_axis(UnitRange(r))
7199

72100
count_interp_dims(::Type{BSI}, n) where BSI<:BSplineInterpolation = count_interp_dims(itptype(BSI), n)
73101

74-
function interpolate(::Type{TWeights}, ::Type{TC}, A, it::IT, gt::GT) where {TWeights,TC,IT<:DimSpec{BSpline},GT<:DimSpec{GridType}}
75-
Apad = prefilter(TWeights, TC, A, it, gt)
76-
BSplineInterpolation(TWeights, Apad, it, gt, axes(A))
102+
function interpolate(::Type{TWeights}, ::Type{TC}, A, it::IT) where {TWeights,TC,IT<:DimSpec{BSpline}}
103+
Apad = prefilter(TWeights, TC, A, it)
104+
BSplineInterpolation(TWeights, Apad, it, axes(A))
77105
end
78106

79107
"""
@@ -91,8 +119,8 @@ It may also be a tuple of such values, if you want to use different interpolatio
91119
92120
`gridstyle` should be one of `OnGrid()` or `OnCell()`.
93121
"""
94-
function interpolate(A::AbstractArray, it::IT, gt::GT) where {IT<:DimSpec{BSpline},GT<:DimSpec{GridType}}
95-
interpolate(tweight(A), tcoef(A), A, it, gt)
122+
function interpolate(A::AbstractArray, it::IT) where {IT<:DimSpec{BSpline}}
123+
interpolate(tweight(A), tcoef(A), A, it)
96124
end
97125

98126
# We can't just return a tuple-of-types due to julia #12500
@@ -106,14 +134,14 @@ tcoef(A::AbstractArray{Float32}) = Float32
106134
tcoef(A::AbstractArray{Rational{Int}}) = Rational{Int}
107135
tcoef(A::AbstractArray{T}) where {T<:Integer} = typeof(float(zero(T)))
108136

109-
function interpolate!(::Type{TWeights}, A, it::IT, gt::GT) where {TWeights,IT<:DimSpec{BSpline},GT<:DimSpec{GridType}}
137+
function interpolate!(::Type{TWeights}, A::AbstractArray, it::IT) where {TWeights,IT<:DimSpec{BSpline}}
110138
# Set the bounds of the interpolant inward, if necessary
111139
axsA = axes(A)
112140
axspad = padded_axes(axsA, it)
113-
BSplineInterpolation(TWeights, prefilter!(TWeights, A, it, gt), it, gt, fix_axis.(padinset.(axsA, axspad)))
141+
BSplineInterpolation(TWeights, prefilter!(TWeights, A, it), it, fix_axis.(padinset.(axsA, axspad)))
114142
end
115-
function interpolate!(A::AbstractArray, it::IT, gt::GT) where {IT<:DimSpec{BSpline},GT<:DimSpec{GridType}}
116-
interpolate!(tweight(A), A, it, gt)
143+
function interpolate!(A::AbstractArray, it::IT) where {IT<:DimSpec{BSpline}}
144+
interpolate!(tweight(A), A, it)
117145
end
118146

119147
lut!(dl, d, du) = lu!(Tridiagonal(dl, d, du), Val(false))

src/b-splines/cubic.jl

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
struct Cubic{BC<:Flag} <: Degree{3} end
2-
Cubic(::BC) where {BC<:Flag} = Cubic{BC}()
1+
struct Cubic{BC<:BoundaryCondition} <: DegreeBC{3}
2+
bc::BC
3+
end
4+
5+
(deg::Cubic)(gt::GridType) = Cubic(deg.bc(gt))
36

47
"""
58
Assuming uniform knots with spacing 1, the `i`th piece of cubic spline
@@ -32,7 +35,7 @@ function base_rem(::Cubic, bounds, x)
3235
end
3336

3437
expand_index(::Cubic{BC}, xi::Number, ax::AbstractUnitRange, δx) where BC = (xi-1, xi, xi+1, xi+2)
35-
expand_index(::Cubic{Periodic}, xi::Number, ax::AbstractUnitRange, δx) =
38+
expand_index(::Cubic{Periodic{GT}}, xi::Number, ax::AbstractUnitRange, δx) where GT<:GridType =
3639
(modrange(xi-1, ax), modrange(xi, ax), modrange(xi+1, ax), modrange(xi+2, ax))
3740

3841
# expand_coefs(::Type{BSpline{Cubic{BC}}}, δx) = cvcoefs(δx)
@@ -45,31 +48,31 @@ expand_index(::Cubic{Periodic}, xi::Number, ax::AbstractUnitRange, δx) =
4548
# end
4649
# end
4750

48-
function value_weights(::Cubic, δx)
51+
function value_weights(::BSpline{<:Cubic}, δx)
4952
x3, xcomp3 = cub(δx), cub(1-δx)
5053
(SimpleRatio(1,6) * xcomp3,
5154
SimpleRatio(2,3) - sqr(δx) + SimpleRatio(1,2)*x3,
5255
SimpleRatio(2,3) - sqr(1-δx) + SimpleRatio(1,2)*xcomp3,
5356
SimpleRatio(1,6) * x3)
5457
end
5558

56-
function gradient_weights(::Cubic, δx)
59+
function gradient_weights(::BSpline{<:Cubic}, δx)
5760
x2, xcomp2 = sqr(δx), sqr(1-δx)
5861
(-SimpleRatio(1,2) * xcomp2,
5962
-2*δx + SimpleRatio(3,2)*x2,
6063
+2*(1-δx) - SimpleRatio(3,2)*xcomp2,
6164
SimpleRatio(1,2) * x2)
6265
end
6366

64-
hessian_weights(::Cubic, δx) = (1-δx, 3*δx-2, 3*(1-δx)-2, δx)
67+
hessian_weights(::BSpline{<:Cubic}, δx) = (1-δx, 3*δx-2, 3*(1-δx)-2, δx)
6568

6669

6770
# ------------ #
6871
# Prefiltering #
6972
# ------------ #
7073

7174
padded_axis(ax::AbstractUnitRange, ::BSpline{<:Cubic}) = first(ax)-1:last(ax)+1
72-
padded_axis(ax::AbstractUnitRange, ::BSpline{Cubic{Periodic}}) = ax
75+
padded_axis(ax::AbstractUnitRange, ::BSpline{Cubic{Periodic{GT}}}) where GT<:GridType = ax
7376

7477
# # Due to padding we can extend the bounds
7578
# lbound(ax, ::BSpline{Cubic{BC}}, ::OnGrid) where BC = first(ax) - 0.5
@@ -97,7 +100,7 @@ Applying this condition yields
97100
-cm + cp = 0
98101
"""
99102
function prefiltering_system(::Type{T}, ::Type{TC}, n::Int,
100-
degree::Cubic{Flat}, ::OnGrid) where {T,TC}
103+
degree::Cubic{Flat{OnGrid}}) where {T,TC}
101104
dl, d, du = inner_system_diags(T, n, degree)
102105
d[1] = d[end] = -oneunit(T)
103106
du[1] = dl[end] = zero(T)
@@ -123,7 +126,7 @@ were to use `y_0'(x)` we would have to introduce new coefficients, so that would
123126
close the system. Instead, we extend the outermost polynomial for an extra half-cell.)
124127
"""
125128
function prefiltering_system(::Type{T}, ::Type{TC}, n::Int,
126-
degree::Cubic{Flat}, ::OnCell) where {T,TC}
129+
degree::Cubic{Flat{OnCell}}) where {T,TC}
127130
dl, d, du = inner_system_diags(T,n,degree)
128131
d[1] = d[end] = -9
129132
du[1] = dl[end] = 11
@@ -152,7 +155,7 @@ were to use `y_0'(x)` we would have to introduce new coefficients, so that would
152155
close the system. Instead, we extend the outermost polynomial for an extra half-cell.)
153156
"""
154157
function prefiltering_system(::Type{T}, ::Type{TC}, n::Int,
155-
degree::Cubic{Line}, ::OnCell) where {T,TC}
158+
degree::Cubic{Line{OnCell}}) where {T,TC}
156159
dl,d,du = inner_system_diags(T,n,degree)
157160
d[1] = d[end] = 3
158161
du[1] = dl[end] = -7
@@ -177,7 +180,7 @@ condition gives:
177180
1 cm -2 c + 1 cp = 0
178181
"""
179182
function prefiltering_system(::Type{T}, ::Type{TC}, n::Int,
180-
degree::Cubic{Line}, ::OnGrid) where {T,TC}
183+
degree::Cubic{Line{OnGrid}}) where {T,TC}
181184
dl,d,du = inner_system_diags(T,n,degree)
182185
d[1] = d[end] = 1
183186
du[1] = dl[end] = -2
@@ -201,7 +204,7 @@ as periodic, yielding
201204
where `N` is the number of data points.
202205
"""
203206
function prefiltering_system(::Type{T}, ::Type{TC}, n::Int,
204-
degree::Cubic{Periodic}, ::GridType) where {T,TC}
207+
degree::Cubic{<:Periodic}) where {T,TC}
205208
dl, d, du = inner_system_diags(T,n,degree)
206209

207210
specs = WoodburyMatrices.sparse_factors(T, n,
@@ -220,7 +223,7 @@ continuous derivative at the second-to-last cell boundary; this means
220223
1 cm -3 c + 3 cp -1 cpp = 0
221224
"""
222225
function prefiltering_system(::Type{T}, ::Type{TC}, n::Int,
223-
degree::Cubic{Free}, ::GridType) where {T,TC}
226+
degree::Cubic{<:Free}) where {T,TC}
224227
dl, d, du = inner_system_diags(T,n,degree)
225228

226229
specs = WoodburyMatrices.sparse_factors(T, n,

src/b-splines/linear.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct Linear <: Degree{1} end
1+
struct Linear <: Degree{1} end # boundary conditions not supported
22

33
"""
44
Assuming uniform knots with spacing 1, the `i`th peice of linear b-spline

0 commit comments

Comments
 (0)