Skip to content

Commit bc863d0

Browse files
authored
Merge pull request #116 from rcjackson/pydda20
PyDDA 2.0!
2 parents 3fafec9 + f749cae commit bc863d0

46 files changed

Lines changed: 1992 additions & 1060 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pypi-release.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Build and Upload PySP2 Release to PyPI
2+
on:
3+
release:
4+
types:
5+
- published
6+
7+
jobs:
8+
build-artifacts:
9+
runs-on: ubuntu-latest
10+
if: github.repository == 'openradar/PyDDA'
11+
steps:
12+
- uses: actions/checkout@v3
13+
with:
14+
fetch-depth: 0
15+
- uses: actions/setup-python@v4
16+
name: Install Python
17+
with:
18+
python-version: 3.11
19+
20+
- name: Install dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
python -m pip install setuptools setuptools-scm wheel twine check-manifest
24+
- name: Build tarball and wheels
25+
run: |
26+
git clean -xdf
27+
git restore -SW .
28+
python -m build --sdist --wheel .
29+
- name: Check built artifacts
30+
run: |
31+
python -m twine check dist/*
32+
pwd
33+
if [ -f dist/pydda-0.0.0.tar.gz ]; then
34+
echo "❌ INVALID VERSION NUMBER"
35+
exit 1
36+
else
37+
echo "✅ Looks good"
38+
fi
39+
- uses: actions/upload-artifact@v3
40+
with:
41+
name: releases
42+
path: dist
43+
44+
test-built-dist:
45+
needs: build-artifacts
46+
runs-on: ubuntu-latest
47+
steps:
48+
- uses: actions/setup-python@v4
49+
name: Install Python
50+
with:
51+
python-version: "3.x"
52+
- uses: actions/download-artifact@v3
53+
with:
54+
name: releases
55+
path: dist
56+
- name: List contents of built dist
57+
run: |
58+
ls -ltrh
59+
ls -ltrh dist
60+
upload-to-pypi:
61+
needs: test-built-dist
62+
if: github.event_name == 'release'
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/download-artifact@v3
66+
with:
67+
name: releases
68+
path: dist
69+
- name: Publish package to PyPI
70+
uses: pypa/gh-action-pypi-publish@v1.8.10
71+
with:
72+
user: __token__
73+
password: ${{ secrets.PYPI_TOKEN }}
74+
verbose: true

.github/workflows/python-package-conda.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
strategy:
1818
fail-fast: false
1919
matrix:
20-
python-version: ["3.9", "3.10", "3.11"]
20+
python-version: ["3.9", "3.10", "3.11", "3.12"]
2121
os: [macOS, ubuntu]
2222
inlcude:
2323
- os: macos-latest

REQUIREMENTS.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ six
88
pooch
99
cmweather
1010
cdsapi
11+
xarray
12+
datatree

doc/source/index.rst

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,19 @@ just type in the following commands assuming you have the above dependencies ins
139139

140140
Finally, PyDDA now supports using `Jax <jax.readthedocs.io>`_ and `TensorFlow <tensorflow.org>`_
141141
for solving the three dimensional wind field. PyDDA requries TensorFlow 2.6 and the
142-
tensorflow-probability package for TensorFlow to be enabled. Both the Jax and
143-
TensorFlow-based engines now use automatic differentiation to solve for the gradients
144-
of each cost function. This therefore will create gradients that are less susceptible
145-
to boundary artifacts and rounding errors. In addition, both of these packages can
146-
utilize CUDA-enabled GPUs for much faster processing. These two
142+
tensorflow-probability package for TensorFlow to be enabled.
143+
In addition, both of these packages can utilize CUDA-enabled GPUs for much faster processing. These two
147144
dependencies are optional as the user can still use PyDDA with the SciPy ecosystem.
145+
The Jax optimizer uses the same optimizer as SciPy's (`L-BFGS-B <https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html>`_).
146+
147+
148+
Known issues
149+
============
150+
151+
The TensorFlow engine uses the unbounded version of this optimizer which removes the constraint that the
152+
the wind magnitudes must be less than 100 m/s. The removal of this constraint can sometimes
153+
result in numerical instability, so it is recommended that the user test out both Jax and TensorFlow
154+
if they desire GPU-accelerated retrievals.
148155

149156
Contents:
150157

doc/source/user_guide/index.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ This is a place to include our user guide.
1414
retrieving_winds.rst
1515
optimizing_wind_retrieval.rst
1616
visualizing_winds.rst
17+
retrieving_winds.rst
18+
optimizing_wind_retrieval.rst
19+
nesting_wind_retrieval.rst

doc/source/user_guide/optimizing_wind_retrieval.rst

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ that we did in :ref:`retrieving_winds`.
9595
grid_shape=grid_shape, gatefilter=gatefilter_kict,
9696
grid_origin=(radar_kict.latitude['data'].filled(),
9797
radar_kict.longitude['data'].filled()))
98-
98+
grid_ktlx = pydda.io.read_from_pyart_grid(grid_ktlx)
99+
grid_kict = pydda.io.read_from_pyart_grid(grid_kict)
99100
grid_kict = pydda.constraints.add_hrrr_constraint_to_grid(grid_kict,
100101
pydda.tests.get_sample_file('ruc2anl_130_20110520_0800_001.grb2'), method='linear')
101102

@@ -200,7 +201,8 @@ to the above retrieval.
200201
grid_shape=grid_shape, gatefilter=gatefilter_kict,
201202
grid_origin=(radar_kict.latitude['data'].filled(),
202203
radar_kict.longitude['data'].filled()))
203-
204+
grid_ktlx = pydda.io.read_from_pyart_grid(grid_ktlx)
205+
grid_kict = pydda.io.read_from_pyart_grid(grid_kict)
204206
grid_kict = pydda.constraints.add_hrrr_constraint_to_grid(grid_kict,
205207
pydda.tests.get_sample_file('ruc2anl_130_20110520_0800_001.grb2'), method='linear')
206208

@@ -302,7 +304,8 @@ Let's see what happens when we increase the level of smoothing.
302304
grid_shape=grid_shape, gatefilter=gatefilter_kict,
303305
grid_origin=(radar_kict.latitude['data'].filled(),
304306
radar_kict.longitude['data'].filled()))
305-
307+
grid_ktlx = pydda.io.read_from_pyart_grid(grid_ktlx)
308+
grid_kict = pydda.io.read_from_pyart_grid(grid_kict)
306309
grid_kict = pydda.constraints.add_hrrr_constraint_to_grid(grid_kict,
307310
pydda.tests.get_sample_file('ruc2anl_130_20110520_0800_001.grb2'), method='linear')
308311

@@ -405,7 +408,8 @@ artifact near the edge of the Dual Doppler lobe re-appears.
405408
grid_shape=grid_shape, gatefilter=gatefilter_kict,
406409
grid_origin=(radar_kict.latitude['data'].filled(),
407410
radar_kict.longitude['data'].filled()))
408-
411+
grid_ktlx = pydda.io.read_from_pyart_grid(grid_ktlx)
412+
grid_kict = pydda.io.read_from_pyart_grid(grid_kict)
409413
grid_kict = pydda.constraints.add_hrrr_constraint_to_grid(grid_kict,
410414
pydda.tests.get_sample_file('ruc2anl_130_20110520_0800_001.grb2'), method='linear')
411415

doc/source/user_guide/retrieving_winds.rst

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,21 @@ point, or model data as a weak constraint in order to increase the chance that P
3838
provide a solution that converges to a physically realistic wind field. For this particular example,
3939
we are lucky enough to have model data from the Rapid Update Cycle that can be used as a constraint.
4040

41-
Therefore, let's first add the model data into our Grid objects so that PyDDA can use the model
42-
data as a constraint.
41+
------------------------
42+
Using PyDDA's data model
43+
------------------------
44+
45+
As of PyDDA 2.0, PyDDA uses `xarray Datasets <https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html>`_
46+
to represent the underlying datastructure. This makes it easier to integrate PyDDA into xarray-based workflows
47+
that are the standard for use in the Geoscientific Python Community. Therefore, anyone using PyART Grids will need
48+
to convert their grids to PyDDA Grids using the :meth:`pydda.io.read_from_pyart_grid` helper function.
49+
50+
.. code-block:: python
51+
52+
grid_ktlx = pydda.io.read_from_pyart_grid(grid_ktlx)
53+
grid_kict = pydda.io.read_from_pyart_grid(grid_kict)
54+
55+
In addition, :meth:`pydda.io.read_grid` will read a cf-Compliant radar grid into a PyDDA Grid.
4356

4457
----------------------------
4558
Using models for constraints
@@ -50,10 +63,11 @@ provide a constraint on the horizontal winds. This helps to constrain the backgr
5063
area where there is suboptimal coverage from the radar network outside of the dual
5164
Doppler lobes. To add either a RUC or HRRR model time period as a constraint, we need
5265
to have the original model data in GRIB format and then use the following line to
53-
load the model data into a PyART grid for processing.
66+
load the model data into a PyDDA grid for processing.
5467

5568
.. code-block:: python
5669
70+
# Add constraints
5771
grid_kict = pydda.constraints.add_hrrr_constraint_to_grid(grid_kict,
5872
pydda.tests.get_sample_file('ruc2anl_130_20110520_0800_001.grb2'), method='linear')
5973
@@ -75,7 +89,7 @@ Retrieving your first wind field
7589
--------------------------------
7690

7791
We will then take a first attempt at retrieving a wind field using PyDDA. This is done using the
78-
:code:`pydda.retrieval.get_dd_wind_field` function. This function takes in a minimum of one input, a list
92+
:meth:`pydda.retrieval.get_dd_wind_field` function. This function takes in a minimum of one input, a list
7993
of input PyART Grids. We will also specify the constants for the constraints. In this case, we are using
8094
the mass continuity, radar observation, smoothness, and model constraints.
8195

@@ -176,7 +190,8 @@ key on the bottom right interior of the plot.
176190
grid_shape=grid_shape, gatefilter=gatefilter_kict,
177191
grid_origin=(radar_kict.latitude['data'].filled(),
178192
radar_kict.longitude['data'].filled()))
179-
193+
grid_ktlx = pydda.io.read_from_pyart_grid(grid_ktlx)
194+
grid_kict = pydda.io.read_from_pyart_grid(grid_kict)
180195
grid_kict = pydda.constraints.add_hrrr_constraint_to_grid(grid_kict,
181196
pydda.tests.get_sample_file('ruc2anl_130_20110520_0800_001.grb2'), method='linear')
182197

examples/plot_examples.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,15 @@
99
1010
"""
1111

12-
import pyart
1312
import pydda
14-
import numpy as np
1513
from matplotlib import pyplot as plt
1614

17-
18-
berr_grid = pyart.io.read_grid(pydda.tests.EXAMPLE_RADAR0)
19-
cpol_grid = pyart.io.read_grid(pydda.tests.EXAMPLE_RADAR1)
20-
15+
berr_grid = pydda.io.read_grid(pydda.tests.EXAMPLE_RADAR0)
16+
cpol_grid = pydda.io.read_grid(pydda.tests.EXAMPLE_RADAR1)
2117

2218
# Load sounding data and insert as an intialization
23-
cpol_grid = pydda.initialization.make_constant_wind_field(
24-
cpol_grid, (0.0, 0.0, 0.0), vel_field="corrected_velocity"
19+
berr_grid = pydda.initialization.make_constant_wind_field(
20+
berr_grid, (0.0, 0.0, 0.0), vel_field="corrected_velocity"
2521
)
2622

2723
# Start the wind retrieval. This example only uses the mass continuity
@@ -41,6 +37,7 @@
4137
wind_tol=0.5,
4238
engine="scipy",
4339
)
40+
4441
# Plot a horizontal cross section
4542
plt.figure(figsize=(9, 9))
4643
pydda.vis.plot_horiz_xsection_barbs(
@@ -50,9 +47,11 @@
5047
w_vel_contours=[5, 10, 15],
5148
barb_spacing_x_km=5.0,
5249
barb_spacing_y_km=15.0,
50+
vmin=0,
51+
vmax=70,
5352
)
5453
plt.show()
55-
plt.savefig("Darwin_horiz.png")
54+
5655
# Plot a vertical X-Z cross section
5756
plt.figure(figsize=(9, 9))
5857
pydda.vis.plot_xz_xsection_barbs(
@@ -62,6 +61,8 @@
6261
w_vel_contours=[5, 10, 15],
6362
barb_spacing_x_km=10.0,
6463
barb_spacing_z_km=2.0,
64+
vmin=0,
65+
vmax=70,
6566
)
6667
plt.show()
6768

@@ -73,5 +74,7 @@
7374
level=40,
7475
barb_spacing_y_km=10.0,
7576
barb_spacing_z_km=2.0,
77+
vmin=0,
78+
vmax=70,
7679
)
77-
plt.savefig("Darwin.png")
80+
plt.show()

examples/plot_fun_with_constraints.py

Lines changed: 0 additions & 117 deletions
This file was deleted.

0 commit comments

Comments
 (0)