hfoil-0.2.0: src/HFoil/Flow.hs
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE FlexibleContexts #-}
module HFoil.Flow
( FlowSol(..)
, solveFlow
) where
import Data.Packed.ST (newMatrix, writeMatrix, freezeMatrix)
import Control.Monad ( forM_ )
import Control.Monad.ST ( runST )
import Numeric.LinearAlgebra hiding(i)
import Foreign.Storable ( Storable )
import HFoil.Foil
data FlowSol a = FlowSol { solFoil :: Foil a
, solVs :: Vector a
, solCps :: Vector a
, solVorticities :: [a]
, solAlpha :: a
, solForces :: (Vector a, Vector a)
, solForce :: (a,a)
, solCl :: a
, solCd :: a
, solCm :: a -- moment about (0,0.25)
, solCenterPressure :: (a,a)
, solKuttaIndices :: [(Int,Int)]
}
solveFlow :: (Num (Vector a), RealFloat a, Field a) => Foil a -> a -> FlowSol a
solveFlow foil@(Foil elements _) alpha =
FlowSol { solFoil = foil
, solVs = vs
, solCps = cps
, solVorticities = vorticities
, solAlpha = alpha
, solForces = (xForces, yForces)
, solForce = (xf,yf)
, solCl = cl
, solCd = cd
, solCm = cm
, solCenterPressure = (xCp, yCp)
, solKuttaIndices = kuttaIndices
}
where
-- surface velocities
(vs,vorticities) = ( (mapVector (\q -> cos(q - alpha)) angles) + (mV <> qsGamma)
, map (qsGamma @>) [n-(length elements),n-1]
)
where
(mA, mV, b) = getAVb geometries angles kuttaIndices alpha
qsGamma = flatten $ linearSolve mA b
n = dim angles
-- pressure coefficients
cps = 1 - vs*vs
-- forces and force coefficients
xForces = -cps*(vjoin $ map (fst . fNormals) elements)
yForces = -cps*(vjoin $ map (snd . fNormals) elements)
xf = sumElements xForces
yf = sumElements yForces
cd = xf*(cos alpha) + yf*(sin alpha)
cl = -xf*(sin alpha) + yf*(cos alpha)
-- midpoints
(xs,ys) = (\(x,y) -> (vjoin x, vjoin y)) $ unzip $ map fMidpoints elements
-- centers of pressure
(xCp, yCp) = ( (sumElements (xs*yForces)) / (sumElements yForces)
, (sumElements (ys*xForces)) / (sumElements xForces)
)
-- moment about cp and quarter chord
cm = sumElements $ (mapVector (\z -> z - 0.25) xs)*yForces - ys*xForces
kuttaIndices = ki 0 (map dim angles')
where
ki _ [] = []
ki n0 (n:ns) = (n0,n0+n-1):(ki (n0+n) ns)
angles = vjoin angles'
angles' = map fAngles elements
-- (mSL,mCL,mSB,mCB)
geometries = setupGeometries angles (xms, yms) (xns, yns) (xnps, ynps)
where
vjoinTuples (x,y) = (vjoin x, vjoin y)
(xms,yms) = vjoinTuples $ unzip $ map fMidpoints elements
(xns,yns) = vjoinTuples $ unzip $ map fInits elements
(xnps,ynps) = vjoinTuples $ unzip $ map fTails elements
-- make influence matrix A and also the matrix V where V*[sources; vortex] == tangential speeds
getAVb :: (Floating a, Num (Vector a), Container Vector a, Storable a) =>
(Matrix a, Matrix a, Matrix a, Matrix a)
-> Vector a
-> [(Int, Int)]
-> a
-> (Matrix a, Matrix a, Matrix a)
getAVb (mSL,mCL,mSB,mCB) angles kuttaIndices alpha =
( scale (1/(2*pi)) $ fromBlocks [[ mAij, fromColumns vsAin]
,[fromRows vsAnj, fromLists ann]]
, scale (1/(2*pi)) $ fromBlocks [[ mSB - mCL
, fromColumns vsTin]]
, asColumn $ vjoin $ (mapVector (\q -> sin $ q - alpha) angles):
(map (\(n0,nf) -> fromList [-cos((angles @> n0) - alpha) - cos((angles @> nf) - alpha)]) kuttaIndices)
) -- (for middle entry mV, mSL + mCB == mAij)
where
n = rows mSL
-- sources influence matrix Aij
mAij = mSL + mCB
-- vortices influence vectors Ains
vsAin = map (\(n0,nf) -> fromList $ map sumElements $ toRows $ subMatrix (0,n0) (n, nf-n0+1) m) kuttaIndices
where
m = (mCL - mSB)
-- vortices tangential velocity outputs
vsTin = map (\(n0,nf) -> fromList $ map sumElements $ toRows $ subMatrix (0,n0) (n, nf-n0+1) m) kuttaIndices
where
m = (mSL + mCB)
-- sources kutta condition influence vector Anj
vsAnj = map (\(n0,nf) -> (getRow n0 mSB) - (getRow n0 mCL) + (getRow nf mSB) - (getRow nf mCL)) kuttaIndices
-- vortices kutta condition influence scalars ann
ann = (flip map) kuttaIndices $ \(ni0,nif) -> (flip map) kuttaIndices $ \(nj0,njf) ->
sumElements $ (getSubRow ni0 (nj0,njf) mSL) + (getSubRow ni0 (nj0,njf) mCB) + (getSubRow nif (nj0,njf) mSL) + (getSubRow nif (nj0,njf) mCB)
getRow i mat = flatten $ subMatrix (i,0) (1,n) mat
getSubRow i (j0,jf) mat = flatten $ subMatrix (i,j0) (1,jf-j0+1) mat
{-
calcuate 4 matrices which will be useful
mSL(i,j) = sin(qi - ij) * ln(rij+1/rij)
mCL(i,j) = cos(qi - ij) * ln(rij+1/rij)
mSB(i,j) = sin(qi - ij) * beta(i,j)
mCB(i,j) = cos(qi - ij) * beta(i,j)
where for i==j: ln(r/r) = 0
beta = pi
are explicitly set
-}
setupGeometries :: (RealFloat t, Storable t) =>
Vector t
-> (Vector t, Vector t)
-> (Vector t, Vector t)
-> (Vector t, Vector t)
-> (Matrix t, Matrix t, Matrix t, Matrix t)
setupGeometries angles (xms, yms) (xns, yns) (xnps, ynps) = runST $ do
let n = dim angles
mSL' <- newMatrix 0 n n
mCL' <- newMatrix 0 n n
mSB' <- newMatrix 0 n n
mCB' <- newMatrix 0 n n
forM_ [0..n-1] $ \i -> forM_ [0..n-1] $ \j -> do
let qi = angles @> i
qj = angles @> j
xmi = xms @> i
ymi = yms @> i
xnj = xns @> j
ynj = yns @> j
xnjp = xnps @> j
ynjp = ynps @> j
s = sin (qi - qj)
c = cos (qi - qj)
l
| i == j = 0
| otherwise = log(r1/r0)
where
r1 = distance (xmi, ymi) (xnjp, ynjp)
r0 = distance (xmi, ymi) (xnj, ynj )
distance (x1,y1) (x0,y0) = sqrt $ dx*dx + dy*dy
where
dx = x1 - x0
dy = y1 - y0
b
| i == j = pi
| otherwise = atan2 (dyjp*dxj - dxjp*dyj)
(dxjp*dxj + dyjp*dyj)
where
dyj = ymi - ynj
dxj = xmi - xnj
dyjp = ymi - ynjp
dxjp = xmi - xnjp
_ <- writeMatrix mSL' i j (s*l)
_ <- writeMatrix mCL' i j (c*l)
_ <- writeMatrix mSB' i j (s*b)
writeMatrix mCB' i j (c*b)
sl' <- freezeMatrix mSL'
cl' <- freezeMatrix mCL'
sb' <- freezeMatrix mSB'
cb' <- freezeMatrix mCB'
return (sl',cl',sb',cb')