Source code for rustmatrix.refractive

"""Refractive-index helpers.

Direct port of ``pytmatrix.refractive``. Provides:

* :func:`mg_refractive` — Maxwell-Garnett effective-medium approximation
  for a multi-component mixture.
* :func:`bruggeman_refractive` — Bruggeman EMA (two-component).
* Tabulated water refractive indices at 0/10/20 °C for the six standard
  radar bands (``m_w_0C``, ``m_w_10C``, ``m_w_20C``).
* :func:`ice_refractive` — factory that returns a callable ``m(wl, rho)``
  interpolating the refractive index of ice/snow against a lookup table.
* ``mi`` — pre-built interpolator using the bundled ``ice_refr.dat`` from
  http://www.atmos.washington.edu/ice_optical_constants/ .
"""

from __future__ import annotations

from os import path

import numpy as np
from scipy import interpolate

from .tmatrix_aux import wl_C, wl_Ka, wl_Ku, wl_S, wl_W, wl_X


[docs] def mg_refractive(m, mix): """Maxwell-Garnett effective-medium refractive index. Parameters ---------- m : tuple of complex Complex refractive indices of the constituent media. mix : tuple of float Volume fractions, ``len(mix) == len(m)``. Renormalised to ``sum(mix) == 1`` if needed. Returns ------- complex Effective complex refractive index of the mixture. Notes ----- For two components, the first element is the matrix and the second is the inclusion — the approximation is asymmetric. For more components the media are mixed recursively from the tail inward. Examples -------- Dry snow as ice inclusions in air (10 %% ice by volume): >>> mg_refractive((complex(1.0, 0.0), complex(1.78, 3e-4)), (0.9, 0.1)) """ if len(m) == 2: cF = float(mix[1]) / (mix[0] + mix[1]) * \ (m[1] ** 2 - m[0] ** 2) / (m[1] ** 2 + 2 * m[0] ** 2) er = m[0] ** 2 * (1.0 + 2.0 * cF) / (1.0 - cF) return np.sqrt(er) m_last = mg_refractive(m[-2:], mix[-2:]) mix_last = mix[-2] + mix[-1] return mg_refractive(m[:-2] + (m_last,), mix[:-2] + (mix_last,))
[docs] def bruggeman_refractive(m, mix): """Bruggeman effective-medium refractive index (two components only). Symmetric counterpart to :func:`mg_refractive`. Takes the same arguments but both media are treated equally. """ f1 = mix[0] / sum(mix) f2 = mix[1] / sum(mix) e1 = m[0] ** 2 e2 = m[1] ** 2 a = -2 * (f1 + f2) b = (2 * f1 * e1 - f1 * e2 + 2 * f2 * e2 - f2 * e1) c = (f1 + f2) * e1 * e2 e_eff = (-b - np.sqrt(b ** 2 - 4 * a * c)) / (2 * a) return np.sqrt(e_eff)
# Water refractive indices at 0 °C for the six standard radar bands. m_w_0C = { wl_S: complex(9.075, 1.253), wl_C: complex(8.328, 2.217), wl_X: complex(7.351, 2.785), wl_Ku: complex(6.265, 2.993), wl_Ka: complex(4.040, 2.388), wl_W: complex(2.880, 1.335), } # Water refractive indices at 10 °C. m_w_10C = { wl_S: complex(9.019, 0.887), wl_C: complex(8.601, 1.687), wl_X: complex(7.942, 2.332), wl_Ku: complex(7.042, 2.777), wl_Ka: complex(4.638, 2.672), wl_W: complex(3.117, 1.665), } # Water refractive indices at 20 °C. m_w_20C = { wl_S: complex(8.876, 0.653), wl_C: complex(8.633, 1.289), wl_X: complex(8.208, 1.886), wl_Ku: complex(7.537, 2.424), wl_Ka: complex(5.206, 2.801), wl_W: complex(3.382, 1.941), } # Solid-ice density in g/cm^3. ice_density = 0.9167
[docs] def ice_refractive(file): """Build a callable interpolator for ice/snow refractive index. Parameters ---------- file : str Path to a refractive-index lookup table with columns ``(wavelength [μm], real, imag)``. The bundled ``ice_refr.dat`` file comes from the Warren & Brandt (2008) optical-constants compilation hosted at http://www.atmos.washington.edu/ice_optical_constants/ . Returns ------- ref : callable ``ref(wl, snow_density)`` where ``wl`` is in mm and ``snow_density`` is in g/cm³. Internally applies Maxwell-Garnett to mix the ice refractive index with air at the given density. Handles scalar or array-like ``wl``. Notes ----- The module-level :data:`mi` instance is a pre-built interpolator using the bundled data file — use that directly in most cases: >>> from rustmatrix.refractive import mi >>> from rustmatrix.tmatrix_aux import wl_W >>> mi(wl_W, 0.9) # ice at 0.9 g/cm³ at W-band """ D = np.loadtxt(file) log_wl = np.log10(D[:, 0] / 1000.0) re = D[:, 1] log_im = np.log10(D[:, 2]) iobj_re = interpolate.interp1d(log_wl, re) iobj_log_im = interpolate.interp1d(log_wl, log_im) def ref(wl, snow_density): lwl = np.log10(wl) try: len(lwl) except TypeError: mi_sqr = complex(iobj_re(lwl), 10 ** iobj_log_im(lwl)) ** 2 else: mi_sqr = np.array( [complex(a, b) for a, b in zip(iobj_re(lwl), 10 ** iobj_log_im(lwl))] ) ** 2 c = (mi_sqr - 1) / (mi_sqr + 2) * snow_density / ice_density return np.sqrt((1 + 2 * c) / (1 - c)) return ref
_module_path = path.split(path.abspath(__file__))[0] mi = ice_refractive(path.join(_module_path, "ice_refr.dat"))