1414from __future__ import annotations
1515
1616import threading
17- from typing import TYPE_CHECKING , List
17+ from typing import TYPE_CHECKING , Any , Dict , List
1818
1919if TYPE_CHECKING :
2020 from iris .cube import Cube , CubeList
@@ -35,11 +35,15 @@ class CombineOptions(threading.local):
3535 -----
3636 The individual configurable options are :
3737
38+ * ``equalise_cubes_kwargs`` = (dict)
39+ Specifies keywords for a :func:`iris.util.equalise_cubes` call, to be applied
40+ before any merge/concatenate step.
41+
3842 * ``merge_concat_sequence`` = "m" / "c" / "cm" / "mc"
3943 Specifies whether to apply :meth:`~iris.cube.CubeList.merge`, or
4044 :meth:`~iris.cube.CubeList.concatenate` operations, or both, in either order.
4145
42- * ``merge_uses_unique `` = True / False
46+ * ``merge_unique `` = True / False
4347 When True, any merge operation will error if its result contains multiple
4448 identical cubes. Otherwise (unique=False), that is a permitted result.
4549
@@ -90,29 +94,40 @@ class CombineOptions(threading.local):
9094 # Useful constants
9195 #: Valid option names
9296 OPTION_KEYS = [
97+ "equalise_cubes_kwargs" , # N.B. gets special treatment in options checking
9398 "merge_concat_sequence" ,
99+ "merge_unique" ,
94100 "repeat_until_unchanged" ,
95101 ] # this is a list, so we can update it in an inheriting class
96102 _OPTIONS_ALLOWED_VALUES = {
97103 "merge_concat_sequence" : ("" , "m" , "c" , "mc" , "cm" ),
104+ "merge_unique" : (True , False ),
98105 "repeat_until_unchanged" : (False , True ),
99106 }
100- #: Settings content
101- SETTINGS = {
107+ #: Standard settings dictionaries
108+ SETTINGS : Dict [ str , Dict [ str , Any ]] = {
102109 "legacy" : dict (
110+ equalise_cubes_kwargs = None ,
103111 merge_concat_sequence = "m" ,
112+ merge_unique = False ,
104113 repeat_until_unchanged = False ,
105114 ),
106115 "default" : dict (
116+ equalise_cubes_kwargs = None ,
107117 merge_concat_sequence = "m" ,
118+ merge_unique = False ,
108119 repeat_until_unchanged = False ,
109120 ),
110121 "recommended" : dict (
122+ equalise_cubes_kwargs = None ,
111123 merge_concat_sequence = "mc" ,
124+ merge_unique = False ,
112125 repeat_until_unchanged = False ,
113126 ),
114127 "comprehensive" : dict (
128+ equalise_cubes_kwargs = {"apply_all" : True },
115129 merge_concat_sequence = "mc" ,
130+ merge_unique = False ,
116131 repeat_until_unchanged = True ,
117132 ),
118133 }
@@ -128,13 +143,14 @@ def __setattr__(self, key, value):
128143 if key not in self .OPTION_KEYS :
129144 raise KeyError (f"LoadPolicy object has no property '{ key } '." )
130145
131- allowed_values = self ._OPTIONS_ALLOWED_VALUES [key ]
132- if value not in allowed_values :
133- msg = (
134- f"{ value !r} is not a valid setting for LoadPolicy.{ key } : "
135- f"must be one of '{ allowed_values } '."
136- )
137- raise ValueError (msg )
146+ if key != "equalise_cubes_kwargs" :
147+ allowed_values = self ._OPTIONS_ALLOWED_VALUES [key ]
148+ if value not in allowed_values :
149+ msg = (
150+ f"{ value !r} is not a valid setting for LoadPolicy.{ key } : "
151+ f"must be one of '{ allowed_values } '."
152+ )
153+ raise ValueError (msg )
138154
139155 self .__dict__ [key ] = value
140156
@@ -157,7 +173,7 @@ def set(self, options: str | dict | None = None, **kwargs):
157173
158174 """
159175 if options is None :
160- options_dict = {}
176+ options_dict : dict = {}
161177 elif isinstance (options , str ):
162178 if options in self .SETTINGS :
163179 options_dict = self .SETTINGS [options ]
@@ -169,12 +185,6 @@ def set(self, options: str | dict | None = None, **kwargs):
169185 raise ValueError (msg )
170186 elif isinstance (options , dict ):
171187 options_dict = options
172- else :
173- msg = ( # type: ignore[unreachable]
174- f"arg 'options' has unexpected type { type (options )!r} , "
175- f"expected one of (None | str | dict)."
176- ) # type: ignore[unreachable]
177- raise TypeError (msg ) # type: ignore[unreachable]
178188
179189 # Override any options with keywords
180190 options_dict = options_dict .copy () # don't modify original
@@ -189,7 +199,7 @@ def set(self, options: str | dict | None = None, **kwargs):
189199 setattr (self , key , value )
190200
191201 def settings (self ) -> dict :
192- """Return an options dict containing the current settings."""
202+ """Return a settings dict containing the current options settings."""
193203 return {key : getattr (self , key ) for key in self .OPTION_KEYS }
194204
195205 def __repr__ (self ):
@@ -234,7 +244,14 @@ def _combine_cubes(cubes: List[Cube], options: dict) -> CubeList:
234244 else :
235245 cubelist = CubeList (cubes )
236246
247+ eq_args = options .get ("equalise_cubes_kwargs" , None )
248+ if eq_args :
249+ from iris .util import equalise_cubes
250+
251+ equalise_cubes (cubelist , ** eq_args )
252+
237253 sequence = options ["merge_concat_sequence" ]
254+ merge_unique = options .get ("merge_unique" , False )
238255 while True :
239256 n_original_cubes = len (cubelist )
240257
@@ -245,7 +262,7 @@ def _combine_cubes(cubes: List[Cube], options: dict) -> CubeList:
245262 # merge if requested
246263 # NOTE: this needs "unique=False" to make "iris.load()" work correctly.
247264 # TODO: make configurable via options.
248- cubelist = cubelist .merge (unique = False )
265+ cubelist = cubelist .merge (unique = merge_unique )
249266 if sequence [- 1 ] == "c" :
250267 # concat if it comes last
251268 cubelist = cubelist .concatenate ()
0 commit comments