Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a plugin to calculate the virtual temperature (version 2) #2061

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Meabh NicGuidhir <[email protected]> <meabh.nicguidhir@metoffice
Neil Crosswaite <[email protected]> <[email protected]>
Paul Abernethy <[email protected]> <[email protected]>
Peter Jordan <[email protected]> <[email protected]>
Phil Relton <[email protected]> <[email protected]>
Phoebe Lambert <[email protected]> <[email protected]>
Sam Griffiths <[email protected]> <[email protected]>
Shafiat Dewan <[email protected]> <[email protected]>
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ below:
- Benjamin Owen (Bureau of Meteorology, Australia)
- Carwyn Pelley (Met Office, UK)
- Tim Pillinger (Met Office, UK)
- Phil Relton (Met Office, UK)
- Fiona Rust (Met Office, UK)
- Chris Sampson (Met Office, UK)
- Caroline Sandford (Met Office, UK)
Expand Down
65 changes: 65 additions & 0 deletions improver/virtual_temperature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# (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.
"""Calculate the virtual temperature from temperature and humidity mixing ratio."""

from typing import Union

from iris.cube import Cube, CubeList

from improver import BasePlugin
from improver.utilities.common_input_handle import as_cubelist


class VirtualTemperature(BasePlugin):
"""Plugin class to handle virtual temperature calculations."""

@staticmethod
def get_virtual_temperature(temperature: Cube, humidity_mixing_ratio: Cube) -> Cube:
"""
Calculate the virtual temperature from temperature and humidity mixing ratio.

Args:
temperature:
Cube of temperature.
humidity_mixing_ratio:
Cube of humidity mixing ratio.

Returns:
Cube of virtual_temperature.
"""
# Calculate the virtual temperature
virtual_temperature = temperature * (1 + 0.61 * humidity_mixing_ratio)

# Update the cube metadata
virtual_temperature.rename("virtual_temperature")
virtual_temperature.attributes["units_metadata"] = "on-scale"

return virtual_temperature

def process(self, *cubes: Union[Cube, CubeList]) -> Cube:
"""
Main entry point for this class.

Args:
cubes:
air_temperature:
Cube of temperature.
humidity_mixing_ratio:
Cube of humidity mixing ratios.

Returns:
Cube of virtual_temperature.
"""

# Get the cubes into the correct format and extract the relevant cubes
cubes = as_cubelist(*cubes)
(self.temperature, self.humidity_mixing_ratio) = cubes.extract_cubes(
["air_temperature", "humidity_mixing_ratio"]
)

# Calculate the virtual temperature
return self.get_virtual_temperature(
self.temperature, self.humidity_mixing_ratio
)
Empty file.
79 changes: 79 additions & 0 deletions improver_tests/virtual_temperature/test_VirtualTemperature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# (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.
"""Unit tests for the VirtualTemperature plugin"""

from unittest.mock import patch, sentinel

import numpy as np
import pytest
from iris.cube import Cube

from improver.virtual_temperature import VirtualTemperature
from improver_tests.utilities.copy_metadata.test_CopyMetadata import HaltExecution


@pytest.fixture(name="temperature_cube")
def temperature_cube_fixture() -> Cube:
"""
Set up a temperature cube for use in tests.
Has 2 realizations, 7 latitudes spanning from 60S to 60N and 3 longitudes.
"""
data = np.full((2, 7, 3), dtype=np.float32, fill_value=273.15)
cube = Cube(
data,
standard_name="air_temperature",
units="K",
)
return cube


@pytest.fixture(name="humidity_mixing_ratio_cube")
def humidity_mixing_ratio_cube_fixture() -> Cube:
"""
Set up a humidity mixing ratio cube for use in tests.
Has 2 realizations, 7 latitudes spanning from 60S to 60N and 3 longitudes.
"""
data = np.full((2, 7, 3), dtype=np.float32, fill_value=0.01)
cube = Cube(
data,
standard_name="humidity_mixing_ratio",
units="1",
)
return cube


@pytest.fixture(name="virtual_temperature_cube")
def virtual_temperature_cube_fixture() -> Cube:
"""
Set up a virtual temperature cube for use in tests.
Has 2 realizations, 7 latitudes spanning from 60S to 60N and 3 longitudes.
"""
data = np.full((2, 7, 3), dtype=np.float32, fill_value=274.81622)
cube = Cube(
data,
standard_name="virtual_temperature",
units="K",
attributes={"units_metadata": "on-scale"},
)
return cube


@patch("improver.virtual_temperature.as_cubelist")
def test_as_cubelist_called(mock_as_cubelist):
"""Test that the as_cubelist function is called with the input cubes"""
mock_as_cubelist.side_effect = HaltExecution
try:
VirtualTemperature()(sentinel.cube1, sentinel.cube2)
except HaltExecution:
pass
mock_as_cubelist.assert_called_once_with(sentinel.cube1, sentinel.cube2)


def test_VirtualTemperature_get_virtual_temperature(
temperature_cube, humidity_mixing_ratio_cube, virtual_temperature_cube
):
"""Test the get_virtual_temperature method produces a virtual temperature cube"""
result = VirtualTemperature()(temperature_cube, humidity_mixing_ratio_cube)
assert result == virtual_temperature_cube