@@ -160,6 +160,39 @@ def name(self):
160160 return self .defn .name ()
161161
162162
163+ class _DerivedCoordAndDims (
164+ namedtuple ("DerivedCoordAndDims" , ["coord" , "dims" , "aux_factory" ])
165+ ):
166+ """
167+ Container for a derived coordinate, the associated AuxCoordFactory, and the
168+ associated data dimension(s) spanned over a :class:`iris.cube.Cube`.
169+
170+ Args:
171+
172+ * coord:
173+ A :class:`iris.coords.DimCoord` or :class:`iris.coords.AuxCoord`
174+ coordinate instance.
175+
176+ * dims:
177+ A tuple of the data dimension(s) spanned by the coordinate.
178+
179+ * aux_factory:
180+ A :class:`iris.aux_factory.AuxCoordFactory` instance.
181+
182+ """
183+
184+ __slots__ = ()
185+
186+ def __eq__ (self , other ):
187+ """Do not take aux factories into account for equality."""
188+ result = NotImplemented
189+ if isinstance (other , _DerivedCoordAndDims ):
190+ equal_coords = self .coord == other .coord
191+ equal_dims = self .dims == other .dims
192+ result = equal_coords and equal_dims
193+ return result
194+
195+
163196class _OtherMetaData (namedtuple ("OtherMetaData" , ["defn" , "dims" ])):
164197 """
165198 Container for the metadata that defines a cell measure or ancillary
@@ -280,6 +313,7 @@ def concatenate(
280313 check_aux_coords = True ,
281314 check_cell_measures = True ,
282315 check_ancils = True ,
316+ check_derived_coords = True ,
283317):
284318 """
285319 Concatenate the provided cubes over common existing dimensions.
@@ -296,6 +330,30 @@ def concatenate(
296330 If True, raise an informative
297331 :class:`~iris.exceptions.ContatenateError` if registration fails.
298332
333+ * check_aux_coords
334+ Checks if the points and bounds of auxiliary coordinates of the cubes
335+ match. This check is not applied to auxiliary coordinates that span the
336+ dimension the concatenation is occurring along. Defaults to True.
337+
338+ * check_cell_measures
339+ Checks if the data of cell measures of the cubes match. This check is
340+ not applied to cell measures that span the dimension the concatenation
341+ is occurring along. Defaults to True.
342+
343+ * check_ancils
344+ Checks if the data of ancillary variables of the cubes match. This
345+ check is not applied to ancillary variables that span the dimension the
346+ concatenation is occurring along. Defaults to True.
347+
348+ * check_derived_coords
349+ Checks if the points and bounds of derived coordinates of the cubes
350+ match. This check is not applied to derived coordinates that span the
351+ dimension the concatenation is occurring along. Note that differences
352+ in scalar coordinates and dimensional coordinates used to derive the
353+ coordinate are still checked. Checks for auxiliary coordinates used to
354+ derive the coordinates can be ignored with `check_aux_coords`. Defaults
355+ to True.
356+
299357 Returns:
300358 A :class:`iris.cube.CubeList` of concatenated :class:`iris.cube.Cube`
301359 instances.
@@ -321,6 +379,7 @@ def concatenate(
321379 check_aux_coords ,
322380 check_cell_measures ,
323381 check_ancils ,
382+ check_derived_coords ,
324383 )
325384 if registered :
326385 axis = proto_cube .axis
@@ -378,6 +437,8 @@ def __init__(self, cube):
378437 self .cm_metadata = []
379438 self .ancillary_variables_and_dims = []
380439 self .av_metadata = []
440+ self .derived_coords_and_dims = []
441+ self .derived_metadata = []
381442 self .dim_mapping = []
382443
383444 # Determine whether there are any anonymous cube dimensions.
@@ -437,6 +498,17 @@ def meta_key_func(dm):
437498 av_and_dims = _CoordAndDims (av , tuple (dims ))
438499 self .ancillary_variables_and_dims .append (av_and_dims )
439500
501+ def name_key_func (factory ):
502+ return factory .name ()
503+
504+ for factory in sorted (cube .aux_factories , key = name_key_func ):
505+ coord = factory .make_coord (cube .coord_dims )
506+ dims = cube .coord_dims (coord )
507+ metadata = _CoordMetaData (coord , dims )
508+ self .derived_metadata .append (metadata )
509+ coord_and_dims = _DerivedCoordAndDims (coord , tuple (dims ), factory )
510+ self .derived_coords_and_dims .append (coord_and_dims )
511+
440512 def _coordinate_differences (self , other , attr , reason = "metadata" ):
441513 """
442514 Determine the names of the coordinates that differ between `self` and
@@ -544,6 +616,14 @@ def match(self, other, error_on_mismatch):
544616 msgs .append (
545617 msg_template .format ("Ancillary variables" , * differences )
546618 )
619+ # Check derived coordinates.
620+ if self .derived_metadata != other .derived_metadata :
621+ differences = self ._coordinate_differences (
622+ other , "derived_metadata"
623+ )
624+ msgs .append (
625+ msg_template .format ("Derived coordinates" , * differences )
626+ )
547627 # Check scalar coordinates.
548628 if self .scalar_coords != other .scalar_coords :
549629 differences = self ._coordinate_differences (
@@ -597,6 +677,7 @@ def __init__(self, cube_signature):
597677 self .ancillary_variables_and_dims = (
598678 cube_signature .ancillary_variables_and_dims
599679 )
680+ self .derived_coords_and_dims = cube_signature .derived_coords_and_dims
600681 self .dim_coords = cube_signature .dim_coords
601682 self .dim_mapping = cube_signature .dim_mapping
602683 self .dim_extents = []
@@ -779,6 +860,11 @@ def concatenate(self):
779860 # Concatenate the new ancillary variables
780861 ancillary_variables_and_dims = self ._build_ancillary_variables ()
781862
863+ # Concatenate the new aux factories
864+ aux_factories = self ._build_aux_factories (
865+ dim_coords_and_dims , aux_coords_and_dims
866+ )
867+
782868 # Concatenate the new data payload.
783869 data = self ._build_data ()
784870
@@ -790,6 +876,7 @@ def concatenate(self):
790876 aux_coords_and_dims = aux_coords_and_dims ,
791877 cell_measures_and_dims = cell_measures_and_dims ,
792878 ancillary_variables_and_dims = ancillary_variables_and_dims ,
879+ aux_factories = aux_factories ,
793880 ** kwargs ,
794881 )
795882 else :
@@ -807,6 +894,7 @@ def register(
807894 check_aux_coords = False ,
808895 check_cell_measures = False ,
809896 check_ancils = False ,
897+ check_derived_coords = False ,
810898 ):
811899 """
812900 Determine whether the given source-cube is suitable for concatenation
@@ -827,6 +915,31 @@ def register(
827915 * error_on_mismatch:
828916 If True, raise an informative error if registration fails.
829917
918+ * check_aux_coords
919+ Checks if the points and bounds of auxiliary coordinates of the
920+ cubes match. This check is not applied to auxiliary coordinates
921+ that span the dimension the concatenation is occurring along.
922+ Defaults to False.
923+
924+ * check_cell_measures
925+ Checks if the data of cell measures of the cubes match. This check
926+ is not applied to cell measures that span the dimension the
927+ concatenation is occurring along. Defaults to False.
928+
929+ * check_ancils
930+ Checks if the data of ancillary variables of the cubes match. This
931+ check is not applied to ancillary variables that span the dimension
932+ the concatenation is occurring along. Defaults to False.
933+
934+ * check_derived_coords
935+ Checks if the points and bounds of derived coordinates of the cubes
936+ match. This check is not applied to derived coordinates that span
937+ the dimension the concatenation is occurring along. Note that
938+ differences in scalar coordinates and dimensional coordinates used
939+ to derive the coordinate are still checked. Checks for auxiliary
940+ coordinates used to derive the coordinates can be ignored with
941+ `check_aux_coords`. Defaults to False.
942+
830943 Returns:
831944 Boolean.
832945
@@ -905,6 +1018,21 @@ def register(
9051018 if not coord_a == coord_b :
9061019 match = False
9071020
1021+ # Check for compatible derived coordinates.
1022+ if match :
1023+ if check_derived_coords :
1024+ for coord_a , coord_b in zip (
1025+ self ._cube_signature .derived_coords_and_dims ,
1026+ cube_signature .derived_coords_and_dims ,
1027+ ):
1028+ # Derived coords that span the candidate axis can differ
1029+ if (
1030+ candidate_axis not in coord_a .dims
1031+ or candidate_axis not in coord_b .dims
1032+ ):
1033+ if not coord_a == coord_b :
1034+ match = False
1035+
9081036 if match :
9091037 # Register the cube as a source-cube for this proto-cube.
9101038 self ._add_skeleton (coord_signature , cube .lazy_data ())
@@ -1088,6 +1216,64 @@ def _build_ancillary_variables(self):
10881216
10891217 return ancillary_variables_and_dims
10901218
1219+ def _build_aux_factories (self , dim_coords_and_dims , aux_coords_and_dims ):
1220+ """
1221+ Generate the aux factories for the new concatenated cube.
1222+
1223+ Args:
1224+
1225+ * dim_coords_and_dims:
1226+ A list of dimension coordinate and dimension tuple pairs from the
1227+ concatenated cube.
1228+
1229+ * aux_coords_and_dims:
1230+ A list of auxiliary coordinates and dimension(s) tuple pairs from
1231+ the concatenated cube.
1232+
1233+ Returns:
1234+ A list of :class:`iris.aux_factory.AuxCoordFactory`.
1235+
1236+ """
1237+ # Setup convenience hooks.
1238+ cube_signature = self ._cube_signature
1239+ old_dim_coords = cube_signature .dim_coords
1240+ old_aux_coords = [a [0 ] for a in cube_signature .aux_coords_and_dims ]
1241+ new_dim_coords = [d [0 ] for d in dim_coords_and_dims ]
1242+ new_aux_coords = [a [0 ] for a in aux_coords_and_dims ]
1243+ scalar_coords = cube_signature .scalar_coords
1244+
1245+ aux_factories = []
1246+
1247+ # Generate all the factories for the new concatenated cube.
1248+ for i , (coord , dims , factory ) in enumerate (
1249+ cube_signature .derived_coords_and_dims
1250+ ):
1251+ # Check whether the derived coordinate of the factory spans the
1252+ # nominated dimension of concatenation.
1253+ if self .axis in dims :
1254+ # Update the dependencies of the factory with coordinates of
1255+ # the concatenated cube. We need to check all coordinate types
1256+ # here (dim coords, aux coords, and scalar coords).
1257+ new_dependencies = {}
1258+ for old_dependency in factory .dependencies .values ():
1259+ if old_dependency in old_dim_coords :
1260+ dep_idx = old_dim_coords .index (old_dependency )
1261+ new_dependency = new_dim_coords [dep_idx ]
1262+ elif old_dependency in old_aux_coords :
1263+ dep_idx = old_aux_coords .index (old_dependency )
1264+ new_dependency = new_aux_coords [dep_idx ]
1265+ else :
1266+ dep_idx = scalar_coords .index (old_dependency )
1267+ new_dependency = scalar_coords [dep_idx ]
1268+ new_dependencies [id (old_dependency )] = new_dependency
1269+
1270+ # Create new factory with the updated dependencies.
1271+ factory = factory .updated (new_dependencies )
1272+
1273+ aux_factories .append (factory )
1274+
1275+ return aux_factories
1276+
10911277 def _build_data (self ):
10921278 """
10931279 Generate the data payload for the new concatenated cube.
0 commit comments