Source code for improver.temperature.layer_mean_temperature

# (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.
import iris
import numpy as np
from cf_units import Unit
from iris.cube import Cube

from improver import BasePlugin


[docs] class LayerTemperatureInterpolation(BasePlugin): """ Plugin to interpolate temperature values at specified layer boundaries. This plugin extracts all temperature levels within a specified vertical layer (between `bottom` and `top` heights, in feet), and interpolates temperature at the exact base and top of the layer. The output is a cube containing temperature at all interior levels plus the interpolated base and top. """
[docs] def process( self, temp_cube: Cube, bottom: float, top: float, verbosity: int = 0 ) -> Cube: """ Interpolate temperature values at layer boundaries. Args: temp_cube: Input temperature cube with a height coordinate in metres. bottom: Lower boundary of the layer in feet. top: Upper boundary of the layer in feet. verbosity: Verbosity level for printing debug information. Returns: Cube containing temperature at all layer heights (base, interior, and top). """ if bottom >= top: raise ValueError(f"Bottom ({bottom} ft) must be less than top ({top} ft).") # Convert layer bounds from feet to metres using cf_units bottom_m = Unit("ft").convert(bottom, "m") top_m = Unit("ft").convert(top, "m") if verbosity: print(f"Interpolating temperature at base: {bottom} ft") # Extract cube of temperature levels within layer between_layer_temp_cube = temp_cube.extract( iris.Constraint(height=lambda point: bottom_m < point < top_m) ) # Interpolate temperature at base of layer base_temp = temp_cube.interpolate( [("height", np.array([bottom_m], dtype=np.float32))], iris.analysis.Linear(), collapse_scalar=False, ) if verbosity: print(f"Interpolating temperature at top: {top} ft") # Interpolate temperature at top of layer top_temp = temp_cube.interpolate( [("height", np.array([top_m], dtype=np.float32))], iris.analysis.Linear(), collapse_scalar=False, ) # Merge cubes of temperature at top, bottom and within layer cubes_to_merge = [base_temp, between_layer_temp_cube, top_temp] cubes_to_merge = [cube for cube in cubes_to_merge if cube is not None] layer_levels_temp_cube = iris.cube.CubeList(cubes_to_merge).concatenate_cube() return layer_levels_temp_cube
[docs] class CalculateLayerMeanTemperature(BasePlugin): """Calculate the vertically weighted mean temperature for a layer."""
[docs] def process(self, layer_cube: Cube, verbosity: int = 0) -> Cube: """ Calculate the altitude-weighted mean temperature across the layer. Args: layer_cube: Cube containing temperature at all heights within the specified layer (including interpolated base and top). verbosity: Set level of output to print. Returns: 2D cube of layer mean temperature. """ # Set up array for holding sum of products of temperature and vertical distance layer_temp_product = np.zeros(layer_cube.data.shape[1:], dtype=np.float32) # Estimate mean temperature of layers and # Weight by vertical extent of layer altitude_array = layer_cube.coord("height").points for alt_index in range(1, len(altitude_array) - 1): layer_thickness = ( altitude_array[alt_index + 1] - altitude_array[alt_index - 1] ) / 2 layer_temp_product += layer_cube.data[alt_index, :, :] * layer_thickness # Add contributions from base and top layer_temp_product += ( layer_cube.data[0, :, :] * (altitude_array[1] - altitude_array[0]) / 2 ) layer_temp_product += ( layer_cube.data[-1, :, :] * (altitude_array[-1] - altitude_array[-2]) / 2 ) # Divide by total thickness to get mean lmt_array = ( layer_temp_product / (altitude_array[-1] - altitude_array[0]) ).astype(np.float32) if verbosity: print("Layer mean temperature array:", lmt_array) # Wrap result in a cube and add required metadata lmt_cube = iris.cube.Cube( lmt_array, var_name="air_temperature", units="K", attributes=layer_cube.attributes.copy(), dim_coords_and_dims=( (layer_cube.coord("projection_y_coordinate"), 0), (layer_cube.coord("projection_x_coordinate"), 1), ), aux_coords_and_dims=( (layer_cube.coord("forecast_period"), ()), (layer_cube.coord("forecast_reference_time"), ()), (layer_cube.coord("time"), ()), ), ) return lmt_cube