module Water.SWMM where
import Control.Applicative ((<$>), (<*>))
import Control.Monad (replicateM)
import Data.Binary.Get (Get, getWord32le, getByteString)
import Data.Binary.IEEE754 (getFloat32le, getFloat64le)
import Data.List.Split (chunksOf)
import Pipes.Binary (decodeGet)
import Pipes.Parse (Parser, evalStateT)
import qualified Data.ByteString.Char8 as BSC (ByteString)
import qualified Data.ByteString.Lazy as BL (ByteString, readFile, drop, take, length)
import qualified Pipes.ByteString as PB (ByteString, fromLazy)
data SWMMParams = SWMMParams { header :: Header
, ids :: ObjectIds
, properties :: ObjectProperties
, variables :: ReportingVariables
, intervals :: ReportingInterval
, closingRecord :: ClosingRecord
} deriving (Show, Eq)
data Header = Header { headerIdNumber :: Int
, versionNumber :: Int
, codeNumber :: Int
, numberOfSubcatchments :: Int
, numberOfNodes :: Int
, numberOfLinks :: Int
, numberOfPollutants :: Int
} deriving (Show, Eq)
data ObjectIds = ObjectIds { subcatchmentIds :: [BSC.ByteString]
, nodeIds :: [BSC.ByteString]
, linkIds :: [BSC.ByteString]
, pollutantIds :: [BSC.ByteString]
, concentrationIds :: [Int]
} deriving (Show, Eq)
data ObjectProperties = ObjectProperties { subcatchmentProperties :: Properties
, nodeProperties :: Properties
, linkProperties :: Properties
} deriving (Show, Eq)
data ReportingVariables = ReportingVariables { subcatchmentVariables :: Variables
, nodeVariables :: Variables
, linkVariables :: Variables
, systemVariables :: Variables
} deriving (Show, Eq)
data ReportingInterval = ReportingInterval { startDateTime :: Double
, timeIntervals :: Int
} deriving (Show, Eq)
data ValuesForOneDateTime = ValuesForOneDateTime { dateTimeValue :: Double
, subcatchmentValue :: [[Float]]
, nodeValue :: [[Float]]
, linkValue :: [[Float]]
, systemValue :: [Float]
} deriving (Show)
data ClosingRecord = ClosingRecord { idBytePosition :: Int
, propertiesBytePosition :: Int
, resultBytePosition :: Int
, numberOfPeriods :: Int
, errorCode :: Int
, closingIdNumber :: Int
} deriving (Show, Eq)
data Properties = Properties { numberOfProperties :: Int
, codeNumberProperties :: [Int]
, valueProperties :: [Float]
} deriving (Show, Eq)
data Variables = Variables { numberOfVariables :: Int
, codeNumberVariables :: [Int]
} deriving (Show, Eq)
instance Ord ValuesForOneDateTime where
a <= b = dateTimeValue a <= dateTimeValue b
instance Eq ValuesForOneDateTime where
a == b = dateTimeValue a == dateTimeValue b
getInt :: Get Int
getInt = fromIntegral <$> getWord32le
getString :: Get BSC.ByteString
getString = getInt >>= getByteString
getHeader :: Get Header
getHeader = Header <$> getInt
<*> getInt
<*> getInt
<*> getInt
<*> getInt
<*> getInt
<*> getInt
getObjectIds :: Header -> Get ObjectIds
getObjectIds h = do
s <- replicateM (numberOfSubcatchments h) getString
n <- replicateM (numberOfNodes h) getString
l <- replicateM (numberOfLinks h) getString
p <- replicateM (numberOfPollutants h) getString
c <- replicateM (numberOfPollutants h) getInt
return $ ObjectIds s n l p c
getProperties :: Int -> Get Properties
getProperties n = do
size <- getInt
codeNumbers <- replicateM size getInt
values <- replicateM (size * n) getFloat32le
return $ Properties size codeNumbers values
getObjectProperties :: Header -> Get ObjectProperties
getObjectProperties h =
ObjectProperties <$> getProperties (numberOfSubcatchments h)
<*> getProperties (numberOfNodes h)
<*> getProperties (numberOfLinks h)
getVariables :: Get Variables
getVariables = do
size <- getInt
codeNumbers <- replicateM size getInt
return $ Variables size codeNumbers
getReportingVariables :: Get ReportingVariables
getReportingVariables = ReportingVariables <$> getVariables
<*> getVariables
<*> getVariables
<*> getVariables
getReportingInterval :: Get ReportingInterval
getReportingInterval = ReportingInterval <$> getFloat64le
<*> getInt
getSWMMTopParams :: Get ( Header, ObjectIds, ObjectProperties
, ReportingVariables, ReportingInterval )
getSWMMTopParams = do
h <- getHeader
oi <- getObjectIds h
op <- getObjectProperties h
rv <- getReportingVariables
ri <- getReportingInterval
return (h, oi, op, rv, ri)
getClosingRecord :: Get ClosingRecord
getClosingRecord = ClosingRecord <$> getInt
<*> getInt
<*> getInt
<*> getInt
<*> getInt
<*> getInt
extractSWMMParams :: FilePath -> IO (SWMMParams, BL.ByteString)
extractSWMMParams f = do
s <- PB.fromLazy <$> BL.readFile f
(h,oi,op,rv,ri) <- evalStateT decodeTopSWMMParameters s
e <- PB.fromLazy . grabClosingRecord <$> BL.readFile f
c <- evalStateT decodeClosingRecord e
results <- dropUptoResults c . takeUptoClosingRecord <$> BL.readFile f
return $ (SWMMParams h oi op rv ri c, results)
where
decodeTopSWMMParameters
:: Parser PB.ByteString IO ( Header, ObjectIds, ObjectProperties
, ReportingVariables, ReportingInterval )
decodeTopSWMMParameters = do
Right top <- decodeGet getSWMMTopParams
return top
decodeClosingRecord :: Parser PB.ByteString IO ClosingRecord
decodeClosingRecord = do
Right closing <- decodeGet getClosingRecord
return closing
closingRecordSize :: Num a => a
closingRecordSize = 4 * 6
grabClosingRecord :: BL.ByteString -> BL.ByteString
grabClosingRecord = (\f -> BL.drop (BL.length f closingRecordSize) f)
takeUptoClosingRecord :: BL.ByteString -> BL.ByteString
takeUptoClosingRecord = (\f -> BL.take (BL.length f closingRecordSize) f)
dropUptoResults :: ClosingRecord -> BL.ByteString -> BL.ByteString
dropUptoResults c = BL.drop (fromIntegral $ resultBytePosition c)
getSplitValues :: Int -> Int -> Get [[Float]]
getSplitValues n nv =
chunksOf n <$> replicateM nv getFloat32le
getValues :: Int -> Get [Float]
getValues n = replicateM n getFloat32le
getResults :: Header -> ReportingVariables -> Get ValuesForOneDateTime
getResults header report =
ValuesForOneDateTime <$> getFloat64le
<*> getSub
<*> getNode
<*> getLink
<*> getVar
where nsub = (numberOfVariables . subcatchmentVariables) report
nnode = (numberOfVariables . nodeVariables ) report
nlink = (numberOfVariables . linkVariables ) report
nvar = (numberOfVariables . systemVariables ) report
getSub = getSplitValues nsub (numberOfSubcatchments header * nsub )
getNode = getSplitValues nnode (numberOfNodes header * nnode)
getLink = getSplitValues nlink (numberOfLinks header * nlink)
getVar = getValues nvar