5454 "add_offset" ,
5555 "calendar" ,
5656 "cell_methods" ,
57+ "featureType" ,
5758 "_FillValue" ,
5859 "flag_masks" ,
5960 "flag_meanings" ,
@@ -210,6 +211,7 @@ class _Meta:
210211 (
211212 "Type" ,
212213 "Identity" ,
214+ "featureType" ,
213215 "Units" ,
214216 "Cell_methods" ,
215217 "Data" ,
@@ -389,6 +391,10 @@ def __init__(
389391 if field_identity :
390392 self .identity = f .get_property (field_identity , None )
391393
394+ # Set the DSG featureType
395+ featureType = f .get_property ("featureType" , None )
396+ self .featureType = featureType
397+
392398 construct_axes = f .constructs .data_axes ()
393399
394400 # ------------------------------------------------------------
@@ -502,6 +508,7 @@ def __init__(
502508 "identity" : dim_identity ,
503509 "key" : dim_coord_key ,
504510 "units" : units ,
511+ "cf_role" : None ,
505512 "hasdata" : dim_coord .has_data (),
506513 "hasbounds" : hasbounds ,
507514 "coordrefs" : self .find_coordrefs (axis ),
@@ -539,11 +546,18 @@ def __init__(
539546 aux_coord , aux_identity , relaxed_units = relaxed_units
540547 )
541548
549+ # Set the cf_role for DSGs
550+ if not featureType :
551+ cf_role = None
552+ else :
553+ cf_role = aux_coord .get_property ("cf_role" , None )
554+
542555 info_aux .append (
543556 {
544557 "identity" : aux_identity ,
545558 "key" : key ,
546559 "units" : units ,
560+ "cf_role" : cf_role ,
547561 "hasdata" : aux_coord .has_data (),
548562 "hasbounds" : aux_coord .has_bounds (),
549563 "coordrefs" : self .find_coordrefs (key ),
@@ -586,12 +600,14 @@ def __init__(
586600
587601 return
588602
603+ identity = f"ncvar%{ identity } "
589604 size = domain_axis .get_size ()
590605
591606 axis_identities = {
592607 "ids" : "identity" ,
593608 "keys" : "key" ,
594609 "units" : "units" ,
610+ "cf_role" : "cf_role" ,
595611 "hasdata" : "hasdata" ,
596612 "hasbounds" : "hasbounds" ,
597613 "coordrefs" : "coordrefs" ,
@@ -1773,6 +1789,9 @@ def structural_signature(self):
17731789 Cell_methods = self .cell_methods
17741790 Data = self .has_field_data
17751791
1792+ # DSG FeatureType
1793+ featureType = self .featureType
1794+
17761795 # Properties
17771796 Properties = self .properties
17781797
@@ -1812,6 +1831,7 @@ def structural_signature(self):
18121831 ]
18131832 ),
18141833 ),
1834+ ("cf_role" , axis [identity ]["cf_role" ]),
18151835 ("hasdata" , axis [identity ]["hasdata" ]),
18161836 ("hasbounds" , axis [identity ]["hasbounds" ]),
18171837 ("coordrefs" , axis [identity ]["coordrefs" ]),
@@ -1918,6 +1938,7 @@ def structural_signature(self):
19181938 self .signature = self ._structural_signature (
19191939 Type = Type ,
19201940 Identity = Identity ,
1941+ featureType = featureType ,
19211942 Units = Units ,
19221943 Cell_methods = Cell_methods ,
19231944 Data = Data ,
@@ -4147,6 +4168,15 @@ def _hash_values(m):
41474168
41484169 hash0 = hash1
41494170
4171+ # If 'count' is 0 then all of the 1-d coordinates have the
4172+ # same values across fields. However, for a DSG featureType
4173+ # axis we can still aggregate it, because it's OK to aggregate
4174+ # featureTypes with the timeseries_id, profile_id, or
4175+ # trajectory_id.
4176+ if not count and dsg_feature_type_axis (m0 , axis ):
4177+ a_identity = axis
4178+ count = 1
4179+
41504180 if count == 1 :
41514181 # --------------------------------------------------------
41524182 # Exactly one axis has different 1-d coordinate values
@@ -4248,10 +4278,22 @@ def _hash_values(m):
42484278 # aggregate anything in this entire group.
42494279 # --------------------------------------------------------
42504280 if info :
4251- meta [
4252- 0
4253- ].message = (
4254- "Some fields have identical sets of 1-d coordinates."
4281+ coord_ids = []
4282+ for k , v in m0 .axis .items ():
4283+ coord_ids .extend ([repr (i ) for i in v ["ids" ]])
4284+
4285+ if len (coord_ids ) > 1 :
4286+ coord_ids = (
4287+ f"{ ', ' .join (coord_ids [:- 1 ])} and { coord_ids [- 1 ]} "
4288+ )
4289+ elif coord_ids :
4290+ coord_ids = coord_ids [0 ]
4291+ else :
4292+ coord_ids = ""
4293+
4294+ meta [0 ].message = (
4295+ f"Some fields have identical sets of 1-d { coord_ids } "
4296+ "coordinates."
42554297 )
42564298
42574299 return ()
@@ -4839,6 +4881,19 @@ def _aggregate_2_fields(
48394881
48404882
48414883def f_identity (meta ):
4884+ """Return the field identity for logging strings.
4885+
4886+ :Parameters:
4887+
4888+ meta: `_Meta`
4889+ The `_Meta` instance containing the field.
4890+
4891+ :Returns:
4892+
4893+ `str`
4894+ The identity.
4895+
4896+ """
48424897 identity = meta .identity
48434898 f_identity = meta .field .identity ()
48444899 if f_identity == identity :
@@ -4847,3 +4902,38 @@ def f_identity(meta):
48474902 identity = f"{ meta .identity !r} ({ f_identity } )"
48484903
48494904 return identity
4905+
4906+
4907+ def dsg_feature_type_axis (meta , axis ):
4908+ """True if the given axis is a DSG featureType axis.
4909+
4910+ A DSG featureType axis has no dimension coordinates and at least
4911+ one 1-d auxiliary coordinate with a ``cf-role`` property.
4912+
4913+ :Parameters:
4914+
4915+ meta: `_Meta`
4916+ The `_Meta` instance
4917+
4918+ axis: `str`
4919+ One of the axes in ``meta.axis_ids``.
4920+
4921+ :Returns:
4922+
4923+ `bool`
4924+ `True` if the given axis is a DSG featureType axis.
4925+
4926+ """
4927+ if not meta .featureType :
4928+ # The field/domain is not a DSG
4929+ return False
4930+
4931+ coords = meta .axis [axis ]
4932+ if coords ["dim_coord_index" ] is not None :
4933+ # The axis has dimension coordinates
4934+ return False
4935+
4936+ # Return True if one of the 1-d auxiliary coordinates has a
4937+ # cf_role property
4938+ cf_role = coords ["cf_role" ]
4939+ return cf_role .count (None ) != len (cf_role )
0 commit comments