{-# LANGUAGE MultiParamTypeClasses #-} -- | -- Module : Simulation.Aivika.Experiment.TimingStatsView -- Copyright : Copyright (c) 2012-2015, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.10.1 -- -- The module defines 'TimingStatsView' that shows the timing statistics -- for the variables for every simulation run separately. -- module Simulation.Aivika.Experiment.TimingStatsView (TimingStatsView(..), defaultTimingStatsView) where import Control.Monad import Control.Monad.Trans import qualified Data.Map as M import Data.IORef import Data.Maybe import Data.Monoid import Simulation.Aivika import Simulation.Aivika.Experiment.Types import Simulation.Aivika.Experiment.WebPageRenderer import Simulation.Aivika.Experiment.ExperimentWriter import Simulation.Aivika.Experiment.HtmlWriter import Simulation.Aivika.Experiment.TimingStatsWriter import Simulation.Aivika.Experiment.Utils (replace) -- | Defines the 'View' that shows the timing statistics -- for variables for every simulation run separately. data TimingStatsView = TimingStatsView { timingStatsTitle :: String, -- ^ The title for the view. timingStatsRunTitle :: String, -- ^ The run title for the view. It may include -- special variables @$RUN_INDEX@, @$RUN_COUNT@ and -- @$TITLE@. -- -- An example is -- -- @ -- timingStatsRunTitle = \"$TITLE / Run $RUN_INDEX of $RUN_COUNT\" -- @ timingStatsDescription :: String, -- ^ The description for the view. timingStatsWriter :: TimingStatsWriter Double, -- ^ It shows the timing statistics. timingStatsPredicate :: Event Bool, -- ^ Specifies when gathering the statistics. timingStatsTransform :: ResultTransform, -- ^ The transform applied to the results before receiving series. timingStatsSeries :: ResultTransform -- ^ It defines the series for which the statistics to be collected. } -- | This is the default view. defaultTimingStatsView :: TimingStatsView defaultTimingStatsView = TimingStatsView { timingStatsTitle = "Timing Statistics", timingStatsRunTitle = "$TITLE / Run $RUN_INDEX of $RUN_COUNT", timingStatsDescription = "The statistical data are gathered in the time points.", timingStatsWriter = defaultTimingStatsWriter, timingStatsPredicate = return True, timingStatsTransform = id, timingStatsSeries = id } instance ExperimentView TimingStatsView (WebPageRenderer a) where outputView v = let reporter exp renderer dir = do st <- newTimingStats v exp let context = WebPageContext $ WebPageWriter { reporterWriteTOCHtml = timingStatsTOCHtml st, reporterWriteHtml = timingStatsHtml st } return ExperimentReporter { reporterInitialise = return (), reporterFinalise = return (), reporterSimulate = simulateTimingStats st, reporterContext = context } in ExperimentGenerator { generateReporter = reporter } -- | The state of the view. data TimingStatsViewState = TimingStatsViewState { timingStatsView :: TimingStatsView, timingStatsExperiment :: Experiment, timingStatsMap :: M.Map Int (IORef [(String, IORef (TimingStats Double))]) } -- | Create a new state of the view. newTimingStats :: TimingStatsView -> Experiment -> ExperimentWriter TimingStatsViewState newTimingStats view exp = do let n = experimentRunCount exp rs <- forM [0..(n - 1)] $ \i -> liftIO $ newIORef [] let m = M.fromList $ zip [0..(n - 1)] rs return TimingStatsViewState { timingStatsView = view, timingStatsExperiment = exp, timingStatsMap = m } -- | Get the timing statistics during the simulation. simulateTimingStats :: TimingStatsViewState -> ExperimentData -> Event DisposableEvent simulateTimingStats st expdata = do let view = timingStatsView st rs = timingStatsSeries view $ timingStatsTransform view $ experimentResults expdata exts = resultsToDoubleValues rs signals = experimentPredefinedSignals expdata signal = filterSignalM (const predicate) $ pureResultSignal signals $ resultSignal rs predicate = timingStatsPredicate view i <- liftParameter simulationIndex let r = fromJust $ M.lookup (i - 1) $ timingStatsMap st ds <- forM exts $ \ext -> do stats <- liftIO $ newIORef emptyTimingStats let name = resultValueName ext liftIO $ modifyIORef r ((:) (name, stats)) handleSignal signal $ \_ -> do t <- liftDynamics time x <- resultValueData ext liftIO $ do y <- readIORef stats let y' = addTimingStats t x y y' `seq` writeIORef stats y' return $ mconcat ds -- | Get the HTML code. timingStatsHtml :: TimingStatsViewState -> Int -> HtmlWriter () timingStatsHtml st index = let n = experimentRunCount $ timingStatsExperiment st in if n == 1 then timingStatsHtmlSingle st index else timingStatsHtmlMultiple st index -- | Get the HTML code for a single run. timingStatsHtmlSingle :: TimingStatsViewState -> Int -> HtmlWriter () timingStatsHtmlSingle st index = do header st index let r = fromJust $ M.lookup 0 (timingStatsMap st) pairs <- liftIO $ readIORef r forM_ (reverse pairs) $ \(name, r) -> do stats <- liftIO $ readIORef r let writer = timingStatsWriter (timingStatsView st) write = timingStatsWrite writer write writer name stats -- | Get the HTML code for multiple runs timingStatsHtmlMultiple :: TimingStatsViewState -> Int -> HtmlWriter () timingStatsHtmlMultiple st index = do header st index let n = experimentRunCount $ timingStatsExperiment st forM_ [0..(n - 1)] $ \i -> do let subtitle = replace "$RUN_INDEX" (show $ i + 1) $ replace "$RUN_COUNT" (show n) $ replace "$TITLE" (timingStatsTitle $ timingStatsView st) (timingStatsRunTitle $ timingStatsView st) writeHtmlHeader4 $ writeHtmlText subtitle let r = fromJust $ M.lookup i (timingStatsMap st) pairs <- liftIO $ readIORef r forM_ (reverse pairs) $ \(name, r) -> do stats <- liftIO $ readIORef r let writer = timingStatsWriter (timingStatsView st) write = timingStatsWrite writer write writer name stats header :: TimingStatsViewState -> Int -> HtmlWriter () header st index = do writeHtmlHeader3WithId ("id" ++ show index) $ writeHtmlText (timingStatsTitle $ timingStatsView st) let description = timingStatsDescription $ timingStatsView st unless (null description) $ writeHtmlParagraph $ writeHtmlText description -- | Get the TOC item timingStatsTOCHtml :: TimingStatsViewState -> Int -> HtmlWriter () timingStatsTOCHtml st index = writeHtmlListItem $ writeHtmlLink ("#id" ++ show index) $ writeHtmlText (timingStatsTitle $ timingStatsView st)