module Simulation.Aivika.Branch.Event
(branchEvent,
futureEvent,
futureEventWith) where
import Data.IORef
import Control.Monad
import Control.Monad.Trans
import qualified Simulation.Aivika.PriorityQueue.Pure as PQ
import Simulation.Aivika.Trans
import Simulation.Aivika.Trans.Internal.Types
import Simulation.Aivika.Branch.Internal.BR
instance EventQueueing (BR IO) where
data EventQueue (BR IO) =
EventQueue { queuePQ :: IORef (PQ.PriorityQueue (Point (BR IO) -> BR IO ())),
queueBusy :: IORef Bool,
queueTime :: IORef Double
}
newEventQueue specs =
do f <- liftIO $ newIORef False
t <- liftIO $ newIORef (spcStartTime specs)
pq <- liftIO $ newIORef PQ.emptyQueue
return EventQueue { queuePQ = pq,
queueBusy = f,
queueTime = t }
enqueueEvent t (Event m) =
Event $ \p ->
BR $ \ps ->
let pq = queuePQ $ runEventQueue $ pointRun p
in modifyIORef pq $ \x -> PQ.enqueue x t m
runEventWith processing (Event e) =
Dynamics $ \p ->
do invokeDynamics p $ processEvents processing
e p
eventQueueCount =
Event $ \p ->
BR $ \ps ->
let pq = queuePQ $ runEventQueue $ pointRun p
in fmap PQ.queueCount $ readIORef pq
processPendingEventsCore :: Bool -> Dynamics (BR IO) ()
processPendingEventsCore includingCurrentEvents = Dynamics r where
r p =
BR $ \ps ->
do let q = runEventQueue $ pointRun p
f = queueBusy q
f' <- readIORef f
unless f' $
do writeIORef f True
call q p ps
writeIORef f False
call q p ps =
do let pq = queuePQ q
r = pointRun p
f <- fmap PQ.queueNull $ readIORef pq
unless f $
do (t2, c2) <- fmap PQ.queueFront $ readIORef pq
let t = queueTime q
t' <- readIORef t
when (t2 < t') $
error "The time value is too small: processPendingEventsCore"
when ((t2 < pointTime p) ||
(includingCurrentEvents && (t2 == pointTime p))) $
do writeIORef t t2
modifyIORef pq PQ.dequeue
let sc = pointSpecs p
t0 = spcStartTime sc
dt = spcDT sc
n2 = fromIntegral $ floor ((t2 t0) / dt)
invokeBR ps $
c2 $ p { pointTime = t2,
pointIteration = n2,
pointPhase = 1 }
call q p ps
processPendingEvents :: Bool -> Dynamics (BR IO) ()
processPendingEvents includingCurrentEvents = Dynamics r where
r p =
BR $ \ps ->
do let q = runEventQueue $ pointRun p
t = queueTime q
t' <- readIORef t
if pointTime p < t'
then error $
"The current time is less than " ++
"the time in the queue: processPendingEvents"
else invokeBR ps $
invokeDynamics p $
processPendingEventsCore includingCurrentEvents
processEventsIncludingCurrent :: Dynamics (BR IO) ()
processEventsIncludingCurrent = processPendingEvents True
processEventsIncludingEarlier :: Dynamics (BR IO) ()
processEventsIncludingEarlier = processPendingEvents False
processEventsIncludingCurrentCore :: Dynamics (BR IO) ()
processEventsIncludingCurrentCore = processPendingEventsCore True
processEventsIncludingEarlierCore :: Dynamics (BR IO) ()
processEventsIncludingEarlierCore = processPendingEventsCore True
processEvents :: EventProcessing -> Dynamics (BR IO) ()
processEvents CurrentEvents = processEventsIncludingCurrent
processEvents EarlierEvents = processEventsIncludingEarlier
processEvents CurrentEventsOrFromPast = processEventsIncludingCurrentCore
processEvents EarlierEventsOrFromPast = processEventsIncludingEarlierCore
branchEvent :: Event (BR IO) a -> Event (BR IO) a
branchEvent (Event m) =
Event $ \p ->
BR $ \ps->
do p2 <- clonePoint p
ps2 <- newBRParams ps
invokeBR ps2 (m p2)
futureEvent :: Double -> Event (BR IO) a -> Event (BR IO) a
futureEvent = futureEventWith CurrentEvents
futureEventWith :: EventProcessing -> Double -> Event (BR IO) a -> Event (BR IO) a
futureEventWith processing t (Event m) =
Event $ \p ->
BR $ \ps ->
do when (t < pointTime p) $
error "The specified time is less than the current modeling time: futureEventWith"
p2 <- clonePoint p
ps2 <- newBRParams ps
let sc = pointSpecs p
t0 = spcStartTime sc
t' = spcStopTime sc
dt = spcDT sc
n = fromIntegral $ floor ((t t0) / dt)
p' = p2 { pointTime = t,
pointIteration = n,
pointPhase = 1 }
invokeBR ps2 $
invokeDynamics p' $
processEvents processing
invokeBR ps2 (m p')
clonePoint :: Point (BR IO) -> IO (Point (BR IO))
clonePoint p =
do let r = pointRun p
q = runEventQueue r
pq <- readIORef (queuePQ q)
t <- readIORef (queueTime q)
pq2 <- newIORef pq
f2 <- newIORef False
t2 <- newIORef t
let q2 = EventQueue { queuePQ = pq2,
queueBusy = f2,
queueTime = t2 }
r2 = r { runEventQueue = q2 }
p2 = p { pointRun = r2 }
return p2