Granular.jl

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

commit 289da8a97695063b56b4ceb0f34e8e57ebafc8c3
parent 57ade89c259cdb4ce92992bab7a0606b68e33f77
Author: Anders Damsgaard <andersd@riseup.net>
Date:   Tue, 25 Apr 2017 15:02:10 -0400

use NetCDF files from Baltic test case, interpolate to B grid

Diffstat:
Msrc/datatypes.jl | 25+++++++++++++------------
Msrc/ocean.jl | 146++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mtest/netcdf.jl | 23++++++++++++-----------
Dtest/prog__0001_006.nc | 0
4 files changed, 141 insertions(+), 53 deletions(-)

diff --git a/src/datatypes.jl b/src/datatypes.jl @@ -91,21 +91,22 @@ type IceFloeArrays end #= -Type containing all relevant data from MOM6 NetCDF file. The ocean grid is a +Type containing all relevant data from MOM6 NetCDF files. The ocean grid is a staggered of Arakawa-C type, with north-east convention centered on the -h-points. + h-points. During read, the velocities are interpolated to the cell corners + (q-points). - q(i-1, j) --- v( i, j) --- q( i, j) + q(i-1, j) ------------------ q( i, j) | | | | | | | | - u(i-1, j) h( i, j) u( i, j) + | h( i, j) | | | | | | | | | - q(i-1,j-1) --- v( i,j-1) --- q( i,j-1) + q(i-1,j-1) ------------------ q( i,j-1) Source: https://mom6.readthedocs.io/en/latest/api/generated/pages/Horizontal_indexing.html @@ -120,13 +121,13 @@ https://mom6.readthedocs.io/en/latest/api/generated/pages/Horizontal_indexing.ht * `zl::Array{Float64, 1}`: layer target potential density [kg m^-3] * `zi::Array{Float64, 1}`: interface target potential density [kg m^-3] * `u::Array{Float64, Int}`: zonal velocity (positive towards west) [m/s], - dimensions correspond to placement in `[xq, yh, zl, time]`. + dimensions correspond to placement in `[xq, yq, zl, time]`. * `v::Array{Float64, Int}`: meridional velocity (positive towards north) [m/s], - dimensions correspond to placement in `[xh, yq, zl, time]`. + dimensions correspond to placement in `[xq, yq, zl, time]`. * `h::Array{Float64, Int}`: layer thickness [m], dimensions correspond to placement in `[xh, yh, zl, time]`. * `e::Array{Float64, Int}`: interface height relative to mean sea level [m], - dimensions correspond to placement in `[xh, yq, zi, time]`. + dimensions correspond to placement in `[xh, yh, zi, time]`. =# type Ocean input_file::Any @@ -134,12 +135,12 @@ type Ocean time::Array{Float64, 1} # q-point (cell corner) positions - xq::Array{Float64, 1} - yq::Array{Float64, 1} + xq::Array{Float64, 2} + yq::Array{Float64, 2} # h-point (cell center) positions - xh::Array{Float64, 1} - yh::Array{Float64, 1} + xh::Array{Float64, 2} + yh::Array{Float64, 2} # Vertical positions zl::Array{Float64, 1} diff --git a/src/ocean.jl b/src/ocean.jl @@ -1,13 +1,17 @@ "Returns empty ocean type for initialization purposes." function createEmptyOcean() return Ocean(false, + zeros(1), + + zeros(1,1), + zeros(1,1), + zeros(1,1), + zeros(1,1), + zeros(1), zeros(1), - zeros(1), - zeros(1), - zeros(1), - zeros(1), + zeros(1,1,1,1), zeros(1,1,1,1), zeros(1,1,1,1), @@ -15,46 +19,128 @@ function createEmptyOcean() end """ -Read NetCDF file generated by MOM6 (e.g. `prog__####_###.nc`) from disk and -return as `Ocean` data structure. +Read ocean NetCDF files generated by MOM6 from disk and return as `Ocean` data +structure. + +# Arguments +* `velocity_file::String`: Path to NetCDF file containing ocean velocities, + etc., (e.g. `prog__####_###.nc`). +* `grid_file::String`: Path to NetCDF file containing ocean super-grid + information (typically `INPUT/ocean_hgrid.nc`). +""" +function readOceanNetCDF(velocity_file::String, grid_file::String) + + time, u, v, h, e, zl, zi = readOceanStateNetCDF(velocity_file) + xh, yh, xq, yq = readOceanGridNetCDF(grid_file) + + if size(u[:,:,1,1]) != size(xq) || size(v[:,:,1,1]) != size(xq) || + size(xq) != size(yq) + error("size mismatch between velocities and grid + (u: $(size(u[:,:,1,1])), v: $(size(v[:,:,1,1])), + xq: $(size(xq)), yq: $(size(yq)))") + end + + ocean = Ocean([grid_file, velocity_file], + + time, + + xq, + yq, + xh, + yh, + + zl, + zi, + + u, + v, + h, + e) + return ocean +end + +""" +Read NetCDF file with ocean state generated by MOM6 (e.g. `prog__####_###.nc` +or `########.ocean_month.nc`) from disk and return time stamps, velocity fields, +layer thicknesses, interface heights, and vertical coordinates. + +# Returns +* `time::Array{Float, 1}`: Time [s] +* `u::Array{Float, 2}`: Cell corner zonal velocity [m/s], + dimensions correspond to placement in `[xq, yq, zl, time]` +* `v::Array{Float, 2}`: Cell corner meridional velocity [m/s], + dimensions correspond to placement in `[xq, yq, zl, time]` +* `h::Array{Float64, 2}`: layer thickness [m], dimensions correspond to + placement in `[xh, yh, zl, time]` +* `e::Array{Float64, 2}`: interface height relative to mean sea level [m], + dimensions correspond to placement in `[xh, yh, zi, time]` +* `zl::Array{Float64, 1}`: layer target potential density [kg m^-3] +* `zi::Array{Float64, 1}`: interface target potential density [kg m^-3] """ -function readOceanNetCDF(filename::String) +function readOceanStateNetCDF(filename::String) if !isfile(filename) error("$(filename) could not be opened") end + u_staggered::Array{float, 4} = NetCDF.ncread(filename, "u") v_staggered::Array{float, 4} = NetCDF.ncread(filename, "v") - u, v = convertToColocatedOceanGrid(u_staggered, v_staggered) + u, v = interpolateOceanVelocitiesToCorners(u_staggered, v_staggered) - ocean = Ocean(filename, - NetCDF.ncread(filename, "Time"), + time = NetCDF.ncread(filename, "time")*24.*60.*60. + h = NetCDF.ncread(filename, "h") + e = NetCDF.ncread(filename, "e") - NetCDF.ncread(filename, "xq"), - NetCDF.ncread(filename, "yq"), - NetCDF.ncread(filename, "xh"), - NetCDF.ncread(filename, "yh"), - NetCDF.ncread(filename, "zl"), - NetCDF.ncread(filename, "zi"), + zl = NetCDF.ncread(filename, "zl") + zi = NetCDF.ncread(filename, "zi") - u, - v, - NetCDF.ncread(filename, "h"), - NetCDF.ncread(filename, "e")) - return ocean + return time, u, v, h, e, zl, zi +end + +""" +Read NetCDF file with ocean *supergrid* information generated by MOM6 (e.g. +`ocean_hrid.nc`) from disk and return as `Ocean` data structure. This file is +located in the simulation `INPUT/` subdirectory. + +# Returns +* `xh::Array{Float, 2}`: Longitude for cell centers [deg] +* `yh::Array{Float, 2}`: Latitude for cell centers [deg] +* `xq::Array{Float, 2}`: Longitude for cell corners [deg] +* `yq::Array{Float, 2}`: Latitude for cell corners [deg] +""" +function readOceanGridNetCDF(filename::String) + + if !isfile(filename) + error("$(filename) could not be opened") + end + x::Array{float, 2} = NetCDF.ncread(filename, "x") + y::Array{float, 2} = NetCDF.ncread(filename, "y") + + xh = x[2:2:end, 2:2:end] + yh = y[2:2:end, 2:2:end] + + xq = x[1:2:end, 1:2:end] + yq = y[1:2:end, 1:2:end] + + return xh, yh, xq, yq end """ -Convert gridded data from staggered (Arakawa-C) to collocated grid (Arakawa-A) -through interpolation. The new data points are located in the centers of the -original staggered grid (spatial coordinates `xh` and `yh`). +Convert gridded data from Arakawa-C type (decomposed velocities at faces) to +Arakawa-B type (velocities at corners) through interpolation. """ -function convertToColocatedOceanGrid(u_in::Array{float, 4}, - v_in::Array{float, 4}) - u = Array{float}(size(u_in)) - v = Array{float}(size(v_in)) - nx = size(u_in)[1] - ny = size(u_in)[2] +function interpolateOceanVelocitiesToCorners(u_in::Array{float, 4}, + v_in::Array{float, 4}) + + if size(u_in) != size(v_in) + error("size of u_in ($(size(u_in))) must match v_in ($(size(v_in)))") + end + + nx, ny, nz, nt = size(u_in) + #u = Array{float}(nx+1, ny+1, nz, nt) + #v = Array{float}(nx+1, ny+1, nz, nt) + u = zeros(nx+1, ny+1, nz, nt) + v = zeros(nx+1, ny+1, nz, nt) for i=1:nx for j=1:ny if j < ny - 1 diff --git a/test/netcdf.jl b/test/netcdf.jl @@ -4,14 +4,15 @@ info("#### $(basename(@__FILE__)) ####") -info("Testing dimensions of content read from prog__0001_006.nc") -ocean = SeaIce.readOceanNetCDF("prog__0001_006.nc") -@test length(ocean.xq) == 44 -@test length(ocean.xh) == 44 -@test length(ocean.yq) == 40 -@test length(ocean.yh) == 40 -@test ocean.time ≈ [5., 10.] -@test size(ocean.u) == (44, 40, 2, 2) -@test size(ocean.v) == (44, 40, 2, 2) -@test size(ocean.h) == (44, 40, 2, 2) -@test size(ocean.e) == (44, 40, 3, 2) +info("Testing dimensions of content read from Baltic test case") +ocean = SeaIce.readOceanNetCDF("Baltic/00010101.ocean_month.nc", + "Baltic/ocean_hgrid.nc") +@test ocean.time/(24.*60.*60.) ≈ [.5, 1.5, 2.5, 3.5, 4.5] +@test size(ocean.xq) == (24, 15) +@test size(ocean.yq) == (24, 15) +@test size(ocean.xh) == (23, 14) +@test size(ocean.yh) == (23, 14) +@test size(ocean.u) == (24, 15, 63, 5) +@test size(ocean.v) == (24, 15, 63, 5) +@test size(ocean.h) == (23, 14, 63, 5) +@test size(ocean.e) == (23, 14, 64, 5) diff --git a/test/prog__0001_006.nc b/test/prog__0001_006.nc Binary files differ.