Пример #1
0
def test_interp_c_to_g_periodic(periodic_1d):
    """Interpolate from c grid to g grid."""
    ds = periodic_1d

    data_c = np.sin(ds['XC'])
    # np.roll(np.arange(5), 1) --> [4, 0, 1, 2, 3]
    # positive roll shifts left
    data_expected = 0.5 * (data_c.values + np.roll(data_c.values, 1))

    grid = Grid(ds)
    data_g = grid.interp(data_c, 'X')

    # check that the dimensions are right
    assert data_g.dims == ('XG', )
    xr.testing.assert_equal(data_g.XG, ds.XG)
    assert len(data_g.XG) == len(data_g)

    # check that the values are right
    np.testing.assert_allclose(data_g.values, data_expected)

    # try the same with chunks
    data_c = np.sin(ds['XC'])
    data_c = data_c.chunk(10)
    data_g = grid.interp(data_c, 'X')
    np.testing.assert_allclose(data_g.values, data_expected)
def calculate_momentum_budget(ds):
    grid = Grid(ds)

    combo = xr.Dataset()

    combo["u"] = ds.u
    combo["v"] = ds.v

    combo["du_dx"] = grid.diff(ds.u, "X") / ds.dxtn
    combo["du_dy"] = grid.diff(ds.u, "Y") / ds.dyte

    combo["u_du_dx"] = grid.interp(-combo["du_dx"] * grid.interp(ds.u, "X"),
                                   "Y")
    combo["v_du_dy"] = grid.interp(-combo["du_dy"] * grid.interp(ds.v, "Y"),
                                   "X")

    combo["hor"] = combo["u_du_dx"] + combo["v_du_dy"]
    combo["hor"].attrs[
        "long_name"] = "Zonal Velocity tendency due to hor divergence of momentum"
    combo["hor"].attrs["units"] = "m/s^(-2)"

    # Add tracer and vertical vel in there to get all relavant. Then drop again
    combo["wt"] = ds.wt  # for now just to include 'sw_ocean'
    combo["temp"] = ds.temp
    combo = combo.drop(["wt", "temp"])
    return combo
Пример #3
0
def add_MITgcm_missing_metrics(dset, periodic=None):
    """
    Infer missing metrics from MITgcm output files.

    Parameters
    ----------
    dset : xarray.Dataset
        A dataset open from a file

    Return
    -------
    dset : xarray.Dataset
        Input dataset with appropriated metrics added
    grid : xgcm.Grid
        The grid with appropriated metrics
    """
    coords = dset.coords
    grid = Grid(dset, periodic=periodic)

    BCx = 'periodic' if grid.axes['X']._periodic else 'fill'
    BCy = 'periodic' if grid.axes['Y']._periodic else 'fill'

    if 'drW' not in coords:  # vertical cell size at u point
        coords['drW'] = dset.hFacW * dset.drF
    if 'drS' not in coords:  # vertical cell size at v point
        coords['drS'] = dset.hFacS * dset.drF
    if 'drC' not in coords:  # vertical cell size at tracer point
        coords['drC'] = dset.hFacC * dset.drF

    if 'dxF' not in coords:
        coords['dxF'] = grid.interp(dset.dxC, 'X', boundary=BCx)
    if 'dyF' not in coords:
        coords['dyF'] = grid.interp(dset.dyC, 'Y', boundary=BCy)
    if 'dxV' not in coords:
        coords['dxV'] = grid.interp(dset.dxG, 'X', boundary=BCx)
    if 'dyU' not in coords:
        coords['dyU'] = grid.interp(dset.dyG, 'Y', boundary=BCy)

    if 'hFacZ' not in coords:
        coords['hFacZ'] = grid.interp(dset.hFacS, 'X', boundary=BCx)
    if 'maskZ' not in coords:
        coords['maskZ'] = coords['hFacZ']

    # Calculate vertical distances located on the cellboundary
    # ds.coords['dzC'] = grid.diff(ds.depth, 'Z', boundary='extrapolate')
    # Calculate vertical distances located on the cellcenter
    # ds.coords['dzT'] = grid.diff(ds.depth_left, 'Z', boundary='extrapolate')

    metrics = {
        ('X', ): ['dxG', 'dxF', 'dxC', 'dxV'],  # X distances
        ('Y', ): ['dyG', 'dyF', 'dyC', 'dyU'],  # Y distances
        ('Z', ): ['drW', 'drS', 'drC', 'drF'],  # Z distances
        ('X', 'Y'): ['rAw', 'rAs', 'rA', 'rAz']
    }  # Areas

    grid._assign_metrics(metrics)

    return dset, grid
def load_gos_data(gos_filenames):
    #import xgcm
    from xgcm import Grid
    from xgcm.autogenerate import generate_grid_ds
    # ====== load in all .nc files and combine into one xarray dataset
    gos_map = xr.open_mfdataset(gos_filenames)
    gos_map = gos_map.rename({'latitude': 'lat'}).rename({'longitude': 'lon'})
    gos_select = gos_map  #.sel(time='2016-11-19',lon=slice(10,16),lat=slice(-28,-24))
    #gos_map.ugos
    #dx = gos_map.lon.diff('lon')
    #gos_map['rel_vort'] = gos_map.vgos.diff('lon')/gos_map.lon.diff('lon')

    #gos_select = gos_map #gos_map.sel(time='2016-11-19',lon=slice(10,16),lat=slice(-28,-24))
    # create grid for interpolation, differencing
    #grid = xgcm.Grid(gos_select)
    # for Satellite data:
    # https://xgcm.readthedocs.io/en/latest/autogenerate_examples.html
    ds_full = generate_grid_ds(gos_select, {'X': 'lon', 'Y': 'lat'})
    ds_full.vgos

    grid = Grid(ds_full, periodic=['X'])

    # compute the difference (in degrees) along the longitude and latitude for both the cell center and the cell face
    # need to specify the boundary_discontinutity in order to avoid the introduction of artefacts at the boundary
    dlong = grid.diff(ds_full.lon, 'X', boundary_discontinuity=360)
    dlonc = grid.diff(ds_full.lon_left, 'X', boundary_discontinuity=360)
    #dlonc_wo_discontinuity = grid.diff(ds_full.lon_left, 'X')
    dlatg = grid.diff(ds_full.lat, 'Y', boundary='fill', fill_value=np.nan)
    dlatc = grid.diff(ds_full.lat_left,
                      'Y',
                      boundary='fill',
                      fill_value=np.nan)

    # converted into approximate cartesian distances on a globe.
    ds_full.coords['dxg'], ds_full.coords['dyg'] = dll_dist(
        dlong, dlatg, ds_full.lon, ds_full.lat)
    ds_full.coords['dxc'], ds_full.coords['dyc'] = dll_dist(
        dlonc, dlatc, ds_full.lon, ds_full.lat)

    # Relative vorticity: ζ = ∂ v/∂ x – ∂ u/∂ y
    ds_full['dv_dx'] = grid.diff(ds_full.vgos, 'X') / ds_full.dxg
    ds_full['du_dy'] = grid.diff(
        ds_full.ugos, 'Y', boundary='fill', fill_value=np.nan) / ds_full.dyg
    dv_dx = grid.interp(ds_full['dv_dx'],
                        'Y',
                        boundary='fill',
                        fill_value=np.nan)  # get dv_dx and du_dy on same grid
    du_dy = grid.interp(ds_full['du_dy'],
                        'X',
                        boundary='fill',
                        fill_value=np.nan)
    ds_full['Rel_Vort'] = dv_dx - du_dy

    # Vorticity Rossby Number = ζ / f
    ds_full['Ro'] = ds_full.Rel_Vort / coriolis(ds_full.Rel_Vort.lat_left)

    return ds_full
def recreate_full_grid_old(ds,
                           lon_bound_name='lon_bounds',
                           lat_bound_name='lat_bounds'):
    ds_full = generate_grid_ds(ds, {
        'X': 'x',
        'Y': 'y'
    },
                               position=('center', 'right'))

    grid = Grid(ds_full)

    # Derive distances at u point
    dlon = 0  # ill interpolate that later
    dlat = ds[lat_bound_name].isel(vertex=1) - ds[lat_bound_name].isel(
        vertex=2)
    # interpolate the centered position
    lat = (ds[lat_bound_name].isel(vertex=1) +
           ds[lat_bound_name].isel(vertex=2)) / 2
    lon = (ds[lon_bound_name].isel(vertex=1) +
           ds[lon_bound_name].isel(vertex=2)) / 2
    _, dy = dll_dist(dlon, dlat, lon, lat)
    # strip coords and rename dims
    ds_full.coords['dye'] = (['y', 'x_right'], dy.data)

    # Derive distances at v point
    dlon = ds[lon_bound_name].isel(vertex=3) - ds[lon_bound_name].isel(
        vertex=2)

    print(dlon)
    # special check for dlon
    temp = dlon.load().data
    temp[temp < 0] = temp[temp < 0] + 360.0
    dlon.data = temp

    dlat = 0  # ill interpolate that later
    # interpolate the centered position
    lat = (ds[lat_bound_name].isel(vertex=3) +
           ds[lat_bound_name].isel(vertex=2)) / 2
    lon = (ds[lon_bound_name].isel(vertex=3) +
           ds[lon_bound_name].isel(vertex=2)) / 2
    dx, _ = dll_dist(dlon, dlat, lon, lat)
    # strip coords and rename dims
    ds_full.coords['dxn'] = (['y_right', 'x'], dx.data)

    # interpolate the missing metrics
    ds_full.coords['dxt'] = grid.interp(ds_full.coords['dxn'], 'Y')
    ds_full.coords['dxne'] = grid.interp(ds_full.coords['dxn'], 'X')

    ds_full.coords['dyt'] = grid.interp(ds_full.coords['dye'], 'X')
    ds_full.coords['dyne'] = grid.interp(ds_full.coords['dye'], 'Y')

    return ds_full
Пример #6
0
def interpolated_velocities(u, v):
    """Interpolate the staggered horizontal velocities to the cell centers

    Use's Ryan Abernathy's xgcm package.

    Parameters
    ----------
    u, v : xr.DataArray
        staggered velocities.

    Returns
    -------
    uint, vint : xr.DataArray
        interpolated velocites on (xc, yc) grid
    """
    # setup grid
    xl = u.x.values
    xc = xl + (xl[1] - xl[0]) / 2

    yl = u.y.values
    yc = yl + (yl[1] - yl[0]) / 2

    u = u.rename({'x': 'xl', 'y': 'yc'}).assign_coords(xl=xl, yc=yc)
    v = v.rename({'x': 'xc', 'y': 'yl'}).assign_coords(yl=yl, xc=xc)

    # combine the data
    ds = xr.Dataset({'u': u, 'v': v})

    # create grid object
    coords = {
        'x': {
            'center': 'xc',
            'left': 'xl'
        },
        'y': {
            'center': 'yc',
            'left': 'yl'
        }
    }
    grid = Grid(ds, periodic=['x'], coords=coords)

    # use grid to interpolate
    uint = grid.interp(ds.u, axis='x')
    vint = grid.interp(ds.v, axis='y', boundary='extend')

    return uint, vint
def recreate_full_grid(ds, lon_name="lon", lat_name="lat"):
    ds_full = generate_grid_ds(ds, {
        "X": "x",
        "Y": "y"
    },
                               position=("center", "right"))
    grid = Grid(ds_full, periodic=['X'])

    # infer dx at eastern bound from tracer points
    lon0, lon1 = grid.axes["X"]._get_neighbor_data_pairs(
        ds_full.lon.load(), "right")
    lat0 = lat1 = ds_full.lat.load().data
    dx = distance(lon0, lat0, lon1, lat1)
    ds_full.coords["dxe"] = xr.DataArray(dx,
                                         coords=grid.interp(ds_full.lon,
                                                            "X").coords)

    # infer dy at northern bound from tracer points
    lat0, lat1 = grid.axes["Y"]._get_neighbor_data_pairs(
        ds_full.lat.load(), "right", boundary="extrapolate")

    lon0 = lon1 = ds_full.lon.load().data
    dy = distance(lon0, lat0, lon1, lat1)
    ds_full.coords["dyn"] = xr.DataArray(dy,
                                         coords=grid.interp(
                                             ds_full.lat,
                                             "Y",
                                             boundary="extrapolate").coords)

    # now simply interpolate all the other metrics
    ds_full.coords['dxt'] = grid.interp(ds_full.coords['dxe'], 'X')
    ds_full.coords['dxne'] = grid.interp(ds_full.coords['dxe'],
                                         'Y',
                                         boundary="extrapolate")
    ds_full.coords['dxn'] = grid.interp(ds_full.coords['dxt'],
                                        'Y',
                                        boundary="extrapolate")

    ds_full.coords['dyt'] = grid.interp(ds_full.coords['dyn'],
                                        'Y',
                                        boundary="extrapolate")
    ds_full.coords['dyne'] = grid.interp(ds_full.coords['dyn'], 'X')
    ds_full.coords['dye'] = grid.interp(ds_full.coords['dyt'], 'X')

    ds_full.coords['area_t'] = ds_full.coords['dxt'] * ds_full.coords['dyt']
    ds_full.coords['area_e'] = ds_full.coords['dxe'] * ds_full.coords['dye']
    ds_full.coords['area_ne'] = ds_full.coords['dxne'] * ds_full.coords['dyne']
    ds_full.coords['area_n'] = ds_full.coords['dxn'] * ds_full.coords['dyn']

    # should i return the coords to dask?
    return ds_full
Пример #8
0
def test_calculate_rel_vorticity():
    datadict = datasets()
    coords = datadict["coords"]
    ds_b = datadict["B"]
    grid_b = Grid(ds_b, coords=coords)

    ds_c = datadict["C"]
    grid_c = Grid(ds_c, coords=coords)

    test_b = (grid_b.diff(grid_b.interp(ds_b.v * ds_b.dy_ne, "Y"), "X") -
              grid_b.diff(grid_b.interp(ds_b.u * ds_b.dx_ne, "X"),
                          "Y")) / ds_b.area_t

    zeta_b = calculate_rel_vorticity(
        grid_b,
        ds_b.u,
        ds_b.v,
        ds_b.dx_ne,
        ds_b.dy_ne,
        ds_b.area_t,
        gridtype=None,
    )

    test_c = (grid_c.diff(ds_c.v * ds_c.dy_n, "X") -
              grid_c.diff(ds_c.u * ds_c.dx_e, "Y")) / ds_c.area_ne

    zeta_c = calculate_rel_vorticity(
        grid_c,
        ds_c.u,
        ds_c.v,
        ds_c.dx_e,
        ds_c.dy_n,
        ds_c.area_ne,
        gridtype=None,
    )

    assert_allclose(test_b, zeta_b)
    assert_allclose(test_c, zeta_c)
    with pytest.raises(RuntimeError):
        zeta_c = calculate_rel_vorticity(
            grid_b,
            ds_c.u,
            ds_c.v,
            ds_c.dx_n,  # wrong coordinate
            ds_c.dy_n,
            ds_c.area_ne,
            gridtype=None,
        )
Пример #9
0
def test_interp_g_to_c_periodic(periodic_1d):
    """Interpolate from c grid to g grid."""
    ds = periodic_1d

    # a linear gradient in the ni direction
    data_g = np.sin(ds['XG'])
    data_expected = 0.5 * (data_g.values + np.roll(data_g.values, -1))

    grid = Grid(ds)
    data_c = grid.interp(data_g, 'X')

    # check that the dimensions are right
    assert data_c.dims == ('XC', )
    xr.testing.assert_equal(data_c.XC, ds.XC)
    assert len(data_c.XC) == len(data_c)

    # check that the values are right
    np.testing.assert_allclose(data_c.values, data_expected)
Пример #10
0
def test_interp_g_to_c_nonperiodic(nonperiodic_1d):
    """Interpolate from g grid to c grid."""

    ds = nonperiodic_1d

    # a linear gradient in the ni direction
    grad = 0.43
    data_u = grad * ds['ni_u']
    data_expected = 0.5 * (data_u.values[1:] + data_u.values[:-1])

    grid = Grid(ds, x_periodic=False)
    data_c = grid.interp(data_u, 'X')

    # check that the dimensions are right
    assert data_c.dims == ('ni', )
    xr.testing.assert_equal(data_c.ni, ds.ni)
    assert len(data_c.ni) == len(data_c)

    # check that the values are right
    np.testing.assert_allclose(data_c.values, data_expected)
Пример #11
0
def add_split_tendencies(ds):
    """Reconstructs various fluxes and tendencies (x-y_x) from the monthly averaged output. Hardcoded to o2 atm."""
    ds = ds.copy()
    rho = 1035  #reference density
    grid = Grid(ds)

    # This should be calculated upstream (see: add_vertical_spacing), it is possibly totally wrong
    if 'dzwt' not in ds.coords:
        print(
            'Vertical spacing for vertical vel cell is approximated!!! Use with caution'
        )
        #         ds.coords['dzwt'] = grid.interp(ds['dzt'], 'Z')
        # This avoids computation. Somehow things are triggered when these fields are coordinates.
        ds['dzwt'] = grid.interp(ds['dzt'], 'Z')
    # These should be less critical, but should be given nontheless
    if 'dxte' not in ds.coords:
        print('Spacing for `dxte` is approximated!!! Use with caution')
        ds.coords['dxte'] = grid.interp(ds['dxt'], 'X')
    if 'dytn' not in ds.coords:
        print('Spacing for `dytn` is approximated!!! Use with caution')
        ds.coords['dytn'] = grid.interp(ds['dyt'], 'Y')

    # Calculate thickness weighted mass transports from velocities according to MOM5 formulation
    uhrho_et, vhrho_nt, wrhot = reconstruct_hrho_trans(ds.u, ds.v, ds.wt,
                                                       ds.dzt * rho,
                                                       ds.dzu * rho, grid, rho)

    # Reconstruct the flux terms
    ds['o2_xflux_adv_recon'], ds['o2_yflux_adv_recon'], ds['o2_zflux_adv_recon'] = \
        approximate_transport_op(uhrho_et, vhrho_nt, wrhot, ds['o2'], grid, boundary='extend')

    # Calculate tendencies from all advective fluxes
    for suffix in ['', '_recon']:
        ds['o2_advection_x'+suffix], \
        ds['o2_advection_y'+suffix], \
        ds['o2_advection_z'+suffix] = tend_from_fluxes(ds['o2_xflux_adv'+suffix],
                                                       ds['o2_yflux_adv'+suffix],
                                                       ds['o2_zflux_adv'+suffix], grid)

    # Reconstruct tendency terms seperately for changes in tracer and changes in transport
    udiv, vdiv, wdiv = tend_from_fluxes(uhrho_et * grid._ds.dyte,
                                        vhrho_nt * grid._ds.dxtn,
                                        wrhot * grid._ds.area_t, grid)
    ds['o2_advection_x_recon_vel'] = ds['o2'] * udiv
    ds['o2_advection_y_recon_vel'] = ds['o2'] * vdiv
    ds['o2_advection_z_recon_vel'] = ds['o2'] * wdiv

    ds['o2_advection_x_recon_tracer'] = -grid.interp(
        grid.diff(ds['o2'], 'X') / grid._ds.dxte * uhrho_et, 'X')
    ds['o2_advection_y_recon_tracer'] = -grid.interp(
        grid.diff(ds['o2'], 'Y') / grid._ds.dytn * vhrho_nt, 'Y')
    ds['o2_advection_z_recon_tracer'] = -grid.interp(
        grid.diff(ds['o2'], 'Z') / grid._ds.dzwt * wrhot, 'Z')

    #     # some interpolations... might want to remap these in the future, to get more accurate estimates
    #     u = grid.interp(ds['u'], 'Y')
    #     v = grid.interp(ds['v'], 'X')
    #     wt = ds['wt']
    # #     dxte = grid.interp(ds.dxu, 'Y')
    # #     dytn = grid.interp(ds.dyu, 'X')

    #     # o2_flux reconstructed from tracer and velocity field (vel*tracer*dyt*dzt*rho)
    #     # Are the values actually interpolated or do they take the center tracer value? Read up in MOM5 manual and correct if needed.
    #     # This will need some more advanced testing...in the end we cannot really reproduce the complex advection scheme, but it is worth trying
    # #     # to get as close as possible.
    # #     ds['o2_xflux_adv_recon'] = grid.interp(ds['o2'], 'X') * u * ds.dzt * ds.dyte * rho
    # #     ds['o2_yflux_adv_recon'] = grid.interp(ds['o2'], 'Y') * v * ds.dzt * ds.dxtn * rho
    # #     ds['o2_zflux_adv_recon'] = grid.interp(ds['o2'], 'Z') * wt * ds.dxt * ds.dyt * rho

    #     # Reconstruct the advective tendencies (as (tracer* s^-1) * dzt * rho)
    #     # also not sure about the numerics here...this implements finite difference approach...which mom used for some variables but not all...

    #     ds['o2_advection_x_recon_full'] = - (grid.diff(ds['o2_xflux_adv_recon'], 'X') / ds.area_t)
    #     ds['o2_advection_x_recon_du'] = - (ds['o2'] * grid.diff(u, 'X') / ds.dxt * ds.dzt * rho)
    #     ds['o2_advection_x_recon_do2'] = - (u * grid.diff(ds['o2'], 'X') / dxte * ds.dzt * rho)

    #     ds['o2_advection_y_recon_full'] = - (grid.diff(ds['o2_yflux_adv_recon'], 'Y') / ds.area_t)
    #     ds['o2_advection_y_recon_dv'] = - (ds['o2'] * grid.diff(v, 'Y') / ds.dyt * ds.dzt * rho)
    #     ds['o2_advection_y_recon_do2'] = - (v * grid.diff(ds['o2'], 'Y') / dytn * ds.dzt * rho)

    #     ds['o2_advection_z_recon_full'] =  (grid.diff(ds['o2_zflux_adv_recon'], 'Z') / ds.area_t)
    #     ds['o2_advection_z_recon_dwt'] =  (ds['o2'] * grid.diff(wt, 'Z') * rho)
    #     ds['o2_advection_z_recon_do2'] =  (wt * grid.diff(ds['o2'], 'Z') * rho)
    return ds
Пример #12
0
def read_topography(dictArgs, coords=None, point_type="t"):
    """Returns topography field based on the values of the CLI
    arguments and the presence of intake catalogs.

    Parameters
    ----------
    dictArgs : dict, optional
        dictionary of arguments obtained from the CLI parser, by default None
    coords : tuple, optional
        target xarray coordinates
    point_type : str, optional
        Requested grid type of t|q|u|v, by default "t"

    Returns
    -------
    xarray.DataArray
        topography array
    """

    point_type = point_type.upper()

    verbose = dictArgs["verbose"] if "verbose" in dictArgs else False

    if dictArgs["topog"] is not None:
        if verbose:
            print("Using optional topg file for depth field")
        ds = xr.open_dataset(dictArgs["topog"])

    elif dictArgs["static"] is not None:
        if verbose:
            print("Using optional static file for depth field.")
        ds = xr.open_dataset(dictArgs["static"])

    elif dictArgs["gridspec"] is not None:
        if verbose:
            print("Using optional gridspec tar file for depth field.")
        tar = tf.open(dictArgs["gridspec"])
        ds = extract_from_tar(tar, "ocean_topog.nc")

    elif dictArgs["platform"] is not None and dictArgs["config"] is not None:
        if verbose:
            print(
                f"Using {dictArgs['platform']} {dictArgs['config']} intake catalog for depth field."
            )
        cat = open_intake_catalog(dictArgs["platform"], dictArgs["config"])
        ds = cat["topog"].to_dask()

    if "deptho" in list(ds.variables):
        depth = ds.deptho
    elif "depth" in list(ds.variables):
        depth = ds.depth

    coords = xr.Dataset(coords)
    xedge = "outer" if (len(coords.xq) == len(coords.xh) + 1) else "right"
    yedge = "outer" if (len(coords.yq) == len(coords.yh) + 1) else "right"
    out_grid = Grid(
        coords,
        coords={
            "X": {
                "center": "xh",
                xedge: "xq"
            },
            "Y": {
                "center": "yh",
                yedge: "yq"
            }
        },
        periodic=["X"],
    )

    if point_type == "V":
        depth = out_grid.interp(depth, "Y", boundary="fill")
    elif point_type == "U":
        depth = out_grid.interp(depth, "X", boundary="fill")

    return depth
Пример #13
0
def load_wrf(date_in, wrf_nc_dir):
    '''Load wrf data

    Because loading large files could cost much time,
    please use "frames_per_outfile=1" in "namelist.input"
    to generate wrfout* files.

    '''

    # get all wrf output files in the same day
    f_wrf_pattern = os.path.join(wrf_nc_dir,
                                 date_in.strftime('wrfout_*_%Y-%m-%d_*'))
    wrf_list = glob.glob(f_wrf_pattern)

    # omit the directory and get "yyyy-mm-dd_hh:mm:ss"
    wrf_dates = [
        datetime.strptime(
            ntpath.basename(name).split('_', maxsplit=2)[-1],
            '%Y-%m-%d_%H:%M:%S') for name in wrf_list
    ]

    # get the index of the closest datetime
    wrf_index = wrf_dates.index(min(wrf_dates, key=lambda d: abs(d - date_in)))
    wrf_file = wrf_list[wrf_index]

    # read selected wrf data
    logging.info(' ' * 4 + f'Reading {wrf_file} ...')
    wrf = xr.open_dataset(wrf_file)
    wrf.attrs['wrfchem_filename'] = os.path.basename(wrf_file)

    # generate lon_bounds and lat_bounds
    wrf = wrf.rename({'XLONG': 'lon', 'XLAT': 'lat'}).isel(Time=0)
    wrf = generate_grid_ds(wrf, {
        'X': 'west_east',
        'Y': 'south_north'
    },
                           position=('center', 'outer'))

    # convert from potential temperature to absolute temperature (K)
    # http://mailman.ucar.edu/pipermail/wrf-users/2010/001896.html
    # http://mailman.ucar.edu/pipermail/wrf-users/2013/003117.html
    wrf['T'] = (wrf['T'] + 300) * ((wrf['P'] + wrf['PB']) / 1e5)**0.2865

    # generate grid and bounds
    grid_ds = Grid(wrf, periodic=False)
    bnd = 'extrapolate'
    wrf.coords['lon_b'] = grid_ds.interp(grid_ds.interp(wrf['lon'],
                                                        'X',
                                                        boundary=bnd,
                                                        fill_value=np.nan),
                                         'Y',
                                         boundary=bnd,
                                         fill_value=np.nan)
    wrf.coords['lat_b'] = grid_ds.interp(grid_ds.interp(wrf['lat'],
                                                        'X',
                                                        boundary=bnd,
                                                        fill_value=np.nan),
                                         'Y',
                                         boundary=bnd,
                                         fill_value=np.nan)
    logging.info(' ' * 8 + 'Finish reading')

    return wrf_file, wrf
Пример #14
0
def get_roms(path):
    ds = xr.open_mfdataset(path,
                           concat_dim='ocean_time',
                           combine='nested',
                           data_vars='minimal',
                           coords='minimal',
                           parallel=True,
                           chunks={
                               'ocean_time': 1,
                           })
    ds = ds.rename({
        'eta_u': 'eta_rho',
        'xi_v': 'xi_rho',
        'xi_psi': 'xi_u',
        'eta_psi': 'eta_v'
    })

    coords = {
        'X': {
            'center': 'xi_rho',
            'inner': 'xi_u'
        },
        'Y': {
            'center': 'eta_rho',
            'inner': 'eta_v'
        },
        'Z': {
            'center': 's_rho',
            'outer': 's_w'
        }
    }

    grid = Grid(ds, coords=coords, periodic=[])

    Zo_rho = (ds.hc * ds.s_rho + ds.Cs_r * ds.h) / (ds.hc + ds.h)
    z_rho = Zo_rho * (ds.zeta + ds.h) + ds.zeta
    Zo_w = (ds.hc * ds.s_w + ds.Cs_w * ds.h) / (ds.hc + ds.h)
    z_w = Zo_w * (ds.zeta + ds.h) + ds.zeta

    ds.coords['z_w'] = z_w.where(ds.mask_rho,
                                 0).transpose('ocean_time', 's_w', 'eta_rho',
                                              'xi_rho')
    ds.coords['z_rho'] = z_rho.where(ds.mask_rho,
                                     0).transpose('ocean_time', 's_rho',
                                                  'eta_rho', 'xi_rho')

    ds['pm_v'] = grid.interp(ds.pm, 'Y')
    ds['pn_u'] = grid.interp(ds.pn, 'X')
    ds['pm_u'] = grid.interp(ds.pm, 'X')
    ds['pn_v'] = grid.interp(ds.pn, 'Y')
    ds['pm_psi'] = grid.interp(grid.interp(ds.pm, 'Y'),
                               'X')  # at psi points (eta_v, xi_u)
    ds['pn_psi'] = grid.interp(grid.interp(ds.pn, 'X'),
                               'Y')  # at psi points (eta_v, xi_u)

    ds['dx'] = 1 / ds.pm
    ds['dx_u'] = 1 / ds.pm_u
    ds['dx_v'] = 1 / ds.pm_v
    ds['dx_psi'] = 1 / ds.pm_psi

    ds['dy'] = 1 / ds.pn
    ds['dy_u'] = 1 / ds.pn_u
    ds['dy_v'] = 1 / ds.pn_v
    ds['dy_psi'] = 1 / ds.pn_psi

    ds['dz'] = grid.diff(ds.z_w, 'Z', boundary='fill')
    ds['dz_w'] = grid.diff(ds.z_rho, 'Z', boundary='fill')
    ds['dz_u'] = grid.interp(ds.dz, 'X')
    ds['dz_w_u'] = grid.interp(ds.dz_w, 'X')
    ds['dz_v'] = grid.interp(ds.dz, 'Y')
    ds['dz_w_v'] = grid.interp(ds.dz_w, 'Y')

    ds['dA'] = ds.dx * ds.dy

    metrics = {
        ('X', ): ['dx', 'dx_u', 'dx_v', 'dx_psi'],  # X distances
        ('Y', ): ['dy', 'dy_u', 'dy_v', 'dy_psi'],  # Y distances
        ('Z', ): ['dz', 'dz_u', 'dz_v', 'dz_w', 'dz_w_u',
                  'dz_w_v'],  # Z distances
        ('X', 'Y'): ['dA']  # Areas
    }
    grid = Grid(ds, coords=coords, metrics=metrics, periodic=[])
    return ds, grid
Пример #15
0
#ds1 = xr.merge([ds1, dsgrid])

# Compute vorticity and divergence
vorticity = (-grid.diff(ds.U * ds.dxC, 'Y', boundary='fill') +
             grid.diff(ds.V * ds.dyC, 'X', boundary='fill')) / ds.rAz

divergence = (grid.diff(ds.U * ds.dxC, 'X', boundary='fill') +
              grid.diff(ds.V * ds.dyC, 'Y', boundary='fill')) / ds.rA

# Select data for a small region
lat1, lat2 = (29., 41.)
# 10x10 deg box near gulf stream
lon1, lon2 = (-121., -109.)
# 1 deg should be avoided on each side for the final data

u1 = grid.interp(ds.U, 'X', boundary='fill')
v1 = grid.interp(ds.V, 'Y', boundary='fill')
vort = grid.interp(grid.interp(vorticity, 'X', boundary='fill'),
                   'Y',
                   boundary='fill')
#w1 = grid1.interp(ds1.W, 'Z', boundary='fill')
#w1 = w1.sel({k = [1,3,5,10,30]})
#eta = ds.Eta

#eta = eta.where((ds.YC > lat1) & (ds.YC < lat2) & (ds.XC > lon1) & (ds.XC < lon2), drop = True)
u1 = u1.where(
    (ds.YC > lat1) & (ds.YC < lat2) & (ds.XC > lon1) & (ds.XC < lon2),
    drop=True)
v1 = v1.where(
    (ds.YC > lat1) & (ds.YC < lat2) & (ds.XC > lon1) & (ds.XC < lon2),
    drop=True)