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
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
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
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
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}")
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)
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)
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'
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
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
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
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')