BesselJ-0.2.0.0: src/Math/BesselJ.hs
module Math.BesselJ
( BesselResult(..), besselJ )
where
import Data.Complex ( imagPart, realPart, Complex(..) )
import Numerical.Integration ( integration, IntegralResult(..) )
import Math.Gamma ( Gamma(gamma) )
import Foreign.C ( CDouble )
-- | Data type to store the result of a computation of the Bessel J-function.
-- The fields are @_result@ for the value, @_errors@ for the error estimates
-- of the integrals used for the computation, and @_codes@ for the convergence
-- codes of these integrals (0 for success).
data BesselResult = BesselResult {
_result :: Complex Double
, _errors :: (Double, Double)
, _codes :: (Int, Int)
} deriving Show
cpxdbl2cpxcdbl :: Complex Double -> Complex CDouble
cpxdbl2cpxcdbl z = realToFrac (realPart z) :+ realToFrac (imagPart z)
dbl2cdbl :: Double -> CDouble
dbl2cdbl = realToFrac
-- | Bessel-J function. It is computed with two integrals. The field @_errors@
-- in the result are the error estimates of the integrals. The field @_codes@
-- is the code indicating success (0) or failure.
besselJn :: Int -- ^ order
-> Complex Double -- ^ variable
-> Double -- ^ target relative error accuracy for the integrals
-> Int -- ^ number of subdivisions for the integrals
-> IO BesselResult -- ^ result
besselJn n z err subdiv = do
let z' = cpxdbl2cpxcdbl z
n' = dbl2cdbl $ fromIntegral n
a = realPart z'
b = imagPart z'
re <- integration
(\t -> (cos (a * sin t + n' * t) * cosh (b * sin t)) / pi)
0 pi 0.0 err subdiv
im <- integration
(\t -> -(sin (a * sin t + n' * t) * sinh (b * sin t)) / pi)
0 pi 0.0 err subdiv
return BesselResult {
_result = _value re :+ _value im
, _errors = (_error re, _error im)
, _codes = (_code re, _code im)
}
-- Re(t^z)
realPartTpowz :: CDouble -> Complex CDouble -> CDouble
realPartTpowz t z =
let x = realPart z
y = imagPart z
in
t**x * cos (y * log t)
-- Im(t^z)
imagPartTpowz :: CDouble -> Complex CDouble -> CDouble
imagPartTpowz t z =
let x = realPart z
y = imagPart z
in
t**x * sin (y * log t)
-- Re(cos(z * cos(t)))
reCosZcosT :: CDouble -> Complex CDouble -> CDouble
reCosZcosT t z =
let x = realPart z
y = imagPart z
in
cos (x * cos t) * cosh (y * cos t)
-- Im(cos(z * cos(t)))
imCosZcosT :: CDouble -> Complex CDouble -> CDouble
imCosZcosT t z =
let x = realPart z
y = imagPart z
in -sin (x * cos t) * sinh (y * cos t)
-- | Bessel-J function. It is computed with two integrals. The field @_errors@
-- in the result provides the error estimates of the integrals. The field
-- @_codes@ provides the codes indicating success (0) or failure of each integral.
besselJnu :: Complex Double -- ^ order, complex number with real part > -0.5
-> Complex Double -- ^ the variable, a complex number
-> Double -- ^ target relative accuracy for the integrals, e.g. 1e-5
-> Int -- ^ number of subdivisions for the integrals, e.g. 5000
-> IO BesselResult -- ^ result
besselJnu nu z err subdiv = do
let z' = cpxdbl2cpxcdbl z
nu' = cpxdbl2cpxcdbl nu
reintegrand t = reCosZcosT t z' * realPartTpowz (sin t) (2*nu')
- imCosZcosT t z' * imagPartTpowz (sin t) (2*nu')
imintegrand t = reCosZcosT t z' * imagPartTpowz (sin t) (2*nu')
+ imCosZcosT t z' * realPartTpowz (sin t) (2*nu')
cst = (z/2)**nu / (sqrt pi * gamma (nu + 0.5))
re <- integration reintegrand 0 pi 0.0 err subdiv
im <- integration imintegrand 0 pi 0.0 err subdiv
return BesselResult {
_result = cst * (_value re :+ _value im)
, _errors = (_error re, _error im)
, _codes = (_code re, _code im)
}
isInteger :: Complex Double -> Bool
isInteger z = y == 0 && x == fromIntegral (floor x :: Int)
where
x = realPart z
y = imagPart z
asInteger :: Complex Double -> Int
asInteger z = floor (realPart z) :: Int
-- | Bessel-J function. It is computed with two integrals. The field @_errors@
-- in the result provides the error estimates of the integrals. The field
-- @_codes@ provides the codes indicating success (0) or failure of each integral.
besselJ :: Complex Double -- ^ order, integer or complex number with real part > -0.5
-> Complex Double -- ^ the variable, a complex number
-> Double -- ^ target relative accuracy for the integrals, e.g. 1e-5
-> Int -- ^ number of subdivisions for the integrals, e.g. 5000
-> IO BesselResult -- ^ result
besselJ nu z err subdiv
| isInteger nu = besselJn (asInteger nu) z err subdiv
| realPart nu > -0.5 = besselJnu nu z err subdiv
| otherwise = error "Invalid value of the order."