Fieldlist: Interpolating from hybrid to pressure levels

This notebook demonstrates how to interpolate GRIB fieldlist data on hybrid model levels to height levels with interpolate_hybrid_to_pressure_levels(). We will use earthkit-data for fieldlist support and earthkit-plots for plotting.

[1]:
import earthkit.data as ekd
import earthkit.plots as ekp
import numpy as np

import earthkit.meteo.vertical.fieldlist as vertical

Hybrid levels

Hybrid levels are terrain following at the bottom and isobaric at the top of the atmosphere with a transition in between. This is the vertical coordinate system in the IFS model.

For details about the hybrid levels see [IFS-CY49R1-Dynamics] Chapter 2, Section 2.2.1. Also see the /how-tos/vertical/hybrid_levels_grib.ipynb notebook for the related computations in earthkit-meteo.

Getting the data

The input is GRIB data on 137 IFS model levels for one step. We fetch the file and extract temperature, specific humidity and the log surface pressure.

[2]:
fl = ekd.from_source("sample", "tq_ml137_europe.grib2").to_fieldlist()

# LNSP → surface pressure [Pa]
lnsp = fl.sel({"parameter.variable": "lnsp", "vertical.level": 1})[0]
sp = lnsp.set(values=np.exp(lnsp.values))

t = fl.sel({"parameter.variable": "t"})  # temperature, 137 levels
q = fl.sel({"parameter.variable": "q"})  # specific humidity, 137 levels

t.head()
[2]:
parameter.variable time.valid_datetime time.base_datetime time.step vertical.level vertical.level_type ensemble.member geography.grid_type
0 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 1 hybrid None regular_ll
1 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 2 hybrid None regular_ll
2 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 3 hybrid None regular_ll
3 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 4 hybrid None regular_ll
4 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 5 hybrid None regular_ll

Please note that although lnsp is valid for the surface in our data it is available on hybrid level 1. This is how IFS forecast and analysis data is stored in ECMWF’s MARS archive (from where our the data is originating).

The A and B coefficients defining the hybrid levels are embedded in the GRIB metadata and are inferred automatically.

Using interpolate_hybrid_to_pressure_levels

interpolate_hybrid_to_pressure_levels() interpolates data from hybrid levels to a set of target pressure levels (Pa).

[3]:
target_p = [70000.0, 50000.0]  # Pa

t_res = vertical.interpolate_hybrid_to_pressure_levels(
    t,  # data to interpolate
    target_p,
    sp,
    interpolation="linear",
)
t_res.ls()
[3]:
parameter.variable time.valid_datetime time.base_datetime time.step vertical.level vertical.level_type ensemble.member geography.grid_type
0 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 700.0 pressure None regular_ll
1 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 500.0 pressure None regular_ll
[4]:
# the first 2 values from the resulting fields
t_res.values[:, :2]
[4]:
array([[256.7211221 , 257.00983999],
       [244.06243185, 244.50014912]])
[5]:
# plot the resulting fields
ekp.quickplot(t_res)
[5]:
<earthkit.plots.components.figures.Figure at 0x126e2bb60>
../_images/tutorials_interpolate_hybrid_to_pl_fieldlist_14_1.png

Using a subset of levels

It is possible to use only a subset of the model levels in the input data. This can significantly speed up the computations and reduce memory usage. The model level range must be contiguous and include the bottom-most level.

Unfortunately, the model level pressure is not constant (changing with gridpoint and time) and to determine which subset we need we have to find the lowest model level that is entirely above the lowest target pressure. We can use a guess for it. for it. E.g. if we want to interpolate data to pressures of 70000 and 50000 Pa the lowest 50 model levels should be enough. For the sake of this exercise we check the validity of this assumption for the current data with pressure_on_hybrid_levels(). The computation shows that the entire model level 88 lies higher than the 50000 Pa isosurface, so our assumption was correct.

[6]:
p_sub = vertical.pressure_on_hybrid_levels(sp, levels=[88], output="full")

# the max pressure on model level 88
p_sub[0].values.max()
[6]:
np.float64(36971.49325737165)

Now we perform the interpolation by using only the lowest 50 model levels.

[7]:
# keep only the 50 lowest model levels
t_sub = t.sel({"vertical.level": list(range(88, 138))})

t_res_sub = vertical.interpolate_hybrid_to_pressure_levels(
    t_sub,
    target_p,
    sp,
    interpolation="linear",
)
t_res_sub.ls()
[7]:
parameter.variable time.valid_datetime time.base_datetime time.step vertical.level vertical.level_type ensemble.member geography.grid_type
0 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 700.0 pressure None regular_ll
1 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 500.0 pressure None regular_ll
[8]:
# the first value from the resulting fields
t_res.values[:, 0]
[8]:
array([256.7211221 , 244.06243185])

Interpolating to a pressure field

The target pressure in interpolate_hybrid_to_pressure_levels() can also be a field/fieldlist, with changing target in each gridpoint. To demonstrate this we interpolate the temperature to the pressure of the 2 PVU potential vorticity level. First, we fetch this field for the same date and time as our input data.

[9]:
pres_pv = ekd.from_source("sample", "pressure_on_pv_europe.grib1").to_fieldlist()[0]
pres_pv.ls()
[9]:
parameter.variable time.valid_datetime time.base_datetime time.step vertical.level vertical.level_type ensemble.member geography.grid_type
0 pres 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 2000 potential_vorticity 0 regular_ll

Next, we interpolate the data to the target field.

[10]:
t_res_pv = vertical.interpolate_hybrid_to_pressure_levels(
    t,
    pres_pv,
    sp,
    interpolation="linear",
)

t_res_pv.ls()
[10]:
parameter.variable time.valid_datetime time.base_datetime time.step vertical.level vertical.level_type ensemble.member geography.grid_type
0 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 431.157539 pressure None regular_ll

Finally, plot the pressure of the 2 PVU potential vorticity level and the temperature interpolated to it side by side.

[11]:
ekp.quickplot(ekd.FieldList.from_fields([pres_pv, t_res_pv]))
[11]:
<earthkit.plots.components.figures.Figure at 0x1698bb750>
../_images/tutorials_interpolate_hybrid_to_pl_fieldlist_27_1.png

Writing to GRIB

The resulting FieldList keeps all the values in memory. We can save the data to GRIB with to_target.

[12]:
t_res.to_target("file", "_hybrid_to_pl.grib")

# read back and verify
ekd.from_source("file", "_hybrid_to_pl.grib").to_fieldlist().ls()
[12]:
parameter.variable time.valid_datetime time.base_datetime time.step vertical.level vertical.level_type ensemble.member geography.grid_type
0 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 7 pressure None regular_ll
1 t 2019-06-02 12:00:00 2019-06-02 12:00:00 0 days 5 pressure None regular_ll
[ ]: