packages feed

gloss-examples-1.7.4.4: raster/Fluid/src-repa/Stage/Boundary.hs

{-# LANGUAGE BangPatterns #-}
module Stage.Boundary
        (setBoundary)
where
import Model
import Data.Array.Repa          as R
import Control.Monad
import Debug.Trace
import Config
import FieldElt

-- | Apply boundary conditions to a velocity field.
setBoundary :: Config -> VelocityField -> IO VelocityField
setBoundary config f
 = let  (width, _)      = configModelSize config
   in   (rebuild width f <=< setBoundary' width <=< grabBorders width) f 


-- | Takes the original VelocityField and the array of edges and replaces
--   edge values with new values
rebuild :: Int -> VelocityField -> VelocityField -> IO VelocityField
rebuild width field edges
 = field `deepSeqArray` edges `deepSeqArray` 
   do   computeUnboxedP $ backpermuteDft field (rebuildPosMap width) edges
{-# INLINE rebuild #-}


rebuildPosMap :: Int -> DIM2 -> Maybe DIM2
rebuildPosMap !width (Z:.j:.i)
        | j == 0          
        = Just (Z:.0:.i)

        | j == width - 1 
        = Just (Z:.1:.i)

        | i == 0          
        = if j == 0        then Just (Z:. 0 :. 0)
          else if j == end then Just (Z:. 1 :. 0)
                           else Just (Z:. 2 :. j)

        | i == width - 1 
        = if j == 0        then Just (Z:. 0 :. (width-1))
          else if j == end then Just (Z:. 1 :. (width-1))
                           else Just (Z:. 3 :. j)

        | otherwise       = Nothing
        where   end = width - 1
{-# INLINE rebuildPosMap #-}



-- | Grabs the border elements of the VelocityField and outputs them as
--   one array, for ease of adding back into the original VelocityField later
grabBorders :: Int -> VelocityField -> IO VelocityField
grabBorders width f
 = f `deepSeqArray` 
   do   traceEventIO "Fluid: grabBorders"
        computeUnboxedP $ backpermute (Z:. 4 :. width) (edgeCases width) f
{-# INLINE grabBorders #-}


-- | Map a position in the edges array to what they were in the original
--   array.
edgeCases :: Int -> DIM2 -> DIM2
edgeCases width (Z:.j:.i)
        | j == 0    = (Z:.0         :.i)
        | j == 1    = (Z:.(width-1) :.i)
        | j == 2    = (Z:.i         :.0)
        | j == 3    = (Z:.i         :.(width-1))
        | otherwise = error "Incorrect coordinate given in setBoundary"
{-# INLINE edgeCases #-}



setBoundary' :: Int -> VelocityField -> IO VelocityField
setBoundary' width e
 = e `deepSeqArray` 
   do   traceEventIO "Fluid: setBoundary'"
        computeUnboxedP $ traverse e id (revBoundary width)
{-# INLINE setBoundary' #-}


-- | Based on position in edges array set the velocity accordingly
revBoundary :: Int -> (DIM2 -> (Float,Float)) -> DIM2 -> (Float,Float)
revBoundary width loc pos@(Z:.j:.i)
        | j == 0    
        = if i == 0        then grabCornerCase loc (Z:.2:.1) (Z:.0:.1)
          else if i == end then grabCornerCase loc (Z:.0:.(width-2)) (Z:.3:.1)
                           else (-p1,p2)
        | j == 1    
        = if i == 0        then grabCornerCase loc (Z:.2:.(width-2)) (Z:.1:.1)
          else if i == end then grabCornerCase loc (Z:.1:.(width-2)) (Z:.3:.(width-2))
                           else (-p1,p2)

        | j == 2        = (p1,-p2)
        | j == 3        = (p1,-p2)

        | otherwise     = error "Fluid: revBoundary"
        where (p1,p2)   = loc pos
              end       = width - 1


{-# INLINE revBoundary #-}

-- | Corner cases are special and are calculated with this function
grabCornerCase :: (DIM2 -> (Float, Float)) -> DIM2 -> DIM2 -> (Float, Float)
grabCornerCase loc pos1 pos2
 = (p1 * q1, p2 * q2) ~*~ 0.5
 where  (p1,p2) = loc pos1
        (q1,q2) = loc pos2
{-# INLINE grabCornerCase #-}