Skip to content

Commit

Permalink
Merge branch 'master' of github.com:thunderhoser/GewitterGefahr into …
Browse files Browse the repository at this point in the history
…djgagne
  • Loading branch information
djgagne committed Nov 10, 2020
2 parents 67cb658 + d819874 commit 8f98eaa
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 83 deletions.
53 changes: 38 additions & 15 deletions gewittergefahr/gg_utils/echo_classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import os.path
import numpy
import netCDF4
from scipy.ndimage import label as label_image
from scipy.interpolate import RectBivariateSpline
from scipy.ndimage.filters import median_filter, convolve
from gewittergefahr.gg_io import netcdf_io
Expand Down Expand Up @@ -49,6 +50,7 @@
HALVE_RESOLUTION_KEY = 'halve_resolution_for_peakedness'
MIN_ECHO_TOP_KEY = 'min_echo_top_m_asl'
ECHO_TOP_LEVEL_KEY = 'echo_top_level_dbz'
MIN_SIZE_KEY = 'min_size_pixels'
MIN_COMPOSITE_REFL_CRITERION1_KEY = 'min_composite_refl_criterion1_dbz'
MIN_COMPOSITE_REFL_CRITERION5_KEY = 'min_composite_refl_criterion5_dbz'
MIN_COMPOSITE_REFL_AML_KEY = 'min_composite_refl_aml_dbz'
Expand All @@ -59,6 +61,7 @@
HALVE_RESOLUTION_KEY: False,
MIN_ECHO_TOP_KEY: 10000.,
ECHO_TOP_LEVEL_KEY: 25.,
MIN_SIZE_KEY: 5,
MIN_COMPOSITE_REFL_CRITERION1_KEY: 25.,
MIN_COMPOSITE_REFL_CRITERION5_KEY: 25.,
MIN_COMPOSITE_REFL_AML_KEY: 45.
Expand Down Expand Up @@ -95,6 +98,9 @@ def _check_input_args(option_dict):
option_dict[MIN_ECHO_TOP_KEY]
))
option_dict[ECHO_TOP_LEVEL_KEY] = float(option_dict[ECHO_TOP_LEVEL_KEY])
option_dict[MIN_SIZE_KEY] = int(numpy.round(
option_dict[MIN_SIZE_KEY]
))
option_dict[MIN_COMPOSITE_REFL_CRITERION5_KEY] = float(
option_dict[MIN_COMPOSITE_REFL_CRITERION5_KEY]
)
Expand All @@ -107,6 +113,7 @@ def _check_input_args(option_dict):
error_checking.assert_is_boolean(option_dict[HALVE_RESOLUTION_KEY])
error_checking.assert_is_greater(option_dict[MIN_ECHO_TOP_KEY], 0)
error_checking.assert_is_greater(option_dict[ECHO_TOP_LEVEL_KEY], 0.)
error_checking.assert_is_greater(option_dict[MIN_SIZE_KEY], 1)
error_checking.assert_is_greater(
option_dict[MIN_COMPOSITE_REFL_CRITERION5_KEY], 0.
)
Expand Down Expand Up @@ -163,7 +170,7 @@ def _neigh_metres_to_rowcol(neigh_radius_metres, grid_metadata_dict):
num_rows = 1 + 2 * int(numpy.ceil(
neigh_radius_metres / y_spacing_metres
))

mean_latitude_deg = (
numpy.max(grid_metadata_dict[LATITUDES_KEY]) -
numpy.min(grid_metadata_dict[LATITUDES_KEY])
Expand Down Expand Up @@ -393,6 +400,8 @@ def _apply_convective_criterion1(
numerator_matrix = numpy.sum(
peakedness_matrix_dbz > peakedness_threshold_matrix_dbz, axis=-1
)

# TODO(thunderhoser): Should check for non-NaN, rather than non-zero.
denominator_matrix = numpy.sum(this_reflectivity_matrix_dbz > 0, axis=-1)
this_flag_matrix = numpy.logical_and(
numerator_matrix == 0, denominator_matrix == 0
Expand Down Expand Up @@ -495,29 +504,32 @@ def _apply_convective_criterion3(
)


def _apply_convective_criterion4(convective_flag_matrix):
def _apply_convective_criterion4(convective_flag_matrix, min_size_pixels):
"""Applies criterion 4 for convective classification.
Criterion 4 states: if pixel (i, j) is marked convective but none of its
neighbours are marked convective, (i, j) is not actually convective.
Criterion 4 states: if pixel (i, j) is marked convective but is not part of
a connected region with size of >= K pixels, (i, j) is not actually
convective.
:param convective_flag_matrix: M-by-N numpy array of Boolean flags (True
if convective, False if not).
:param min_size_pixels: Minimum size of connected region.
:return: convective_flag_matrix: Updated version of input.
"""

weight_matrix = numpy.full((3, 3), 1.)
weight_matrix = weight_matrix / weight_matrix.size
region_id_matrix = label_image(
convective_flag_matrix.astype(int), structure=numpy.full((3, 3), 1.)
)[0]
num_regions = numpy.max(region_id_matrix)

average_matrix = convolve(
convective_flag_matrix.astype(float), weights=weight_matrix,
mode='constant', cval=0.
)
for i in range(num_regions):
these_indices = numpy.where(region_id_matrix == i + 1)
if len(these_indices[0]) >= min_size_pixels:
continue

return numpy.logical_and(
convective_flag_matrix,
average_matrix > weight_matrix[0, 0] + TOLERANCE
)
convective_flag_matrix[these_indices] = False

return convective_flag_matrix


def _apply_convective_criterion5(
Expand Down Expand Up @@ -586,6 +598,8 @@ def find_convective_pixels(reflectivity_matrix_dbz, grid_metadata_dict,
used for criterion 3.
option_dict['echo_top_level_dbz'] Critical reflectivity (used to compute
echo top for criterion 3).
option_dict['min_size_pixels']: Minimum connected-region size (for criterion
4).
option_dict['min_composite_refl_criterion1_dbz'] Minimum composite
(column-max) reflectivity for criterion 1. This may be None.
option_dict['min_composite_refl_criterion5_dbz'] Minimum composite
Expand All @@ -611,6 +625,7 @@ def find_convective_pixels(reflectivity_matrix_dbz, grid_metadata_dict,
halve_resolution_for_peakedness = option_dict[HALVE_RESOLUTION_KEY]
min_echo_top_m_asl = option_dict[MIN_ECHO_TOP_KEY]
echo_top_level_dbz = option_dict[ECHO_TOP_LEVEL_KEY]
min_size_pixels = option_dict[MIN_SIZE_KEY]
min_composite_refl_criterion1_dbz = (
option_dict[MIN_COMPOSITE_REFL_CRITERION1_KEY]
)
Expand Down Expand Up @@ -692,7 +707,8 @@ def find_convective_pixels(reflectivity_matrix_dbz, grid_metadata_dict,

print('Applying criterion 4 for convective classification...')
convective_flag_matrix = _apply_convective_criterion4(
convective_flag_matrix
convective_flag_matrix=convective_flag_matrix,
min_size_pixels=min_size_pixels
)

print('Number of convective pixels = {0:d}'.format(
Expand Down Expand Up @@ -793,6 +809,7 @@ def write_classifications(convective_flag_matrix, grid_metadata_dict,
halve_resolution_for_peakedness = option_dict[HALVE_RESOLUTION_KEY]
min_echo_top_m_asl = option_dict[MIN_ECHO_TOP_KEY]
echo_top_level_dbz = option_dict[ECHO_TOP_LEVEL_KEY]
min_size_pixels = option_dict[MIN_SIZE_KEY]
min_composite_refl_criterion1_dbz = (
option_dict[MIN_COMPOSITE_REFL_CRITERION1_KEY]
)
Expand All @@ -818,6 +835,7 @@ def write_classifications(convective_flag_matrix, grid_metadata_dict,
)
netcdf_dataset.setncattr(MIN_ECHO_TOP_KEY, min_echo_top_m_asl)
netcdf_dataset.setncattr(ECHO_TOP_LEVEL_KEY, echo_top_level_dbz)
netcdf_dataset.setncattr(MIN_SIZE_KEY, min_size_pixels)
netcdf_dataset.setncattr(
MIN_COMPOSITE_REFL_CRITERION1_KEY, min_composite_refl_criterion1_dbz
)
Expand Down Expand Up @@ -900,6 +918,11 @@ def read_classifications(netcdf_file_name):
getattr(netcdf_dataset, MIN_COMPOSITE_REFL_AML_KEY)
}

try:
option_dict[MIN_SIZE_KEY] = getattr(netcdf_dataset, MIN_SIZE_KEY)
except:
option_dict[MIN_SIZE_KEY] = 2

if option_dict[MIN_COMPOSITE_REFL_CRITERION1_KEY] < 0:
option_dict[MIN_COMPOSITE_REFL_CRITERION1_KEY] = None

Expand Down
13 changes: 9 additions & 4 deletions gewittergefahr/gg_utils/echo_classification_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,12 @@ def test_apply_convective_criterion4_main(self):
"""

this_flag_matrix = echo_classifn._apply_convective_criterion4(
CRITERION3_FLAG_MATRIX)
convective_flag_matrix=CRITERION3_FLAG_MATRIX, min_size_pixels=2
)

self.assertTrue(numpy.array_equal(
this_flag_matrix, CRITERION4_FLAG_MATRIX))
this_flag_matrix, CRITERION4_FLAG_MATRIX
))

def test_apply_convective_criterion4_dummy(self):
"""Ensures correct output from _apply_convective_criterion4.
Expand All @@ -286,10 +288,13 @@ def test_apply_convective_criterion4_dummy(self):
"""

this_flag_matrix = echo_classifn._apply_convective_criterion4(
DUMMY_CRITERION3_FLAG_MATRIX)
convective_flag_matrix=DUMMY_CRITERION3_FLAG_MATRIX,
min_size_pixels=2
)

self.assertTrue(numpy.array_equal(
this_flag_matrix, DUMMY_CRITERION4_FLAG_MATRIX))
this_flag_matrix, DUMMY_CRITERION4_FLAG_MATRIX
))

def test_apply_convective_criterion5(self):
"""Ensures correct output from _apply_convective_criterion5."""
Expand Down
Loading

0 comments on commit 8f98eaa

Please sign in to comment.