示例#1
0
    def convert_file(self, file, title="SeaGlider Obs"):
        """
        Load a SeaGlider .pro file and convert into an obs structure
        """
        import re

        # Load the text file. All data goes into the pro dictionary
        # as defined by dtype. The header information needs to be parsed
        with open(file) as myfile:
            header = [myfile.readline() for i in range(19)]
            pro = np.loadtxt(myfile, self.dtype, delimiter=',', comments='%')

        # Parse the header information
        parser = re.compile('^%(\w+): (.*)$')
        params = {}
        for line in header:
            try:
                opt = parser.findall(line)
                params[opt[0][0]] = opt[0][1]
            except:
                pass

        # Determine the needed information from the headers
        glider_name = "GLIDER" if params.get("glider", None) is None else \
                      "GLIDER_SG" + params["glider"]
        provenance = seapy.roms.obs.asprovenance(glider_name)
        try:
            date = [int(s) for s in re.findall('([\d]{2})\s', params["start"])]
            start_time = datetime.datetime.strptime(params["start"].strip(),
                                                    "%m %d 1%y %H %M %S")
            dtime = (start_time - self.epoch).total_seconds() / 86400
        except:
            raise ValueError("date format incorrect in file: " + file)

        # Make sure that the GPS fix isn't screwy
        if self.grid.east():
            pro["lon"][pro["lon"] < 0] += 360
        dist = seapy.earth_distance(pro["lon"][0], pro["lat"][0],
                                    pro["lon"][-1], pro["lat"][-1])
        velocity = dist / pro["time"][-1]
        if velocity > 2:
            warn("WARNING: GPS fix is incorrect for " + file)
            return None

        # Build the data with masked entries
        temp = np.ma.masked_outside(pro["temp"], self.temp_limits[0],
                                    self.temp_limits[1])
        salt = np.ma.masked_outside(pro["salt"], self.salt_limits[0],
                                    self.salt_limits[1])
        depth = np.ma.masked_greater(-pro["depth"], self.depth_limit)
        good = ~np.ma.getmaskarray(depth)

        # Grid it
        data = [seapy.roms.obs.raw_data("TEMP", provenance, temp[good],
                                        None, self.temp_error),
                seapy.roms.obs.raw_data("SALT", provenance, salt[good],
                                        None, self.salt_error)]
        return seapy.roms.obs.gridder(self.grid, pro["time"][good] / 86400 + dtime,
                                      pro["lon"][good],
                                      pro["lat"][good],
                                      depth.compressed(),
                                      data, self.dt, title)
示例#2
0
文件: ezgrid.py 项目: ivicajan/seapy
def calc_latlon(llcrnrlat, llcrnrlon, reseta, resxi=None, rotate=0):
    """
    Generate arrays for latitude and longitude for use in
    creating simple grids.

    NOTE: You can specify variational resolutions and rotations;
    however, this uses a simple algorithm to step along from the
    origin, and discrepencies are averaged together. THEREFORE,
    if you are not specifying inconsistent resolutions or
    rotations within single row or columns, you may get results
    slightly different than specified.

    Parameters
    ----------
    llcrnrlat : float,
      Latitude for the lower, left of the grid
    llcrnrlon : float,
      Longitude for the lower, left of the grid
    reseta : ndarray,
      Resolution in meters in the eta-direction of each grid cell.
      A 2D array that is the horizontal size of the grid (e.g.,
      resolution.shape = (100,70)) and the values stored are
      the desired resolution of the grid cell in m. Hence,
      a 100 x 70 array of ones would create a grid that is
      100 in the eta-direction, 70 in the xi-direction, and
      a constant resolution of 1km. If the lat and lon is
      specified as arrays, this is optional.
    resxi : ndarray, optional,
      Resolution in meters in the xi-direction of each grid cell.
      This is the same as reseta; however, it is optional, and
      is set to the same as reseta if not specified.
    rotate : float or ndarray,
      Amount to rotate the grid in degrees. If given as a scalar,
      the entire grid is rotated at the same angle; otherwise, the
      grid my have curvilinear shape. The angle is geometric
      (counter-clockwise).

    Returns
    -------
    lat, lon : ndarray
       Two arrays of size resolution containing the computed lat
       and lons

    Examples
    --------
    Create a grid of 1km resolution in both eta and xi, 
    rotated toward the SE by 33 degrees, with the lower left
    point at 20N, 150E:

    >>> res = np.ones((100,70)) * 1000
    >>> lat, lon = calc_latlon(20, 150, res, rotate=-33)
    """

    # Set up the resolutions
    if resxi is None:
        resxi = reseta
    else:
        if resxi.shape != reseta.shape:
            raise AttributeError(
                "xi and eta resolutions must be same size array")
    # Since we are taking steps, average the values together as we
    # can't take the final step
    reseta[:-1, :] = 0.5 * (reseta[:-1, :] + reseta[1:, :])
    resxi[:, :-1] = 0.5 * (resxi[:, :-1] + resxi[:, 1:])

    # Set up the rotations
    if np.isscalar(rotate):
        rotate = np.ones(reseta.shape) * rotate
    rotate = np.radians(rotate)

    # Set up the lat and lon arrays
    lat = np.ones(reseta.shape) * np.nan
    lon = np.ones(reseta.shape) * np.nan
    lat[0, 0] = llcrnrlat
    lon[0, 0] = llcrnrlon
    pi2 = np.pi / 2.0

    # Loop over each row, and within the row, step along in the
    # xi-direction before moving to the next row
    for j in seapy.progressbar.progress(range(lat.shape[0] - 1)):
        for i in range(lat.shape[1] - 1):
            # Compute the local deltas
            dlat = seapy.earth_distance(
                lon[j, i], lat[j, i] - 0.5, lon[j, i], lat[j, i] + 0.5)
            dlon = seapy.earth_distance(
                lon[j, i] - 0.5, lat[j, i], lon[j, i] + 0.5, lat[j, i])

            # Compute how far to step in xi
            xdx = resxi[j, i + 1] * np.cos(rotate[j, i]) / dlon
            xdy = resxi[j, i + 1] * np.sin(rotate[j, i]) / dlat

            # Take the step
            lat[j, i + 1] = lat[j, i] + xdy
            lon[j, i + 1] = lon[j, i] + xdx

            # Compute how far to step in eta
            edx = reseta[j + 1, i] * np.cos(rotate[j, i] + pi2) / dlon
            edy = reseta[j + 1, i] * np.sin(rotate[j, i] + pi2) / dlat

            # Take the step
            lat[j + 1, i] = lat[j, i] + edy
            lon[j + 1, i] = lon[j, i] + edx

    lon[-1, -1] = lon[-1, -2] + xdx
    lat[-1, -1] = lat[-2, -1] + edy

    return lat, lon
示例#3
0
def transect(lon, lat, depth, data, nx=200, nz=40, z=None):
    """
    Generate an equidistant transect from data at varying depths. Can be
    used to plot a slice of model or observational data.

    Parameters
    ----------
    lat: array
        n-dimensional array with latitude of points
    lon: array
        n-dimensional array with longitude of points
    depth: array
        [k,n] dimensional array of the depths for all k-layers at each n point
    data: array
        [k,n] dimensional array of values
    nx: int, optional
        number of horizontal points desired in the transect
    nz: int, optional
        number of vertical points desired in the transect
    z: array, optional
        list of depths to use if you do not want equidistant depths

    Returns
    -------
    x: array
        x-location values in [m] along transect
    z: array
        depth values in [m] of the new transect
    vals: np.ma.array
        data values of the new transect with masked values

    Examples
    --------
    Generate series of transects from ROMS output

    >>> nc = seapy.netcdf('roms_his.nc')
    >>> grid = seapy.model.asgrid(nc)
    >>> data = nc.variables['salt'][:,:,150,:]
    >>> shp = (data.shape[0], 50, 400)
    >>> transect = np.zeros(shp)
    >>> for i in range(shp[0]):
    >>>     x, z, d = \
    >>>          seapy.roms.analysis.transect(grid.lon_rho[150,:],
    >>>                                       grid.lat_rho[150,:],
    >>>                                       grid.depth_rho[:,150,:],
    >>>                                       data[i,:,:], nx=shp[2],
    >>>                                       nz=shp[1])
    >>>     transect[i,:,:] = d.filled(np.nan)
    >>> nc.close()
    >>> plt.pcolormesh(x/1000, z, transect[0, :, :])
    """
    from scipy.interpolate import griddata
    depth = np.atleast_2d(depth)
    data = np.ma.atleast_2d(data).filled(np.mean(data))
    lon = np.atleast_1d(lon)
    lat = np.atleast_1d(lat)

    # Generate the depths
    depth[depth > 0] *= -1
    if z is None:
        z = np.linspace(depth.min() - 2, depth.max(), nz)
    else:
        z[z > 0] *= -1
        nz = len(z)
    dz = np.abs(np.diff(z).mean())

    # Determine the distance between points and the weighting to apply
    dist = np.hstack(
        ([0], seapy.earth_distance(lon[0], lat[0], lon[1:], lat[1:])))
    dx = np.diff(dist).mean()
    zscale = np.maximum(1, 10**int(np.log10(dx / dz)))
    dx /= zscale
    x = np.linspace(0, dist.max(), nx)

    # All arrays have to be the same size
    xx, zz = np.meshgrid(x / zscale, z)

    # For the source data, we don't want to extrpolate,
    # so make the data go from the surface to twice its
    # depth.
    zl = np.argsort(depth[:, 0])
    dep = np.vstack((np.ones(
        (1, depth.shape[1])) * 2 * depth.min(), depth[zl, :],
                     np.zeros((1, depth.shape[1]))))

    # repeat the same data at the top and bottom
    dat = np.vstack((data[zl[0], :], data[zl], data[zl[-1], :]))
    dist = np.tile(dist.T, [dep.shape[0], 1]) / zscale

    # Find the bottom indices to create a mask for nodata/land
    idx = np.interp(xx[0, :], dist[0, :],
                    np.interp(depth.min(axis=0), z, np.arange(nz))).astype(int)
    mask = np.arange(nz)[:, np.newaxis] <= idx

    # Interpolate
    ndat = np.ma.array(griddata((dist.ravel(), dep.ravel()),
                                dat.ravel(), (xx.ravel(), zz.ravel()),
                                method='cubic').reshape(xx.shape),
                       mask=mask)

    # Return everything
    return x, z, ndat
示例#4
0
文件: grid.py 项目: ivicajan/seapy
    def set_dims(self):
        """
        Compute the dimension attributes of the grid based upon the information provided.

        Parameters
        ----------
        None

        Returns
        -------
        None : sets attributes in grid
        """
        # If C-Grid, set the dimensions for consistency
        if self.cgrid:
            self.eta_rho = self.ln
            self.eta_u = self.ln
            self.eta_v = self.ln - 1
            self.xi_rho = self.lm
            self.xi_u = self.lm - 1
            self.xi_v = self.lm

        # Set the number of layers
        if "n" not in self.__dict__:
            if "s_rho" in self.__dict__:
                self.n = int(self.s_rho.size)
            elif "z" in self.__dict__:
                self.n = int(self.z.size)
            else:
                self.n = 1
                self.z = np.zeros(self.lat_rho.shape)
        else:
            self.n = int(self.n)

        # Generate the u- and v-grids
        if ("lat_u" or "lon_u") not in self.__dict__:
            if self.cgrid:
                self.lat_u = 0.5 * \
                    (self.lat_rho[:, 1:] - self.lat_rho[:, 0:-1])
                self.lon_u = 0.5 * \
                    (self.lon_rho[:, 1:] - self.lon_rho[:, 0:-1])
            else:
                self.lat_u = self.lat_rho
                self.lon_u = self.lon_rho
        if ("lat_v" or "lon_v") not in self.__dict__:
            if self.cgrid:
                self.lat_v = 0.5 * \
                    (self.lat_rho[1:, :] - self.lat_rho[0:-1, :])
                self.lon_v = 0.5 * \
                    (self.lon_rho[1:, :] - self.lon_rho[0:-1, :])
            else:
                self.lat_v = self.lat_rho
                self.lon_v = self.lon_rho
        if "mask_rho" in self.__dict__:
            if "mask_u" not in self.__dict__:
                if self.cgrid:
                    self.mask_u = self.mask_rho[:, 1:] * self.mask_rho[:, 0:-1]
                else:
                    self.mask_u = self.mask_rho
            if "mask_v" not in self.__dict__:
                if self.cgrid:
                    self.mask_v = self.mask_rho[1:, :] * self.mask_rho[0:-1, :]
                else:
                    self.mask_v = self.mask_rho

        # Compute the resolution
        if "pm" in self.__dict__:
            self.dm = 1.0 / self.pm
        else:
            self.dm = np.ones(self.lon_rho.shape, dtype=np.float32)
            self.dm[:, 0:-1] = seapy.earth_distance(
                self.lon_rho[:, 1:], self.lat_rho[:, 1:],
                self.lon_rho[:, 0:-1], self.lat_rho[:,
                                                    0:-1]).astype(np.float32)
            self.dm[:, -1] = self.dm[:, -2]
            self.pm = 1.0 / self.dm
        if "pn" in self.__dict__:
            self.dn = 1.0 / self.pn
        else:
            self.dn = np.ones(self.lat_rho.shape, dtype=np.float32)
            self.dn[0:-1, :] = seapy.earth_distance(
                self.lon_rho[1:, :], self.lat_rho[1:, :],
                self.lon_rho[0:-1, :],
                self.lat_rho[0:-1, :]).astype(np.float32)
            self.dn[-1, :] = self.dn[-2, :]
            self.pn = 1.0 / self.dn

        # Compute the Coriolis
        if "f" not in self.__dict__:
            omega = 2 * np.pi * seapy.secs2day
            self.f = 2 * omega * np.sin(np.radians(self.lat_rho))

        # Set the grid index coordinates
        self.I, self.J = np.meshgrid(np.arange(0, self.lm),
                                     np.arange(0, self.ln))
示例#5
0
文件: ezgrid.py 项目: ivicajan/seapy
def create_grid(grid_file, lat, lon):
    """
    Create a new, basic grid. This will construct the ROMS grid netcdf file
    given a set of latitude and longitude coordinates for the rho-grid.
    The coordinates can be spaced however specified, and the grid will be
    created; however, this does not guarantee the grid will be valid.
    After the grid is created, the bathymetry and mask will need to be
    generated independently.

    Parameters
    ----------
    grid_file : string,
      Name of the grid file to create. NOTE: this file will be
      overwritten.
    lat : ndarray,
      latitude of the grid cells. The array must be the size
      of the desired grid.
    lon : ndarray,
      longitude of the grid cells. The array must be the size
      of the desired grid.

    Returns
    -------
    netCDF4 : 
      The netcdf object of the new grid

    Examples
    --------
    To create a basic, evenly spaced grid:

    >>> lat = np.linspace(10,20,0.25)
    >>> lon = np.linspace(-100,-80,0.25)
    >>> lon, lat = np.meshgrid(lon, lat)
    >>> create_grid('mygrid.nc', lat, lon)

    To create more advanced grids, simply generate the
    2D arrays of lat and lon in the manner you want your cells
    and call create_grid:

    >>> create_grid('mygrid.nc', lat, lon)

    """

    # Put lat/lon into proper arrays
    lat = np.atleast_2d(lat)
    lon = np.atleast_2d(lon)

    if lat.shape != lon.shape:
        raise AttributeError("lat and lon shapes are not equal")

    # Calculate the angle between the points
    angle = np.zeros(lat.shape)
    angle[:, :-1] = seapy.earth_angle(lon[:, :-1],
                                      lat[:, :-1], lon[:, 1:], lat[:, 1:])
    angle[:, -1] = angle[:, -2]

    # Calculate distances/parameters
    f = 2.0 * 7.2921150e-5 * np.sin(lat * np.pi / 180.0)
    dx = np.zeros(f.shape)
    dy = np.zeros(f.shape)
    dx[:, 1:] = seapy.earth_distance(
        lon[:, 1:], lat[:, 1:], lon[:, :-1], lat[:, :-1])
    dy[1:, :] = seapy.earth_distance(
        lon[1:, :], lat[1:, :], lon[:-1, :], lat[:-1, :])
    dx[:, 0] = dx[:, 1]
    dy[0, :] = dy[1, :]
    pm = 1.0 / dx
    pn = 1.0 / dy
    dndx = np.zeros(dx.shape)
    dmde = np.zeros(dx.shape)
    dndx[:, 1:-1] = 0.5 * (dy[:, 2:] - dy[:, :-2])
    dmde[1:-1, :] = 0.5 * (dx[2:, :] - dx[:-2, :])
    xl = seapy.earth_distance(
        np.min(lon), np.mean(lat), np.max(lon), np.mean(lat))
    el = seapy.earth_distance(
        np.mean(lon), np.min(lat), np.mean(lon), np.max(lat))

    # Generate rho-grid coordinates
    x_rho = np.zeros(lat.shape)
    y_rho = np.zeros(lat.shape)
    x_rho[:, 1:] = seapy.earth_distance(
        lon[:, :-1], lat[:, :-1], lon[:, 1:], lat[:, 1:])
    x_rho = np.cumsum(x_rho, axis=1)
    y_rho[1:, :] = seapy.earth_distance(
        lon[:-1, :], lat[:-1, :], lon[1:, :], lat[1:, :])
    y_rho = np.cumsum(y_rho, axis=0)

    # Create u-grid
    lat_u = 0.5 * (lat[:, 1:] + lat[:, :-1])
    lon_u = 0.5 * (lon[:, 1:] + lon[:, 0:-1])
    x_u = np.zeros(lat_u.shape)
    y_u = np.zeros(lat_u.shape)
    x_u[:, 1:] = seapy.earth_distance(
        lon_u[:, :-1], lat_u[:, :-1], lon_u[:, 1:], lat_u[:, 1:])
    x_u = np.cumsum(x_u, axis=1)
    y_u[1:, :] = seapy.earth_distance(
        lon_u[:-1, :], lat_u[:-1, :], lon_u[1:, :], lat_u[1:, :])
    y_u = np.cumsum(y_u, axis=0)

    # Create v-grid
    lat_v = 0.5 * (lat[1:, :] + lat[0:-1, :])
    lon_v = 0.5 * (lon[1:, :] + lon[0:-1, :])
    x_v = np.zeros(lat_v.shape)
    y_v = np.zeros(lat_v.shape)
    x_v[:, 1:] = seapy.earth_distance(
        lon_v[:, :-1], lat_v[:, :-1], lon_v[:, 1:], lat_v[:, 1:])
    x_v = np.cumsum(x_v, axis=1)
    y_v[1:, :] = seapy.earth_distance(
        lon_v[:-1, :], lat_v[:-1, :], lon_v[1:, :], lat_v[1:, :])
    y_v = np.cumsum(y_v, axis=0)

    # Create psi-grid
    lat_psi = lat_v[:, :-1]
    lon_psi = lon_u[:-1, :]
    x_psi = np.zeros(lat_psi.shape)
    y_psi = np.zeros(lat_psi.shape)
    x_psi[:, 1:] = seapy.earth_distance(
        lon_psi[:, :-1], lat_psi[:, :-1], lon_psi[:, 1:], lat_psi[:, 1:])
    x_psi = np.cumsum(x_psi, axis=1)
    y_psi[1:, :] = seapy.earth_distance(
        lon_psi[:-1, :], lat_psi[:-1, :], lon_psi[1:, :], lat_psi[1:, :])
    y_psi = np.cumsum(y_psi, axis=0)

    # Create the new grid
    nc = seapy.roms.ncgen.create_grid(
        grid_file, lat.shape[0], lat.shape[1], clobber=True)

    nc.variables["xl"][:] = xl
    nc.variables["el"][:] = el
    nc.variables["spherical"][:] = 1
    nc.variables["f"][:] = f
    nc.variables["pm"][:] = pm
    nc.variables["pn"][:] = pn
    nc.variables["dndx"][:] = dndx
    nc.variables["dmde"][:] = dmde
    nc.variables["x_rho"][:] = x_rho
    nc.variables["y_rho"][:] = y_rho
    nc.variables["x_psi"][:] = x_psi
    nc.variables["y_psi"][:] = y_psi
    nc.variables["x_u"][:] = x_u
    nc.variables["y_u"][:] = y_u
    nc.variables["x_v"][:] = x_v
    nc.variables["y_v"][:] = y_v
    nc.variables["lat_rho"][:] = lat
    nc.variables["lon_rho"][:] = lon
    nc.variables["lat_psi"][:] = lat_psi
    nc.variables["lon_psi"][:] = lon_psi
    nc.variables["lat_u"][:] = lat_u
    nc.variables["lon_u"][:] = lon_u
    nc.variables["lat_v"][:] = lat_v
    nc.variables["lon_v"][:] = lon_v
    nc.variables["N"][:] = 1
    nc.variables["mask_rho"][:] = np.ones(lat.shape)
    nc.variables["mask_u"][:] = np.ones(lat_u.shape)
    nc.variables["mask_v"][:] = np.ones(lat_v.shape)
    nc.variables["mask_psi"][:] = np.ones(lat_psi.shape)
    nc.variables["angle"][:] = angle
    nc.variables["rdrag"][:] = np.ones(lon.shape) * 0.0003
    nc.variables["rdrag2"][:] = np.ones(lon.shape) * 0.003
    nc.variables["ZoBot"][:] = np.ones(lon.shape) * 0.02
    nc.sync()

    return nc
示例#6
0
def calc_latlon(llcrnrlat, llcrnrlon, reseta, resxi=None, rotate=0):
    """
    Generate arrays for latitude and longitude for use in
    creating simple grids.

    NOTE: You can specify variational resolutions and rotations;
    however, this uses a simple algorithm to step along from the
    origin, and discrepencies are averaged together. THEREFORE,
    if you are not specifying inconsistent resolutions or
    rotations within single row or columns, you may get results
    slightly different than specified.

    Parameters
    ----------
    llcrnrlat : float,
      Latitude for the lower, left of the grid
    llcrnrlon : float,
      Longitude for the lower, left of the grid
    reseta : ndarray,
      Resolution in meters in the eta-direction of each grid cell.
      A 2D array that is the horizontal size of the grid (e.g.,
      resolution.shape = (100,70)) and the values stored are
      the desired resolution of the grid cell in m. Hence,
      a 100 x 70 array of ones would create a grid that is
      100 in the eta-direction, 70 in the xi-direction, and
      a constant resolution of 1km. If the lat and lon is
      specified as arrays, this is optional.
    resxi : ndarray, optional,
      Resolution in meters in the xi-direction of each grid cell.
      This is the same as reseta; however, it is optional, and
      is set to the same as reseta if not specified.
    rotate : float or ndarray,
      Amount to rotate the grid in degrees. If given as a scalar,
      the entire grid is rotated at the same angle; otherwise, the
      grid my have curvilinear shape. The angle is geometric
      (counter-clockwise).

    Returns
    -------
    lat, lon : ndarray
       Two arrays of size resolution containing the computed lat
       and lons

    Examples
    --------
    Create a grid of 1km resolution in both eta and xi,
    rotated toward the SE by 33 degrees, with the lower left
    point at 20N, 150E:

    >>> res = np.ones((100,70)) * 1000
    >>> lat, lon = calc_latlon(20, 150, res, rotate=-33)
    """

    # Set up the resolutions
    if resxi is None:
        resxi = reseta
    else:
        if resxi.shape != reseta.shape:
            raise AttributeError(
                "xi and eta resolutions must be same size array")
    # Since we are taking steps, average the values together as we
    # can't take the final step
    reseta[:-1, :] = 0.5 * (reseta[:-1, :] + reseta[1:, :])
    resxi[:, :-1] = 0.5 * (resxi[:, :-1] + resxi[:, 1:])

    # Set up the rotations
    if np.isscalar(rotate):
        rotate = np.ones(reseta.shape) * rotate
    rotate = np.radians(rotate)

    # Set up the lat and lon arrays
    lat = np.ones(reseta.shape) * np.nan
    lon = np.ones(reseta.shape) * np.nan
    lat[0, 0] = llcrnrlat
    lon[0, 0] = llcrnrlon
    pi2 = np.pi / 2.0

    # Loop over each row, and within the row, step along in the
    # xi-direction before moving to the next row
    for j in seapy.progressbar.progress(range(lat.shape[0] - 1)):
        for i in range(lat.shape[1] - 1):
            # Compute the local deltas
            dlat = seapy.earth_distance(lon[j, i], lat[j, i] - 0.5, lon[j, i],
                                        lat[j, i] + 0.5)
            dlon = seapy.earth_distance(lon[j, i] - 0.5, lat[j, i],
                                        lon[j, i] + 0.5, lat[j, i])

            # Compute how far to step in xi
            xdx = resxi[j, i + 1] * np.cos(rotate[j, i]) / dlon
            xdy = resxi[j, i + 1] * np.sin(rotate[j, i]) / dlat

            # Take the step
            lat[j, i + 1] = lat[j, i] + xdy
            lon[j, i + 1] = lon[j, i] + xdx

            # Compute how far to step in eta
            edx = reseta[j + 1, i] * np.cos(rotate[j, i] + pi2) / dlon
            edy = reseta[j + 1, i] * np.sin(rotate[j, i] + pi2) / dlat

            # Take the step
            lat[j + 1, i] = lat[j, i] + edy
            lon[j + 1, i] = lon[j, i] + edx

    lon[-1, -1] = lon[-1, -2] + xdx
    lat[-1, -1] = lat[-2, -1] + edy

    return lat, lon
示例#7
0
文件: boundary.py 项目: powellb/seapy
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
示例#8
0
def create_grid(grid_file, lat, lon):
    """
    Create a new, basic grid. This will construct the ROMS grid netcdf file
    given a set of latitude and longitude coordinates for the rho-grid.
    The coordinates can be spaced however specified, and the grid will be
    created; however, this does not guarantee the grid will be valid.
    After the grid is created, the bathymetry and mask will need to be
    generated independently.

    Parameters
    ----------
    grid_file : string,
      Name of the grid file to create. NOTE: this file will be
      overwritten.
    lat : ndarray,
      latitude of the grid cells. The array must be the size
      of the desired grid.
    lon : ndarray,
      longitude of the grid cells. The array must be the size
      of the desired grid.

    Returns
    -------
    netCDF4 :
      The netcdf object of the new grid

    Examples
    --------
    To create a basic, evenly spaced grid:

    >>> lat = np.linspace(10,20,0.25)
    >>> lon = np.linspace(-100,-80,0.25)
    >>> lon, lat = np.meshgrid(lon, lat)
    >>> create_grid('mygrid.nc', lat, lon)

    To create more advanced grids, simply generate the
    2D arrays of lat and lon in the manner you want your cells
    and call create_grid:

    >>> create_grid('mygrid.nc', lat, lon)

    """

    # Put lat/lon into proper arrays
    lat = np.atleast_2d(lat)
    lon = np.atleast_2d(lon)

    if lat.shape != lon.shape:
        raise AttributeError("lat and lon shapes are not equal")

    # Calculate the angle between the points
    angle = np.zeros(lat.shape)
    angle[:, :-1] = seapy.earth_angle(lon[:, :-1], lat[:, :-1], lon[:, 1:],
                                      lat[:, 1:])
    angle[:, -1] = angle[:, -2]

    # Calculate distances/parameters
    f = 2.0 * 7.2921150e-5 * np.sin(lat * np.pi / 180.0)
    dx = np.zeros(f.shape)
    dy = np.zeros(f.shape)
    dx[:, 1:] = seapy.earth_distance(lon[:, 1:], lat[:, 1:], lon[:, :-1],
                                     lat[:, :-1])
    dy[1:, :] = seapy.earth_distance(lon[1:, :], lat[1:, :], lon[:-1, :],
                                     lat[:-1, :])
    dx[:, 0] = dx[:, 1]
    dy[0, :] = dy[1, :]
    pm = 1.0 / dx
    pn = 1.0 / dy
    dndx = np.zeros(dx.shape)
    dmde = np.zeros(dx.shape)
    dndx[:, 1:-1] = 0.5 * (dy[:, 2:] - dy[:, :-2])
    dmde[1:-1, :] = 0.5 * (dx[2:, :] - dx[:-2, :])
    xl = seapy.earth_distance(np.min(lon), np.mean(lat), np.max(lon),
                              np.mean(lat))
    el = seapy.earth_distance(np.mean(lon), np.min(lat), np.mean(lon),
                              np.max(lat))

    # Generate rho-grid coordinates
    x_rho = np.zeros(lat.shape)
    y_rho = np.zeros(lat.shape)
    x_rho[:, 1:] = seapy.earth_distance(lon[:, :-1], lat[:, :-1], lon[:, 1:],
                                        lat[:, 1:])
    x_rho = np.cumsum(x_rho, axis=1)
    y_rho[1:, :] = seapy.earth_distance(lon[:-1, :], lat[:-1, :], lon[1:, :],
                                        lat[1:, :])
    y_rho = np.cumsum(y_rho, axis=0)

    # Create u-grid
    lat_u = 0.5 * (lat[:, 1:] + lat[:, :-1])
    lon_u = 0.5 * (lon[:, 1:] + lon[:, 0:-1])
    x_u = np.zeros(lat_u.shape)
    y_u = np.zeros(lat_u.shape)
    x_u[:, 1:] = seapy.earth_distance(lon_u[:, :-1], lat_u[:, :-1],
                                      lon_u[:, 1:], lat_u[:, 1:])
    x_u = np.cumsum(x_u, axis=1)
    y_u[1:, :] = seapy.earth_distance(lon_u[:-1, :], lat_u[:-1, :],
                                      lon_u[1:, :], lat_u[1:, :])
    y_u = np.cumsum(y_u, axis=0)

    # Create v-grid
    lat_v = 0.5 * (lat[1:, :] + lat[0:-1, :])
    lon_v = 0.5 * (lon[1:, :] + lon[0:-1, :])
    x_v = np.zeros(lat_v.shape)
    y_v = np.zeros(lat_v.shape)
    x_v[:, 1:] = seapy.earth_distance(lon_v[:, :-1], lat_v[:, :-1],
                                      lon_v[:, 1:], lat_v[:, 1:])
    x_v = np.cumsum(x_v, axis=1)
    y_v[1:, :] = seapy.earth_distance(lon_v[:-1, :], lat_v[:-1, :],
                                      lon_v[1:, :], lat_v[1:, :])
    y_v = np.cumsum(y_v, axis=0)

    # Create psi-grid
    lat_psi = lat_v[:, :-1]
    lon_psi = lon_u[:-1, :]
    x_psi = np.zeros(lat_psi.shape)
    y_psi = np.zeros(lat_psi.shape)
    x_psi[:, 1:] = seapy.earth_distance(lon_psi[:, :-1], lat_psi[:, :-1],
                                        lon_psi[:, 1:], lat_psi[:, 1:])
    x_psi = np.cumsum(x_psi, axis=1)
    y_psi[1:, :] = seapy.earth_distance(lon_psi[:-1, :], lat_psi[:-1, :],
                                        lon_psi[1:, :], lat_psi[1:, :])
    y_psi = np.cumsum(y_psi, axis=0)

    # Create the new grid
    nc = seapy.roms.ncgen.create_grid(grid_file,
                                      lat.shape[0],
                                      lat.shape[1],
                                      clobber=True)

    nc.variables["xl"][:] = xl
    nc.variables["el"][:] = el
    nc.variables["spherical"][:] = 1
    nc.variables["f"][:] = f
    nc.variables["pm"][:] = pm
    nc.variables["pn"][:] = pn
    nc.variables["dndx"][:] = dndx
    nc.variables["dmde"][:] = dmde
    nc.variables["x_rho"][:] = x_rho
    nc.variables["y_rho"][:] = y_rho
    nc.variables["x_psi"][:] = x_psi
    nc.variables["y_psi"][:] = y_psi
    nc.variables["x_u"][:] = x_u
    nc.variables["y_u"][:] = y_u
    nc.variables["x_v"][:] = x_v
    nc.variables["y_v"][:] = y_v
    nc.variables["lat_rho"][:] = lat
    nc.variables["lon_rho"][:] = lon
    nc.variables["lat_psi"][:] = lat_psi
    nc.variables["lon_psi"][:] = lon_psi
    nc.variables["lat_u"][:] = lat_u
    nc.variables["lon_u"][:] = lon_u
    nc.variables["lat_v"][:] = lat_v
    nc.variables["lon_v"][:] = lon_v
    nc.variables["N"][:] = 1
    nc.variables["mask_rho"][:] = np.ones(lat.shape)
    nc.variables["mask_u"][:] = np.ones(lat_u.shape)
    nc.variables["mask_v"][:] = np.ones(lat_v.shape)
    nc.variables["mask_psi"][:] = np.ones(lat_psi.shape)
    nc.variables["angle"][:] = angle
    nc.variables["rdrag"][:] = np.ones(lon.shape) * 0.0003
    nc.variables["rdrag2"][:] = np.ones(lon.shape) * 0.003
    nc.variables["visc_factor"][:] = np.ones(lon.shape)
    nc.variables["diff_factor"][:] = np.ones(lon.shape)
    nc.variables["ZoBot"][:] = np.ones(lon.shape) * 0.02
    nc.sync()

    return nc
示例#9
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
示例#10
0
    def set_dims(self):
        """
        Compute the dimension attributes of the grid based upon the information provided.

        Parameters
        ----------
        None

        Returns
        -------
        None : sets attributes in grid
        """
        # If C-Grid, set the dimensions for consistency
        if self.cgrid:
            self.eta_rho = self.ln
            self.eta_u = self.ln
            self.eta_v = self.ln - 1
            self.xi_rho = self.lm
            self.xi_u = self.lm - 1
            self.xi_v = self.lm

        # Set the number of layers
        if "n" not in self.__dict__:
            if "s_rho" in self.__dict__:
                self.n = int(self.s_rho.size)
            elif "z" in self.__dict__:
                self.n = int(self.z.size)
            else:
                self.n = 1
                self.z = np.zeros(self.lat_rho.shape)
        else:
            self.n = int(self.n)

        # Generate the u- and v-grids
        if ("lat_u" or "lon_u") not in self.__dict__:
            if self.cgrid:
                self.lat_u = 0.5 * \
                    (self.lat_rho[:, 1:] - self.lat_rho[:, 0:-1])
                self.lon_u = 0.5 * \
                    (self.lon_rho[:, 1:] - self.lon_rho[:, 0:-1])
            else:
                self.lat_u = self.lat_rho
                self.lon_u = self.lon_rho
        if ("lat_v" or "lon_v") not in self.__dict__:
            if self.cgrid:
                self.lat_v = 0.5 * \
                    (self.lat_rho[1:, :] - self.lat_rho[0:-1, :])
                self.lon_v = 0.5 * \
                    (self.lon_rho[1:, :] - self.lon_rho[0:-1, :])
            else:
                self.lat_v = self.lat_rho
                self.lon_v = self.lon_rho
        if "mask_rho" in self.__dict__:
            if "mask_u" not in self.__dict__:
                if self.cgrid:
                    self.mask_u = self.mask_rho[:, 1:] * self.mask_rho[:, 0:-1]
                else:
                    self.mask_u = self.mask_rho
            if "mask_v" not in self.__dict__:
                if self.cgrid:
                    self.mask_v = self.mask_rho[1:, :] * self.mask_rho[0:-1, :]
                else:
                    self.mask_v = self.mask_rho

        # Compute the resolution
        if "pm" in self.__dict__:
            self.dm = 1.0 / self.pm
        else:
            self.dm = np.ones(self.lon_rho.shape, dtype=np.float32)
            self.dm[:, 0:-1] = seapy.earth_distance(self.lon_rho[:, 1:],
                                                    self.lat_rho[:, 1:],
                                                    self.lon_rho[:, 0:-1],
                                                    self.lat_rho[:, 0:-1]).astype(np.float32)
            self.dm[:, -1] = self.dm[:, -2]
        if "pn" in self.__dict__:
            self.dn = 1.0 / self.pn
        else:
            self.dn = np.ones(self.lat_rho.shape, dtype=np.float32)
            self.dn[0:-1, :] = seapy.earth_distance(self.lon_rho[1:, :],
                                                    self.lat_rho[1:, :],
                                                    self.lon_rho[0:-1, :],
                                                    self.lat_rho[0:-1, :]).astype(np.float32)
            self.dn[-1, :] = self.dn[-2, :]

        # Compute the Coriolis
        if "f" not in self.__dict__:
            omega = 2 * np.pi * seapy.secs2day
            self.f = 2 * omega * np.sin(np.radians(self.lat_rho))

        # Set the grid index coordinates
        self.I, self.J = np.meshgrid(
            np.arange(0, self.lm), np.arange(0, self.ln))
示例#11
0
文件: analysis.py 项目: powellb/seapy
def transect(lon, lat, depth, data, nx=200, nz=40, z=None):
    """
    Generate an equidistant transect from data at varying depths. Can be
    used to plot a slice of model or observational data.

    Parameters
    ----------
    lat: array
        n-dimensional array with latitude of points
    lon: array
        n-dimensional array with longitude of points
    depth: array
        [k,n] dimensional array of the depths for all k-layers at each n point
    data: array
        [k,n] dimensional array of values
    nx: int, optional
        number of horizontal points desired in the transect
    nz: int, optional
        number of vertical points desired in the transect
    z: array, optional
        list of depths to use if you do not want equidistant depths

    Returns
    -------
    x: array
        x-location values in [m] along transect
    z: array
        depth values in [m] of the new transect
    vals: np.ma.array
        data values of the new transect with masked values

    Examples
    --------
    Generate series of transects from ROMS output

    >>> nc = seapy.netcdf('roms_his.nc')
    >>> grid = seapy.model.asgrid(nc)
    >>> data = nc.variables['salt'][:,:,150,:]
    >>> shp = (data.shape[0], 50, 400)
    >>> transect = np.zeros(shp)
    >>> for i in range(shp[0]):
    >>>     x, z, d = \
    >>>          seapy.roms.analysis.transect(grid.lon_rho[150,:],
    >>>                                       grid.lat_rho[150,:],
    >>>                                       grid.depth_rho[:,150,:],
    >>>                                       data[i,:,:], nx=shp[2],
    >>>                                       nz=shp[1])
    >>>     transect[i,:,:] = d.filled(np.nan)
    >>> nc.close()
    >>> plt.pcolormesh(x/1000, z, transect[0, :, :])
    """
    from scipy.interpolate import griddata
    depth = np.atleast_2d(depth)
    data = np.ma.atleast_2d(data).filled(np.mean(data))
    lon = np.atleast_1d(lon)
    lat = np.atleast_1d(lat)

    # Generate the depths
    depth[depth > 0] *= -1
    if z is None:
        z = np.linspace(depth.min() - 2, depth.max(), nz)
    else:
        z[z > 0] *= -1
        nz = len(z)
    dz = np.abs(np.diff(z).mean())

    # Determine the distance between points and the weighting to apply
    dist = np.hstack(([0], seapy.earth_distance(
        lon[0], lat[0], lon[1:], lat[1:])))
    dx = np.diff(dist).mean()
    zscale = np.maximum(1, 10**int(np.log10(dx / dz)))
    dx /= zscale
    x = np.linspace(0, dist.max(), nx)

    # All arrays have to be the same size
    xx, zz = np.meshgrid(x / zscale, z)

    # For the source data, we don't want to extrpolate,
    # so make the data go from the surface to twice its
    # depth.
    zl = np.argsort(depth[:, 0])
    dep = np.vstack((np.ones((1, depth.shape[1])) * 2 * depth.min(),
                     depth[zl, :],
                     np.zeros((1, depth.shape[1]))))

    # repeat the same data at the top and bottom
    dat = np.vstack((data[zl[0], :], data[zl],
                     data[zl[-1], :]))
    dist = np.tile(dist.T, [dep.shape[0], 1]) / zscale

    # Find the bottom indices to create a mask for nodata/land
    idx = np.interp(xx[0, :], dist[0, :],
                    np.interp(depth.min(axis=0), z,
                              np.arange(nz))).astype(int)
    mask = np.arange(nz)[:, np.newaxis] <= idx

    # Interpolate
    ndat = np.ma.array(griddata(
        (dist.ravel(), dep.ravel()), dat.ravel(), (xx.ravel(), zz.ravel()),
        method='cubic').reshape(xx.shape), mask=mask)

    # Return everything
    return x, z, ndat
示例#12
0
    def convert_file(self, file, title="SeaGlider Obs"):
        """
        Load a SeaGlider .pro file and convert into an obs structure
        """
        import re

        # Load the text file. All data goes into the pro dictionary
        # as defined by dtype. The header information needs to be parsed
        with open(file) as myfile:
            header = [myfile.readline() for i in range(19)]
            pro = np.loadtxt(myfile, self.dtype, delimiter=',', comments='%')

        # Parse the header information
        parser = re.compile('^%(\w+): (.*)$')
        params = {}
        for line in header:
            try:
                opt = parser.findall(line)
                params[opt[0][0]] = opt[0][1]
            except:
                pass

        # Determine the needed information from the headers
        glider_name = "GLIDER" if params.get("glider", None) is None else \
                      "GLIDER_SG" + params["glider"]
        provenance = seapy.roms.obs.asprovenance(glider_name)
        try:
            date = [int(s) for s in re.findall('([\d]{2})\s', params["start"])]
            start_time = datetime.datetime.strptime(params["start"].strip(),
                                                    "%m %d 1%y %H %M %S")
            dtime = (start_time - self.epoch).total_seconds() / 86400
        except:
            raise ValueError("date format incorrect in file: " + file)

        # Make sure that the GPS fix isn't screwy
        if self.grid.east():
            pro["lon"][pro["lon"] < 0] += 360
        dist = seapy.earth_distance(pro["lon"][0], pro["lat"][0],
                                    pro["lon"][-1], pro["lat"][-1])
        velocity = dist / pro["time"][-1]
        if velocity > 2:
            warn("WARNING: GPS fix is incorrect for " + file)
            return None

        # Build the data with masked entries
        temp = np.ma.masked_outside(pro["temp"], self.temp_limits[0],
                                    self.temp_limits[1])
        salt = np.ma.masked_outside(pro["salt"], self.salt_limits[0],
                                    self.salt_limits[1])
        depth = np.ma.masked_greater(-pro["depth"], self.depth_limit)
        good = ~np.ma.getmaskarray(depth)

        # Grid it
        data = [seapy.roms.obs.raw_data("TEMP", provenance, temp[good],
                                        None, self.temp_error),
                seapy.roms.obs.raw_data("SALT", provenance, salt[good],
                                        None, self.salt_error)]
        return seapy.roms.obs.gridder(self.grid, pro["time"][good] / 86400 + dtime,
                                      pro["lon"][good],
                                      pro["lat"][good],
                                      depth.compressed(),
                                      data, self.dt, title)