Skip to content

Commit d88140b

Browse files
mjrenomjreno
authored andcommitted
netcdf input utils baseline
1 parent 618f8ee commit d88140b

File tree

6 files changed

+767
-3
lines changed

6 files changed

+767
-3
lines changed

autotest/test_netcdf.py

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
from pathlib import Path
2+
3+
import numpy as np
4+
5+
from modflow_devtools.netcdf import (
6+
DNODATA,
7+
FILLNA_FLOAT64,
8+
FILLNA_INT32,
9+
ModelNetCDFConfig,
10+
NetCDFInput,
11+
PkgNetCDFConfig,
12+
)
13+
from modflow_devtools.netcdf_schema import validate
14+
15+
PROJ_ROOT = Path(__file__).parents[1]
16+
DFN_DIR = PROJ_ROOT / "autotest" / "temp" / "dfn"
17+
TOML_DIR = DFN_DIR / "toml"
18+
19+
20+
def test_validate_model():
21+
variables = [
22+
{
23+
"param": "gwf/welg/aux",
24+
"attrs": {"modflow_input": "<GWF_NAME>/<WELG_NAME>/AUX", "modflow_iaux": 1},
25+
"encodings": {"_FillValue": 3e30},
26+
"shape": ["time", "z", "y", "x"],
27+
"varname": "welg_0_aux",
28+
"numeric_type": "f8",
29+
},
30+
{
31+
"param": "gwf/welg/q",
32+
"attrs": {"modflow_input": "<GWF_NAME>/<WELG_NAME>/Q"},
33+
"encodings": {"_FillValue": 3e30},
34+
"shape": ["time", "z", "y", "x"],
35+
"varname": "welg_0_q",
36+
"numeric_type": "f8",
37+
},
38+
]
39+
nc_meta = {
40+
"attrs": {
41+
"modflow_grid": "structured",
42+
"modflow_model": "gwf6: gwfmodel",
43+
},
44+
"variables": variables,
45+
}
46+
47+
validate(nc_meta, TOML_DIR, grid_dims=[1, 1, 1])
48+
49+
50+
def test_validate_model_mesh():
51+
variables = [
52+
{
53+
"param": "gwf/welg/aux",
54+
"attrs": {
55+
"modflow_input": "<GWF_NAME>/<WELG_NAME>/AUX",
56+
"modflow_iaux": 1,
57+
"layer": 1,
58+
},
59+
"encodings": {"_FillValue": 3e30},
60+
"shape": ["time", "z", "y", "x"],
61+
"varname": "welg_0_aux",
62+
"numeric_type": "f8",
63+
},
64+
{
65+
"param": "gwf/welg/q",
66+
"attrs": {"modflow_input": "<GWF_NAME>/<WELG_NAME>/Q", "layer": 1},
67+
"encodings": {"_FillValue": 3e30},
68+
"shape": ["time", "z", "y", "x"],
69+
"varname": "welg_0_q",
70+
"numeric_type": "f8",
71+
},
72+
]
73+
nc_meta = {
74+
"attrs": {
75+
"modflow_grid": "structured",
76+
"modflow_model": "gwf6: gwfmodel",
77+
"mesh": "layered",
78+
},
79+
"variables": variables,
80+
}
81+
82+
validate(nc_meta, TOML_DIR, grid_dims=[1, 1])
83+
84+
85+
def test_xarray_structured_mesh():
86+
nc_cfg = ModelNetCDFConfig(
87+
name="twri",
88+
type="gwf",
89+
grid_type="structured",
90+
dims=[2, 4, 3, 2], # ["time", "z", "y", "x"]
91+
)
92+
93+
nc_cfg.packages.append(PkgNetCDFConfig("npf", "npf", params=["k", "k22"]))
94+
nc_cfg.packages.append(PkgNetCDFConfig("welg_0", "welg", params=["q"]))
95+
96+
nc_input = NetCDFInput(TOML_DIR, nc_cfg)
97+
ds = nc_input.to_xarray()
98+
99+
assert ds.attrs["modflow_grid"] == "structured"
100+
assert ds.attrs["modflow_model"] == "gwf: twri"
101+
assert "mesh" not in ds.attrs
102+
assert "npf_k" in ds
103+
assert "npf_k22" in ds
104+
assert "welg_0_q" in ds
105+
assert np.allclose(ds["npf_k"].values, FILLNA_FLOAT64)
106+
assert np.allclose(ds["npf_k22"].values, FILLNA_FLOAT64)
107+
assert np.allclose(ds["welg_0_q"].values, DNODATA)
108+
assert ds["npf_k"].dims == ("z", "y", "x")
109+
assert ds["npf_k22"].dims == ("z", "y", "x")
110+
assert ds["welg_0_q"].dims == ("time", "z", "y", "x")
111+
assert ds.dims["time"] == 2
112+
assert ds.dims["z"] == 4
113+
assert ds.dims["y"] == 3
114+
assert ds.dims["x"] == 2
115+
assert len(ds) == 3
116+
117+
nc_fpath = Path.cwd() / "twri.input.nc"
118+
ds.to_netcdf(
119+
nc_fpath,
120+
format="NETCDF4",
121+
engine="netcdf4",
122+
)
123+
124+
assert nc_fpath.is_file()
125+
126+
127+
def test_xarray_layered_mesh():
128+
nc_cfg = ModelNetCDFConfig(
129+
name="twri",
130+
type="gwf",
131+
grid_type="structured",
132+
mesh_type="layered",
133+
dims=[2, 4, 6], # ["time", "z", "nmesh_face"]
134+
)
135+
136+
nc_cfg.packages.append(PkgNetCDFConfig("npf", "npf", params=["k", "k22"]))
137+
nc_cfg.packages.append(PkgNetCDFConfig("welg_0", "welg", params=["q"]))
138+
139+
nc_input = NetCDFInput(TOML_DIR, nc_cfg)
140+
ds = nc_input.to_xarray()
141+
142+
assert ds.attrs["modflow_grid"] == "structured"
143+
assert ds.attrs["modflow_model"] == "gwf: twri"
144+
assert ds.attrs["mesh"] == "layered"
145+
for k in range(4):
146+
layer = k + 1
147+
assert f"npf_k_l{layer}" in ds
148+
assert f"npf_k22_l{layer}" in ds
149+
assert f"welg_0_q_l{layer}" in ds
150+
assert np.allclose(ds[f"npf_k_l{layer}"].values, FILLNA_FLOAT64)
151+
assert np.allclose(ds[f"npf_k22_l{layer}"].values, FILLNA_FLOAT64)
152+
assert np.allclose(ds[f"welg_0_q_l{layer}"].values, DNODATA)
153+
assert ds[f"npf_k_l{layer}"].dims == ("z", "nmesh_face")
154+
assert ds[f"npf_k22_l{layer}"].dims == ("z", "nmesh_face")
155+
assert ds[f"welg_0_q_l{layer}"].dims == ("time", "z", "nmesh_face")
156+
assert ds.dims["time"] == 2
157+
assert ds.dims["z"] == 4
158+
assert ds.dims["nmesh_face"] == 6
159+
assert len(ds) == 12
160+
161+
nc_fpath = Path.cwd() / "twri.input.nc"
162+
ds.to_netcdf(
163+
nc_fpath,
164+
format="NETCDF4",
165+
engine="netcdf4",
166+
)
167+
168+
assert nc_fpath.is_file()
169+
170+
171+
def test_xarray_disv():
172+
nc_cfg = ModelNetCDFConfig(
173+
name="twri",
174+
type="gwf",
175+
grid_type="vertex",
176+
mesh_type="layered",
177+
dims=[2, 4, 6],
178+
)
179+
180+
nc_cfg.packages.append(PkgNetCDFConfig("npf", "npf", params=["k", "k22"]))
181+
nc_cfg.packages.append(PkgNetCDFConfig("welg_0", "welg", params=["q"]))
182+
183+
nc_input = NetCDFInput(TOML_DIR, nc_cfg)
184+
ds = nc_input.to_xarray()
185+
186+
assert ds.attrs["modflow_grid"] == "vertex"
187+
assert ds.attrs["modflow_model"] == "gwf: twri"
188+
assert ds.attrs["mesh"] == "layered"
189+
for k in range(4):
190+
layer = k + 1
191+
assert f"npf_k_l{layer}" in ds
192+
assert f"npf_k22_l{layer}" in ds
193+
assert f"welg_0_q_l{layer}" in ds
194+
assert np.allclose(ds[f"npf_k_l{layer}"].values, FILLNA_FLOAT64)
195+
assert np.allclose(ds[f"npf_k22_l{layer}"].values, FILLNA_FLOAT64)
196+
assert np.allclose(ds[f"welg_0_q_l{layer}"].values, DNODATA)
197+
assert ds[f"npf_k_l{layer}"].dims == ("z", "nmesh_face")
198+
assert ds[f"npf_k22_l{layer}"].dims == ("z", "nmesh_face")
199+
assert ds[f"welg_0_q_l{layer}"].dims == ("time", "z", "nmesh_face")
200+
assert ds.dims["time"] == 2
201+
assert ds.dims["z"] == 4
202+
assert ds.dims["nmesh_face"] == 6
203+
assert len(ds) == 12
204+
205+
nc_fpath = Path.cwd() / "disv.input.nc"
206+
ds.to_netcdf(
207+
nc_fpath,
208+
format="NETCDF4",
209+
engine="netcdf4",
210+
)
211+
212+
assert nc_fpath.is_file()
213+
214+
215+
def test_xarray_disv_aux():
216+
nc_cfg = ModelNetCDFConfig(
217+
name="twri",
218+
type="gwf",
219+
grid_type="vertex",
220+
mesh_type="layered",
221+
dims=[2, 4, 6],
222+
)
223+
224+
nc_cfg.packages.append(PkgNetCDFConfig("npf", "npf", params=["k", "k22"]))
225+
nc_cfg.packages.append(
226+
PkgNetCDFConfig(
227+
"welg_0",
228+
"welg",
229+
auxiliary=["concentration", "temperature"],
230+
params=["q", "aux"],
231+
)
232+
)
233+
234+
nc_input = NetCDFInput(TOML_DIR, nc_cfg)
235+
ds = nc_input.to_xarray()
236+
237+
assert ds.attrs["modflow_grid"] == "vertex"
238+
assert ds.attrs["modflow_model"] == "gwf: twri"
239+
assert ds.attrs["mesh"] == "layered"
240+
for k in range(4):
241+
layer = k + 1
242+
assert f"npf_k_l{layer}" in ds
243+
assert f"npf_k22_l{layer}" in ds
244+
assert f"welg_0_q_l{layer}" in ds
245+
assert np.allclose(ds[f"npf_k_l{layer}"].values, FILLNA_FLOAT64)
246+
assert np.allclose(ds[f"npf_k22_l{layer}"].values, FILLNA_FLOAT64)
247+
assert np.allclose(ds[f"welg_0_q_l{layer}"].values, DNODATA)
248+
assert ds[f"npf_k_l{layer}"].dims == ("z", "nmesh_face")
249+
assert ds[f"npf_k22_l{layer}"].dims == ("z", "nmesh_face")
250+
assert ds[f"welg_0_q_l{layer}"].dims == ("time", "z", "nmesh_face")
251+
assert ds.dims["time"] == 2
252+
assert ds.dims["z"] == 4
253+
assert ds.dims["nmesh_face"] == 6
254+
assert len(ds) == 20
255+
256+
nc_fpath = Path.cwd() / "disv_aux.input.nc"
257+
ds.to_netcdf(
258+
nc_fpath,
259+
format="NETCDF4",
260+
engine="netcdf4",
261+
)
262+
263+
assert nc_fpath.is_file()
264+
265+
266+
def test_xarray_disv_all_params():
267+
nc_cfg = ModelNetCDFConfig(
268+
name="twri",
269+
type="gwf",
270+
grid_type="vertex",
271+
mesh_type="layered",
272+
dims=[2, 4, 6],
273+
)
274+
275+
nc_cfg.packages.append(PkgNetCDFConfig("npf", "npf"))
276+
nc_cfg.packages.append(PkgNetCDFConfig("welg_0", "welg"))
277+
278+
nc_input = NetCDFInput(TOML_DIR, nc_cfg)
279+
ds = nc_input.to_xarray()
280+
281+
assert ds.attrs["modflow_grid"] == "vertex"
282+
assert ds.attrs["modflow_model"] == "gwf: twri"
283+
assert ds.attrs["mesh"] == "layered"
284+
for k in range(4):
285+
layer = k + 1
286+
assert f"npf_icelltype_l{layer}" in ds
287+
assert f"npf_k_l{layer}" in ds
288+
assert f"npf_k22_l{layer}" in ds
289+
assert f"npf_k33_l{layer}" in ds
290+
assert f"npf_angle1_l{layer}" in ds
291+
assert f"npf_angle2_l{layer}" in ds
292+
assert f"npf_angle3_l{layer}" in ds
293+
assert f"npf_wetdry_l{layer}" in ds
294+
assert f"welg_0_q_l{layer}" in ds
295+
assert np.allclose(ds[f"npf_icelltype_l{layer}"].values, FILLNA_INT32)
296+
assert np.allclose(ds[f"npf_k_l{layer}"].values, FILLNA_FLOAT64)
297+
assert np.allclose(ds[f"npf_k22_l{layer}"].values, FILLNA_FLOAT64)
298+
assert np.allclose(ds[f"npf_k33_l{layer}"].values, FILLNA_FLOAT64)
299+
assert np.allclose(ds[f"npf_angle1_l{layer}"].values, FILLNA_FLOAT64)
300+
assert np.allclose(ds[f"npf_angle2_l{layer}"].values, FILLNA_FLOAT64)
301+
assert np.allclose(ds[f"npf_angle3_l{layer}"].values, FILLNA_FLOAT64)
302+
assert np.allclose(ds[f"npf_wetdry_l{layer}"].values, FILLNA_FLOAT64)
303+
assert np.allclose(ds[f"welg_0_q_l{layer}"].values, DNODATA)
304+
assert ds[f"npf_icelltype_l{layer}"].dims == ("z", "nmesh_face")
305+
assert ds[f"npf_k_l{layer}"].dims == ("z", "nmesh_face")
306+
assert ds[f"npf_k22_l{layer}"].dims == ("z", "nmesh_face")
307+
assert ds[f"npf_k33_l{layer}"].dims == ("z", "nmesh_face")
308+
assert ds[f"npf_angle1_l{layer}"].dims == ("z", "nmesh_face")
309+
assert ds[f"npf_angle2_l{layer}"].dims == ("z", "nmesh_face")
310+
assert ds[f"npf_angle3_l{layer}"].dims == ("z", "nmesh_face")
311+
assert ds[f"npf_wetdry_l{layer}"].dims == ("z", "nmesh_face")
312+
assert ds[f"welg_0_q_l{layer}"].dims == ("time", "z", "nmesh_face")
313+
assert ds.dims["time"] == 2
314+
assert ds.dims["z"] == 4
315+
assert ds.dims["nmesh_face"] == 6
316+
assert len(ds) == 36
317+
318+
nc_fpath = Path.cwd() / "disv_all.input.nc"
319+
ds.to_netcdf(
320+
nc_fpath,
321+
format="NETCDF4",
322+
engine="netcdf4",
323+
)
324+
325+
assert nc_fpath.is_file()

modflow_devtools/dfn/schema/field.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ class Field:
1818
developmode: bool = False
1919
shape: str | None = None
2020
valid: tuple[str, ...] | None = None
21+
netcdf: bool = False

modflow_devtools/dfn/schema/v1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class FieldV1(Field):
3636
deprecated: bool = False
3737
removed: bool = False
3838
mf6internal: str | None = None
39-
netcdf: str | None = None
39+
netcdf: bool = False
4040
block_variable: bool = False
4141
just_data: bool = False
4242

0 commit comments

Comments
 (0)