|
191 | 191 | )
|
192 | 192 |
|
193 | 193 | _xxx = namedtuple(
|
194 |
| - "data_dimension", ["size", "axis", "key", "coord", "coord_type", "scalar"] |
| 194 | + "data_dimension", ["size", "axis", "keys", "coords", "coord_type", "scalar"] |
195 | 195 | )
|
196 | 196 |
|
197 | 197 | # _empty_set = set()
|
@@ -1006,35 +1006,63 @@ def _binary_operation(self, other, method):
|
1006 | 1006 |
|
1007 | 1007 | if identity is None and relaxed_identities:
|
1008 | 1008 | identity = coord.identity(relaxed=True, default=None)
|
| 1009 | + |
| 1010 | + keys = (key,) |
| 1011 | + coords = (coord,) |
1009 | 1012 | else:
|
1010 |
| - key, coord = f.auxiliary_coordinate( |
1011 |
| - item=True, |
1012 |
| - default=(None, None), |
1013 |
| - filter_by_axis=(axis,), |
1014 |
| - axis_mode="exact", |
1015 |
| - ) |
1016 |
| - if coord is not None: |
1017 |
| - # This axis of the domain does not have a |
1018 |
| - # dimension coordinate but it does have |
1019 |
| - # exactly one 1-d auxiliary coordinate, so |
1020 |
| - # that will do. |
1021 |
| - identity = coord.identity(strict=True, default=None) |
1022 |
| - |
1023 |
| - if identity is None and relaxed_identities: |
1024 |
| - identity = coord.identity( |
1025 |
| - relaxed=True, default=None |
1026 |
| - ) |
| 1013 | + discrete_axis = f.has_property('featureType') or f.domain_topologies(todict=True) |
| 1014 | + if discrete_axis: |
| 1015 | + x = {} |
| 1016 | + for key, coord in f.auxiliary_coordinates( |
| 1017 | + filter_by_axis=(axis,), |
| 1018 | + axis_mode="exact", todict=True |
| 1019 | + ).items(): |
| 1020 | + identity = coord.identity(strict=True, default=None) |
| 1021 | + if identity is None and relaxed_identities: |
| 1022 | + identity = coord.identity( |
| 1023 | + relaxed=True, default=None |
| 1024 | + ) |
| 1025 | + if identity is not None: |
| 1026 | + x[identity] = key |
| 1027 | + |
| 1028 | + if x: |
| 1029 | + # E.g. {2:3, 4:6, 1:7} -> (1, 2, 4), (7, 3, 6) |
| 1030 | + identity, keys = tuple(zip(*sorted(x.items()))) |
| 1031 | + coords = tuple(f.auxiliary_coordinate(key) for key in keys) |
| 1032 | + else: |
| 1033 | + identity = None |
| 1034 | + keys = None |
| 1035 | + coords = None |
| 1036 | + else: |
| 1037 | + key, coord = f.auxiliary_coordinate( |
| 1038 | + item=True, |
| 1039 | + default=(None, None), |
| 1040 | + filter_by_axis=(axis,), |
| 1041 | + axis_mode="exact", |
| 1042 | + ) |
| 1043 | + if coord is not None: |
| 1044 | + # This axis of the domain does not have a |
| 1045 | + # dimension coordinate but it does have |
| 1046 | + # exactly one 1-d auxiliary coordinate, so |
| 1047 | + # that will do. |
| 1048 | + coords = (coord,) |
| 1049 | + identity = coord.identity(strict=True, default=None) |
| 1050 | + if identity is None and relaxed_identities: |
| 1051 | + identity = coord.identity( |
| 1052 | + relaxed=True, default=None |
| 1053 | + ) |
| 1054 | + |
1027 | 1055 |
|
1028 | 1056 | if identity is None:
|
1029 | 1057 | identity = i
|
1030 | 1058 | else:
|
1031 |
| - coord_type = coord.construct_type |
| 1059 | + coord_type = coords[0].construct_type |
1032 | 1060 |
|
1033 | 1061 | out[identity] = _xxx(
|
1034 | 1062 | size=f.domain_axis(axis).get_size(),
|
1035 | 1063 | axis=axis,
|
1036 |
| - key=key, |
1037 |
| - coord=coord, |
| 1064 | + keys=keys, |
| 1065 | + coords=coords, |
1038 | 1066 | coord_type=coord_type,
|
1039 | 1067 | scalar=(axis not in data_axes),
|
1040 | 1068 | )
|
@@ -1178,48 +1206,49 @@ def _binary_operation(self, other, method):
|
1178 | 1206 | f"{a.size} {identity!r} axis"
|
1179 | 1207 | )
|
1180 | 1208 |
|
1181 |
| - # Ensure matching axis directions |
1182 |
| - if y.coord.direction() != a.coord.direction(): |
1183 |
| - other.flip(y.axis, inplace=True) |
1184 |
| - |
1185 |
| - # Check for matching coordinate values |
1186 |
| - if not y.coord._equivalent_data(a.coord): |
1187 |
| - raise ValueError( |
1188 |
| - f"Can't combine size {y.size} {identity!r} axes with " |
1189 |
| - f"non-matching coordinate values" |
1190 |
| - ) |
1191 |
| - |
1192 |
| - # Check coord refs |
1193 |
| - refs1 = field1.get_coordinate_reference(construct=y.key, key=True) |
1194 |
| - refs0 = field0.get_coordinate_reference(construct=a.key, key=True) |
1195 |
| - |
1196 |
| - n_refs = len(refs1) |
1197 |
| - n0_refs = len(refs0) |
1198 |
| - |
1199 |
| - if n_refs != n0_refs: |
1200 |
| - raise ValueError( |
1201 |
| - f"Can't combine {self.__class__.__name__!r} with " |
1202 |
| - f"{other.__class__.__name__!r} because the coordinate " |
1203 |
| - f"references have different lengths: {n_refs} and " |
1204 |
| - f"{n0_refs}." |
1205 |
| - ) |
1206 |
| - |
1207 |
| - n_equivalent_refs = 0 |
1208 |
| - for ref1 in refs1: |
1209 |
| - for ref0 in refs0[:]: |
1210 |
| - if field1._equivalent_coordinate_references( |
1211 |
| - field0, key0=ref1, key1=ref0, axis_map=axis_map |
1212 |
| - ): |
1213 |
| - n_equivalent_refs += 1 |
1214 |
| - refs0.remove(ref0) |
1215 |
| - break |
1216 |
| - |
1217 |
| - if n_equivalent_refs != n_refs: |
1218 |
| - raise ValueError( |
1219 |
| - f"Can't combine {self.__class__.__name__!r} with " |
1220 |
| - f"{other.__class__.__name__!r} because the fields have " |
1221 |
| - "incompatible coordinate references." |
1222 |
| - ) |
| 1209 | + for a_key, a_coord, y_key, y_coord in zip(a.keys, a.coords, y.keys, y.coords): |
| 1210 | + # Ensure matching axis directions |
| 1211 | + if y_coord.direction() != a_coord.direction(): |
| 1212 | + other.flip(y.axis, inplace=True) |
| 1213 | + |
| 1214 | + # Check for matching coordinate values |
| 1215 | + if not y_coord._equivalent_data(a_coord): |
| 1216 | + raise ValueError( |
| 1217 | + f"Can't combine size {y.size} {identity!r} axes with " |
| 1218 | + f"non-matching coordinate values" |
| 1219 | + ) |
| 1220 | + |
| 1221 | + # Check coord refs |
| 1222 | + refs1 = field1.get_coordinate_reference(construct=y_key, key=True) |
| 1223 | + refs0 = field0.get_coordinate_reference(construct=a_key, key=True) |
| 1224 | + |
| 1225 | + n_refs = len(refs1) |
| 1226 | + n0_refs = len(refs0) |
| 1227 | + |
| 1228 | + if n_refs != n0_refs: |
| 1229 | + raise ValueError( |
| 1230 | + f"Can't combine {self.__class__.__name__!r} with " |
| 1231 | + f"{other.__class__.__name__!r} because the coordinate " |
| 1232 | + f"references have different lengths: {n_refs} and " |
| 1233 | + f"{n0_refs}." |
| 1234 | + ) |
| 1235 | + |
| 1236 | + n_equivalent_refs = 0 |
| 1237 | + for ref1 in refs1: |
| 1238 | + for ref0 in refs0[:]: |
| 1239 | + if field1._equivalent_coordinate_references( |
| 1240 | + field0, key0=ref1, key1=ref0, axis_map=axis_map |
| 1241 | + ): |
| 1242 | + n_equivalent_refs += 1 |
| 1243 | + refs0.remove(ref0) |
| 1244 | + break |
| 1245 | + |
| 1246 | + if n_equivalent_refs != n_refs: |
| 1247 | + raise ValueError( |
| 1248 | + f"Can't combine {self.__class__.__name__!r} with " |
| 1249 | + f"{other.__class__.__name__!r} because the fields have " |
| 1250 | + "incompatible coordinate references." |
| 1251 | + ) |
1223 | 1252 |
|
1224 | 1253 | # Change the domain axis sizes in field0 so that they match
|
1225 | 1254 | # the broadcasted result data
|
@@ -1425,7 +1454,12 @@ def _conform_cell_methods(self):
|
1425 | 1454 | axis_map = {}
|
1426 | 1455 |
|
1427 | 1456 | for cm in self.cell_methods(todict=True).values():
|
1428 |
| - for axis in cm.get_axes(()): |
| 1457 | + |
| 1458 | + axes = cm.get_axis_identities(None) |
| 1459 | + if axes is None: |
| 1460 | + axes = cm.get_axes(None) |
| 1461 | + |
| 1462 | + for axis in axes: |
1429 | 1463 | if axis in axis_map:
|
1430 | 1464 | continue
|
1431 | 1465 |
|
|
0 commit comments