Source code for pylawr.transform.attenuation.atten_corr_dual

#!/bin/env python
# -*- coding: utf-8 -*-

# System modules
import logging

# External modules
import numpy as np
from sklearn.isotonic import IsotonicRegression

# Internal modules
from pylawr.utilities import log_decorator
from pylawr.transform.memorymixin import MemoryMixin
from pylawr.grid import PolarGrid

logger = logging.getLogger(__name__)


[docs]class AttenuationCorrectionDual(MemoryMixin): """ This class provides methods to estimate the attenuation based on the maximum apparent attenuation comparing reflectivities of attenuation-influenced frequency bands (X-band) and observations from less attenuated radar systems (C-band). The reflectivites have to be on the same grid. One approach is to interpolate the reflectivites on a joint coarse grid, estimate the attenuation and interpolate the attenuation on the fine grid of the attenuation-influenced frequency band. To estimate the attenuation of the attenuation-influenced frequency band (X-band) with the less attenuated radar system (C-band) the maximum apparent attenuation :math:`K_{\mathrm{max}}` (dB) is used: .. math: K_{\mathrm{max}} &= 10\cdot{}log[z_{\mathrm{C}} / z_{\mathrm{X}}] \\ &= Z_{\mathrm{C}} - Z_{\mathrm{X}} The attenuation should increase with increasing distance in theory. To get an increasing attenuation some regression is applied on :math:`K_{\mathrm{max}}`, e.g. the isotonic regression. For further information look up Lengfeld et. al (2016). Attributes ---------- attenuation : :py:class:`xarray.DataArray` or None Estimated attenuation for the correction, negative values are not allowed """ def __init__(self): super().__init__() self._attenuation = None self._trainable_vars = ('attenuation',) self.attenuation = None @property def attenuation(self): return self._attenuation @attenuation.setter def attenuation(self, new_attenuation): """ ... sets values for the attenuation correction. Parameters ---------- new_attenuation: :py:class:`xarray.DataArray` or None new attenuation field """ self._attenuation = new_attenuation @property def fitted(self): return self.attenuation is not None
[docs] @staticmethod def _check_grids(grid_first, grid_second): """ Checks if the grids are child of :py:class:`pylawr.grid.polar.PolarGrid` and have equal attributes. Parameters ---------- grid : child of :py:class:`pylawr.grid.base.BaseGrid` grid of data """ if not isinstance(grid_first, PolarGrid): raise TypeError( 'First grid is not a polar grid.' ) if not isinstance(grid_second, PolarGrid): raise TypeError( 'Second grid is not a polar grid.' ) if not grid_first == grid_second: raise ValueError( 'The grids have different attributes.' )
[docs] @staticmethod def _calc_kmax(refl_attenuated, refl_robust): """ Calculate the attenuation factor between attenuated reflectivity and robust reflectivity. Only where the attenuated reflectivity is available the attenuation factor is calculated (otherwise we would create a reflectivity signal due to e.g. different radar resolution and incorrect alignment). Parameters ---------- refl_attenuated: :py:class:`xarray.DataArray` Reflectivity of attenuation-influenced radar system refl_robust: :py:class:`xarray.DataArray` Reflectivity of less attenuated radar system Returns ------- k_max : :py:class:`xarray.DataArray` The attenuation between the two radar fields. """ k_max = refl_robust - refl_attenuated k_max.values[refl_attenuated.values < 0] = np.nan return k_max
[docs] @staticmethod def _regress_attenuation(k_max, regression=IsotonicRegression(), replace_neg=0): """ Calculate attenuation based on given regression object. Parameters ---------- k_max : :py:class:`xarray.DataArray` The attenuation is estimated based on this array. regression: object Regression object with `.fit_transform(x, y)` method. Default is :py:class:`sklearn.isotonic.IsotonicRegression`. replace_neg: float, NaN or None Negative values are replace with float values or `np.nan`. If this parameter is set to `None` negative values will not replaced, e.g. for research reasons. Returns ------- attenuation : :py:class:`xarray.DataArray` Estimated attenuation array based on given reflectivity differences and regression. """ attenuation = k_max.interpolate_na(dim='range') attenuation = k_max.fillna(0.) for ind in np.ndindex(*k_max.shape[:-1]): attenuation[ind] = regression.fit_transform( attenuation['range'].values.astype(np.float64), attenuation[ ind] ) if replace_neg is not None: attenuation = attenuation.where(attenuation > 0, replace_neg) attenuation = attenuation.where(~np.isnan(k_max)) return attenuation
[docs] @log_decorator(logger) def fit(self, refl_attenuated, refl_robust, regression=IsotonicRegression(), replace_neg=0): """ Estimates the attenuation correction. Negative values are not allowed, otherwise grid resolution effects of the less attenuated radar systems could occure. In default the isotonic regression is applied according to Lengfeld et. al (2016). Parameters ---------- refl_attenuated: :py:class:`xarray.DataArray` Reflectivity of attenuation-influenced radar system refl_robust: :py:class:`xarray.DataArray` Reflectivity of less attenuated radar system regression: object Regression object with `.fit_transform(x, y)` method. Default is :py:class:`sklearn.isotonic.IsotonicRegression`. If this is None, no regression is used and raw differences between given fields are used as attenuation. replace_neg: float, NaN or None Negative values are replace with float values or `np.nan`. If this parameter is set to `None` negative values will not replaced, e.g. for research reasons. """ self._check_grids(refl_attenuated.lawr.grid, refl_robust.lawr.grid) k_max = self._calc_kmax(refl_attenuated, refl_robust) if regression is None: attenuation = k_max else: attenuation = self._regress_attenuation( k_max, regression=regression, replace_neg=replace_neg ) self.attenuation = attenuation self.attenuation = self.attenuation.lawr.set_variable('pia')
[docs] def to_xarray(self): """ Serialize this filter's parameters to an :any:`xarray.Dataset` Returns ------- :any:`xarray.Dataset` the filter's parameters as dataset """ ds = self.attenuation.to_dataset() ds.attrs["type"] = self.__class__.__name__ return ds