Example on retrieving and plotting winds#

This is a simple example for how to retrieve and plot winds from 2 radars using PyDDA.

Author: Robert C. Jackson

import pydda
from matplotlib import pyplot as plt

berr_grid = pydda.io.read_grid(pydda.tests.EXAMPLE_RADAR0)
cpol_grid = pydda.io.read_grid(pydda.tests.EXAMPLE_RADAR1)

# Load sounding data and insert as an intialization
berr_grid = pydda.initialization.make_constant_wind_field(
    berr_grid, (0.0, 0.0, 0.0), vel_field="corrected_velocity"
)

# Start the wind retrieval. This example only uses the mass continuity
# and data weighting constraints.
Grids, _ = pydda.retrieval.get_dd_wind_field(
    [berr_grid, cpol_grid],
    Co=1.0,
    Cm=256.0,
    Cx=0.0,
    Cy=0.0,
    Cz=0.0,
    Cb=0.0,
    frz=5000.0,
    filter_window=5,
    mask_outside_opt=True,
    upper_bc=1,
    wind_tol=0.5,
    engine="scipy",
    parallel=False,
)

# Plot a horizontal cross section
plt.figure(figsize=(9, 9))
pydda.vis.plot_horiz_xsection_barbs(
    Grids,
    background_field="reflectivity",
    level=6,
    w_vel_contours=[5, 10, 15],
    barb_spacing_x_km=5.0,
    barb_spacing_y_km=15.0,
    vmin=0,
    vmax=70,
)
plt.show()

# Plot a vertical X-Z cross section
plt.figure(figsize=(9, 9))
pydda.vis.plot_xz_xsection_barbs(
    Grids,
    background_field="reflectivity",
    level=40,
    w_vel_contours=[5, 10, 15],
    barb_spacing_x_km=10.0,
    barb_spacing_z_km=2.0,
    vmin=0,
    vmax=70,
)
plt.show()

# Plot a vertical Y-Z cross section
plt.figure(figsize=(9, 9))
pydda.vis.plot_yz_xsection_barbs(
    Grids,
    background_field="reflectivity",
    level=40,
    barb_spacing_y_km=10.0,
    barb_spacing_z_km=2.0,
    vmin=0,
    vmax=70,
)
plt.show()
## You are using the Python ARM Radar Toolkit (Py-ART), an open source
## library for working with weather radar data. Py-ART is partly supported
## by the U.S. Department of Energy Office of Science as part of
## the Atmospheric Radiation Measurement (ARM) User Facility.
##
## If you use this software to prepare a publication, please cite:
##
##     JJ Helmus and SM Collis, JORS 2016, doi: 10.5334/jors.119
Welcome to PyDDA 2.5.0
If you are using PyDDA in your publications, please cite:
Jackson et al. (2020) Journal of Open Research Science
Detecting Jax...
Jax/JaxOpt are not installed on your system, unable to use Jax engine.
Detecting TensorFlow...
Unable to load both TensorFlow and tensorflow-probability. TensorFlow engine disabled.
No module named 'tensorflow'
False
Calculating weights for radars 0 and 1
Calculating weights for radars 1 and 0
Calculating weights for models...
Starting solver 
rmsVR = 6.827303971100176
Total points: 81194
The max of w_init is 0.0
Total number of model points: 0
Nfeval | Jvel    | Jmass   | Jsmooth |   Jbg   | Jvort   | Jmodel  | Jpoint  | Max w  
      0|83859.8222|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000
The gradient of the cost functions is 0.6631357968851933
Nfeval | Jvel    | Jmass   | Jsmooth |   Jbg   | Jvort   | Jmodel  | Jpoint  | Max w  
     10|   1.8782|  41.0864|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000|  11.3085
Max change in w: 10.561
The gradient of the cost functions is 0.1375063575316335
Nfeval | Jvel    | Jmass   | Jsmooth |   Jbg   | Jvort   | Jmodel  | Jpoint  | Max w  
     20|   0.3524|  18.6065|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000|  11.8823
Max change in w: 6.008
The gradient of the cost functions is 0.09826186599547497
Nfeval | Jvel    | Jmass   | Jsmooth |   Jbg   | Jvort   | Jmodel  | Jpoint  | Max w  
     30|   0.1953|  10.8042|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000|  12.4878
Max change in w: 4.002
The gradient of the cost functions is 0.09669298385312876
Nfeval | Jvel    | Jmass   | Jsmooth |   Jbg   | Jvort   | Jmodel  | Jpoint  | Max w  
     40|   0.1234|   7.0816|   0.0000|   0.0000|   0.0000|   0.0000|   0.0000|  16.4750
Max change in w: 5.363
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[1], line 14
     10 )
     11 
     12 # Start the wind retrieval. This example only uses the mass continuity
     13 # and data weighting constraints.
---> 14 Grids, _ = pydda.retrieval.get_dd_wind_field(
     15     [berr_grid, cpol_grid],
     16     Co=1.0,
     17     Cm=256.0,

File ~/work/PyDDA/PyDDA/pydda/retrieval/wind_retrieve.py:1587, in get_dd_wind_field(Grids, u_init, v_init, w_init, engine, **kwargs)
   1580     w_init = new_grids[0]["w"].values.squeeze()
   1582 if (
   1583     engine.lower() == "scipy"
   1584     or engine.lower() == "jax"
   1585     or engine.lower() == "auglag"
   1586 ):
-> 1587     return _get_dd_wind_field_scipy(
   1588         new_grids, u_init, v_init, w_init, engine, **kwargs
   1589     )
   1590 elif engine.lower() == "tensorflow":
   1591     return _get_dd_wind_field_tensorflow(
   1592         new_grids, u_init, v_init, w_init, **kwargs
   1593     )

File ~/work/PyDDA/PyDDA/pydda/retrieval/wind_retrieve.py:667, in _get_dd_wind_field_scipy(Grids, u_init, v_init, w_init, engine, points, vel_name, refl_field, u_back, v_back, z_back, frz, Co, Cm, Cx, Cy, Cz, Cb, Cv, Cmod, Cpoint, cvtol, gtol, Jveltol, Ut, Vt, low_pass_filter, mask_outside_opt, weights_obs, weights_model, weights_bg, max_iterations, mask_w_outside_opt, filter_type, filter_window, filter_order, leise_nstep, min_bca, max_bca, upper_bc, above, model_fields, output_cost_functions, roi, wind_tol, tolerance, const_boundary_cond, max_wind_mag, parallel)
    665 parameters.print_out = False
    666 if engine.lower() == "scipy":
--> 667     winds = fmin_l_bfgs_b(
    668         J_function,
    669         winds,
    670         args=(parameters,),
    671         maxiter=max_iterations,
    672         pgtol=tolerance,
    673         bounds=bounds,
    674         fprime=grad_J,
    675         callback=_vert_velocity_callback,
    676     )
    677 else:
    679     def loss_and_gradient(x):

File /usr/share/miniconda/envs/pydda-docs/lib/python3.14/site-packages/scipy/optimize/_lbfgsb_py.py:259, in fmin_l_bfgs_b(func, x0, fprime, args, approx_grad, bounds, m, factr, pgtol, epsilon, maxfun, maxiter, callback, maxls)
    249 callback = _wrap_callback(callback)
    250 opts = {'maxcor': m,
    251         'ftol': factr * np.finfo(float).eps,
    252         'gtol': pgtol,
   (...)    256         'callback': callback,
    257         'maxls': maxls}
--> 259 res = _minimize_lbfgsb(fun, x0, args=args, jac=jac, bounds=bounds,
    260                        **opts)
    261 d = {'grad': res['jac'],
    262      'task': res['message'],
    263      'funcalls': res['nfev'],
    264      'nit': res['nit'],
    265      'warnflag': res['status']}
    266 f = res['fun']

File /usr/share/miniconda/envs/pydda-docs/lib/python3.14/site-packages/scipy/optimize/_lbfgsb_py.py:420, in _minimize_lbfgsb(fun, x0, args, jac, bounds, maxcor, ftol, gtol, eps, maxfun, maxiter, callback, maxls, finite_diff_rel_step, workers, **unknown_options)
    412 _lbfgsb.setulb(m, x, low_bnd, upper_bnd, nbd, f, g, factr, pgtol, wa,
    413                iwa, task, lsave, isave, dsave, maxls, ln_task)
    415 if task[0] == 3:
    416     # The minimization routine wants f and g at the current x.
    417     # Note that interruptions due to maxfun are postponed
    418     # until the completion of the current minimization iteration.
    419     # Overwrite f and g:
--> 420     f, g = func_and_grad(x)
    421 elif task[0] == 1:
    422     # new iteration
    423     n_iterations += 1

File /usr/share/miniconda/envs/pydda-docs/lib/python3.14/site-packages/scipy/optimize/_differentiable_functions.py:413, in ScalarFunction.fun_and_grad(self, x)
    411     self._update_x(x)
    412 self._update_fun()
--> 413 self._update_grad()
    414 return self.f, self.g

File /usr/share/miniconda/envs/pydda-docs/lib/python3.14/site-packages/scipy/optimize/_differentiable_functions.py:375, in ScalarFunction._update_grad(self)
    373 if self._orig_grad in FD_METHODS:
    374     self._update_fun()
--> 375 self.g = self._wrapped_grad(self.x, f0=self.f)
    376 self.g_updated = True

File /usr/share/miniconda/envs/pydda-docs/lib/python3.14/site-packages/scipy/optimize/_differentiable_functions.py:39, in _ScalarGradWrapper.__call__(self, x, f0, **kwds)
     35 def __call__(self, x, f0=None, **kwds):
     36     # Send a copy because the user may overwrite it.
     37     # The user of this class might want `x` to remain unchanged.
     38     if callable(self.grad):
---> 39         g = np.atleast_1d(self.grad(np.copy(x), *self.args))
     40     elif self.grad in FD_METHODS:
     41         g, dct = approx_derivative(
     42             self.fun,
     43             x,
     44             f0=f0,
     45             **self.finite_diff_options,
     46         )

File ~/work/PyDDA/PyDDA/pydda/cost_functions/cost_functions.py:635, in grad_J(winds, parameters)
    633     grad = sum(f.result() for f in futures)
    634 else:
--> 635     grad = _cost_functions_numpy.calculate_grad_radial_vel(
    636         parameters.vrs,
    637         parameters.els,
    638         parameters.azs,
    639         winds[0],
    640         winds[1],
    641         winds[2],
    642         parameters.wts,
    643         parameters.weights,
    644         parameters.rmsVr,
    645         coeff=parameters.Co,
    646         upper_bc=parameters.upper_bc,
    647         upper_bc_mask=parameters.upper_bc_mask,
    648     )
    650     if parameters.Cm > 0:
    651         grad += _cost_functions_numpy.calculate_mass_continuity_gradient(
    652             winds[0],
    653             winds[1],
   (...)    661             upper_bc_mask=parameters.upper_bc_mask,
    662         )

File ~/work/PyDDA/PyDDA/pydda/cost_functions/_cost_functions_numpy.py:214, in calculate_grad_radial_vel(vrs, els, azs, u, v, w, wts, weights, rmsVr, coeff, upper_bc, upper_bc_mask, parallel)
    210 p_z1 = np.zeros(vrs[0].shape)
    212 for i in range(len(vrs)):
    213     v_ar = (
--> 214         np.cos(els[i]) * np.sin(azs[i]) * u
    215         + np.cos(els[i]) * np.cos(azs[i]) * v
    216         + np.sin(els[i]) * (w - np.abs(wts[i]))
    217     )
    219     x_grad = (
    220         2 * (v_ar - vrs[i]) * np.cos(els[i]) * np.sin(azs[i]) * weights[i]
    221     ) * lambda_o
    222     y_grad = (
    223         2 * (v_ar - vrs[i]) * np.cos(els[i]) * np.cos(azs[i]) * weights[i]
    224     ) * lambda_o

KeyboardInterrupt: