-
Notifications
You must be signed in to change notification settings - Fork 71
Add support for NETCDF3_64BIT_DATA in write_netcdf()
#633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
xylar
merged 7 commits into
MPAS-Dev:master
from
xylar:add-cdf5-handling-to-write-netcdf
Jun 6, 2025
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
10a66de
Add support for `NETCDF3_64BIT_DATA` in `write_netcdf()`
xylar 8657511
Convert int64 to int32 in write_netcdf()
xylar 923aa9d
Add docs for I/O tools
xylar 5eff64f
Add nco as a dependency
xylar 6626294
Add unit tests for write_netcdf()
xylar a60477f
Add logger when writting netcdf in mask module
xylar 2fab2c8
Delete the temporary NETCDF4 file if converting to CDF5
xylar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ hdf5 | |
| inpoly | ||
| libnetcdf | ||
| matplotlib-base>=3.9.0 | ||
| nco | ||
| netcdf4 | ||
| networkx | ||
| numpy>=2.0,<3.0 | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,8 @@ analyzing simulations, and in other MPAS-related workflows. | |
|
|
||
| config | ||
|
|
||
| io | ||
|
|
||
| logging | ||
|
|
||
| transects | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| .. _io: | ||
|
|
||
| ********* | ||
| I/O Tools | ||
| ********* | ||
|
|
||
| The :py:mod:`mpas_tools.io` module provides utilities for reading and writing | ||
| NetCDF files, especially for compatibility with MPAS mesh and data conventions. | ||
|
|
||
| write_netcdf | ||
| ============ | ||
|
|
||
| The :py:func:`mpas_tools.io.write_netcdf()` function writes an | ||
| ``xarray.Dataset`` to a NetCDF file, ensuring MPAS compatibility (e.g., | ||
| converting int64 to int32, handling fill values, and updating the history | ||
| attribute). It also supports writing in various NetCDF formats, including | ||
| conversion to ``NETCDF3_64BIT_DATA`` using ``ncks`` if needed. | ||
|
|
||
| Example usage: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| import xarray as xr | ||
| from mpas_tools.io import write_netcdf | ||
|
|
||
| # Create a simple dataset | ||
| ds = xr.Dataset({'foo': (('x',), [1, 2, 3])}) | ||
| write_netcdf(ds, 'output.nc') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| import os | ||
| import subprocess | ||
|
|
||
| import numpy as np | ||
| import pytest | ||
| import xarray as xr | ||
|
|
||
| from mpas_tools.io import write_netcdf | ||
|
|
||
| from .util import get_test_data_file | ||
|
|
||
| TEST_MESH = get_test_data_file('mesh.QU.1920km.151026.nc') | ||
|
|
||
|
|
||
| @pytest.mark.skipif( | ||
| not os.path.exists(TEST_MESH), reason='Test mesh not available' | ||
| ) | ||
| def test_write_netcdf_basic(tmp_path): | ||
| ds = xr.open_dataset(TEST_MESH) | ||
| out_file = tmp_path / 'test_basic.nc' | ||
| write_netcdf(ds, str(out_file)) | ||
| ds2 = xr.open_dataset(out_file) | ||
| # Should have same dimensions and variables | ||
| assert set(ds.dims) == set(ds2.dims) | ||
| for var in ds.data_vars: | ||
| assert var in ds2.data_vars | ||
| ds2.close() | ||
|
|
||
|
|
||
| @pytest.mark.skipif( | ||
| not os.path.exists(TEST_MESH), reason='Test mesh not available' | ||
| ) | ||
| def test_write_netcdf_cdf5_format(tmp_path): | ||
| ds = xr.open_dataset(TEST_MESH) | ||
| out_file = tmp_path / 'test_cdf5.nc' | ||
| write_netcdf(ds, str(out_file), format='NETCDF3_64BIT_DATA') | ||
| # Use ncdump -k to check format | ||
| result = subprocess.run( | ||
| ['ncdump', '-k', str(out_file)], | ||
| capture_output=True, | ||
| text=True, | ||
| check=True, | ||
| ) | ||
| # Should be cdf5 for NETCDF3_64BIT_DATA | ||
| assert result.stdout.strip() == 'cdf5' | ||
| # Check that the temporary file was deleted | ||
| tmp_file = ( | ||
| out_file.parent / f'_tmp_{out_file.stem}.netcdf4{out_file.suffix}' | ||
| ) | ||
| assert not os.path.exists(tmp_file) | ||
|
|
||
|
|
||
| def test_write_netcdf_int64_conversion_and_attr(tmp_path): | ||
| # Create a dataset with int64 variable and an attribute | ||
| arr = np.array([1, 2, 3], dtype=np.int64) | ||
| ds = xr.Dataset({'foo': (('x',), arr)}) | ||
| ds['foo'].attrs['myattr'] = 'testattr' | ||
| out_file = tmp_path / 'test_int64.nc' | ||
| write_netcdf(ds, str(out_file)) | ||
| ds2 = xr.open_dataset(out_file) | ||
| # Should be int32, not int64 | ||
| assert ds2['foo'].dtype == np.int32 | ||
| # Attribute should be preserved | ||
| assert ds2['foo'].attrs['myattr'] == 'testattr' | ||
| ds2.close() | ||
|
|
||
|
|
||
| def test_write_netcdf_fill_value(tmp_path): | ||
| # Test that NaN values are written with correct fill value | ||
| arr = np.array([1.0, np.nan, 3.0], dtype=np.float32) | ||
| ds = xr.Dataset({'bar': (('x',), arr)}) | ||
| out_file = tmp_path / 'test_fill.nc' | ||
| write_netcdf(ds, str(out_file)) | ||
| ds2 = xr.open_dataset(out_file) | ||
| # The second value should be the default fill value for float32 | ||
| fill_value = ds2['bar'].encoding.get('_FillValue', None) | ||
| assert fill_value is not None | ||
| assert np.isnan(ds2['bar'].values[1]) | ||
| ds2.close() | ||
|
|
||
|
|
||
| def test_write_netcdf_string_dim_name(tmp_path): | ||
| # Test that custom char_dim_name is used in encoding | ||
| arr = np.array([b'abc', b'def']) | ||
| ds = xr.Dataset({'baz': (('x',), arr)}) | ||
| out_file = tmp_path / 'test_strdim.nc' | ||
| write_netcdf(ds, str(out_file), char_dim_name='CustomStrLen') | ||
| ds2 = xr.open_dataset(out_file) | ||
| # Should have the variable and correct shape | ||
| assert 'baz' in ds2.variables | ||
| ds2.close() | ||
| arr = np.array([1, 2, 3], dtype=np.int64) | ||
| ds = xr.Dataset({'foo': (('x',), arr)}) | ||
| ds['foo'].attrs['myattr'] = 'testattr' | ||
| out_file = tmp_path / 'test_int64.nc' | ||
| write_netcdf(ds, str(out_file)) | ||
| ds2 = xr.open_dataset(out_file) | ||
| # Should be int32, not int64 | ||
| assert ds2['foo'].dtype == np.int32 | ||
| # Attribute should be preserved | ||
| assert ds2['foo'].attrs['myattr'] == 'testattr' | ||
| ds2.close() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.