Source code for xclim.indicators.convert._conversion

"""Atmospheric conversion definitions."""

from __future__ import annotations

import warnings

from xclim.core.cfchecks import cfcheck_from_name
from xclim.core.indicator import Indicator
from xclim.core.utils import InputKind
from xclim.indices import converters

__all__ = [
    "clearness_index",
    "dewpoint_from_specific_humidity",
    "heat_index",
    "humidex",
    "longwave_upwelling_radiation_from_net_downwelling",
    "mean_radiant_temperature",
    "mean_temperature_from_max_and_min",
    "potential_evapotranspiration",
    "rain_approximation",
    "relative_humidity",
    "relative_humidity_from_dewpoint",
    "saturation_vapor_pressure",
    "shortwave_upwelling_radiation_from_net_downwelling",
    "snd_to_snw",
    "snowfall_approximation",
    "snw_to_snd",
    "specific_humidity",
    "specific_humidity_from_dewpoint",
    "tg",
    "universal_thermal_climate_index",
    "vapor_pressure",
    "vapor_pressure_deficit",
    "water_budget",
    "water_budget_from_tas",
    "wind_chill_index",
    "wind_power_potential",
    "wind_profile",
    "wind_speed_from_vector",
    "wind_vector_from_speed",
]


class Converter(Indicator):
    """Class for indicators doing variable conversion (dimension-independent 1-to-1 computation)."""

    def cfcheck(self, **das) -> None:
        r"""
        Verify the CF-compliance of the input data.

        Parameters
        ----------
        **das : Mapping[str, xarray.DataArray]
            The input data arrays.
        """
        for varname, vardata in das.items():
            try:
                # Only check standard_name, and not cell_methods which depends on the variable's frequency.
                cfcheck_from_name(varname, vardata, attrs=["standard_name"])
            except KeyError:  # noqa: S110
                # Silently ignore unknown variables.
                pass


humidex = Converter(
    title="Humidex",
    identifier="humidex",
    units="C",
    standard_name="air_temperature",
    long_name="Humidex index",
    description="Humidex index describing the temperature felt by the average person in response to relative humidity.",
    cell_methods="",
    keywords="heatwave",
    abstract="The humidex describes the temperature felt by a person when relative humidity is taken into account. "
    "It can be interpreted as the equivalent temperature felt when the air is dry.",
    compute=converters.humidex,
)


heat_index = Converter(
    title="Heat index",
    identifier="heat_index",
    units="C",
    standard_name="air_temperature",
    long_name="Heat index",
    description="Perceived temperature after relative humidity is taken into account.",
    cell_methods="",
    abstract="The heat index is an estimate of the temperature felt by a person in the shade "
    "when relative humidity is taken into account.",
    compute=converters.heat_index,
)


[docs] def tg(*args, **kwargs): # numpydoc ignore=GL08 warnings.warn( "The `tg` function is deprecated and will be removed in a future release. " "Use `mean_temperature_from_max_and_min` instead.", DeprecationWarning, stacklevel=2, ) return mean_temperature_from_max_and_min(*args, **kwargs)
mean_temperature_from_max_and_min = Converter( title="Mean temperature", identifier="mean_temperature_from_max_and_min", units="K", standard_name="air_temperature", long_name="Daily mean temperature", description="Estimated mean temperature from maximum and minimum temperatures.", cell_methods="time: mean within days", abstract="The average daily temperature assuming a symmetrical temperature distribution (Tg = (Tx + Tn) / 2).", compute=converters.tas_from_tasmin_tasmax, ) wind_speed_from_vector = Converter( title="Wind speed and direction from vector", identifier="wind_speed_from_vector", var_name=["sfcWind", "sfcWindfromdir"], units=["m s-1", "degree"], standard_name=["wind_speed", "wind_from_direction"], description=[ "Wind speed computed as the magnitude of the (uas, vas) vector.", "Wind direction computed as the angle of the (uas, vas) vector." " A direction of 0° is attributed to winds with a speed under {calm_wind_thresh}.", ], long_name=["Near-surface wind speed", "Near-surface wind from direction"], cell_methods="", abstract="Calculation of the magnitude and direction of the wind speed " "from the two components west-east and south-north.", compute=converters.uas_vas_to_sfcwind, ) wind_vector_from_speed = Converter( title="Wind vector from speed and direction", identifier="wind_vector_from_speed", var_name=["uas", "vas"], units=["m s-1", "m s-1"], standard_name=["eastward_wind", "northward_wind"], long_name=["Near-surface eastward wind", "Near-surface northward wind"], description=[ "Eastward wind speed computed from the magnitude of its speed and direction of origin.", "Northward wind speed computed from magnitude of its speed and direction of origin.", ], cell_methods="", abstract="Calculation of the two components (west-east and north-south) of the wind " "from the magnitude of its speed and direction of origin.", compute=converters.sfcwind_to_uas_vas, ) wind_power_potential = Converter( title="Wind power potential", identifier="wind_power_potential", units="", long_name="Wind power potential", description="Wind power potential using a semi-idealized turbine power curve using a cut_in speed of {cut_in}, " "a rated speed of {rated}, and a cut_out speed of {cut_out}.", cell_methods="", abstract="Calculation of the wind power potential using a semi-idealized turbine power curve.", compute=converters.wind_power_potential, ) wind_profile = Converter( title="Wind profile", identifier="wind_profile", var_name="wind_speed", units="m s-1", standard_name="wind_speed", long_name="Wind speed at height {h}", description="Wind speed at a height of {h} computed from the wind speed at {h_r} using a power law profile.", cell_methods="", abstract="Calculation of the wind speed at a given height from the wind speed at a reference height.", compute=converters.wind_profile, ) saturation_vapor_pressure = Converter( title="Saturation vapour pressure (e_sat)", identifier="e_sat", units="Pa", long_name='Saturation vapour pressure ("{method}" method)', description=lambda **kws: ( ("The saturation vapour pressure was calculated from a temperature according to the {method} method.") + ( " The computation was done in reference to ice for temperatures below {ice_thresh}." if kws["ice_thresh"] is not None else "" ) ), abstract="Calculation of the saturation vapour pressure from the temperature, according to a given method. " "If ice_thresh is given, the calculation is done with reference to ice for temperatures below this threshold.", compute=converters.saturation_vapor_pressure, ) relative_humidity_from_dewpoint = Converter( title="Relative humidity from temperature and dewpoint temperature", identifier="hurs_fromdewpoint", units="%", var_name="hurs", long_name='Relative humidity ("{method}" method)', standard_name="relative_humidity", description=lambda **kws: ( ( "Computed from temperature, and dew point temperature through the " "saturation vapour pressures, which were calculated " "according to the {method} method." ) + ( " The computation was done in reference to ice for temperatures below {ice_thresh}." if kws["ice_thresh"] is not None else "" ) ), abstract="Calculation of relative humidity from temperature and dew point using the saturation vapour pressure.", compute=converters.relative_humidity, parameters={ "tdps": {"kind": InputKind.VARIABLE}, "huss": None, "ps": None, "invalid_values": {"default": "mask"}, }, ) relative_humidity = Converter( title="Relative humidity from temperature, specific humidity, and pressure", identifier="hurs", units="%", var_name="hurs", long_name='Relative Humidity ("{method}" method)', standard_name="relative_humidity", description=lambda **kws: ( ( "Computed from temperature, specific humidity and pressure through the saturation vapour pressure, " "which was calculated from temperature according to the {method} method." ) + ( " The computation was done in reference to ice for temperatures below {ice_thresh}." if kws["ice_thresh"] is not None else "" ) ), abstract="Calculation of relative humidity from temperature, " "specific humidity, and pressure using the saturation vapour pressure.", compute=converters.relative_humidity, parameters={ "tdps": None, "huss": {"kind": InputKind.VARIABLE}, "ps": {"kind": InputKind.VARIABLE}, "invalid_values": {"default": "mask"}, }, ) specific_humidity = Converter( title="Specific humidity from temperature, relative humidity, and pressure", identifier="huss", units="", var_name="huss", long_name='Specific Humidity ("{method}" method)', standard_name="specific_humidity", description=lambda **kws: ( ( "Computed from temperature, relative humidity and pressure through the saturation vapour pressure, " "which was calculated from temperature according to the {method} method." ) + ( " The computation was done in reference to ice for temperatures below {ice_thresh}." if kws["ice_thresh"] is not None else "" ) ), abstract="Calculation of specific humidity from temperature, " "relative humidity, and pressure using the saturation vapour pressure.", compute=converters.specific_humidity, parameters={"invalid_values": "mask"}, ) specific_humidity_from_dewpoint = Converter( title="Specific humidity from dew point temperature and pressure", identifier="huss_fromdewpoint", units="", long_name="Specific humidity", standard_name="specific_humidity", var_name="huss", description=( "Computed from dewpoint temperature and pressure through the saturation " "vapor pressure, which was calculated according to the {method} method." ), abstract="Calculation of the specific humidity from dew point temperature " "and pressure using the saturation vapour pressure.", compute=converters.specific_humidity_from_dewpoint, ) dewpoint_from_specific_humidity = Converter( identifier="tdps_from_huss", units="K", var_name="tdps", description=( "Temperature at which the current water vapour reaches saturation. " "Equation from {method} is used for saturation vapour pressure." ), compute=converters.dewpoint_from_specific_humidity, ) vapor_pressure = Converter( identifier="vapor_pressure", units="Pa", standard_name="water_vapor_partial_pressure_in_air", description="Water vapour partial pressure computed from specific humidity and total pressure.", compute=converters.vapor_pressure, ) vapor_pressure_deficit = Converter( title="Water vapour pressure deficit", identifier="vapor_pressure_deficit", units="Pa", long_name='Vapour pressure deficit ("{method}" method)', standard_name="water_vapor_saturation_deficit_in_air", description=lambda **kws: ( ( "The difference between the saturation vapour pressure and the actual vapour pressure," "calculated from temperature and relative humidity according to the {method} method." ) + ( " The computation was done in reference to ice for temperatures below {ice_thresh}." if kws["ice_thresh"] is not None else "" ) ), abstract="Difference between the saturation vapour pressure and the actual vapour pressure.", compute=converters.vapor_pressure_deficit, ) snowfall_approximation = Converter( title="Snowfall approximation", identifier="prsn", units="kg m-2 s-1", standard_name="solid_precipitation_flux", long_name='Solid precipitation ("{method}" method with temperature at or below {thresh})', description=( "Solid precipitation estimated from total precipitation and temperature" " with method {method} and threshold temperature {thresh}." ), abstract="Solid precipitation estimated from total precipitation and temperature " "with a given method and temperature threshold.", compute=converters.snowfall_approximation, context="hydro", ) snd_to_snw = Converter( title="Surface snow amount", identifier="snd_to_snw", units="kg m-2", standard_name="surface_snow_amount", long_name="Approximation of daily snow amount from snow depth and density", description="The approximation of daily snow amount from snow depth and density.", var_name="snw", compute=converters.snd_to_snw, ) snw_to_snd = Converter( title="Surface snow depth", identifier="snw_to_snd", units="m", standard_name="surface_snow_thickness", long_name="Approximation of daily snow depth from snow amount and density", description="The approximation of daily snow depth from snow amount and density.", var_name="snd", compute=converters.snw_to_snd, ) rain_approximation = Converter( title="Rainfall approximation", identifier="prlp", units="kg m-2 s-1", standard_name="precipitation_flux", long_name='Liquid precipitation ("{method}" method with temperature at or above {thresh})', description=( "Liquid precipitation estimated from total precipitation and temperature" " with method {method} and threshold temperature {thresh}." ), abstract="Liquid precipitation estimated from total precipitation and temperature " "with a given method and temperature threshold.", compute=converters.rain_approximation, context="hydro", ) wind_chill_index = Converter( title="Wind chill", identifier="wind_chill", units="degC", long_name="Wind chill factor", description=lambda **kws: ( ("Wind chill index describing the temperature felt by the average person in response to cold wind.") + ( "A slow-wind version of the wind chill index was used for wind speeds under 5 km/h and invalid " "temperatures were masked (T > 0°C)." if kws["method"] == "CAN" else "Invalid temperatures (T > 50°F) and winds (V < 3 mph) where masked." ) ), abstract="Wind chill factor is an index that equates to how cold an average person feels. " "It is calculated from the temperature and the wind speed at 10 m. " "As defined by Environment and Climate Change Canada, a second formula is used for light winds. " "The standard formula is otherwise the same as used in the United States.", compute=converters.wind_chill_index, parameters={"mask_invalid": True}, ) potential_evapotranspiration = Converter( title="Potential evapotranspiration", identifier="potential_evapotranspiration", var_name="evspsblpot", units="kg m-2 s-1", standard_name="water_potential_evapotranspiration_flux", long_name='Potential evapotranspiration ("{method}" method)', description=( "The potential for water evaporation from soil and transpiration by plants if the water " "supply is sufficient, calculated with the {method} method." ), abstract=( "The potential for water evaporation from soil and transpiration by plants if the water " "supply is sufficient, calculated with a given method." ), compute=converters.potential_evapotranspiration, ) water_budget_from_tas = Converter( title="Water budget", identifier="water_budget_from_tas", units="kg m-2 s-1", long_name='Water budget ("{method}" method)', description=( "Precipitation minus potential evapotranspiration as a measure of an approximated surface water budget, " "where the potential evapotranspiration is calculated with the {method} method." ), abstract=( "Precipitation minus potential evapotranspiration as a measure of an approximated surface water budget, " "where the potential evapotranspiration is calculated with a given method." ), compute=converters.water_budget, parameters={"evspsblpot": None}, ) water_budget = Converter( title="Water budget", identifier="water_budget", units="kg m-2 s-1", long_name="Water budget", description=( "Precipitation minus potential evapotranspiration as a measure of an approximated surface water budget." ), abstract="Precipitation minus potential evapotranspiration as a measure of an approximated surface water budget.", compute=converters.water_budget, parameters={ "method": None, "evspsblpot": {"kind": InputKind.VARIABLE}, "tasmin": None, "tasmax": None, "tas": None, "lat": None, "hurs": None, "rsds": None, "rsus": None, "rlds": None, "rlus": None, "sfcWind": None, }, ) universal_thermal_climate_index = Converter( title="Universal Thermal Climate Index (UTCI)", identifier="utci", units="K", long_name="Universal Thermal Climate Index (UTCI)", description="UTCI is the equivalent temperature for the environment derived from a reference environment " "and is used to evaluate heat stress in outdoor spaces.", abstract="UTCI is the equivalent temperature for the environment derived from a reference environment " "and is used to evaluate heat stress in outdoor spaces.", cell_methods="", var_name="utci", compute=converters.universal_thermal_climate_index, ) mean_radiant_temperature = Converter( title="Mean radiant temperature", identifier="mean_radiant_temperature", units="K", long_name="Mean radiant temperature", description="The incidence of radiation on the body from all directions.", abstract="The average temperature of solar and thermal radiation incident on the body's exterior.", cell_methods="", var_name="mrt", compute=converters.mean_radiant_temperature, ) shortwave_upwelling_radiation_from_net_downwelling = Converter( title="Upwelling shortwave radiation", identifier="shortwave_upwelling_radiation_from_net_downwelling", units="W m-2", standard_name="surface_upwelling_shortwave_flux", long_name="Upwelling shortwave flux", description="The calculation of upwelling shortwave radiative flux from net surface shortwave " "and downwelling surface shortwave fluxes.", var_name="rsus", compute=converters.shortwave_upwelling_radiation_from_net_downwelling, ) longwave_upwelling_radiation_from_net_downwelling = Converter( title="Upwelling longwave radiation", identifier="longwave_upwelling_radiation_from_net_downwelling", units="W m-2", standard_name="surface_upwelling_longwave_flux", long_name="Upwelling longwave flux", description="The calculation of upwelling longwave radiative flux from net surface longwave " "and downwelling surface longwave fluxes.", var_name="rlus", compute=converters.longwave_upwelling_radiation_from_net_downwelling, ) clearness_index = Converter( title="Clearness index", identifier="clearness_index", units="", long_name="Clear index", description="The ratio of shortwave downwelling radiation to extraterrestrial radiation.", var_name="ci", compute=converters.clearness_index, )