Пример #1
0
    def calculate(cubes):
        """Compute vegetation fraction from bare soil fraction."""
        baresoilfrac_cube = cubes.extract_cube(
            var_name_constraint('baresoilFrac'))
        residualfrac_cube = cubes.extract_cube(
            var_name_constraint('residualFrac'))
        sftlf_cube = cubes.extract_cube(var_name_constraint('sftlf'))

        # Add time dimension to sftlf
        target_shape_sftlf = (baresoilfrac_cube.shape[0], *sftlf_cube.shape)
        sftlf_data = iris.util.broadcast_to_shape(sftlf_cube.data,
                                                  target_shape_sftlf, (1, 2))
        sftlf_cube = baresoilfrac_cube.copy(sftlf_data)
        sftlf_cube.data = sftlf_cube.lazy_data()

        # Regrid sftlf if necessary and adapt mask
        if sftlf_cube.shape != baresoilfrac_cube.shape:
            sftlf_cube = regrid(sftlf_cube, baresoilfrac_cube, 'linear')
        sftlf_cube.data = da.ma.masked_array(
            sftlf_cube.core_data(),
            mask=da.ma.getmaskarray(baresoilfrac_cube.core_data()))

        # Calculate vegetation fraction
        baresoilfrac_cube.data = (sftlf_cube.core_data() -
                                  baresoilfrac_cube.core_data() -
                                  residualfrac_cube.core_data())
        return baresoilfrac_cube
Пример #2
0
    def calculate(cubes):
        """Compute liquid water path.

        Note
        ----
        Some datasets output the variable `clwvi` which only contains `lwp`. In
        these cases, the input `clwvi` cube is just returned.

        """
        # CMIP5 and CMIP6 names are slightly different, so use
        # variable name instead to extract cubes
        clwvi_cube = cubes.extract_cube(var_name_constraint('clwvi'))
        clivi_cube = cubes.extract_cube(var_name_constraint('clivi'))

        # CMIP5 and CMIP6 have different global attributes that we use
        # to determine model name and project name:
        #   - CMIP5: model_id and project_id
        #   - CMIP6: source_id and mip_era
        project = clwvi_cube.attributes.get('project_id')
        if project:
            dataset = clwvi_cube.attributes.get('model_id')
        else:
            project = clwvi_cube.attributes.get('mip_era')
            dataset = clwvi_cube.attributes.get('source_id')

        # Should we check that the model_id/project_id are the same on both
        # cubes?

        bad_datasets = [
            'CESM1-CAM5-1-FV2',
            'CESM1-CAM5',
            'CMCC-CESM',
            'CMCC-CM',
            'CMCC-CMS',
            'IPSL-CM5A-MR',
            'IPSL-CM5A-LR',
            'IPSL-CM5B-LR',
            'CCSM4',
            'IPSL-CM5A-MR',
            'MIROC-ESM',
            'MIROC-ESM-CHEM',
            'MIROC-ESM',
            'CSIRO-Mk3-6-0',
            'MPI-ESM-MR',
            'MPI-ESM-LR',
            'MPI-ESM-P',
            'CAMS-CSM1-0',
            'GISS-E2-1-G',
            'GISS-E2-1-H',
        ]
        affected_projects = ["CMIP5", "CMIP5_ETHZ", "CMIP6"]
        if (project in affected_projects and dataset in bad_datasets):
            logger.info(
                "Assuming that variable clwvi from %s dataset %s "
                "contains only liquid water", project, dataset)
            lwp_cube = clwvi_cube
        else:
            lwp_cube = clwvi_cube - clivi_cube

        return lwp_cube
Пример #3
0
    def calculate(cubes):
        """Compute surface albedo."""
        rsdscs_cube = cubes.extract_strict(var_name_constraint('rsdscs'))
        rsuscs_cube = cubes.extract_strict(var_name_constraint('rsuscs'))

        rsnscs_cube = rsuscs_cube / rsdscs_cube

        return rsnscs_cube
Пример #4
0
    def calculate(cubes):
        """Compute Latent Heat Release from Precipitation."""
        hfls_cube = cubes.extract_strict(var_name_constraint('hfls'))
        pr_cube = cubes.extract_strict(var_name_constraint('pr'))
        evspsbl_cube = cubes.extract_strict(var_name_constraint('evspsbl'))

        lvp_cube = hfls_cube * (pr_cube / evspsbl_cube)

        return lvp_cube
Пример #5
0
def get_bounds_cube(cubes, coord_var_name):
    """Find bound cube for a given variable in a :class:`iris.cube.CubeList`.

    Parameters
    ----------
    cubes : iris.cube.CubeList
        List of cubes containing the coordinate bounds for the desired
        coordinate as single cube.
    coord_var_name : str
        ``var_name`` of the desired coordinate (without suffix ``_bnds`` or
        ``_bounds``).

    Returns
    -------
    iris.cube.Cube
        Bounds cube.

    Raises
    ------
    ValueError
        ``cubes`` do not contain the desired coordinate bounds or multiple
        copies of them.

    """
    for bounds in ('bnds', 'bounds'):
        bound_var = f'{coord_var_name}_{bounds}'
        cube = cubes.extract(var_name_constraint(bound_var))
        if len(cube) == 1:
            return cube[0]
        if len(cube) > 1:
            raise ValueError(
                f"Multiple cubes with var_name '{bound_var}' found")
    raise ValueError(
        f"No bounds for coordinate variable '{coord_var_name}' available in "
        f"cubes\n{cubes}")
Пример #6
0
def add_aux_coords_from_cubes(cube, cubes, coord_dict):
    """Add auxiliary coordinate to cube from another cube in list of cubes.

    Parameters
    ----------
    cube : iris.cube.Cube
        Input cube to which the auxiliary coordinates will be added.
    cubes : iris.cube.CubeList
        List of cubes which contains the desired coordinates as single cubes.
    coord_dict : dict
        Dictionary of the form ``coord_name: coord_dims``, where ``coord_name``
        is the ``var_name`` (:obj:`str`) of the desired coordinates and
        ``coord_dims`` a :obj:`tuple` of :obj:`int` describing the coordinate
        dimensions in ``cube``.

    Raises
    ------
    ValueError
        ``cubes`` do not contain a desired coordinate or multiple copies of
        it.

    """
    for (coord_name, coord_dims) in coord_dict.items():
        coord_cube = cubes.extract(var_name_constraint(coord_name))
        if len(coord_cube) != 1:
            raise ValueError(
                f"Expected exactly one coordinate cube '{coord_name}' in "
                f"list of cubes {cubes}, got {len(coord_cube):d}")
        coord_cube = coord_cube[0]
        aux_coord = cube_to_aux_coord(coord_cube)
        cube.add_aux_coord(aux_coord, coord_dims)
        cubes.remove(coord_cube)
Пример #7
0
def test_add_aux_coords_from_cubes(coord_dict, output):
    """Test extraction of auxiliary coordinates from cubes."""
    cube = iris.cube.Cube([[0.0]])
    cubes = iris.cube.CubeList([
        iris.cube.Cube(0.0, var_name='a'),
        iris.cube.Cube([0.0], var_name='b'),
        iris.cube.Cube([0.0], var_name='c'),
        iris.cube.Cube([0.0], var_name='c'),
        iris.cube.Cube([[0.0]], var_name='d'),
    ])
    if output == 1:
        add_aux_coords_from_cubes(cube, cubes, coord_dict)
        for (coord_name, coord_dims) in coord_dict.items():
            coord = cube.coord(var_name=coord_name)
            if len(cube.coord_dims(coord)) == 1:
                assert cube.coord_dims(coord)[0] == coord_dims
            else:
                assert cube.coord_dims(coord) == coord_dims
            points = np.full(coord.shape, 0.0)
            assert coord.points == points
            assert not cubes.extract(var_name_constraint(coord_name))
        assert len(cubes) == 5 - len(coord_dict)
        return
    with pytest.raises(ValueError) as err:
        add_aux_coords_from_cubes(cube, cubes, coord_dict)
    if output == 0:
        assert "Expected exactly one coordinate cube 'x'" in str(err.value)
        assert "got 0" in str(err.value)
    else:
        assert "Expected exactly one coordinate cube 'c'" in str(err.value)
        assert "got 2" in str(err.value)
Пример #8
0
def hybrid_height_coord_fix_metadata(nc_path, short_name, fix):
    """Test ``fix_metadata`` of file with hybrid height coord."""
    cubes = iris.load(nc_path)

    # Raw cubes
    assert len(cubes) == 3
    var_names = [cube.var_name for cube in cubes]
    assert short_name in var_names
    assert 'orog' in var_names
    assert 'b_bnds' in var_names

    # Raw cube
    cube = cubes.extract_strict(var_name_constraint(short_name))
    height_coord = cube.coord('altitude')
    assert height_coord.points is not None
    assert height_coord.bounds is not None
    np.testing.assert_allclose(height_coord.points, HEIGHT_POINTS)
    np.testing.assert_allclose(height_coord.bounds, HEIGHT_BOUNDS_WRONG)
    assert not np.allclose(height_coord.bounds, HEIGHT_BOUNDS_RIGHT)
    assert not cube.coords('air_pressure')

    # Apply fix
    fixed_cubes = fix.fix_metadata(cubes)
    assert len(fixed_cubes) == 1
    fixed_cube = fixed_cubes.extract_strict(var_name_constraint(short_name))
    fixed_height_coord = fixed_cube.coord('altitude')
    assert fixed_height_coord.points is not None
    assert fixed_height_coord.bounds is not None
    np.testing.assert_allclose(fixed_height_coord.points, HEIGHT_POINTS)
    np.testing.assert_allclose(fixed_height_coord.bounds, HEIGHT_BOUNDS_RIGHT)
    assert not np.allclose(fixed_height_coord.bounds, HEIGHT_BOUNDS_WRONG)
    air_pressure_coord = cube.coord('air_pressure')
    np.testing.assert_allclose(air_pressure_coord.points, PRESSURE_POINTS)
    np.testing.assert_allclose(air_pressure_coord.bounds, PRESSURE_BOUNDS)
    assert air_pressure_coord.var_name == 'plev'
    assert air_pressure_coord.standard_name == 'air_pressure'
    assert air_pressure_coord.long_name == 'pressure'
    assert air_pressure_coord.units == 'Pa'
Пример #9
0
def hybrid_pressure_coord_fix_metadata(nc_path, short_name, fix):
    """Test ``fix_metadata`` of file with hybrid pressure coord."""
    cubes = iris.load(nc_path)

    # Raw cubes
    assert len(cubes) == 4
    var_names = [cube.var_name for cube in cubes]
    assert short_name in var_names
    assert 'ps' in var_names
    assert 'b_bnds' in var_names

    # Raw cube
    cube = cubes.extract_strict(var_name_constraint(short_name))
    air_pressure_coord = cube.coord('air_pressure')
    assert air_pressure_coord.points is not None
    assert air_pressure_coord.bounds is None
    np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS)

    # Raw ps cube
    ps_cube = cubes.extract_strict('surface_air_pressure')
    assert ps_cube.attributes == {'additional_attribute': 'xyz'}

    # Apply fix
    fixed_cubes = fix.fix_metadata(cubes)
    assert len(fixed_cubes) == 1
    fixed_cube = fixed_cubes.extract_strict(var_name_constraint(short_name))
    fixed_air_pressure_coord = fixed_cube.coord('air_pressure')
    assert fixed_air_pressure_coord.points is not None
    assert fixed_air_pressure_coord.bounds is not None
    np.testing.assert_allclose(fixed_air_pressure_coord.points,
                               AIR_PRESSURE_POINTS)
    np.testing.assert_allclose(fixed_air_pressure_coord.bounds,
                               AIR_PRESSURE_BOUNDS)
    surface_pressure_coord = fixed_cube.coord(var_name='ps')
    assert surface_pressure_coord.attributes == {}

    return var_names
Пример #10
0
def cloud_area_fraction(cubes, tau_constraint, plev_constraint):
    """Calculate cloud area fraction for different parameters."""
    clisccp_cube = cubes.extract_cube(var_name_constraint('clisccp'))
    new_cube = clisccp_cube
    new_cube = new_cube.extract(tau_constraint & plev_constraint)
    coord_names = [
        coord.standard_name for coord in new_cube.coords()
        if len(coord.points) > 1
    ]
    if 'atmosphere_optical_thickness_due_to_cloud' in coord_names:
        new_cube = new_cube.collapsed(
            'atmosphere_optical_thickness_due_to_cloud', iris.analysis.SUM)
    if 'air_pressure' in coord_names:
        new_cube = new_cube.collapsed('air_pressure', iris.analysis.SUM)

    return new_cube
Пример #11
0
    def calculate(cubes):
        """Compute soil moisture.

        Note
        ----
        Convert moisture content of soil layer (kg/m2) into volumetric soil
        moisture (m3/m3), assuming density of water 998.2 kg/m2 (at temperature
        20 deg C).

        """
        mrsos_cube = cubes.extract_strict(var_name_constraint('mrsos'))

        depth = mrsos_cube.coord('depth').bounds.astype(np.float32)
        layer_thickness = depth[..., 1] - depth[..., 0]

        sm_cube = mrsos_cube / layer_thickness / 998.2
        sm_cube.units = cf_units.Unit('m3 m^-3')

        return sm_cube
Пример #12
0
def test_var_name_constraint(cubes):
    """Test :func:`esmvalcore.iris_helpers.var_name_constraint`."""
    out_cubes = cubes.extract(var_name_constraint('a'))
    assert out_cubes == iris.cube.CubeList([
        iris.cube.Cube(0.0, var_name='a', long_name='a'),
        iris.cube.Cube(0.0, var_name='a', long_name='b'),
    ])
    out_cubes = cubes.extract(var_name_constraint('b'))
    assert out_cubes == iris.cube.CubeList([])
    out_cubes = cubes.extract(var_name_constraint('c'))
    assert out_cubes == iris.cube.CubeList([
        iris.cube.Cube(0.0, var_name='c', long_name='d'),
    ])
    with pytest.raises(iris.exceptions.ConstraintMismatchError):
        cubes.extract_cube(var_name_constraint('a'))
    with pytest.raises(iris.exceptions.ConstraintMismatchError):
        cubes.extract_cube(var_name_constraint('b'))
    out_cube = cubes.extract_cube(var_name_constraint('c'))
    assert out_cube == iris.cube.Cube(0.0, var_name='c', long_name='d')