Source code for salishsea_tools.LiveOcean_grid

# Tools for getting grid information from Live Ocean results
# Adapted from Parker MacCready's code

# Nancy Soontiens 2016
# updated based on Parker's 2019 version by Susan Allen and Doug Latornell

import netCDF4 as nc
import numpy as np


[docs] def get_basic_info(fn, only_G=False, only_S=False, only_T=False): """ Gets grid, vertical coordinate, and time info from a ROMS NetCDF history file with full name 'fn' Input: the filename (with path if needed) Output: dicts G, S, and T Example calls: G, S, T = zfun.get_basic_info(fn) T = zfun.get_basic_info(fn, only_T=True) """ ds = nc.Dataset(fn, "r") def make_G(ds): # get grid and bathymetry info g_varlist = [ "h", "lon_rho", "lat_rho", "lon_u", "lat_u", "lon_v", "lat_v", "mask_rho", "mask_u", "mask_v", "pm", "pn", ] G = dict() for vv in g_varlist: G[vv] = ds.variables[vv][:] G["DX"] = 1 / G["pm"] G["DY"] = 1 / G["pn"] G["M"], G["L"] = np.shape(G["lon_rho"]) # M = rows, L = columns # make the masks boolean (True = water, False = land, opposite of masked arrays!) G["mask_rho"] = G["mask_rho"] == 1 G["mask_u"] = G["mask_u"] == 1 G["mask_v"] = G["mask_v"] == 1 return G def make_S(ds): # get vertical sigma-coordinate info (vectors are bottom to top) s_varlist = ["s_rho", "hc", "Cs_r", "Vtransform"] S = dict() for vv in s_varlist: S[vv] = ds.variables[vv][:] S["N"] = len(S["s_rho"]) # number of vertical levels return S def make_T(ds): # get time info t_varlist = ["ocean_time"] T = dict() for vv in t_varlist: T[vv] = ds.variables[vv][:] T_units = ds.variables["ocean_time"].units tt = nc.num2date(T["ocean_time"][:], T_units) T["time"] = tt return T # return results if only_G: return make_G(ds) elif only_S: return make_S(ds) elif only_T: return make_T(ds) else: return make_G(ds), make_S(ds), make_T(ds)
[docs] def get_z(h, zeta, S): """ Used to calculate the z position of fields in a ROMS history file. Only for rho levels Input: arrays h (bathymetry depth) and zeta (sea surface height) which must be the same size, and dict S created by get_basic_info() Output: 3-D arrays of z_rho NOTE: one foible is that if you input arrays of h and zeta that are vectors of length VL, the output array (e.g. z_rho) will have size (N, VL) (i.e. it will never return an array with size (N, VL, 1), even if (VL, 1) was the input shape). This is a result of the initial and final squeeze calls. """ # input error checking if (not isinstance(h, np.ndarray)) or ( not isinstance(zeta, (np.ndarray, np.ma.core.MaskedArray)) ): print("WARNING from get_z(): Inputs must be numpy arrays") if not isinstance(S, dict): print("WARNING from get_z(): S must be a dict") # number of vertical levels N = S["N"] # remove singleton dimensions h = h.squeeze() zeta = zeta.squeeze() # ensure that we have enough dimensions h = np.atleast_2d(h) zeta = np.atleast_2d(zeta) # check that the dimensions are the same if h.shape != zeta.shape: print("WARNING from get_z(): h and zeta must be the same shape") M, L = h.shape # rho # create some useful arrays csr = S["Cs_r"] csrr = csr.reshape(N, 1, 1).copy() Cs_r = np.tile(csrr, [1, M, L]) H_r = np.tile(h.reshape(1, M, L).copy(), [N, 1, 1]) Zeta_r = np.tile(zeta.reshape(1, M, L).copy(), [N, 1, 1]) if S["hc"] == 0: # if hc = 0 the transform is simpler (and faster) z_rho = H_r * Cs_r + Zeta_r + Zeta_r * Cs_r elif S["hc"] != 0: # need to calculate a few more useful arrays sr = S["s_rho"] srr = sr.reshape(N, 1, 1).copy() S_rho = np.tile(srr, [1, M, L]) Hc_r = np.tile(S["hc"], [N, M, L]) if S["Vtransform"] == 1: zr0 = (S_rho - Cs_r) * Hc_r + Cs_r * H_r z_rho = zr0 + Zeta_r * (1 + zr0 / H_r) elif S["Vtransform"] == 2: zr0 = (S_rho * Hc_r + Cs_r * H_r) / (Hc_r + H_r) z_rho = Zeta_r + (Zeta_r + H_r) * zr0 z_rho = z_rho.squeeze() return z_rho