Source code for meteodatalab.operators.relhum

"""Relative humidity operators."""

# Standard library
from typing import Literal

# Third-party
import xarray as xr
from earthkit.meteo import thermo  # type: ignore

# Local
from .. import metadata


[docs] def relhum( w, t, p, clipping=True, phase: Literal["water", "ice", "water+ice"] = "water" ): """Calculate relative humidity. Parameters ---------- w : xarray.DataArray water vapor mixing ratio in kg/kg t : xarray.DataArray temperature in Kelvin p : xarray.DataArray pressure in Pa clipping : bool clips the relative humidity to [0,100] interval. Only upper bound is controlled by this parameter, since lower bound clipping is always performed. phase : Literal["water", "ice", "water+ice"] Customizes how relative humidity is computed. 'water' over water 'ice' over ice 'water+ice' over mixed phase Returns ------- xarray.DataArray relative humidity field in % """ max = 100 if clipping else None # Phase-specific metadata and saturation vapor pressure configuration phase_conditions = { "water": { "shortName": "RELHUM", "svp_phase": "water", }, "ice": { "shortName": "RH_ICE", "svp_phase": "ice", }, "water+ice": { "shortName": "RH_MIX_EC", "svp_phase": "mixed", # earthkit-meteo op. requires "mixed" }, } if phase not in phase_conditions: raise ValueError("Invalid phase. Phase must be 'water', 'ice', or 'water+ice'.") # Convert mixing ratio (w) to specific humidity (q) q = w / (1 + w) pb, tb, qb = xr.broadcast(p, t, q) # Use the mapped saturation vapor pressure phase svp_phase = phase_conditions[phase]["svp_phase"] # Compute relative humidity using earthkit-meteo operators rh = ( 100 * thermo.vapour_pressure_from_specific_humidity(qb, pb) / thermo.saturation_vapour_pressure(tb.values, phase=svp_phase) ).clip(0, max) # Return RH with appropriate metadata return xr.DataArray( data=rh, attrs=metadata.override( t.metadata, shortName=phase_conditions[phase]["shortName"] ), )