Skip to content
Merged
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
36 changes: 33 additions & 3 deletions improver/psychrometric_calculations/condensation_trails.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

from improver import BasePlugin
from improver.constants import EARTH_REPSILON
from improver.psychrometric_calculations.psychrometric_calculations import (
calculate_svp_in_air,
)
from improver.utilities.common_input_handle import as_cubelist


Expand Down Expand Up @@ -73,8 +76,32 @@ def _calculate_engine_mixing_ratios(
/ EARTH_REPSILON
)

def _find_local_vapour_pressure(self, pressure_levels: np.ndarray) -> np.ndarray:
"""
Calculate the local vapour pressure (svp) at the given pressure levels using the temperature and pressure data.

Args:
pressure_levels (np.ndarray): Pressure levels (Pa).

Returns:
np.ndarray: The localised vapour pressure at the given
pressure levels (Pa).
"""
# Pressure levels has to be reshaped to match the temperature and humidity dimensions
pressure_levels_reshaped = np.reshape(
pressure_levels,
(len(pressure_levels),) + (1,) * (self.temperature.ndim - 1),
)
svp = calculate_svp_in_air(
temperature=self.temperature, pressure=pressure_levels_reshaped
)
return self.relative_humidity * svp

def process_from_arrays(
self, temperature: np.ndarray, humidity: np.ndarray, pressure_levels: np.ndarray
self,
temperature: np.ndarray,
relative_humidity: np.ndarray,
pressure_levels: np.ndarray,
) -> np.ndarray:
"""
Main entry point of this class for data as Numpy arrays
Expand All @@ -84,19 +111,22 @@ def process_from_arrays(

Args:
temperature (np.ndarray): Temperature data on pressure levels where pressure is the leading axis (K).
humidity (np.ndarray): Relative humidity data on pressure levels where pressure is the leading axis (kg kg-1).
relative_humidity (np.ndarray): Relative humidity data on pressure levels where pressure is the leading axis (kg kg-1).
pressure_levels (np.ndarray): Pressure levels (Pa).

Returns:
np.ndarray: The calculated engine mixing ratios on pressure levels (Pa/K).
This is a placeholder until the full contrail formation logic is implemented.
"""
self.temperature = temperature
self.humidity = humidity
self.relative_humidity = relative_humidity
self.pressure_levels = pressure_levels
self.engine_mixing_ratios = self._calculate_engine_mixing_ratios(
self.pressure_levels
)
self.local_vapour_pressure = self._find_local_vapour_pressure(
self.pressure_levels
)
return self.engine_mixing_ratios

def process(self, *cubes: Union[Cube, CubeList]) -> Cube:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from improver.psychrometric_calculations.condensation_trails import (
CondensationTrailFormation,
)
from improver.psychrometric_calculations.psychrometric_calculations import (
calculate_svp_in_air,
)
from improver.synthetic_data.set_up_test_cubes import set_up_variable_cube

LOCAL_MANDATORY_ATTRIBUTES = {
Expand Down Expand Up @@ -168,3 +171,55 @@ def test_engine_mixing_ratio(
# Check that _calculate_engine_mixing_ratios works after process
mixing_ratios = plugin._calculate_engine_mixing_ratios(pressure_levels)
np.testing.assert_array_equal(mixing_ratios, expected_mixing_ratios)


@pytest.mark.parametrize(
"temperature, relative_humidity, pressure_levels, expected_vapour_pressure",
[
# Test with multiple pressure levels
(
np.array([250.0, 260.0, 270.0], dtype=np.float32),
np.array([0.5, 0.6, 0.7], dtype=np.float32),
np.array([100000, 90000, 80000], dtype=np.float32),
calculate_svp_in_air(
np.array([250.0, 260.0, 270.0], dtype=np.float32),
np.array([100000, 90000, 80000], dtype=np.float32),
)
* np.array([0.5, 0.6, 0.7], dtype=np.float32),
),
# Test with single pressure level
(
np.array([280.0], dtype=np.float32),
np.array([0.8], dtype=np.float32),
np.array([95000], dtype=np.float32),
calculate_svp_in_air(
np.array([280.0], dtype=np.float32),
np.array([95000], dtype=np.float32),
)
* np.array([0.8], dtype=np.float32),
),
],
)
def test_find_local_vapour_pressure(
temperature: np.ndarray,
relative_humidity: np.ndarray,
pressure_levels: np.ndarray,
expected_vapour_pressure: np.ndarray,
) -> None:
"""
Test that _find_local_vapour_pressure returns the expected local vapour pressure values.

This test sets the temperature and relative_humidity attributes on the plugin,
calls _find_local_vapour_pressure, and checks the output against expected values.

Args:
temperature (np.ndarray): Array of temperature values (K).
relative_humidity (np.ndarray): Array of relative humidity values (fraction).
pressure_levels (np.ndarray): Array of pressure levels (Pa).
expected_vapour_pressure (np.ndarray): Expected vapour pressure output (Pa).
"""
plugin = CondensationTrailFormation()
plugin.temperature = temperature
plugin.relative_humidity = relative_humidity
result = plugin._find_local_vapour_pressure(pressure_levels)
np.testing.assert_allclose(result, expected_vapour_pressure)
Loading