Skip to content

Commit 1525178

Browse files
authored
Add compression and documentation options to netCDF exporter (#352)
* add scale, offset, fill_value, datatype and descriptions as option to netCDF exporter * fix: revert realization to ens_number in createvariable for tests * add tests using new function argument options
1 parent bdfa575 commit 1525178

File tree

3 files changed

+73
-13
lines changed

3 files changed

+73
-13
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/psf/black
3-
rev: 22.6.0
3+
rev: 24.3.0
44
hooks:
55
- id: black
66
language_version: python3

pysteps/io/exporters.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,11 @@ def initialize_forecast_exporter_netcdf(
371371
shape,
372372
metadata,
373373
n_ens_members=1,
374+
datatype=np.float32,
374375
incremental=None,
376+
fill_value=None,
377+
scale_factor=None,
378+
offset=None,
375379
**kwargs,
376380
):
377381
"""
@@ -401,12 +405,35 @@ def initialize_forecast_exporter_netcdf(
401405
n_ens_members: int
402406
Number of ensemble members in the forecast. This argument is ignored if
403407
incremental is set to 'member'.
408+
datatype: np.dtype, optional
409+
The datatype of the output values. Defaults to np.float32.
404410
incremental: {None,'timestep','member'}, optional
405411
Allow incremental writing of datasets into the netCDF files.\n
406412
The available options are: 'timestep' = write a forecast or a forecast
407413
ensemble for a given time step; 'member' = write a forecast sequence
408414
for a given ensemble member. If set to None, incremental writing is
409415
disabled.
416+
fill_value: int, optional
417+
Fill_value for missing data. Defaults to None, which means that the
418+
standard netCDF4 fill_value is used.
419+
scale_factor: float, optional
420+
The scale factor to scale the data as: store_value = scale_factor *
421+
precipitation_value + offset. Defaults to None. The scale_factor
422+
can be used to reduce data storage.
423+
offset: float, optional
424+
The offset to offset the data as: store_value = scale_factor *
425+
precipitation_value + offset. Defaults to None.
426+
427+
Other Parameters
428+
----------------
429+
institution: str
430+
The instute, company or community that has created the nowcast.
431+
Default: the pySTEPS community (https://pysteps.github.io)
432+
references: str
433+
Any references to be included in the netCDF file. Defaults to " ".
434+
comment: str
435+
Any comments about the data or storage protocol that should be
436+
included in the netCDF file. Defaults to " ".
410437
411438
Returns
412439
-------
@@ -448,18 +475,25 @@ def initialize_forecast_exporter_netcdf(
448475
if n_ens_members > 1:
449476
n_ens_gt_one = True
450477

478+
# Kwargs to be used as description strings in the netCDF
479+
institution = kwargs.get(
480+
"institution", "the pySTEPS community (https://pysteps.github.io)"
481+
)
482+
references = kwargs.get("references", "")
483+
comment = kwargs.get("comment", "")
484+
451485
exporter = {}
452486

453487
outfn = os.path.join(outpath, outfnprefix + ".nc")
454488
ncf = netCDF4.Dataset(outfn, "w", format="NETCDF4")
455489

456490
ncf.Conventions = "CF-1.7"
457491
ncf.title = "pysteps-generated nowcast"
458-
ncf.institution = "the pySTEPS community (https://pysteps.github.io)"
492+
ncf.institution = institution
459493
ncf.source = "pysteps" # TODO(exporters): Add pySTEPS version here
460494
ncf.history = ""
461-
ncf.references = ""
462-
ncf.comment = ""
495+
ncf.references = references
496+
ncf.comment = comment
463497

464498
h, w = shape
465499

@@ -559,14 +593,22 @@ def initialize_forecast_exporter_netcdf(
559593
if incremental == "member" or n_ens_gt_one:
560594
var_f = ncf.createVariable(
561595
var_name,
562-
np.float32,
596+
datatype=datatype,
563597
dimensions=("ens_number", "time", "y", "x"),
598+
compression="zlib",
564599
zlib=True,
565600
complevel=9,
601+
fill_value=fill_value,
566602
)
567603
else:
568604
var_f = ncf.createVariable(
569-
var_name, np.float32, dimensions=("time", "y", "x"), zlib=True, complevel=9
605+
var_name,
606+
datatype=datatype,
607+
dimensions=("time", "y", "x"),
608+
compression="zlib",
609+
zlib=True,
610+
complevel=9,
611+
fill_value=fill_value,
570612
)
571613

572614
if var_standard_name is not None:
@@ -576,6 +618,11 @@ def initialize_forecast_exporter_netcdf(
576618
var_f.units = var_unit
577619
if grid_mapping_var_name is not None:
578620
var_f.grid_mapping = grid_mapping_var_name
621+
# Add gain and offset
622+
if scale_factor is not None:
623+
var_f.scale_factor = scale_factor
624+
if offset is not None:
625+
var_f.add_offset = offset
579626

580627
exporter["method"] = "netcdf"
581628
exporter["ncfile"] = ncf

pysteps/tests/test_exporters.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,21 @@
1616
from pysteps.tests.helpers import get_precipitation_fields, get_invalid_mask
1717

1818
# Test arguments
19-
exporter_arg_names = ("n_ens_members", "incremental")
19+
exporter_arg_names = (
20+
"n_ens_members",
21+
"incremental",
22+
"datatype",
23+
"fill_value",
24+
"scale_factor",
25+
"offset",
26+
)
2027

2128
exporter_arg_values = [
22-
(1, None),
23-
(1, "timestep"),
24-
(2, None),
25-
(2, "timestep"),
26-
(2, "member"),
29+
(1, None, np.float32, None, None, None),
30+
(1, "timestep", np.float32, 65535, None, None),
31+
(2, None, np.float32, 65535, None, None),
32+
(2, "timestep", np.float32, None, None, None),
33+
(2, "member", np.float64, None, 0.01, 1.0),
2734
]
2835

2936

@@ -46,7 +53,9 @@ def test_get_geotiff_filename():
4653

4754

4855
@pytest.mark.parametrize(exporter_arg_names, exporter_arg_values)
49-
def test_io_export_netcdf_one_member_one_time_step(n_ens_members, incremental):
56+
def test_io_export_netcdf_one_member_one_time_step(
57+
n_ens_members, incremental, datatype, fill_value, scale_factor, offset
58+
):
5059
"""
5160
Test the export netcdf.
5261
Also, test that the exported file can be read by the importer.
@@ -78,7 +87,11 @@ def test_io_export_netcdf_one_member_one_time_step(n_ens_members, incremental):
7887
shape,
7988
metadata,
8089
n_ens_members=n_ens_members,
90+
datatype=datatype,
8191
incremental=incremental,
92+
fill_value=fill_value,
93+
scale_factor=scale_factor,
94+
offset=offset,
8295
)
8396

8497
if n_ens_members > 1:

0 commit comments

Comments
 (0)