GRIB encoder

An encoder is used to generate data in a suitable format that can be written/added to a target. Encoders are typically used implicitly via to_target() but we can also instantiate an object and work with it directly.

[1]:
import earthkit.data as ekd
[2]:
# get some input GRIB data
ds = ekd.from_source("file", "test.grib")
ds[0].ls()
[2]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20200513 1200 0 an 0 regular_ll
[3]:
# create a GribEncoder
encoder = ekd.create_encoder("grib")
encoder
[3]:
<earthkit.data.encoders.grib.GribEncoder at 0x1076ed7b0>

The method to call is GribEncoder.encode(). The template argument can be a GRIB field. In the example below GribEncoder.encode() will simply create a copy (clone) of the underlying GRIB handle in the field.

[4]:
r = encoder.encode(template=ds[0])
r
[4]:
<earthkit.data.encoders.grib.GribEncodedData at 0x107875de0>

The resulting object can be used in various ways.

[5]:
r.to_bytes()[:10]
[5]:
b'GRIB\x00\x02\x0e\x01\x00\x00'
[6]:
f = r.to_field()
f.ls()
[6]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20200513 1200 0 an 0 regular_ll
[7]:
with open("_res_encoded.grib", "wb") as out:
    r.to_file(out)

Specifying metadata

When we specify metadata it will be written into the resulting GRIB fields.

[8]:
r = encoder.encode(template=ds[0], metadata={"date": 20210514})
r.to_field().ls()
[8]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20210514 1200 0 an 0 regular_ll
[9]:
r = encoder.encode(template=ds[0], date=20210514)
r.to_field().ls()
[9]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20210514 1200 0 an 0 regular_ll

Specifying new values

To replace the values in the resulting fields we need to use the values keyword argument.

[10]:
vals = ds[0].values
r = encoder.encode(values=vals + 1, template=ds[0])
ds[0].values.max(), r.to_field().values.max()
[10]:
(315.4599609375, 316.4599609375)

Specifying a field

When a field is specified as the data it is used as a template.

[11]:
r = encoder.encode(data=ds[1], metadata={"date": 20210514})
r.to_field().ls()
[11]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf msl surface 0 20210514 1200 0 an 0 regular_ll

When both data and template are specified, the values from the field in data will be copied into the GRIB message created from the template.

[12]:
r = encoder.encode(data=ds[1], template=ds[0], metadata={"date": 20210514})
r.to_field().ls()
[12]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20210514 1200 0 an 0 regular_ll

We cannot use data, values and template together.

[13]:
try:
    r = encoder.encode(data=ds[1], template=ds[0], values=vals, metadata={"date": 20210514})
except Exception as e:
    print(e)
Cannot provide data, values and template together

Encoding without a template

It is possible to encode GRIB data without providing a template using only values and metadata. This is an experimental feature and only works for certain metadata keys and the grid has to be either global lat-lon or reduced Gaussian grid. The geography is inferred from the shape of the specified values.

[14]:
# global 1x1 degree data
import numpy as np

vals = np.random.normal(0, 1, (181, 360))
r = encoder.encode(values=vals, date=20250108, param="2t", time=12, step=24, edition=2)
r.to_field().ls()
[14]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t heightAboveGround 2 20250108 1200 24 af None regular_ll
[15]:
vals.max(), r.to_field().values.max()
[15]:
(4.697071658422638, 4.696983814239502)
[16]:
# encode as a pressure level field
r = encoder.encode(values=vals, date=20250108, param="t", level=700, levtype="pl", time=12, step=24, edition=2)
r.to_field().ls()
[16]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf t isobaricInhPa 700 20250108 1200 24 af None regular_ll
[17]:
# global O96 data
vals = np.random.normal(0, 1, 40320)
r = encoder.encode(values=vals, date=20250108, param="2t", time=12, step=24, edition=2)
r.to_field().ls(extra_keys=["isOctahedral","N"])
[17]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType isOctahedral N
0 ecmf 2t heightAboveGround 2 20250108 1200 24 af None reduced_gg 1 96

Using preset options

[18]:
# create a GribEncoder with preset options
encoder = ekd.create_encoder("grib", date=20250108, template=ds[0])

d1 = encoder.encode(step=12)
d2 = encoder.encode(step=24)
[19]:
d1.to_field().ls()
[19]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20250108 1200 12 fc 0 regular_ll
[20]:
d2.to_field().ls()
[20]:
centre shortName typeOfLevel level dataDate dataTime stepRange dataType number gridType
0 ecmf 2t surface 0 20250108 1200 24 fc 0 regular_ll

Working with fieldlists

[21]:
# create a GribEncoder
encoder = ekd.create_encoder("grib")

# encode now returns a generator
for d in encoder.encode(data=ds, step=18):
    print(d.to_field().ls(keys=["shortName", "step"]))
  shortName  step
0        2t    18
  shortName  step
0       msl    18
[ ]: