Fix cluster matching for "partial" secondary inputs - #2417
Conversation
… the secondary forecast source are fewer than the number of clusters.
…izations in the secondary inputs changed over time, so a merge would fail. This has now been handled by curtailing any secondary input if there is a change in the number of realizations.
…nputs and _process_full_realization_inputs.
mo-jbeaver
left a comment
There was a problem hiding this comment.
Added some comments, mostly for areas that might need more explanations. Tests all ran successfully.
| Cross-fp avg MSE (new code): | ||
| - vs cluster A: (4 + 7225)/2 = 3614.5 | ||
| - vs cluster B: (7744 + 25)/2 = 3884.5 | ||
| => new code assigns to cluster A at both fps |
There was a problem hiding this comment.
Should a small note be added that for MSE, lower is better?
| """Partial inputs must assign the same cluster at every lead time. | ||
|
|
||
| A partial secondary input (fewer realizations than n_clusters) was previously | ||
| matched independently per forecast period, allowing the same realization to be |
There was a problem hiding this comment.
forecast period (fp) might be a helpful addition
There was a problem hiding this comment.
I've added (fp) given that that's used in lots of variable names later on.
| @@ -705,8 +717,31 @@ def _categorise_secondary_inputs( | |||
| ) | |||
| continue # No valid forecast periods after filtering | |||
There was a problem hiding this comment.
Would it make more sense to move this section to below the lines:
valid_fp_cube_pairs = [ (fp, cube) for fp, cube in fp_cube_pairs if fp in primary_fps ] valid_fp_cube_pairs = sorted(valid_fp_cube_pairs, key=lambda x: x[0])
There was a problem hiding this comment.
Yes, I've moved the if not valid_fp_cube_pairs block up a few lines to be next to where the valid_fp_cube_pairs variable is defined.
| fp_6_data = result.extract(iris.Constraint(forecast_period=6 * 3600)).data | ||
| assert np.allclose(fp_6_data, 106.0, atol=5.0), ( | ||
| "fp=6 should fall back to clustered primary when secondary is truncated" | ||
| ) |
There was a problem hiding this comment.
Could an additional comment be added here to explain this section.
There was a problem hiding this comment.
I've added an additional comment to explain the meaning of the result.
| ) | ||
|
|
||
|
|
||
| def test_clusterandmatch_full_input_inconsistent_realization_counts_truncates(): |
There was a problem hiding this comment.
This test seems very similar to the previous one. Could more descriptions be added to both to explain the differences between them.
There was a problem hiding this comment.
I've parameterised these tests, as you're right that they were very similar. The unit test description has been extended.
| Updates replaced_realizations, cluster_sources, and | ||
| secondary_input_realizations_to_clusters in-place. Called from the |
There was a problem hiding this comment.
Maybe mention what these variables are, attributes, dimensions ect.
There was a problem hiding this comment.
I've added a bit more detail to the docstring.
| candidate_name: Name of the secondary input. | ||
| fps: Forecast period values in seconds to extract. | ||
| cubes: CubeList containing all input cubes. | ||
| target_grid_cube: Target grid cube for optional regridding. |
There was a problem hiding this comment.
Is the regridding optional?
There was a problem hiding this comment.
I've tried to clarify that the target_grid_cube variable can be optional and updated the type hints and docstrings to reflect this.
Related issues: #2259, #2305, #2332, #2340, #2359, #2361
Description
This PR consists of 3 commits to address issues with the current clustering logic.
Commit 1: This addresses the primary goal because I noticed that it was possible for secondary inputs (e.g. UKV), that only have enough realizations to cover a subset of the total number of clusters, to match to different clusters at different forecast periods e.g. see the output below. This wasn't the intention but was arising from the fact that the call to RealizationToClusterMatcher was inside the loop over forecast period in
_process_partial_realization_inputs. This was unlike the methodology in_process_full_realization_inputswhere RealizationToClusterMatcher was called prior to the for loop over forecast period, so that the realization to cluster matching used all forecast periods. This commit therefore makes_process_partial_realization_inputsmore like_process_full_realization_inputs, so that secondary inputs with a "partial" set of realizations are matched to the clusters using all the forecast periods.Commit 2: This addresses an issue that was found in testing commit 1, where it was possible for a secondary input to have an inconsistent number of realizations at different forecast periods. This led to an inability to merge the secondary inputs across forecast periods in
_process_partial_realization_inputs. This hadn't been an issue previously because different "partial" forecast sources were being matched independently at each forecast period.To fix this issue, I've modified the _categorise_secondary_inputs method, which was previously only checking the number of realizations present on the first cube. This has been extended, so that if the number of realizations changes across forecast periods within the secondary input then the secondary input will be truncated, so as to avoid a possible pulsing if a forecast source is missing particular forecast periods.
Commit 3: This commit does some refactoring to centralise some of the code present in
_process_partial_realization_inputsand_process_full_realization_inputsand avoid duplication where possible.Testing: