def constant_depth(field, grid, depth, zeta=None, threads=2): """ Find the values of a 3-D field at a constant depth for all times given. Parameters ---------- field : ndarray, ROMS 3-D field to interpolate onto a constant depth level. If 4-D, it will calculate through time. grid : seapy.model.grid or string or list, Grid that defines the depths and stretching for the field given depth : float, Depth (in meters) to find all values zeta : ndarray, optional, ROMS zeta field corresponding to field if you wish to apply the SSH correction to the depth calculations. threads : int, optional, Number of threads to use for processing Returns ------- nfield : ndarray, Values from ROMS field on the given constant depth """ grid = seapy.model.asgrid(grid) field = np.ma.masked_invalid(field, copy=False) depth = depth if depth < 0 else -depth if depth is None or grid.depth_rho.min() > depth > grid.depth_rho.max(): warn("Error: {:f} is out of range for the depth.".format(value)) return if np.ndim(field) == 3: field = seapy.adddim(field) nt = field.shape[0] threads = np.minimum(nt, threads) if zeta is None: zeta = np.zeros((nt, 1, 1)) if np.ndim(zeta) == 2: zeta = seapy.adddim(zeta, nt) v_grid = u_grid = False if field.shape[-2:] == grid.mask_u: u_grid = True elif field.shape[-2:] == grid.mask_v: v_grid = True return np.ma.array(Parallel(n_jobs=threads, verbose=2)( delayed(__find_surface_thread)(grid, field[i, :], depth, zeta[i, :], const_depth=True, u_grid=u_grid, v_grid=v_grid) for i in range(nt)), copy=False)
def constant_value_k(field, grid, value, zeta=None, threads=2): """ Find the layer number of the value across the field. For example, find the k of a given isopycnal if the field is density. Parameters ---------- field : ndarray, ROMS 3-D field to interpolate onto a constant depth level. If 4-D, it will calculate through time. grid : seapy.model.grid or string or list, Grid that defines the depths and stretching for the field given value : float, Value to find the depths for in same units as the 'field' threads : int, optional, Number of threads to use for processing Returns ------- nfield : ndarray, Depths from ROMS field on the given value """ grid = seapy.model.asgrid(grid) field = np.ma.masked_invalid(field, copy=False) if value is None or field.min() > value > field.max(): warn("Error: {:f} is out of range for the field.".format(value)) return if np.ndim(field) == 3: field = seapy.adddim(field) nt = field.shape[0] threads = np.minimum(nt, threads) if zeta is None: zeta = np.zeros((nt, 1, 1)) if np.ndim(zeta) == 2: zeta = seapy.adddim(zeta, nt) v_grid = u_grid = False if field.shape[-2:] == grid.mask_u: u_grid = True elif field.shape[-2:] == grid.mask_v: v_grid = True return np.ma.array(Parallel(n_jobs=threads, verbose=2)( delayed(__find_surface_thread)(grid, field[i, :], value, zeta[i, :], k_values=True, u_grid=u_grid, v_grid=v_grid) for i in range(nt)), copy=False)
def convert_file(self, file, title="REMSS SST Obs"): """ Load an REMSS file and convert into an obs structure """ # Load REMSS Data nc = seapy.netcdf(file) lon = nc.variables["lon"][:] lat = nc.variables["lat"][:] dat = np.ma.masked_outside(np.squeeze( nc.variables["sea_surface_temperature"][:]) - 273.15, self.temp_limits[0], self.temp_limits[1]) err = np.ma.masked_outside(np.squeeze( nc.variables["SSES_standard_deviation_error"][:]), 0.01, 2.0) dat[err.mask] = np.ma.masked # Check the data flags flags = np.ma.masked_not_equal( np.squeeze(nc.variables["rejection_flag"][:]), 0) dat[flags.mask] = np.ma.masked err[flags.mask] = np.ma.masked # Grab the observation time time = netCDF4.num2date(nc.variables["time"][:], nc.variables["time"].units) time = np.array([(t - self.epoch).total_seconds() * seapy.secs2day for t in time]) sst_time = nc.variables["sst_dtime"][:] * seapy.secs2day for n, i in enumerate(time): sst_time[n, :, :] += i sst_time[dat.mask] = np.ma.masked # Set up the coordinate lon, lat = np.meshgrid(lon, lat) lon = np.ma.masked_where(dat.mask, seapy.adddim(lon, len(time))) lat = np.ma.masked_where(dat.mask, seapy.adddim(lat, len(time))) nc.close() if self.grid.east(): lon[lon < 0] += 360 data = [seapy.roms.obs.raw_data("TEMP", self.provenance, dat.compressed(), err.compressed(), self.temp_error)] # Grid it return seapy.roms.obs.gridder(self.grid, sst_time.compressed(), lon.compressed(), lat.compressed, None, data, self.dt, title)
def convert_file(self, file, title="VIIRS SST Obs"): """ Load a VIIRS file and convert into an obs structure """ # Load VIIRS Data nc = seapy.netcdf(file, aggdim="time") lon = nc.variables["lon"][:] lat = nc.variables["lat"][:] dat = np.ma.masked_outside( nc.variables["sea_surface_temperature"][:] - 273.15, self.temp_limits[0], self.temp_limits[1]) err = np.ma.masked_outside( nc.variables["sses_standard_deviation"][:], 0.01, 2.0) dat[err.mask] = np.ma.masked # Check the data flags if self.check_qc_flags: flags = np.ma.masked_not_equal( nc.variables["quality_level"][:], 5) dat[flags.mask] = np.ma.masked else: dat = np.ma.masked_where( nc.variables["quality_level"][:].data == 1, dat) # Grab the observation time time = netCDF4.num2date(nc.variables["time"][:], nc.variables["time"].units) - self.epoch time = np.asarray([x.total_seconds() for x in time])[ :, np.newaxis, np.newaxis] dtime = nc.variables["sst_dtime"][:] time = (time + dtime) * seapy.secs2day nc.close() # Set up the coordinate lon = np.ma.masked_where(dat.mask, seapy.adddim(lon, len(time))) lat = np.ma.masked_where(dat.mask, seapy.adddim(lat, len(time))) if self.grid.east(): lon[lon < 0] += 360 good = dat.nonzero() data = [seapy.roms.obs.raw_data("TEMP", self.provenance, dat.compressed(), err[good], self.temp_error)] # Grid it return seapy.roms.obs.gridder(self.grid, time[good], lon[good], lat[good], None, data, self.dt, title)
def convert_file(self, file, title="REMSS SST Obs"): """ Load an REMSS file and convert into an obs structure """ # Load REMSS Data nc = seapy.netcdf(file) lon = nc.variables["lon"][:] lat = nc.variables["lat"][:] dat = np.ma.masked_outside(np.squeeze( nc.variables["sea_surface_temperature"][:]) - 273.15, self.temp_limits[0], self.temp_limits[1]) err = np.ma.masked_outside(np.squeeze( nc.variables["SSES_standard_deviation_error"][:]), 0.01, 2.0) dat[err.mask] = np.ma.masked # Check the data flags flags = np.ma.masked_not_equal( np.squeeze(nc.variables["rejection_flag"][:]), 0) dat[flags.mask] = np.ma.masked err[flags.mask] = np.ma.masked # Grab the observation time time = seapy.roms.num2date(nc, "time", epoch=self.epoch) sst_time = nc.variables["sst_dtime"][:] * seapy.secs2day for n, i in enumerate(time): sst_time[n, :, :] += i sst_time[dat.mask] = np.ma.masked # Set up the coordinate lon, lat = np.meshgrid(lon, lat) lon = np.ma.masked_where(dat.mask, seapy.adddim(lon, len(time))) lat = np.ma.masked_where(dat.mask, seapy.adddim(lat, len(time))) nc.close() if self.grid.east(): lon[lon < 0] += 360 data = [seapy.roms.obs.raw_data("TEMP", self.provenance, dat.compressed(), err.compressed(), self.temp_error)] # Grid it return seapy.roms.obs.gridder(self.grid, sst_time.compressed(), lon.compressed(), lat.compressed, None, data, self.dt, title)
def constant_depth(field, grid, depth, zeta=None, threads=-2): """ Find the values of a 3-D field at a constant depth for all times given. Parameters ---------- field : ndarray, ROMS 3-D field to interpolate onto a constant depth level. Can be two- or three-dimensional array (first dimension assumed to be time). grid : seapy.model.grid or string or list, Grid that defines the depths and stretching for the field given depth : float, Depth (in meters) to find all values zeta : ndarray, optional, ROMS zeta field corresponding to field if you wish to apply the SSH correction to the depth calculations. threads : int, optional, Number of threads to use for processing Returns ------- nfield : ndarray, Values from ROMS field on the given constant depth """ # Make sure our inputs are all valid grid = seapy.model.asgrid(grid) if np.ndim(field) == 3: field = seapy.adddim(field) if zeta is not None and np.ndim(zeta == 2): zeta = seapy.adddim(zeta) depth = depth if depth < 0 else -depth # Set up some arrays x, y = np.meshgrid(np.arange(field.shape[-1]), np.arange(field.shape[-2])) fz, pmap = seapy.oasurf(x, y, x, x, y, None, 5, 1, 1) fz = seapy.adddim(np.ones(x.shape)) * depth # Loop over all times, generate new field at depth nfield = np.ma.array(Parallel(n_jobs=threads, verbose=2)( delayed(__dinterp)(x, y, grid.depth_rho, np.squeeze(field[i, :, :, :]), fz, pmap) for i in range(field.shape[0])), copy=False) return nfield
def constant_depth(field, grid, depth, zeta=None, threads=-2): """ Find the values of a 3-D field at a constant depth for all times given. Parameters ---------- field : ndarray, ROMS 3-D field to interpolate onto a constant depth level. Can be two- or three-dimensional array (first dimension assumed to be time). grid : seapy.model.grid or string or list, Grid that defines the depths and stretching for the field given depth : float, Depth (in meters) to find all values zeta : ndarray, optional, ROMS zeta field corresponding to field if you wish to apply the SSH correction to the depth calculations. threads : int, optional, Number of threads to use for processing Returns ------- nfield : ndarray, Values from ROMS field on the given constant depth """ # Make sure our inputs are all valid grid = seapy.model.asgrid(grid) if np.ndim(field) == 3: field = seapy.adddim(field) if zeta is not None and np.ndim(zeta == 2): zeta = seapy.adddim(zeta) depth = depth if depth < 0 else -depth # Set up some arrays x, y = np.meshgrid(np.arange(field.shape[-1]), np.arange(field.shape[-2])) fz, pmap = seapy.oasurf(x, y, x, x, y, None, 5, 1, 1) fz = seapy.adddim(np.ones(x.shape)) * depth # Loop over all times, generate new field at depth nfield = np.ma.array(Parallel(n_jobs=threads, verbose=2) (delayed(__dinterp)(x, y, grid.depth_rho, np.squeeze(field[i, :, :, :]), fz, pmap) for i in range(field.shape[0])), copy=False) return nfield
def __interp3_thread(rx, ry, rz, data, zx, zy, zz, pmap, weight, nx, ny, mask, up_factor=1.0, down_factor=1.0): """ internal routine: 3D interpolation thread for parallel interpolation """ # Make the mask 3D mask = seapy.adddim(mask, zz.shape[0]) data = np.ma.fix_invalid(data, copy=False) # To avoid extrapolation, we are going to convolve ocean over the land # and add a new top and bottom layer that replicates the data of the # existing current and top. 1) iteratively convolve until we have # filled most of the points, 2) Determine which way the # depth goes and add/subtract new layers, and 3) fill in masked values # from the layer above/below. gradsrc = (rz[0, 1, 1] - rz[-1, 1, 1]) > 0 # Convolve the water over the land ksize = 2 * np.round(np.sqrt((nx / np.median(np.diff(rx)))**2 + (ny / np.median(np.diff(ry.T)))**2)) + 1 if ksize < _ksize_range[0]: warn("nx or ny values are too small for stable OA, {:f}".format(ksize)) ksize = _ksize_range[0] elif ksize > _ksize_range[1]: warn("nx or ny values are too large for stable OA, {:f}".format(ksize)) ksize = _ksize_range[1] # Iterate at most 5 times, but we will hopefully break out before that by # checking if we have filled at least 40% of the bottom to be like # the surface bot = -1 if gradsrc else 0 top = 0 if gradsrc else -1 topmask = np.maximum(1, np.ma.count_masked(data[top, :, :])) if np.ma.count_masked(data[bot, :, :]) > 0: for iter in range(5): # Check if we have most everything by checking the bottom data = seapy.convolve_mask(data, ksize=ksize + iter, copy=False) if topmask / np.maximum(1, np.ma.count_masked(data[bot, :, :])) > 0.4: break # Now fill vertically nrz = np.zeros((data.shape[0] + 2, data.shape[1], data.shape[2])) nrz[1:-1, :, :] = rz nrz[bot, :, :] = rz[bot, :, :] - 5000 nrz[top, :, :] = 1 if not gradsrc: # The first level is the bottom # factor = down_factor levs = np.arange(data.shape[0], 0, -1) - 1 else: # The first level is the top # factor = up_factor levs = np.arange(0, data.shape[0]) # Fill in missing values where we have them from the shallower layer for k in levs[1:]: if np.ma.count_masked(data[k, :, :]) == 0: continue idx = np.nonzero(np.logical_xor(data.mask[k, :, :], data.mask[k - 1, :, :])) data.mask[k, idx[0], idx[1]] = data.mask[k - 1, idx[0], idx[1]] data[k, idx[0], idx[1]] = data[k - 1, idx[0], idx[1]] * down_factor # Add upper and lower boundaries ndat = np.zeros((data.shape[0] + 2, data.shape[1], data.shape[2])) ndat[bot, :, :] = data[bot, :, :].filled(np.nan) * down_factor ndat[1:-1, :, :] = data.filled(np.nan) ndat[top, :, :] = data[top, :, :].filled(np.nan) * up_factor # Interpolate the field and return the result with timeout(minutes=30): if gradsrc: res, pm = seapy.oavol(rx, ry, nrz[::-1, :, :], ndat[::-1, :, :], zx, zy, zz, pmap, weight, nx, ny) else: res, pm = seapy.oavol(rx, ry, nrz, ndat, zx, zy, zz, pmap, weight, nx, ny) return np.ma.masked_where(np.logical_or(mask == 0, np.abs(res) > 9e4), res, copy=False)
def plot_depths(self, row=None, col=None, ax=None): """ Plot the depths of a model grid along a row or column transect. If the bathymetry is known, it is plotted also. Parameters ---------- row : int, optional The row number to plot col : int, optional The column number to plot ax : matplotlib.axes, optional The axes to use for the figure Returns ------- ax : matplotlib.axes The axes containing the plot """ import matplotlib.pyplot as plt # Create the axes if we don't have any if not ax: fig = plt.figure() ax = fig.add_subplot(111) # ax.set_bg_color('darkseagreen') # Get the data if row: sz = np.s_[:, row, :] s = np.s_[row, :] x = self.lon_rho[s] label = "Longitude" elif col: sz = np.s_[:, :, col] s = np.s_[:, col] x = self.lat_rho[s] label = "Latitude" else: warn("You must specify a row or column") return # If it is ROMS, we should plot the top and bottom of the cells if self._isroms: sr, csr = seapy.roms.stretching(self.vstretching, self.theta_s, self.theta_b, self.hc, self.n, w_grid=True) dep = np.ma.masked_where( seapy.adddim(self.mask_rho[s], self.n + 1) == 0, seapy.roms.depth(self.vtransform, self.h[s], self.hc, sr, csr, w_grid=True)) else: dep = np.ma.masked_where( seapy.adddim(self.mask_rho[s], self.n) == 0, self.depth_rho[sz]) h = -self.h[s] # Begin with the bathymetric data ax.fill_between(x, h, np.min(h), facecolor="darkseagreen", interpolate=True) # Plot the layers ax.plot(x, dep.T, color="grey") # Labels ax.set_xlabel(label + " [deg]") ax.set_ylabel("Depth [m]") # Make it tight plt.autoscale(ax, tight=True) return ax
def from_stations(station_file, bry_file, grid=None): """ Construct a boundary forcing file from a stations file generated by a parent-grid. The stations.in file must have been generated by the seapy.roms.gen_stations method; otherwise, the order will be incorrect. Parameters ========== station_file : string Filename of the stations file that is the source for the boundary data bry_file : string Filename of the boundary conditions file to generate grid : string or seapy.model.grid Grid that the boundary conditions are created for Returns ------- None """ grid = seapy.model.asgrid(grid) ncstation = netCDF4.Dataset(station_file) src_ref, time = seapy.roms.get_reftime(ncstation) # Create the boundary file and fill up the descriptive data ncbry = seapy.roms.ncgen.create_bry(bry_file, eta_rho=grid.eta_rho, xi_rho=grid.xi_rho, s_rho=grid.n, reftime=src_ref, clobber=False, title="generated from " + station_file) grid.to_netcdf(ncbry) # Load the times: we need to see if the times are duplicated # because if using assimilation, they may be duplicated for every # outer-loop. Currently, it overwrites the first one each time (this # will need to be fixed if ROMS is fixed). statime = ncstation.variables[time][:] dup = np.where(statime[1:] == statime[0])[0] rng = np.s_[:] if dup.size > 0: rng = np.s_[0:np.min(dup)] statime = statime[rng] brytime = seapy.roms.get_timevar(ncbry) ncbry.variables[brytime][:] = seapy.roms.date2num( seapy.roms.num2date(ncstation, time, rng), ncbry, brytime) # Set up the indices bry = { "south": range(0, grid.lm), "north": range(grid.lm, 2 * grid.lm), "west": range(2 * grid.lm, 2 * grid.lm + grid.ln), "east": range(2 * grid.lm + grid.ln, 2 * (grid.lm + grid.ln)) } # Get the information to construct the depths of the station data sta_vt = ncstation.variables["Vtransform"][:] sta_hc = ncstation.variables["hc"][:] sta_s_rho = ncstation.variables["s_rho"][:] sta_cs_r = ncstation.variables["Cs_r"][:] sta_h = ncstation.variables["h"][:] sta_angle = ncstation.variables["angle"][:] sta_lon = ncstation.variables["lon_rho"][:] sta_lat = ncstation.variables["lat_rho"][:] sta_mask = np.ones(sta_lat.shape) sta_mask[sta_lon * sta_lat > 1e10] = 0 # Load the station data as we need to manipulate it sta_zeta = np.ma.masked_greater(ncstation.variables["zeta"][rng], 100) sta_ubar = np.ma.masked_greater(ncstation.variables["ubar"][rng], 100) sta_vbar = np.ma.masked_greater(ncstation.variables["vbar"][rng], 100) sta_temp = np.ma.masked_greater(ncstation.variables["temp"][rng], 100) sta_salt = np.ma.masked_greater(ncstation.variables["salt"][rng], 100) sta_u = np.ma.masked_greater(ncstation.variables["u"][rng], 100) sta_v = np.ma.masked_greater(ncstation.variables["v"][rng], 100) ncstation.close() # Create the true positions and mask grid_h = np.concatenate([grid.h[0, :], grid.h[-1, :], grid.h[:, 0], grid.h[:, -1]]) grid_lon = np.concatenate([grid.lon_rho[0, :], grid.lon_rho[-1, :], grid.lon_rho[:, 0], grid.lon_rho[:, -1]]) grid_lat = np.concatenate([grid.lat_rho[0, :], grid.lat_rho[-1, :], grid.lat_rho[:, 0], grid.lat_rho[:, -1]]) grid_mask = np.concatenate([grid.mask_rho[0, :], grid.mask_rho[-1, :], grid.mask_rho[:, 0], grid.mask_rho[:, -1]]) grid_angle = np.concatenate([grid.angle[0, :], grid.angle[-1, :], grid.angle[:, 0], grid.angle[:, -1]]) # Search for bad stations due to child grid overlaying parent mask. # Unfortunately, ROMS will give points that are not at the locations # you specify if those points conflict with the mask. So, these points # are simply replaced with the nearest. dist = np.sqrt((sta_lon - grid_lon)**2 + (sta_lat - grid_lat)**2) bad_pts = np.where(np.logical_and(dist > 0.001, grid_mask == 1))[0] good_pts = np.where(np.logical_and(dist < 0.001, grid_mask == 1))[0] for i in bad_pts: didx = np.sqrt((sta_lon[i] - sta_lon[good_pts])**2 + (sta_lat[i] - sta_lat[good_pts])**2).argmin() index = good_pts[didx] sta_h[i] = sta_h[index] sta_angle[i] = sta_angle[index] sta_lon[i] = sta_lon[index] sta_lat[i] = sta_lat[index] sta_zeta[:, i] = sta_zeta[:, index] sta_ubar[:, i] = sta_ubar[:, index] sta_vbar[:, i] = sta_vbar[:, index] sta_temp[:, i, :] = sta_temp[:, index, :] sta_salt[:, i, :] = sta_salt[:, index, :] sta_u[:, i, :] = sta_u[:, index, :] sta_v[:, i, :] = sta_v[:, index, :] # Construct the boundaries: a dictionary of boundary side and two element # array whether the u[0] or v[1] dimensions need to be averaged sides = {"north": [True, False], "south": [True, False], "east": [False, True], "west": [False, True]} delta_angle = sta_angle - grid_angle sta_ubar, sta_vbar = seapy.rotate(sta_ubar, sta_vbar, delta_angle) sta_u, sta_v = seapy.rotate(sta_u, sta_v, np.tile(delta_angle, (sta_u.shape[-1], 1)).T) # Set up the parameters for depth-interpolated wght = 5 nx = 3 ny = 9 # Build a non-extrapolating field to interpolate. Generate the # position and depth def __expand_field(x): shp = x.shape y = np.zeros((shp[0] + 2, shp[1] + 2)) y[1:-1, 1:-1] = x y[1:-1, 0] = x[:, 0] y[1:-1, -1] = x[:, -1] y[0, :] = y[1, :] y[-1, :] = y[-2, :] return y for side in sides: print(side) # Masks sta_ocean = np.where(sta_mask[bry[side]] == 1)[0] ocean = np.where(grid_mask[bry[side]] == 1)[0] # If we have a masked boundary, skip it if not np.any(ocean): continue # 1) Zeta ncbry.variables["zeta_" + side][:, ocean] = sta_zeta[:, bry[side]][:, ocean] # 2) Ubar if sides[side][0]: ncbry.variables["ubar_" + side][:] = 0.5 * ( sta_ubar[:, bry[side][0:-1]] + sta_ubar[:, bry[side][1:]]) else: ncbry.variables["ubar_" + side][:] = sta_ubar[:, bry[side]] # 3) Vbar if sides[side][1]: ncbry.variables["vbar_" + side][:] = 0.5 * ( sta_vbar[:, bry[side][0:-1]] + sta_vbar[:, bry[side][1:]]) else: ncbry.variables["vbar_" + side][:] = sta_vbar[:, bry[side]] # For 3D variables, we need to loop through time and interpolate # onto the child grid. Construct the distances x = np.zeros(len(bry[side])) x[1:] = np.cumsum(seapy.earth_distance(grid_lon[bry[side][0:-1]], grid_lat[bry[side][0:-1]], grid_lon[bry[side][1:]], grid_lat[bry[side][1:]])) sta_x = seapy.adddim(x, len(sta_s_rho)) x = seapy.adddim(x, len(grid.s_rho)) for n, t in seapy.progressbar.progress(enumerate(statime), statime.size): sta_depth = seapy.roms.depth(sta_vt, sta_h[bry[side]], sta_hc, sta_s_rho, sta_cs_r, sta_zeta[n, bry[side]]) depth = seapy.roms.depth(grid.vtransform, grid_h[bry[side]], grid.hc, grid.s_rho, grid.cs_r, sta_zeta[n, bry[side]]) in_x = __expand_field(sta_x[:, sta_ocean]) in_x[:, 0] = in_x[:, 0] - 3600 in_x[:, -1] = in_x[:, -1] + 3600 in_depth = __expand_field(sta_depth[:, sta_ocean]) in_depth[0, :] = in_depth[0, :] - 1000 in_depth[-1, :] = in_depth[-1, :] + 10 # 4) Temp in_data = __expand_field(np.transpose( sta_temp[n, bry[side], :][sta_ocean, :])) ncbry.variables["temp_" + side][n, :] = 0.0 ncbry.variables["temp_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], nx=nx, ny=ny, weight=wght) # 5) Salt in_data = __expand_field(np.transpose( sta_salt[n, bry[side], :][sta_ocean, :])) ncbry.variables["salt_" + side][n, :] = 0.0 ncbry.variables["salt_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) # 6) U in_data = __expand_field(np.transpose( sta_u[n, bry[side], :][sta_ocean, :])) data = np.zeros(x.shape) data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][0]: ncbry.variables["u_" + side][n, :] = 0.5 * ( data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["u_" + side][n, :] = data # 7) V in_data = __expand_field(np.transpose( sta_v[n, bry[side], :][sta_ocean, :])) data = data * 0 data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][1]: ncbry.variables["v_" + side][n, :] = 0.5 * ( data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["v_" + side][n, :] = data ncbry.sync() ncbry.close() pass
def from_stations(station_file, bry_file, grid=None): """ Construct a boundary forcing file from a stations file generated by a parent-grid. The stations.in file must have been generated by the seapy.roms.gen_stations method; otherwise, the order will be incorrect. Parameters ========== station_file : string Filename of the stations file that is the source for the boundary data bry_file : string Filename of the boundary conditions file to generate grid : string or seapy.model.grid Grid that the boundary conditions are created for Returns ------- None """ grid = seapy.model.asgrid(grid) ncstation = netCDF4.Dataset(station_file) src_ref, time = seapy.roms.get_reftime(ncstation) # Create the boundary file and fill up the descriptive data ncbry = seapy.roms.ncgen.create_bry(bry_file, eta_rho=grid.eta_rho, xi_rho=grid.xi_rho, s_rho=grid.n, reftime=src_ref, clobber=False, title="generated from " + station_file) grid.to_netcdf(ncbry) # Load the times: we need to see if the times are duplicated # because if using assimilation, they may be duplicated for every # outer-loop. Currently, it overwrites the first one each time (this # will need to be fixed if ROMS is fixed). statime = ncstation.variables[time][:] dup = np.where(statime[1:] == statime[0])[0] rng = np.s_[:] if dup.size > 0: rng = np.s_[0:np.min(dup)] statime = statime[rng] brytime = seapy.roms.get_timevar(ncbry) ncbry.variables[brytime][:] = seapy.roms.date2num( seapy.roms.num2date(ncstation, time, rng), ncbry, brytime) # Set up the indices bry = { "south": range(0, grid.lm), "north": range(grid.lm, 2 * grid.lm), "west": range(2 * grid.lm, 2 * grid.lm + grid.ln), "east": range(2 * grid.lm + grid.ln, 2 * (grid.lm + grid.ln)) } # Get the information to construct the depths of the station data sta_vt = ncstation.variables["Vtransform"][:] sta_hc = ncstation.variables["hc"][:] sta_s_rho = ncstation.variables["s_rho"][:] sta_cs_r = ncstation.variables["Cs_r"][:] sta_h = ncstation.variables["h"][:] sta_angle = ncstation.variables["angle"][:] sta_lon = ncstation.variables["lon_rho"][:] sta_lat = ncstation.variables["lat_rho"][:] sta_mask = np.ones(sta_lat.shape) sta_mask[sta_lon * sta_lat > 1e10] = 0 # Load the station data as we need to manipulate it sta_zeta = np.ma.masked_greater(ncstation.variables["zeta"][rng], 100) sta_ubar = np.ma.masked_greater(ncstation.variables["ubar"][rng], 100) sta_vbar = np.ma.masked_greater(ncstation.variables["vbar"][rng], 100) sta_temp = np.ma.masked_greater(ncstation.variables["temp"][rng], 100) sta_salt = np.ma.masked_greater(ncstation.variables["salt"][rng], 100) sta_u = np.ma.masked_greater(ncstation.variables["u"][rng], 100) sta_v = np.ma.masked_greater(ncstation.variables["v"][rng], 100) ncstation.close() # Create the true positions and mask grid_h = np.concatenate( [grid.h[0, :], grid.h[-1, :], grid.h[:, 0], grid.h[:, -1]]) grid_lon = np.concatenate([ grid.lon_rho[0, :], grid.lon_rho[-1, :], grid.lon_rho[:, 0], grid.lon_rho[:, -1] ]) grid_lat = np.concatenate([ grid.lat_rho[0, :], grid.lat_rho[-1, :], grid.lat_rho[:, 0], grid.lat_rho[:, -1] ]) grid_mask = np.concatenate([ grid.mask_rho[0, :], grid.mask_rho[-1, :], grid.mask_rho[:, 0], grid.mask_rho[:, -1] ]) grid_angle = np.concatenate([ grid.angle[0, :], grid.angle[-1, :], grid.angle[:, 0], grid.angle[:, -1] ]) # Search for bad stations due to child grid overlaying parent mask. # Unfortunately, ROMS will give points that are not at the locations # you specify if those points conflict with the mask. So, these points # are simply replaced with the nearest. dist = np.sqrt((sta_lon - grid_lon)**2 + (sta_lat - grid_lat)**2) bad_pts = np.where(np.logical_and(dist > 0.001, grid_mask == 1))[0] good_pts = np.where(np.logical_and(dist < 0.001, grid_mask == 1))[0] for i in bad_pts: didx = np.sqrt((sta_lon[i] - sta_lon[good_pts])**2 + (sta_lat[i] - sta_lat[good_pts])**2).argmin() index = good_pts[didx] sta_h[i] = sta_h[index] sta_angle[i] = sta_angle[index] sta_lon[i] = sta_lon[index] sta_lat[i] = sta_lat[index] sta_zeta[:, i] = sta_zeta[:, index] sta_ubar[:, i] = sta_ubar[:, index] sta_vbar[:, i] = sta_vbar[:, index] sta_temp[:, i, :] = sta_temp[:, index, :] sta_salt[:, i, :] = sta_salt[:, index, :] sta_u[:, i, :] = sta_u[:, index, :] sta_v[:, i, :] = sta_v[:, index, :] # Construct the boundaries: a dictionary of boundary side and two element # array whether the u[0] or v[1] dimensions need to be averaged sides = { "north": [True, False], "south": [True, False], "east": [False, True], "west": [False, True] } delta_angle = sta_angle - grid_angle sta_ubar, sta_vbar = seapy.rotate(sta_ubar, sta_vbar, delta_angle) sta_u, sta_v = seapy.rotate(sta_u, sta_v, np.tile(delta_angle, (sta_u.shape[-1], 1)).T) # Set up the parameters for depth-interpolated wght = 5 nx = 3 ny = 9 # Build a non-extrapolating field to interpolate. Generate the # position and depth def __expand_field(x): shp = x.shape y = np.zeros((shp[0] + 2, shp[1] + 2)) y[1:-1, 1:-1] = x y[1:-1, 0] = x[:, 0] y[1:-1, -1] = x[:, -1] y[0, :] = y[1, :] y[-1, :] = y[-2, :] return y for side in sides: print(side) # Masks sta_ocean = np.where(sta_mask[bry[side]] == 1)[0] ocean = np.where(grid_mask[bry[side]] == 1)[0] # If we have a masked boundary, skip it if not np.any(ocean): continue # 1) Zeta ncbry.variables["zeta_" + side][:, ocean] = sta_zeta[:, bry[side]][:, ocean] # 2) Ubar if sides[side][0]: ncbry.variables["ubar_" + side][:] = 0.5 * (sta_ubar[:, bry[side][0:-1]] + sta_ubar[:, bry[side][1:]]) else: ncbry.variables["ubar_" + side][:] = sta_ubar[:, bry[side]] # 3) Vbar if sides[side][1]: ncbry.variables["vbar_" + side][:] = 0.5 * (sta_vbar[:, bry[side][0:-1]] + sta_vbar[:, bry[side][1:]]) else: ncbry.variables["vbar_" + side][:] = sta_vbar[:, bry[side]] # For 3D variables, we need to loop through time and interpolate # onto the child grid. Construct the distances x = np.zeros(len(bry[side])) x[1:] = np.cumsum( seapy.earth_distance(grid_lon[bry[side][0:-1]], grid_lat[bry[side][0:-1]], grid_lon[bry[side][1:]], grid_lat[bry[side][1:]])) sta_x = seapy.adddim(x, len(sta_s_rho)) x = seapy.adddim(x, len(grid.s_rho)) for n, t in seapy.progressbar.progress(enumerate(statime), statime.size): sta_depth = seapy.roms.depth(sta_vt, sta_h[bry[side]], sta_hc, sta_s_rho, sta_cs_r, sta_zeta[n, bry[side]]) depth = seapy.roms.depth(grid.vtransform, grid_h[bry[side]], grid.hc, grid.s_rho, grid.cs_r, sta_zeta[n, bry[side]]) in_x = __expand_field(sta_x[:, sta_ocean]) in_x[:, 0] = in_x[:, 0] - 3600 in_x[:, -1] = in_x[:, -1] + 3600 in_depth = __expand_field(sta_depth[:, sta_ocean]) in_depth[0, :] = in_depth[0, :] - 1000 in_depth[-1, :] = in_depth[-1, :] + 10 # 4) Temp in_data = __expand_field( np.transpose(sta_temp[n, bry[side], :][sta_ocean, :])) ncbry.variables["temp_" + side][n, :] = 0.0 ncbry.variables["temp_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], nx=nx, ny=ny, weight=wght) # 5) Salt in_data = __expand_field( np.transpose(sta_salt[n, bry[side], :][sta_ocean, :])) ncbry.variables["salt_" + side][n, :] = 0.0 ncbry.variables["salt_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) # 6) U in_data = __expand_field( np.transpose(sta_u[n, bry[side], :][sta_ocean, :])) data = np.zeros(x.shape) data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][0]: ncbry.variables["u_" + side][n, :] = 0.5 * (data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["u_" + side][n, :] = data # 7) V in_data = __expand_field( np.transpose(sta_v[n, bry[side], :][sta_ocean, :])) data = data * 0 data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][1]: ncbry.variables["v_" + side][n, :] = 0.5 * (data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["v_" + side][n, :] = data ncbry.sync() ncbry.close() pass
def plot_depths(self, row=None, col=None, ax=None): """ Plot the depths of a model grid along a row or column transect. If the bathymetry is known, it is plotted also. Parameters ---------- row : int, optional The row number to plot col : int, optional The column number to plot ax : matplotlib.axes, optional The axes to use for the figure Returns ------- ax : matplotlib.axes The axes containing the plot """ import matplotlib.pyplot as plt # Create the axes if we don't have any if not ax: fig = plt.figure() ax = fig.add_subplot(111) # ax.set_bg_color('darkseagreen') # Get the data if row: sz = np.s_[:, row, :] s = np.s_[row, :] x = self.lon_rho[s] label = "Longitude" elif col: sz = np.s_[:, :, col] s = np.s_[:, col] x = self.lat_rho[s] label = "Latitude" else: warn("You must specify a row or column") return # If it is ROMS, we should plot the top and bottom of the cells if self._isroms: sr, csr = seapy.roms.stretching( self.vstretching, self.theta_s, self.theta_b, self.hc, self.n, w_grid=True) dep = np.ma.masked_where(seapy.adddim(self.mask_rho[s], self.n + 1) == 0, seapy.roms.depth(self.vtransform, self.h[s], self.hc, sr, csr, w_grid=True)) else: dep = np.ma.masked_where(seapy.adddim(self.mask_rho[s], self.n) == 0, self.depth_rho[sz]) h = -self.h[s] # Begin with the bathymetric data ax.fill_between(x, h, np.min(h), facecolor="darkseagreen", interpolate=True) # Plot the layers ax.plot(x, dep.T, color="grey") # Labels ax.set_xlabel(label + " [deg]") ax.set_ylabel("Depth [m]") # Make it tight plt.autoscale(ax, tight=True) return ax