示例#1
0
def main(cubes, **kwargs):
    """Calculate and plot the distance from the tropopause to cloud
    """
    pv = convert.calc('ertel_potential_vorticity', cubes)
    cl = convert.calc('mass_fraction_of_cloud', cubes)

    # Get altitude as a cube
    z = grid.make_cube(pv, 'altitude')

    # Add pv and cloud as coordinates to the altitude cube
    pv = grid.make_coord(pv)
    z.add_aux_coord(pv, [0, 1, 2])
    cl = grid.make_coord(cl)
    z.add_aux_coord(cl, [0, 1, 2])

    # Calculate the height of the cloud top
    z_cloud = interpolate.to_level(z, mass_fraction_of_cloud=[1e-5])[0]

    # Calculate the height of the tropopause
    z_pv2 = interpolate.to_level(z, ertel_potential_vorticity=[2])[0]

    # Calculate the distance from tropopause to cloud
    dz = z_pv2.data - z_cloud.data
    dz = z_pv2.copy(data=dz)

    # Plot
    plot.pcolormesh(dz, **kwargs)
    plt.show()
示例#2
0
def Eady(theta,
         u,
         P,
         pressure_levels=(85000, 40000),
         theta_0=iris.cube.Cube(300, units='K')):
    r"""Calculate the Eady growth rate

    :math:`\sigma = 0.31 \frac{f}{N} |\frac{dU}{dz}|`

    Where :math:`f = 2 \Omega sin(\phi)`

    and :math:`N^2 = \frac{g}{\theta_0} \frac{d \theta}{dz}`

    Args:
        theta (iris.cube.Cube): Air potential temperature

        u (iris.cube.Cube): Zonal wind speed

        P (iris.cube.Cube): Air pressure

        pressure_levels:

        theta_0:

    Returns:
        iris.cube.Cube: Eady growth rate
    """
    # Calculate Coriolis parameter
    f = coriolis_parameter(theta)

    # Extract altitude as a cube
    z = grid.make_cube(P, 'altitude')

    # Add pressure as a coordinate to the cubes
    P = grid.make_coord(P)
    for cube in (theta, u, z):
        cube.add_aux_coord(P, [0, 1, 2])

    # Interpolate variables to pressure levels
    theta = interpolate.to_level(theta, air_pressure=pressure_levels)
    u = interpolate.to_level(u, air_pressure=pressure_levels)
    z = interpolate.to_level(z, air_pressure=pressure_levels)

    # Calculate the Brunt-Vaisala frequency
    dtheta_dz = calculus.multidim(theta, z, 'z')
    N_sq = dtheta_dz * (constants.g / theta_0)
    N_sq.units = 's-2'
    N = N_sq**0.5

    # Calclate the wind shear
    du_dz = calculus.multidim(u, z, P.name())
    du_dz.data = np.abs(du_dz.data)

    # Calculate the Eady index
    sigma = 0.31 * (du_dz / N) * f

    return sigma
示例#3
0
def create_startpoints(cubes, coordinate, values):
    """Create an array of startpoints

    Output array has shape Nx3 where N is the number of startpoints and the 3
    indices are for longitude, latitude and altitude.

    Args:
        cubes (iris.cube.CubeList)

    Returns:
        train (np.array): The input array to trajectory calculations
    """
    # Get the values of altitude at the given coordinate
    P = convert.calc(coordinate, cubes)
    z = grid.make_cube(P, 'altitude')
    z.add_aux_coord(grid.make_coord(P), [0, 1, 2])
    z500 = interpolate.to_level(z, **{coordinate: values})[0].data

    lon, lat = grid.get_xy_grids(P)

    # Convert the 2d arrays to an Nx3 array
    train = np.array([lon.flatten(),
                      lat.flatten(),
                      z500.flatten()]).transpose()

    return train
示例#4
0
def main(cubes, dz, name, **kwargs):
    """Produces cross section plots above and below the tropopause
    """
    # Extract cube to be plotted
    cube = convert.calc(name, cubes)

    # Find the height of the tropopause
    ztrop, fold_t, fold_b = tropopause.height(cubes)

    # Produce a new co-ordinate above and below the tropopause
    ny, nx = ztrop.shape
    new_coord = np.zeros([3, ny, nx])
    new_coord[0, :, :] = ztrop.data - dz
    new_coord[1, :, :] = ztrop.data
    new_coord[2, :, :] = ztrop.data + dz

    # Interpolate the cubes to be plotted to the coordinate above and below the
    # tropopause
    plotcube = interpolate.to_level(cube, altitude=new_coord)

    # Plot the cross sections
    for n in xrange(3):
        plt.figure()
        plot.pcolormesh(plotcube[n], **kwargs)
    plt.show()
示例#5
0
def calc(names, cubelist, levels=None):
    """Wrapper function to ``calculate`` with the option to interpolate to a
    level

    Args:
        names (str or list[str]): The CF standard name(s) of the
            variable(s) to be calculated.

        cubelist (iris.cube.CubeList): Contains either the requested variable or
            the variables required to calculate the requested variable.

        levels (tuple or None): The name and levels to interpolate the
            requested variable to. Default is None.

    Returns:
        iris.cube.Cube or iris.cube.CubeList: The variable(s) requested.
        Optionally interpolated to the given level
    """
    if type(names) == str:
        names = [names]
    # Call calculate to get the cube
    cubes = [calculate(name, cubelist) for name in names]

    # Return the cube if interpolation is not requested
    if levels is None:
        if len(names) == 1:
            return cubes[0]
        else:
            return iris.cube.CubeList(cubes)

    else:
        output = iris.cube.CubeList()

        # Extract the coordinate requested
        coord_name, values = levels

        for cube in cubes:
            # Extract the coordinate from the cubes
            try:
                coord = cube.coord(coord_name)

            # Alternatively use a cube from the cubelist
            except iris.exceptions.CoordinateNotFoundError:
                coord = calculate(coord_name, cubelist)
                coord = grid.make_coord(coord)
                cube.add_aux_coord(coord, range(cube.ndim))

            # Interpolate to the requested coordinate levels
            if coord.points.ndim == 1:
                result = cube.interpolate([(coord_name, values)],
                                          iris.analysis.Linear())
            else:
                result = interpolate.to_level(cube, **{coord_name: values})
            output.append(result)

        if len(names) == 1:
            return output[0]
        else:
            return output
示例#6
0
def isentropic_circulation(pv, pressure, mask=None):
    r"""Calculate the circulation associated with a given PV anomaly

    :math:`C_{\theta} = \iint_R \sigma Q dx dy`

    args:
        pv (iris.cube.Cube or iris.cube.CubeList): Potential vorticity on
            isentropic levels

        pressure (iris.cube.Cube): Pressure at gridpoints

        mask (numpy.ndarray): Mask for area to integrate over

    returns:
        iris.cube.CubeList: The circulation from each PV in q
    """
    # Make sure to iterate over list or cubelist
    if type(pv) == iris.cube.Cube:
        pv = [pv]

    # Calculate pressure halfway between theta levels
    theta_levs = pv[0].coord('air_potential_temperature').points
    dtheta = theta_levs[1] - theta_levs[0]
    theta_levs = list(theta_levs - dtheta / 2)
    theta_levs.append(theta_levs[-1] + dtheta)
    p_theta = interpolate.to_level(pressure,
                                   air_potential_temperature=theta_levs)

    # Calculate isentropic theta gradient at theta levels
    dp_dtheta = calculus.differentiate(p_theta, 'air_potential_temperature')

    # Calculate isentropic density
    sigma = -1 * dp_dtheta / constants.g

    # Apply the mask to sigma as it is used in each calculation
    if mask is not None:
        sigma.data = np.ma.masked_where(mask, sigma.data)

    # Extract the xy coordinate names to collapse the cube over
    coords = [sigma.coord(axis=axis).name() for axis in ['x', 'y']]

    # Calculate the weights to perform area integration
    # weights = iris.analysis.cartography.area_weights(sigma)
    weights = grid.volume(pressure)[-1].data * np.ones_like(pv[0].data)

    circulation = iris.cube.CubeList()
    for pv_i in pv:
        # Calculate the circulation
        c_i = (sigma * pv_i).collapsed(coords,
                                       iris.analysis.MEAN,
                                       weights=weights)

        # Give the cube a meaningful name
        c_i.rename('circulation_due_to_' + pv_i.name())
        circulation.append(c_i)

    return circulation
示例#7
0
def profile(x, surface, dz, mask=None, aggregator=MEAN, **kwargs):
    """Calculate an averaged vertical profile relative to a specified surface

    Args:
        x (iris.cube.Cube or iris.cube.CubeList): The variable(s) to produce
            profiles of.

        surface (iris.cube.Cube): 2d cube specifying the altitude of the
            surface to interpolate relative to.

        dz: Iterable container of floats prescribing the distances from the
            given surface to interpolate to.

        mask (array, optional): A binary array with the same shape as the cubes
            data. Default is None.

        aggregator (iris.analysis.Aggregator): The aggregator used to collapse
            the cube on each level. Default is MEAN.

        **kwargs: Additional keywords to be supplied to the
            :py:func:`iris.cube.Cube.collapsed` method (e.g. weights).

    Returns:
        iris.cube.CubeList: A vertical profile for each variable in x
    """
    # Check that inputs are correct
    if type(x) == iris.cube.Cube:
        x = [x.copy()]
    elif type(x) == iris.cube.CubeList or type(x) == list:
        x = [cube.copy() for cube in x]
    else:
        raise TypeError('Must specify cube or cubelist')

    # Loop over each cube
    cubes = iris.cube.CubeList()
    for cube in x:
        # Create a new coordinate for the cube
        newcoord = cube.coord('altitude').points - surface.data
        coord_name = 'distance_from_' + surface.name()
        newcoord = AuxCoord(newcoord, long_name=coord_name, units='m')
        cube.add_aux_coord(newcoord, [0, 1, 2])

        # Interpolate the cube relative to the surface
        newcube = interpolate.to_level(cube, **{coord_name: dz})

        # Apply the mask to the cube
        if mask is not None:
            newcube.data = np.ma.masked_where(
                mask * np.ones_like(newcube.data), newcube.data)

        # Collapse the cube along the horizontal dimensions
        xcoord = cube.coord(axis='X', dim_coords=True)
        ycoord = cube.coord(axis='Y', dim_coords=True)
        cubes.append(newcube.collapsed([xcoord, ycoord], aggregator, **kwargs))

    return cubes
示例#8
0
def get_tropopause_height(pv):
    """Find the altitude where pv=2 is crossed

    Args:
        pv (iris.cube.Cube):

    Returns:
        zpv2 (iris.cube.Cube): 2-dimensional cube with the altitude of the
            highest 2 PVU surface
    """
    z = grid.make_cube(pv, 'altitude')
    pv = grid.make_coord(pv)
    z.add_aux_coord(pv, [0, 1, 2])

    zpv2 = interpolate.to_level(z, ertel_potential_vorticity=[2])[0]

    return zpv2
示例#9
0
def bl_heights(cubes, theta, areamask):
    # Interpolate theta to bl height
    z_bl = convert.calc('boundary_layer_height', cubes)
    theta_bl = interpolate.to_level(theta, altitude=z_bl.data[None, :, :])[0]

    # Theta on bl defines cold and warm sectors
    cold_mask = np.logical_or(areamask, theta_bl.data > 300)
    warm_mask = np.logical_or(areamask, theta_bl.data < 300)

    # Calculate average bl height in cold and warm sectors
    p = convert.calc('air_pressure',
                     cubes,
                     levels=('altitude', z_bl.data[None, :, :]))[0]
    p.convert_units('hPa')
    cold_z = np.ma.masked_where(cold_mask, p.data)
    warm_z = np.ma.masked_where(warm_mask, p.data)

    return cold_z.mean(), warm_z.mean()
示例#10
0
def main(cubes, dz):
    # Calculate dp/dz (Hydrostatic balance dp/dz = -\rho g
    P = convert.calc('air_pressure', cubes)
    z = grid.make_cube(P, 'altitude')
    dP_dz = calculus.multidim(P, z, 'z')
    dP_dz = remap_3d(dP_dz, P)

    # Calculate absolute vorticity
    u = convert.calc('x_wind', cubes)
    v = convert.calc('y_wind', cubes)
    du_dy = calculus.diff_by_axis(u, 'y')
    du_dy = remap_3d(du_dy, P)
    dv_dx = calculus.diff_by_axis(v, 'x')
    dv_dx = remap_3d(dv_dx, P)
    vort = du_dy.data - dv_dx.data

    lat = grid.true_coords(P)[1]
    abs_vort = 2 * convert.omega.data * np.cos(lat) * vort

    # Calculate Nsq for each PV tracer
    tracers = convert.calc(names, cubes)
    nsq_0 = variable.N_sq(convert.calc('air_potential_temperature', cubes))
    nsq = []
    nsq.append(nsq_0)
    for tracer in tracers:
        nsq_i = -1 * tracer * dP_dz / abs_vort
        nsq_i.rename(tracer.name())
        nsq.append(nsq_i)

    # Create an average profile
    thetapv2 = convert.calc('air_potential_temperature',
                            cubes,
                            levels=('ertel_potential_vorticity', [2]))
    ridges, troughs = rossby_waves.make_nae_mask(thetapv2)
    pv = grid.make_coord(convert.calc('advection_only_pv', cubes))
    z.add_aux_coord(pv, [0, 1, 2])
    zpv = interpolate.to_level(z, advection_only_pv=[3.5])[0]
    y = diagnostics.profile(nsq, zpv, dz, mask=troughs)

    # Plot nsq

    plot.multiline(y)
    plt.show()
示例#11
0
def geopotential_height(P, levels):
    """Calculate height above sea level on pressure levels

    Args:
        P (iris.cube.Cube):

        levels (list or numpy.ndarray): The values of pressure to output the
            geopotential height on

    Returns:
        iris.cube.Cube: Height on pressure levels
    """
    # Create a height cube
    z = grid.make_cube(P, 'altitude')

    # Add pressure as a coordinate to the height
    pressure_coord = grid.make_coord(P)
    z.add_aux_coord(pressure_coord, range(z.ndim))

    # Interpolate the height on to pressure levels
    geopotential = interpolate.to_level(z, **{P.name(): levels})
    geopotential.rename('Geopotential_height')

    return geopotential