packages feed

dph-examples-0.5.1.1: real/NBody/Solver/NestedBH/Solver.hs

{-# LANGUAGE ParallelArrays #-}
{-# OPTIONS -fvectorise #-}
module Solver.NestedBH.Solver 
	(calcAccelsWithBoxPA)
where
import Data.Array.Parallel
import Data.Array.Parallel.Prelude.Double
import qualified Data.Array.Parallel.Prelude.Int as I
import qualified Prelude

data BoundingBox	
	= Box	Double 		-- lower left  X
		Double 		-- lower left  Y
		Double		-- upper right X
		Double		-- upper right Y

data MassPoint
	= MP 	Double 		-- pos X
		Double		-- pos Y
		Double		-- mass

type Accel	
	= (Double, Double)

data BHTree
	= BHT	Double		-- size of cell
		Double		-- centroid X
		Double		-- centroid Y
		Double		-- centroid mass
		[:BHTree:]	-- children

calcAccelsWithBoxPA
	:: Double
	-> Double -> Double -> Double -> Double
	-> PArray (Double, Double, Double)
	-> PArray (Double, Double)

calcAccelsWithBoxPA epsilon llx lly rux ruy mpts
 = let	mpts'	= [: MP x y m | (x, y, m) <- fromPArrayP mpts :]
	accs'	= calcAccelsWithBox epsilon llx lly rux ruy mpts'
   in	toPArrayP accs'
	

-- | Given the extend of a bounding box containing all the points,
--   calculate the accelerations on all of them.
calcAccelsWithBox
	:: Double
	-> Double -> Double -> Double -> Double
	-> [: MassPoint :]
	-> [: Accel :]

calcAccelsWithBox epsilon llx lly rux ruy mspts
 = accs
 where	accs = [: calcAccel epsilon m tree | m <- mspts :]
	tree = buildTree (Box llx lly rux ruy) mspts


-- | Build the Barnes-Hut quadtree tree.
buildTree :: BoundingBox -> [: MassPoint :] -> BHTree
buildTree bb particles
 | lengthP particles I.<= 1	= BHT s x y m emptyP
 | otherwise			= BHT s x y m subTrees
 where	(MP x y m)		= calcCentroid particles
	(boxes, splitPnts)	= splitPoints bb particles 
    	subTrees		= [:buildTree bb' ps | (bb', ps) <- zipP boxes splitPnts:]
  
	(Box llx lly rux ruy)	= bb
	sx			= rux - llx
	sy			= ruy - lly
	s			= if sx < sy then sx else sy


-- | Split massPoints according to their locations in the quadrants.
splitPoints
	:: BoundingBox
	-> [: MassPoint :]
	-> ([:BoundingBox:], [:[: MassPoint :]:])

splitPoints b@(Box llx lly rux  ruy) particles 
  | noOfPoints I.<= 1 = (singletonP b, singletonP particles)
  | otherwise         
  = unzipP [: (b,p) | (b,p) <- zipP boxes splitPars, lengthP p I.> 0:]
  where	noOfPoints	= lengthP particles
	lls		= [: p | p <- particles, inBox b1 p :]
	lus		= [: p | p <- particles, inBox b2 p :]
	rus		= [: p | p <- particles, inBox b3 p :]
	rls		= [: p | p <- particles, inBox b4 p :]
	b1		= Box llx  lly  midx midy
	b2		= Box llx  midy midx  ruy
	b3		= Box midx midy rux   ruy
 	b4		= Box midx lly  rux  midy
	boxes		= singletonP b1  +:+ singletonP b2  +:+ singletonP b3 +:+ singletonP b4 
	splitPars	= singletonP lls +:+ singletonP lus +:+ singletonP rus +:+ singletonP rls
	(midx,  midy)	= ((llx + rux) / 2.0 , (lly + ruy) / 2.0) 


-- | Checks if particle is in box (excluding left and lower border)
inBox :: BoundingBox -> MassPoint -> Bool
inBox (Box llx  lly rux  ruy) (MP px  py  _) 
 	= (px > llx) && (px <= rux) && (py > lly) && (py <= ruy)


-- | Calculate the centroid of some points.
calcCentroid:: [:MassPoint:] -> MassPoint
calcCentroid mpts 
 = MP  (sumP xs / mass) (sumP ys / mass) mass
 where	mass     = sumP [: m | MP _ _ m  <- mpts :]
	(xs, ys) = unzipP [: (m * x, m * y) | MP x y m <- mpts :]   


-- | Calculate the accelleration of a point due to the points in the given tree.
calcAccel :: Double -> MassPoint -> BHTree -> (Double, Double)
calcAccel epsilon mpt (BHT s x y m subtrees)
	| lengthP subtrees I.== 0
	= accel epsilon mpt (MP x y m)

	| isFar mpt s x y 
	= accel epsilon mpt (MP x y m)

	| otherwise
	= let	(xs, ys) = unzipP [: calcAccel epsilon mpt st | st <- subtrees :]
	  in	(sumP xs, sumP ys)


-- | Calculate the acceleration on a point due to some other point.
accel 	:: Double 	-- ^ If the distance between the points is smaller than this
			--   then ignore the forces between them.
	-> MassPoint	-- ^ The point being acclerated.
	-> MassPoint	-- ^ Neibouring point.
	-> Accel

accel epsilon (MP x1 y1 _) (MP x2 y2 m)  
 = (aabs * dx / r , aabs * dy / r)  
 where	rsqr = (dx * dx) + (dy * dy) + epsilon
	r    = sqrt rsqr 
	dx   = x1 - x2 
	dy   = y1 - y2 
	aabs = m / rsqr 


-- | If the point is far from a cell in the tree then we can use
--   it's centroid as an approximation of all the points in the region.
isFar 	:: MassPoint 	-- point being accelerated
	-> Double	-- size of region
	-> Double	-- position of center of mass of cell
	-> Double	-- position of center of mass of cell
	-> Bool

isFar (MP x1 y1 m) s x2 y2 
 = let	dx	= x2 - x1
	dy	= y2 - y1
	dist	= sqrt (dx * dx + dy * dy)
   in	(s / dist) < 1