Granular.jl

Julia package for granular dynamics simulation
git clone git://src.adamsgaard.dk/Granular.jl
Log | Files | Refs | README | LICENSE

commit 94e04429fe360c2a5fec893229973145516381de
parent abeaa9bfc5a527df9a1bb1fbf63aa580a0139971
Author: Anders Damsgaard <andersd@riseup.net>
Date:   Wed, 26 Apr 2017 15:59:58 -0400

add conformal mapping functions

Diffstat:
Msrc/grid.jl | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mtest/grid.jl | 31+++++++++++++++++++++++++++++++
2 files changed, 122 insertions(+), 15 deletions(-)

diff --git a/src/grid.jl b/src/grid.jl @@ -65,34 +65,50 @@ end """ -Check if a 2d point is contained inside a cell from the ocean grid. Returns -`true`/`false`. +Check if a 2d point is contained inside a cell from the ocean grid. +The function uses either an area-based approach (`method = "Area"`), or a +conformal mapping approach (`method = "Conformal"`). The area-based approach is +more robust. This function returns `true` or `false`. """ -function isPointInCell(ocean::Ocean, i::Int, j::Int, point::Array{float, 1}) - - sw, nw, se, ne = getCellCornerCoordinates(ocean, i, j) +function isPointInCell(ocean::Ocean, i::Int, j::Int, point::Array{float, 1}; + method::String="Area") + + sw, se, ne, nw = getCellCornerCoordinates(ocean, i, j) + + if method == "Area" + if areaOfQuadrilateral(sw, se, ne, nw) ≈ + areaOfTriangle(point, sw, se) + + areaOfTriangle(point, se, ne) + + areaOfTriangle(point, ne, nw) + + areaOfTriangle(point, nw, sw) + return true + else + return false + end - if areaOfQuadrilateral(sw, nw, se, ne) ≈ - areaOfTriangle(point, sw, se) + - areaOfTriangle(point, se, ne) + - areaOfTriangle(point, ne, nw) + - areaOfTriangle(point, nw, sw) - return true + elseif method == "Conformal" + x_tilde, y_tilde = conformalQuadrilateralCoordinates(sw, se, ne, nw, + point) + if x_tilde >= 0. && x_tilde <= 1. && y_tilde >= 0. && y_tilde <= 1. + return true + else + return false + end else - return false + error("method not understood") end end """ Returns ocean-grid corner coordinates in the following order (south-west corner, -north-west corner, south-east corner, north-east corner). +south-east corner, north-east corner, north-west corner). """ function getCellCornerCoordinates(ocean::Ocean, i::Int, j::Int) sw = [ocean.xq[i-1, j-1], ocean.yq[i-1, j-1]] - nw = [ocean.xq[i-1, j], ocean.yq[i-1, j]] se = [ocean.xq[ i, j-1], ocean.yq[ i, j-1]] ne = [ocean.xq[ i, j], ocean.yq[ i, j]] - return sw, nw, se, ne + nw = [ocean.xq[i-1, j], ocean.yq[i-1, j]] + return sw, se, ne, nw end "Returns the area of an triangle with corner coordinates `a`, `b`, and `c`." @@ -119,3 +135,63 @@ function areaOfQuadrilateral(a::Array{float, 1}, return areaOfTriangle(a, b, c) + areaOfTriangle(c, d, a) end +""" +Returns the non-dimensional coordinates `[x_tilde, y_tilde]` of a point `p` +within a quadrilateral with corner coordinates `A`, `B`, `C`, and `D`. +Points must be ordered in counter-clockwise order, starting from south-west +corner. +""" +function conformalQuadrilateralCoordinates(A::Array{float, 1}, + B::Array{float, 1}, + C::Array{float, 1}, + D::Array{float, 1}, + p::Array{float, 1}) + alpha = B[1] - A[1] + delta = B[2] - A[2] + beta = D[1] - A[1] + epsilon = D[2] - A[2] + gamma = C[1] - A[1] - (alpha + beta) + kappa = C[2] - A[2] - (delta + epsilon) + a = kappa*beta - gamma*epsilon + dx = p[1] - A[1] + dy = p[2] - A[2] + b = (delta*beta - alpha*epsilon) - (kappa*dx - gamma*dy) + c = alpha*dy - delta*dx + if abs(a) > 0. + d = b^2./4. - a*c + if d >= 0. + yy1 = -(b/2. + sqrt(d))/a + yy2 = -(b/2. - sqrt(d))/a + if abs(yy1 - .5) < abs(yy2 - .5) + y_tilde = yy1 + else + y_tilde = yy2 + end + else + error("could not perform conformal mapping\n", + "A = $(A), B = $(B), C = $(C), D = $(D), point = $(p),\n", + "alpha = $(alpha), beta = $(beta), gamma = $(gamma), ", + "delta = $(delta), epsilon = $(epsilon), kappa = $(kappa)") + end + else + if !(b ≈ 0.) + y_tilde = -c/b + else + y_tilde = 0. + end + end + a = alpha + gamma*y_tilde + b = delta + kappa*y_tilde + if !(a ≈ 0.) + x_tilde = (dx - beta*y_tilde)/a + elseif !(b ≈ 0.) + x_tilde = (dy - epsilon*y_tilde)/b + else + error("could not determine non-dimensional position in quadrilateral ", + "(a = 0. and b = 0.)\n", + "A = $(A), B = $(B), C = $(C), D = $(D), point = $(p),\n", + "alpha = $(alpha), beta = $(beta), gamma = $(gamma), ", + "delta = $(delta), epsilon = $(epsilon), kappa = $(kappa)") + end + return [x_tilde, y_tilde] +end diff --git a/test/grid.jl b/test/grid.jl @@ -7,9 +7,12 @@ info("#### $(basename(@__FILE__)) ####") ocean = SeaIce.readOceanNetCDF("Baltic/00010101.ocean_month.nc", "Baltic/ocean_hgrid.nc") +info("Testing area-determination methods") @test SeaIce.areaOfTriangle([0., 0.], [1., 0.], [0., 1.]) ≈ .5 @test SeaIce.areaOfTriangle([1., 0.], [0., 1.], [0., 0.]) ≈ .5 @test SeaIce.areaOfQuadrilateral([1., 0.], [0., 1.], [0., 0.], [1., 1.]) ≈ 1. + +info("Testing area-based cell content determination") @test SeaIce.isPointInCell(ocean, 2, 2, [6.5, 53.5]) == true @test SeaIce.isPointInCell(ocean, 2, 2, [6.1, 53.5]) == true @test SeaIce.isPointInCell(ocean, 2, 2, [6.0, 53.5]) == true @@ -19,3 +22,31 @@ ocean = SeaIce.readOceanNetCDF("Baltic/00010101.ocean_month.nc", @test SeaIce.isPointInCell(ocean, 2, 2, [7.5, 53.5]) == false @test SeaIce.isPointInCell(ocean, 2, 2, [0.0, 53.5]) == false +info("Testing conformal mapping methods") +@test SeaIce.conformalQuadrilateralCoordinates([0., 0.], + [5., 0.], + [5., 3.], + [0., 3.], + [2.5, 1.5]) ≈ [0.5, 0.5] +@test SeaIce.conformalQuadrilateralCoordinates([0., 0.], + [5., 0.], + [5., 3.], + [0., 3.], + [7.5, 1.5]) ≈ [1.5, 0.5] +@test SeaIce.conformalQuadrilateralCoordinates([0., 0.], + [5., 0.], + [5., 3.], + [0., 3.], + [7.5,-1.5]) ≈ [1.5,-0.5] +info("Checking cell content using conformal mapping methods") +@test SeaIce.isPointInCell(ocean, 2, 2, [6.4, 53.4], method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 2, 2, [6.1, 53.5], method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 2, 2, [6.0, 53.5], method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 2, 2, [6.1, 53.7], method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 2, 2, [6.1, 53.9], method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 2, 2, [6.1, 53.99999], + method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 2, 2, [7.5, 53.5], + method="Conformal") == false +@test SeaIce.isPointInCell(ocean, 2, 2, [0.0, 53.5], + method="Conformal") == false