feat(netcdf): support writing simulation
NetCDF simulation write support:
netcdfargument supported forwrite_simulation()- write and load support for
READARRAYGRIDpackages including layered aux variables - simulation load not yet supported for
NetCDFinput files - write argument can create structured, layered or no
NetCDFfile (ASCII inputs updated in all cases) - model and package support for interactively creating dataset
Non-interactive example:
# load ascii simulation
sim = flopy.mf6.MFSimulation.load(sim_ws=ascii_ws)
# set simulation path and write simulation
sim.set_sim_path(netcdf_ws)
sim.write_simulation(netcdf="structured")
Internally, the dataset is generated by the model modelgrid object. Package variables and data are added by mf6 package objects. User facing functions are intended, however, to support different approaches to building the dataset.
Create a base dataset:
ds = gwf.modelgrid.dataset(modeltime=gwf.modeltime)
Add package variables with data:
dis = gwf.get_package("dis")
ds = dis.update_dataset(ds)
To retrieve information about package netcdf variables without requesting the package to update the dataset:
nc_meta = dis.netcdf_meta()
{'botm': {'attrs': {'_FillValue': 9.96920996838687e+36,
'longname': 'cell bottom elevation',
'modflow_input': 'UZF01/DIS/BOTM'},
'netcdf_shape': ['z', 'y', 'x'],
'varname': 'dis_botm',
'xarray_type': <class 'numpy.float64'>},
'delc': {'attrs': {'_FillValue': 9.96920996838687e+36,
'longname': 'spacing along a column',
'modflow_input': 'UZF01/DIS/DELC'},
'netcdf_shape': ['y'],
'varname': 'dis_delc',
'xarray_type': <class 'numpy.float64'>},
'delr': {'attrs': {'_FillValue': 9.96920996838687e+36,
'longname': 'spacing along a row',
'modflow_input': 'UZF01/DIS/DELR'},
'netcdf_shape': ['x'],
'varname': 'dis_delr',
'xarray_type': <class 'numpy.float64'>},
'idomain': {'attrs': {'_FillValue': np.int32(-2147483647),
'longname': 'idomain existence array',
'modflow_input': 'UZF01/DIS/IDOMAIN'},
'netcdf_shape': ['z', 'y', 'x'],
'varname': 'dis_idomain',
'xarray_type': <class 'numpy.int32'>},
'top': {'attrs': {'_FillValue': 9.96920996838687e+36,
'longname': 'cell top elevation',
'modflow_input': 'UZF01/DIS/TOP'},
'netcdf_shape': ['y', 'x'],
'varname': 'dis_top',
'xarray_type': <class 'numpy.float64'>}}
Or, if a simulation doesn't yet exist:
import flopy
netcdf_meta = flopy.mf6.mfpackage.MFPackage.netcdf_package("GWF", "DIS")
In this case the dictionary contains very similar information to the one above but that is not always true. This approach offers the ability to construct a MODFLOW 6 compliant NetCDF input file with help from modelgrid / modeltime and FloPy static model and package functions.
Layered Mesh (UGRID) versions of these examples also are supported.
Codecov Report
:x: Patch coverage is 13.64162% with 747 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 72.2%. Comparing base (556c088) to head (5ea9e0a).
:warning: Report is 75 commits behind head on develop.
Additional details and impacted files
@@ Coverage Diff @@
## develop #2564 +/- ##
===========================================
+ Coverage 55.5% 72.2% +16.6%
===========================================
Files 644 667 +23
Lines 124135 130721 +6586
===========================================
+ Hits 68947 94422 +25475
+ Misses 55188 36299 -18889
| Files with missing lines | Coverage Δ | |
|---|---|---|
| flopy/mf6/mfbase.py | 74.7% <100.0%> (-12.9%) |
:arrow_down: |
| flopy/mf6/mfsimbase.py | 62.1% <100.0%> (-13.3%) |
:arrow_down: |
| flopy/utils/datautil.py | 65.0% <100.0%> (-4.8%) |
:arrow_down: |
| flopy/discretization/grid.py | 75.7% <87.5%> (-0.2%) |
:arrow_down: |
| flopy/discretization/unstructuredgrid.py | 75.0% <50.0%> (-6.5%) |
:arrow_down: |
| flopy/mf6/utils/codegen/__init__.py | 0.0% <0.0%> (ø) |
|
| flopy/mf6/data/mfstructure.py | 74.1% <81.8%> (+0.3%) |
:arrow_up: |
| flopy/mf6/utils/codegen/filters.py | 0.0% <0.0%> (ø) |
|
| flopy/mf6/data/mffileaccess.py | 70.9% <33.3%> (-5.1%) |
:arrow_down: |
| flopy/mf6/data/mfdatastorage.py | 69.8% <39.3%> (-4.1%) |
:arrow_down: |
| ... and 6 more |
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
I think the pattern here is great — iteratively building a dataset from select packages.
A similar way to achieve the same thing with xarray could be a to_dataset() method on the packages. Then you can do xr.merge on any number of them to create a combined dataset. And I think this gets rid of the need for the "template" datasets with the right size and dummy data currently being created for dimension-consuming packages — as long as the dimension-defining package is in the merge, the others can reference its dims.