|
| 1 | +#!/usr/bin/env python |
| 2 | +# coding: utf-8 |
| 3 | + |
| 4 | +# # Creating HFB input data from shapefile linework |
| 5 | +# |
| 6 | +# This notebook shows examples of how to create a horizontal flow barrier package (HFB) from shapefile information in FloPy. FloPy has support for creating HFB inputs from LineString like vector data for all discretization types (`DIS`, `DISV`, `DISU`). |
| 7 | +# |
| 8 | +# This notebook starts off by generating two simple models: |
| 9 | +# - a structured (DIS) model |
| 10 | +# - a vertex (DISV) model |
| 11 | +# |
| 12 | +# And then shows how to generate HFB input for each of these models. |
| 13 | + |
| 14 | +# In[1]: |
| 15 | + |
| 16 | + |
| 17 | +from tempfile import TemporaryDirectory |
| 18 | + |
| 19 | +import geopandas as gpd |
| 20 | +import matplotlib.pyplot as plt |
| 21 | +import numpy as np |
| 22 | +from shapely.geometry import LineString |
| 23 | + |
| 24 | +import flopy |
| 25 | +from flopy.utils import make_hfb_array |
| 26 | + |
| 27 | +temp_dir = TemporaryDirectory() |
| 28 | +workspace = temp_dir.name |
| 29 | + |
| 30 | + |
| 31 | +# ### Generate a Structured (DIS) model |
| 32 | + |
| 33 | +# In[2]: |
| 34 | + |
| 35 | + |
| 36 | +def simple_structured_model(): |
| 37 | + """ |
| 38 | + Method to generate a 10x10 structured grid model |
| 39 | + """ |
| 40 | + lx = 100 |
| 41 | + ly = 100 |
| 42 | + nlay = 1 |
| 43 | + nrow = 10 |
| 44 | + ncol = 10 |
| 45 | + delc = np.full((nrow,), ly / nrow) |
| 46 | + delr = np.full((ncol,), ly / ncol) |
| 47 | + top = np.full((nrow, ncol), 10) |
| 48 | + botm = np.zeros((nlay, nrow, ncol)) |
| 49 | + idomain = np.ones(botm.shape, dtype=int) |
| 50 | + |
| 51 | + sim = flopy.mf6.MFSimulation(sim_ws="tmp_struct") |
| 52 | + ims = flopy.mf6.ModflowIms(sim, complexity="SIMPLE") |
| 53 | + tdis = flopy.mf6.ModflowTdis(sim) |
| 54 | + |
| 55 | + gwf = flopy.mf6.ModflowGwf(sim, modelname="hfb_model") |
| 56 | + dis = flopy.mf6.ModflowGwfdis( |
| 57 | + gwf, |
| 58 | + nlay=nlay, |
| 59 | + nrow=nrow, |
| 60 | + ncol=ncol, |
| 61 | + delc=delc, |
| 62 | + delr=delr, |
| 63 | + top=top, |
| 64 | + botm=botm, |
| 65 | + idomain=idomain, |
| 66 | + ) |
| 67 | + npf = flopy.mf6.ModflowGwfnpf(gwf) |
| 68 | + sto = flopy.mf6.ModflowGwfsto(gwf) |
| 69 | + ic = flopy.mf6.ModflowGwfic(gwf, strt=np.full((nlay, nrow, ncol), 9)) |
| 70 | + |
| 71 | + return sim |
| 72 | + |
| 73 | + |
| 74 | +# ### Generate a voronoi (DISV) model |
| 75 | + |
| 76 | +# In[3]: |
| 77 | + |
| 78 | + |
| 79 | +def simple_vertex_model(workspace): |
| 80 | + """ |
| 81 | + Method to generate a voroni vertex grid model |
| 82 | + """ |
| 83 | + from flopy.utils.triangle import Triangle |
| 84 | + from flopy.utils.voronoi import VoronoiGrid |
| 85 | + |
| 86 | + geom = [(0, 0), (100, 0), (100, 100), (0, 100), (0, 0)] |
| 87 | + tri = Triangle(angle=30, model_ws=workspace) |
| 88 | + tri.add_polygon(geom) |
| 89 | + tri.add_region((5, 5), 0, maximum_area=40) |
| 90 | + tri.build() |
| 91 | + |
| 92 | + vor = VoronoiGrid(tri) |
| 93 | + pkg_props = vor.get_disv_gridprops() |
| 94 | + ncpl = pkg_props["ncpl"] |
| 95 | + nlay = 1 |
| 96 | + top = np.full((ncpl,), 10) |
| 97 | + botm = np.zeros((nlay, ncpl)) |
| 98 | + idomain = np.ones(botm.shape, dtype=int) |
| 99 | + |
| 100 | + sim = flopy.mf6.MFSimulation(sim_ws="tmp_struct") |
| 101 | + ims = flopy.mf6.ModflowIms(sim, complexity="SIMPLE") |
| 102 | + tdis = flopy.mf6.ModflowTdis(sim) |
| 103 | + |
| 104 | + gwf = flopy.mf6.ModflowGwf(sim, modelname="hfb_model") |
| 105 | + disv = flopy.mf6.ModflowGwfdisv( |
| 106 | + gwf, nlay=1, top=top, botm=botm, idomain=idomain, **pkg_props |
| 107 | + ) |
| 108 | + npf = flopy.mf6.ModflowGwfnpf(gwf) |
| 109 | + sto = flopy.mf6.ModflowGwfsto(gwf) |
| 110 | + ic = flopy.mf6.ModflowGwfic(gwf, strt=np.full((nlay, ncpl), 9)) |
| 111 | + |
| 112 | + return sim |
| 113 | + |
| 114 | + |
| 115 | +# ### Create HFB inputs for a Structured (DIS) model |
| 116 | + |
| 117 | +# The `make_hfb_array` utility is used to intersect `LineString` like data with `modelgrid` instances to produce HFB data. The `make_hfb_array` utility accepts two parameters: |
| 118 | +# - `modelgrid` : a flopy Grid instance |
| 119 | +# - `geom` : a geospatial LineString like object that can be: an iterable of points, GeoJSON, Shapely.geometry.LineString, or a shapefile.Shape object. |
| 120 | + |
| 121 | +# First define a fault line and visualize it with the model |
| 122 | + |
| 123 | +# In[4]: |
| 124 | + |
| 125 | + |
| 126 | +# define a fault line |
| 127 | +fault = [(0, 4), (50, 55), (100, 55)] |
| 128 | + |
| 129 | +# build simulation |
| 130 | +sim = simple_structured_model() |
| 131 | +gwf = sim.get_model() |
| 132 | +modelgrid = gwf.modelgrid |
| 133 | + |
| 134 | +fig, ax = plt.subplots(figsize=(5, 5)) |
| 135 | +pmv = flopy.plot.PlotMapView(modelgrid=modelgrid, ax=ax) |
| 136 | +pmv.plot_grid() |
| 137 | +plt.plot(np.array(fault).T[0], np.array(fault).T[1], "r--") |
| 138 | +# now create hfb data with the `make_hfb_array` utility |
| 139 | + |
| 140 | +# In[5]: |
| 141 | + |
| 142 | + |
| 143 | +hfbs = flopy.utils.make_hfb_array(modelgrid, fault) |
| 144 | +hfbs[0:5] |
| 145 | + |
| 146 | + |
| 147 | +# notice that the `hydchr` parameter is not filled out; this is left up to the user |
| 148 | + |
| 149 | +# In[6]: |
| 150 | + |
| 151 | + |
| 152 | +hfbs["hydchr"] = 1e-05 |
| 153 | + |
| 154 | + |
| 155 | +# Now we can build an HFB package and compare it to the fault line that was defined above |
| 156 | + |
| 157 | +# In[7]: |
| 158 | + |
| 159 | + |
| 160 | +hfb = flopy.mf6.ModflowGwfhfb(gwf, stress_period_data={0: hfbs}) |
| 161 | + |
| 162 | + |
| 163 | +# In[8]: |
| 164 | + |
| 165 | + |
| 166 | +# plot up the results |
| 167 | +fig, ax = plt.subplots(figsize=(5, 5)) |
| 168 | +pmv = flopy.plot.PlotMapView(modelgrid=modelgrid) |
| 169 | +pmv.plot_grid() |
| 170 | +pmv.plot_bc(package=hfb, color="b", lw=2) |
| 171 | +plt.plot(np.array(fault).T[0], np.array(fault).T[1], "r--") |
| 172 | +# ### Create HFB inputs for a Vertex (DISV) model |
| 173 | + |
| 174 | +# The `make_hfb_array` utility is used to intersect `LineString` like data with `modelgrid` instances to produce HFB data. The `make_hfb_array` utility accepts two parameters: |
| 175 | +# - `modelgrid` : a flopy Grid instance |
| 176 | +# - `geom` : a geospatial LineString like object that can be: an iterable of points, GeoJSON, Shapely.geometry.LineString, or a shapefile.Shape object. |
| 177 | + |
| 178 | +# First define a fault line and visualize it with the model |
| 179 | + |
| 180 | +# In[9]: |
| 181 | + |
| 182 | + |
| 183 | +# define a fault line |
| 184 | +fault = [(0, 4), (50, 55), (100, 55)] |
| 185 | + |
| 186 | +# build simulation |
| 187 | +sim = simple_vertex_model(workspace) |
| 188 | +gwf = sim.get_model() |
| 189 | +modelgrid = gwf.modelgrid |
| 190 | + |
| 191 | +fig, ax = plt.subplots(figsize=(5, 5)) |
| 192 | +pmv = flopy.plot.PlotMapView(modelgrid=modelgrid, ax=ax) |
| 193 | +pmv.plot_grid() |
| 194 | +plt.plot(np.array(fault).T[0], np.array(fault).T[1], "r--") |
| 195 | +# now create hfb data with the `make_hfb_array` utility |
| 196 | + |
| 197 | +# In[10]: |
| 198 | + |
| 199 | + |
| 200 | +hfbs = flopy.utils.make_hfb_array(modelgrid, fault) |
| 201 | +hfbs[0:5] |
| 202 | + |
| 203 | + |
| 204 | +# notice that the `hydchr` parameter is not filled out; this is left up to the user |
| 205 | + |
| 206 | +# In[11]: |
| 207 | + |
| 208 | + |
| 209 | +hfbs["hydchr"] = 3e-06 |
| 210 | + |
| 211 | + |
| 212 | +# Now we can build an HFB package and compare it to the fault line that was defined above |
| 213 | + |
| 214 | +# In[12]: |
| 215 | + |
| 216 | + |
| 217 | +hfb = flopy.mf6.ModflowGwfhfb(gwf, stress_period_data={0: hfbs}) |
| 218 | + |
| 219 | + |
| 220 | +# In[13]: |
| 221 | + |
| 222 | + |
| 223 | +# plot up the results |
| 224 | +fig, ax = plt.subplots(figsize=(5, 5)) |
| 225 | +pmv = flopy.plot.PlotMapView(modelgrid=modelgrid) |
| 226 | +pmv.plot_grid() |
| 227 | +pmv.plot_bc(package=hfb, color="b", lw=2) |
| 228 | +plt.plot(np.array(fault).T[0], np.array(fault).T[1], "r--") |
| 229 | +# ### Working with multiple fault geometries |
| 230 | +# |
| 231 | +# The `make_hfb_array` only accepts a single LineString geometry at a time. This was done by design for a couple reasons. |
| 232 | +# 1) This gives the user an opportunity to set the `hydchr` parameter to a unique value for each fault line |
| 233 | +# 2) This also gives the user an opportunity to save the boundary condition data (cellid1, cellid2) to an external file for each fault line that can be useful during model calibration with automated software (e.g., PEST++) |
| 234 | +# |
| 235 | +# As a result of these design descisions recarray data will need to be concatenated when working with multiple LineString geometries. |
| 236 | + |
| 237 | +# For this example, a geodataframe of two fault lines is created and then used to build a HFB package |
| 238 | + |
| 239 | +# In[14]: |
| 240 | + |
| 241 | + |
| 242 | +geom1 = LineString([(0, 4), (50, 44)]) |
| 243 | +geom2 = LineString([(50, 44), (88, 100)]) |
| 244 | +d = {"name": ["fault1", "fault2"], "geometry": [geom1, geom2]} |
| 245 | +gdf = gpd.GeoDataFrame(d) |
| 246 | +gdf |
| 247 | + |
| 248 | + |
| 249 | +# Visualize the faults on the modelgrid |
| 250 | + |
| 251 | +# In[15]: |
| 252 | + |
| 253 | + |
| 254 | +# build simulation |
| 255 | +sim = simple_structured_model() |
| 256 | +gwf = sim.get_model() |
| 257 | +modelgrid = gwf.modelgrid |
| 258 | + |
| 259 | +fig, ax = plt.subplots(figsize=(5, 5)) |
| 260 | +pmv = flopy.plot.PlotMapView(modelgrid=modelgrid, ax=ax) |
| 261 | +pmv.plot_grid() |
| 262 | +gdf.geometry.plot(color="r", linestyle="--", ax=ax) |
| 263 | +# now create hfb data with the `make_hfb_array` utility and concatenate it |
| 264 | + |
| 265 | +# In[16]: |
| 266 | + |
| 267 | + |
| 268 | +hydchrs = {"fault1": 1e-05, "fault2": 1e-06} |
| 269 | + |
| 270 | +hfb_ras = [] |
| 271 | +for name, geom in zip(gdf.name, gdf.geometry): |
| 272 | + hfbs = make_hfb_array(modelgrid, geom) |
| 273 | + hfbs["hydchr"] = hydchrs[name] |
| 274 | + hfb_ras.append(hfbs) |
| 275 | + |
| 276 | +hfbs = np.concatenate(hfb_ras) |
| 277 | +hfbs = hfbs.view(np.recarray) |
| 278 | +hfbs |
| 279 | + |
| 280 | + |
| 281 | +# Now create the hfb package from this data and visualize it for comparison with the fault lines |
| 282 | + |
| 283 | +# In[17]: |
| 284 | + |
| 285 | + |
| 286 | +hfb = flopy.mf6.ModflowGwfhfb(gwf, stress_period_data={0: hfbs}) |
| 287 | + |
| 288 | + |
| 289 | +# In[18]: |
| 290 | + |
| 291 | + |
| 292 | +# plot up the results |
| 293 | +fig, ax = plt.subplots(figsize=(5, 5)) |
| 294 | +pmv = flopy.plot.PlotMapView(modelgrid=modelgrid) |
| 295 | +pmv.plot_grid() |
| 296 | +pmv.plot_bc(package=hfb, color="b", lw=2) |
| 297 | +gdf.geometry.plot(color="r", linestyle="--", ax=ax) |
| 298 | +# In[19]: |
| 299 | + |
| 300 | + |
| 301 | +try: |
| 302 | + temp_dir.cleanup() |
| 303 | +except PermissionError: |
| 304 | + # can occur on windows: https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryDirectory |
| 305 | + pass |
0 commit comments