Skip to content

Commit 31fe0d4

Browse files
malininaeElizaveta Malinina
andauthored
Adding hurs (realtive humidity) derivation script (#2397)
Co-authored-by: Elizaveta Malinina <[email protected]>
1 parent 2e6804a commit 31fe0d4

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Derivation of variable `hurs`."""
2+
3+
import cf_units
4+
import dask.array as da
5+
import iris
6+
from iris import NameConstraint
7+
8+
from ._baseclass import DerivedVariableBase
9+
10+
# Constants
11+
GAS_CONSTANT_WV = 461.5 # JK-1kg-1
12+
ENTALPY_OF_VAPORIZATION = 2.501e6 # Jkg-1
13+
14+
15+
class DerivedVariable(DerivedVariableBase):
16+
"""Derivation of variable `hurs`."""
17+
18+
@staticmethod
19+
def required(project):
20+
"""Declare the variables needed for derivation."""
21+
required = [
22+
{"short_name": "tdps"},
23+
{"short_name": "tas"},
24+
]
25+
return required
26+
27+
@staticmethod
28+
def calculate(cubes):
29+
"""Compute relative humidity.
30+
31+
Relative humidity computed from dewpoint temperature and
32+
surface air temperature following Bohren and Albrecht 1998.
33+
"""
34+
tdps_cube = cubes.extract_cube(NameConstraint(var_name="tdps"))
35+
tas_cube = cubes.extract_cube(NameConstraint(var_name="tas"))
36+
37+
cubes_difference = tas_cube - tdps_cube
38+
cubes_product = tas_cube * tdps_cube
39+
40+
log_humidity_cube = (
41+
-ENTALPY_OF_VAPORIZATION
42+
* cubes_difference
43+
/ (GAS_CONSTANT_WV * cubes_product)
44+
)
45+
46+
hurs_cube = 100 * iris.analysis.maths.exp(log_humidity_cube)
47+
48+
hurs_cube.units = cf_units.Unit("%")
49+
50+
hurs_cube.data = da.ma.where(
51+
hurs_cube.core_data() > 100.0, 100.0, hurs_cube.core_data()
52+
)
53+
54+
return hurs_cube
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""Test derivation of ``hurs``."""
2+
3+
import iris
4+
import numpy as np
5+
import pytest
6+
from iris.cube import Cube, CubeList
7+
8+
from esmvalcore.preprocessor._derive import hurs
9+
10+
11+
@pytest.fixture
12+
def cubes():
13+
"""Input cubes for derivation of ``hurs``."""
14+
time_coord = iris.coords.DimCoord(
15+
[0.0, 1.0, 2.0, 3.0],
16+
standard_name="time",
17+
var_name="time",
18+
units="days since 1950-01-01 00:00:00",
19+
)
20+
lat_coord = iris.coords.DimCoord(
21+
[45.0], standard_name="latitude", var_name="lat", units="degrees"
22+
)
23+
lon_coord = iris.coords.DimCoord(
24+
[10.0], standard_name="longitude", var_name="lon", units="degrees"
25+
)
26+
27+
coord_specs = [(time_coord, 0), (lat_coord, 1), (lon_coord, 2)]
28+
29+
tdps_cube = Cube(
30+
[[[279.17]], [[282.73]], [[288.15]], [[288.25]]],
31+
dim_coords_and_dims=coord_specs,
32+
standard_name="dew_point_temperature",
33+
var_name="tdps",
34+
units="K",
35+
)
36+
tas_cube = Cube(
37+
[[[288.15]], [[288.15]], [[288.15]], [[288.15]]],
38+
dim_coords_and_dims=coord_specs,
39+
standard_name="air_temperature",
40+
var_name="tas",
41+
units="K",
42+
)
43+
return CubeList([tdps_cube, tas_cube])
44+
45+
46+
def test_hurs_calculate(cubes):
47+
"""Test function ``calculate``."""
48+
derived_var = hurs.DerivedVariable()
49+
required_vars = derived_var.required("CMIP6")
50+
expected_required_vars = [
51+
{"short_name": "tdps"},
52+
{"short_name": "tas"},
53+
]
54+
assert required_vars == expected_required_vars
55+
out_cube = derived_var.calculate(cubes)
56+
assert out_cube.shape == (4, 1, 1)
57+
assert out_cube.units == "%"
58+
assert out_cube.coords("time")
59+
assert out_cube.coords("latitude")
60+
assert out_cube.coords("longitude")
61+
np.testing.assert_allclose(
62+
out_cube.data,
63+
[[[54.6093]], [[69.7301]], [[100.0]], [[100.0]]],
64+
rtol=0.00005,
65+
)
66+
np.testing.assert_allclose(
67+
out_cube.coord("time").points, [0.0, 1.0, 2.0, 3.0]
68+
)
69+
np.testing.assert_allclose(out_cube.coord("latitude").points, [45.0])
70+
np.testing.assert_allclose(out_cube.coord("longitude").points, [10.0])

0 commit comments

Comments
 (0)