Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IM-1787: Improve memory efficiency of threshold plugin #1913

Merged
merged 35 commits into from
Oct 20, 2023

Conversation

bayliffe
Copy link
Contributor

This PR changes the way in which the thresholding plugin works to reduce its memory requirements. The reason for this work is the requirement to handle the larger 51 member ECMWF ensembles, which without this work is prohibitively expensive in terms of required memory resources.

The changes are designed to avoid the need to hold a thresholded, multi-realization array in memory. Prior to this change the threshold plugin will threshold each realization in turn, and if collapsing realizations, perform an iris.analysis.MEAN over that realization dimension. The rewritten version, when collapsing realizations, will handle each realization in turn, summing up the contributions to each threshold, and then divide this sum by the total number of contributions which is also accrued.

More discussion can be found here: #1787

This PR makes some other changes which are simplifying and made this work more tractable:

  • Move almost all the CLI functionality into the plugin. Some of this was required to enable the vicinity processing to be applied to each realization-threshold pair without having to retain both coordinates throughout the process, which led to large memory usage. Other functionality may have been moved simply to yield a simpler CLI which is desirable.
  • Removal of the ability to provide a arbitrary function to the plugin. We have only ever used realization collapse and so that option is now all that is left. This changes the CLI and plugin interfaces, meaning we need to notify collaborators and ensure we make a suitable suite change to accompany this PR.
  • Removal of the option to provide fuzzy-bounds as an argument to the plugin. This was used by the CLI which would interpret a threshold config that the user might provide and then pass in the bounds. Nearly all of the CLI functionality has now been moved into the plugin to simplify the CLI, meaning the threshold config is instead passed to the plugin, making the fuzzy-bounds argument redundant.

There could be more unit tests added to cover the CLI functionality that has now moved into the plugin. I've added a little to cover the vicinity processing, but would appreciate a list of additional functionality that needs testing from the reviewers.

Testing:

  • Ran tests and they passed OK
  • Added new tests for the new feature(s)

bayliffe added 18 commits June 16, 2023 09:55
…d value rather than a list. Work through interface changes in other plugins.
…reshold plugin. This was previously all performed in the CLI and thus not covered by unit tests in this specific context.
@codecov
Copy link

codecov bot commented Jun 16, 2023

Codecov Report

All modified lines are covered by tests ✅

Comparison is base (e1523c2) 98.37% compared to head (ffd3fb9) 97.13%.
Report is 19 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1913      +/-   ##
==========================================
- Coverage   98.37%   97.13%   -1.25%     
==========================================
  Files         122      123       +1     
  Lines       11707    11938     +231     
==========================================
+ Hits        11517    11596      +79     
- Misses        190      342     +152     
Files Coverage Δ
...precipitation_type/shower_condition_probability.py 100.00% <100.00%> (ø)
improver/regrid/landsea.py 99.21% <100.00%> (ø)
improver/threshold.py 100.00% <100.00%> (ø)
improver/utilities/spatial.py 97.44% <100.00%> (-1.65%) ⬇️
improver/utilities/textural.py 100.00% <100.00%> (ø)

... and 18 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@MoseleyS MoseleyS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the changes are basically ok, it just needs tightening up on the documentation and testing side.

improver/threshold.py Outdated Show resolved Hide resolved
improver/utilities/spatial.py Outdated Show resolved Hide resolved
improver/utilities/spatial.py Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/utilities/spatial.py Outdated Show resolved Hide resolved
improver/utilities/spatial.py Outdated Show resolved Hide resolved
improver_tests/acceptance/test_threshold.py Show resolved Hide resolved
@@ -393,6 +393,101 @@ def process(self, cube: Cube) -> Tuple[Cube, Cube]:
return tuple(gradients)


def maximum_within_vicinity(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new methods in spatial need unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

bayliffe added 2 commits July 27, 2023 16:37
* upstream/master:
  Fix to the wind vertical displacement adjustment implementation (metoppv#1927)
  Add function which normalises input cubes according to a reference (metoppv#1919)
  Skip ECC bounds usage when converting probabilities to percentiles (metoppv#1926)
  Add CLIs to support rescaling of the forecast based on altitude difference (metoppv#1917)
  Changes to the modal code to increase the percentage to 30% and change the groupings to provide a more representative daily summary symbol. (metoppv#1925)
  Add plugins to support rescaling of the forecast based on altitude difference (metoppv#1916)
  Support conversion from percentiles to probabilities (metoppv#1924)
  Correct handling of reference time in weather_code plugin (metoppv#1920)
  Add CLI for clipping cubes (metoppv#1918)
  Update cbh ecc name (metoppv#1922)
  Updates Broadcast and expand_bounds in Combine Plugin (metoppv#1914)
  Mobt515 cloud base height spot extraction (metoppv#1911)
  MOBT-494: Cube title setting in weather symbol code (metoppv#1912)
  MOBT512-masking percentiles for cloud base height (metoppv#1908)
  Mobt 496 enforce forecast between references (metoppv#1907)
This commit also addresses many of the review comments
though the expansion / rewriting of the unit tests will
be undertaken separately.

The BasicThreshold plugin is also renamed to Threshold.
@bayliffe
Copy link
Contributor Author

bayliffe commented Aug 4, 2023

@MoseleyS I have added some tests (a lot of tests!). This ensures the unit tests now capture the vicinity processing and realization collapse behaviour that was previously only lightly tested by acceptance tests as the functionality sat within the CLI.

To try and retain the evidence that the efficiency changes in this PR have not changed any results I have, as things stand, left the original unit tests as they were when you last reviewed them. These are the tests we've had for a long time, with small interface tweaks as required by the changes, that show values are unaffected. I've then added a new test_ThresholdPytest.py file which will replace this in time. I've not tried to use the same values in these tests but have wholesale replaced them.

I've yet to add unit test for the spatial.py utilities that got spun out of the OccurenceWithinVicinity plugin, but there is plenty here to review without those for now.

Copy link
Contributor

@MoseleyS MoseleyS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review not completed. I've got as far as line 217 in test_ThresholdPytest.py

improver/regrid/landsea.py Outdated Show resolved Hide resolved
improver/utilities/textural.py Outdated Show resolved Hide resolved
Comment on lines 438 to 439
if landmask is not None:
landmask = np.where(landmask.data >= 0.5, True, False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This point still stands.

improver_tests/threshold/conftest.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
# fuzzy_bounds contains one value
(
{"threshold_config": {"0.6": [0.4]}},
"Invalid bounds for one threshold: \\(0.4,\\).",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Optional] You can reduce the double-escapes to single-escapes with regex-strings. It isn't a massive improvement though.

Suggested change
"Invalid bounds for one threshold: \\(0.4,\\).",
r"Invalid bounds for one threshold: \(0.4,\).",

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New-fangled (by which I mean unfamiliar). I'll leave this as is so I understand what I've done.

improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver/cli/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
Copy link
Contributor

@MoseleyS MoseleyS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought of a couple of ways this could be very slightly improved.

improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver_tests/threshold/conftest.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
MoseleyS
MoseleyS previously approved these changes Sep 18, 2023
Copy link
Contributor

@gavinevans gavinevans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all this work, @bayliffe. This re-write is clearly a significant undertaking 👍

I've added some comments and queries.

improver/cli/threshold.py Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver/threshold.py Outdated Show resolved Hide resolved
improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver/threshold.py Show resolved Hide resolved
improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver_tests/threshold/test_ThresholdPytest.py Outdated Show resolved Hide resolved
improver/threshold.py Show resolved Hide resolved
Copy link
Contributor

@gavinevans gavinevans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, @bayliffe 👍

@gavinevans gavinevans merged commit 27531d9 into metoppv:master Oct 20, 2023
10 checks passed
MoseleyS pushed a commit to MoseleyS/improver that referenced this pull request Aug 22, 2024
* Making threshold more efficient.

* Getting the contributions right when using masked data.

* Correct unit setting bug.

* Beginning to restructure.

* Changing interfaces, simplifying CLI, and updating unit tests. Incomplete.

* Break up the occurence within vicinity plugin to allow use of components within the threshold plugin.

* Figuring out where the vicinity processing differs.

* Add new exception. May be temporary.

* more incremental changes.

* Final fixes to vicinity processing from within the threshold plugin.

* Format changes.

* Remove defunct test. Add array slicing to avoid swelling memory when broadcasting arrays.

* Use a slice iterator rather than list to reduce memory usage. As suggested by Stephen Moseley.

* Set self.fill_masked which was lost in rebase.

* Remove assumption of realization coordinate added when changing to use iterator.

* Modify threshold such that it can once again accept a single threshold value rather than a list. Work through interface changes in other plugins.

* Remove doc-strings relating to fuzzy-bounds, the setting of which using an argument has been removed.

* Add unit tests to cover the vicinity processing applied within the threshold plugin. This was previously all performed in the CLI and thus not covered by unit tests in this specific context.

* Format fixes.

* Replicate percentile collapse functionality.

This commit also addresses many of the review comments
though the expansion / rewriting of the unit tests will
be undertaken separately.

The BasicThreshold plugin is also renamed to Threshold.

* Added back land mask without vicinity exception.

* Rewriting threshold unit tests using pytest and extending coverage. Incomplete.

* Unit test replacement complete. Doc-strings and formatting changes to come.

* Add docstrings and typing to the conftest functions.

* Formatting corrections.

* Remove broken new test added to BasicThreshold tests which is not needed due to new pytests.

* Add vicinity tests using a landmask.

* Partial review response.

* Further review changes.

* Further review tweaks.

* Tweak to type checking.

* Isort reorder

* Review changes.

* Fix my test for bounds set to None.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants