module Simulation.Aivika.Lattice.Internal.LIO
(LIOParams(..),
LIO(..),
LIOLattice(..),
lattice,
newRandomLattice,
newRandomLatticeWithProb,
invokeLIO,
runLIO,
lioParams,
rootLIOParams,
parentLIOParams,
upSideLIOParams,
downSideLIOParams,
shiftLIOParams,
lioParamsAt,
latticeTimeIndex,
latticeMemberIndex,
latticeParentMemberIndex,
latticeTime,
latticeTimes,
latticeTimeStep,
latticePoint,
latticeSize,
findLatticeTimeIndex) where
import Data.IORef
import Data.Maybe
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Fix
import Control.Exception (throw, catch, finally)
import Simulation.Aivika.Trans.Exception
import Simulation.Aivika.Trans.Internal.Types
import Simulation.Aivika.Trans.Internal.Specs
import Simulation.Aivika.Trans.Parameter
import Simulation.Aivika.Lattice.Internal.Lattice
newtype LIO a = LIO { unLIO :: LIOParams -> IO a
}
data LIOParams =
LIOParams { lioLattice :: LIOLattice,
lioTimeIndex :: !Int,
lioMemberIndex :: !Int
}
instance Monad LIO where
return = LIO . const . return
(LIO m) >>= k = LIO $ \ps ->
m ps >>= \a ->
let m' = unLIO (k a) in m' ps
instance Applicative LIO where
pure = return
(<*>) = ap
instance Functor LIO where
fmap f (LIO m) = LIO $ fmap f . m
instance MonadIO LIO where
liftIO = LIO . const . liftIO
instance MonadFix LIO where
mfix f =
LIO $ \ps ->
do { rec { a <- invokeLIO ps (f a) }; return a }
instance MonadException LIO where
catchComp (LIO m) h = LIO $ \ps ->
catch (m ps) (\e -> unLIO (h e) ps)
finallyComp (LIO m1) (LIO m2) = LIO $ \ps ->
finally (m1 ps) (m2 ps)
throwComp e = LIO $ \ps ->
throw e
invokeLIO :: LIOParams -> LIO a -> IO a
invokeLIO ps (LIO m) = m ps
runLIO :: LIOLattice -> LIO a -> IO a
runLIO lattice m = unLIO m $ rootLIOParams lattice
lioParams :: LIO LIOParams
lioParams = LIO return
rootLIOParams :: LIOLattice -> LIOParams
rootLIOParams lattice =
LIOParams { lioLattice = lattice,
lioTimeIndex = 0,
lioMemberIndex = 0 }
parentLIOParams :: LIOParams -> Maybe LIOParams
parentLIOParams ps
| i == 0 = Nothing
| otherwise = Just $ ps { lioTimeIndex = i 1, lioMemberIndex = k' }
where i = lioTimeIndex ps
k = lioMemberIndex ps
k' = lioParentMemberIndex (lioLattice ps) i k
upSideLIOParams :: LIOParams -> LIOParams
upSideLIOParams ps = ps { lioTimeIndex = 1 + i }
where i = lioTimeIndex ps
downSideLIOParams :: LIOParams -> LIOParams
downSideLIOParams ps = ps { lioTimeIndex = 1 + i, lioMemberIndex = 1 + k }
where i = lioTimeIndex ps
k = lioMemberIndex ps
shiftLIOParams :: Int
-> Int
-> LIOParams
-> LIOParams
shiftLIOParams di dk ps
| i' < 0 = error "The time index cannot be negative: shiftLIOParams"
| k' < 0 = error "The member index cannot be negative: shiftLIOParams"
| k' > i' = error "The member index cannot be greater than the time index: shiftLIOParams"
| otherwise = ps { lioTimeIndex = i', lioMemberIndex = k' }
where i = lioTimeIndex ps
i' = i + di
k = lioMemberIndex ps
k' = k + dk
lioParamsAt :: Int
-> Int
-> LIOParams
-> LIOParams
lioParamsAt i k ps
| i < 0 = error "The time index cannot be negative: lioParamsAt"
| k < 0 = error "The member index cannot be negative: lioParamsAt"
| k > i = error "The member index cannot be greater than the time index: lioParamsAt"
| otherwise = ps { lioTimeIndex = i, lioMemberIndex = k }
latticeTimeIndex :: LIO Int
latticeTimeIndex = LIO $ return . lioTimeIndex
latticeMemberIndex :: LIO Int
latticeMemberIndex = LIO $ return . lioMemberIndex
latticeParentMemberIndex :: LIO (Maybe Int)
latticeParentMemberIndex = LIO $ return . fmap lioMemberIndex . parentLIOParams
latticeTime :: Parameter LIO Double
latticeTime =
Parameter $ \r ->
LIO $ \ps ->
let i = lioTimeIndex ps
in invokeLIO ps $
invokeParameter r $
getLatticeTimeByIndex i
latticeTimes :: Parameter LIO [Double]
latticeTimes =
Parameter $ \r ->
LIO $ \ps ->
let m = lioSize $ lioLattice ps
in forM [0 .. m] $ \i ->
invokeLIO ps $
invokeParameter r $
getLatticeTimeByIndex i
latticePoint :: Parameter LIO (Point LIO)
latticePoint =
Parameter $ \r ->
do t <- invokeParameter r latticeTime
return $ pointAt r t
latticeTimeStep :: Parameter LIO Double
latticeTimeStep =
Parameter $ \r ->
LIO $ \ps ->
do let sc = runSpecs r
t0 = spcStartTime sc
t2 = spcStopTime sc
m = lioSize $ lioLattice ps
dt = (t2 t0) / (fromIntegral m)
return dt
latticeSize :: LIO Int
latticeSize = LIO $ return . lioSize . lioLattice
findLatticeTimeIndex :: Double -> Parameter LIO Int
findLatticeTimeIndex t =
Parameter $ \r ->
LIO $ \ps ->
do let sc = runSpecs r
t0 = spcStartTime sc
t2 = spcStopTime sc
m = lioSize $ lioLattice ps
i | t == t0 = 0
| t == t2 = m
| otherwise = floor (fromIntegral m * ((t t0) / (t2 t0)))
return i
getLatticeTimeByIndex :: Int -> Parameter LIO Double
getLatticeTimeByIndex i =
Parameter $ \r ->
LIO $ \ps ->
let sc = runSpecs r
t0 = spcStartTime sc
t2 = spcStopTime sc
m = lioSize $ lioLattice ps
dt = (t2 t0) / (fromIntegral m)
t | i == 0 = t0
| i == m = t2
| otherwise = t0 + (fromInteger $ toInteger i) * dt
in return t