Skip to content

Commit

Permalink
Mobt 157 nbhood refactor consolidate unit tests rebased (metoppv#1664)
Browse files Browse the repository at this point in the history
* Make options for neighbourhood sum more consistent.

* Consolidate percentile from neighbourhood unit tests

* Made weighted mode only a valid option if you are using circular kernel

* Updated __init__unit tests

* Added tests for _calculate_neigbourhood method

* Added unit tests for process method

* Remove all unnecessary tests

* Updated incorrect docstrings

* Fixed mistakes introduced when merging in feature branch

* Responding to first review comments

* Responding to second review comments

* improved test docstring
  • Loading branch information
fionaRust authored Feb 23, 2022
1 parent 28faf19 commit 62d5f05
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 1,196 deletions.
44 changes: 23 additions & 21 deletions improver/nbhood/square_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
circular_kernel,
)
from improver.nbhood.nbhood import BaseNeighbourhoodProcessing
from improver.utilities.cube_checker import check_cube_coordinates
from improver.utilities.neighbourhood_tools import boxsum
from improver.utilities.spatial import (
check_if_grid_is_equal_area,
Expand Down Expand Up @@ -93,6 +92,12 @@ def __init__(
mask is not applied. Therefore, the neighbourhood processing
may result in values being present in areas that were
originally masked.
Raises:
ValueError: If the neighbourhood_method is not either
"square" or "circular".
ValueError: If the weighted_mode is used with a
neighbourhood_method that is not "circular".
"""
super().__init__(radii, lead_times=lead_times)
if neighbourhood_method in ["square", "circular"]:
Expand All @@ -111,7 +116,9 @@ def __init__(
self.sum_only = sum_only
self.re_mask = re_mask

def _calculate_neighbourhood(self, data: ndarray, mask: ndarray) -> ndarray:
def _calculate_neighbourhood(
self, data: ndarray, mask: ndarray = None
) -> Union[ndarray, np.ma.MaskedArray]:
"""
Apply neighbourhood processing.
Expand All @@ -122,7 +129,7 @@ def _calculate_neighbourhood(self, data: ndarray, mask: ndarray) -> ndarray:
Mask of valid input data elements.
Returns:
Array containing the smoothed field after the square
Array containing the smoothed field after the
neighbourhood method has been applied.
"""
if not self.sum_only:
Expand Down Expand Up @@ -191,26 +198,28 @@ def _calculate_neighbourhood(self, data: ndarray, mask: ndarray) -> ndarray:

def process(self, cube: Cube, mask_cube: Optional[Cube] = None) -> Cube:
"""
Call the methods required to apply a square neighbourhood
method to a cube.
Call the methods required to apply a neighbourhood processing to a cube.
Applies neighbourhood processing to each 2D x-y-slice of the input cube.
The steps undertaken are:
If the input cube is masked the neighbourhood sum is calculated from
the total of the unmasked data in the neighbourhood around each grid
point. The neighbourhood mean is then calculated by dividing the
neighbourhood sum at each grid point by the total number of valid grid
points that contributed to that sum. If a mask_cube is provided then
this is used to mask each x-y-slice prior to the neighburhood sum
or mean being calculated.
1. Set up cubes by determining, if the arrays are masked.
2. Pad the input array with a halo and then calculate the neighbourhood
of the haloed array.
3. Remove the halo from the neighbourhooded array and deal with a mask,
if required.
Args:
cube:
Cube containing the array to which the square neighbourhood
Cube containing the array to which the neighbourhood processing
will be applied.
mask_cube:
Cube containing the array to be used as a mask.
Returns:
Cube containing the smoothed field after the square
Cube containing the smoothed field after the
neighbourhood method has been applied.
"""
super().process(cube)
Expand All @@ -219,8 +228,7 @@ def process(self, cube: Cube, mask_cube: Optional[Cube] = None) -> Cube:
# If the data is masked, the mask will be processed as well as the
# original_data * mask array.
check_radius_against_distance(cube, self.radius)
original_attributes = cube.attributes
original_methods = cube.cell_methods

grid_cells = distance_to_number_of_grid_cells(cube, self.radius)
if self.neighbourhood_method == "circular":
self.kernel = circular_kernel(grid_cells, self.weighted_mode)
Expand All @@ -240,10 +248,4 @@ def process(self, cube: Cube, mask_cube: Optional[Cube] = None) -> Cube:
result_slices.append(cube_slice)
neighbourhood_averaged_cube = result_slices.merge_cube()

neighbourhood_averaged_cube.cell_methods = original_methods
neighbourhood_averaged_cube.attributes = original_attributes

neighbourhood_averaged_cube = check_cube_coordinates(
cube, neighbourhood_averaged_cube
)
return neighbourhood_averaged_cube
2 changes: 1 addition & 1 deletion improver/utilities/spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def calculate_grid_spacing(

def distance_to_number_of_grid_cells(
cube: Cube, distance: float, axis: str = "x", return_int: bool = True
) -> float:
) -> Union[float, int]:
"""
Return the number of grid cells in the x and y direction based on the
input distance in metres. Requires an equal-area grid on which the spacing
Expand Down
Loading

0 comments on commit 62d5f05

Please sign in to comment.