Skip to content

Commit eeba313

Browse files
committed
fix(model_attributes_to_shapefile): use modelgrid kwarg if provided
1 parent 228668c commit eeba313

4 files changed

Lines changed: 98 additions & 8 deletions

File tree

autotest/test_shapefile_utils.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
"""
44

55
import numpy as np
6+
import pytest
67
from modflow_devtools.markers import requires_pkg
78

89
import flopy
910
from flopy.discretization import StructuredGrid, UnstructuredGrid, VertexGrid
11+
from flopy.discretization.grid import Grid
1012
from flopy.export.shapefile_utils import model_attributes_to_shapefile, shp2recarray
1113
from flopy.utils.crs import get_shapefile_crs
1214

@@ -49,6 +51,75 @@ def test_model_attributes_to_shapefile(example_data_path, function_tmpdir):
4951
assert shpfile_path.exists()
5052

5153

54+
@requires_pkg("geopandas")
55+
def test_model_attributes_to_shapefile_modelgrid_kwarg(function_tmpdir):
56+
"""Repro https://github.com/modflowpy/flopy/issues/2744
57+
58+
The modelgrid kwarg to model_attributes_to_shapefile should be used as
59+
the geometry source if provided, overriding the model's own modelgrid.
60+
"""
61+
import warnings
62+
63+
nrow, ncol = 3, 4
64+
delr = np.ones(ncol) * 10.0
65+
delc = np.ones(nrow) * 10.0
66+
crs = 26916
67+
68+
# Model without a DIS package: modelgrid is a bare Grid with no geometry.
69+
# Without the fix, this reproduces the reported error:
70+
# TypeError: Grid.to_geodataframe() missing 1 required positional argument: 'features'
71+
sim = flopy.mf6.MFSimulation(sim_name="test", sim_ws=str(function_tmpdir))
72+
gwf = flopy.mf6.ModflowGwf(sim, modelname="test")
73+
mg = StructuredGrid(delr=delr, delc=delc, nlay=1, crs=crs)
74+
shpfile = function_tmpdir / "test_no_dis.shp"
75+
with warnings.catch_warnings():
76+
warnings.simplefilter("ignore", DeprecationWarning)
77+
model_attributes_to_shapefile(shpfile, gwf, modelgrid=mg)
78+
assert shpfile.exists()
79+
80+
# The error in the issue could also have been avoided by retrieving the
81+
# modelgrid after attaching the DIS to the model. Before DIS is added,
82+
# gwf.modelgrid is the bare base Grid class. After DIS is attached, it
83+
# becomes a StructuredGrid whose to_geodataframe() needs no 'features'.
84+
sim1 = flopy.mf6.MFSimulation(sim_name="test_b", sim_ws=str(function_tmpdir))
85+
gwf1 = flopy.mf6.ModflowGwf(sim1, modelname="test_b")
86+
assert isinstance(gwf1.modelgrid, Grid)
87+
flopy.mf6.ModflowGwfdis(gwf1, nlay=1, nrow=nrow, ncol=ncol)
88+
mg1 = gwf1.modelgrid
89+
assert isinstance(mg1, StructuredGrid)
90+
shpfile1 = function_tmpdir / "test_dis_first.shp"
91+
with warnings.catch_warnings():
92+
warnings.simplefilter("ignore", DeprecationWarning)
93+
model_attributes_to_shapefile(
94+
shpfile1, gwf1, package_names=["dis"], modelgrid=mg1
95+
)
96+
assert shpfile1.exists()
97+
98+
# Without a modelgrid kwarg and no DIS, to_geodataframe raises a clear
99+
# AttributeError rather than the cryptic TypeError about 'features'.
100+
sim_err = flopy.mf6.MFSimulation(sim_name="test_err", sim_ws=str(function_tmpdir))
101+
gwf_err = flopy.mf6.ModflowGwf(sim_err, modelname="test_err")
102+
with warnings.catch_warnings():
103+
warnings.simplefilter("ignore", DeprecationWarning)
104+
with pytest.raises(AttributeError, match="discretization package"):
105+
model_attributes_to_shapefile(function_tmpdir / "test_err.shp", gwf_err)
106+
107+
# Model with a DIS package but a different modelgrid passed via kwarg:
108+
# the kwarg modelgrid's CRS should take precedence.
109+
sim2 = flopy.mf6.MFSimulation(sim_name="test2", sim_ws=str(function_tmpdir))
110+
gwf2 = flopy.mf6.ModflowGwf(sim2, modelname="test2")
111+
flopy.mf6.ModflowGwfdis(gwf2, nlay=1, nrow=nrow, ncol=ncol)
112+
113+
mg2 = StructuredGrid(delr=delr, delc=delc, nlay=1, crs=crs)
114+
shpfile2 = function_tmpdir / "test_with_dis.shp"
115+
with warnings.catch_warnings():
116+
warnings.simplefilter("ignore", DeprecationWarning)
117+
model_attributes_to_shapefile(
118+
shpfile2, gwf2, package_names=["dis"], modelgrid=mg2
119+
)
120+
assert shpfile2.exists()
121+
122+
52123
@requires_pkg("geopandas")
53124
def test_create_geodataframe(
54125
minimal_unstructured_grid_info, minimal_vertex_grid_info, function_tmpdir

flopy/export/shapefile_utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,15 @@ def model_attributes_to_shapefile(
220220
else:
221221
package_names = [pak.name[0] for pak in ml.packagelist]
222222

223-
gdf = ml.to_geodataframe(package_names=package_names, shorten_attr=True)
223+
modelgrid = kwargs.pop("modelgrid", None)
224+
init_gdf = modelgrid.to_geodataframe() if modelgrid is not None else None
225+
gdf = ml.to_geodataframe(
226+
gdf=init_gdf, package_names=package_names, shorten_attr=True
227+
)
224228

225229
if array_dict:
226-
modelgrid = ml.modelgrid
230+
if modelgrid is None:
231+
modelgrid = ml.modelgrid
227232
for name, array in array_dict.items():
228233
if modelgrid.grid_type() == "unstructured":
229234
gdf[name] = array.ravel()

flopy/mbase.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -790,13 +790,20 @@ def to_geodataframe(self, gdf=None, kper=0, package_names=None, shorten_attr=Fal
790790
gdf : GeoDataFrame
791791
"""
792792
if gdf is None:
793+
from .discretization.grid import Grid
794+
793795
modelgrid = self.modelgrid
794-
if modelgrid is not None:
795-
gdf = modelgrid.to_geodataframe()
796-
else:
796+
if modelgrid is None:
797797
raise AttributeError(
798798
"model does not have a grid instance, please supply a geodataframe"
799799
)
800+
if type(modelgrid) is Grid:
801+
raise AttributeError(
802+
"model does not have a discretization package; cannot build a "
803+
"GeoDataFrame without geometry. Attach a discretization package "
804+
"or supply a gdf."
805+
)
806+
gdf = modelgrid.to_geodataframe()
800807

801808
if package_names is None:
802809
package_names = [pak.name[0] for pak in self.packagelist]

flopy/mf6/mfmodel.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,14 +816,21 @@ def to_geodataframe(self, gdf=None, kper=0, package_names=None, shorten_attr=Fal
816816
gdf : GeoDataFrame
817817
"""
818818
if gdf is None:
819+
from ..discretization.grid import Grid
820+
819821
modelgrid = self.modelgrid
820-
if modelgrid is not None:
821-
gdf = modelgrid.to_geodataframe()
822-
else:
822+
if modelgrid is None:
823823
raise AttributeError(
824824
"model does not have a grid instance, "
825825
"please supply a geodataframe"
826826
)
827+
if type(modelgrid) is Grid:
828+
raise AttributeError(
829+
"model does not have a discretization package; cannot build a "
830+
"GeoDataFrame without geometry. Attach a discretization package "
831+
"or supply a gdf."
832+
)
833+
gdf = modelgrid.to_geodataframe()
827834

828835
if package_names is None:
829836
package_names = [pak.name[0] for pak in self.packagelist]

0 commit comments

Comments
 (0)