Within this notebook, we will cover:
Concepts | Importance | Notes |
---|---|---|
Xarray Basics | Helpful | Basic Dataset/DataArray |
Matplotlib Basics | Helpful | Basic Plotting |
Intro to Cartopy | Helpful | Projections |
import datetime as dt
import glob
import os
import sys
import warnings
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from scipy.integrate import cumulative_trapezoid
import wradlib as wrl
import xradar as xd
As a quick example to show the algorithm, we use a file from Down Under. For the further processing we use x-band data from BoXPol research radar at the University of Bonn, Germany.
boxpol = "data/hdf5/boxpol/2014-11-16--03_45_00,00.mvol"
terrey = "data/hdf5/terrey_39.h5"
swp0 = xr.open_dataset(terrey, engine="odim")
swp0 = swp0.pipe(wrl.georef.georeference_dataset).compute()
(we use .compute() here to load the data out of a dask array, otherwise some functions below will complain)
System PHIDP aka Phase Offset
The following function returns phase offset as well as start and stop ranges of the region of interest (first precipitating bins).
def phase_offset(phioff, method=None, rng=3000.0, npix=None, **kwargs):
"""Calculate Phase offset.
Parameter
---------
phioff : xarray.DataArray
differential phase DataArray
Keyword Arguments
-----------------
method : str
aggregation method, defaults to 'median'
rng : float
range in m to calculate system phase offset
Return
------
phidp_offset : xarray.Dataset
Dataset with PhiDP offset and start/stop ranges
"""
range_step = np.diff(phioff.range)[0]
nprec = int(rng / range_step)
if not nprec % 2:
nprec += 1
if npix is None:
npix = nprec // 2 + 1
# create binary array
phib = xr.where(np.isnan(phioff), 0, 1)
# take nprec range bins and calculate sum
phib_sum = phib.rolling(range=nprec, **kwargs).sum(skipna=True)
# find at least N pixels in
# phib_sum_N = phib_sum.where(phib_sum >= npix)
phib_sum_N = xr.where(phib_sum <= npix, phib_sum, npix)
# get start range of first N consecutive precip bins
start_range = (
phib_sum_N.idxmax(dim="range") - nprec // 2 * np.diff(phib_sum.range)[0]
)
start_range = xr.where(start_range < 0, 0, start_range)
# get stop range
stop_range = start_range + rng
# get phase values in specified range
off = phioff.where(
(phioff.range >= start_range) & (phioff.range <= stop_range), drop=False
)
# calculate nan median over range
if method is None:
method = "median"
func = getattr(off, method)
off_func = func(dim="range", skipna=True)
return xr.Dataset(
dict(
PHIDP_OFFSET=off_func,
start_range=start_range,
stop_range=stop_range,
phib_sum=phib_sum,
phib=phib,
)
)
dr_m = swp0.range.diff("range").median()
swp_msk = swp0.where((swp0.DBZH >= 0.0))
swp_msk = swp_msk.where(swp_msk.RHOHV > 0.8)
swp_msk = swp_msk.where(swp_msk.range > dr_m * 5)
phi_masked = swp_msk.PHIDP.copy()
off = phase_offset(
phi_masked, method="median", rng=2000.0, npix=7, center=True, min_periods=4
)
phioff = off.PHIDP_OFFSET.median(dim="azimuth", skipna=True)
fig = plt.figure(figsize=(16, 7))
ax1 = plt.subplot(111, projection="polar")
# set the lable go clockwise and start from the top
ax1.set_theta_zero_location("N")
# clockwise
ax1.set_theta_direction(-1)
theta = np.linspace(0, 2 * np.pi, num=360, endpoint=False)
ax1.plot(theta, off.PHIDP_OFFSET, color="b", linewidth=3)
ax1.plot(theta, np.ones_like(theta) * phioff.values, color="r", lw=2)
ti = off.time.values.astype("M8[s]")[0]
om = phioff.values
tx = ax1.set_title(f"{ti}\n" + r"$\phi_{DP}-Offset$ " + f"{om:.1f} (deg)")
tx.set_y(1.1)
xticks = ax1.set_xticks(np.pi / 180.0 * np.linspace(0, 360, 36, endpoint=False))
ax1.set_ylim(50, 150)
(50.0, 150.0)
fig = plt.figure(figsize=(18, 5))
swp_msk.DBZH.plot(x="azimuth")
off.start_range.plot(c="b", lw=2)
off.stop_range.plot(c="r", lw=2)
plt.gca().set_ylim(0, 25000)
(0.0, 25000.0)
swp = xr.open_dataset(boxpol, engine="gamic")
swp = swp.pipe(wrl.georef.georeference_dataset)
swp
<xarray.Dataset> Dimensions: (azimuth: 360, range: 1000) Coordinates: (12/13) * azimuth (azimuth) float64 0.5301 1.53 2.516 ... 357.5 358.5 359.5 elevation (azimuth) float64 1.505 1.505 1.505 ... 1.505 1.505 1.505 time (azimuth) datetime64[ns] ... * range (range) float32 50.0 150.0 250.0 ... 9.985e+04 9.995e+04 longitude float64 7.072 latitude float64 50.73 ... ... x (azimuth, range) float64 0.4624 1.387 ... -856.2 -857.0 y (azimuth, range) float64 49.98 149.9 ... 9.988e+04 z (azimuth, range) float64 100.9 103.5 ... 3.313e+03 gr (azimuth, range) float64 49.98 149.9 ... 9.988e+04 rays (azimuth, range) float64 0.5301 0.5301 ... 359.5 359.5 bins (azimuth, range) float32 50.0 150.0 ... 9.995e+04 Data variables: (12/17) KDP (azimuth, range) float32 ... PHIDP (azimuth, range) float32 ... DBZH (azimuth, range) float32 ... DBZV (azimuth, range) float32 ... RHOHV (azimuth, range) float32 ... DBTH (azimuth, range) float32 ... ... ... ZDR (azimuth, range) float32 ... sweep_mode <U20 'azimuth_surveillance' sweep_number int64 ... prt_mode <U7 ... follow_mode <U7 ... sweep_fixed_angle float64 ...
vol = wrl.io.open_gamic_dataset(boxpol)
/home/jgiles/mambaforge/envs/wradlib4/lib/python3.11/site-packages/wradlib/io/hdf.py:139: FutureWarning: `open_gamic_dataset` functionality has been moved to `xradar`-package and will be removed in 2.0. Use `open_gamic_datatree` from `xradar`-package. return open_radar_dataset(filename_or_obj, engine=GamicBackendEntrypoint, **kwargs)
display(vol)
<wradlib.RadarVolume> Dimension(s): (sweep: 1) Elevation(s): (1.5)
swp = vol[0].copy()
swp = swp.pipe(wrl.georef.georeference_dataset)
display(swp)
<xarray.Dataset> Dimensions: (azimuth: 360, range: 1000) Coordinates: (12/15) * azimuth (azimuth) float64 0.5 1.5 2.5 3.5 ... 357.5 358.5 359.5 * range (range) float32 50.0 150.0 250.0 ... 9.985e+04 9.995e+04 elevation (azimuth) float64 1.505 1.505 1.505 ... 1.505 1.505 1.505 rtime (azimuth) datetime64[ns] ... sweep_mode <U20 'azimuth_surveillance' longitude float64 7.072 ... ... x (azimuth, range) float64 0.4362 1.309 ... -870.7 -871.6 y (azimuth, range) float64 49.98 149.9 ... 9.988e+04 z (azimuth, range) float64 100.9 103.5 ... 3.313e+03 gr (azimuth, range) float64 49.98 149.9 ... 9.988e+04 rays (azimuth, range) float64 0.5 0.5 0.5 ... 359.5 359.5 bins (azimuth, range) float32 50.0 150.0 ... 9.995e+04 Data variables: (12/16) KDP (azimuth, range) float32 ... PHIDP (azimuth, range) float32 ... DBZH (azimuth, range) float32 ... DBZV (azimuth, range) float32 ... RHOHV (azimuth, range) float32 ... DBTH (azimuth, range) float32 ... ... ... WRADV (azimuth, range) float32 ... ZDR (azimuth, range) float32 ... sweep_number int64 ... prt_mode <U7 ... follow_mode <U7 ... sweep_fixed_angle float64 1.5 Attributes: fixed_angle: 1.5
fig = plt.figure(figsize=(13, 5))
ax1 = fig.add_subplot(121)
im1 = swp.PHIDP.where(swp.RHOHV > 0.8).plot(x="x", y="y", ax=ax1, cmap="turbo")
t = plt.title(r"Uncorrected $\phi_{DP}$")
t.set_y(1.1)
ax2 = fig.add_subplot(122)
im2 = swp.DBZH.where(swp.RHOHV > 0.8).plot(
x="x", y="y", ax=ax2, cmap="turbo", vmin=-10, vmax=50
)
t = plt.title(r"Uncorrected $Z_{H}$")
t.set_y(1.1)
fig.suptitle(swp.time.values, fontsize=14)
fig.subplots_adjust(wspace=0.25)
dr_m = swp.range.diff("range").median()
swp_msk = swp.where((swp.DBZH >= 0.0))
swp_msk = swp_msk.where(swp_msk.RHOHV > 0.8)
swp_msk = swp_msk.where(swp_msk.range > dr_m * 2)
phi_masked = swp_msk.PHIDP.copy()
off = phase_offset(
phi_masked, method="median", rng=2000.0, npix=7, center=True, min_periods=2
)
phioff = off.PHIDP_OFFSET.median(dim="azimuth", skipna=True)
fig = plt.figure(figsize=(16, 7))
ax1 = plt.subplot(111, projection="polar")
# set the lable go clockwise and start from the top
ax1.set_theta_zero_location("N")
# clockwise
ax1.set_theta_direction(-1)
theta = np.linspace(0, 2 * np.pi, num=360, endpoint=False)
ax1.plot(theta, off.PHIDP_OFFSET, color="b", linewidth=3)
ax1.plot(theta, np.ones_like(theta) * phioff.values, color="r", lw=2)
ti = off.time.values.astype("M8[s]")
om = phioff.values
tx = ax1.set_title(f"{ti}\n" + r"$\phi_{DP}-Offset$ " + f"{om:.1f} (deg)")
tx.set_y(1.1)
xticks = ax1.set_xticks(np.pi / 180.0 * np.linspace(0, 360, 36, endpoint=False))
ax1.set_ylim(-120, -70)
(-120.0, -70.0)
fig = plt.figure(figsize=(18, 5))
swp_msk.DBZH.plot(x="azimuth")
off.start_range.plot(c="b", lw=2)
off.stop_range.plot(c="r", lw=2)
plt.gca().set_ylim(0, 10000)
(0.0, 10000.0)
Pleaser refer to the ZPHI-Method section at the bottom of this notebook for references and equations.
We will use the simple method of finding the first and the last non NAN values per ray from ϕcorrDP.
This is the most simple and probably not very robust method.
def phase_zphi(phi, rng=1000.0, **kwargs):
range_step = np.diff(phi.range)[0]
nprec = int(rng / range_step)
if nprec % 2:
nprec += 1
# create binary array
phib = xr.where(np.isnan(phi), 0, 1)
# take nprec range bins and calculate sum
phib_sum = phib.rolling(range=nprec, **kwargs).sum(skipna=True)
offset = nprec // 2 * np.diff(phib_sum.range)[0]
offset_idx = nprec // 2
start_range = phib_sum.idxmax(dim="range") - offset
start_range_idx = phib_sum.argmax(dim="range") - offset_idx
stop_range = phib_sum[:, ::-1].idxmax(dim="range") - offset
stop_range_idx = (
len(phib_sum.range) - (phib_sum[:, ::-1].argmax(dim="range") - offset_idx) - 2
)
# get phase values in specified range
first = phi.where(
(phi.range >= start_range) & (phi.range <= start_range + rng), drop=True
).quantile(0.15, dim="range", skipna=True)
last = phi.where(
(phi.range >= stop_range - rng) & (phi.range <= stop_range), drop=True
).quantile(0.95, dim="range", skipna=True)
return xr.Dataset(
dict(
phib=phib_sum,
offset=offset,
offset_idx=offset_idx,
start_range=start_range,
stop_range=stop_range,
first=first.drop("quantile"),
first_idx=start_range_idx,
last=last.drop("quantile"),
last_idx=stop_range_idx,
)
)
Apply extraction of phase parameters.
cphase = phase_zphi(swp_msk.PHIDP, rng=2000.0, center=True, min_periods=7)
Apply azimuthal averaging.
cphase = (
cphase.pad(pad_width={"azimuth": 2}, mode="wrap")
.rolling(azimuth=5, center=True)
.median(skipna=True)
.isel(azimuth=slice(2, -2))
)
This visualizes first
and last
indizes including ΔϕDP.
dphi = cphase.last - cphase.first
dphi = dphi.where(dphi >= 0).fillna(0)
fig = plt.figure(figsize=(20, 9))
ax1 = plt.subplot(131, projection="polar")
ax2 = plt.subplot(132, projection="polar")
ax3 = plt.subplot(133, projection="polar")
# set the lable go clockwise and start from the top
ax1.set_theta_zero_location("N")
ax2.set_theta_zero_location("N")
ax3.set_theta_zero_location("N")
# clockwise
ax1.set_theta_direction(-1)
ax2.set_theta_direction(-1)
ax3.set_theta_direction(-1)
theta = np.linspace(0, 2 * np.pi, num=360, endpoint=False)
ax1.plot(theta, cphase.start_range, color="b", linewidth=2)
ax1.plot(theta, cphase.stop_range, color="r", linewidth=2)
_ = ax1.set_title("Start/Stop Range")
ax2.plot(theta, cphase.first, color="b", linewidth=2)
ax2.plot(theta, cphase.last, color="r", linewidth=2)
_ = ax2.set_title("Start/Stop PHIDP")
ax2.set_ylim(-110, -40)
ax3.plot(theta, dphi, color="g", linewidth=3)
# ax3.plot(theta, dphi_old, color="k", linewidth=1)
_ = ax3.set_title("Delta PHIDP")
# Coefficients for x-band
alphax = 0.28
betax = 0.05
bx = 0.78
# need to expand alphax to dphi-shape
fdphi = 10 ** (0.1 * bx * alphax * dphi) - 1
fdphi
<xarray.DataArray (azimuth: 360)> array([1.50772944, 1.621746 , 1.63263211, 1.59938947, 1.77879842, 1.87674877, 1.91353846, 1.96305219, 1.96305219, 1.97124872, 2.29083108, 2.25196971, 2.31872025, 2.39724185, 2.37292792, 2.49336399, 2.49336399, 2.4702802 , 2.05962546, 2.05962546, 1.98779264, 2.08415043, 2.22085707, 2.33125884, 2.6826384 , 2.81518698, 2.85859008, 2.86824995, 2.85859008, 3.03636579, 3.18973989, 3.43876111, 3.89274439, 3.89274439, 4.04998114, 3.7410609 , 3.5279313 , 3.29545116, 3.13226527, 2.63714176, 2.68060436, 2.68060436, 2.63714176, 2.6171011 , 2.68569016, 2.42366977, 2.41700817, 2.38506371, 2.17667475, 2.11841838, 1.87436571, 1.71911294, 1.76150461, 1.87436571, 2.01607107, 2.01607107, 2.04992121, 2.0311066 , 2.01923888, 1.77006176, 1.88550466, 1.85852905, 1.88630187, 1.93616259, 1.94184574, 1.94184574, 1.88550356, 1.88494586, 1.87801993, 1.87801993, 1.98605966, 1.96387083, 1.98770987, 2.13569607, 2.00903912, 2.00903912, 2.06478535, 2.02759084, 1.92099396, 1.90831172, 1.87333389, 1.8936456 , 1.8936456 , 2.05362955, 2.0340373 , 2.36548218, 2.40602815, 2.40602815, 2.39442893, 2.43024869, 2.40475917, 2.35513227, 2.38211962, 2.38960217, 2.68670986, 2.4064985 , 2.76909239, 2.76909239, 2.44544354, 2.39161573, ... 1.48493842, 1.40258469, 1.35526881, 1.35526881, 1.36570186, 1.36570186, 1.41723047, 1.45288304, 1.43734691, 1.43734691, 1.3127157 , 1.18417608, 1.20235217, 1.17394212, 1.17394212, 1.27155794, 1.1853823 , 1.30506194, 1.35140081, 1.39781004, 1.4092306 , 1.42928047, 1.41776486, 1.40155549, 1.43562991, 1.3748696 , 1.41656282, 1.3748696 , 1.3429393 , 1.31783232, 1.19242757, 1.14515811, 1.13333828, 1.12173051, 1.14116148, 1.14836012, 1.21945297, 1.22129306, 1.3589155 , 1.34099845, 1.32938614, 1.38209702, 1.34488177, 1.28477377, 1.27847183, 1.32231864, 1.39661884, 1.47261211, 1.47261211, 1.40856539, 1.45742654, 1.4808222 , 1.64794967, 1.53695988, 1.51025952, 1.38473063, 1.38078131, 1.37093571, 1.34734462, 1.30716441, 1.26344584, 1.34410378, 1.34734462, 1.39605621, 1.67293704, 1.77450348, 1.81270428, 1.91514859, 1.84828094, 1.93210992, 1.83807067, 1.93210992, 1.94916881, 1.97239827, 1.91192921, 1.93486535, 1.91192921, 1.94949493, 1.91353846, 2.13396412, 2.1055235 , 1.96059651, 1.86960552, 1.96059651, 1.89748485, 1.80843354, 1.85056417, 1.74022627, 1.80525399, 1.80215598, 1.66482719, 1.64868023, 1.62798185, 1.45695102, 1.47261211, 1.28540588, 1.26341505, 1.26341505, 1.30888565, 1.28414358]) Coordinates: * azimuth (azimuth) float64 0.5 1.5 2.5 3.5 ... 356.5 357.5 358.5 359.5 elevation (azimuth) float64 1.505 1.505 1.505 1.505 ... 1.505 1.505 1.505 rtime (azimuth) datetime64[ns] 2014-11-16T03:48:49 ... 2014-11-16T0... sweep_mode <U20 'azimuth_surveillance' longitude float64 7.072 latitude float64 50.73 altitude float64 99.5 time datetime64[ns] 2014-11-16T03:48:38
We do not restrict (mask) the reflectivities for now, but switch between DBTH
and DBZH
to see the difference.
zhraw = swp.DBZH.where(
(swp.range > cphase.start_range) & (swp.range < cphase.stop_range)
)
zhraw.plot(x="x", y="y", cmap="turbo", vmin=0, vmax=100)
<matplotlib.collections.QuadMesh at 0x7f58841fb310>
# calculate linear reflectivity and ^b
zax = zhraw.pipe(wrl.trafo.idecibel).fillna(0)
za = zax**bx
# set masked to zero for integration
za_zero = za.fillna(0)
Calculate cumulative integral, and subtract from maximum. That way we have the cumulative sum for every bin until the end of the ray.
def cumulative_trapezoid_xarray(da, dim, initial=0):
"""Intgration with the scipy.integrate.cumtrapz.
Parameter
---------
da : xarray.DataArray
array with differential phase data
dim : int
size of window in range dimension
Keyword Arguments
-----------------
initial : float
minimum number of valid bins
Return
------
kdp : xarray.DataArray
DataArray with specific differential phase values
"""
x = da[dim]
dx = x.diff(dim).median(dim).values
if x.attrs["units"] == "meters":
dx /= 1000.0
return xr.apply_ufunc(
cumulative_trapezoid,
da,
input_core_dims=[[dim]],
output_core_dims=[[dim]],
dask="parallelized",
kwargs=dict(axis=da.get_axis_num(dim), initial=initial, dx=dx),
dask_gufunc_kwargs=dict(allow_rechunk=True),
)
iza_x = 0.46 * bx * za_zero.pipe(cumulative_trapezoid_xarray, "range", initial=0)
iza = iza_x.max("range") - iza_x
We can reduce the number of operations by rearranging the equation like this:
AH(r)=[Za(r)]b0.46b∫r2r1[Za(s)]bdsf(ΔϕDP)+0.46b∫r2r[Za(s)]bdsiza_fdphi = iza / fdphi
idx = cphase.first_idx.astype(int)
iza_first = iza_fdphi[:, idx]
ah = za / (iza_first + iza)
Give it a name!
ah.name = "AH"
ah.attrs["short_name"] = "specific_attenuation_h"
ah.attrs["long_name"] = "Specific attenuation H"
ah.attrs["units"] = "dB/km"
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111)
ticks_ah = np.arange(0, 5, 0.2)
im = ah.plot(x="x", y="y", ax=ax, cmap="turbo", levels=np.arange(0, 0.5, 0.025))
phical = 2 * (ah / alphax).pipe(cumulative_trapezoid_xarray, "range", initial=0)
phical.name = "PHICAL"
phical.attrs = xd.model.sweep_vars_mapping["PHIDP"]
phical.where(swp_msk.PHIDP).plot(x="x", y="y", vmin=0, vmax=50, cmap="turbo")
<matplotlib.collections.QuadMesh at 0x7f586ff7d050>
print(alphax)
0.28
zhraw = swp.DBZH.copy()
zdrraw = swp.ZDR.copy()
with xr.set_options(keep_attrs=True):
zhcorr = zhraw + alphax * (phical)
zdiff = zhcorr - zhraw
zdrcorr = zdrraw + betax * (phical)
zdrdiff = zdrcorr - zdrraw
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(
nrows=2,
ncols=2,
figsize=(15, 12),
sharex=True,
sharey=True,
squeeze=True,
constrained_layout=True,
)
scantime = zhraw.time.values.astype("<M8[s]")
t = fig.suptitle(scantime, fontsize=14)
t.set_y(1.05)
zhraw.plot(x="x", y="y", ax=ax1, cmap="turbo", levels=np.arange(0, 40, 2))
ax1.set_title(r"Uncorrected $Z_{H}$", fontsize=16)
zhcorr.plot(x="x", y="y", ax=ax2, cmap="turbo", levels=np.arange(0, 40, 2))
ax2.set_title(r"Corrected $Z_{H}$", fontsize=16)
zdrraw.plot(x="x", y="y", ax=ax3, cmap="turbo", levels=np.arange(-0.5, 3, 0.1))
ax3.set_title(r"Uncorrected $Z_{DR}$", fontsize=16)
zdrcorr.plot(x="x", y="y", ax=ax4, cmap="turbo", levels=np.arange(-0.5, 3, 0.1))
ax4.set_title(r"Corrected $Z_{DR}$", fontsize=16)
Text(0.5, 1.0, 'Corrected $Z_{DR}$')
What are the benefits of KDP(AH)?
def kdp_from_phidp(da, winlen, min_periods=2):
"""Derive KDP from PHIDP (based on convolution filter).
Parameter
---------
da : xarray.DataArray
array with differential phase data
winlen : int
size of window in range dimension
Keyword Arguments
-----------------
min_periods : int
minimum number of valid bins
Return
------
kdp : xarray.DataArray
DataArray with specific differential phase values
"""
dr = da.range.diff("range").median("range").values / 1000.0
print("range res [km]:", dr)
print("processing window [km]:", dr * winlen)
return xr.apply_ufunc(
wrl.dp.kdp_from_phidp,
da,
input_core_dims=[["range"]],
output_core_dims=[["range"]],
dask="parallelized",
kwargs=dict(winlen=winlen, dr=dr, min_periods=min_periods),
dask_gufunc_kwargs=dict(allow_rechunk=True),
)
def kdp_phidp_vulpiani(da, winlen, min_periods=2):
"""Derive KDP from PHIDP (based on Vulpiani).
ParameterRHOHV_NC
---------
da : xarray.DataArray
array with differential phase data
winlen : int
size of window in range dimension
Keyword Arguments
-----------------
min_periods : int
minimum number of valid bins
Return
------
kdp : xarray.DataArray
DataArray with specific differential phase values
"""
dr = da.range.diff("range").median("range").values / 1000.0
print("range res [km]:", dr)
print("processing window [km]:", dr * winlen)
return xr.apply_ufunc(
wrl.dp.process_raw_phidp_vulpiani,
da,
input_core_dims=[["range"]],
output_core_dims=[["range"], ["range"]],
dask="parallelized",
kwargs=dict(winlen=winlen, dr=dr, min_periods=min_periods),
dask_gufunc_kwargs=dict(allow_rechunk=True),
)
%%time
kdp1 = kdp_from_phidp(swp_msk.PHIDP, winlen=31, min_periods=11)
kdp1.attrs = xd.model.sweep_vars_mapping["KDP"]
kdp2 = kdp_phidp_vulpiani(swp.PHIDP, winlen=71, min_periods=21)[1]
kdp2.attrs = xd.model.sweep_vars_mapping["KDP"]
kdp3 = xr.zeros_like(kdp1)
kdp3.attrs = xd.model.sweep_vars_mapping["KDP"]
kdp3.data = ah / alphax
range res [km]: 0.1 processing window [km]: 3.1 range res [km]: 0.1 processing window [km]: 7.1000000000000005 CPU times: user 383 ms, sys: 84.9 ms, total: 468 ms Wall time: 475 ms
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(
nrows=2, ncols=2, figsize=(12, 10), constrained_layout=True
)
swp.KDP.plot(
x="x",
y="y",
ax=ax1,
cmap="turbo",
levels=np.arange(-0.5, 1, 0.1),
cbar_kwargs=dict(shrink=0.64),
)
ax1.set_title(r"$K_{DP}$ - Signalprocessor", fontsize=16)
ax1.set_aspect("equal")
kdp1.plot(
x="x",
y="y",
ax=ax2,
cmap="turbo",
levels=np.arange(-0.5, 1, 0.1),
cbar_kwargs=dict(shrink=0.64),
)
ax2.set_title(r"$K_{DP}$ - Simple Derivative", fontsize=16)
ax2.set_aspect("equal")
kdp2.plot(
x="x",
y="y",
ax=ax3,
cmap="turbo",
levels=np.arange(-0.5, 1, 0.1),
cbar_kwargs=dict(shrink=0.64),
)
ax3.set_title(r"$K_{DP}$ - Vulpiani", fontsize=16)
ax3.set_aspect("equal")
kdp3.plot(
x="x",
y="y",
ax=ax4,
cmap="turbo",
levels=np.arange(-0.5, 1, 0.1),
cbar_kwargs=dict(shrink=0.64),
)
ax4.set_title(r"$K_{DP}$ - spec. Attenuation/ZPHI", fontsize=16)
ax4.set_aspect("equal")
We've just learned how to derive System Phase Offset and Specific Attenuation AH using the ZPHI-Method. Different KDP derivation methods have been compared.
see Testud et.al. (chapter 4. p. 339ff.), Diederich et.al. (chapter 3. p. 492 ff).
There is a equational difference in the two papers, which can be solved like this:
fΔϕDP=100.1⋅b⋅α⋅ΔϕDP−1
C(b,PIA)=exp[0.23⋅b⋅(PIA)]−1
with
PIA=α⋅ΔϕDP
C(b,PIA)=exp[0.23⋅b⋅α⋅ΔϕDP]−1
Both expressions are used equivalently:
100.1⋅b⋅α⋅ΔϕDP−1=exp[0.23⋅b⋅α⋅ΔϕDP]−1
Using logarithmic identities:
lnur=r⋅lnu
explnx=x
the left hand side can be further expressed as:
exp[ln100.1⋅b⋅α⋅ΔϕDP]−1=exp[0.23⋅b⋅α⋅ΔϕDP]−1
exp[0.1⋅b⋅α⋅ΔϕDP⋅ln10]−1=exp[0.23⋅b⋅α⋅ΔϕDP]−1
leading to equality
exp[0.23⋅b⋅α⋅ΔϕDP]−1=exp[0.23⋅b⋅α⋅ΔϕDP]−1