module Math.SphericalHarmonics
(
SphericalHarmonicModel(..)
, combine
, scale
, evaluateModel
, evaluateModelGradient
, evaluateModelGradientInLocalTangentPlane
)
where
import Math.SphericalHarmonics.AssociatedLegendre
import Numeric.AD
data SphericalHarmonicModel a = SphericalHarmonicModel
{
modelDegree :: Int
, referenceRadius :: a
, coefficients :: [(a, a)]
}
deriving (Functor)
combine :: (Num a, Eq a) => SphericalHarmonicModel a -> SphericalHarmonicModel a -> SphericalHarmonicModel a
combine m1 m2 | (referenceRadius m1 /= referenceRadius m2) = error "Incompatible model reference radii."
| otherwise = SphericalHarmonicModel
{
modelDegree = max (modelDegree m1) (modelDegree m2)
, referenceRadius = referenceRadius m1
, coefficients = combineCoefficients (coefficients m1) (coefficients m2)
}
where
combineCoefficients c1 c2 = take (max (length c1) (length c2)) $ zipWith addPairs (c1 ++ repeat (0,0)) (c2 ++ repeat (0,0))
addPairs (g1, h1) (g2, h2) = (g1 + g2, h1 + h2)
scale :: (Num a) => a -> SphericalHarmonicModel a -> SphericalHarmonicModel a
scale x m = m { coefficients = fmap scalePair (coefficients m) }
where
scalePair (g, h) = (x * g, x * h)
evaluateModel :: (Floating a, Ord a) => SphericalHarmonicModel a
-> a
-> a
-> a
-> a
evaluateModel model r colat lon = refR * sumOverDegree
where
refR = referenceRadius model
deg = modelDegree model
gs = map fst $ coefficients model
hs = map snd $ coefficients model
sumOverDegree = sum $ fmap degreeTerm [0..deg]
degreeTerm n = ((refR / r) ^ (n + 1)) * (sum $ fmap (orderTerm n) [0..n])
orderTerm n m = lonFactor * (p (cos colat))
where
scaledLon = lon * fromIntegral m
lonFactor = (g * cos scaledLon) + (h * sin scaledLon)
p = schmidtSemiNormalizedAssociatedLegendreFunction n m
g = gs !! computeIndex n m
h = hs !! computeIndex n m
evaluateModelGradient :: (Floating a, Ord a) => SphericalHarmonicModel a
-> a
-> a
-> a
-> (a, a, a)
evaluateModelGradient model r colat lon = makeTuple . fmap negate $ modelGrad [r, colat, lon]
where
modelGrad = grad (\[r', c', l'] -> evaluateModel (fmap auto model) r' c' l')
makeTuple [x, y, z] = (x, y, z)
evaluateModelGradientInLocalTangentPlane :: (Floating a, Ord a) => SphericalHarmonicModel a
-> a
-> a
-> a
-> (a, a, a)
evaluateModelGradientInLocalTangentPlane model r colat lon = (e, n, u)
where
(r', colat', lon') = evaluateModelGradient model r colat lon
e = lon' / (r * sin colat)
n = colat' / r
u = r'
computeIndex :: Int -> Int -> Int
computeIndex n m = triangle n + m
triangle :: Int -> Int
triangle n = (n * (n + 1)) `div` 2