Created
February 15, 2016 23:38
-
-
Save dpiponi/7a074bf6c1dfe6a3eb97 to your computer and use it in GitHub Desktop.
Haskell heat engine simulation. (Not quite complete.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE FlexibleContexts #-} | |
import Data.Array | |
import Data.Array.IO | |
import Data.Foldable | |
import Data.List hiding (sum) | |
import System.Random | |
import Control.Monad.State | |
import Prelude hiding (sum) | |
--import Debug.Trace | |
-- Find number of solutions to e ≤ n²β ≤ f, n ≥ 0 | |
count' :: Double -> Double -> Double -> Int | |
count' β e f = | |
let nmin = if e >= 0 then ceiling (sqrt (e/β)) else 0 | |
nmax = if f >= 0 then floor (sqrt (f/β)) else -1 | |
in if nmax >= nmin then nmax-nmin+1 else 0 | |
-- Find number of solutions to e ≤ m²α+n²β ≤ f, n ≥ 0 | |
count :: Double -> Double -> Double -> Double -> Int | |
count α β e f = | |
sum [count' β (e-α*fromIntegral m*fromIntegral m) (f-α*fromIntegral m*fromIntegral m) | m <- [0::Int ..floor (sqrt (f/α))]] | |
-- Pick the ith solution to e ≤ α*m²+β*n² ≤ f counting from first | |
-- solution of form (m, ·) | |
pick' :: Int -> Int -> Double -> Double -> Double -> Double -> (Int, Int) | |
pick' i m α β e f = do | |
let e' = e-α*fromIntegral m*fromIntegral m | |
f' = f-α*fromIntegral m*fromIntegral m | |
c = count' β e' f' | |
if i >= c | |
then pick' (i-c) (m+1) α β e f | |
else (m, i+if e' >= 0 then ceiling (sqrt (e'/β)) else 0) | |
-- Pick a solution to e ≤ α*m²+β*n² ≤ f uniformly | |
pick :: (MonadState StdGen m) => Double -> Double -> Double -> Double -> m (Int, Int) | |
pick α β e f = do | |
let n = count α β e f | |
i <- state $ randomR (0, n-1) | |
return $ pick' i 0 α β e f | |
-- Collide particles with slack | |
shareEnergy :: (MonadState StdGen m) => | |
Double -> Double -> Double -> Double -> Int -> Int -> | |
m (Int, Int, Double) | |
shareEnergy α β slack maxSlack m n = do | |
let e = α*fromIntegral m*fromIntegral m+β*fromIntegral n*fromIntegral n | |
(m', n') <- pick α β (e-maxSlack+slack) (e+slack) | |
let e' = α*fromIntegral m'*fromIntegral m'+β*fromIntegral n'*fromIntegral n' | |
let slack' = slack-(e'-e) | |
return (m', n', slack') | |
{- | |
equilibriate1 :: Int -> Double -> Double -> Double -> Array Int Int -> | |
StateT StdGen IO (Double, Array Int Int) | |
equilibriate1 n slack maxSlack α states = do | |
arr <- lift $ thaw states :: StateT StdGen IO (IOArray Int Int) | |
slack' <- flip execStateT slack $ replicateM_ n $ do | |
i <- lift $ state $ randomR (bounds states) | |
j <- lift $ state $ randomR (bounds states) | |
when (i /= j) $ do | |
u <- lift $ lift $ readArray arr i | |
v <- lift $ lift $ readArray arr j | |
slack0 <- get | |
(u', v', slack') <- lift $ shareEnergy α α slack0 maxSlack u v | |
put slack' | |
lift $ lift $ writeArray arr i u' | |
lift $ lift $ writeArray arr j v' | |
states' <- lift $ freeze arr | |
return (slack', states') | |
-} | |
listPick :: (MonadState StdGen m) => [Int] -> m (Int, Int) | |
listPick lens = do | |
let sums = scanl1 (+) lens :: [Int] | |
let s = last sums | |
i <- state $ randomR (0, s-1) | |
let Just j = findIndex (> i) sums | |
return (j, if j == 0 then i else i-sums!!(j-1)) | |
equilibriate :: Int -> Double -> Double -> [Double] -> [Array Int Int] -> | |
StateT StdGen IO (Double, [Array Int Int]) | |
equilibriate n slack maxSlack αs states = do | |
let lens = map (\(a, b) -> b-a+1) $ map bounds states | |
arr <- lift $ mapM thaw states :: StateT StdGen IO [IOArray Int Int] | |
slack' <- flip execStateT slack $ replicateM_ n $ do | |
i@(arrayI, indexI) <- lift $ listPick lens | |
j@(arrayJ, indexJ) <- lift $ listPick lens | |
when (i /= j) $ do | |
u <- lift $ lift $ readArray (arr!!arrayI) indexI | |
v <- lift $ lift $ readArray (arr!!arrayJ) indexJ | |
slack0 <- get | |
(u', v', slack') <- lift $ shareEnergy (αs!!arrayI) (αs!!arrayJ) slack0 maxSlack u v | |
put slack' | |
lift $ lift $ writeArray (arr!!arrayI) indexI u' | |
lift $ lift $ writeArray (arr!!arrayJ) indexJ v' | |
states' <- lift $ mapM freeze arr | |
return (slack', states') | |
--return (slack', states') | |
totalEnergy :: Double -> Array Int Int -> Double | |
totalEnergy α arr = α*sum (fmap (\x -> fromIntegral (x*x)) arr) | |
data EngineState = E { | |
slack1 :: Double, | |
slack2 :: Double, | |
reservoir1 :: Array Int Int, | |
piston :: Array Int Int, | |
reservoir2 :: Array Int Int, | |
input :: Double, | |
output :: Double | |
} deriving Show | |
len :: Array Int a -> Int | |
len a = let (b, e) = bounds a in (e-b+1) | |
dump :: MonadIO m => Double -> EngineState -> m () | |
dump α engineState = do | |
liftIO $ putStrLn $ "reservoir1 energy/particle = " ++ show (totalEnergy 1.0 (reservoir1 engineState)/(fromIntegral (len (reservoir1 engineState)))) | |
liftIO $ putStrLn $ "piston energy/particle = " ++ show (totalEnergy α (piston engineState)/(fromIntegral (len (piston engineState)))) | |
liftIO $ putStrLn $ "reservoir2 energy/particle = " ++ show (totalEnergy 1.0 (reservoir2 engineState)/(fromIntegral (len (reservoir2 engineState)))) | |
liftIO $ putStrLn $ "total input = " ++ show (input engineState) | |
liftIO $ putStrLn $ "total output = " ++ show (output engineState) | |
liftIO $ putStrLn $ "efficiency = " ++ show (output engineState/input engineState) | |
equilibriate1 :: Int -> Double -> Double -> EngineState -> StateT StdGen IO EngineState | |
equilibriate1 n α maxSlack state = do | |
(slack, [r1', r2']) <- equilibriate n (slack1 state) | |
maxSlack | |
[1.0, α] | |
[reservoir1 state, piston state] | |
return $ state { slack1 = slack, reservoir1 = r1', piston = r2' } | |
equilibriate2 :: Int -> Double -> Double -> EngineState -> StateT StdGen IO EngineState | |
equilibriate2 n β maxSlack state = do | |
(slack, [r1', r2']) <- equilibriate n (slack2 state) | |
maxSlack | |
[β, 1.0] | |
[piston state, reservoir2 state] | |
return $ state { slack2 = slack, piston = r1', reservoir2 = r2' } | |
main :: IO () | |
main = | |
flip evalStateT (mkStdGen 670) $ do | |
r1 <- replicateM 1000 $ state $ randomR (0 :: Int, 99) | |
let r1' = array (0, 999) $ zip [0..999] r1 | |
p <- replicateM 100 $ state $ randomR (0 :: Int, 49) | |
let p' = array (0, 99) $ zip [0..99] p | |
r2 <- replicateM 1000 $ state $ randomR (0 :: Int, 49) | |
let r2' = array (0, 999) $ zip [0..999] r2 | |
let engineState = E 0.0 0.0 r1' p' r2' 0.0 0.0 | |
flip evalStateT engineState $ replicateM_ 10 $ do | |
engineState <- get | |
liftIO $ dump 1.0 engineState | |
liftIO $ putStrLn "Equilibriating with reservoir1" | |
engineState' <- lift $ equilibriate1 100000 1.0 100.0 engineState | |
liftIO $ dump 1.0 engineState' | |
liftIO $ putStrLn "Expanding" | |
liftIO $ dump 0.5 engineState' | |
liftIO $ putStrLn "Equilibriating with reservoir2" | |
engineState'' <- lift $ equilibriate2 100000 0.5 100.0 engineState' | |
liftIO $ dump 0.5 engineState'' | |
liftIO $ putStrLn "Compressing" | |
liftIO $ dump 1.0 engineState'' | |
put engineState'' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment