Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
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)