Source code for improver.psychrometric_calculations.temperature_saturated_air_parcel

# (C) Crown Copyright, Met Office. All rights reserved.
#
# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license.
# See LICENSE in the root of the repository for full licensing details.

"""module to calculate the temperature of a saturated air parcel that has
ascended adiabatically from the cloud condensation level (CCL) to a pressure
level."""

from typing import Union

import numpy as np
from iris.coords import DimCoord
from iris.cube import Cube, CubeList

from improver import BasePlugin
from improver.metadata.utilities import (
    create_new_diagnostic_cube,
    generate_mandatory_attributes,
)
from improver.psychrometric_calculations.cloud_condensation_level import (
    CloudCondensationLevel,
)
from improver.psychrometric_calculations.psychrometric_calculations import (
    HumidityMixingRatio,
    adjust_for_latent_heat,
    dry_adiabatic_temperature,
    saturated_humidity,
)
from improver.utilities.common_input_handle import as_cubelist


[docs] class TemperatureSaturatedAirParcel(BasePlugin): """ Plugin to calculate the temperature of a saturated air parcel as it rises adiabatically from the cloud condensation level (CCL) to a pressure level. The default is set at 500 hPa for the Lifted Index (LI) calculations. """
[docs] def __init__(self, pressure_level: float = 50000.0): """ Set up class Args: pressure_level: The pressure level that the air parcel is lifted to adiabatically from the cloud condensation level. (Pa) """ self.pressure_level = pressure_level self.temperature: Cube = None self.pressure: Cube = None
[docs] def parcel_temp_after_ascent(self) -> np.array: """Calculates the temperature of a saturated air parcel when it has been lifted from the CCL to a pressure level. This has been set at 500 hPa for the easy calculation of Lifted Index (LI). Returns: Array of temperature of an air parcel at a pressure level (K) """ relative_humidity_cube = self.make_saturated_relative_humidity_cube() humidity_cube = HumidityMixingRatio()( [self.temperature, self.pressure, relative_humidity_cube] ) ccl_temperature_cube, ccl_pressure_cube = CloudCondensationLevel()( [self.temperature, self.pressure, humidity_cube] ) humidity_mixing_ratio_at_ccl = saturated_humidity( ccl_temperature_cube.data, ccl_pressure_cube.data ) dry_parcel_temperature_after_ascent = dry_adiabatic_temperature( ccl_temperature_cube.data, ccl_pressure_cube.data, self.pressure_level ) parcel_temperature_after_ascent, _ = adjust_for_latent_heat( dry_parcel_temperature_after_ascent, humidity_mixing_ratio_at_ccl, self.pressure_level, ) return parcel_temperature_after_ascent
[docs] def make_saturated_relative_humidity_cube(self) -> Cube: """Creates a cube of relative humidity at the cloud condensation level (CCL) with a value of 1.0, as by definition the relative humidity is 100 percent at the CCL. The temperature cube is used as a template for the metadata of the relative humidity cube. Only the name and units will be replaced. Returns: A cube of relative humidity at the CCL with a value of 1.0. """ relative_humidity_cube = self.temperature.copy( np.ones_like(self.temperature.data) ) relative_humidity_cube.rename("relative_humidity") relative_humidity_cube.units = "1" return relative_humidity_cube
[docs] def make_temperature_cube(self, temp_after_saturated_ascent: np.ndarray) -> Cube: """Puts the temperature information into a cube with appropriate metadata. Uses self.temperature as a template for the metadata of the temperature cube. Only the name and units will be replaced. Args: temp_after_saturated_ascent: An n dimensional array of the temperature after a saturated air parcel has been lifted adiabatically from the cloud condensation level (CCL) to a pressure level (K). Returns: A cube of the temperature of a saturated air parcel when it has been lifted adiabatically from the CCL to another pressure level (K) """ temp_cube = create_new_diagnostic_cube( name="parcel_temperature_after_saturated_ascent_from_ccl_to_pressure_level", units="K", template_cube=self.temperature, mandatory_attributes=generate_mandatory_attributes([self.temperature]), data=temp_after_saturated_ascent, ) temp_cube.add_aux_coord( DimCoord( self.pressure_level, long_name="pressure", units="Pa", attributes={"positive": "down"}, ) ) return temp_cube
[docs] def process(self, *cubes: Union[Cube, CubeList]) -> Cube: """ Calculates the temperature of a saturated air parcel that has risen adiabatically from the cloud condensation level to a pressure level. Args: cubes: Cubes of temperature (K), pressure (Pa) at the cloud condensation level. Returns: Cube of parcel_temperature_after_saturated_ascent_from_ccl_to_pressure_level """ cubes = as_cubelist(cubes) (self.temperature, self.pressure) = CubeList(cubes).extract( [ "air_temperature_at_condensation_level", "air_pressure_at_condensation_level", ] ) self.temperature.convert_units("K") self.pressure.convert_units("Pa") parcel_temp_at_pressure_level = self.parcel_temp_after_ascent() temp_cube = self.make_temperature_cube(parcel_temp_at_pressure_level) return temp_cube