Skip to content

Commit 8275663

Browse files
authored
Example of using p-FEM (#173)
* Example of using p-FEM * Fix bugs * update tests * Update index.md * v0.12.6
1 parent 5e25514 commit 8275663

File tree

11 files changed

+188
-24
lines changed

11 files changed

+188
-24
lines changed

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2+
version: 2
3+
updates:
4+
- package-ecosystem: "github-actions"
5+
directory: "/" # Location of package manifests
6+
schedule:
7+
interval: "weekly"

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ClassicalOrthogonalPolynomials"
22
uuid = "b30e2e7b-c4ee-47da-9d5f-2c5c27239acd"
33
authors = ["Sheehan Olver <[email protected]>"]
4-
version = "0.12.5"
4+
version = "0.12.6"
55

66
[deps]
77
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -29,7 +29,7 @@ ArrayLayouts = "1.3.1"
2929
BandedMatrices = "0.17.17, 1"
3030
BlockArrays = "0.16.9"
3131
BlockBandedMatrices = "0.12"
32-
ContinuumArrays = "0.17"
32+
ContinuumArrays = "0.17.3"
3333
DomainSets = "0.6, 0.7"
3434
FFTW = "1.1"
3535
FastGaussQuadrature = "0.5, 1"

docs/src/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ ClassicalOrthogonalPolynomials.ShuffledFFT
224224
ClassicalOrthogonalPolynomials.legendre_grammatrix
225225
```
226226
```@docs
227+
ClassicalOrthogonalPolynomials.weightedgrammatrix
228+
```
229+
```@docs
227230
ClassicalOrthogonalPolynomials.WeightedOPLayout
228231
```
229232
```@docs

examples/pfem.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using ClassicalOrthogonalPolynomials, Plots, Test
2+
3+
4+
#
5+
# We can solve ODEs like an inhomogenous Airy equation
6+
# $$
7+
# u'' - x*u = f
8+
# $$
9+
# with zero Dirichlet conditions using a basis built from ultraspherical polynomials
10+
# of the form
11+
# $$
12+
# (1-x^2)*C_n^{(3/2)}(x) = c_n (1-x^2) P_n^{(1,1)}(x)
13+
# $$
14+
# We construct this basis as follows:
15+
16+
C = Ultraspherical(3/2)
17+
W = Weighted(C)
18+
plot(W[:,1:4])
19+
20+
# We can differentiate using the `diff` command. Unlike arrays, for quasi-arrays `diff(W)` defaults to
21+
# `diff(W; dims=1)`.
22+
23+
plot(diff(W)[:,1:4])
24+
25+
# We can get out a differentiation matrix via
26+
27+
= C \ diff(diff(W))
28+
29+
# We can construct the multiplication by $x$ with the connection between `W` and `C`
30+
# via:
31+
32+
x = axes(W,1)
33+
X = C \ (x .* W)
34+
35+
# Thus our operator becomes:
36+
37+
L =- X
38+
39+
# We can compute the coefficients using:
40+
41+
c = L \ transform(C, exp)
42+
u = W*c
43+
plot(u)
44+
45+
46+
# Weak form for the Laplacian with W as a basis for test/trial is given by
47+
# $$
48+
# ⟨dv/dx, du/dx ⟩ ≅ diff(v)'diff(u) = diff(W*d)'*diff(W*c) == d'*diff(W)'diff(W)*c
49+
# $$
50+
# where $v = W*d$ for some vector of coeficients d. For $x$ we have
51+
# $$
52+
# ⟨v, x*u ⟩ ≅ v'(x .* u) == d'*W'(x .* W)*c
53+
# $$
54+
55+
Δ = -(diff(W)'diff(W)) # or weaklaplacian(W)
56+
x = axes(W,1)
57+
X = W' * (x .* W)
58+
L = Δ - X
59+
60+
# We can solve an ODE via:
61+
62+
c = L \ W'exp.(x)
63+
u = W*c
64+
plot!(u)
65+
66+
67+
# The two solvers match!

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@ orthogonalityweight(P::SubQuasiArray{<:Any,2,<:Any,<:Tuple{AbstractAffineQuasiVe
226226

227227
weighted(P::AbstractQuasiMatrix) = Weighted(P)
228228

229+
"""
230+
gives the inner products of OPs with their weight, i.e., Weighted(P)'P.
231+
"""
229232
weightedgrammatrix(P) = weightedgrammatrix_layout(MemoryLayout(P), P)
230233
function weightedgrammatrix_layout(::MappedOPLayout, P)
231234
Q = parent(P)
@@ -292,6 +295,16 @@ plotgrid_layout(::AbstractOPLayout, P, n::Integer) = grid(P, min(40n, MAX_PLOT_P
292295
plotgrid_layout(::MappedOPLayout, P, n::Integer) = plotgrid_layout(MappedBasisLayout(), P, n)
293296
plotvalues_layout(::ExpansionLayout{MappedOPLayout}, f, x...) = plotvalues_layout(ExpansionLayout{MappedBasisLayout}(), f, x...)
294297

298+
hasboundedendpoints(_) = false # assume blow up
299+
function plotgrid_layout(::WeightedOPLayout, P, n::Integer)
300+
if hasboundedendpoints(weight(P))
301+
plotgrid(unweighted(P), n)
302+
else
303+
grid(unweighted(P), min(40n, MAX_PLOT_POINTS))
304+
end
305+
end
306+
307+
295308
function golubwelsch(X::AbstractMatrix)
296309
D, V = eigen(symtridiagonalize(X)) # Eigenvalue decomposition
297310
D, V[1,:].^2

src/classical/chebyshev.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ ChebyshevWeight() = ChebyshevWeight{1,Float64}()
1212
AbstractQuasiArray{T}(::ChebyshevWeight{kind}) where {T,kind} = ChebyshevWeight{kind,T}()
1313
AbstractQuasiVector{T}(::ChebyshevWeight{kind}) where {T,kind} = ChebyshevWeight{kind,T}()
1414

15+
const ChebyshevTWeight = ChebyshevWeight{1}
16+
const ChebyshevUWeight = ChebyshevWeight{2}
17+
18+
getproperty(w::ChebyshevTWeight{T}, ::Symbol) where T = -one(T)/2
19+
getproperty(w::ChebyshevUWeight{T}, ::Symbol) where T = one(T)/2
1520

16-
getproperty(w::ChebyshevWeight{1,T}, ::Symbol) where T = -one(T)/2
17-
getproperty(w::ChebyshevWeight{2,T}, ::Symbol) where T = one(T)/2
21+
hasboundedendpoints(::ChebyshevUWeight) = true
1822

1923
"""
2024
Chebyshev{kind,T}()
@@ -28,8 +32,6 @@ Chebyshev{kind}() where kind = Chebyshev{kind,Float64}()
2832
AbstractQuasiArray{T}(::Chebyshev{kind}) where {T,kind} = Chebyshev{kind,T}()
2933
AbstractQuasiMatrix{T}(::Chebyshev{kind}) where {T,kind} = Chebyshev{kind,T}()
3034

31-
const ChebyshevTWeight = ChebyshevWeight{1}
32-
const ChebyshevUWeight = ChebyshevWeight{2}
3335
const ChebyshevT = Chebyshev{1}
3436
const ChebyshevU = Chebyshev{2}
3537

src/classical/jacobi.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ summary(io::IO, w::JacobiWeight) = print(io, "(1-x)^$(w.a) * (1+x)^$(w.b) on -1.
4646

4747
sum(P::JacobiWeight) = jacobimoment(P.a, P.b)
4848

49+
hasboundedendpoints(w::AbstractJacobiWeight) = w.a 0 && w.b 0
50+
4951

5052
# support auto-basis determination
5153

src/classical/ultraspherical.jl

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ end
2828

2929
sum(w::UltrasphericalWeight{T}) where T = sqrt(convert(T,π))*exp(loggamma(one(T)/2 + w.λ)-loggamma(1+w.λ))
3030

31+
hasboundedendpoints(w::UltrasphericalWeight) = 2w.λ 1
32+
3133

3234
struct Ultraspherical{T,Λ} <: AbstractJacobi{T}
3335
λ::Λ
@@ -80,6 +82,19 @@ plan_transform(P::Ultraspherical{T}, szs::NTuple{N,Int}, dims...) where {T,N} =
8082
Jacobi(C::Ultraspherical{T}) where T = Jacobi(C.λ-one(T)/2,C.λ-one(T)/2)
8183

8284

85+
86+
######
87+
# Weighted Gram Matrix
88+
######
89+
90+
# 2^(1-2λ)*π*gamma(n+2λ)/((n+λ)*gamma(λ)^2 * n!)
91+
function weightedgrammatrix(P::Ultraspherical{T}) where T
92+
λ = P.λ
93+
n = 0:
94+
c = 2^(1-2λ) * convert(T,π)/gamma(λ)^2
95+
Diagonal(c * exp.(loggamma.(n .+ 2λ) .- loggamma.(n .+ 1) ) ./ (n .+ λ))
96+
end
97+
8398
########
8499
# Jacobi Matrix
85100
########
@@ -129,7 +144,11 @@ function diff(WS::Weighted{T,<:Ultraspherical}; dims=1) where T
129144
else
130145
n = (0:∞)
131146
A = _BandedMatrix((-one(T)/(2*-1)) * ((n.+1) .* (n .+ (2λ-1))))', ℵ₀, 1,-1)
132-
ApplyQuasiMatrix(*, Weighted(Ultraspherical{T}-1)), A)
147+
if λ == 3/2
148+
ApplyQuasiMatrix(*, Legendre{T}(), A)
149+
else
150+
ApplyQuasiMatrix(*, Weighted(Ultraspherical{T}-1)), A)
151+
end
133152
end
134153
end
135154

@@ -174,6 +193,8 @@ function \(U::Ultraspherical{<:Any,<:Integer}, C::ChebyshevU)
174193
U\Ultraspherical(C)
175194
end
176195

196+
197+
177198
\(T::Chebyshev, C::Ultraspherical) = inv(C \ T)
178199

179200
function \(C2::Ultraspherical{<:Any,<:Integer}, C1::Ultraspherical{<:Any,<:Integer})
@@ -209,29 +230,48 @@ function \(C2::Ultraspherical, C1::Ultraspherical)
209230
end
210231
end
211232

212-
function \(w_A::WeightedUltraspherical, w_B::WeightedUltraspherical)
213-
wA,A = w_A.args
214-
wB,B = w_B.args
233+
function \(w_A::Weighted{<:Any,<:Ultraspherical}, w_B::Weighted{<:Any,<:Ultraspherical})
234+
A = w_A.P
235+
B = w_B.P
215236
T = promote_type(eltype(w_A),eltype(w_B))
216237

217-
if wA == wB
218-
A \ B
219-
elseif B.λ == A.λ+1 && wB.λ == wA.λ+1 # Lower
238+
if A == B
239+
SquareEye{T}(ℵ₀)
240+
elseif B.λ == A.λ+1
220241
λ = convert(T,A.λ)
221242
_BandedMatrix(Vcat(((2λ:∞) .* ((2λ+1):∞) ./ (4λ .*+1:∞)))',
222243
Zeros{T}(1,∞),
223244
(-(1:∞) .* (2:∞) ./ (4λ .*+1:∞)))'), ℵ₀, 2,0)
245+
elseif B.λ > A.λ+1
246+
J = Weighted(Ultraspherical(B.λ-1))
247+
(w_A\J) * (J\w_B)
224248
else
225-
error("not implemented for $A and $wB")
249+
error("not implemented for $w_A and $w_B")
226250
end
227251
end
228252

229-
\(w_A::Weighted{<:Any,<:Ultraspherical}, w_B::Weighted{<:Any,<:Ultraspherical}) = convert(WeightedBasis, w_A) \ convert(WeightedBasis, w_B)
230253

231-
\(A::Legendre, wB::WeightedUltraspherical) = Ultraspherical(A) \ wB
232254

233-
function \(A::Ultraspherical, w_B::WeightedUltraspherical)
234-
(UltrasphericalWeight(zero(A.λ)) .* A) \ w_B
255+
function \(w_A::WeightedUltraspherical, w_B::WeightedUltraspherical)
256+
wA,A = w_A.args
257+
wB,B = w_B.args
258+
T = promote_type(eltype(w_A),eltype(w_B))
259+
260+
if wA == wB
261+
A \ B
262+
elseif wA.λ == A.λ && wB.λ == B.λ # weighted
263+
Weighted(A) \ Weighted(B)
264+
elseif wB.λ wA.λ+1 # lower
265+
J = UltrasphericalWeight(wB.λ-1) .* Ultraspherical(B.λ-1)
266+
(w_A\J) * (J\w_B)
267+
else
268+
error("not implemented for $w_A and $w_B")
269+
end
235270
end
236271

272+
\(w_A::WeightedUltraspherical, w_B::Weighted{<:Any,<:Ultraspherical}) = w_A \ convert(WeightedBasis,w_B)
273+
\(w_A::Weighted{<:Any,<:Ultraspherical}, w_B::WeightedUltraspherical) = convert(WeightedBasis,w_A) \ w_B
274+
\(A::Ultraspherical, w_B::Weighted{<:Any,<:Ultraspherical}) = A \ convert(WeightedBasis,w_B)
275+
\(A::Ultraspherical, w_B::WeightedUltraspherical) = (UltrasphericalWeight(one(A.λ)/2) .* A) \ w_B
276+
\(A::Legendre, wB::WeightedUltraspherical) = Ultraspherical(A) \ wB
237277

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,4 @@ end
7878
struct MyIncompleteJacobi <: ClassicalOrthogonalPolynomials.AbstractJacobi{Float64} end
7979
@test_throws ErrorException jacobimatrix(MyIncompleteJacobi())
8080
@test_throws ErrorException plan_transform(MyIncompleteJacobi(), 5)
81-
end
81+
end

test/test_odes.jl

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using ClassicalOrthogonalPolynomials, ContinuumArrays, QuasiArrays, BandedMatrices,
2-
SemiseparableMatrices, LazyArrays, ArrayLayouts, Test
2+
LazyArrays, ArrayLayouts, Test
3+
4+
using SemiseparableMatrices
35

46
import QuasiArrays: MulQuasiMatrix
57
import ClassicalOrthogonalPolynomials: oneto
6-
import ContinuumArrays: MappedBasisLayout, MappedWeightedBasisLayout
8+
import ContinuumArrays: MappedBasisLayout, MappedWeightedBasisLayout, weaklaplacian
79
import LazyArrays: arguments, ApplyMatrix, colsupport, MemoryLayout
810
import SemiseparableMatrices: VcatAlmostBandedLayout
911

@@ -246,4 +248,31 @@ import SemiseparableMatrices: VcatAlmostBandedLayout
246248
@test u[0.1] 1.6878004187402804
247249
end
248250
end
251+
252+
253+
@testset "ultraspherical ODE" begin
254+
@testset "strong form" begin
255+
C = Ultraspherical(3/2)
256+
W = Weighted(C)
257+
= C \ diff(diff(W))
258+
x = axes(W,1)
259+
X = C \ (x .* W)
260+
L =- X
261+
c = L \ transform(C, exp)
262+
u = W*c
263+
@test u[0.0] -0.536964648316337 # mathematica
264+
end
265+
266+
@testset "weak form" begin
267+
C = Ultraspherical(3/2)
268+
W = Weighted(C)
269+
Δ = weaklaplacian(W)
270+
x = axes(W,1)
271+
X = W' * (x .* W)
272+
L = Δ - X
273+
c = L \ (W'exp.(x))
274+
u = W*c
275+
@test u[0.0] -0.536964648316337 # mathematica
276+
end
277+
end
249278
end

test/test_ultraspherical.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using ClassicalOrthogonalPolynomials, ContinuumArrays, BandedMatrices, LazyArrays, ForwardDiff, Test
1+
using ClassicalOrthogonalPolynomials, ContinuumArrays, BandedMatrices, LazyArrays, Test
2+
using ForwardDiff
23
using LazyArrays: rowsupport, colsupport
34
using ClassicalOrthogonalPolynomials: grammatrix
45

@@ -72,8 +73,8 @@ using ClassicalOrthogonalPolynomials: grammatrix
7273
@test rowsupport(U\C,5) == rowsupport(T\C,5) == 5:
7374
end
7475
@testset "Legendre" begin
75-
@test Ultraspherical(0.5) \ (UltrasphericalWeight(0.0) .* Ultraspherical(0.5)) == Eye(∞)
76-
@test Legendre() \ (UltrasphericalWeight(0.0) .* Ultraspherical(0.5)) == Eye(∞)
76+
@test Ultraspherical(0.5) \ (UltrasphericalWeight(0.5) .* Ultraspherical(0.5)) == Eye(∞)
77+
@test Legendre() \ (UltrasphericalWeight(0.5) .* Ultraspherical(0.5)) == Eye(∞)
7778
@test (Legendre() \ Ultraspherical(1.5))[1:10,1:10] inv(Ultraspherical(1.5) \ Legendre())[1:10,1:10]
7879
@test UltrasphericalWeight(LegendreWeight()) == UltrasphericalWeight(1/2)
7980
end

0 commit comments

Comments
 (0)