Skip to content

Commit 12b6199

Browse files
update triangle.jl to remove ApproxFun dependency
1 parent a38d815 commit 12b6199

File tree

2 files changed

+76
-36
lines changed

2 files changed

+76
-36
lines changed

examples/sphere.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
# To verify, we sample the function on a 5×9 tensor product grid
99
# at equispaced points-in-angle defined by:
1010
#
11-
# θₙ = (n+1/2)π/N, for 0 ≤ n < N,
12-
#
13-
# and
11+
# θₙ = (n+1/2)π/N, for 0 ≤ n < N, and
1412
#
1513
# φₘ = 2π m/M, for 0 ≤ m < M;
1614
#
1715
# we convert the function samples to Fourier coefficients using
18-
# `FastTransforms.plan_analysis`; and finally, we transform
16+
# `plan_sph_analysis`; and finally, we transform
1917
# the Fourier coefficients to spherical harmonic coefficients using
2018
# `plan_sph2fourier`.
2119
#
@@ -33,7 +31,7 @@ function threshold!(A::AbstractArray, ϵ)
3331
A
3432
end
3533

36-
using FastTransforms
34+
using FastTransforms, LinearAlgebra
3735

3836
# The colatitudinal grid (mod π):
3937
N = 5

examples/triangle.jl

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,89 @@
11
#############
2-
# This demonstrates the Triangle harmonic transform and inverse transform,
3-
# explaining precisely the normalization and points
2+
# In this example, we sample a bivariate function:
43
#
5-
# Note we use the duffy map
6-
# x == (s+1)/2
7-
# y== (t+1)/2*(1-(s+1)/2)
4+
# f(x,y) = 1/(1+x^2+y^2),
5+
#
6+
# on the reference triangle with vertices (0,0), (0,1), and (1,0) and analyze it
7+
# in a Proriol series. Then, we find Proriol series for each component of its
8+
# gradient by term-by-term differentiation of our expansion, and we compare them
9+
# with the true Proriol series by sampling an exact expression for the gradient.
10+
#
11+
# We analyze f(x,y) on an N×M mapped tensor product grid defined by:
12+
#
13+
# x = (1+u)/2, and y = (1-u)*(1+v)/4, where:
14+
#
15+
# uₙ = cos[(n+1/2)π/N], for 0 ≤ n < N, and
16+
#
17+
# vₘ = cos[(m+1/2)π/M], for 0 ≤ m < M;
18+
#
19+
# we convert the function samples to mapped Chebyshev² coefficients using
20+
# `plan_tri_analysis`; and finally, we transform the mapped Chebyshev²
21+
# coefficients to Proriol coefficients using `plan_tri2cheb`.
22+
#
23+
# For the storage pattern of the arrays, please consult the documentation.
824
#############
925

26+
using FastTransforms, LinearAlgebra
1027

11-
using ApproxFun, FastTransforms
28+
f = (x,y) -> 1/(1+x^2+y^2)
29+
fx = (x,y) -> -2x/(1+x^2+y^2)^2
30+
fy = (x,y) -> -2y/(1+x^2+y^2)^2
1231

13-
jacobinorm(n,a,b) = if n 0
14-
sqrt((2n+a+b+1))*exp((lgamma(n+a+b+1)+lgamma(n+1)-log(2)*(a+b+1)-lgamma(n+a+1)-lgamma(n+b+1))/2)
15-
else
16-
sqrt(exp(lgamma(a+b+2)-log(2)*(a+b+1)-lgamma(a+1)-lgamma(b+1)))
17-
end
18-
njacobip(n,a,b,x) = jacobinorm(n,a,b) * jacobip(n,a,b,x)
32+
N = 10
33+
M = N
34+
35+
α, β, γ = 0, 0, 0
1936

20-
P = (ℓ,m,x,y) -> (2*(1-x))^m*njacobip(ℓ-m,2m,0,2x-1)*njacobip(m,-0.5,-0.5,2y/(1-x)-1)
37+
P = plan_tri2cheb(Float64, N, α, β, γ)
38+
Px = plan_tri2cheb(Float64, N, α+1, β, γ+1)
39+
Py = plan_tri2cheb(Float64, N, α, β+1, γ+1)
40+
PA = plan_tri_analysis(Float64, N, M)
2141

42+
u = [sinpi((N-2n-1)/(2N)) for n in 0:N-1]
43+
v = [sinpi((M-2m-1)/(2M)) for m in 0:M-1]
2244

45+
# Instead of using the u, v grid, we use one with more accuracy near the origin.
46+
x = [sinpi((2N-2n-1)/(4N))^2 for n in 0:N-1]
47+
w = [sinpi((2M-2m-1)/(4M))^2 for m in 0:M-1]
2348

24-
p_T = chebyshevpoints(40)
25-
f = (x,y) -> exp(x + cos(y))
26-
= (s,t) -> f((s+1)/2, (t+1)/2*(1-(s+1)/2))
49+
(1 .+ u)./2 x
50+
(1 .- u).*(1 .+ v')/4 reverse(x).*w'
2751

28-
F = .(p_T, p_T')
29-
for j = 1:size(F,2)
30-
F[:,j] = chebyshevtransform(F[:,j])
31-
end
32-
for k = 1:size(F,1)
33-
F[k,:] = chebyshevtransform(F[k,:])
34-
end
52+
# On the mapped tensor product grid, our function samples are:
53+
F = [f(x[n+1], x[N-n]*w[m+1]) for n in 0:N-1, m in 0:M-1]
3554

36-
= cheb2tri(F, 0.0, -0.5, -0.5)
55+
# Its Proriol-(α,β,γ) coefficients are:
56+
U = P\(PA*F)
3757

58+
# Similarly, our function's gradient samples are:
59+
Fx = [fx(x[n+1], x[N-n]*w[m+1]) for n in 0:N-1, m in 0:M-1]
60+
Fy = [fy(x[n+1], x[N-n]*w[m+1]) for n in 0:N-1, m in 0:M-1]
3861

39-
= function(x,y)
40-
ret = 0.0
41-
for j=1:size(F,2), k=1:size(F,1)-j+1
42-
ret += F̌[k,j] * P(k+j-2,j-1,x,y)
43-
end
44-
ret
62+
# For the partial derivative with respect to x, Olver et al.
63+
# derive simple expressions for the representation of this component
64+
# using a Proriol-(α+1,β,γ+1) series. For the partial derivative with respect
65+
# to y, the analogous formulae result in a Proriol-(α,β+1,γ+1) series.
66+
# These expressions are adapted from https://arxiv.org/abs/1902.04863.
67+
Gx = zeros(Float64, N, M)
68+
for m = 0:M-2
69+
for n = 0:N-2
70+
cf1 = m == 0 ? sqrt((n+1)*(n+2m+α+β+γ+3)/(2m+β+γ+2)*(m+γ+1)*8) : sqrt((n+1)*(n+2m+α+β+γ+3)/(2m+β+γ+1)*(m+β+γ+1)/(2m+β+γ+2)*(m+γ+1)*8)
71+
cf2 = sqrt((n+α+1)*(m+1)/(2m+β+γ+2)*(m+β+1)/(2m+β+γ+3)*(n+2m+β+γ+3)*8)
72+
Gx[n+1, m+1] = cf1*U[n+2, m+1] + cf2*U[n+1, m+2]
4573
end
74+
end
75+
Ux = Px\(PA*Fx)
76+
77+
Gy = zeros(Float64, N, M)
78+
for m = 0:M-2
79+
for n = 0:N-2
80+
Gy[n+1, m+1] = 4*sqrt((m+1)*(m+β+γ+2))*U[n+1, m+2]
81+
end
82+
end
83+
Uy = Py\(PA*Fy)
84+
85+
# The 2-norm relative error in differentiating the Proriol series
86+
# for f(x,y) term-by-term and its sampled gradient is:
87+
hypot(norm(Ux-Gx), norm(Uy-Gy))/hypot(norm(Ux), norm(Uy))
4688

47-
(0.1,0.2) f(0.1,0.2)
89+
# This error can be improved upon by increasing N and M.

0 commit comments

Comments
 (0)