Skip to content

Commit 6c18d2b

Browse files
committed
add support to compute both Jv and Yv and Hankel
1 parent 142e823 commit 6c18d2b

File tree

6 files changed

+147
-10
lines changed

6 files changed

+147
-10
lines changed

src/Bessels.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ include("Polynomials/besselj_polys.jl")
4242
include("asymptotics.jl")
4343
include("gamma.jl")
4444

45-
#include("hankel.jl")
45+
include("hankel.jl")
4646

4747
end

src/U_polynomials.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function besseljy_debye(v, x)
4141
return coef_Jn * Uk_Jn, coef_Yn * Uk_Yn
4242
end
4343

44-
besseljy_debye_cutoff(nu, x) = nu > 2.0 + 1.00035*x + Base.Math._approx_cbrt(Float64(302.681)*x) && x > 15
44+
besseljy_debye_cutoff(nu, x) = nu > 2.0 + 1.00035*x + Base.Math._approx_cbrt(Float64(302.681)*x) && nu > 15
4545

4646
"""
4747
hankel_debye(nu, x::T)

src/bessely.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ function bessely_positive_args(nu, x::T) where T
310310
hankel_debye_cutoff(nu, x) && return imag(hankel_debye(nu, x))
311311

312312
# use power series for small x and for when nu > x
313-
bessely_series_cutoff(nu, x) && return bessely_power_series(nu, x)
313+
bessely_series_cutoff(nu, x) && return bessely_power_series(nu, x)[1]
314314

315315
# for x ∈ (6, 19) we use Chebyshev approximation and forward recurrence
316-
besseljy_chebyshev_cutoff(nu, x) && return bessely_chebyshev(nu, x)
316+
besseljy_chebyshev_cutoff(nu, x) && return bessely_chebyshev(nu, x)[1]
317317

318318
# at this point x > 19.0 (for Float64) and fairly close to nu
319319
# shift nu down and use the debye expansion for Hankel function (valid x > nu) then use forward recurrence
@@ -340,6 +340,7 @@ end
340340
341341
Computes ``Y_{nu}(x)`` using the power series when nu is not an integer.
342342
In general, this is most accurate for small arguments and when nu > x.
343+
Outpus both (Y_{nu}(x), J_{nu}(x)).
343344
"""
344345
function bessely_power_series(v, x::T) where T
345346
MaxIter = 3000
@@ -358,7 +359,7 @@ function bessely_power_series(v, x::T) where T
358359
b *= -inv((-v + i + one(T)) * (i + one(T))) * t2
359360
end
360361
s, c = sincospi(v)
361-
return (out*c - out2) / s
362+
return (out*c - out2) / s, out
362363
end
363364
bessely_series_cutoff(v, x) = (x < 7.0) || v > 1.35*x - 4.5
364365

@@ -375,7 +376,7 @@ Forward recurrence is used to fill orders starting at low orders ν ∈ (0, 2).
375376
function bessely_chebyshev(v, x)
376377
v_floor, _ = modf(v)
377378
Y0, Y1 = bessely_chebyshev_low_orders(v_floor, x)
378-
return besselj_up_recurrence(x, Y1, Y0, v_floor + 1, v)[1]
379+
return besselj_up_recurrence(x, Y1, Y0, v_floor + 1, v)
379380
end
380381

381382
# only implemented for Float64 so far

src/hankel.jl

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,134 @@
1-
#= Aren't tested yet
1+
function besseljy(nu::Real, x::T) where T
2+
isinteger(nu) && return besseljy(Int(nu), x)
3+
abs_nu = abs(nu)
4+
abs_x = abs(x)
5+
6+
Ynu = bessely_positive_args(abs_nu, abs_x)
7+
Jnu = bessely_positive_args(abs_nu, abs_x)
8+
9+
if nu >= zero(T)
10+
if x >= zero(T)
11+
return Jnu, Ynu
12+
else
13+
return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
14+
#return Ynu * cispi(-nu) + 2im * besselj_positive_args(abs_nu, abs_x) * cospi(abs_nu)
15+
end
16+
else
17+
spi, cpi = sincospi(abs_nu)
18+
out = Jnu * cpi - Ynu * spi
19+
if x >= zero(T)
20+
return out, Ynu * cpi + Jnu * spi
21+
else
22+
return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
23+
#return cpi * (Ynu * cispi(nu) + 2im * Jnu * cpi) + Jnu * spi * cispi(abs_nu)
24+
end
25+
end
26+
end
27+
28+
function besseljy(nu::Integer, x::T) where T
29+
abs_nu = abs(nu)
30+
abs_x = abs(x)
31+
sg = iseven(abs_nu) ? 1 : -1
32+
33+
Jnu = besselj_positive_args(abs_nu, abs_x)
34+
Ynu = bessely_positive_args(abs_nu, abs_x)
35+
if nu >= zero(T)
36+
if x >= zero(T)
37+
return Jnu, Ynu
38+
else
39+
return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
40+
#return Jnu * sg, Ynu * sg + 2im * sg * besselj_positive_args(abs_nu, abs_x)
41+
end
42+
else
43+
if x >= zero(T)
44+
return Jnu * sg, Ynu * sg
45+
else
46+
spi, cpi = sincospi(abs_nu)
47+
return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
48+
#return (cpi*Jnu - spi*Ynu) * sg, Ynu + 2im * besselj_positive_args(abs_nu, abs_x)
49+
end
50+
end
51+
end
52+
53+
function besseljy_positive_args(nu::Real, x::T) where T
54+
nu == 0 && return (besselj0(x), bessely0(x))
55+
nu == 1 && return (besselj1(x), bessely1(x))
56+
iszero(x) && return (besselj_positive_args(nu, x), bessely_positive_args(nu, x))
57+
58+
# x < ~nu branch see src/U_polynomials.jl
59+
besseljy_debye_cutoff(nu, x) && return besseljy_debye(nu, x)
60+
61+
# large argument branch see src/asymptotics.jl
62+
besseljy_large_argument_cutoff(nu, x) && return besseljy_large_argument(nu, x)
63+
64+
# x > ~nu branch see src/U_polynomials.jl on computing Hankel function
65+
if hankel_debye_cutoff(nu, x)
66+
H = hankel_debye(nu, x)
67+
return real(H), imag(H)
68+
end
69+
70+
# use forward recurrence if nu is an integer up until the continued fraction becomes inefficient
71+
if isinteger(nu) && nu < 150
72+
Y0 = bessely0(x)
73+
Y1 = bessely1(x)
74+
Ynm1, Yn = besselj_up_recurrence(x, bessely1(x), bessely0(x), 1, nu-1)
75+
76+
ratio_Jv_Jvm1 = besselj_ratio_jnu_jnum1(nu, x)
77+
Jn = 2 /*x * (Ynm1 - Yn / ratio_Jv_Jvm1))
78+
return Jn, Yn
79+
end
80+
81+
# use power series for small x and for when nu > x
82+
if bessely_series_cutoff(nu, x) && besselj_series_cutoff(nu, x)
83+
Yn, Jn = bessely_power_series(nu, x)
84+
return Jn, Yn
85+
end
86+
87+
# for x ∈ (6, 19) we use Chebyshev approximation and forward recurrence
88+
if besseljy_chebyshev_cutoff(nu, x)
89+
Yn, Ynp1 = bessely_chebyshev(nu, x)
90+
ratio_Jvp1_Jv = besselj_ratio_jnu_jnum1(nu+1, x)
91+
Jnp1 = 2 /*x * (Yn - Ynp1 / ratio_Jvp1_Jv))
92+
return Jnp1 / ratio_Jvp1_Jv, Yn
93+
end
94+
95+
# at this point x > 19.0 (for Float64) and fairly close to nu
96+
# shift nu down and use the debye expansion for Hankel function (valid x > nu) then use forward recurrence
97+
nu_shift = floor(nu) - ceil(Int, -1.5 + x + Base.Math._approx_cbrt(-411*x))
98+
v2 = maximum((nu - nu_shift, modf(nu)[1] + 1))
99+
100+
Hnu = hankel_debye(v2, x)
101+
Hnum1 = hankel_debye(v2 - 1, x)
102+
# forward recurrence is stable for Hankel when x >= nu
103+
if x >= nu
104+
H = besselj_up_recurrence(x, Hnu, Hnum1, v2, nu)[1]
105+
return real(H), imag(H)
106+
else
107+
Yn, Ynp1 = besselj_up_recurrence(x, imag(Hnu), imag(Hnum1), v2, nu)
108+
ratio_Jvp1_Jv = besselj_ratio_jnu_jnum1(nu+1, x)
109+
Jnp1 = 2 /*x * (Yn - Ynp1 / ratio_Jvp1_Jv))
110+
return Jnp1 / ratio_Jvp1_Jv, Yn
111+
end
112+
end
113+
114+
function besselj_ratio_jnu_jnum1(n, x::T) where T
115+
MaxIter = 5000
116+
xinv = inv(x)
117+
xinv2 = 2 * xinv
118+
d = x / (n + n)
119+
a = d
120+
h = a
121+
b = muladd(2, n, 2) * xinv
122+
for _ in 1:MaxIter
123+
d = inv(b - d)
124+
a *= muladd(b, d, -1)
125+
h = h + a
126+
b = b + xinv2
127+
abs(a / h) <= eps(T) && break
128+
end
129+
return h
130+
end
131+
2132
function besselh(nu::Float64, k::Integer, x::AbstractFloat)
3133
if k == 1
4134
return complex(besselj(nu, x), bessely(nu, x))
@@ -11,4 +141,3 @@ end
11141

12142
hankelh1(nu, z) = besselh(nu, 1, z)
13143
hankelh2(nu, z) = besselh(nu, 2, z)
14-
=#

test/besselj_test.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ for v in nu, xx in x
8585
xx *= v
8686
sf = SpecialFunctions.besselj(BigFloat(v), BigFloat(xx))
8787
@test isapprox(besselj(v, xx), Float64(sf), rtol=5e-14)
88+
@test isapprox(Bessels.besseljy_positive_args(v, xx)[1], Float64(sf), rtol=5e-14)
8889
end
8990

9091
# test half orders (SpecialFunctions does not give big float precision)
@@ -94,15 +95,19 @@ x = [0.05, 0.1, 0.2, 0.4, 0.5, 0.6, 0.7, 0.75, 0.8, 0.85, 0.9, 0.92, 0.95, 0.97,
9495
nu = [0.1, 0.4567, 0.8123, 1.5, 2.5, 4.1234, 6.8, 12.3, 18.9, 28.2345, 38.1235, 51.23, 72.23435, 80.5, 98.5, 104.2]
9596
for v in nu, xx in x
9697
xx *= v
97-
@test isapprox(besselj(v, xx), SpecialFunctions.besselj(v, xx), rtol=1e-12)
98+
sf = SpecialFunctions.besselj(v, xx)
99+
@test isapprox(besselj(v, xx), sf, rtol=1e-12)
100+
@test isapprox(Bessels.besseljy_positive_args(v, xx)[1], sf, rtol=1e-12)
98101
end
99102

100103
## test large orders
101104
nu = [150, 165.2, 200.0, 300.0, 500.0, 1000.0, 5000.2, 10000.0, 50000.0]
102105
x = [0.2, 0.4, 0.5, 0.6, 0.7, 0.75, 0.8, 0.85, 0.9, 0.92,0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99,0.995, 0.999, 1.0, 1.01, 1.05, 1.08, 1.1, 1.2]
103106
for v in nu, xx in x
104107
xx *= v
105-
@test isapprox(besselj(v, xx), SpecialFunctions.besselj(v, xx), rtol=5e-11)
108+
sf = SpecialFunctions.besselj(v, xx)
109+
@test isapprox(besselj(v, xx), sf, rtol=5e-11)
110+
@test isapprox(Bessels.besseljy_positive_args(v, xx)[1], Float64(sf), rtol=5e-11)
106111
end
107112

108113
## test large arguments

test/bessely_test.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ for v in nu, xx in x
7575
xx *= v
7676
sf = SpecialFunctions.bessely(BigFloat(v), BigFloat(xx))
7777
@test isapprox(bessely(v, xx), Float64(sf), rtol=2e-13)
78+
@test isapprox(Bessels.besseljy_positive_args(v, xx)[2], Float64(sf), rtol=5e-12)
7879
end
7980

8081
# test decimal orders
@@ -85,6 +86,7 @@ nu = [0.1, 0.4567, 0.8123, 1.5, 2.5, 4.1234, 6.8, 12.3, 18.9, 28.2345, 38.1235,
8586
for v in nu, xx in x
8687
xx *= v
8788
@test isapprox(bessely(v, xx), SpecialFunctions.bessely(v, xx), rtol=5e-12)
89+
@test isapprox(Bessels.besseljy_positive_args(v, xx)[2], SpecialFunctions.bessely(v, xx), rtol=5e-12)
8890
end
8991

9092
# need to test accuracy of negative orders and negative arguments and all combinations within

0 commit comments

Comments
 (0)