Skip to content

Commit b56c421

Browse files
committed
Adds basic CFAD calculation and tests
Fixes #1010
1 parent 4e98a26 commit b56c421

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

src/CSET/operators/plot.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,60 @@ def _validate_cubes_coords(
19211921
)
19221922

19231923

1924+
def _calculate_CFAD(
1925+
cube: iris.cube.Cube, vertical_coordinate: str, bin_edges: list[float]
1926+
) -> iris.cube.Cube:
1927+
"""Calculate a Contour Frequency by Altitude Diagram (CFAD).
1928+
1929+
Parameters
1930+
----------
1931+
cube: iris.cube.Cube
1932+
A cube of the data to be turned into a CFAD. It should be a minimum
1933+
of two dimensions with one being a user specified vertical coordinate.
1934+
vertical_coordinate: str
1935+
The vertical coordinate of the cube for the CFAD to be calculated over.
1936+
bin_edges: list[float]
1937+
The bin edges for the histogram. The bins need to be specified to
1938+
ensure consistency across the CFAD, otherwise it cannot be interpreted.
1939+
"""
1940+
# Setup empty array for containing the CFAD data.
1941+
CFAD_values = np.zeros(
1942+
(len(cube.coord(vertical_coordinate).points), len(bin_edges) - 1)
1943+
)
1944+
1945+
# Set iterator for CFAD values.
1946+
i = 0
1947+
1948+
# Calculate the CFAD as a histogram summing to one for each level.
1949+
for level_cube in cube.slices_over(vertical_coordinate):
1950+
# Note setting density to True does not produce the correct
1951+
# normalization for a CFAD, where each row must sum to one.
1952+
CFAD_values[i, :] = (
1953+
np.histogram(level_cube.data.reshape(level_cube.data.size), bins=bin_edges)[
1954+
0
1955+
]
1956+
/ level_cube.data.size
1957+
)
1958+
i += 1
1959+
# calculate central points for bins
1960+
bins = (np.array(bin_edges[:-1]) + np.array(bin_edges[1:])) / 2.0
1961+
bin_bounds = np.array((bin_edges[:-1], bin_edges[1:])).T
1962+
# Now construct the coordinates for the cube.
1963+
vert_coord = cube.coord(vertical_coordinate)
1964+
bin_coord = iris.coords.DimCoord(
1965+
bins, bounds=bin_bounds, standard_name=cube.standard_name, units=cube.units
1966+
)
1967+
# Now construct the cube that is to be output.
1968+
CFAD = iris.cube.Cube(
1969+
CFAD_values,
1970+
dim_coords_and_dims=[(vert_coord, 0), (bin_coord, 1)],
1971+
standard_name=cube.standard_name,
1972+
units="1",
1973+
)
1974+
CFAD.attributes["type"] = "Contour Frequency by Altitude Diagram (CFAD)"
1975+
return CFAD
1976+
1977+
19241978
####################
19251979
# Public functions #
19261980
####################

tests/operators/test_plot.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
from CSET.operators import collapse, misc, plot, read
2929

3030

31+
@pytest.fixture()
32+
def xwind() -> iris.cube.Cube:
33+
"""Get regridded xwind to run tests on."""
34+
return iris.load_cube("tests/test_data/ageofair/aoa_in_rgd.nc", "x_wind")
35+
36+
3137
def test_check_single_cube():
3238
"""Conversion to a single cube, and rejection where not possible."""
3339
cube = iris.cube.Cube([0.0])
@@ -1158,3 +1164,24 @@ def test_append_to_plot_index_aggregation(monkeypatch, tmp_working_dir):
11581164
with open("meta.json", "rt") as fp:
11591165
meta = json.load(fp)
11601166
assert "case_date" not in meta
1167+
1168+
1169+
def test_calculate_CFAD(xwind):
1170+
"""Test calculating a CFAD."""
1171+
bins = np.array([-50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50])
1172+
calculated_CFAD = np.zeros((len(xwind.coord("pressure").points), len(bins) - 1))
1173+
j = 0
1174+
for level_cube in xwind.slices_over("pressure"):
1175+
calculated_CFAD[j, :] = (
1176+
np.histogram(level_cube.data.reshape(level_cube.data.size), bins=bins)[0]
1177+
/ level_cube.data.size
1178+
)
1179+
j += 1
1180+
assert np.allclose(
1181+
plot._calculate_CFAD(
1182+
xwind, "pressure", [-50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50]
1183+
).data,
1184+
calculated_CFAD,
1185+
rtol=1e-06,
1186+
atol=1e-02,
1187+
)

0 commit comments

Comments
 (0)