Skip to content

Commit a90d68a

Browse files
committed
add more generics
1 parent 41ae451 commit a90d68a

File tree

2 files changed

+44
-35
lines changed

2 files changed

+44
-35
lines changed

src/sphericalbessel.jl

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,44 @@
2222
Spherical bessel function of the first kind of order `nu`, ``j_ν(x)``. This is the non-singular
2323
solution to the radial part of the Helmholz equation in spherical coordinates.
2424
"""
25-
function sphericalbesselj(nu::Real, x::T) where T
25+
sphericalbesselj(nu::Real, x::Real) = _sphericalbesselj(nu, float(x))
26+
27+
_sphericalbesselj(nu, x::Float16) = Float16(_sphericalbesselj(nu, Float32(x)))
28+
29+
function _sphericalbesselj(nu::Real, x::T) where T <: Union{Float32, Float64}
2630
isnan(nu) || isnan(x) && return NaN
2731
x < zero(T) && return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
2832

29-
if nu < zero(T)
30-
return SQPIO2(T) * besselj(nu + 1/2, x) / sqrt(x)
31-
else
32-
return sphericalbesselj_positive_args(nu, x)
33-
end
33+
return nu < zero(T) ? sphericalbesselj_generic(nu, x) : sphericalbesselj_positive_args(nu, x)
3434
end
3535

36+
sphericalbesselj_generic(nu, x::T) where T = SQPIO2(T) * besselj(nu + one(T)/2, x) / sqrt(x)
37+
3638
#####
3739
##### Positive arguments for `sphericalbesselj`
3840
#####
3941

4042
function sphericalbesselj_positive_args(nu::Real, x::T) where T
41-
if x^2 / (4*nu + 110) < eps(T)
42-
# small arguments power series expansion
43-
x2 = x^2 / 4
44-
coef = evalpoly(x2, (1, -inv(3/2 + nu), -inv(5 + nu), -inv(21/2 + nu), -inv(18 + nu)))
45-
a = sqrt(T(pi)/2) / (gamma(T(3)/2 + nu) * 2^(nu + T(1)/2))
46-
return x^nu * a * coef
47-
elseif isinteger(nu)
48-
if (x >= nu && nu < 250) || (x < nu && nu < 60)
49-
return sphericalbesselj_recurrence(Int(nu), x)
50-
else
51-
return SQPIO2(T) * besselj(nu + 1/2, x) / sqrt(x)
52-
end
53-
else
54-
return SQPIO2(T) * besselj(nu + 1/2, x) / sqrt(x)
55-
end
43+
isinteger(nu) && return sphericalbesselj_positive_args(Int(nu), x)
44+
return sphericalbesselj_small_args_cutoff(nu, x) ? sphericalbesselj_small_args(nu, x) : sphericalbesselj_generic(nu, x)
45+
end
46+
47+
function sphericalbesselj_positive_args(nu::Integer, x::T) where T
48+
sphericalbesselj_small_args_cutoff(nu, x) && return sphericalbesselj_small_args(nu, x)
49+
return (x >= nu && nu < 250) || (x < nu && nu < 60) ? sphericalbesselj_recurrence(nu, x) : sphericalbesselj_generic(nu, x)
50+
end
51+
52+
#####
53+
##### Power series expansion for small arguments
54+
#####
55+
56+
sphericalbesselj_small_args_cutoff(nu, x::T) where T = x^2 / (4*nu + 110) < eps(T)
57+
58+
function sphericalbesselj_small_args(nu, x::T) where T
59+
x2 = x^2 / 4
60+
coef = evalpoly(x2, (1, -inv(T(3)/2 + nu), -inv(5 + nu), -inv(T(21)/2 + nu), -inv(18 + nu)))
61+
a = sqrt(T(pi)/2) / (gamma(T(3)/2 + nu) * 2^(nu + T(1)/2))
62+
return x^nu * a * coef
5663
end
5764

5865
#####
@@ -62,7 +69,7 @@ end
6269
# very accurate approach however need to consider some performance issues
6370
# if recurrence is stable (x>=nu) can generate very fast up to orders around 250
6471
# for larger orders it is more efficient to use other expansions
65-
# if (x<nu) we can use forward recurrence from sphericalbesselj_recurrence and
72+
# if (x<nu) we can use forward recurrence from sphericalbessely_recurrence and
6673
# then use a continued fraction approach. However, for largish orders (>60) the
6774
# continued fraction is slower converging and more efficient to use other methods
6875
function sphericalbesselj_recurrence(nu::Integer, x::T) where T
@@ -98,30 +105,32 @@ Spherical bessel function of the second kind at order `nu`, ``y_ν(x)``. This is
98105
solution to the radial part of the Helmholz equation in spherical coordinates. Sometimes
99106
known as a spherical Neumann function.
100107
"""
101-
function sphericalbessely(nu::Real, x::T) where T
108+
sphericalbessely(nu::Real, x::Real) = _sphericalbessely(nu, float(x))
109+
110+
_sphericalbessely(nu, x::Float16) = Float16(_sphericalbessely(nu, Float32(x)))
111+
112+
function _sphericalbessely(nu::Real, x::T) where T <: Union{Float32, Float64}
102113
isnan(nu) || isnan(x) && return NaN
103114
x < zero(T) && return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
104-
abs_nu = abs(nu)
105-
106-
if nu < zero(T)
107-
return SQPIO2(T) * bessely(nu + 1/2, x) / sqrt(x)
108-
else
109-
return sphericalbessely_positive_args(nu, x)
110-
end
115+
return nu < zero(T) ? sphericalbessely_generic(nu, x) : sphericalbessely_positive_args(nu, x)
111116
end
112117

118+
sphericalbessely_generic(nu, x::T) where T = SQPIO2(T) * bessely(nu + one(T)/2, x) / sqrt(x)
119+
113120
#####
114121
##### Positive arguments for `sphericalbesselj`
115122
#####
116123

117-
function sphericalbessely_positive_args(nu::Real, x::T) where T
124+
sphericalbessely_positive_args(nu::Real, x) = isinteger(nu) ? sphericalbessely_positive_args(Int(nu), x) : sphericalbessely_generic(nu, x)
125+
126+
function sphericalbessely_positive_args(nu::Integer, x::T) where T
118127
if besseljy_debye_cutoff(nu, x)
119128
# for very large orders use expansion nu >> x to avoid overflow in recurrence
120129
return SQPIO2(T) * besseljy_debye(nu + 1/2, x)[2] / sqrt(x)
121-
elseif isinteger(nu) && nu < 250
122-
return sphericalbessely_forward_recurrence(Int(nu), x)[1]
130+
elseif nu < 250
131+
return sphericalbessely_forward_recurrence(nu, x)[1]
123132
else
124-
return SQPIO2(T) * bessely(nu + 1/2, x) / sqrt(x)
133+
return sphericalbessely_generic(nu, x)
125134
end
126135
end
127136

test/sphericalbessel_test.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ x = 1e-15
99
@test Bessels.sphericalbessely(5.5, x) SpecialFunctions.sphericalbessely(5.5, x)
1010
@test Bessels.sphericalbessely(10, x) SpecialFunctions.sphericalbessely(10, x)
1111

12-
for x in 0.5:1.0:100.0, v in [0, 1, 5.5, 10]
12+
for x in 0.5:1.0:100.0, v in [0, 1, 5.5, 8.2, 10]
1313
@test isapprox(Bessels.sphericalbesselj(v, x), SpecialFunctions.sphericalbesselj(v, x), rtol=1e-12)
1414
@test isapprox(Bessels.sphericalbessely(v, x), SpecialFunctions.sphericalbessely(v, x), rtol=1e-12)
1515
end

0 commit comments

Comments
 (0)