Skip to content

Commit 6d0a561

Browse files
committed
fix some spacing, overflow, and cutoff, add docs
1 parent a3bfb4c commit 6d0a561

File tree

4 files changed

+52
-24
lines changed

4 files changed

+52
-24
lines changed

src/besselk.jl

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,11 @@ function besselk_positive_args(nu, x::T) where T <: Union{Float32, Float64}
226226
# dispatch to avoid uniform expansion when nu = 0
227227
iszero(nu) && return besselk0(x)
228228

229-
# pre-compute the uniform asymptotic expansion cutoff:
230-
debye_cut = besselik_debye_cutoff(nu, x)
231-
232-
# check if nu is a half-integer:
233-
(isinteger(nu-1/2) && !debye_cut) && return sphericalbesselk(nu-1/2, x)*SQRT_PID2(T)*sqrt(x)
229+
# check if nu is a half-integer
230+
(isinteger(nu-1/2) && sphericalbesselk_cutoff(nu)) && return sphericalbesselk_int(Int(nu-1/2), x)*SQPIO2(T)*sqrt(x)
234231

235232
# use uniform debye expansion if x or nu is large
236-
debye_cut && return besselk_large_orders(nu, x)
233+
besselik_debye_cutoff(nu, x) && return besselk_large_orders(nu, x)
237234

238235
# for integer nu use forward recurrence starting with K_0 and K_1
239236
isinteger(nu) && return besselk_up_recurrence(x, besselk1(x), besselk0(x), 1, nu)[1]
@@ -430,4 +427,3 @@ function besselk_power_series(v, x::T) where T
430427
end
431428
besselk_power_series_cutoff(nu, x::Float64) = x < 2.0 || nu > 1.6x - 1.0
432429
besselk_power_series_cutoff(nu, x::Float32) = x < 10.0f0 || nu > 1.65f0*x - 8.0f0
433-

src/constants.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,3 @@ const Q3_k1(::Type{Float64}) = (
284284
2.88448064302447607e1, 2.27912927104139732e0,
285285
2.50358186953478678e-2
286286
)
287-
288-
const SQRT_PID2(::Type{Float64}) = 1.2533141373155003
289-
const SQRT_PID2(::Type{Float32}) = 1.2533141f0

src/modifiedsphericalbessel.jl

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,58 @@
1-
1+
# Modified Spherical Bessel functions
2+
#
3+
# sphericalbesselk(nu, x)
4+
#
5+
# A numerical routine to compute the modified spherical bessel functions of the second kind.
6+
# For moderate sized integer orders, forward recurrence is used starting from explicit formulas for k0(x) [1] and k1(x) [2].
7+
# Large orders are determined from the uniform asymptotic expansions (see src/besselk.jl for details)
8+
# For non-integer orders, we directly call the besselk routine using the relation k_{n}(x) = sqrt(pi/(2x))*besselk(n+1/2, x) [3].
9+
#
10+
# [1] http://dlmf.nist.gov/10.49.E12
11+
# [2] http://dlmf.nist.gov/10.49.E13
12+
# [3] http://dlmf.nist.gov/10.47.E9
13+
#
214
"""
315
sphericalbesselk(nu, x::T) where T <: {Float32, Float64}
416
517
Computes `k_{ν}(x)`, the modified second-kind spherical Bessel function, and offers special branches for integer orders.
618
"""
7-
sphericalbesselk(nu, x) = _sphericalbesselk(nu, float(x))
19+
sphericalbesselk(nu::Real, x::Real) = _sphericalbesselk(nu, float(x))
20+
21+
_sphericalbesselk(nu, x::Float16) = Float16(_sphericalbesselk(nu, Float32(x)))
822

9-
function _sphericalbesselk(nu, x::T) where T
10-
isnan(x) && return NaN
11-
if isinteger(nu) && nu < 41.5
23+
function _sphericalbesselk(nu, x::T) where T <: Union{Float32, Float64}
24+
if ~isfinite(x)
25+
isnan(x) && return x
26+
isinf(x) && return zero(x)
27+
end
28+
if isinteger(nu) && sphericalbesselk_cutoff(nu)
1229
if x < zero(x)
1330
return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
1431
end
15-
# using ifelse here to hopefully cut out a branch on nu < 0 or not. The
16-
# symmetry here is that
32+
# using ifelse here to cut out a branch on nu < 0 or not.
33+
# The symmetry here is that
1734
# k_{-n} = (...)*K_{-n + 1/2}
1835
# = (...)*K_{|n| - 1/2}
1936
# = (...)*K_{|n|-1 + 1/2}
20-
# = k_{|n|-1}
37+
# = k_{|n|-1}
2138
_nu = ifelse(nu<zero(nu), -one(nu)-nu, nu)
2239
return sphericalbesselk_int(Int(_nu), x)
2340
else
24-
return inv(SQRT_PID2(T)*sqrt(x))*besselk(nu+1/2, x)
41+
return inv(SQPIO2(T)*sqrt(x))*besselk(nu+1/2, x)
2542
end
2643
end
44+
sphericalbesselk_cutoff(nu) = nu < 41.5
2745

2846
function sphericalbesselk_int(v::Int, x)
29-
b0 = inv(x)
30-
b1 = (x+one(x))/(x*x)
31-
iszero(v) && return b0*exp(-x)
47+
xinv = inv(x)
48+
b0 = exp(-x) * xinv
49+
b1 = b0 * (x + one(x)) * xinv
50+
iszero(v) && return b0
3251
_v = one(v)
3352
invx = inv(x)
3453
while _v < v
3554
_v += one(_v)
3655
b0, b1 = b1, b0 + (2*_v - one(_v))*b1*invx
3756
end
38-
exp(-x)*b1
57+
b1
3958
end
40-

test/sphericalbessel_test.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ x = 1e-15
88
@test Bessels.sphericalbessely(1, x) SpecialFunctions.sphericalbessely(1, x)
99
@test Bessels.sphericalbessely(5.5, x) SpecialFunctions.sphericalbessely(5.5, x)
1010
@test Bessels.sphericalbessely(10, x) SpecialFunctions.sphericalbessely(10, x)
11+
@test Bessels.sphericalbesselk(5.5, x) SpecialFunctions.besselk(5.5 + 1/2, x) * sqrt( 2 / (x*pi))
12+
@test Bessels.sphericalbesselk(10, x) SpecialFunctions.besselk(10 + 1/2, x) * sqrt( 2 / (x*pi))
1113

1214
# test zero
1315
@test isone(Bessels.sphericalbesselj(0, 0.0))
@@ -20,33 +22,48 @@ x = 1e-15
2022
@test Bessels.sphericalbessely(10, 0.0) == -Inf
2123
@test Bessels.sphericalbessely(290, 0.0) == -Inf
2224

25+
@test isinf(Bessels.sphericalbesselk(0, 0.0))
26+
@test isinf(Bessels.sphericalbesselk(4, 0.0))
27+
@test isinf(Bessels.sphericalbesselk(10.2, 0.0))
28+
2329
# test Inf
2430
@test iszero(Bessels.sphericalbesselj(1, Inf))
2531
@test iszero(Bessels.sphericalbesselj(10.2, Inf))
2632
@test iszero(Bessels.sphericalbessely(3, Inf))
2733
@test iszero(Bessels.sphericalbessely(4.5, Inf))
2834

35+
@test iszero(Bessels.sphericalbesselk(0, Inf))
36+
@test iszero(Bessels.sphericalbesselk(4, Inf))
37+
@test iszero(Bessels.sphericalbesselk(10.2, Inf))
38+
2939
# test NaN
3040
@test isnan(Bessels.sphericalbesselj(1.4, NaN))
3141
@test isnan(Bessels.sphericalbesselj(4.0, NaN))
3242
@test isnan(Bessels.sphericalbessely(1.4, NaN))
3343
@test isnan(Bessels.sphericalbessely(4.0, NaN))
3444

45+
@test isnan(Bessels.sphericalbesselk(1.4, NaN))
46+
@test isnan(Bessels.sphericalbesselk(4.0, NaN))
47+
3548
# test Float16, Float32 types
3649
@test Bessels.sphericalbesselj(Float16(1.4), Float16(1.2)) isa Float16
3750
@test Bessels.sphericalbessely(Float16(1.4), Float16(1.2)) isa Float16
3851
@test Bessels.sphericalbesselj(1.4f0, 1.2f0) isa Float32
3952
@test Bessels.sphericalbessely(1.4f0, 1.2f0) isa Float32
4053

54+
@test Bessels.sphericalbesselk(Float16(1.4), Float16(1.2)) isa Float16
55+
@test Bessels.sphericalbesselk(1.0f0, 1.2f0) isa Float32
4156

4257
for x in 0.5:1.0:100.0, v in [0, 1, 5.5, 8.2, 10]
4358
@test isapprox(Bessels.sphericalbesselj(v, x), SpecialFunctions.sphericalbesselj(v, x), rtol=1e-12)
4459
@test isapprox(Bessels.sphericalbessely(v, x), SpecialFunctions.sphericalbessely(v, x), rtol=1e-12)
60+
@test isapprox(Bessels.sphericalbesselk(v, x), SpecialFunctions.besselk(v+1/2, x) * sqrt( 2 / (x*pi)), rtol=1e-12)
4561
end
4662

4763
for x in 5.5:4.0:160.0, v in [20, 25.0, 32.4, 40.0, 45.12, 50.0, 55.2, 60.124, 70.23, 75.0, 80.0, 92.3, 100.0, 120.0]
4864
@test isapprox(Bessels.sphericalbesselj(v, x), SpecialFunctions.sphericalbesselj(v, x), rtol=3e-12)
4965
@test isapprox(Bessels.sphericalbessely(v, x), SpecialFunctions.sphericalbessely(v, x), rtol=3e-12)
66+
@test isapprox(Bessels.sphericalbesselk(v, x), SpecialFunctions.besselk(v+1/2, x) * sqrt( 2 / (x*pi)), rtol=1e-12)
5067
end
5168

5269
@test isapprox(Bessels.sphericalbessely(270, 240.0), SpecialFunctions.sphericalbessely(270, 240.0), rtol=3e-12)

0 commit comments

Comments
 (0)