示例#1
0
def create_cube(lon_min, lon_max, bounds=False):
    n_lons = max(lon_min, lon_max) - min(lon_max, lon_min)
    data = np.arange(4 * 3 * n_lons, dtype='f4').reshape(4, 3, n_lons)
    data = biggus.NumpyArrayAdapter(data)
    cube = Cube(data, standard_name='x_wind', units='ms-1')
    cube.add_dim_coord(
        iris.coords.DimCoord([0, 20, 40, 80],
                             long_name='level_height',
                             units='m'), 0)
    cube.add_aux_coord(
        iris.coords.AuxCoord([1.0, 0.9, 0.8, 0.6], long_name='sigma'), 0)
    cube.add_dim_coord(
        iris.coords.DimCoord([-45, 0, 45], 'latitude', units='degrees'), 1)
    step = 1 if lon_max > lon_min else -1
    cube.add_dim_coord(
        iris.coords.DimCoord(np.arange(lon_min, lon_max, step),
                             'longitude',
                             units='degrees'), 2)
    if bounds:
        cube.coord('longitude').guess_bounds()
    cube.add_aux_coord(
        iris.coords.AuxCoord(np.arange(3 * n_lons).reshape(3, n_lons) * 10,
                             'surface_altitude',
                             units='m'), [1, 2])
    cube.add_aux_factory(
        iris.aux_factory.HybridHeightFactory(cube.coord('level_height'),
                                             cube.coord('sigma'),
                                             cube.coord('surface_altitude')))
    return cube
示例#2
0
    def test_hybrid_height_with_non_standard_coords(self):
        # Check the save rules are using the AuxFactory to find the
        # hybrid height coordinates and not relying on their names.
        ny, nx = 30, 40
        sigma_lower, sigma, sigma_upper = 0.75, 0.8, 0.75
        delta_lower, delta, delta_upper = 150, 200, 250

        cube = Cube(np.zeros((ny, nx)), 'air_temperature')
        level_coord = AuxCoord(0, 'model_level_number')
        cube.add_aux_coord(level_coord)
        delta_coord = AuxCoord(delta, bounds=[[delta_lower, delta_upper]],
                               long_name='moog', units='m')
        sigma_coord = AuxCoord(sigma, bounds=[[sigma_lower, sigma_upper]],
                               long_name='mavis')
        surface_altitude_coord = AuxCoord(np.zeros((ny, nx)),
                                          'surface_altitude', units='m')
        cube.add_aux_coord(delta_coord)
        cube.add_aux_coord(sigma_coord)
        cube.add_aux_coord(surface_altitude_coord, (0, 1))
        cube.add_aux_factory(HybridHeightFactory(delta_coord, sigma_coord,
                                                 surface_altitude_coord))

        field = iris.fileformats.pp.PPField3()
        field.lbfc = 0
        field.lbvc = 0
        field.brsvd = [None, None]
        field.lbuser = [None] * 7
        iris.fileformats.pp._ensure_save_rules_loaded()
        iris.fileformats.pp._save_rules.verify(cube, field)

        self.assertEqual(field.blev, delta)
        self.assertEqual(field.brlev, delta_lower)
        self.assertEqual(field.bhlev, sigma)
        self.assertEqual(field.bhrlev, sigma_lower)
        self.assertEqual(field.brsvd, [delta_upper, sigma_upper])
示例#3
0
    def test_hybrid_pressure_with_non_standard_coords(self):
        # Check the save rules are using the AuxFactory to find the
        # hybrid pressure coordinates and not relying on their names.
        ny, nx = 30, 40
        sigma_lower, sigma, sigma_upper = 0.75, 0.8, 0.75
        delta_lower, delta, delta_upper = 0.15, 0.2, 0.25

        cube = Cube(np.zeros((ny, nx)), 'air_temperature')
        level_coord = AuxCoord(0, 'model_level_number')
        cube.add_aux_coord(level_coord)
        delta_coord = AuxCoord(delta, bounds=[[delta_lower, delta_upper]],
                               long_name='moog', units='Pa')
        sigma_coord = AuxCoord(sigma, bounds=[[sigma_lower, sigma_upper]],
                               long_name='mavis')
        surface_air_pressure_coord = AuxCoord(np.zeros((ny, nx)),
                                              'surface_air_pressure',
                                              units='Pa')
        cube.add_aux_coord(delta_coord)
        cube.add_aux_coord(sigma_coord)
        cube.add_aux_coord(surface_air_pressure_coord, (0, 1))
        cube.add_aux_factory(HybridPressureFactory(
            delta_coord, sigma_coord, surface_air_pressure_coord))

        field = iris.fileformats.pp.PPField3()
        field.lbfc = 0
        field.lbvc = 0
        field.brsvd = [None, None]
        field.lbuser = [None] * 7
        field = verify(cube, field)

        self.assertEqual(field.bhlev, delta)
        self.assertEqual(field.bhrlev, delta_lower)
        self.assertEqual(field.blev, sigma)
        self.assertEqual(field.brlev, sigma_lower)
        self.assertEqual(field.brsvd, [sigma_upper, delta_upper])
示例#4
0
def create_cube(lon_min, lon_max, bounds=False):
    n_lons = max(lon_min, lon_max) - min(lon_max, lon_min)
    data = np.arange(4 * 3 * n_lons, dtype='f4').reshape(4, 3, n_lons)
    data = biggus.NumpyArrayAdapter(data)
    cube = Cube(data, standard_name='x_wind', units='ms-1')
    cube.add_dim_coord(iris.coords.DimCoord([0, 20, 40, 80],
                                            long_name='level_height',
                                            units='m'), 0)
    cube.add_aux_coord(iris.coords.AuxCoord([1.0, 0.9, 0.8, 0.6],
                                            long_name='sigma'), 0)
    cube.add_dim_coord(iris.coords.DimCoord([-45, 0, 45], 'latitude',
                                            units='degrees'), 1)
    step = 1 if lon_max > lon_min else -1
    circular = (abs(lon_max - lon_min) == 360)
    cube.add_dim_coord(iris.coords.DimCoord(np.arange(lon_min, lon_max, step),
                                            'longitude', units='degrees',
                                            circular=circular), 2)
    if bounds:
        cube.coord('longitude').guess_bounds()
    cube.add_aux_coord(iris.coords.AuxCoord(
        np.arange(3 * n_lons).reshape(3, n_lons) * 10, 'surface_altitude',
        units='m'), [1, 2])
    cube.add_aux_factory(iris.aux_factory.HybridHeightFactory(
        cube.coord('level_height'), cube.coord('sigma'),
        cube.coord('surface_altitude')))
    return cube
示例#5
0
    def test_hybrid_pressure_with_non_standard_coords(self):
        # Check the save rules are using the AuxFactory to find the
        # hybrid pressure coordinates and not relying on their names.
        ny, nx = 30, 40
        sigma_lower, sigma, sigma_upper = 0.75, 0.8, 0.75
        delta_lower, delta, delta_upper = 0.15, 0.2, 0.25

        cube = Cube(np.zeros((ny, nx)), "air_temperature")
        level_coord = AuxCoord(0, "model_level_number")
        cube.add_aux_coord(level_coord)
        delta_coord = AuxCoord(delta, bounds=[[delta_lower, delta_upper]], long_name="moog", units="Pa")
        sigma_coord = AuxCoord(sigma, bounds=[[sigma_lower, sigma_upper]], long_name="mavis")
        surface_air_pressure_coord = AuxCoord(np.zeros((ny, nx)), "surface_air_pressure", units="Pa")
        cube.add_aux_coord(delta_coord)
        cube.add_aux_coord(sigma_coord)
        cube.add_aux_coord(surface_air_pressure_coord, (0, 1))
        cube.add_aux_factory(HybridPressureFactory(delta_coord, sigma_coord, surface_air_pressure_coord))

        field = iris.fileformats.pp.PPField3()
        field.lbfc = 0
        field.lbvc = 0
        field.brsvd = [None, None]
        field.lbuser = [None] * 7
        iris.fileformats.pp._ensure_save_rules_loaded()
        iris.fileformats.pp._save_rules.verify(cube, field)

        self.assertEqual(field.bhlev, delta)
        self.assertEqual(field.bhrlev, delta_lower)
        self.assertEqual(field.blev, sigma)
        self.assertEqual(field.brlev, sigma_lower)
        self.assertEqual(field.brsvd, [sigma_upper, delta_upper])
示例#6
0
文件: __init__.py 项目: SciTools/iris
def ocean_sigma_z():
    """
    Return a sample cube with an
    :class:`iris.aux_factory.OceanSigmaZFactory` vertical coordinate.

    This is a fairly small cube with real coordinate arrays.  The coordinate
    values are derived from the sample data linked at
    https://github.com/SciTools/iris/pull/509#issuecomment-23565381.

    """
    co_time = DimCoord([0.0, 1.0], standard_name='time', units='')
    co_lats = DimCoord([-58.1, -52.7, -46.9],
                       standard_name='latitude', units=Unit('degrees'))
    co_lons = DimCoord([65.1,   72.9,   83.7,  96.5],
                       standard_name='longitude', units=Unit('degrees'))
    co_ssh = AuxCoord([[[-0.63157895, -0.52631579, -0.42105263, -0.31578947],
                        [-0.78947368, -0.68421053, -0.57894737, -0.47368421],
                        [-0.94736842, -0.84210526, -0.73684211, -0.63157895]],
                       [[-0.84210526, -0.73684211, -0.63157895, -0.52631579],
                        [-1.00000000, -0.89473684, -0.78947368, -0.68421053],
                        [-1.15789474, -1.05263158, -0.94736842, -0.84210526]]],
                      standard_name=u'sea_surface_height', units=Unit('m'))

    co_sigma = AuxCoord([0., -0.1, -0.6, -1., -1.],
                        standard_name=u'ocean_sigma_z_coordinate',
                        units=Unit('1'),
                        attributes={'positive': 'up'})

    co_zlay = AuxCoord([-137.2, -137.3, -137.4, -368.4, -1495.6],
                       long_name='layer_depth', units=Unit('m'))
    co_depth = AuxCoord([[1625.7, 3921.2, 4106.4, 5243.5],
                         [3615.4, 4942.6, 3883.6, 4823.1],
                         [3263.2, 2816.3, 2741.8, 3883.6]],
                        standard_name=u'depth', units=Unit('m'))
    co_depthc = DimCoord(137.9, long_name='depth_c', units=Unit('m'))
    co_nsigma = DimCoord(3, long_name='nsigma')

    cube = Cube(np.zeros((2, 5, 3, 4)))
    cube.add_dim_coord(co_time, 0)
    cube.add_dim_coord(co_lats, 2)
    cube.add_dim_coord(co_lons, 3)
    cube.add_aux_coord(co_zlay, 1)
    cube.add_aux_coord(co_sigma, 1)
    cube.add_aux_coord(co_ssh, (0, 2, 3))
    cube.add_aux_coord(co_depth, (2, 3))
    cube.add_aux_coord(co_depthc)
    cube.add_aux_coord(co_nsigma)

    fact = iris.aux_factory.OceanSigmaZFactory(
        depth=co_depth, eta=co_ssh, depth_c=co_depthc, zlev=co_zlay,
        sigma=co_sigma, nsigma=co_nsigma)
    cube.add_aux_factory(fact)
    return cube
示例#7
0
def empty_model_level_cube(data, name=None, unit=None, stash=None, **kwargs):
    """
    Create a model_level cube from input data.
    """
    if data is None:
        data = np.empty([1, 3, 8, 6])
    assert data.shape == (3, 8, 6)
    # Make axis=0 for time dim_coord
    new_data = data[np.newaxis, :]
    cube = Cube(new_data)

    # time = AuxCoord([0], 'time', units='hours since epoch')
    time = DimCoord([0], 'time', units='hours since epoch')

    # model = DimCoord([1, 2, 3], 'model_level_number',
    #                  attributes={'positive': 'up'})
    model = DimCoord([1, 2, 3], 'air_pressure',
                     attributes={'positive': 'up'})
    latitude = DimCoord([6.26953125, 6.38671875, 6.50390625, 6.62109375,
                         6.73828125, 6.85546875, 6.97265625, 7.08984375],
                        standard_name='latitude', units='degrees')

    longitude = DimCoord([81.12304688, 81.29882812, 81.47460938,
                          81.65039062, 81.82617188, 82.00195312],
                         standard_name='longitude', units='degrees')

    level_heights = np.array([20., 53.336, 100.])
    level_height = DimCoord(level_heights, long_name='level_height', units='m')
    surface = AuxCoord(topo_aps3.data, 'surface_altitude', units='m')

    sigma = AuxCoord([0.99772321, 0.99393402, 0.98864199], long_name='sigma')

    cube.add_dim_coord(time, 0)
    cube.add_dim_coord(model, 1)
    cube.add_dim_coord(latitude, 2)
    cube.add_dim_coord(longitude, 3)

    cube.add_aux_coord(level_height, 1)
    cube.add_aux_coord(sigma, 1)
    cube.add_aux_coord(surface, (2, 3))

    # Now that we have all of the necessary information, construct a
    # HybridHeight derived "altitude" coordinate.
    cube.add_aux_factory(HybridHeightFactory(level_height, sigma, surface))

    if name:
        cube.long_name = name
    if unit:
        cube.units = unit
    if stash:
        cube.attributes['STASH'] = stash

    return cube
def uk_cube():
    data = np.arange(12, dtype=np.float32).reshape(3, 4)
    uk = Cube(data)
    cs = OSGB()
    y_coord = DimCoord(range(3), 'projection_y_coordinate', units='m',
                       coord_system=cs)
    x_coord = DimCoord(range(4), 'projection_x_coordinate', units='m',
                       coord_system=cs)
    uk.add_dim_coord(y_coord, 0)
    uk.add_dim_coord(x_coord, 1)
    surface = AuxCoord(data * 10, 'surface_altitude', units='m')
    uk.add_aux_coord(surface, (0, 1))
    uk.add_aux_factory(HybridHeightFactory(orography=surface))
    return uk
示例#9
0
def uk_cube():
    data = np.arange(12, dtype=np.float32).reshape(3, 4)
    uk = Cube(data)
    cs = OSGB()
    y_coord = DimCoord(np.arange(3), 'projection_y_coordinate', units='m',
                       coord_system=cs)
    x_coord = DimCoord(np.arange(4), 'projection_x_coordinate', units='m',
                       coord_system=cs)
    uk.add_dim_coord(y_coord, 0)
    uk.add_dim_coord(x_coord, 1)
    surface = AuxCoord(data * 10, 'surface_altitude', units='m')
    uk.add_aux_coord(surface, (0, 1))
    uk.add_aux_factory(HybridHeightFactory(orography=surface))
    return uk
示例#10
0
文件: test_pp.py 项目: wilbertcs/iris
    def test_hybrid_height_with_non_standard_coords(self):
        # Check the save rules are using the AuxFactory to find the
        # hybrid height coordinates and not relying on their names.
        ny, nx = 30, 40
        sigma_lower, sigma, sigma_upper = 0.75, 0.8, 0.75
        delta_lower, delta, delta_upper = 150, 200, 250

        cube = Cube(np.zeros((ny, nx)), "air_temperature")
        level_coord = AuxCoord(0, "model_level_number", units="1")
        cube.add_aux_coord(level_coord)
        delta_coord = AuxCoord(
            delta,
            bounds=[[delta_lower, delta_upper]],
            long_name="moog",
            units="m",
        )
        sigma_coord = AuxCoord(
            sigma,
            bounds=[[sigma_lower, sigma_upper]],
            long_name="mavis",
            units="1",
        )
        surface_altitude_coord = AuxCoord(
            np.zeros((ny, nx)), "surface_altitude", units="m"
        )
        cube.add_aux_coord(delta_coord)
        cube.add_aux_coord(sigma_coord)
        cube.add_aux_coord(surface_altitude_coord, (0, 1))
        cube.add_aux_factory(
            HybridHeightFactory(
                delta_coord, sigma_coord, surface_altitude_coord
            )
        )

        field = iris.fileformats.pp.PPField3()
        field.lbfc = 0
        field.lbvc = 0
        field.brsvd = [None, None]
        field.lbuser = [None] * 7
        field = verify(cube, field)

        self.assertEqual(field.blev, delta)
        self.assertEqual(field.brlev, delta_lower)
        self.assertEqual(field.bhlev, sigma)
        self.assertEqual(field.bhrlev, sigma_lower)
        self.assertEqual(field.brsvd, [delta_upper, sigma_upper])
示例#11
0
def _copy_cube_transformed(src_cube, data, coord_func):
    """
    Returns a new cube based on the src_cube, but with the given data,
    and with the coordinates transformed via coord_func.

    The data must have the same number of dimensions as the source cube.

    """
    from iris.cube import Cube

    assert src_cube.ndim == data.ndim

    # Start with just the metadata and the data...
    new_cube = Cube(data)
    new_cube.metadata = src_cube.metadata
    new_cube.metadata = src_cube.metadata

    # ... and then create all the coordinates.

    # Record a mapping from old coordinate IDs to new coordinates,
    # for subsequent use in creating updated aux_factories.
    coord_mapping = {}

    def copy_coords(source_coords, add_method):
        for coord in source_coords:
            new_coord = coord_func(coord)
            add_method(new_coord, src_cube.coord_dims(coord))
            coord_mapping[id(coord)] = new_coord

    copy_coords(src_cube.dim_coords, new_cube.add_dim_coord)
    copy_coords(src_cube.aux_coords, new_cube.add_aux_coord)

    for factory in src_cube.aux_factories:
        new_cube.add_aux_factory(factory.updated(coord_mapping))

    return new_cube
示例#12
0
def ocean_sigma_z():
    """
    Return a sample cube with an
    :class:`iris.aux_factory.OceanSigmaZFactory` vertical coordinate.

    This is a fairly small cube with real coordinate arrays.  The coordinate
    values are derived from the sample data linked at
    https://github.com/SciTools/iris/pull/509#issuecomment-23565381.

    """
    co_time = DimCoord([0.0, 1.0], standard_name="time", units="")
    co_lats = DimCoord([-58.1, -52.7, -46.9],
                       standard_name="latitude",
                       units=Unit("degrees"))
    co_lons = DimCoord(
        [65.1, 72.9, 83.7, 96.5],
        standard_name="longitude",
        units=Unit("degrees"),
    )
    co_ssh = AuxCoord(
        [
            [
                [-0.63157895, -0.52631579, -0.42105263, -0.31578947],
                [-0.78947368, -0.68421053, -0.57894737, -0.47368421],
                [-0.94736842, -0.84210526, -0.73684211, -0.63157895],
            ],
            [
                [-0.84210526, -0.73684211, -0.63157895, -0.52631579],
                [-1.00000000, -0.89473684, -0.78947368, -0.68421053],
                [-1.15789474, -1.05263158, -0.94736842, -0.84210526],
            ],
        ],
        standard_name="sea_surface_height",
        units=Unit("m"),
    )

    co_sigma = AuxCoord(
        [0.0, -0.1, -0.6, -1.0, -1.0],
        standard_name="ocean_sigma_z_coordinate",
        units=Unit("1"),
        attributes={"positive": "up"},
    )

    co_zlay = AuxCoord(
        [-137.2, -137.3, -137.4, -368.4, -1495.6],
        long_name="layer_depth",
        units=Unit("m"),
    )
    co_depth = AuxCoord(
        [
            [1625.7, 3921.2, 4106.4, 5243.5],
            [3615.4, 4942.6, 3883.6, 4823.1],
            [3263.2, 2816.3, 2741.8, 3883.6],
        ],
        standard_name="depth",
        units=Unit("m"),
    )
    co_depthc = DimCoord(137.9, long_name="depth_c", units=Unit("m"))
    co_nsigma = DimCoord(3, long_name="nsigma")

    cube = Cube(np.zeros((2, 5, 3, 4)))
    cube.add_dim_coord(co_time, 0)
    cube.add_dim_coord(co_lats, 2)
    cube.add_dim_coord(co_lons, 3)
    cube.add_aux_coord(co_zlay, 1)
    cube.add_aux_coord(co_sigma, 1)
    cube.add_aux_coord(co_ssh, (0, 2, 3))
    cube.add_aux_coord(co_depth, (2, 3))
    cube.add_aux_coord(co_depthc)
    cube.add_aux_coord(co_nsigma)

    fact = iris.aux_factory.OceanSigmaZFactory(
        depth=co_depth,
        eta=co_ssh,
        depth_c=co_depthc,
        zlev=co_zlay,
        sigma=co_sigma,
        nsigma=co_nsigma,
    )
    cube.add_aux_factory(fact)
    return cube
示例#13
0
文件: _regrid.py 项目: wilbertcs/iris
    def _create_cube(
        data,
        src,
        x_dim,
        y_dim,
        src_x_coord,
        src_y_coord,
        grid_x_coord,
        grid_y_coord,
        sample_grid_x,
        sample_grid_y,
        regrid_callback,
    ):
        """
        Return a new Cube for the result of regridding the source Cube onto
        the new grid.

        All the metadata and coordinates of the result Cube are copied from
        the source Cube, with two exceptions:
            - Grid dimension coordinates are copied from the grid Cube.
            - Auxiliary coordinates which span the grid dimensions are
              ignored, except where they provide a reference surface for an
              :class:`iris.aux_factory.AuxCoordFactory`.

        Args:

        * data:
            The regridded data as an N-dimensional NumPy array.
        * src:
            The source Cube.
        * x_dim:
            The X dimension within the source Cube.
        * y_dim:
            The Y dimension within the source Cube.
        * src_x_coord:
            The X :class:`iris.coords.DimCoord`.
        * src_y_coord:
            The Y :class:`iris.coords.DimCoord`.
        * grid_x_coord:
            The :class:`iris.coords.DimCoord` for the new grid's X
            coordinate.
        * grid_y_coord:
            The :class:`iris.coords.DimCoord` for the new grid's Y
            coordinate.
        * sample_grid_x:
            A 2-dimensional array of sample X values.
        * sample_grid_y:
            A 2-dimensional array of sample Y values.
        * regrid_callback:
            The routine that will be used to calculate the interpolated
            values of any reference surfaces.

        Returns:
            The new, regridded Cube.

        """
        from iris.cube import Cube

        #
        # XXX: At the moment requires to be a static method as used by
        # experimental regrid_area_weighted_rectilinear_src_and_grid
        #
        # Create a result cube with the appropriate metadata
        result = Cube(data)
        result.metadata = copy.deepcopy(src.metadata)

        # Copy across all the coordinates which don't span the grid.
        # Record a mapping from old coordinate IDs to new coordinates,
        # for subsequent use in creating updated aux_factories.
        coord_mapping = {}

        def copy_coords(src_coords, add_method):
            for coord in src_coords:
                dims = src.coord_dims(coord)
                if coord == src_x_coord:
                    coord = grid_x_coord
                elif coord == src_y_coord:
                    coord = grid_y_coord
                elif x_dim in dims or y_dim in dims:
                    continue
                result_coord = coord.copy()
                add_method(result_coord, dims)
                coord_mapping[id(coord)] = result_coord

        copy_coords(src.dim_coords, result.add_dim_coord)
        copy_coords(src.aux_coords, result.add_aux_coord)

        def regrid_reference_surface(
            src_surface_coord,
            surface_dims,
            x_dim,
            y_dim,
            src_x_coord,
            src_y_coord,
            sample_grid_x,
            sample_grid_y,
            regrid_callback,
        ):
            # Determine which of the reference surface's dimensions span the X
            # and Y dimensions of the source cube.
            surface_x_dim = surface_dims.index(x_dim)
            surface_y_dim = surface_dims.index(y_dim)
            surface = regrid_callback(
                src_surface_coord.points,
                surface_x_dim,
                surface_y_dim,
                src_x_coord,
                src_y_coord,
                sample_grid_x,
                sample_grid_y,
            )
            surface_coord = src_surface_coord.copy(surface)
            return surface_coord

        # Copy across any AuxFactory instances, and regrid their reference
        # surfaces where required.
        for factory in src.aux_factories:
            for coord in factory.dependencies.values():
                if coord is None:
                    continue
                dims = src.coord_dims(coord)
                if x_dim in dims and y_dim in dims:
                    result_coord = regrid_reference_surface(
                        coord,
                        dims,
                        x_dim,
                        y_dim,
                        src_x_coord,
                        src_y_coord,
                        sample_grid_x,
                        sample_grid_y,
                        regrid_callback,
                    )
                    result.add_aux_coord(result_coord, dims)
                    coord_mapping[id(coord)] = result_coord
            try:
                result.add_aux_factory(factory.updated(coord_mapping))
            except KeyError:
                msg = ("Cannot update aux_factory {!r} because of dropped"
                       " coordinates.".format(factory.name()))
                warnings.warn(msg)
        return result
示例#14
0
文件: mock.py 项目: cedadev/cis
def make_mock_cube(lat_dim_length=5, lon_dim_length=3, lon_range=None, alt_dim_length=0, pres_dim_length=0,
                   time_dim_length=0,
                   horizontal_offset=0, altitude_offset=0, pressure_offset=0, time_offset=0, data_offset=0,
                   surf_pres_offset=0,
                   hybrid_ht_len=0, hybrid_pr_len=0, geopotential_height=False, dim_order=None, mask=False):
    """
    Makes a cube of any shape required, with coordinate offsets from the default available. If no arguments are
    given get a 5x3 cube of the form:
        array([[1,2,3],
               [4,5,6],
               [7,8,9],
               [10,11,12],
               [13,14,15]])
        and coordinates in latitude:
            array([ -10, -5, 0, 5, 10 ])
        longitude:
            array([ -5, 0, 5 ])
    :param lat_dim_length: Latitude grid length
    :param lon_dim_length: Longitude grid length
    :param alt_dim_length: Altitude grid length
    :param pres_dim_length: Pressure grid length
    :param time_dim_length: Time grid length
    :param horizontal_offset: Offset from the default grid, in degrees, in lat and lon
    :param altitude_offset: Offset from the default grid in altitude
    :param pressure_offset: Offset from the default grid in pressure
    :param time_offset: Offset from the default grid in time
    :param data_offset: Offset from the default data values
    :param surf_pres_offset: Offset for the optional surface pressure field
    :param hybrid_ht_len: Hybrid height grid length
    :param hybrid_pr_len: Hybrid pressure grid length
    :param geopotential_height: Include a geopotential height field when calcluting a hybrid pressure? (default False)
    :param dim_order: List of 'lat', 'lon', 'alt', 'pres', 'time' in the order in which the dimensions occur
    :param mask: A mask to apply to the data, this should be either a scalar or the same shape as the data
    :return: A cube with well defined data.
    """
    import iris
    from iris.aux_factory import HybridHeightFactory, HybridPressureFactory

    data_size = 1
    DIM_NAMES = ['lat', 'lon', 'alt', 'pres', 'time', 'hybrid_ht', 'hybrid_pr']
    dim_lengths = [lat_dim_length, lon_dim_length, alt_dim_length, pres_dim_length, time_dim_length, hybrid_ht_len,
                   hybrid_pr_len]
    lon_range = lon_range or (-5., 5.)

    if dim_order is None:
        dim_order = list(DIM_NAMES)

    if any([True for d in dim_order if d not in DIM_NAMES]):
        raise ValueError("dim_order contains unrecognised name")

    for idx, dim in enumerate(DIM_NAMES):
        if dim_lengths[idx] == 0 and dim in dim_order:
            del dim_order[dim_order.index(dim)]

    coord_map = {}
    for idx, dim in enumerate(dim_order):
        coord_map[dim] = dim_order.index(dim)
    coord_list = [None] * len(coord_map)

    if lat_dim_length:
        coord_list[coord_map['lat']] = (DimCoord(np.linspace(-10., 10., lat_dim_length) + horizontal_offset,
                                                 standard_name='latitude', units='degrees', var_name='lat'),
                                        coord_map['lat'])
        data_size *= lat_dim_length

    if lon_dim_length:
        coord_list[coord_map['lon']] = (
            DimCoord(np.linspace(lon_range[0], lon_range[1], lon_dim_length) + horizontal_offset,
                     standard_name='longitude', units='degrees', var_name='lon'), coord_map['lon'])
        data_size *= lon_dim_length

    if alt_dim_length:
        coord_list[coord_map['alt']] = (DimCoord(np.linspace(0., 7., alt_dim_length) + altitude_offset,
                                                 standard_name='altitude', units='metres', var_name='alt'),
                                        coord_map['alt'])
        data_size *= alt_dim_length

    if pres_dim_length:
        coord_list[coord_map['pres']] = (DimCoord(np.linspace(0., 7., pres_dim_length) + pressure_offset,
                                                  standard_name='air_pressure', units='hPa', var_name='pres'),
                                         coord_map['pres'])
        data_size *= pres_dim_length

    if time_dim_length:
        t0 = datetime.datetime(1984, 8, 27)
        times = np.array([t0 + datetime.timedelta(days=d + time_offset) for d in range(time_dim_length)])
        time_nums = convert_datetime_to_std_time(times)
        time_bounds = None
        if time_dim_length == 1:
            time_bounds = convert_datetime_to_std_time(np.array([times[0] - datetime.timedelta(days=0.5),
                                                                       times[0] + datetime.timedelta(days=0.5)]))
        coord_list[coord_map['time']] = (DimCoord(time_nums, standard_name='time',
                                                  units='days since 1600-01-01 00:00:00', var_name='time',
                                                  bounds=time_bounds),
                                         coord_map['time'])
        data_size *= time_dim_length

    if hybrid_ht_len:
        coord_list[coord_map['hybrid_ht']] = (DimCoord(np.arange(hybrid_ht_len, dtype='i8') + 10,
                                                       "model_level_number", units="1"), coord_map['hybrid_ht'])
        data_size *= hybrid_ht_len

    if hybrid_pr_len:
        coord_list[coord_map['hybrid_pr']] = (DimCoord(np.arange(hybrid_pr_len, dtype='i8'),
                                                       "atmosphere_hybrid_sigma_pressure_coordinate", units="1"),
                                              coord_map['hybrid_pr'])
        data_size *= hybrid_pr_len

    data = np.reshape(np.arange(data_size) + data_offset + 1., tuple(len(i[0].points) for i in coord_list))

    if mask:
        data = np.ma.asarray(data)
        data.mask = mask

    return_cube = Cube(data, dim_coords_and_dims=coord_list, var_name='rain', standard_name='rainfall_rate',
                       long_name="TOTAL RAINFALL RATE: LS+CONV KG/M2/S", units="kg m-2 s-1")

    if hybrid_ht_len:
        return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_ht_len, dtype='i8') + 40,
                                                       long_name="level_height",
                                                       units="m", var_name='hybrid_ht'), coord_map['hybrid_ht'])
        return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_ht_len, dtype='i8') + 50,
                                                       long_name="sigma", units="1", var_name='sigma'),
                                  coord_map['hybrid_ht'])
        return_cube.add_aux_coord(iris.coords.AuxCoord(
            np.arange(lat_dim_length * lon_dim_length, dtype='i8').reshape(lat_dim_length, lon_dim_length) + 100,
            long_name="surface_altitude",
            units="m"), [coord_map['lat'], coord_map['lon']])

        return_cube.add_aux_factory(HybridHeightFactory(
            delta=return_cube.coord("level_height"),
            sigma=return_cube.coord("sigma"),
            orography=return_cube.coord("surface_altitude")))
    elif hybrid_pr_len:
        return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_pr_len, dtype='i8') + 40,
                                                       long_name="hybrid A coefficient at layer midpoints",
                                                       units="Pa", var_name='a'), coord_map['hybrid_pr'])
        return_cube.add_aux_coord(iris.coords.AuxCoord(np.arange(hybrid_pr_len, dtype='f8') + 50,
                                                       long_name="hybrid B coefficient at layer midpoints", units="1",
                                                       var_name='b'),
                                  coord_map['hybrid_pr'])
        return_cube.add_aux_coord(
            iris.coords.AuxCoord(np.arange(lat_dim_length * lon_dim_length * time_dim_length, dtype='i8')
                                 .reshape(lat_dim_length, lon_dim_length, time_dim_length) * 100000 + surf_pres_offset,
                                 "surface_air_pressure", units="Pa"),
            [coord_map['lat'], coord_map['lon'], coord_map['time']])

        if geopotential_height:
            return_cube.add_aux_coord(iris.coords.AuxCoord(
                np.arange(lat_dim_length * lon_dim_length * time_dim_length * hybrid_pr_len, dtype='i8')
                .reshape(lat_dim_length, lon_dim_length, time_dim_length, hybrid_pr_len) + 10,
                "altitude", long_name="Geopotential height at layer midpoints", units="meter"),
                [coord_map['lat'], coord_map['lon'], coord_map['time'], coord_map['hybrid_pr']])

        return_cube.add_aux_factory(HybridPressureFactory(
            delta=return_cube.coord("hybrid A coefficient at layer midpoints"),
            sigma=return_cube.coord("hybrid B coefficient at layer midpoints"),
            surface_air_pressure=return_cube.coord("surface_air_pressure")))

    for coord in return_cube.coords(dim_coords=True):
        if coord.bounds is None:
            coord.guess_bounds()

    return return_cube