"""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,
)