Skip to content

Commit 2d4db2b

Browse files
mjrenomjreno
authored andcommitted
alignment with mf6
1 parent 38f060c commit 2d4db2b

File tree

7 files changed

+94
-100
lines changed

7 files changed

+94
-100
lines changed

autotest/regression/test_model_netcdf.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,9 @@ def compare_netcdf(base, gen, projection=False, update=None):
3232

3333
# coordinates
3434
for coordname, da in xrb.coords.items():
35-
assert coordname in xrg.coords
36-
assert np.allclose(xrb.coords[coordname].data, xrg.coords[coordname].data)
37-
for a in da.attrs:
38-
if a == "grid_mapping" and not projection:
39-
continue
40-
assert a in xrg.coords[coordname].attrs
41-
assert da.attrs[a] == xrg.coords[coordname].attrs[a]
35+
compare_netcdf_var(
36+
coordname, xrb.coords, xrg.data_vars, xrg.coords, projection, update
37+
)
4238

4339
# variables
4440
for varname, da in xrb.data_vars.items():
@@ -147,7 +143,7 @@ def test_load_gwfsto01(function_tmpdir, example_data_path):
147143

148144
# load example
149145
sim = flopy.mf6.MFSimulation.load(sim_ws=base_path)
150-
gwf = sim.get_model("gwf_sto01")
146+
# gwf = sim.get_model("gwf_sto01")
151147

152148
# set simulation path and write simulation
153149
sim.set_sim_path(test_path)
@@ -200,8 +196,7 @@ def test_update_gwfsto01(function_tmpdir, example_data_path):
200196

201197
nlay, nrow, ncol = 3, 10, 10
202198

203-
dis_delr = np.array([1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010])
204-
dis_delc = [2000, 2010, 2020, 2030, 2040, 2050, 2060, 2070, 2080, 2090]
199+
dis_top = np.full((nrow, ncol), 50)
205200

206201
# ic
207202
strt1 = np.full((nrow, ncol), 0.15)
@@ -210,8 +205,7 @@ def test_update_gwfsto01(function_tmpdir, example_data_path):
210205
ic_strt = np.array([strt1, strt2, strt3])
211206

212207
update = {
213-
"dis_delr": dis_delr,
214-
"dis_delc": dis_delc,
208+
"dis_top": dis_top.flatten()[0],
215209
"ic_strt": ic_strt,
216210
"ic_strt_l1": ic_strt[0].flatten(),
217211
"ic_strt_l2": ic_strt[1].flatten(),
@@ -233,9 +227,8 @@ def test_update_gwfsto01(function_tmpdir, example_data_path):
233227
# get model instance
234228
gwf = sim.get_model("gwf_sto01")
235229

236-
# update dis delr and delc
237-
gwf.dis.delr = dis_delr
238-
gwf.dis.delc = dis_delc
230+
# update dis top
231+
gwf.dis.top = dis_top
239232

240233
# update ic strt
241234
gwf.ic.strt.set_data(ic_strt)
@@ -1090,7 +1083,9 @@ def test_dis_transform(function_tmpdir, example_data_path):
10901083
ws = transform_ws / t
10911084
sim.set_sim_path(ws)
10921085
sim.write_simulation(netcdf=t)
1093-
compare_netcdf_data(cmp_pth / f"transform.{t}.nc", ws / "transform.in.nc")
1086+
compare_netcdf(
1087+
cmp_pth / f"transform.{t}.nc", ws / "transform.in.nc", projection=True
1088+
)
10941089

10951090

10961091
@requires_exe("triangle")
@@ -1184,8 +1179,7 @@ def test_disv_transform(function_tmpdir, example_data_path):
11841179

11851180
sim.write_simulation(netcdf=nc_type)
11861181

1187-
compare_netcdf_data(cmp_pth / f"tri.{nc_type}.nc", mf6_ws / "tri.in.nc")
1188-
# compare_netcdf_data(cmp_pth / f"tri.{nc_type}.nc", vertex_ws / "tri.nc")
1182+
compare_netcdf(cmp_pth / f"tri.{nc_type}.nc", mf6_ws / "tri.in.nc", projection=True)
11891183

11901184

11911185
@pytest.mark.regression
1 Byte
Binary file not shown.
-1 Bytes
Binary file not shown.

flopy/mf6/data/mfdatastorage.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,7 +1840,6 @@ def store_external(
18401840
)
18411841

18421842
if self.netcdf:
1843-
# TODO: multiplier NETCDF-DEV
18441843
file_access.set_netcdf_array(
18451844
self.layer_storage[layer].nc_dataset,
18461845
self.data_dimensions.structure.get_package(),
@@ -3063,7 +3062,10 @@ def _layer_prep(self, layer):
30633062
if layer is None:
30643063
# layer is none means the data provided is for all layers or this
30653064
# is not layered data
3066-
layer = (0,)
3065+
if self.netcdf:
3066+
layer = (-1,)
3067+
else:
3068+
layer = (0,)
30673069
self.layer_storage.list_shape = (1,)
30683070
self.layer_storage.multi_dim_list = [
30693071
self.layer_storage.first_item()

flopy/mf6/data/mfstructure.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ def __init__(self):
594594
self.parameter_name = None
595595
self.one_per_pkg = False
596596
self.jagged_array = None
597+
self.netcdf = False
597598

598599
def set_value(self, line, common):
599600
arr_line = line.strip().split()
@@ -771,6 +772,8 @@ def set_value(self, line, common):
771772
self.one_per_pkg = bool(arr_line[1])
772773
elif arr_line[0] == "jagged_array":
773774
self.jagged_array = arr_line[1]
775+
elif arr_line[0] == "netcdf":
776+
self.netcdf = arr_line[1]
774777

775778
def get_type_string(self):
776779
return f"[{self.type_string}]"
@@ -1089,14 +1092,7 @@ def __init__(self, data_item, model_data, package_type, dfn_list):
10891092
or "nodes" in data_item.shape
10901093
or len(data_item.layer_dims) > 1
10911094
)
1092-
self.netcdf = (
1093-
("ncol" in data_item.shape
1094-
or "nrow" in data_item.shape
1095-
or "ncpl" in data_item.shape
1096-
or "nlay" in data_item.shape
1097-
or "nodes" in data_item.shape)
1098-
and data_item.block_name == 'griddata'
1099-
)
1095+
self.netcdf = data_item.netcdf
11001096
self.num_data_items = len(data_item.data_items)
11011097
self.record_within_record = False
11021098
self.file_data = False

flopy/mf6/mfmodel.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,11 @@ def write(
13481348
"""
13491349

13501350
# write netcdf file
1351-
if netcdf or self._nc_dataset is not None:
1351+
if (netcdf or self._nc_dataset is not None) and (
1352+
self.model_type == "gwf6"
1353+
or self.model_type == "gwt6"
1354+
or self.model_type == "gwe6"
1355+
):
13521356
kwargs = {}
13531357
if self._nc_dataset is None:
13541358
from ..utils.model_netcdf import create_dataset
@@ -1361,7 +1365,7 @@ def write(
13611365

13621366
# create netcdf dataset
13631367
self._nc_dataset = create_dataset(
1364-
self.model_type, self.name, netcdf, nc_fname, self._modelgrid
1368+
self.model_type, self.name, netcdf, nc_fname, self.modelgrid
13651369
)
13661370

13671371
# reset data storage and populate netcdf file
@@ -1375,6 +1379,7 @@ def write(
13751379
kwargs["chunk_y"] = pp.chunk_y.get_data()
13761380
kwargs["chunk_z"] = pp.chunk_z.get_data()
13771381
kwargs["wkt"] = pp.wkt.get_data()
1382+
13781383
pp._set_netcdf_storage(
13791384
self._nc_dataset, create=True
13801385
)

flopy/utils/model_netcdf.py

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
FILLNA_INT32 = np.int32(-2147483647)
2121
FILLNA_DBL = 9.96920996838687e36
22+
lenunits = {0: "m", 1: "ft", 2: "m", 3: "m"}
2223

2324

2425
class ModelNetCDFDataset:
@@ -45,7 +46,6 @@ class ModelNetCDFDataset:
4546
"""
4647

4748
def __init__(self):
48-
# TODO: grid offsets
4949
self._modelname = None
5050
self._modeltype = None
5151
self._dataset = None
@@ -157,6 +157,9 @@ def set_array(
157157
):
158158
"""
159159
Set data in an existing array. Override this function in a derived class.
160+
Do not use to update variables that establish the vertices and cell
161+
structure of the grid, e.g. delr/delc or vertex/cell parameters, only
162+
model data associated with the grid.
160163
161164
Args:
162165
package (str): A package name provided as an argument to create_array().
@@ -286,64 +289,59 @@ def _set_encoding(self, **kwargs):
286289
return encoding
287290

288291
def _set_projection(self):
289-
if self._dis:
290-
crs = None
291-
wkt = None
292-
if self._dis.crs:
293-
try:
294-
crs = CRS.from_user_input(self._dis.crs)
295-
wkt = crs.to_wkt()
296-
except Exception as e:
297-
# crs = None
298-
# wkt = None
299-
warnings.warn(
300-
f"Cannot generate CRS object from user input: {e}",
301-
UserWarning,
302-
)
303-
304-
if wkt is not None:
305-
# add projection variable
306-
self._dataset = self._dataset.assign(
307-
{"projection": ([], np.int32(1))}
308-
)
309-
if self._nc_type == "structured":
310-
self._dataset["projection"].attrs["crs_wkt"] = wkt
311-
self._dataset["x"].attrs["grid_mapping"] = "projection"
312-
self._dataset["y"].attrs["grid_mapping"] = "projection"
313-
elif self._nc_type == "mesh2d":
314-
self._dataset["projection"].attrs["wkt"] = wkt
315-
self._dataset["mesh_node_x"].attrs["grid_mapping"] = (
316-
"projection"
317-
)
318-
self._dataset["mesh_node_y"].attrs["grid_mapping"] = (
319-
"projection"
320-
)
321-
self._dataset["mesh_face_x"].attrs["grid_mapping"] = (
322-
"projection"
323-
)
324-
self._dataset["mesh_face_y"].attrs["grid_mapping"] = (
292+
if not self._dis:
293+
return
294+
295+
crs = None
296+
wkt = None
297+
projection = False
298+
if self._dis.crs:
299+
try:
300+
crs = CRS.from_user_input(self._dis.crs)
301+
wkt = crs.to_wkt()
302+
projection = True
303+
except Exception as e:
304+
warnings.warn(
305+
f"Cannot generate CRS object from user input: {e}",
306+
UserWarning,
307+
)
308+
309+
# update coords based on crs
310+
coords = self._set_coords(crs)
311+
312+
# Don't define projection and grid mapping if using
313+
# geographic coordinates in the structured type
314+
if self._nc_type == "structured" and coords == "lon lat":
315+
projection = False
316+
317+
if projection:
318+
# add projection variable
319+
self._dataset = self._dataset.assign({"projection": ([], np.int32(1))})
320+
if self._nc_type == "structured":
321+
self._dataset["projection"].attrs["crs_wkt"] = wkt
322+
self._dataset["x"].attrs["grid_mapping"] = "projection"
323+
self._dataset["y"].attrs["grid_mapping"] = "projection"
324+
elif self._nc_type == "mesh2d":
325+
self._dataset["projection"].attrs["wkt"] = wkt
326+
self._dataset["mesh_node_x"].attrs["grid_mapping"] = "projection"
327+
self._dataset["mesh_node_y"].attrs["grid_mapping"] = "projection"
328+
self._dataset["mesh_face_x"].attrs["grid_mapping"] = "projection"
329+
self._dataset["mesh_face_y"].attrs["grid_mapping"] = "projection"
330+
331+
# set grid_mapping and coordinates attributes
332+
for p in self._tags:
333+
for l in self._tags[p]:
334+
dims = self._dataset[self._tags[p][l]].dims
335+
if (self._nc_type == "structured" and len(dims) > 1) or (
336+
self._nc_type == "mesh2d"
337+
and ("nmesh_face" in dims or "nmesh_node" in dims)
338+
):
339+
if projection:
340+
self._dataset[self._tags[p][l]].attrs["grid_mapping"] = (
325341
"projection"
326342
)
327-
328-
# update coords based on crs
329-
coords = self._set_coords(crs)
330-
331-
# set grid_mapping and coordinates attributes
332-
for p in self._tags:
333-
for l in self._tags[p]:
334-
dims = self._dataset[self._tags[p][l]].dims
335-
if (self._nc_type == "structured" and len(dims) > 1) or (
336-
self._nc_type == "mesh2d"
337-
and ("nmesh_face" in dims or "nmesh_node" in dims)
338-
):
339-
if crs is not None:
340-
self._dataset[self._tags[p][l]].attrs["grid_mapping"] = (
341-
"projection"
342-
)
343-
if coords is not None:
344-
self._dataset[self._tags[p][l]].attrs["coordinates"] = (
345-
coords
346-
)
343+
if coords is not None:
344+
self._dataset[self._tags[p][l]].attrs["coordinates"] = coords
347345

348346
def _set_modflow_attrs(self):
349347
if self._modeltype.endswith("6"):
@@ -545,10 +543,16 @@ def array(self, package: str, param: str, layer=None):
545543
return self._dataset[self._tags[path][-1]].data
546544

547545
def _set_grid(self, dis):
548-
lenunits = {0: "m", 1: "feet", 2: "m", 3: "cm"}
546+
if dis.angrot != 0.0:
547+
xoff = 0.0
548+
yoff = 0.0
549+
else:
550+
xoff = dis.xoffset
551+
yoff = dis.yoffset
549552

553+
# set coordinate var bounds
550554
x_bnds = []
551-
xv = dis.xoffset + dis.xyedges[0]
555+
xv = xoff + dis.xyedges[0]
552556
for idx, val in enumerate(xv):
553557
if idx + 1 < len(xv):
554558
bnd = []
@@ -557,18 +561,17 @@ def _set_grid(self, dis):
557561
x_bnds.append(bnd)
558562

559563
y_bnds = []
560-
yv = dis.yoffset + dis.xyedges[1]
564+
yv = yoff + dis.xyedges[1]
561565
for idx, val in enumerate(yv):
562566
if idx + 1 < len(yv):
563567
bnd = []
564568
bnd.append(yv[idx + 1])
565569
bnd.append(yv[idx])
566570
y_bnds.append(bnd)
567571

568-
# x = dis.xcellcenters[0]
569-
# y = dis.ycellcenters[:, 0]
570-
x = dis.xoffset + dis.xycenters[0]
571-
y = dis.yoffset + dis.xycenters[1]
572+
# set coordinate vars
573+
x = xoff + dis.xycenters[0]
574+
y = yoff + dis.xycenters[1]
572575
z = [float(x) for x in range(1, dis.nlay + 1)]
573576

574577
# create coordinate vars
@@ -715,8 +718,6 @@ def array(self, package: str, param: str, layer=None):
715718
return None
716719

717720
def _set_grid(self, dis):
718-
lenunits = {0: "m", 1: "feet", 2: "m", 3: "cm"}
719-
720721
# mesh container variable
721722
self._dataset = self._dataset.assign({"mesh": ([], np.int32(1))})
722723
self._dataset["mesh"].attrs["cf_role"] = "mesh_topology"
@@ -770,7 +771,6 @@ def _set_grid(self, dis):
770771
y_bnds.append(bnd)
771772

772773
var_d = {
773-
# TODO modflow6 and flopy results differ for mesh_face_x gwf_sto01
774774
"mesh_face_x": (["nmesh_face"], dis.xcellcenters.flatten()),
775775
"mesh_face_xbnds": (["nmesh_face", "max_nmesh_face_nodes"], x_bnds),
776776
"mesh_face_y": (["nmesh_face"], dis.ycellcenters.flatten()),
@@ -861,9 +861,6 @@ def array(self, package: str, param: str, layer=None):
861861
return None
862862

863863
def _set_grid(self, disv):
864-
# default metric "m" when undefined
865-
lenunits = {0: "m", 1: "feet", 2: "m", 3: "cm"}
866-
867864
# mesh container variable
868865
self._dataset = self._dataset.assign({"mesh": ([], np.int32(1))})
869866
self._dataset["mesh"].attrs["cf_role"] = "mesh_topology"

0 commit comments

Comments
 (0)