"""Module for creating Cloudnet drizzle product."""
import numpy as np
from numpy import ma
from scipy.special import gamma
from cloudnetpy import output, utils
from cloudnetpy.metadata import MetaData
from cloudnetpy.products.drizzle_error import get_drizzle_error
from cloudnetpy.products.drizzle_tools import (
DrizzleClassification,
DrizzleSolver,
DrizzleSource,
SpectralWidth,
)
[docs]
def generate_drizzle(
categorize_file: str,
output_file: str,
uuid: str | None = None,
) -> str:
"""Generates Cloudnet drizzle product.
This function calculates different drizzle properties from
cloud radar and lidar measurements. The results are written in a netCDF file.
Args:
categorize_file: Categorize file name.
output_file: Output file name.
uuid: Set specific UUID for the file.
Returns:
str: UUID of the generated file.
Examples:
>>> from cloudnetpy.products import generate_drizzle
>>> generate_drizzle('categorize.nc', 'drizzle.nc')
References:
O’Connor, E.J., R.J. Hogan, and A.J. Illingworth, 2005:
Retrieving Stratocumulus Drizzle Parameters Using Doppler Radar and Lidar.
J. Appl. Meteor., 44, 14–27, https://doi.org/10.1175/JAM-2181.1
"""
with DrizzleSource(categorize_file) as drizzle_source:
drizzle_class = DrizzleClassification(categorize_file)
spectral_width = SpectralWidth(categorize_file)
drizzle_solver = DrizzleSolver(drizzle_source, drizzle_class, spectral_width)
derived_products = DrizzleProducts(drizzle_source, drizzle_solver)
errors = get_drizzle_error(drizzle_source, drizzle_solver)
retrieval_status = RetrievalStatus(drizzle_class)
results = {
**drizzle_solver.params,
**derived_products.derived_products,
**errors,
}
results = _screen_rain(results, drizzle_class)
results["drizzle_retrieval_status"] = retrieval_status.retrieval_status
_append_data(drizzle_source, results)
date = drizzle_source.get_date()
attributes = output.add_time_attribute(DRIZZLE_ATTRIBUTES, date)
output.update_attributes(drizzle_source.data, attributes)
return output.save_product_file("drizzle", drizzle_source, output_file, uuid)
[docs]
class DrizzleProducts:
"""Calculates additional quantities from the drizzle properties.
Args:
drizzle_source: The :class:`DrizzleSource` instance.
drizzle_solver: The :class:`DrizzleSolver` instance.
Attributes:
derived_products (dict): Dictionary containing derived drizzle products:
'drizzle_N', 'drizzle_lwc', 'drizzle_lwf', 'v_drizzle', 'v_air'.
"""
def __init__(self, drizzle_source: DrizzleSource, drizzle_solver: DrizzleSolver):
self._data = drizzle_source
self._params = drizzle_solver.params
self._ind_drizzle, self._ind_lut = self._find_indices()
self.derived_products = self._calc_derived_products()
def _find_indices(self) -> tuple:
drizzle_ind = np.where(self._params["Do"])
ind_mu = np.searchsorted(self._data.mie["mu"], self._params["mu"][drizzle_ind])
ind_dia = np.searchsorted(self._data.mie["Do"], self._params["Do"][drizzle_ind])
n_widths, n_dia = len(self._data.mie["mu"]), len(self._data.mie["Do"])
ind_mu[ind_mu >= n_widths] = n_widths - 1
ind_dia[ind_dia >= n_dia] = n_dia - 1
return drizzle_ind, (ind_mu, ind_dia)
def _calc_derived_products(self) -> dict:
density = self._calc_density()
lwc = self._calc_lwc()
lwf = self._calc_lwf(lwc)
v_drizzle = self._calc_fall_velocity()
v_air = self._calc_v_air(v_drizzle)
return {
"drizzle_N": density,
"drizzle_lwc": lwc,
"drizzle_lwf": lwf,
"v_drizzle": v_drizzle,
"v_air": v_air,
}
def _calc_density(self) -> np.ndarray:
"""Calculates drizzle number density (m-3)."""
a = self._data.z * 3.67**6
b = self._params["Do"] ** 6
return np.divide(a, b, out=np.zeros_like(a), where=b != 0)
def _calc_lwc(self) -> np.ndarray:
"""Calculates drizzle liquid water content (kg m-3)."""
rho_water = 1000
dia, mu, s = (self._params.get(key) for key in ("Do", "mu", "S"))
dia = ma.array(dia)
mu = ma.array(mu)
s = ma.array(s)
gamma_ratio = gamma(4 + mu) / gamma(3 + mu) / (3.67 + mu)
return rho_water / 3 * self._data.beta * s * dia * gamma_ratio
def _calc_lwf(self, lwc_in) -> np.ndarray:
"""Calculates drizzle liquid water flux."""
flux = ma.copy(lwc_in)
flux[self._ind_drizzle] *= (
self._data.mie["lwf"][self._ind_lut]
* self._data.mie["termv"][self._ind_lut[1]]
)
return flux
def _calc_fall_velocity(self) -> np.ndarray:
"""Calculates drizzle droplet fall velocity (m s-1)."""
velocity = np.zeros_like(self._params["Do"])
velocity[self._ind_drizzle] = -self._data.mie["v"][self._ind_lut]
return velocity
def _calc_v_air(self, droplet_velocity) -> np.ndarray:
"""Calculates vertical air velocity."""
velocity = -np.copy(droplet_velocity)
velocity[self._ind_drizzle] += self._data.v[self._ind_drizzle]
return velocity
[docs]
class RetrievalStatus:
"""Estimates the status of drizzle retrievals.
Args:
drizzle_class: The :class:`DrizzleClassification` instance.
Attributes:
drizzle_class: The :class:`DrizzleClassification` instance.
retrieval_status (ndarray): 2D array containing drizzle retrieval
status information.
"""
def __init__(self, drizzle_class: DrizzleClassification):
self.drizzle_class = drizzle_class
self.retrieval_status: np.ndarray = np.array([])
self._get_retrieval_status()
def _get_retrieval_status(self) -> None:
self.retrieval_status = np.copy(self.drizzle_class.drizzle).astype(int)
self._find_retrieval_below_melting()
self.retrieval_status[self.drizzle_class.would_be_drizzle == 1] = 3
self._find_retrieval_in_warm_liquid()
self.retrieval_status[self.drizzle_class.is_rain == 1, :] = 5
def _find_retrieval_below_melting(self) -> None:
cold_rain = utils.transpose(self.drizzle_class.cold_rain)
below_melting = cold_rain * self.drizzle_class.drizzle
self.retrieval_status[below_melting == 1] = 2
def _find_retrieval_in_warm_liquid(self) -> None:
in_warm_liquid = (self.retrieval_status == 0) * self.drizzle_class.warm_liquid
self.retrieval_status[in_warm_liquid == 1] = 4
def _screen_rain(results: dict, classification: DrizzleClassification) -> dict:
"""Removes rainy profiles from drizzle variables.."""
for key in results:
if not utils.isscalar(results[key]):
results[key][classification.is_rain, :] = 0
return results
def _append_data(drizzle_data: DrizzleSource, results: dict) -> None:
"""Save retrieved fields to the drizzle_data object."""
for key, value in results.items():
if key != "drizzle_retrieval_status":
arr = ma.masked_where(value == 0, value)
else:
arr = value
drizzle_data.append_data(arr, key)
DRIZZLE_ATTRIBUTES = {
"drizzle_N": MetaData(
long_name="Drizzle number concentration",
units="m-3",
ancillary_variables="drizzle_N_error drizzle_N_bias",
),
"drizzle_N_error": MetaData(
long_name="Random error in drizzle number concentration",
units="dB",
),
"drizzle_N_bias": MetaData(
long_name="Possible bias in drizzle number concentration",
units="dB",
),
"drizzle_lwc": MetaData(
long_name="Drizzle liquid water content",
units="kg m-3",
ancillary_variables="drizzle_lwc_error drizzle_lwc_bias",
standard_name="mass_concentration_of_drizzle_in_air",
),
"drizzle_lwc_error": MetaData(
long_name="Random error in drizzle liquid water content",
units="dB",
),
"drizzle_lwc_bias": MetaData(
long_name="Possible bias in drizzle liquid water content",
units="dB",
),
"drizzle_lwf": MetaData(
long_name="Drizzle liquid water flux",
units="kg m-2 s-1",
ancillary_variables="drizzle_lwf_error drizzle_lwf_bias",
),
"drizzle_lwf_error": MetaData(
long_name="Random error in drizzle liquid water flux",
units="dB",
),
"drizzle_lwf_bias": MetaData(
long_name="Possible bias in drizzle liquid water flux",
units="dB",
),
"v_drizzle": MetaData(
long_name="Drizzle droplet fall velocity", # TODO: should include 'terminal' ?
units="m s-1",
ancillary_variables="v_drizzle_error v_drizzle_bias",
comment="Positive values are towards the ground.",
),
"v_drizzle_error": MetaData(
long_name="Random error in drizzle droplet fall velocity",
units="dB",
),
"v_drizzle_bias": MetaData(
long_name="Possible bias in drizzle droplet fall velocity",
units="dB",
),
"v_air": MetaData(
long_name="Vertical air velocity",
units="m s-1",
ancillary_variables="v_air_error",
comment="Positive values are towards the sky.",
standard_name="upward_air_velocity",
),
"v_air_error": MetaData(
long_name="Random error in vertical air velocity",
units="dB",
),
"Do": MetaData(
long_name="Drizzle median diameter",
units="m",
ancillary_variables="Do_error Do_bias",
),
"Do_error": MetaData(
long_name="Random error in drizzle median diameter",
units="dB",
),
"Do_bias": MetaData(
long_name="Possible bias in drizzle median diameter",
units="dB",
),
"mu": MetaData(
long_name="Drizzle droplet size distribution shape parameter",
ancillary_variables="mu_error",
units="1",
),
"mu_error": MetaData(
long_name="Random error in drizzle droplet size distribution shape parameter",
units="dB",
),
"S": MetaData(
long_name="Lidar backscatter-to-extinction ratio",
ancillary_variables="S_error",
units="sr",
),
"S_error": MetaData(
long_name="Random error in lidar backscatter-to-extinction ratio",
units="dB",
),
"beta_corr": MetaData(long_name="Lidar backscatter correction factor", units="1"),
"drizzle_retrieval_status": MetaData(
long_name="Drizzle parameter retrieval status",
units="1",
),
}