Skip to content

Commit

Permalink
wradlib 2.0 RC (#640)
Browse files Browse the repository at this point in the history
  • Loading branch information
kmuehlbauer committed Sep 27, 2023
1 parent d84ebbb commit de98d69
Show file tree
Hide file tree
Showing 45 changed files with 869 additions and 1,210 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
- [ ] Closes #xxxx
- [ ] Tests added
- [ ] Passes `isort -rc . && black . && flake8`
- [ ] Fully documented (eg. docstrings)
- [ ] Fully documented (e.g. docstrings)
4 changes: 4 additions & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ Thomas Pfaff - Blue Yonder
Irene Crisologo - University of Potsdam
Stephan Jacobi - University of Potsdam
Christian Chwala - Karlsruhe Institute of Technology
Julián Alberto Giles - University of Bonn

See a full list of contributors at
https://github.com/wradlib/wradlib/graphs/contributors.
1 change: 1 addition & 0 deletions ci/requirements/notebooktests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dependencies:
- python>=3.9
- bottleneck
- cartopy
- cmweather
- codecov
- coverage
- dask
Expand Down
2 changes: 1 addition & 1 deletion doc/dev_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ $ git clone https://github.com/wradlib/wradlib.git

## Contributing

Everyone can contribute to the developement of {{wradlib}} by using the Fork and Pull model. For this purpose, you need to set up ``Git`` (see section [](#setup)). Then start a [Pull Request](https://help.github.com/articles/creating-a-pull-request)!
Everyone can contribute to the developement of {{wradlib}} by using the Fork and Pull model. For this purpose, you need to set up ``Git`` (see section [Setup](#setup)). Then start a [Pull Request](https://help.github.com/articles/creating-a-pull-request)!

* **Step 1:** [Fork](https://github.com/wradlib/wradlib) your own {{wradlib}} repository from the {{wradlib}} [main repo](https://github.com/wradlib/wradlib).
* **Step 2:** Implement your changes into the forked repository. Test your code.
Expand Down
6 changes: 3 additions & 3 deletions doc/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Anaconda/Conda

In order to run {{wradlib}}, you need to have a Python interpreter installed on your local computer, as well as a number of Python packages ([](#dependencies)). We recommend installing [Anaconda](https://www.anaconda.com/download) as it includes Python, numerous required packages, and other useful tools (e.g. [Spyder](https://www.spyder-ide.org/)).
In order to run {{wradlib}}, you need to have a Python interpreter installed on your local computer, as well as [a number of Python packages](#dependencies). We recommend installing [Anaconda](https://www.anaconda.com/download) as it includes Python, numerous required packages, and other useful tools (e.g. [Spyder](https://www.spyder-ide.org/)).

Using Anaconda the installation process is harmonised across platforms. Download and install the latest [Anaconda distribution](https://www.anaconda.com/download) for your specific OS. We recommend using the minimal distributions [Miniconda](https://conda.io/miniconda.html) or [Miniforge/Mambaforge](https://github.com/conda-forge/miniforge) if you do not want to install a full scientific python stack.

Expand Down Expand Up @@ -72,13 +72,13 @@ If everything is ok, this will show the running {{wradlib}} version. If the {{wr
ImportError: No module named wradlib
```

Alternatively, you can install the [](#bleeding-edge-code), but you have to keep track of {{wradlib}}'s dependencies yourself.
Alternatively, you can install the [Dependencies](#bleeding-edge-code), but you have to keep track of {{wradlib}}'s dependencies yourself.


## Bleeding edge code

:::{warning}
The {{wradlib}} version on [PyPI](https://pypi.org/project/wradlib) might lag behind the actual developments. You can use the bleeding edge code from the {{wradlib}} [repository](https://github.com/wradlib/wradlib). Note, however, that you need to make sure yourself that all [](#dependencies) are met (see below).
The {{wradlib}} version on [PyPI](https://pypi.org/project/wradlib) might lag behind the actual developments. You can use the bleeding edge code from the {{wradlib}} [repository](https://github.com/wradlib/wradlib). Note, however, that you need to make sure yourself that all [Dependencies](#dependencies) are met (see below).
:::

[Download](https://codeload.github.com/wradlib/wradlib/zip/main) the source, unzip, and run:
Expand Down
6 changes: 4 additions & 2 deletions doc/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ Implemented Xarray Accessor `wrl.dp` ({py:class}`wradlib.dp.DpMethods`) with:

- {py:func}`~wradlib.dp.depolarization`
- {py:func}`~wradlib.dp.kdp_from_phidp`
- {py:func}`~wradlib.dp.process_raw_phidp_vulpiani`
- {py:func}`~wradlib.dp.phidp_kdp_vulpiani`
- {py:func}`~wradlib.dp.texture`
- {py:func}`~wradlib.dp.unfold_phi_naive`
- {py:func}`~wradlib.dp.unfold_phi`
- {py:func}`~wradlib.dp.unfold_phi_vulpiani`

### Deprecations

- `wrl.dp.linear_despeckle` -> {py:func}`wradlib.util.despeckle`
- `wrl.dp.process_raw_phidp_vulpiani` -> {py:func}`wradlib.dp.phidp_kdp_vulpiani`
- `wrl.dp.unfold_phi_naive` -> {py:func}`~wradlib.dp.unfold_phi`


## georef-submodule
### Additions
Expand Down
13 changes: 13 additions & 0 deletions doc/refs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,16 @@ @article{Ryzhkov2017
doi = {10.1175/JAMC-D-16-0098.1},
URL = {https://doi.org/10.1175/JAMC-D-16-0098.1},
}

@article{Pejcic2022,
title={Five years calibrated observations from the University of Bonn X-band weather radar (BoXPol)},
author={Pejcic, Velibor and Soderholm, Joshua and M{\"u}hlbauer, Kai and Louf, Valentin and Tr{\"o}mel, Silke},
journal={Scientific Data},
volume={9},
number={1},
pages={551},
year={2022},
publisher={Nature Publishing Group UK London},
doi = {https://doi.org/10.1038/s41597-022-01656-0},
URL = {https://www.nature.com/articles/s41597-022-01656-0},
}
17 changes: 13 additions & 4 deletions doc/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@ You can install the latest {{wradlib}} release from PyPI via ``$ python -m pip i

## Version 2.0.0

This version brings doc-folder back into wradlib repo. The wradlib-docs repo will be archived. wradlib-notebooks repo has been restructured as well, with a single point of access.
For {{wradlib}} 2.0 there have been quite some deprecations and additions. Most of the changes have been announced over the latest {{wradlib}} 1 versions. Nevertheless, to make a clean cut it was neccessary to change and remove code and functionality which was not yet being officially deprecated.

Please follow up at <project:#migration>.

The major new feature is the smooth integration into `xradar`-based reading into `xarray`-based data structures and the convenient use of `xarray`-accessors. The tutorials and examples got a major overhaul to show the capabilities of {{wradlib}} in the light of these new developments.

This version also brings doc-folder back into wradlib repo. The wradlib-docs repo will be archived. wradlib-notebooks repo has been restructured as well, with a single point of access.

**New features**

* final wradlib 2.0.0-rc.1 preparations ({pull}`636`) by {at}`kmuehlbauer`
* wradlib 2.0 preparations ({pull}`635`) by {at}`kmuehlbauer`
* DOC: reintroduce doc into wradlib repository ({pull}`619`) by {at}`kmuehlbauer`
* ENH: introduce wradlib xarray accessor for easy access of wradlib functions from xarray.DataArray (with dp and zr modules prefilled), adapt tests ({pull}`621`) by {at}`kmuehlbauer`
* DOC: reintroduce doc into wradlib repository ({pull}`619`) by {at}`kmuehlbauer`
* FIX: wrong prediction_time variable in radolan xarray.Dataset ({pull}`639`) by {at}`Hoffmann77`
* wradlib 2.0 preparations ({pull}`635`) by {at}`kmuehlbauer`
* final wradlib 2.0.0-rc.1 preparations ({pull}`636`) by {at}`kmuehlbauer`
* wradlib docstring updates ({pull}`638`) by {at}`kmuehlbauer`
* wradlib 2.0 RC ({pull}`640`) by {at}`kmuehlbauer`

**Maintenance - CI**

Expand Down
1 change: 1 addition & 0 deletions requirements_optional.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cartopy
cmweather
dask
gdal>=3
geopandas
Expand Down
49 changes: 27 additions & 22 deletions wradlib/adjust.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
adjuster = AdjustAdd(obs_coords, raw_coords)
adjusted = adjuster(obs, raw)
Both ``obs`` and ``raw`` need to be flat (1-dimensional) arrays of shape (n,)
that have the same length as the the ``obs_coords`` and ``raw_coords`` arrays,
Both ``obs`` and ``raw`` need to be flat (1-dimensional) arrays of shape (n, )
that have the same length as the ``obs_coords`` and ``raw_coords`` arrays,
respectively.
The user can specify the approach that should be used to interpolate the error
Expand All @@ -72,8 +72,8 @@
influence the behaviour of the adjustment (which gauges are considered,
how is an error interpolation carried out, ...). Read the docs carefully
and try to experiment with the effects of the different control parameters.
There might be situations in which the algorithms decides - based on the
control parameter - not to do an adjustment and just return the unadjusted
There might be situations in which the algorithms decide - based on the
control parameter - not to do an adjustment and just return the unadjusted
values.
Cross validation
Expand Down Expand Up @@ -158,7 +158,7 @@ class AdjustBase(ipol.IpolBase):
- *method*: string
defaults to 'linregr' which fits a regression line through observed
and estimated values and than gets the bias from the inverse of
and estimated values and then gets the bias from the inverse of
the slope.
Other values: 'mean' or 'median' compute the mean or the median of
the ratios between gauge and radar observations.
Expand Down Expand Up @@ -201,10 +201,11 @@ def __init__(
# Check arguments
if mfb_args is None:
mfb_args = dict(method="linregr", minslope=0.1, minr=0.5, maxp=0.01)
assert mfb_args["method"] in ["mean", "median", "linregr"], (
"Argument mfb_args['method'] has to be one "
"out of 'mean', 'median' or 'linregr'."
)
if mfb_args["method"] not in ["mean", "median", "linregr"]:
raise ValueError(
"Argument mfb_args['method'] has to be one "
"out of `mean`, `median` or `linregr`."
)

# These are the coordinates of the rain gage locations and
# the radar bin locations
Expand Down Expand Up @@ -242,7 +243,7 @@ def _checkip(self, ix, targets):
When an instance of an Adjust... class is created, an instance of the
desired
Interpolation class (argument ipclass) is created as attribute
*self.ip*). However, this instance is only valid in case all
*self.ip*. However, this instance is only valid in case all
observation points (attribute *self.obs_coords*) have valid
observation-radar pairs. In case points are missing (or in case the
instance is called in the sourse of cross validation), a new instance
Expand All @@ -266,7 +267,7 @@ def _checkip(self, ix, targets):
an instance of a class that inherited from :class:`wradlib.ipol.IpolBase`
"""
# first, set interpolation targets (default: the radar coordinates)
# first, set interpolation targets (default: the radar coordinates)
targets_default = False
if targets is None:
targets = self.raw_coords
Expand All @@ -283,12 +284,12 @@ def __call__(self, obs, raw, *, targets=None, rawatobs=None, ix=None):
Parameters
----------
obs : :py:class:`numpy:numpy.ndarray`
flat (1-D) array of floats with shape (num gauges,)
flat (1-D) array of floats with shape (num gauges, )
These are the gage observations used for adjustment. This array
needs to be of the same length as the array "obs_coords" used to
initialize the adjustment object.
raw : :py:class:`numpy:numpy.ndarray`
flat (1-D) array of floats with shape (num radar cells,)
flat (1-D) array of floats with shape (num radar cells, )
These are the raw (unadjusted) radar rainfall values. This array
needs to be of the same length as the array "raw_coords" used to
initialize the adjustment object.
Expand Down Expand Up @@ -517,7 +518,7 @@ class AdjustMixed(AdjustBase):
R_{gage} = R_{radar} \\cdot (1 + \\delta) +0 \\epsilon
:math:`\\delta` and :math:`\\epsilon` have to be assumed to be independent
and normally distributed. The present implementation is based on a Least
and normally distributed. The present implementation is based on teh Least
Squares estimation of :math:`\\delta` and :math:`\\epsilon` for each rain
gage location. :math:`\\delta` and :math:`\\epsilon` are then interpolated
and used to correct the radar rainfall field.
Expand Down Expand Up @@ -579,7 +580,7 @@ def __call__(self, obs, raw, *, targets=None, rawatobs=None, ix=None):
ip = self._checkip(ix, targets)

# -----------------THIS IS THE ACTUAL ADJUSTMENT APPROACH--------------
# computing epsilon and delta from least squares
# computing epsilon and delta from the least squares
epsilon = (obs[ix] - rawatobs[ix]) / (rawatobs[ix] ** 2 + 1.0)
delta = ((obs[ix] - epsilon) / rawatobs[ix]) - 1.0
# interpolate error fields
Expand Down Expand Up @@ -821,8 +822,7 @@ def _get_neighbours_ix(obs_coords, raw_coords, nnear):
array of float of shape (num_points,ndim)
from these coordinate pairs the neighbours are selected
nnear : int
number of neighbours to be selected per coordinate pair of
``obs_coords``
number of neighbours to be selected per coordinate ``obs_coords``
"""
# plant a tree
Expand Down Expand Up @@ -854,7 +854,7 @@ def newfunc(x, y):
newfunc = best
else:
# if no function can be found, raise an Exception
raise NameError("Unknown function name option: " + funcname)
raise NameError(f"Unknown function name option: {funcname!r}")
return newfunc


Expand All @@ -877,12 +877,17 @@ def best(x, y, /):
"""
if type(x) == np.ndarray:
assert x.ndim == 1, "x must be a 1-d array of floats or a float."
assert len(x) == len(y), "Length of x and y must be equal."
if x.ndim != 1:
raise ValueError("`x` must be a 1-d array of floats or a float.")
if len(x) != len(y):
raise ValueError(
f"Length of `x` ({len(x)}) and `y` ({len(y)}) must be equal."
)
if type(y) == np.ndarray:
assert y.ndim <= 2, "y must be 1-d or 2-d array of floats."
if y.ndim > 2:
raise ValueError("'y' must be 1-d or 2-d array of floats.")
else:
raise ValueError("y must be 1-d or 2-d array of floats.")
raise ValueError("`y` must be 1-d or 2-d array of floats.")
x = np.array(x).reshape((-1, 1))
if y.ndim == 1:
y = np.array(y).reshape((1, -1))
Expand Down
40 changes: 17 additions & 23 deletions wradlib/atten.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,15 @@ def correct_attenuation_hb(
polar form with `m` azimuths and `n` range-bins the input array's
shape can be either (l,m,n) or (m,l,n)
data has to be provided in decibel representation of reflectivity [dBZ]
a : float
proportionality factor of the k-Z relation (:math:`k=a \\cdot Z^{b}`).
Per default set to 1.67e-4.
b : float
exponent of the k-Z relation ( :math:`k=a \\cdot Z^{b}` ). Per default
set to 0.7.
gate_length : float
length of a range gate [km]. Per default set to 1.0.
coefficients : dict
- a : float
proportionality factor of the k-Z relation (:math:`k=a \\cdot Z^{b}`).
Per default set to 1.67e-4.
- b : float
exponent of the k-Z relation ( :math:`k=a \\cdot Z^{b}` ). Per default
set to 0.7.
- gate_length : float
length of a range gate [km]. Per default set to 1.0.
mode : str
controls how the function reacts, if the sum of signal and attenuation
exceeds the threshold ``thrs``
Expand Down Expand Up @@ -206,7 +207,7 @@ def bisect_reference_attenuation(
max_iterations=10,
):
"""Find the optimal attenuation coefficients for a gateset to achieve a \
given reference attenuation using a the forward correction algorithm in \
given reference attenuation using the forward correction algorithm in \
combination with the bisection method.
Parameters
Expand All @@ -222,12 +223,12 @@ def bisect_reference_attenuation(
the last dimension is 1, as it constitutes the reference pia [dB] of
the last range gate of every beam.
a_max : float
Upper bound of the bisection interval within the linear coefficient a
Upper bound of the bisection interval within the linear coefficient ``a``
of the k-Z relation has to be. ( :math:`k=a \\cdot Z^{b}` ).
Per default set to 1.67e-4.
a_min : float
Lower bound of the bisection interval within the linear coefficient a
Lower bound of the bisection interval within the linear coefficient ``a``
of the k-Z relation has to be. ( :math:`k=a \\cdot Z^{b}` ).
Per default set to 2.33e-5.
Expand Down Expand Up @@ -295,7 +296,7 @@ def bisect_reference_attenuation(
undershoot = ((pia[..., -1] - pia_ref) / pia_ref) < -thrs
hit = (np.abs(pia[..., -1] - pia_ref) / pia_ref) < thrs
else:
raise Exception("Unknown mode type " + mode + ".")
raise Exception(f"Unknown mode type {mode}.")
# Define new bounds of linear k-Z relation coefficient for over- and
# undershooting pia calculations.
a_hi[overshoot] = a_mid[overshoot]
Expand Down Expand Up @@ -345,7 +346,7 @@ def _interp_atten(pia, invalidbeams):
"""Interpolate reference pia of most distant rangebin of small invalid
sectors as a prerequisite for the backward calculation of attenuation.
"""
# Build an spatial equidistant array for interpolation of the ahead and
# Build a spatial equidistant array for interpolation of the ahead and
# behind extended temporary pia-array for handling invalid sectors
# overlapping the seam of the radarcircle.
x = np.arange(3 * pia.shape[1])
Expand Down Expand Up @@ -395,50 +396,42 @@ def correct_attenuation_constrained(
to be performed) are supposed to vary along the last array-dimension
and the azimuths are supposed to vary along the next to last
array-dimension.
Data has to be provided in decibel representation of reflectivity
[dBZ].
a_max : float
Initial value for linear coefficient of the k-Z relation
( :math:`k=a \\cdot Z^{b}` ).
Per default set to 1.67e-4.
a_min : float
Minimal allowed linear coefficient of the k-Z relation
( :math:`k=a \\cdot Z^{b}` ) in the downwards iteration of 'a' in case
of breaching one of thresholds ``constr_args`` of the optional
conditions ``constraints``.
Per default set to 2.33e-5.
n_a : int
Number of iterations from ``a_max`` to ``a_min``.
Per default set to 4.
b_max : float
Initial value for exponential coefficient of the k-Z relation
( :math:`k=a \\cdot Z^{b}` ).
Per default set to 0.7.
b_min : float
Minimal allowed exponential coefficient of the k-Z relation
( :math:`k=a \\cdot Z^{b}` ) in the downwards iteration of 'b' in case
of breaching one of thresholds ``constr_args`` of the optional
conditions ``constraints`` and the linear coefficient 'a' has already
reached the lower limit ``a_min``.
Per default set to 0.65.
n_b : int
Number of iterations from ``b_max`` to ``b_min``.
Per default set to 6.
gate_length : float
Radial length of a range gate [km].
Per default set to 1.0.
constraints : list
List of constraint functions. The signature of these functions has to
be constraint_function(`gateset`, `k`, `*constr_args`). Their return
value must be a boolean array of shape gateset.shape[:-1] set to True
value must be a boolean array of shape `gateset.shape[:-1]` set to True
for beams, which do not fulfill the constraint.
constraint_args : list
List of lists, which are to be passed to the individual constraint
Expand Down Expand Up @@ -634,7 +627,8 @@ def correct_radome_attenuation_empirical(
A numpy function for statistical aggregation of the
central rangebins defined by n_r.
Potential options: np.mean, np.median, np.max, np.min.
Potential options: :func:`numpy:numpy.mean`, :func:`numpy:numpy.median`,
:func:`numpy:numpy.max`, :func:`numpy:numpy.min`.
Returns
-------
Expand Down
Loading

0 comments on commit de98d69

Please sign in to comment.