@@ -2311,7 +2311,7 @@ def aggregate(
23112311 **Units**
23122312
23132313 Units must be provided on the conditions where applicable,
2314- since conditions without defined units do not match
2314+ since conditions without defined units will not match
23152315 dimension coordinate constructs with defined units.
23162316
23172317 **Multiple conditions**
@@ -2358,18 +2358,26 @@ def aggregate(
23582358
23592359 >>> x = cf.aggregate(fl, cells=cf.climatology_cells())
23602360
2361+ **Storage of conditions**
2362+
2363+ All returned field or domain constructs that have passed
2364+ dimension coordinate cell conditions will have those
2365+ conditions stored on the appropriate dimension coordinate
2366+ constructs, retrievable via their
2367+ `DimensionCoordinate.get_cell_characteristics` methods.
2368+
23612369 **Performance**
23622370
23632371 The testing of the conditions has a computational
23642372 overhead, as well as an I/O overhead if the dimension
2365- coordinate data are on disk. Try to avoid setting redundant
2366- conditions. For instance, if the inputs comprise monthly mean air
2367- temperature and daily mean precipitation fields, then the
2368- different field identities alone will ensure a correct
2369- aggregation. In this case, adding cell conditions of
2370- ``{'T': [{'cellsize': cf.D()}, {'cellsize': cf.M()}]}``
2371- will not change the result, but tests will still be
2372- carried out.
2373+ coordinate data are on disk. Try to avoid setting
2374+ redundant conditions. For instance, if the inputs comprise
2375+ monthly mean air temperature and daily mean precipitation
2376+ fields, then the different field identities alone will
2377+ ensure a correct aggregation. In this case, adding cell
2378+ conditions of ``{'T': [{'cellsize': cf.D()}, {'cellsize':
2379+ cf.M()}]}`` will not change the result, but tests will
2380+ still be carried out.
23732381
23742382 When setting a sequence of conditions, performance will be
23752383 improved if the conditions towards the beginning of the
@@ -2489,9 +2497,9 @@ def aggregate(
24892497 # Initialise the cache of canonical metadata attributes
24902498 canonical = _Canonical ()
24912499
2492- output_constructs = []
2493-
2494- output_constructs_append = output_constructs . append
2500+ output_meta = []
2501+ output_meta_append = output_meta . append
2502+ output_meta_extend = output_meta . extend
24952503
24962504 if exclude :
24972505 exclude = " NOT"
@@ -2655,10 +2663,10 @@ def aggregate(
26552663 # This field does not have a structural signature, so
26562664 # it can't be aggregated. Put it straight into the
26572665 # output list and move on to the next input construct.
2658- if not copy :
2659- output_constructs_append ( f )
2660- else :
2661- output_constructs_append ( f . copy () )
2666+ if copy :
2667+ meta = meta . copy ( )
2668+
2669+ output_meta_append ( meta )
26622670
26632671 continue
26642672
@@ -2724,11 +2732,10 @@ def aggregate(
27242732 # add it straight to the output list and move on to the
27252733 # next signature.
27262734 # --------------------------------------------------------
2727- if not copy :
2728- output_constructs_append (meta [0 ].field )
2729- else :
2730- output_constructs_append (meta [0 ].field .copy ())
2735+ if copy :
2736+ meta [0 ] = meta [0 ].copy ()
27312737
2738+ output_meta_append (meta [0 ])
27322739 continue
27332740
27342741 if not relaxed_units and not meta [0 ].units .isvalid :
@@ -2741,9 +2748,9 @@ def aggregate(
27412748
27422749 if not exclude :
27432750 if copy :
2744- output_constructs . extend ( m . field .copy () for m in meta )
2751+ output_meta_extend ( m .copy () for m in meta )
27452752 else :
2746- output_constructs . extend ( m . field for m in meta )
2753+ output_meta_extend ( meta )
27472754
27482755 continue
27492756
@@ -3035,11 +3042,16 @@ def aggregate(
30353042 status = 1
30363043 if not exclude :
30373044 if copy :
3038- output_constructs . extend (( m . field . copy () for m in meta0 ) )
3045+ output_meta_extend ( m . copy () for m in meta0 )
30393046 else :
3040- output_constructs . extend (( m . field for m in meta0 ) )
3047+ output_meta_extend ( meta0 )
30413048 else :
3042- output_constructs .extend ((m .field for m in meta ))
3049+ output_meta_extend (meta )
3050+
3051+ if cells :
3052+ _set_cell_conditions (output_meta )
3053+
3054+ output_constructs = [m .field for m in output_meta ]
30433055
30443056 aggregate .status = status
30453057
@@ -3058,6 +3070,52 @@ def aggregate(
30583070 return output_constructs
30593071
30603072
3073+ def _set_cell_conditions (output_meta ):
3074+ """Store the cell characteristics from any cell conditions.
3075+
3076+ The cell size and cell spacing characteristics are stored on the
3077+ appropriate dimension coordinate constructs.
3078+
3079+ .. versionadded:: 3.15.4
3080+
3081+ :Parameters:
3082+
3083+ output_meta: `list`
3084+ The list of `_Meta` objects, each of which contains an
3085+ output field or domain construct. The field or constructs
3086+ are updated in-place.
3087+
3088+ :Returns:
3089+
3090+ `None`
3091+
3092+ """
3093+ for m in output_meta :
3094+ for value in m .axis .values ():
3095+ dim_index = value ["dim_coord_index" ]
3096+ if dim_index is None :
3097+ # There is no dimension coordinate construct for this
3098+ # axis
3099+ continue
3100+
3101+ cellsize = value ["cellsize" ][dim_index ]
3102+ if cellsize is None :
3103+ # There is no cell size condition
3104+ continue
3105+
3106+ spacing = value ["spacing" ][dim_index ]
3107+ if spacing is None :
3108+ # There is no cell spacing condition
3109+ continue
3110+
3111+ # Set the cell conditions on the dimension coordinate
3112+ # construct
3113+ dim_coord = m .field .dimension_coordinate (value ["keys" ][dim_index ])
3114+ dim_coord .set_cell_characteristics (
3115+ cellsize = cellsize , spacing = spacing
3116+ )
3117+
3118+
30613119# --------------------------------------------------------------------
30623120# Initialise the status
30633121# --------------------------------------------------------------------
@@ -3179,7 +3237,7 @@ def climatology_cells(
31793237 {'cellsize': <CF Query: (isclose 12 hour)>},
31803238 {'cellsize': <CF TimeDuration: P1M (Y-M-01 00:00:00)>}]}
31813239
3182- Add a condition that separately aggregates decadal data:
3240+ Add a condition for decadal data:
31833241
31843242 >>> cells['T'].append({'cellsize': cf.wi(3600, 3660, 'day')})
31853243 >>> cells
@@ -4169,6 +4227,7 @@ def _aggregate_2_fields(
41694227 verbose = None ,
41704228 concatenate = True ,
41714229 data_concatenation = None ,
4230+ cell_conditions = None ,
41724231 relaxed_units = False ,
41734232 copy = True ,
41744233):
@@ -4192,7 +4251,8 @@ def _aggregate_2_fields(
41924251
41934252 data_concatenation: `dict`
41944253 The dictionary that contains the data arrays for each
4195- construct type that will need concatenating.
4254+ construct type that will need concatenating. Will be
4255+ updated in-place.
41964256
41974257 .. versionadded:: 3.15.1
41984258
@@ -4248,10 +4308,6 @@ def _aggregate_2_fields(
42484308 hash_values1 = m1 .hash_values [a_identity ]
42494309
42504310 for i , (hash0 , hash1 ) in enumerate (zip (hash_values0 , hash_values1 )):
4251- # try:
4252- # hash_values0[i].append(hash_values1[i])
4253- # except AttributeError:
4254- # hash_values0[i] = [hash_values0[i], hash_values1[i]]
42554311 hash_values0 [i ] = hash_values0 [i ] + hash_values1 [i ]
42564312
42574313 # N-d auxiliary coordinates
0 commit comments