Source code for rustmatrix.radar
"""Polarimetric radar observables.
Direct port of ``pytmatrix.radar``. Given a configured ``Scatterer``
(optionally with a ``psd_integrator`` for N(D)-weighted averages), these
helpers compute the standard radar observables:
* :func:`radar_xsect` — radar cross-section.
* :func:`refl` / :func:`Zi` — reflectivity (with N=1 when no PSD).
* :func:`Zdr` — differential reflectivity (linear ratio).
* :func:`delta_hv` — backscatter differential phase.
* :func:`rho_hv` — copolar correlation coefficient.
* :func:`Kdp` — specific differential phase [°/km] (forward geometry).
* :func:`Ai` — specific attenuation [dB/km] (forward geometry).
Inputs: particle diameter and wavelength must be given in mm for ``Kdp``
and ``Ai`` to come out in the documented units. ``Zi`` returns linear
reflectivity; take ``10 * log10(Zi)`` for dBZ.
"""
from __future__ import annotations
import numpy as np
from .scatter import ext_xsect
[docs]
def radar_xsect(scatterer, h_pol: bool = True):
"""Radar cross-section σ [mm²] for the scatterer's current geometry.
Parameters
----------
scatterer : Scatterer
h_pol : bool
True (default) for horizontal polarisation, False for vertical.
Returns
-------
sigma : float
Polarised radar cross-section. When ``scatterer.psd_integrator`` is
set, the PSD-integrated value is returned.
"""
Z = scatterer.get_Z()
if h_pol:
return 2 * np.pi * (Z[0, 0] - Z[0, 1] - Z[1, 0] + Z[1, 1])
return 2 * np.pi * (Z[0, 0] + Z[0, 1] + Z[1, 0] + Z[1, 1])
[docs]
def refl(scatterer, h_pol: bool = True):
"""Linear reflectivity [mm⁶·m⁻³] (a.k.a. Z_h or Z_v).
Parameters
----------
scatterer : Scatterer
With ``wavelength`` in mm and ``Kw_sqr`` set. A ``psd_integrator``
is optional; without one, the value is for N = 1 particle / m³.
h_pol : bool
Horizontal (default) or vertical polarisation.
Returns
-------
Z : float
Linear reflectivity. Convert to dBZ with ``10 * log10(Z)``.
Notes
-----
Uses the radar convention ``Z = λ⁴ / (π⁵ |K_w|²) · σ``. Wavelengths
and diameters must be in mm for the unit to be mm⁶·m⁻³.
"""
return (
scatterer.wavelength ** 4
/ (np.pi ** 5 * scatterer.Kw_sqr)
* radar_xsect(scatterer, h_pol)
)
# Compatibility alias mirroring pytmatrix.
Zi = refl
[docs]
def Zdr(scatterer):
"""Differential reflectivity as a linear H/V ratio.
Convert to dB with ``10 * log10(Zdr(...))``. Identical to the ratio
``refl(s, True) / refl(s, False)``.
"""
return radar_xsect(scatterer, True) / radar_xsect(scatterer, False)
[docs]
def delta_hv(scatterer):
"""Backscatter differential phase δ_hv in radians.
Positive for oblate hydrometeors at typical radar wavelengths. Convert
to degrees with ``numpy.degrees``.
"""
Z = scatterer.get_Z()
return np.arctan2(Z[2, 3] - Z[3, 2], -Z[2, 2] - Z[3, 3])
[docs]
def rho_hv(scatterer):
"""Copolar correlation coefficient ρ_hv (dimensionless, 0–1).
Drops from 1 as the particle population becomes more heterogeneous
in shape, orientation, or composition.
"""
Z = scatterer.get_Z()
a = (Z[2, 2] + Z[3, 3]) ** 2 + (Z[3, 2] - Z[2, 3]) ** 2
b = Z[0, 0] - Z[0, 1] - Z[1, 0] + Z[1, 1]
c = Z[0, 0] + Z[0, 1] + Z[1, 0] + Z[1, 1]
return np.sqrt(a / (b * c))
[docs]
def Kdp(scatterer):
"""Specific differential phase K_dp in ° per km.
Parameters
----------
scatterer : Scatterer
Must be in a forward-scatter geometry (``thet0 == thet`` and
``phi0 == phi``). Wavelength must be in mm.
Returns
-------
Kdp : float
Specific differential phase, ° / km.
Raises
------
ValueError
If the geometry is not forward-scatter.
"""
if scatterer.thet0 != scatterer.thet or scatterer.phi0 != scatterer.phi:
raise ValueError(
"A forward scattering geometry is needed to compute the "
"specific differential phase."
)
S = scatterer.get_S()
return 1e-3 * (180.0 / np.pi) * scatterer.wavelength * (S[1, 1] - S[0, 0]).real
[docs]
def Ai(scatterer, h_pol: bool = True):
"""Specific attenuation A in dB per km.
Parameters
----------
scatterer : Scatterer
In a forward-scatter geometry, with wavelength in mm.
h_pol : bool
Horizontal (default) or vertical polarisation.
Returns
-------
A : float
Specific attenuation in dB / km. Computed from the extinction
cross-section via the optical theorem.
"""
return 4.343e-3 * ext_xsect(scatterer, h_pol=h_pol)