from __future__ import annotations
import numpy as np
from rustmatrix import Scatterer, SpectralIntegrator, radar, spectra
from rustmatrix.psd import ExponentialPSD, PSDIntegrator
from rustmatrix.refractive import m_w_20C
from rustmatrix.tmatrix_aux import (K_w_sqr, dsr_thurai_2007,
geom_vert_back, wl_W)
def build_drop(D_mm: float, oblate: bool = True) -> Scatterer:
axis_ratio = 1.0 / dsr_thurai_2007(D_mm) if oblate else 1.0
s = Scatterer(radius=D_mm / 2, wavelength=wl_W, m=m_w_20C[wl_W],
Kw_sqr=K_w_sqr[wl_W], axis_ratio=axis_ratio,
ddelt=1e-4, ndgs=2)
s.set_geometry(geom_vert_back)
return s
def build_rain_W(num_points: int = 256) -> Scatterer:
s = Scatterer(wavelength=wl_W, m=m_w_20C[wl_W],
Kw_sqr=K_w_sqr[wl_W], ddelt=1e-4, ndgs=2)
integ = PSDIntegrator()
integ.D_max = 5.0
integ.num_points = num_points
integ.axis_ratio_func = lambda D: 1.0 / dsr_thurai_2007(D)
integ.geometries = (geom_vert_back,)
s.psd_integrator = integ
s.psd_integrator.init_scatter_table(s)
s.psd = ExponentialPSD(N0=8e3, Lambda=2.2, D_max=5.0)
return s
def main() -> None:
fall = spectra.fall_speed.atlas_srivastava_sekhon_1973
D_grid = np.linspace(0.05, 5.0, 400)
sigma_b_obl = np.array([radar.radar_xsect(build_drop(D, True)) for D in D_grid])
sigma_b_sph = np.array([radar.radar_xsect(build_drop(D, False)) for D in D_grid])
v_t = fall(D_grid)
def first_min(sigma_b: np.ndarray) -> tuple[float, float]:
mask = (D_grid > 1.3) & (D_grid < 2.2)
i = int(np.argmin(sigma_b[mask]))
return D_grid[mask][i], v_t[mask][i]
D_min_obl, v_min_obl = first_min(sigma_b_obl)
D_min_sph, v_min_sph = first_min(sigma_b_sph)
rain64 = build_rain_W(num_points=64)
rain256 = build_rain_W(num_points=256)
def run(sc: Scatterer, turb, w: float = 0.0):
return SpectralIntegrator(
sc, fall_speed=fall, turbulence=turb, w=w,
v_min=-1.0, v_max=12.0, n_bins=1024,
geometry_backscatter=geom_vert_back,
).run()
r_sparse = run(rain64, spectra.NoTurbulence())
r_dense = run(rain256, spectra.NoTurbulence())
r_turb = run(rain256, spectra.GaussianTurbulence(0.1))
r_w = run(rain256, spectra.GaussianTurbulence(0.1), w=1.0)
band = (r_w.v > v_min_obl) & (r_w.v < v_min_obl + 2.5)
v_obs = r_w.v[band][int(np.argmin(r_w.sZ_h[band]))]
print("Kollias, Albrecht, Marks (2002) — W-band Mie spectrum in convective rain")
print("-" * 72)
print("First Mie minimum:")
print(f" oblate (Thurai 2007): D = {D_min_obl:.3f} mm, v = {v_min_obl:.3f} m/s")
print(f" sphere : D = {D_min_sph:.3f} mm, v = {v_min_sph:.3f} m/s")
print(f" shift (oblate − sphere) = {100 * (v_min_obl - v_min_sph):+.1f} cm/s")
print()
print("Sampling artifact (NoTurbulence, 1024 velocity bins):")
print(f" non-zero bins (num_points=64) = {int((r_sparse.sZ_h > 0).sum())}")
print(f" non-zero bins (num_points=256) = {int((r_dense.sZ_h > 0).sum())}")
print(f" non-zero bins (σ_t=0.1 m/s) = {int((r_turb.sZ_h > 0).sum())}")
print()
print("Air-motion retrieval (prescribed w = +1.00 m/s):")
print(f" observed 1st Mie min v = {v_obs:.2f} m/s")
print(f" still-air 1st Mie min v = {v_min_obl:.2f} m/s")
print(f" retrieved w = {v_obs - v_min_obl:+.2f} m/s")
if __name__ == "__main__":
main()