Source code for improver.cli.spot_extract

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# (C) British Crown copyright. The Met Office.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""Script to run spotdata extraction."""

from improver import cli


[docs]@cli.clizefy @cli.with_output def process( *cubes: cli.inputcube, apply_lapse_rate_correction=False, land_constraint=False, similar_altitude=False, extract_percentiles: cli.comma_separated_list = None, ignore_ecc_bounds_exceedance=False, new_title: str = None, suppress_warnings=False, realization_collapse=False, ): """Module to run spot data extraction. Extract diagnostic data from gridded fields for spot data sites. It is possible to apply a temperature lapse rate adjustment to temperature data that helps to account for differences between the spot site's real altitude and that of the grid point from which the temperature data is extracted. Args: cubes (iris.cube.Cube): A list of cubes containing the diagnostic data to be extracted, the lapse rate (optional) and the neighbour cube. Where the lapse rate cube contains temperature lapse rates. If this cube is provided and a screen temperature cube is being processed, the lapse rates will be used to adjust the temperature to better represent each spot's site-altitude. And the neighbour cube is a cube of spot-data neighbours and the spot site information. apply_lapse_rate_correction (bool): Use to apply a lapse-rate correction to screen temperature data so that the data are a better match the altitude of the spot site for which they have been extracted. land_constraint (bool): Use to select the nearest-with-land-constraint neighbour-selection method from the neighbour_cube. This means that the grid points should be land points except for sites where none were found within the search radius when the neighbour cube was created. May be used with similar_altitude. similar_altitude (bool): Use to select the nearest-with-height-constraint neighbour-selection method from the neighbour_cube. These are grid points that were found to be the closest in altitude to the spot site within the search radius defined when the neighbour cube was created. May be used with land_constraint. extract_percentiles (list or int): If set to a percentile value or a list of percentile values, data corresponding to those percentiles will be returned. For example "25, 50, 75" will result in the 25th, 50th and 75th percentiles being returned from a cube of probabilities, percentiles or realizations. Deterministic input data will raise a warning message. Note that for percentile inputs, if the desired percentile(s) do not exist in the input cube the available percentiles will be resampled to produce those requested. ignore_ecc_bounds (bool): Demotes exceptions where calculated percentiles are outside the ECC bounds range to warnings. new_title (str): New title for the spot-extracted data. If None, this attribute is removed from the output cube since it has no prescribed standard and may therefore contain grid information that is no longer correct after spot-extraction. suppress_warnings (bool): Suppress warning output. This option should only be used if it is known that warnings will be generated but they are not required. realization_collapse (bool): Triggers equal-weighting blending of the realization coord if required. Use this if a threshold coord is also present on the input cube. Returns: iris.cube.Cube: Cube of spot data. Warns: warning: If diagnostic cube is not a known probabilistic type. warning: If a lapse rate cube was not provided, but the option to apply the lapse rate correction was enabled. """ import warnings import iris import numpy as np from iris.exceptions import CoordinateNotFoundError from improver.ensemble_copula_coupling.ensemble_copula_coupling import ( ConvertProbabilitiesToPercentiles, ResamplePercentiles, ) from improver.metadata.probabilistic import find_percentile_coordinate from improver.percentile import PercentileConverter from improver.spotdata.apply_lapse_rate import SpotLapseRateAdjust from improver.spotdata.neighbour_finding import NeighbourSelection from improver.spotdata.spot_extraction import SpotExtraction from improver.utilities.cube_extraction import extract_subcube from improver.utilities.cube_manipulation import collapse_realizations neighbour_cube = cubes[-1] cube = cubes[0] if realization_collapse: cube = collapse_realizations(cube) neighbour_selection_method = NeighbourSelection( land_constraint=land_constraint, minimum_dz=similar_altitude ).neighbour_finding_method_name() result = SpotExtraction(neighbour_selection_method=neighbour_selection_method)( neighbour_cube, cube, new_title=new_title ) # If a probability or percentile diagnostic cube is provided, extract # the given percentile if available. This is done after the spot-extraction # to minimise processing time; usually there are far fewer spot sites than # grid points. if extract_percentiles: extract_percentiles = [np.float32(x) for x in extract_percentiles] try: perc_coordinate = find_percentile_coordinate(result) except CoordinateNotFoundError: if "probability_of_" in result.name(): result = ConvertProbabilitiesToPercentiles( ecc_bounds_warning=ignore_ecc_bounds_exceedance )(result, percentiles=extract_percentiles) result = iris.util.squeeze(result) elif result.coords("realization", dim_coords=True): fast_percentile_method = not np.ma.isMaskedArray(result.data) result = PercentileConverter( "realization", percentiles=extract_percentiles, fast_percentile_method=fast_percentile_method, )(result) else: msg = ( "Diagnostic cube is not a known probabilistic type. " "The {} percentile could not be extracted. Extracting " "data from the cube including any leading " "dimensions.".format(extract_percentiles) ) if not suppress_warnings: warnings.warn(msg) else: if set(extract_percentiles).issubset(perc_coordinate.points): constraint = [ "{}={}".format(perc_coordinate.name(), extract_percentiles) ] result = extract_subcube(result, constraint) else: result = ResamplePercentiles()(result, percentiles=extract_percentiles) # Check whether a lapse rate cube has been provided if apply_lapse_rate_correction: if len(cubes) == 3: plugin = SpotLapseRateAdjust( neighbour_selection_method=neighbour_selection_method ) result = plugin(result, neighbour_cube, cubes[-2]) elif not suppress_warnings: warnings.warn( "A lapse rate cube was not provided, but the option to " "apply the lapse rate correction was enabled. No lapse rate " "correction could be applied." ) # Remove the internal model_grid_hash attribute if present. result.attributes.pop("model_grid_hash", None) return result