@@ -103,6 +103,112 @@ def add_cell_measure(
103103 )
104104
105105
106+ def find_matching_coord_dims (
107+ coord_to_match : iris .coords .DimCoord ,
108+ cube : iris .cube .Cube ,
109+ ) -> tuple [int ] | None :
110+ """Find a matching coordinate from the ancillary variable in the cube.
111+
112+ Parameters
113+ ----------
114+ coord_to_match: iris.coords.DimCoord
115+ Coordinate from an ancillary cube to match.
116+ cube: iris.cube.Cube
117+ Iris cube with variable data.
118+
119+ Returns
120+ -------
121+ cube_dims: tuple or None
122+ Tuple containing the matched cube coordinate dimension for the
123+ coordinate from the ancillary cube in the data cube. If no match
124+ is found, None is returned.
125+ """
126+ cube_dims = None
127+ for cube_coord in cube .coords ():
128+ if (
129+ (
130+ cube_coord .var_name == coord_to_match .var_name
131+ or (
132+ coord_to_match .standard_name is not None
133+ and cube_coord .standard_name
134+ == coord_to_match .standard_name
135+ )
136+ or (
137+ coord_to_match .long_name is not None
138+ and cube_coord .long_name == coord_to_match .long_name
139+ )
140+ )
141+ and cube_coord .units == coord_to_match .units
142+ and cube_coord .shape == coord_to_match .shape
143+ ):
144+ cube_dims = cube .coord_dims (cube_coord )
145+ msg = (
146+ f"Found a matching coordinate for { coord_to_match .var_name } "
147+ f" with coordinate { cube_coord .var_name } "
148+ f" in the cube of variable '{ cube .var_name } '."
149+ )
150+ logger .debug (msg )
151+ break
152+ return cube_dims
153+
154+
155+ def get_data_dims (
156+ cube : Cube ,
157+ ancillary : Cube | iris .coords .AncillaryVariable ,
158+ ) -> list [None | int ]:
159+ """Get matching data dimensions between cube and ancillary variable.
160+
161+ Parameters
162+ ----------
163+ cube: iris.cube.Cube
164+ Iris cube with input data.
165+ ancillary: Cube or iris.coords.AncillaryVariable
166+ Iris cube or AncillaryVariable with ancillary data.
167+
168+ Returns
169+ -------
170+ data_dims: list
171+ List with as many entries as the ancillary variable dimensions.
172+ The i-th entry corresponds to the i-th ancillary variable
173+ dimension match with the cube's dimensions. If there is no match
174+ between the ancillary variable and the cube for a dimension, then entry
175+ defaults to None.
176+ """
177+ # Match the coordinates of the ancillary cube to coordinates and
178+ # dimensions in the input cube before adding the ancillary variable.
179+ data_dims : list [None | int ] = []
180+ if isinstance (ancillary , iris .coords .AncillaryVariable ):
181+ start_dim = cube .ndim - len (ancillary .shape )
182+ data_dims = list (range (start_dim , cube .ndim ))
183+ else :
184+ data_dims = [None ] * ancillary .ndim
185+ for coord in ancillary .coords ():
186+ try :
187+ cube_dims = cube .coord_dims (coord )
188+ except iris .exceptions .CoordinateNotFoundError :
189+ cube_dims = find_matching_coord_dims (coord , cube )
190+ if cube_dims is not None :
191+ for ancillary_dim , cube_dim in zip (
192+ ancillary .coord_dims (coord ),
193+ cube_dims ,
194+ strict = True ,
195+ ):
196+ data_dims [ancillary_dim ] = cube_dim
197+ if None in data_dims :
198+ none_dims = ", " .join (
199+ str (i ) for i , d in enumerate (data_dims ) if d is None
200+ )
201+ msg = (
202+ f"Failed to add\n { ancillary } \n as ancillary var "
203+ f"to the cube\n { cube } \n "
204+ f"Mismatch between ancillary cube and variable cube coordinate"
205+ f" { none_dims } "
206+ )
207+ logger .error (msg )
208+ raise iris .exceptions .CoordinateNotFoundError (msg )
209+ return data_dims
210+
211+
106212def add_ancillary_variable (
107213 cube : Cube ,
108214 ancillary_cube : Cube | iris .coords .AncillaryVariable ,
@@ -132,42 +238,12 @@ def add_ancillary_variable(
132238 )
133239 except AttributeError as err :
134240 msg = (
135- f"Failed to add { ancillary_cube } to { cube } as ancillary var. "
241+ f"Failed to add\n { ancillary_cube } \n as ancillary var to the cube \n { cube } \n "
136242 "ancillary_cube should be either an iris.cube.Cube or an "
137243 "iris.coords.AncillaryVariable object."
138244 )
139245 raise ValueError (msg ) from err
140- # Match the coordinates of the ancillary cube to coordinates and
141- # dimensions in the input cube before adding the ancillary variable.
142- data_dims : list [int | None ] = []
143- if isinstance (ancillary_cube , iris .coords .AncillaryVariable ):
144- start_dim = cube .ndim - len (ancillary_var .shape )
145- data_dims = list (range (start_dim , cube .ndim ))
146- else :
147- data_dims = [None ] * ancillary_cube .ndim
148- for coord in ancillary_cube .coords ():
149- try :
150- for ancillary_dim , cube_dim in zip (
151- ancillary_cube .coord_dims (coord ),
152- cube .coord_dims (coord ),
153- strict = False ,
154- ):
155- data_dims [ancillary_dim ] = cube_dim
156- except iris .exceptions .CoordinateNotFoundError :
157- logger .debug (
158- "%s from ancillary cube not found in cube coords." ,
159- coord ,
160- )
161- if None in data_dims :
162- none_dims = ", " .join (
163- str (i ) for i , d in enumerate (data_dims ) if d is None
164- )
165- msg = (
166- f"Failed to add { ancillary_cube } to { cube } as ancillary var."
167- f"No coordinate associated with ancillary cube dimensions"
168- f"{ none_dims } "
169- )
170- raise ValueError (msg )
246+ data_dims = get_data_dims (cube , ancillary_cube )
171247 if ancillary_cube .has_lazy_data ():
172248 cube_chunks = tuple (cube .lazy_data ().chunks [d ] for d in data_dims )
173249 ancillary_var .data = ancillary_cube .lazy_data ().rechunk (cube_chunks )
0 commit comments