haskell Integration¶
This example demonstrates how to use fluids from haskell.
Source Code¶
1{-# LANGUAGE OverloadedStrings #-}
2module Main where
3
4import qualified CPython as Py
5import qualified CPython.Types.Module as Py
6import qualified CPython.Protocols.Object as Py
7import qualified CPython.Protocols.Number as PyNum
8import qualified CPython.Types.Dictionary as PyDict
9import qualified CPython.Types.Tuple as PyTuple
10import qualified CPython.Types.Unicode as PyUnicode
11import qualified CPython.Types.Float as PyFloat
12import qualified CPython.Types.Exception as PyExc
13import Control.Exception (handle)
14import Text.Printf (printf)
15import Data.Maybe (fromMaybe)
16import qualified Data.Text as T
17import System.Clock
18
19testBasics :: IO ()
20testBasics = do
21 putStrLn "Testing basic fluids functionality..."
22
23 -- Import fluids module
24 fluidsModule <- Py.importModule (T.pack "fluids")
25
26 -- Get version
27 versionName <- PyUnicode.toUnicode (T.pack "__version__")
28 versionObj <- Py.getAttribute fluidsModule versionName
29 Just versionStr <- Py.cast versionObj
30 version <- PyUnicode.fromUnicode versionStr
31 putStrLn $ "✓ Fluids version: " ++ T.unpack version
32
33 -- Test Reynolds number calculation
34 let calcReynolds = do
35 -- Get Reynolds function
36 reynoldsName <- PyUnicode.toUnicode (T.pack "Reynolds")
37 reynolds <- Py.getAttribute fluidsModule reynoldsName
38
39 -- Create arguments
40 v <- PyFloat.toFloat 2.5
41 d <- PyFloat.toFloat 0.1
42 rho <- PyFloat.toFloat 1000.0
43 mu <- PyFloat.toFloat 0.001
44 args <- PyTuple.toTuple [Py.toObject v, Py.toObject d, Py.toObject rho, Py.toObject mu]
45 kwargs <- PyDict.new
46
47 -- Call function
48 result <- Py.call reynolds args kwargs
49 x <- PyNum.castToNumber result
50 let num = fromMaybe (error "Could not convert Reynolds number") x
51 PyFloat.fromFloat =<< PyNum.toFloat num
52
53 re <- calcReynolds
54 putStrLn $ printf "✓ Reynolds number calculation: %.1f" re
55
56 -- Test friction factor
57 let calcFriction = do
58 -- Get friction_factor function
59 frictionName <- PyUnicode.toUnicode (T.pack "friction_factor")
60 friction <- Py.getAttribute fluidsModule frictionName
61
62 -- Create arguments
63 re <- PyFloat.toFloat 1e5
64 ed <- PyFloat.toFloat 0.0001
65 args <- PyTuple.toTuple [Py.toObject re, Py.toObject ed]
66 kwargs <- PyDict.new
67
68 -- Call function
69 result <- Py.call friction args kwargs
70 x <- PyNum.castToNumber result
71 let num = fromMaybe (error "Could not convert friction factor") x
72 PyFloat.fromFloat =<< PyNum.toFloat num
73
74 fd <- calcFriction
75 putStrLn $ printf "✓ Friction factor calculation: %.6f" fd
76 putStrLn "Basic tests completed successfully!\n"
77
78testAtmosphere :: IO ()
79testAtmosphere = do
80 putStrLn "\nTesting atmosphere at 5000m elevation:"
81
82 -- Import fluids module
83 fluidsModule <- Py.importModule (T.pack "fluids")
84
85 -- Create argument for constructor
86 zArg <- PyFloat.toFloat 5000.0
87 args <- PyTuple.toTuple [Py.toObject zArg]
88 kwargs <- PyDict.new
89
90 -- Get ATMOSPHERE_1976 class and create instance
91 atmosClass <- PyUnicode.toUnicode (T.pack "ATMOSPHERE_1976") >>= Py.getAttribute fluidsModule
92 atm <- Py.call atmosClass args kwargs
93
94 -- Get and print properties
95 let getProperty name = do
96 nameObj <- PyUnicode.toUnicode name
97 prop <- Py.getAttribute atm nameObj
98 x <- PyNum.castToNumber prop
99 let num = fromMaybe (error $ "Could not get " ++ T.unpack name ++ " as number") x
100 PyFloat.fromFloat =<< PyNum.toFloat num
101
102 temp <- getProperty (T.pack "T")
103 pressure <- getProperty (T.pack "P")
104 density <- getProperty (T.pack "rho")
105 gravity <- getProperty (T.pack "g")
106 viscosity <- getProperty (T.pack "mu")
107 conductivity <- getProperty (T.pack "k")
108 sonicVel <- getProperty (T.pack "v_sonic")
109
110 putStrLn $ printf "✓ Temperature: %.4f" temp
111 putStrLn $ printf "✓ Pressure: %.4f" pressure
112 putStrLn $ printf "✓ Density: %.6f" density
113 putStrLn $ printf "✓ Gravity: %.6f" gravity
114 putStrLn $ printf "✓ Viscosity: %.6e" viscosity
115 putStrLn $ printf "✓ Thermal conductivity: %.6f" conductivity
116 putStrLn $ printf "✓ Sonic velocity: %.4f" sonicVel
117
118benchmarkFluids :: IO ()
119benchmarkFluids = do
120 putStrLn "\nRunning benchmarks:"
121
122 -- Import fluids module
123 fluidsModule <- Py.importModule (T.pack "fluids")
124
125 -- Get friction_factor function
126 frictionName <- PyUnicode.toUnicode (T.pack "friction_factor")
127 friction <- Py.getAttribute fluidsModule frictionName
128
129 -- Prepare arguments that will be reused
130 re <- PyFloat.toFloat 1e5
131 ed <- PyFloat.toFloat 0.0001
132 args <- PyTuple.toTuple [Py.toObject re, Py.toObject ed]
133 kwargs <- PyDict.new
134
135 -- Time the operations
136 putStrLn "\nBenchmarking friction_factor:"
137 start <- getTime Monotonic
138
139 -- Do 1,000,000 iterations like in Julia version
140 sequence_ $ replicate 1000000 $ do
141 result <- Py.call friction args kwargs
142 x <- PyNum.castToNumber result
143 let num = fromMaybe (error "Could not convert friction factor") x
144 PyFloat.fromFloat =<< PyNum.toFloat num
145
146 end <- getTime Monotonic
147 let diff = (fromIntegral (toNanoSecs (diffTimeSpec end start)) :: Double) / 1000000000
148
149 putStrLn $ printf "Time for 1e6 friction_factor calls: %.6f seconds" diff
150 putStrLn $ printf "Average time per call: %.6f seconds" (diff / 1000000)
151
152main :: IO ()
153main = handle pyExceptionHandler $ do
154 putStrLn "Running fluids tests from Haskell..."
155 Py.initialize
156 testBasics
157 testAtmosphere
158 benchmarkFluids
159 Py.finalize
160 putStrLn "\nAll tests completed!"
161 where
162 pyExceptionHandler :: PyExc.Exception -> IO ()
163 pyExceptionHandler e = do
164 putStrLn $ "Python error occurred: " ++ show e
165 Py.finalize
Requirements¶
Python with fluids installed
cabal
haskell cpython package
Usage Notes¶
The example is incomplete and crashes on termination
Performance is 2 microsecond for friction factor, indicating there is very little overhead