module Simulation.Aivika.Dynamics.Internal.Dynamics
(
Dynamics(..),
DynamicsLift(..),
Point(..),
runDynamicsInStart,
runDynamicsInFinal,
runDynamics,
basicTime,
iterationBnds,
iterationHiBnd,
iterationLoBnd,
phaseBnds,
phaseHiBnd,
phaseLoBnd) where
import Control.Monad
import Control.Monad.Trans
import Simulation.Aivika.Dynamics.Internal.Simulation
newtype Dynamics a = Dynamics (Point -> IO a)
data Point = Point { pointSpecs :: Specs,
pointRun :: Run,
pointTime :: Double,
pointIteration :: Int,
pointPhase :: Int
} deriving (Eq, Ord, Show)
iterations :: Specs -> [Int]
iterations sc = [i1 .. i2] where
i1 = 0
i2 = round ((spcStopTime sc
spcStartTime sc) / spcDT sc)
iterationBnds :: Specs -> (Int, Int)
iterationBnds sc = (0, round ((spcStopTime sc
spcStartTime sc) / spcDT sc))
iterationLoBnd :: Specs -> Int
iterationLoBnd sc = 0
iterationHiBnd :: Specs -> Int
iterationHiBnd sc = round ((spcStopTime sc
spcStartTime sc) / spcDT sc)
phases :: Specs -> [Int]
phases sc =
case spcMethod sc of
Euler -> [0]
RungeKutta2 -> [0, 1]
RungeKutta4 -> [0, 1, 2, 3]
phaseBnds :: Specs -> (Int, Int)
phaseBnds sc =
case spcMethod sc of
Euler -> (0, 0)
RungeKutta2 -> (0, 1)
RungeKutta4 -> (0, 3)
phaseLoBnd :: Specs -> Int
phaseLoBnd sc = 0
phaseHiBnd :: Specs -> Int
phaseHiBnd sc =
case spcMethod sc of
Euler -> 0
RungeKutta2 -> 1
RungeKutta4 -> 3
basicTime :: Specs -> Int -> Int -> Double
basicTime sc n ph =
if ph < 0 then
error "Incorrect phase: basicTime"
else
spcStartTime sc + n' * spcDT sc + delta (spcMethod sc) ph
where n' = fromInteger (toInteger n)
delta Euler 0 = 0
delta RungeKutta2 0 = 0
delta RungeKutta2 1 = spcDT sc
delta RungeKutta4 0 = 0
delta RungeKutta4 1 = spcDT sc / 2
delta RungeKutta4 2 = spcDT sc / 2
delta RungeKutta4 3 = spcDT sc
instance Monad Dynamics where
return = returnD
m >>= k = bindD m k
returnD :: a -> Dynamics a
returnD a = Dynamics (\p -> return a)
bindD :: Dynamics a -> (a -> Dynamics b) -> Dynamics b
bindD (Dynamics m) k =
Dynamics $ \p ->
do a <- m p
let Dynamics m' = k a
m' p
runDynamicsInStart :: Dynamics a -> Simulation a
runDynamicsInStart (Dynamics m) =
Simulation $ \r ->
do let sc = runSpecs r
n = 0
t = spcStartTime sc
m Point { pointSpecs = sc,
pointRun = r,
pointTime = t,
pointIteration = n,
pointPhase = 0 }
runDynamicsInFinal :: Dynamics a -> Simulation a
runDynamicsInFinal (Dynamics m) =
Simulation $ \r ->
do let sc = runSpecs r
n = iterationHiBnd sc
t = basicTime sc n 0
m Point { pointSpecs = sc,
pointRun = r,
pointTime = t,
pointIteration = n,
pointPhase = 0 }
runDynamics :: Dynamics a -> Simulation [IO a]
runDynamics (Dynamics m) =
Simulation $ \r ->
do let sc = runSpecs r
(nl, nu) = iterationBnds sc
point n = Point { pointSpecs = sc,
pointRun = r,
pointTime = basicTime sc n 0,
pointIteration = n,
pointPhase = 0 }
return $ map (m . point) [nl .. nu]
instance Functor Dynamics where
fmap = liftMD
instance Eq (Dynamics a) where
x == y = error "Can't compare dynamics."
instance Show (Dynamics a) where
showsPrec _ x = showString "<< Dynamics >>"
liftMD :: (a -> b) -> Dynamics a -> Dynamics b
liftMD f (Dynamics x) =
Dynamics $ \p -> do { a <- x p; return $ f a }
liftM2D :: (a -> b -> c) -> Dynamics a -> Dynamics b -> Dynamics c
liftM2D f (Dynamics x) (Dynamics y) =
Dynamics $ \p -> do { a <- x p; b <- y p; return $ f a b }
instance (Num a) => Num (Dynamics a) where
x + y = liftM2D (+) x y
x y = liftM2D () x y
x * y = liftM2D (*) x y
negate = liftMD negate
abs = liftMD abs
signum = liftMD signum
fromInteger i = return $ fromInteger i
instance (Fractional a) => Fractional (Dynamics a) where
x / y = liftM2D (/) x y
recip = liftMD recip
fromRational t = return $ fromRational t
instance (Floating a) => Floating (Dynamics a) where
pi = return pi
exp = liftMD exp
log = liftMD log
sqrt = liftMD sqrt
x ** y = liftM2D (**) x y
sin = liftMD sin
cos = liftMD cos
tan = liftMD tan
asin = liftMD asin
acos = liftMD acos
atan = liftMD atan
sinh = liftMD sinh
cosh = liftMD cosh
tanh = liftMD tanh
asinh = liftMD asinh
acosh = liftMD acosh
atanh = liftMD atanh
instance MonadIO Dynamics where
liftIO m = Dynamics $ const m
instance SimulationLift Dynamics where
liftSimulation = liftDS
liftDS :: Simulation a -> Dynamics a
liftDS (Simulation m) =
Dynamics $ \p -> m $ pointRun p
class Monad m => DynamicsLift m where
liftDynamics :: Dynamics a -> m a