Skip to content

Commit

Permalink
fix(gridgen): support arbitrary path-like for shapefiles
Browse files Browse the repository at this point in the history
  • Loading branch information
wpbonelli committed Dec 1, 2023
1 parent b3510e9 commit 8057d5b
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 72 deletions.
94 changes: 60 additions & 34 deletions autotest/test_gridgen.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
from pathlib import Path
from shutil import copyfile, which
from shutil import which

import matplotlib.pyplot as plt
import numpy as np
Expand All @@ -16,7 +16,7 @@


@requires_exe("gridgen")
def test_ctor_accepts_path_or_string(function_tmpdir):
def test_ctor_accepts_path_or_string_model_ws(function_tmpdir):
grid = GridCases().structured_small()

g = Gridgen(grid, model_ws=function_tmpdir)
Expand All @@ -27,11 +27,14 @@ def test_ctor_accepts_path_or_string(function_tmpdir):


def base_grid():
"""Get a small version of the first grid in:
.docs/Notebooks/gridgen_example.py"""

Lx = 100.0
Ly = 100.0
nlay = 2
nrow = 51
ncol = 51
nrow = 11
ncol = 11
delr = Lx / ncol
delc = Ly / nrow
h0 = 10
Expand All @@ -54,37 +57,60 @@ def base_grid():


@requires_exe("gridgen")
def test_add_refinement_feature_accepts_filename(function_tmpdir):
# same as 1st grid in .docs/Notebooks/gridgen_example.py
grid = base_grid()

# create initial refined grid (writing refinement
# feature shapefile to the workspace)
g1 = Gridgen(grid, model_ws=function_tmpdir)
g1.add_refinement_features(
def test_add_active_domain(function_tmpdir):
bgrid = base_grid()
grids = []

# test providing active domain various ways
for feature in [
[[[(0, 0), (0, 60), (40, 80), (60, 0), (0, 0)]]],
"polygon",
1,
range(grid.nlay),
)
g1.build()
g1 = VertexGrid(**g1.get_gridprops_vertexgrid())
# g1.plot()
# g1.show()

# recreate gridgen object, using shapefile from the
# first run
g2 = Gridgen(grid, model_ws=function_tmpdir)
g2.add_refinement_features("rf0.shp", "polygon", 1, range(grid.nlay))
g2.build()
g2 = VertexGrid(**g2.get_gridprops_vertexgrid())
# g2.plot()
# plt.show()

assert g1.nnodes > grid.nnodes
assert g1.ncpl != grid.ncpl
assert g1.ncpl == g2.ncpl
assert g1.nnodes == g2.nnodes
function_tmpdir / "ad0.shp",
"ad0.shp",
]:
gridgen = Gridgen(bgrid, model_ws=function_tmpdir)
gridgen.add_active_domain(
feature,
range(bgrid.nlay),
)
gridgen.build()
grid = VertexGrid(**gridgen.get_gridprops_vertexgrid())
grid.plot()
grids.append(grid)
# plt.show()

assert grid.nnodes < bgrid.nnodes
assert grid.ncpl != bgrid.ncpl
assert all(grid.ncpl == g.ncpl for g in grids)
assert all(grid.nnodes == g.nnodes for g in grids)


@requires_exe("gridgen")
def test_add_refinement_feature(function_tmpdir):
bgrid = base_grid()
grids = []

# test providing refinement feature various ways
for features in [
[[[(0, 0), (0, 60), (40, 80), (60, 0), (0, 0)]]],
function_tmpdir / "rf0.shp",
"rf0.shp",
]:
gridgen = Gridgen(bgrid, model_ws=function_tmpdir)
gridgen.add_refinement_features(
features,
"polygon",
1,
range(bgrid.nlay),
)
gridgen.build()
grid = VertexGrid(**gridgen.get_gridprops_vertexgrid())
grid.plot()
# plt.show()

assert grid.nnodes > bgrid.nnodes
assert grid.ncpl != bgrid.ncpl
assert all(grid.ncpl == g.ncpl for g in grids)
assert all(grid.nnodes == g.nnodes for g in grids)


@pytest.mark.slow
Expand Down
95 changes: 57 additions & 38 deletions flopy/utils/gridgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import numpy as np

from flopy.utils.flopy_io import relpath_safe

from ..discretization import StructuredGrid
from ..export.shapefile_utils import shp2recarray
from ..mbase import resolve_exe
Expand Down Expand Up @@ -57,7 +59,7 @@ def features_to_shapefile(
featuretype : str
Must be 'point', 'line', 'linestring', or 'polygon'
filename : str or PathLike
Path of the shapefile to write
The shapefile to write (extension is optional)
Returns
-------
Expand Down Expand Up @@ -363,15 +365,15 @@ def add_active_domain(self, feature, layers):
"""
Parameters
----------
feature : str or list
feature : str, path-like or array-like
feature can be:
a string containing the name of a polygon
a list of polygons
flopy.utils.geometry.Collection object of Polygons
shapely.geometry.Collection object of Polygons
geojson.GeometryCollection object of Polygons
list of shapefile.Shape objects
shapefile.Shapes object
a shapefile name (str) or Pathlike
a list of polygons
a flopy.utils.geometry.Collection object of Polygons
a shapely.geometry.Collection object of Polygons
a geojson.GeometryCollection object of Polygons
a list of shapefile.Shape objects
a shapefile.Shapes object
layers : list
A list of layers (zero based) for which this active domain
applies.
Expand All @@ -385,37 +387,45 @@ def add_active_domain(self, feature, layers):
self.nodes = 0
self.nja = 0

# Create shapefile or set shapefile to feature
adname = f"ad{len(self._addict)}"
if isinstance(feature, list):
# Create a shapefile
adname_w_path = os.path.join(self.model_ws, adname)
features_to_shapefile(feature, "polygon", adname_w_path)
shapefile = adname
# expand shapefile path or create one from polygon feature
if isinstance(feature, (str, os.PathLike)):
# try expanding absolute path to shapefile
shapefile_path = Path(feature).expanduser().absolute()
if not shapefile_path.is_file():
# look for shapefile in workspace
shapefile_path = self.model_ws / feature
elif isinstance(feature, list):
shapefile_path = self.model_ws / f"ad{len(self._addict)}.shp"
features_to_shapefile(feature, "polygon", shapefile_path)
else:
shapefile = feature
raise ValueError(
f"Feature must be a pathlike (shapefile) or array-like of geometries"
)

self._addict[adname] = shapefile
sn = os.path.join(self.model_ws, f"{shapefile}.shp")
assert os.path.isfile(sn), f"Shapefile does not exist: {sn}"
# make sure shapefile exists
assert (
shapefile_path.is_file()
), f"Shapefile does not exist: {shapefile_path}"

# store shapefile info
self._addict[shapefile_path.stem] = shapefile_path
for k in layers:
self._active_domain[k] = adname
self._active_domain[k] = shapefile_path.stem

def add_refinement_features(self, features, featuretype, level, layers):
"""
Parameters
----------
features : str or array-like
features : str, path-like or array-like
features can be
a str, the name of a shapefile in the workspace
a shapefile name (str) or Pathlike
a list of points, lines, or polygons
flopy.utils.geometry.Collection object
a flopy.utils.geometry.Collection object
a list of flopy.utils.geometry objects
shapely.geometry.Collection object
geojson.GeometryCollection object
a shapely.geometry.Collection object
a geojson.GeometryCollection object
a list of shapefile.Shape objects
shapefile.Shapes object
a shapefile.Shapes object
featuretype : str
Must be either 'point', 'line', or 'polygon'
level : int
Expand All @@ -434,24 +444,33 @@ def add_refinement_features(self, features, featuretype, level, layers):
self.nja = 0

# Create shapefile or set shapefile to feature
rfname = f"rf{len(self._rfdict)}"
if isinstance(features, str):
shapefile = features
if isinstance(features, (str, os.PathLike)):
# try expanding absolute path to shapefile
shapefile_path = Path(features).expanduser().absolute()
if not shapefile_path.is_file():
# look for shapefile in workspace
shapefile_path = self.model_ws / features
elif isinstance(features, (list, tuple, np.ndarray)):
rfname_w_path = os.path.join(self.model_ws, rfname)
features_to_shapefile(features, featuretype, rfname_w_path)
shapefile = f"{rfname}.shp"
shapefile_path = self.model_ws / f"rf{len(self._rfdict)}.shp"
features_to_shapefile(features, featuretype, shapefile_path)
else:
raise ValueError(
f"Features must be str (shapefile name) or array-like (of geometries)"
f"Features must be a pathlike (shapefile) or array-like of geometries"
)

self._rfdict[rfname] = [shapefile, featuretype, level]
sn = os.path.join(self.model_ws, shapefile)
assert os.path.isfile(sn), f"Shapefile does not exist: {sn}"
# make sure shapefile exists
assert (
shapefile_path.is_file()
), f"Shapefile does not exist: {shapefile_path}"

# store shapefile info
self._rfdict[shapefile_path.stem] = [
shapefile_path,
featuretype,
level,
]
for k in layers:
self._refinement_features[k].append(rfname)
self._refinement_features[k].append(shapefile_path.stem)

def build(self, verbose=False):
"""
Expand Down

0 comments on commit 8057d5b

Please sign in to comment.