diff --git a/lib/iris/_merge.py b/lib/iris/_merge.py index 15a6933c49..14d17da6ee 100644 --- a/lib/iris/_merge.py +++ b/lib/iris/_merge.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2012, Met Office +# (C) British Crown Copyright 2010 - 2013, Met Office # # This file is part of Iris. # @@ -582,7 +582,7 @@ def _is_dependent(dependent, independent, positions, function_mapping=None): return valid -def _derive_separable_consistent_groups(relation_matrix, separable_group): +def _derive_consistent_groups(relation_matrix, separable_group): """ Determine the largest combinations of candidate dimensions within the separable group that are self consistently separable from one another. @@ -656,7 +656,7 @@ def _build_separable_group(space, group, separable_consistent_groups, positions, participates in a functional relationship. Returns: - None. + Boolean. """ valid = False @@ -683,8 +683,8 @@ def _build_separable_group(space, group, separable_consistent_groups, positions, if valid: space.update({name: None for name in independent}) space.update({name: tuple(independent) for name in dependent}) - else: - raise iris.exceptions.NotYetImplementedError('No functional relationship between separable and inseparable candidate dimensions.') + + return valid def _build_inseparable_group(space, group, positions, function_matrix): @@ -820,7 +820,8 @@ def derive_space(groups, relation_matrix, positions, function_matrix=None): participates in a functional relationship. Returns: - A space dictionary describing the relationship between each candidate dimension. + A space dictionary describing the relationship between each + candidate dimension. """ space = {} @@ -829,22 +830,33 @@ def derive_space(groups, relation_matrix, positions, function_matrix=None): separable_group = _derive_separable_group(relation_matrix, group) if len(group) == 1 and not separable_group: - # This single candidate dimension is separable from all other candidate dimensions - # in the group, therefore it is a genuine dimension of the space. + # This single candidate dimension is separable from all other + # candidate dimensions in the group, therefore it is a genuine + # dimension of the space. space.update({name: None for name in group}) elif separable_group: - # Determine the largest combination of the candidate dimensions + # Determine the largest combination of the candidate dimensions # in the separable group that are consistently separable. - separable_consistent_groups = _derive_separable_consistent_groups(relation_matrix, separable_group) - _build_separable_group(space, group, separable_consistent_groups, positions, function_matrix) + consistent_groups = _derive_consistent_groups(relation_matrix, + separable_group) + if not _build_separable_group(space, group, consistent_groups, + positions, function_matrix): + # There is no relationship between any of the candidate + # dimensions in the separable group, so merge them together + # into a new combined dimesion of the space. + _build_combination_group(space, group, + positions, function_matrix) else: # Determine whether there is a scalar relationship between one of - # the candidate dimensions and each of the other candidate dimensions - # in this inseparable group. - if not _build_inseparable_group(space, group, positions, function_matrix): - # There is no relationship between any of the candidate dimensions in this - # inseparable group, so merge them together into a new combined dimension of the space. - _build_combination_group(space, group, positions, function_matrix) + # the candidate dimensions and each of the other candidate + # dimensions in this inseparable group. + if not _build_inseparable_group(space, group, + positions, function_matrix): + # There is no relationship between any of the candidate + # dimensions in this inseparable group, so merge them together + # into a new combined dimension of the space. + _build_combination_group(space, group, + positions, function_matrix) return space @@ -1064,7 +1076,8 @@ def _define_space(self, space, positions, indexes, function_matrix): dim_by_name[name] = len(self._shape) self._nd_names.append(name) self._shape.append(len(cells)) - self._cache_by_name[name] = {cell:index for index, cell in enumerate(cells)} + self._cache_by_name[name] = {cell: index for index, cell \ + in enumerate(cells)} else: # TODO: Consider appropriate sort order (ascending, decending) i.e. use CF positive attribute. cells = sorted(indexes[name]) @@ -1086,7 +1099,8 @@ def _define_space(self, space, positions, indexes, function_matrix): else: self._dim_templates.append(_Template(dim, points, bounds, kwargs)) self._shape.append(len(cells)) - self._cache_by_name[name] = {cell:index for index, cell in enumerate(cells)} + self._cache_by_name[name] = {cell: index for index, cell \ + in enumerate(cells)} # Second pass - Build the auxiliary coordinate templates for the space. for name in names: @@ -1202,10 +1216,10 @@ def _build_coordinates(self): # Build the dimension coordinates. for template in self._dim_templates: - # sometimes its not possible to build a dim coord (for example, - # if your bounds are not monotonic, so try building the coordinate, - # and if it fails make the coordinate into a aux coord. This will - # ultimately make an anonymous dimension. + # Sometimes it's not possible to build a dim coordinate e.g. + # the bounds are not monotonic, so try building the coordinate, + # and if it fails make the coordinate into an auxiliary coordinate. + # This will ultimately make an anonymous dimension. try: coord = iris.coords.DimCoord(template.points, bounds=template.bounds, @@ -1214,10 +1228,10 @@ def _build_coordinates(self): except ValueError: self._aux_templates.append(template) - # there is the potential that there are still anonymous dimensions - # get a list of the dimensions which are not anonymous at this stage - covered_dims = [dim_coord_and_dim.dims - for dim_coord_and_dim in dim_coords_and_dims] + # There is the potential that there are still anonymous dimensions. + # Get a list of the dimensions which are not anonymous at this stage. + covered_dims = [dim_coord_and_dim.dims \ + for dim_coord_and_dim in dim_coords_and_dims] # Build the auxiliary coordinates. for template in self._aux_templates: @@ -1271,6 +1285,10 @@ def _add_cube(self, cube, coord_payload): """Create and add the source-cube skeleton to the ProtoCube.""" skeleton = _Skeleton(coord_payload.scalar.values, cube._data) + # Attempt to do something sensible with mixed scalar dtypes. + for i, metadata in enumerate(coord_payload.scalar.metadata): + if metadata.points_dtype > self._coord_metadata[i].points_dtype: + self._coord_metadata[i] = metadata self._skeletons.append(skeleton) def _extract_coord_payload(self, cube): @@ -1301,10 +1319,14 @@ def _extract_coord_payload(self, cube): hint_dict = {name: i for i, name in zip(range(len(self._hints), 0, -1), self._hints[::-1])} # Coordinate axis ordering dictionary. axis_dict = {'T': 0, 'Z': 1, 'Y': 2, 'X': 3} - # Coordinate sort function - by coordinate hint, then by guessed coordinate axis, then - # by coordinate definition, in ascending order. - key_func = lambda coord: (hint_dict.get(coord.name(), len(hint_dict) + 1), - axis_dict.get(iris.util.guess_coord_axis(coord), len(axis_dict) + 1), + # Coordinate sort function. + key_func = lambda coord: (not np.issubdtype(coord.points.dtype, + np.number), + not isinstance(coord, iris.coords.DimCoord), + hint_dict.get(coord.name(), + len(hint_dict) + 1), + axis_dict.get(iris.util.guess_coord_axis(coord), + len(axis_dict) + 1), coord._as_defn()) # Order the coordinates by hints, axis, and definition. diff --git a/lib/iris/etc/pp_rules.txt b/lib/iris/etc/pp_rules.txt index 490e0d45cf..f63172d4af 100644 --- a/lib/iris/etc/pp_rules.txt +++ b/lib/iris/etc/pp_rules.txt @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2012, Met Office +# (C) British Crown Copyright 2010 - 2013, Met Office # # This file is part of Iris. # @@ -356,7 +356,7 @@ f.lbvc == 65 THEN #need orography field to calculate 3D array height coord ###, coord_system=HybridHeightCS(Reference('orography'))) -CoordAndDims(AuxCoord(f.lblev, standard_name='model_level_number', attributes={'positive': 'up'})) +CoordAndDims(DimCoord(f.lblev, standard_name='model_level_number', attributes={'positive': 'up'})) CoordAndDims(DimCoord(f.blev, long_name='level_height', units='m', bounds=[f.brlev, f.brsvd[0]], attributes={'positive': 'up'})) CoordAndDims(AuxCoord(f.bhlev, long_name='sigma', bounds=[f.bhrlev, f.brsvd[1]])) Factory(HybridHeightFactory, [{'long_name': 'level_height'}, {'long_name': 'sigma'}, Reference('orography')]) diff --git a/lib/iris/tests/results/file_load/theta_levels.cml b/lib/iris/tests/results/file_load/theta_levels.cml index b597015916..68b22e83b5 100644 --- a/lib/iris/tests/results/file_load/theta_levels.cml +++ b/lib/iris/tests/results/file_load/theta_levels.cml @@ -30,11 +30,11 @@ - + - + @@ -80,11 +80,11 @@ - + - + @@ -130,11 +130,11 @@ - + - + @@ -180,11 +180,11 @@ - + - + @@ -230,11 +230,11 @@ - + - + @@ -280,11 +280,11 @@ - + - + @@ -330,11 +330,11 @@ - + - + @@ -380,11 +380,11 @@ - + - + @@ -430,11 +430,11 @@ - + - + @@ -480,11 +480,11 @@ - + - + @@ -530,11 +530,11 @@ - + - + @@ -580,11 +580,11 @@ - + - + @@ -630,11 +630,11 @@ - + - + @@ -680,11 +680,11 @@ - + - + @@ -730,11 +730,11 @@ - + - + @@ -780,11 +780,11 @@ - + - + @@ -830,11 +830,11 @@ - + - + @@ -880,11 +880,11 @@ - + - + @@ -930,11 +930,11 @@ - + - + @@ -980,11 +980,11 @@ - + - + @@ -1030,11 +1030,11 @@ - + - + @@ -1080,11 +1080,11 @@ - + - + @@ -1130,11 +1130,11 @@ - + - + @@ -1180,11 +1180,11 @@ - + - + @@ -1230,11 +1230,11 @@ - + - + @@ -1280,11 +1280,11 @@ - + - + @@ -1330,11 +1330,11 @@ - + - + @@ -1380,11 +1380,11 @@ - + - + @@ -1430,11 +1430,11 @@ - + - + @@ -1480,11 +1480,11 @@ - + - + @@ -1530,11 +1530,11 @@ - + - + @@ -1580,11 +1580,11 @@ - + - + @@ -1630,11 +1630,11 @@ - + - + @@ -1680,11 +1680,11 @@ - + - + @@ -1730,11 +1730,11 @@ - + - + @@ -1780,11 +1780,11 @@ - + - + @@ -1830,11 +1830,11 @@ - + - + @@ -1880,11 +1880,11 @@ - + - + diff --git a/lib/iris/tests/results/file_load/u_wind_levels.cml b/lib/iris/tests/results/file_load/u_wind_levels.cml index 2bfdd01cab..b51d171d15 100644 --- a/lib/iris/tests/results/file_load/u_wind_levels.cml +++ b/lib/iris/tests/results/file_load/u_wind_levels.cml @@ -31,11 +31,11 @@ - + - + @@ -82,11 +82,11 @@ - + - + @@ -133,11 +133,11 @@ - + - + @@ -184,11 +184,11 @@ - + - + @@ -235,11 +235,11 @@ - + - + @@ -286,11 +286,11 @@ - + - + @@ -337,11 +337,11 @@ - + - + @@ -388,11 +388,11 @@ - + - + @@ -439,11 +439,11 @@ - + - + @@ -490,11 +490,11 @@ - + - + @@ -541,11 +541,11 @@ - + - + @@ -592,11 +592,11 @@ - + - + @@ -643,11 +643,11 @@ - + - + @@ -694,11 +694,11 @@ - + - + @@ -745,11 +745,11 @@ - + - + @@ -796,11 +796,11 @@ - + - + @@ -847,11 +847,11 @@ - + - + @@ -898,11 +898,11 @@ - + - + @@ -949,11 +949,11 @@ - + - + @@ -1000,11 +1000,11 @@ - + - + @@ -1051,11 +1051,11 @@ - + - + @@ -1102,11 +1102,11 @@ - + - + @@ -1153,11 +1153,11 @@ - + - + @@ -1204,11 +1204,11 @@ - + - + @@ -1255,11 +1255,11 @@ - + - + @@ -1306,11 +1306,11 @@ - + - + @@ -1357,11 +1357,11 @@ - + - + @@ -1408,11 +1408,11 @@ - + - + @@ -1459,11 +1459,11 @@ - + - + @@ -1510,11 +1510,11 @@ - + - + @@ -1561,11 +1561,11 @@ - + - + @@ -1612,11 +1612,11 @@ - + - + @@ -1663,11 +1663,11 @@ - + - + @@ -1714,11 +1714,11 @@ - + - + @@ -1765,11 +1765,11 @@ - + - + @@ -1816,11 +1816,11 @@ - + - + @@ -1867,11 +1867,11 @@ - + - + @@ -1918,11 +1918,11 @@ - + - + diff --git a/lib/iris/tests/results/file_load/v_wind_levels.cml b/lib/iris/tests/results/file_load/v_wind_levels.cml index 0ffb6ca612..a37f8621d0 100644 --- a/lib/iris/tests/results/file_load/v_wind_levels.cml +++ b/lib/iris/tests/results/file_load/v_wind_levels.cml @@ -31,11 +31,11 @@ - + - + @@ -82,11 +82,11 @@ - + - + @@ -133,11 +133,11 @@ - + - + @@ -184,11 +184,11 @@ - + - + @@ -235,11 +235,11 @@ - + - + @@ -286,11 +286,11 @@ - + - + @@ -337,11 +337,11 @@ - + - + @@ -388,11 +388,11 @@ - + - + @@ -439,11 +439,11 @@ - + - + @@ -490,11 +490,11 @@ - + - + @@ -541,11 +541,11 @@ - + - + @@ -592,11 +592,11 @@ - + - + @@ -643,11 +643,11 @@ - + - + @@ -694,11 +694,11 @@ - + - + @@ -745,11 +745,11 @@ - + - + @@ -796,11 +796,11 @@ - + - + @@ -847,11 +847,11 @@ - + - + @@ -898,11 +898,11 @@ - + - + @@ -949,11 +949,11 @@ - + - + @@ -1000,11 +1000,11 @@ - + - + @@ -1051,11 +1051,11 @@ - + - + @@ -1102,11 +1102,11 @@ - + - + @@ -1153,11 +1153,11 @@ - + - + @@ -1204,11 +1204,11 @@ - + - + @@ -1255,11 +1255,11 @@ - + - + @@ -1306,11 +1306,11 @@ - + - + @@ -1357,11 +1357,11 @@ - + - + @@ -1408,11 +1408,11 @@ - + - + @@ -1459,11 +1459,11 @@ - + - + @@ -1510,11 +1510,11 @@ - + - + @@ -1561,11 +1561,11 @@ - + - + @@ -1612,11 +1612,11 @@ - + - + @@ -1663,11 +1663,11 @@ - + - + @@ -1714,11 +1714,11 @@ - + - + @@ -1765,11 +1765,11 @@ - + - + @@ -1816,11 +1816,11 @@ - + - + @@ -1867,11 +1867,11 @@ - + - + @@ -1918,11 +1918,11 @@ - + - + diff --git a/lib/iris/tests/results/file_load/wind_levels.cml b/lib/iris/tests/results/file_load/wind_levels.cml index b53327b191..15ebd44c9c 100644 --- a/lib/iris/tests/results/file_load/wind_levels.cml +++ b/lib/iris/tests/results/file_load/wind_levels.cml @@ -31,11 +31,11 @@ - + - + @@ -82,11 +82,11 @@ - + - + @@ -133,11 +133,11 @@ - + - + @@ -184,11 +184,11 @@ - + - + @@ -235,11 +235,11 @@ - + - + @@ -286,11 +286,11 @@ - + - + @@ -337,11 +337,11 @@ - + - + @@ -388,11 +388,11 @@ - + - + @@ -439,11 +439,11 @@ - + - + @@ -490,11 +490,11 @@ - + - + @@ -541,11 +541,11 @@ - + - + @@ -592,11 +592,11 @@ - + - + @@ -643,11 +643,11 @@ - + - + @@ -694,11 +694,11 @@ - + - + @@ -745,11 +745,11 @@ - + - + @@ -796,11 +796,11 @@ - + - + @@ -847,11 +847,11 @@ - + - + @@ -898,11 +898,11 @@ - + - + @@ -949,11 +949,11 @@ - + - + @@ -1000,11 +1000,11 @@ - + - + @@ -1051,11 +1051,11 @@ - + - + @@ -1102,11 +1102,11 @@ - + - + @@ -1153,11 +1153,11 @@ - + - + @@ -1204,11 +1204,11 @@ - + - + @@ -1255,11 +1255,11 @@ - + - + @@ -1306,11 +1306,11 @@ - + - + @@ -1357,11 +1357,11 @@ - + - + @@ -1408,11 +1408,11 @@ - + - + @@ -1459,11 +1459,11 @@ - + - + @@ -1510,11 +1510,11 @@ - + - + @@ -1561,11 +1561,11 @@ - + - + @@ -1612,11 +1612,11 @@ - + - + @@ -1663,11 +1663,11 @@ - + - + @@ -1714,11 +1714,11 @@ - + - + @@ -1765,11 +1765,11 @@ - + - + @@ -1816,11 +1816,11 @@ - + - + @@ -1867,11 +1867,11 @@ - + - + @@ -1918,11 +1918,11 @@ - + - + @@ -1969,11 +1969,11 @@ - + - + @@ -2020,11 +2020,11 @@ - + - + @@ -2071,11 +2071,11 @@ - + - + @@ -2122,11 +2122,11 @@ - + - + @@ -2173,11 +2173,11 @@ - + - + @@ -2224,11 +2224,11 @@ - + - + @@ -2275,11 +2275,11 @@ - + - + @@ -2326,11 +2326,11 @@ - + - + @@ -2377,11 +2377,11 @@ - + - + @@ -2428,11 +2428,11 @@ - + - + @@ -2479,11 +2479,11 @@ - + - + @@ -2530,11 +2530,11 @@ - + - + @@ -2581,11 +2581,11 @@ - + - + @@ -2632,11 +2632,11 @@ - + - + @@ -2683,11 +2683,11 @@ - + - + @@ -2734,11 +2734,11 @@ - + - + @@ -2785,11 +2785,11 @@ - + - + @@ -2836,11 +2836,11 @@ - + - + @@ -2887,11 +2887,11 @@ - + - + @@ -2938,11 +2938,11 @@ - + - + @@ -2989,11 +2989,11 @@ - + - + @@ -3040,11 +3040,11 @@ - + - + @@ -3091,11 +3091,11 @@ - + - + @@ -3142,11 +3142,11 @@ - + - + @@ -3193,11 +3193,11 @@ - + - + @@ -3244,11 +3244,11 @@ - + - + @@ -3295,11 +3295,11 @@ - + - + @@ -3346,11 +3346,11 @@ - + - + @@ -3397,11 +3397,11 @@ - + - + @@ -3448,11 +3448,11 @@ - + - + @@ -3499,11 +3499,11 @@ - + - + @@ -3550,11 +3550,11 @@ - + - + @@ -3601,11 +3601,11 @@ - + - + @@ -3652,11 +3652,11 @@ - + - + @@ -3703,11 +3703,11 @@ - + - + @@ -3754,11 +3754,11 @@ - + - + @@ -3805,11 +3805,11 @@ - + - + @@ -3856,11 +3856,11 @@ - + - + diff --git a/lib/iris/tests/results/merge/a_aux_b_aux.cml b/lib/iris/tests/results/merge/a_aux_b_aux.cml new file mode 100644 index 0000000000..13c49ddfa3 --- /dev/null +++ b/lib/iris/tests/results/merge/a_aux_b_aux.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/a_aux_b_dim.cml b/lib/iris/tests/results/merge/a_aux_b_dim.cml new file mode 100644 index 0000000000..13c49ddfa3 --- /dev/null +++ b/lib/iris/tests/results/merge/a_aux_b_dim.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/a_dim_b_aux.cml b/lib/iris/tests/results/merge/a_dim_b_aux.cml new file mode 100644 index 0000000000..13c49ddfa3 --- /dev/null +++ b/lib/iris/tests/results/merge/a_dim_b_aux.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/a_dim_b_dim.cml b/lib/iris/tests/results/merge/a_dim_b_dim.cml new file mode 100644 index 0000000000..13c49ddfa3 --- /dev/null +++ b/lib/iris/tests/results/merge/a_dim_b_dim.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/separable_combination.cml b/lib/iris/tests/results/merge/separable_combination.cml new file mode 100644 index 0000000000..9ca61c352c --- /dev/null +++ b/lib/iris/tests/results/merge/separable_combination.cml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/string_a_b.cml b/lib/iris/tests/results/merge/string_a_b.cml new file mode 100644 index 0000000000..f868af4d2d --- /dev/null +++ b/lib/iris/tests/results/merge/string_a_b.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/string_a_with_aux.cml b/lib/iris/tests/results/merge/string_a_with_aux.cml new file mode 100644 index 0000000000..323b9dac80 --- /dev/null +++ b/lib/iris/tests/results/merge/string_a_with_aux.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/string_a_with_dim.cml b/lib/iris/tests/results/merge/string_a_with_dim.cml new file mode 100644 index 0000000000..323b9dac80 --- /dev/null +++ b/lib/iris/tests/results/merge/string_a_with_dim.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/merge/string_b_with_dim.cml b/lib/iris/tests/results/merge/string_b_with_dim.cml new file mode 100644 index 0000000000..111147663f --- /dev/null +++ b/lib/iris/tests/results/merge/string_b_with_dim.cml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/test_merge.py b/lib/iris/tests/test_merge.py index ab0cc8b68a..bf392cd454 100644 --- a/lib/iris/tests/test_merge.py +++ b/lib/iris/tests/test_merge.py @@ -1,4 +1,4 @@ -# (C) British Crown Copyright 2010 - 2012, Met Office +# (C) British Crown Copyright 2010 - 2013, Met Office # # This file is part of Iris. # @@ -27,7 +27,7 @@ import iris import iris.cube import iris.exceptions -from iris.coords import DimCoord +from iris.coords import DimCoord, AuxCoord import iris.coords import iris.tests.stock @@ -179,6 +179,186 @@ def test_multi_split(self): self.assertCML(cube, ('merge', 'multi_split.cml')) +class TestCombination(tests.IrisTest): + def _make_cube(self, a, b, c, d, data=0): + cube_data = np.empty((4, 5), dtype=np.float32) + cube_data[:] = data + cube = iris.cube.Cube(cube_data) + cube.add_dim_coord(DimCoord(np.array([0, 1, 2, 3, 4], dtype=np.int32), + long_name='x', units='1'), 1) + cube.add_dim_coord(DimCoord(np.array([0, 1, 2, 3], dtype=np.int32), + long_name='y', units='1'), 0) + + for name, value in zip(['a', 'b', 'c', 'd'], [a, b, c, d]): + dtype = np.str if isinstance(value, basestring) else np.float32 + cube.add_aux_coord(AuxCoord(np.array([value], dtype=dtype), + long_name=name, units='1')) + + return cube + + def test_separable_combination(self): + cubes = iris.cube.CubeList() + cubes.append(self._make_cube('2005', 'ECMWF', + 'HOPE-E, Sys 1, Met 1, ENSEMBLES', 0)) + cubes.append(self._make_cube('2005', 'ECMWF', + 'HOPE-E, Sys 1, Met 1, ENSEMBLES', 1)) + cubes.append(self._make_cube('2005', 'ECMWF', + 'HOPE-E, Sys 1, Met 1, ENSEMBLES', 2)) + cubes.append(self._make_cube('2026', 'UK Met Office', + 'HadGEM2, Sys 1, Met 1, ENSEMBLES', 0)) + cubes.append(self._make_cube('2026', 'UK Met Office', + 'HadGEM2, Sys 1, Met 1, ENSEMBLES', 1)) + cubes.append(self._make_cube('2026', 'UK Met Office', + 'HadGEM2, Sys 1, Met 1, ENSEMBLES', 2)) + cubes.append(self._make_cube('2002', 'CERFACS', + 'GELATO, Sys 0, Met 1, ENSEMBLES', 0)) + cubes.append(self._make_cube('2002', 'CERFACS', + 'GELATO, Sys 0, Met 1, ENSEMBLES', 1)) + cubes.append(self._make_cube('2002', 'CERFACS', + 'GELATO, Sys 0, Met 1, ENSEMBLES', 2)) + cubes.append(self._make_cube('2002', 'IFM-GEOMAR', + 'ECHAM5, Sys 1, Met 10, ENSEMBLES', 0)) + cubes.append(self._make_cube('2002', 'IFM-GEOMAR', + 'ECHAM5, Sys 1, Met 10, ENSEMBLES', 1)) + cubes.append(self._make_cube('2002', 'IFM-GEOMAR', + 'ECHAM5, Sys 1, Met 10, ENSEMBLES', 2)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 10, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 11, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 12, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 13, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 14, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 15, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 16, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 17, ENSEMBLES', 0)) + cubes.append(self._make_cube('2502', 'UK Met Office', + 'HadCM3, Sys 51, Met 18, ENSEMBLES', 0)) + cube = cubes.merge() + self.assertCML(cube, ('merge', 'separable_combination.cml'), + checksum=False) + + +class TestDimSelection(tests.IrisTest): + def _make_cube(self, a, b, data=0, a_dim=False, b_dim=False): + cube_data = np.empty((4, 5), dtype=np.float32) + cube_data[:] = data + cube = iris.cube.Cube(cube_data) + cube.add_dim_coord(DimCoord(np.array([0, 1, 2, 3, 4], dtype=np.int32), + long_name='x', units='1'), 1) + cube.add_dim_coord(DimCoord(np.array([0, 1, 2, 3], dtype=np.int32), + long_name='y', units='1'), 0) + + for name, value, dim in zip(['a', 'b'], [a, b], [a_dim, b_dim]): + dtype = np.str if isinstance(value, basestring) else np.float32 + ctype = DimCoord if dim else AuxCoord + coord = ctype(np.array([value], dtype=dtype), + long_name=name, units='1') + cube.add_aux_coord(coord) + + return cube + + def test_string_a_with_aux(self): + templates = (('a', 0), ('b', 1), ('c', 2), ('d', 3)) + cubes = [self._make_cube(a, b) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'string_a_with_aux.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), AuxCoord)) + self.assertTrue(isinstance(cube.coord('b'), DimCoord)) + self.assertTrue(cube.coord('b') in cube.dim_coords) + + def test_string_b_with_aux(self): + templates = ((0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')) + cubes = [self._make_cube(a, b) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'string_b_with_aux.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), DimCoord)) + self.assertTrue(cube.coord('a') in cube.dim_coords) + self.assertTrue(isinstance(cube.coord('b'), AuxCoord)) + + def test_string_a_with_dim(self): + templates = (('a', 0), ('b', 1), ('c', 2), ('d', 3)) + cubes = [self._make_cube(a, b, b_dim=True) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'string_a_with_dim.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), AuxCoord)) + self.assertTrue(isinstance(cube.coord('b'), DimCoord)) + self.assertTrue(cube.coord('b') in cube.dim_coords) + + def test_string_b_with_aux(self): + templates = ((0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')) + cubes = [self._make_cube(a, b, a_dim=True) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'string_b_with_dim.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), DimCoord)) + self.assertTrue(cube.coord('a') in cube.dim_coords) + self.assertTrue(isinstance(cube.coord('b'), AuxCoord)) + + def test_string_a_b(self): + templates = (('a', '0'), ('b', '1'), ('c', '2'), ('d', '3')) + cubes = [self._make_cube(a, b) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'string_a_b.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), AuxCoord)) + self.assertTrue(isinstance(cube.coord('b'), AuxCoord)) + + def test_a_aux_b_aux(self): + templates = ((0, 10), (1, 11), (2, 12), (3, 13)) + cubes = [self._make_cube(a, b) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'a_aux_b_aux.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), DimCoord)) + self.assertTrue(cube.coord('a') in cube.dim_coords) + self.assertTrue(isinstance(cube.coord('b'), DimCoord)) + self.assertTrue(cube.coord('b') in cube.aux_coords) + + def test_a_aux_b_dim(self): + templates = ((0, 10), (1, 11), (2, 12), (3, 13)) + cubes = [self._make_cube(a, b, b_dim=True) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'a_aux_b_dim.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), DimCoord)) + self.assertTrue(cube.coord('a') in cube.aux_coords) + self.assertTrue(isinstance(cube.coord('b'), DimCoord)) + self.assertTrue(cube.coord('b') in cube.dim_coords) + + def test_a_dim_b_aux(self): + templates = ((0, 10), (1, 11), (2, 12), (3, 13)) + cubes = [self._make_cube(a, b, a_dim=True) for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'a_dim_b_aux.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), DimCoord)) + self.assertTrue(cube.coord('a') in cube.dim_coords) + self.assertTrue(isinstance(cube.coord('b'), DimCoord)) + self.assertTrue(cube.coord('b') in cube.aux_coords) + + def test_a_dim_b_dim(self): + templates = ((0, 10), (1, 11), (2, 12), (3, 13)) + cubes = [self._make_cube(a, b, a_dim=True, b_dim=True) \ + for a, b in templates] + cube = iris.cube.CubeList(cubes).merge()[0] + self.assertCML(cube, ('merge', 'a_dim_b_dim.cml'), + checksum=False) + self.assertTrue(isinstance(cube.coord('a'), DimCoord)) + self.assertTrue(cube.coord('a') in cube.dim_coords) + self.assertTrue(isinstance(cube.coord('b'), DimCoord)) + self.assertTrue(cube.coord('b') in cube.aux_coords) + + class TestTimeTripleMerging(tests.IrisTest): def _make_cube(self, a, b, c, data=0): cube_data = np.empty((4, 5), dtype=np.float32)