packages feed

dsmc-tools-0.1.0.0: src/Runner.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Main

where

import Control.Monad.Error
import Control.Exception

import Data.ByteString.Char8 as B

import Data.ConfigFile

import Data.Functor

import Data.Vector.Unboxed as VU hiding ((++))

import System.Console.CmdArgs.Implicit

import System.IO

import System.Log
import System.Log.Logger
import System.Log.Formatter
import System.Log.Handler (setFormatter)
import System.Log.Handler.Simple

import DSMC
import DSMC.Domain
import DSMC.Macroscopic
import DSMC.Particles
import DSMC.Surface
import DSMC.Traceables.Parser
import DSMC.Util.Constants
import DSMC.Util.Vector

import Data.Array.Repa as R hiding ((++))

import GHC.Conc.Sync


simSection :: String
simSection = "Simulation"


flowSection :: String
flowSection = "Flow"


domainSection :: String
domainSection = "Domain"


macroSection :: String
macroSection = "Macroscopic"

bodySection :: String
bodySection = "Body"


surfaceSection :: String
surfaceSection = "Surface"


-- | Command-line options for the runner.
data Options = Options
    { probDef :: FilePath
    , macroFile :: Maybe FilePath
    , ensFile :: Maybe FilePath
    }
    deriving (Data, Typeable)

main :: IO ()
main =
  let
    sample = Options
             { probDef = def &= argPos 0 &= typ "PROBLEM-FILE"
             , macroFile = Nothing &= help "Macroscopic data output file"
             , ensFile = Nothing &= help "Ensemble data output file"
             }
             &= program "dsmc-runner"
  in do
    -- | Setup logging to stderr
    let lf = simpleLogFormatter "$time: [$prio] $msg"
    lh <- (flip setFormatter) lf <$> verboseStreamHandler stderr DEBUG
    updateGlobalLogger rootLoggerName (setLevel DEBUG . setHandlers [lh] )

    debugM rootLoggerName "Program started"

    Options{..} <- cmdArgs $ sample

    debugM rootLoggerName ("Using problem file: " ++ probDef)

    debugM rootLoggerName $
           maybe "Not writing macroscopic data"
           ("Macroscopic data output file: " ++) macroFile

    debugM rootLoggerName $
           maybe "Not writing ensemble data"
           ("Ensemble data output file: " ++) ensFile

    -- Use number of cores as split factor for parallelizing
    -- stochastic processes.
    nsplit <- getNumCapabilities

    (hr :: Either IOException Handle) <- try (openFile probDef ReadMode)
    case hr of
      Left e -> errorM rootLoggerName $ show e
      Right hdl -> do
        res <- runErrorT $ do
                 cp <- join $ liftIO $ readhandle emptyCP hdl
                 -- Must refactor this somehow (actually, write CmdArgs
                 -- for .ini files)
                 n <- get cp flowSection "n"
                 t <- get cp flowSection "t"
                 m <- get cp flowSection "m"
                 v <- get cp flowSection "velocity"
                 sw <- get cp flowSection "fn"
                 w <- get cp domainSection "w"
                 l <- get cp domainSection "l"
                 h <- get cp domainSection "h"
                 ex <- get cp simSection "ex"
                 dt <- get cp simSection "dt"
                 ssteps <- get cp simSection "steady_steps"
                 sepsilon <- get cp simSection "steady_epsilon"
                 emptys <- get cp simSection "empty_start"
                 vpts <- get cp bodySection "volume_points"
                 mx <- get cp macroSection "mx"
                 my <- get cp macroSection "my"
                 mz <- get cp macroSection "mz"
                 bodyDef <- get cp bodySection "definition"
                 -- Interface to diffuse model only
                 surfTemp <- get cp surfaceSection "temperature"
                 return $ (Flow n t (m * amu) v sw, ex, dt,
                           ssteps, sepsilon, emptys,
                           vpts,
                           mx, my, mz,
                           makeDomain origin w l h,
                           bodyDef,
                           surfTemp)
        case res of
          Left e -> errorM rootLoggerName $
                    "Problem when reading problem definition: " ++ show e
          Right (flow, ex, dt,
                 ssteps, sepsilon, emptys,
                 vpts,
                 mx, my, mz,
                 domain,
                 bodyDef,
                 surfTemp) -> do
                   -- Try to read body from definition file
                   debugM rootLoggerName $
                          "Using body definition file: " ++ bodyDef
                   body <- parseBodyFile bodyDef
                   case body of
                     Left e -> errorM rootLoggerName $
                               "Problem when reading body definition: " ++ e
                     Right b ->
                       do
                         debugM rootLoggerName $ "Starting simulation"
                         -- Run the simulation, obtaining
                         -- iteration count and final
                         -- distributions
                         !(iters, e, macro) <-
                           simulate domain b flow dt
                                    emptys ex sepsilon ssteps
                                    (Diffuse surfTemp $
                                     DSMC.Particles.mass flow)
                                    (mx, my, mz) vpts nsplit
                         debugM rootLoggerName "Simulation complete"
                         debugM rootLoggerName $
                                "Particle count when finished: " ++
                                (show $ ensembleSize e)
                         debugM rootLoggerName $
                                "Total iterations done: " ++
                                (show iters)
                         case macroFile of
                           Just fp -> do
                             debugM rootLoggerName $
                               "Writing macroscopic data to file file: " ++ fp
                             saveMacroscopic macro fp
                           Nothing -> return ()
                         case ensFile of
                           Just fp -> do
                             debugM rootLoggerName $
                               "Writing ensemble data to file: " ++ fp
                             saveEnsemble e fp
                           Nothing -> return ()
                         return ()


fd :: Double -> ByteString
fd = pack . show
{-# INLINE fd #-}


ws :: ByteString
ws = B.singleton ' '


nl :: ByteString
nl = B.singleton '\n'


-- | Save Repa array in text file, one element per line.
saveRepa :: (Unbox e) =>
         (e -> ByteString)
         -- ^ Formatter function.
         -> ByteString
         -- ^ Header line
         -> R.Array U sh e
         -> FilePath
         -> IO ()
saveRepa f header array path = do
    h <- openFile path WriteMode
    hPut h $ B.concat [header, nl]
    VU.forM_ (R.toUnboxed array) (hPut h . f)


formatParticle :: Particle -> ByteString
formatParticle ((x, y, z), (u, v, w)) =
    B.concat [fd x, ws, fd y, ws, fd z, ws, fd u, ws, fd v, ws, fd w, nl]
{-# INLINE formatParticle #-}


formatMacroParameters :: (Point, IntensiveMacroParameters) -> ByteString
formatMacroParameters ((x, y, z), (n, (u, v, w), p, t)) =
    if n == 0.0 then B.empty
    else B.concat [fd x, ws, fd y, ws, fd z, ws,
                   fd n, ws,
                   fd u, ws, fd v, ws, fd w, ws,
                   fd p, ws, fd t, nl]
{-# INLINE formatMacroParameters #-}


saveMacroscopic :: MacroField -> FilePath -> IO ()
saveMacroscopic =
  saveRepa formatMacroParameters $
  B.pack "x y z number_density u_avg v_avg w_avg pressure trans_temp"


saveEnsemble :: Ensemble -> FilePath -> IO ()
saveEnsemble = saveRepa formatParticle $ B.pack "x y z u v w"