ci(rtd): generate tutorial files and save as action asset (#979)

develop
jdhughes-usgs 2020-08-29 00:37:51 -04:00 committed by GitHub
parent 2763a1d5ef
commit 893bd11a81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 959 additions and 683 deletions

View File

@ -20,52 +20,27 @@ sys.path.insert(0, os.path.abspath(".."))
from flopy import __version__
import pymake
# -- determine if running on readthedocs ------------------------------------
on_rtd = os.environ.get('READTHEDOCS') == 'True'
# -- get the MODFLOW executables --------------------------------------------
ws = ".bin"
if os.path.isdir(ws):
shutil.rmtree(ws)
os.makedirs(ws)
osname = sys.platform.lower()
if osname == "darwin":
platform = "mac"
else:
platform = osname
pymake.getmfexes(pth=ws, platform=platform, verbose=True)
if not on_rtd:
ws = ".bin"
if os.path.isdir(ws):
shutil.rmtree(ws)
os.makedirs(ws)
osname = sys.platform.lower()
if osname == "darwin":
platform = "mac"
else:
platform = osname
pymake.getmfexes(pth=ws, platform=platform, verbose=True)
# -- copy the example problems ----------------------------------------------
srcdir = "pysrc"
ws = ".working"
if os.path.isdir(ws):
shutil.rmtree(ws)
os.makedirs(ws)
scripts = []
for fname in os.listdir(srcdir):
if fname.endswith(".py"):
scripts.append(fname)
src = os.path.join(srcdir, fname)
dst = os.path.join(ws, fname)
shutil.copyfile(src, dst)
# -- run the example problems -----------------------------------------------
for s in scripts:
args = ("python", s)
proc = Popen(args, stdout=PIPE, stderr=PIPE, cwd=ws)
stdout, stderr = proc.communicate()
if stdout:
print(stdout.decode("utf-8"))
if stderr:
print("Errors:\n{}".format(stderr.decode("utf-8")))
# -- copy the output to _static directory -----------------------------------
ws_dst = "_static"
if os.path.isdir(ws_dst):
shutil.rmtree(ws_dst)
os.makedirs(ws_dst)
for fname in os.listdir(ws):
if fname.endswith(".png"):
src = os.path.join(ws, fname)
dst = os.path.join(ws_dst, fname)
shutil.copyfile(src, dst)
# -- convert the tutorial scripts -------------------------------------------
if not on_rtd:
cmd = ("python", "tutorials2ipynb.py")
print(" ".join(cmd))
os.system(" ".join(cmd))
# -- Create the flopy rst files ---------------------------------------------
args = ("sphinx-apidoc", "-e", "-o", "source/", "../flopy/")
@ -78,7 +53,6 @@ if stderr:
# -- Project information -----------------------------------------------------
project = "flopy"
copyright = "2020, Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Leaf, A. T., Paulinski, S. R., Larsen, J. D., Toews, M. W., Morway, E. D., Bellino, J. C., Starn, J. J., and Fienen, M. N."
author = "Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Leaf, A. T., Paulinski, S. R., Larsen, J. D., Toews, M. W., Morway, E. D., Bellino, J. C., Starn, J. J., and Fienen, M. N."
@ -111,6 +85,18 @@ extensions = [
"recommonmark",
]
# nbsphinx_execute_arguments = [
# "--InlineBackend.figure_formats={'svg', 'pdf'}",
# "--InlineBackend.rc={'figure.dpi': 200}",
# ]
# Settings for GitHub actions integration
if on_rtd:
extensions.append("rtds_action")
rtds_action_github_repo = "modflowpy/flopy"
rtds_action_path = "_notebooks"
rtds_action_artifact_prefix = "notebooks-for-"
rtds_action_github_token = os.environ.get("GITHUB_TOKEN", None)
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
@ -183,7 +169,7 @@ html_css_files = [
# A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = "flopy"
html_favicon = ".._images/flopylogo.png"
html_favicon = "_images/flopylogo.png"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,

View File

@ -1,4 +0,0 @@
#!/bin/bash
sphinx-apidoc -e -o source/ ../flopy/
make html

View File

@ -1,202 +0,0 @@
Tutorial 1: Confined Steady-State Flow Model
============================================
This tutorial demonstrates use of FloPy to develop a simple MODFLOW model.
Note that you can access the latest version this tutorial python script from
`here <https://github.com/modflowpy/flopy/blob/develop/.docs/pysrc/tutorial01.py>`_.
Getting Started
---------------
If FloPy has been properly installed, then it can be imported as follows::
import flopy
Now that we can import flopy, we begin creating our simple MODFLOW model.
Creating the MODFLOW Model
--------------------------
One of the nice things about creating models in python is that it is very easy
to change one or two things and completely change the grid resolution for your
model. So in this example, we will design our python script so that the number
of layers, columns, and rows can be easily changed.
We can create a very simple MODFLOW model that has a basic package (BAS),
discretization input file (DIS), layer-property flow (LPF) package, output
control (OC), and preconditioned conjugate gradient (PCG) solver. Each one of
these has its own input file, which will be created automatically by flopy,
provided that we pass flopy the correct information.
Discretization
^^^^^^^^^^^^^^
We start by creating our flopy model object as follows::
# Assign name and create modflow model object
modelname = 'tutorial1'
mf = flopy.modflow.Modflow(modelname, exe_name='mf2005')
Next, let's proceed by defining our model domain and creating a MODFLOW grid
to span the domain::
# Model domain and grid definition
Lx = 1000.
Ly = 1000.
ztop = 0.
zbot = -50.
nlay = 1
nrow = 10
ncol = 10
delr = Lx / ncol
delc = Ly / nrow
delv = (ztop - zbot) / nlay
botm = np.linspace(ztop, zbot, nlay + 1)
With this information, we can now create the flopy discretization object by
entering the following::
# Create the discretization object
dis = flopy.modflow.ModflowDis(mf, nlay, nrow, ncol, delr=delr, delc=delc,
top=ztop, botm=botm[1:])
The obvious question at this point is, how do I know which arguments are
required by this strange thing called flopy.modflow.ModflowDis? Fortunately,
there is an online help page for each one of the model objects. The page for
the DIS input file is located at `flopy.modflow.mfdis <mfdis.html>`__.
Basic Package
^^^^^^^^^^^^^
Next we can create a flopy object that represents the MODFLOW Basic Package.
Details on the flopy BAS class are at: `flopy.modflow.mfbas <mfbas.html>`__.
For this simple model, we will assign constant head values of 10. and 0. to the
first and last model columns (in all layers), respectively. The python code
for doing this is::
# Variables for the BAS package
ibound = np.ones((nlay, nrow, ncol), dtype=np.int32)
ibound[:, :, 0] = -1
ibound[:, :, -1] = -1
strt = np.ones((nlay, nrow, ncol), dtype=np.float32)
strt[:, :, 0] = 10.
strt[:, :, -1] = 0.
bas = flopy.modflow.ModflowBas(mf, ibound=ibound, strt=strt)
Layer-Property Flow Package
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Details on the flopy LPF class are at: `flopy.modflow.mflpf <mflpf.html>`__.
Values of 10. are assigned for the horizontal and vertical hydraulic
conductivity::
# Add LPF package to the MODFLOW model
lpf = flopy.modflow.ModflowLpf(mf, hk=10., vka=10., ipakcb=53)
Because we did not specify a value for laytyp, Flopy will use the default value
of 0, which means that this model will be confined.
Output Control
^^^^^^^^^^^^^^
Details on the flopy OC class are at: `flopy.modflow.mfoc <mfoc.html>`__. Here
we can use the default OC settings by specifying the following::
# Add OC package to the MODFLOW model
spd = {(0, 0): ['print head', 'print budget', 'save head', 'save budget']}
oc = flopy.modflow.ModflowOc(mf, stress_period_data=spd, compact=True)
The stress period dictionary is used to set what output is saved for the
corresponding stress period and time step. In this case, the tuple (0, 0)
means that stress period 1 and time step 1 for MODFLOW will have output saved.
Head and budgets will be printed and head and budget information will be saved.
Preconditioned Conjugate Gradient Package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Details on the flopy PCG class are at: `flopy.modflow.mfpcg <mfpcg.html>`__.
The default settings used by flopy will be used by specifying the following
commands::
# Add PCG package to the MODFLOW model
pcg = flopy.modflow.ModflowPcg(mf)
Writing the MODFLOW Data Files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The MODFLOW input data files are written by simply issuing the following::
# Write the MODFLOW model input files
mf.write_input()
Running the Modeling
--------------------
Flopy can also be used to run the model. The model object (mf in this example)
has an attached method that will run the model. For this to work, the MODFLOW
program must be located somewhere within the system path, or within the working
directory. In this example, we have specified that the name of the executable
program is 'mf2005'. Issue the following to run the model::
# Run the MODFLOW model
success, buff = mf.run_model()
Here we have used run_model, and we could also have specified values for the
optional keywords silent, pause, and report.
Post-Processing the Results
---------------------------
Now that we have successfully built and run our MODFLOW model, we can look at
the results. MODFLOW writes the simulated heads to a binary data output file.
We cannot look at these heads with a text editor, but flopy has a binary
utility that can be used to read the heads. The following statements will
read the binary head file and create a plot of simulated heads for layer 1::
import matplotlib.pyplot as plt
import flopy.utils.binaryfile as bf
plt.subplot(1, 1, 1, aspect='equal')
hds = bf.HeadFile(modelname + '.hds')
head = hds.get_data(totim=1.0)
levels = np.arange(1, 10, 1)
extent = (delr / 2., Lx - delr / 2., Ly - delc / 2., delc / 2.)
plt.contour(head[0, :, :], levels=levels, extent=extent)
plt.savefig('tutorial1a.png')
If everything has worked properly, you should see the following head contours.
.. figure:: _static/tutorial1a.png
:alt: head contours in first layer
:scale: 100 %
:align: left
Flopy also has some pre-canned plotting capabilities can can be accessed using
the PlotMapView class. The following code shows how to use the plotmapview
class to plot boundary conditions (IBOUND), plot the grid, plot head contours,
and plot vectors::
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(1, 1, 1, aspect='equal')
hds = bf.HeadFile(modelname + '.hds')
times = hds.get_times()
head = hds.get_data(totim=times[-1])
levels = np.linspace(0, 10, 11)
cbb = bf.CellBudgetFile(modelname + '.cbc')
kstpkper_list = cbb.get_kstpkper()
frf = cbb.get_data(text='FLOW RIGHT FACE', totim=times[-1])[0]
fff = cbb.get_data(text='FLOW FRONT FACE', totim=times[-1])[0]
pmv = flopy.plot.PlotMapView(model=mf, layer=0)
qm = pmv.plot_ibound()
lc = pmv.plot_grid()
cs = pmv.contour_array(head, levels=levels)
quiver = pmv.plot_discharge(frf, fff, head=head)
plt.savefig('tutorial1b.png')
.. figure:: _static/tutorial1b.png
:alt: head contours in first layer
:scale: 100 %
:align: left

View File

@ -1,295 +0,0 @@
Tutorial 2: Unconfined Transient Flow Model
===========================================
In this example, we will convert the tutorial 1 model into an unconfined,
transient flow model with time varying boundaries. Instead of using constant
heads for the left and right boundaries (by setting ibound to -1), we will use
general head boundaries. We will have the model consider the following
conditions:
* Initial conditions -- head is 10.0 everywhere
* Period 1 (1 day) -- steady state with left and right GHB stage = 10.
* Period 2 (100 days) -- left GHB with stage = 10., right GHB with stage set
to 0.
* Period 3 (100 days) -- pumping well at model center with rate = -500., left
and right GHB = 10., and 0.
We will start with selected model commands from the previous tutorial.
Note that you can access the latest version of this tutorial python script from
`here <https://github.com/modflowpy/flopy/blob/develop/.docs/pysrc/tutorial02.py>`_.
Getting Started
---------------
As shown in the previous tutorial, import flopy using your preferred method,
such as::
import numpy as np
import flopy
Creating the MODFLOW Model
--------------------------
Define the Model Extent, Grid Resolution, and Characteristics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Assign the model information::
# Model domain and grid definition
Lx = 1000.
Ly = 1000.
ztop = 10.
zbot = -50.
nlay = 1
nrow = 10
ncol = 10
delr = Lx / ncol
delc = Ly / nrow
delv = (ztop - zbot) / nlay
botm = np.linspace(ztop, zbot, nlay + 1)
hk = 1.
vka = 1.
sy = 0.1
ss = 1.e-4
laytyp = 1
# Variables for the BAS package
# Note that changes from the previous tutorial!
ibound = np.ones((nlay, nrow, ncol), dtype=np.int32)
strt = 10. * np.ones((nlay, nrow, ncol), dtype=np.float32)
Define the Stress Periods
^^^^^^^^^^^^^^^^^^^^^^^^^
To create a model with multiple stress periods, we need to define nper, perlen,
nstp, and steady. This is done in the following block in a manner that allows
us to pass these variable directly to the discretization object::
# Time step parameters
nper = 3
perlen = [1, 100, 100]
nstp = [1, 100, 100]
steady = [True, False, False]
Create Time-Invariant Flopy Objects
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With this information, we can now create the static flopy objects that do
not change with time::
# Flopy objects
modelname = 'tutorial2'
mf = flopy.modflow.Modflow(modelname, exe_name='mf2005')
dis = flopy.modflow.ModflowDis(mf, nlay, nrow, ncol, delr=delr, delc=delc,
top=ztop, botm=botm[1:],
nper=nper, perlen=perlen, nstp=nstp, steady=steady)
bas = flopy.modflow.ModflowBas(mf, ibound=ibound, strt=strt)
lpf = flopy.modflow.ModflowLpf(mf, hk=hk, vka=vka, sy=sy, ss=ss, laytyp=laytyp, ipakcb=53)
pcg = flopy.modflow.ModflowPcg(mf)
Transient General-Head Boundary Package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
At this point, our model is ready to add our transient boundary packages.
First, we will create the GHB object, which is of the following
type: `flopy.modflow.ModflowGhb <mfghb.html>`__.
The key to creating Flopy transient boundary packages is recognizing that
the boundary data is stored in a dictionary with key values equal to the
zero-based stress period number and values equal to the boundary conditions
for that stress period. For a GHB the values can be a two-dimensional nested
list of [layer, row, column, stage, conductance]::
# Make list for stress period 1
stageleft = 10.
stageright = 10.
bound_sp1 = []
for il in range(nlay):
condleft = hk * (stageleft - zbot) * delc
condright = hk * (stageright - zbot) * delc
for ir in range(nrow):
bound_sp1.append([il, ir, 0, stageleft, condleft])
bound_sp1.append([il, ir, ncol - 1, stageright, condright])
print('Adding ', len(bound_sp1), 'GHBs for stress period 1.')
# Make list for stress period 2
stageleft = 10.
stageright = 0.
condleft = hk * (stageleft - zbot) * delc
condright = hk * (stageright - zbot) * delc
bound_sp2 = []
for il in range(nlay):
for ir in range(nrow):
bound_sp2.append([il, ir, 0, stageleft, condleft])
bound_sp2.append([il, ir, ncol - 1, stageright, condright])
print('Adding ', len(bound_sp2), 'GHBs for stress period 2.')
# We do not need to add a dictionary entry for stress period 3.
# Flopy will automatically take the list from stress period 2 and apply it
# to the end of the simulation, if necessary
stress_period_data = {0: bound_sp1, 1: bound_sp2}
# Create the flopy ghb object
ghb = flopy.modflow.ModflowGhb(mf, stress_period_data=stress_period_data)
Transient Well Package
^^^^^^^^^^^^^^^^^^^^^^
Now we can create the well package object, which is of the type,
`flopy.modflow.ModflowWel <mfwel.html>`__.::
# Create the well package
# Remember to use zero-based layer, row, column indices!
pumping_rate = -500.
wel_sp1 = [[0, nrow/2 - 1, ncol/2 - 1, 0.]]
wel_sp2 = [[0, nrow/2 - 1, ncol/2 - 1, 0.]]
wel_sp3 = [[0, nrow/2 - 1, ncol/2 - 1, pumping_rate]]
stress_period_data = {0: wel_sp1, 1: wel_sp2, 2: wel_sp3}
wel = flopy.modflow.ModflowWel(mf, stress_period_data=stress_period_data)
Output Control
^^^^^^^^^^^^^^
Here we create the output control package object, which is of the
type `flopy.modflow.ModflowOc <mfoc.html>`__.::
stress_period_data = {}
for kper in range(nper):
for kstp in range(nstp[kper]):
stress_period_data[(kper, kstp)] = ['save head',
'save drawdown',
'save budget',
'print head',
'print budget']
oc = flopy.modflow.ModflowOc(mf, stress_period_data=stress_period_data,
compact=True)
Running the Modeling
--------------------
Run the model with the run_model method, which returns a success flag and
the stream of output. With run_model, we have some finer control, that allows us to suppress the output.::
# Write the model input files
mf.write_input()
# Run the model
success, mfoutput = mf.run_model(silent=True, pause=False, report=True)
if not success:
raise Exception('MODFLOW did not terminate normally.')
Post-Processing the Results
---------------------------
Once again, we can read heads from the MODFLOW binary output file, using
the `flopy.utils.binaryfile <binaryfile.html>`__ module. Included with the
HeadFile object are several methods that we will use here:
* get_times() will return a list of times contained in the binary head file
* get_data() will return a three-dimensional head array for the specified time
* get_ts() will return a time series array [ntimes, headval] for the specified cell
Using these methods, we can create head plots and hydrographs from the
model results.::
# Imports
import matplotlib.pyplot as plt
import flopy.utils.binaryfile as bf
# Create the headfile and budget file objects
headobj = bf.HeadFile(modelname + '.hds')
times = headobj.get_times()
cbb = bf.CellBudgetFile(modelname + '.cbc')
# Setup contour parameters
levels = np.linspace(0, 10, 11)
extent = (delr / 2., Lx - delr / 2., delc / 2., Ly - delc / 2.)
print('Levels: ', levels)
print('Extent: ', extent)
# Well point
wpt = ((float(ncol / 2) - 0.5) * delr, (float(nrow / 2 - 1) + 0.5) * delc)
wpt = (450., 550.)
# Make the plots
mytimes = [1.0, 101.0, 201.0]
for iplot, time in enumerate(mytimes):
print('*****Processing time: ', time)
head = headobj.get_data(totim=time)
#Print statistics
print('Head statistics')
print(' min: ', head.min())
print(' max: ', head.max())
print(' std: ', head.std())
# Extract flow right face and flow front face
frf = cbb.get_data(text='FLOW RIGHT FACE', totim=time)[0]
fff = cbb.get_data(text='FLOW FRONT FACE', totim=time)[0]
#Create the plot
f = plt.figure()
plt.subplot(1, 1, 1, aspect='equal')
plt.title('stress period ' + str(iplot + 1))
pmv = flopy.plot.PlotMapView(model=mf, layer=0)
qm = pmv.plot_ibound()
lc = pmv.plot_grid()
qm = pmv.plot_bc('GHB', alpha=0.5)
cs = pmv.contour_array(head, levels=levels)
plt.clabel(cs, inline=1, fontsize=10, fmt='%1.1f')
quiver = pmv.plot_discharge(frf, fff, head=head)
mfc = 'None'
if (iplot + 1) == len(mytimes):
mfc='black'
plt.plot(wpt[0], wpt[1], lw=0, marker='o', markersize=8,
markeredgewidth=0.5,
markeredgecolor='black', markerfacecolor=mfc, zorder=9)
plt.text(wpt[0] + 25, wpt[1] - 25, 'well', size=12, zorder=12)
plt.savefig('tutorial2-{}.png'.format(iplot))
If everything has worked properly, you should see the following head contours.
.. figure:: _static/tutorial2-0.png
:alt: head contours for stress period 1
:scale: 100 %
:align: center
.. figure:: _static/tutorial2-1.png
:alt: head contours for stress period 2
:scale: 100 %
:align: center
.. figure:: _static/tutorial2-2.png
:alt: head contours for stress period 3
:scale: 100 %
:align: center
Plot Head Versus Time
^^^^^^^^^^^^^^^^^^^^^
Make a plot of head versus time by extracting the binary heads from
the headobj::
# Plot the head versus time
idx = (0, int(nrow / 2) - 1, int(ncol / 2) - 1)
ts = headobj.get_ts(idx)
plt.subplot(1, 1, 1)
ttl = 'Head at cell ({0},{1},{2})'.format(idx[0] + 1, idx[1] + 1, idx[2] + 1)
plt.title(ttl)
plt.xlabel('time')
plt.ylabel('head')
plt.plot(ts[:, 0], ts[:, 1], 'bo-')
plt.savefig('tutorial2-ts.png')
.. figure:: _static/tutorial2-ts.png
:alt: head contours in first layer
:scale: 100 %
:align: center

View File

@ -1,10 +1,33 @@
Tutorials
=========
MODFLOW 6 Tutorials
-----------------
Contents:
.. toctree::
:maxdepth: 2
tutorial1
tutorial2
_notebooks/tutorial01_mf6
MODFLOW Tutorials
-----------------
Contents:
.. toctree::
:maxdepth: 2
_notebooks/tutorial01_mf
_notebooks/tutorial02_mf
SEAWAT Tutorials
-----------------
Contents:
.. toctree::
:maxdepth: 2
_notebooks/tutorial01_seawat

36
.docs/tutorials2ipynb.py Normal file
View File

@ -0,0 +1,36 @@
import os
import shutil
wpth = ".working"
if os.path.isdir(wpth):
shutil.rmtree(wpth)
os.makedirs(wpth)
# copy the python files
pth = os.path.join("..", "examples", "Tutorials")
py_files = [file_name for file_name in os.listdir(pth)
if file_name.endswith(".py")]
for file_name in py_files:
src = os.path.join(pth, file_name)
dst = os.path.join(wpth, file_name)
shutil.copyfile(src, dst)
py_pth = os.path.join(wpth, "*.py")
cmd = (
"jupytext",
"--to ipynb",
"--execute",
py_pth,
)
print(" ".join(cmd))
os.system(" ".join(cmd))
npth = "_notebooks"
# copy notebooks
if os.path.isdir(npth):
shutil.rmtree(npth)
os.makedirs(npth)
for file_name in py_files:
src = os.path.join(wpth, file_name.replace(".py", ".ipynb"))
dst = os.path.join(npth, file_name.replace(".py", ".ipynb"))
shutil.copyfile(src, dst)
shutil.rmtree(".working")

View File

@ -104,12 +104,18 @@ jobs:
activate-environment: flopy
use-only-tar-bz2: true
- name: Add jupyter and nbconvert to one matrix entry
- name: Add jupyter and nbconvert to notebooks run
if: matrix.run-type == 'nb'
shell: bash -l {0}
run: |
conda install -n flopy -c conda-forge jupyter nbconvert
- name: Add jupyter and jupytext to scripts run
if: matrix.run-type == 'script'
shell: bash -l {0}
run: |
conda install -n flopy -c conda-forge jupyter jupytext
- name: Add packages to flopy environment
shell: bash -l {0}
run: |
@ -154,3 +160,44 @@ jobs:
uses: codecov/codecov-action@v1.0.12
with:
file: ./coverage.xml
- name: Run jupytext on tutorials
if: matrix.run-type == 'script'
shell: bash -l {0}
run: |
cd .docs/
python tutorials2ipynb.py
cd ../
- name: Upload completed jupyter notebooks as an artifact for ReadtheDocs
if: matrix.run-type == 'script' && github.repository_owner == 'modflowpy'
uses: actions/upload-artifact@v2
with:
name: notebooks-for-${{ github.sha }}
path: |
.docs/_notebooks
# trigger rtd if previous job was successful
rtd:
name: ReadtheDocs
needs: flopyCI
runs-on: ubuntu-latest
if: github.repository_owner == 'modflowpy'
steps:
- name: Checkout flopy repo
uses: actions/checkout@v2
- name: Output repo information
run: |
echo ${{ github.repository_owner }}
echo ${{ github.repository }}
echo ${{ github.ref }}
- name: Trigger RTDs build on master and develop branches
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop'
uses: dfm/rtds-action@v1.0.0
with:
webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }}
webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }}
commit_ref: ${{ github.ref }}

2
.gitignore vendored
View File

@ -85,4 +85,6 @@ autotest/.noseids
.docs/_templates
.docs/source
.docs/.bin
.docs/_temp
.docs/_notebooks

View File

@ -3,6 +3,7 @@ from __future__ import print_function
import os
import sys
import shutil
from subprocess import Popen, PIPE
exclude = ['flopy_swi2_ex2.py', 'flopy_swi2_ex5.py']
for arg in sys.argv:
@ -10,6 +11,7 @@ for arg in sys.argv:
exclude = []
sdir = os.path.join('..', 'examples', 'scripts')
tdir = os.path.join("..", "examples", "Tutorials")
# make working directories
tempdir = os.path.join('.', 'temp')
@ -17,17 +19,20 @@ if os.path.isdir(tempdir):
shutil.rmtree(tempdir)
os.mkdir(tempdir)
testdir = os.path.join('.', 'temp', 'scripts')
if os.path.isdir(testdir):
shutil.rmtree(testdir)
os.mkdir(testdir)
testdirs = (os.path.join('.', 'temp', 'scripts'),
os.path.join('.', 'temp', 'tutorials'),
)
for testdir in testdirs:
if os.path.isdir(testdir):
shutil.rmtree(testdir)
os.mkdir(testdir)
# add testdir to python path
sys.path.append(testdir)
# add testdir to python path
sys.path.append(testdir)
def copy_scripts():
files = [f for f in os.listdir(sdir) if f.endswith('.py')]
def copy_scripts(src_dir, dst_dir):
files = [f for f in sorted(os.listdir(src_dir)) if f.endswith('.py')]
# exclude unwanted files
for e in exclude:
@ -35,13 +40,13 @@ def copy_scripts():
files.remove(e)
# copy files
for fn in files:
pth = os.path.join(sdir, fn)
opth = os.path.join(testdir, fn)
for file_name in files:
src = os.path.join(src_dir, file_name)
dst = os.path.join(dst_dir, file_name)
# copy script
print('copying {} from {} to {}'.format(fn, sdir, testdir))
shutil.copyfile(pth, opth)
print('copying {} from {} to {}'.format(file_name, src_dir, testdir))
shutil.copyfile(src, dst)
return files
@ -52,7 +57,7 @@ def import_from(mod, name):
return main
def run_scripts(fn):
def run_scripts(fn, testdir):
# import run function from scripts
s = os.path.splitext(fn)[0]
run = import_from(s, 'run')
@ -73,19 +78,42 @@ def run_scripts(fn):
assert ival == 0, 'could not run {}'.format(fn)
def test_notebooks():
def run_tutorial_scripts(fn, testdir):
args = ("python", fn)
print("running...'{}'".format(" ".join(args)))
proc = Popen(args, stdout=PIPE, stderr=PIPE, cwd=testdir)
stdout, stderr = proc.communicate()
if stdout:
print(stdout.decode("utf-8"))
if stderr:
print("Errors:\n{}".format(stderr.decode("utf-8")))
return
def test_scripts():
# get list of scripts to run
files = copy_scripts()
files = copy_scripts(sdir, testdirs[0])
for fn in files:
yield run_scripts, fn
yield run_scripts, fn, testdirs[0]
def test_tutorial_scripts():
# get list of scripts to run
files = copy_scripts(tdir, testdirs[1])
for fn in files:
yield run_tutorial_scripts, fn, testdirs[1]
if __name__ == '__main__':
# # get list of scripts to run
# files = copy_scripts(sdir, testdirs[0])
# for fn in files:
# run_scripts(fn, testdirs[0])
# get list of scripts to run
files = copy_scripts()
# get list of tutorial scripts to run
files = copy_scripts(tdir, testdirs[1])
for fn in files:
run_scripts(fn)
run_tutorial_scripts(fn, testdirs[1])

View File

@ -4,6 +4,7 @@ These are the examples that are distributed with MODFLOW-USG.
"""
import os
import sys
import flopy
import pymake
@ -37,6 +38,10 @@ v = flopy.which(mfnwt_exe)
run = True
if v is None:
run = False
# fix for intermittent CI failure on windows
else:
if sys.platform.lower() in ("win32", "darwin"):
run = False
#

View File

@ -1,81 +0,0 @@
import numpy as np
import flopy
# Assign name and create modflow model object
modelname = "tutorial1"
mf = flopy.modflow.Modflow(modelname, exe_name="mf2005")
# Model domain and grid definition
Lx = 1000.0
Ly = 1000.0
ztop = 0.0
zbot = -50.0
nlay = 1
nrow = 10
ncol = 10
delr = Lx / ncol
delc = Ly / nrow
delv = (ztop - zbot) / nlay
botm = np.linspace(ztop, zbot, nlay + 1)
# Create the discretization object
dis = flopy.modflow.ModflowDis(
mf, nlay, nrow, ncol, delr=delr, delc=delc, top=ztop, botm=botm[1:]
)
# Variables for the BAS package
ibound = np.ones((nlay, nrow, ncol), dtype=np.int32)
ibound[:, :, 0] = -1
ibound[:, :, -1] = -1
strt = np.ones((nlay, nrow, ncol), dtype=np.float32)
strt[:, :, 0] = 10.0
strt[:, :, -1] = 0.0
bas = flopy.modflow.ModflowBas(mf, ibound=ibound, strt=strt)
# Add LPF package to the MODFLOW model
lpf = flopy.modflow.ModflowLpf(mf, hk=10.0, vka=10.0, ipakcb=53)
# Add OC package to the MODFLOW model
spd = {(0, 0): ["print head", "print budget", "save head", "save budget"]}
oc = flopy.modflow.ModflowOc(mf, stress_period_data=spd, compact=True)
# Add PCG package to the MODFLOW model
pcg = flopy.modflow.ModflowPcg(mf)
# Write the MODFLOW model input files
mf.write_input()
# Run the MODFLOW model
success, buff = mf.run_model()
# Post process the results
import matplotlib.pyplot as plt
import flopy.utils.binaryfile as bf
plt.subplot(1, 1, 1, aspect="equal")
hds = bf.HeadFile(modelname + ".hds")
head = hds.get_data(totim=1.0)
levels = np.arange(1, 10, 1)
extent = (delr / 2.0, Lx - delr / 2.0, Ly - delc / 2.0, delc / 2.0)
plt.contour(head[0, :, :], levels=levels, extent=extent)
plt.savefig("tutorial1a.png")
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
hds = bf.HeadFile(modelname + ".hds")
times = hds.get_times()
head = hds.get_data(totim=times[-1])
levels = np.linspace(0, 10, 11)
cbb = bf.CellBudgetFile(modelname + ".cbc")
kstpkper_list = cbb.get_kstpkper()
frf = cbb.get_data(text="FLOW RIGHT FACE", totim=times[-1])[0]
fff = cbb.get_data(text="FLOW FRONT FACE", totim=times[-1])[0]
modelmap = flopy.plot.ModelMap(model=mf, layer=0)
qm = modelmap.plot_ibound()
lc = modelmap.plot_grid()
cs = modelmap.contour_array(head, levels=levels)
quiver = modelmap.plot_discharge(frf, fff, head=head)
plt.savefig("tutorial1b.png")

View File

@ -0,0 +1,187 @@
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: light
# format_version: '1.5'
# jupytext_version: 1.5.1
# kernelspec:
# display_name: Python 3
# language: python
# name: python3
# ---
# # MODFLOW Tutorial 1: Confined Steady-State Flow Model
#
# This tutorial demonstrates use of FloPy to develop a simple MODFLOW-2005
# model.
# ## Getting Started
#
# If FloPy has been properly installed, then it can be imported as follows:
import numpy as np
import flopy
# Now that we can import flopy, we begin creating our simple MODFLOW model.
# numpy is imported to create arrays of model data.
# ## Creating the MODFLOW Model
#
# One of the nice things about creating models in python is that it is very
# easy to change one or two things and completely change the grid resolution
# for your model. So in this example, we will design our python script so
# that the number of layers, columns, and rows can be easily changed.
#
# We can create a very simple MODFLOW model that has a basic package (BAS),
# discretization input file (DIS), layer-property flow (LPF) package, output
# control (OC), and preconditioned conjugate gradient (PCG) solver. Each one
# of these has its own input file, which will be created automatically by
# flopy, provided that we pass flopy the correct information.
# ### Discretization
#
# We start by creating our flopy model object.
modelname = "tutorial1_mf"
mf = flopy.modflow.Modflow(modelname, exe_name="mf2005")
# Next, let's proceed by defining our model domain and creating a MODFLOW grid
# to span the domain.
Lx = 1000.0
Ly = 1000.0
ztop = 0.0
zbot = -50.0
nlay = 1
nrow = 10
ncol = 10
delr = Lx / ncol
delc = Ly / nrow
delv = (ztop - zbot) / nlay
botm = np.linspace(ztop, zbot, nlay + 1)
# With this information, we can now create the flopy discretization object by
# entering the following:
dis = flopy.modflow.ModflowDis(
mf, nlay, nrow, ncol, delr=delr, delc=delc, top=ztop, botm=botm[1:]
)
# ### Basic Package
#
# Next we can create a flopy object that represents the MODFLOW Basic Package.
# For this simple model, we will assign constant head values of 10. and 0. to
# the first and last model columns (in all layers), respectively. The python
# code for doing this is:
ibound = np.ones((nlay, nrow, ncol), dtype=np.int32)
ibound[:, :, 0] = -1
ibound[:, :, -1] = -1
strt = np.ones((nlay, nrow, ncol), dtype=np.float32)
strt[:, :, 0] = 10.0
strt[:, :, -1] = 0.0
bas = flopy.modflow.ModflowBas(mf, ibound=ibound, strt=strt)
# ### Layer-Property Flow Package
# Constant values of 10. are assigned for the horizontal and vertical
# hydraulic conductivity:
lpf = flopy.modflow.ModflowLpf(mf, hk=10.0, vka=10.0, ipakcb=53)
# Because we did not specify a value for laytyp, Flopy will use the default
# value of 0, which means that this model will be confined.
# ### Output Control
#
# Here we can use the default OC settings by specifying the following:
spd = {(0, 0): ["print head", "print budget", "save head", "save budget"]}
oc = flopy.modflow.ModflowOc(mf, stress_period_data=spd, compact=True)
# The stress period dictionary is used to set what output is saved for the
# corresponding stress period and time step. In this case, the tuple `(0, 0)`
# means that stress period 1 and time step 1 for MODFLOW will have output
# saved. Head and budgets will be printed and head and budget information
# will be saved.
# ### Preconditioned Conjugate Gradient Package
#
# The default settings used by flopy will be used by specifying the following
# commands:
pcg = flopy.modflow.ModflowPcg(mf)
# ### Writing the MODFLOW Data Files
#
# The MODFLOW input data files are written by simply issuing the following:
mf.write_input()
# ## Running the Model
#
# Flopy can also be used to run the model. The model object (`mf` in this
# example) has an attached method that will run the model. For this to work,
# the MODFLOW program must be located somewhere within the system path, or
# within the working directory. In this example, we have specified that the
# name of the executable program is 'mf2005'. Issue the following to run
# the model:
success, buff = mf.run_model()
if not success:
raise Exception("MODFLOW did not terminate normally.")
# Here we have used run_model, and we could also have specified values for the
# optional keywords silent, pause, and report.
# ## Post-Processing the Results
#
# Now that we have successfully built and run our MODFLOW model, we can look at
# the results. MODFLOW writes the simulated heads to a binary data output file.
# We cannot look at these heads with a text editor, but flopy has a binary
# utility that can be used to read the heads. The following statements will
# read the binary head file and create a plot of simulated heads for layer 1:
import matplotlib.pyplot as plt
import flopy.utils.binaryfile as bf
# Extract the heads
hds = bf.HeadFile(modelname + ".hds")
head = hds.get_data(totim=1.0)
# Contour the heads
extent = (delr / 2.0, Lx - delr / 2.0, Ly - delc / 2.0, delc / 2.0)
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
ax.contour(head[0, :, :], levels=np.arange(1, 10, 1), extent=extent)
# Flopy also has some pre-canned plotting capabilities can can be accessed
# using the `PlotMapView()` class. The following code shows how to use the
# plotmapview class to plot boundary conditions (`IBOUND`), plot the grid,
# plot head contours, and plot vectors:
# Extract the heads
hds = bf.HeadFile(modelname + ".hds")
times = hds.get_times()
head = hds.get_data(totim=times[-1])
# Extract the cell-by-cell flows
cbb = bf.CellBudgetFile(modelname + ".cbc")
kstpkper_list = cbb.get_kstpkper()
frf = cbb.get_data(text="FLOW RIGHT FACE", totim=times[-1])[0]
fff = cbb.get_data(text="FLOW FRONT FACE", totim=times[-1])[0]
# Create the figure
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
modelmap = flopy.plot.PlotMapView(model=mf, layer=0, ax=ax)
qm = modelmap.plot_ibound()
lc = modelmap.plot_grid()
cs = modelmap.contour_array(head, levels=np.linspace(0, 10, 11))
quiver = modelmap.plot_discharge(frf, fff, head=head)

View File

@ -0,0 +1,255 @@
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: light
# format_version: '1.5'
# jupytext_version: 1.5.1
# kernelspec:
# display_name: Python 3
# language: python
# name: python3
# ---
# # MODFLOW 6 Tutorial 1: Unconfined Steady-State Flow Model
#
# This tutorial demonstrates use of FloPy to develop a simple MODFLOW 6
# model.
# ## Getting Started
import os
import numpy as np
import matplotlib.pyplot as plt
import flopy
# We are creating a square model with a specified head equal to `h1` along
# all boundaries. The head at the cell in the center in the top layer is
# fixed to `h2`. First, set the name of the model and the parameters of the
# model: the number of layers `Nlay`, the number of rows and columns `N`,
# lengths of the sides of the model `L`, aquifer thickness `H`,
# hydraulic conductivity `k`
name = "tutorial01_mf6"
h1 = 100
h2 = 90
Nlay = 10
N = 101
L = 400.0
H = 50.0
k = 1.0
# ### Create the Flopy Model Objects
#
# One big difference between MODFLOW 6 and previous MODFLOW versions is
# that MODFLOW 6 is based on the concept of a simulation.
# A simulation consists of the following:
#
# * Temporal discretization (`TDIS`)
# * One or more models
# * Zero or more exchanges (instructions for how models are coupled)
# * Solutions
#
# For this simple example, the simulation consists of the temporal
# discretization (`TDIS`) package, a groundwater flow (`GWF`) model, and
# an iterative model solution (`IMS`), which controls how the GWF model is
# solved.
# ### Create the Flopy simulation object
sim = flopy.mf6.MFSimulation(
sim_name=name, exe_name="mf6", version="mf6", sim_ws="."
)
# ### Create the Flopy `TDIS` object
tdis = flopy.mf6.ModflowTdis(
sim, pname="tdis", time_units="DAYS", nper=1, perioddata=[(1.0, 1, 1.0)]
)
# ### Create the Flopy `IMS` Package object
ims = flopy.mf6.ModflowIms(sim, pname="ims", complexity="SIMPLE")
# Create the Flopy groundwater flow (gwf) model object
model_nam_file = "{}.nam".format(name)
gwf = flopy.mf6.ModflowGwf(sim, modelname=name, model_nam_file=model_nam_file)
# Now that the overall simulation is set up, we can focus on building the
# groundwater flow model. The groundwater flow model will be built by
# adding packages to it that describe the model characteristics.
# ### Create the discretization (`DIS`) Package
#
# Define the discretization of the model. All layers are given equal thickness.
# The `bot` array is build from `H` and the `Nlay` values to indicate top and
# bottom of each layer, and `delrow` and `delcol` are computed from model
# size `L` and number of cells `N`. Once these are all computed, the
# Discretization file is built.
bot = np.linspace(-H / Nlay, -H, Nlay)
delrow = delcol = L / (N - 1)
dis = flopy.mf6.ModflowGwfdis(
gwf,
nlay=Nlay,
nrow=N,
ncol=N,
delr=delrow,
delc=delcol,
top=0.0,
botm=bot,
)
# ### Create the initial conditions (`IC`) Package
start = h1 * np.ones((Nlay, N, N))
ic = flopy.mf6.ModflowGwfic(gwf, pname="ic", strt=start)
# ### Create the node property flow (`NPF`) Package
npf = flopy.mf6.ModflowGwfnpf(gwf, icelltype=1, k=k, save_flows=True)
# ### Create the constant head (`CHD`) Package
#
# List information is created a bit differently for MODFLOW 6 than for other
# MODFLOW versions. The cellid (layer, row, column, for a regular grid)
# can be entered as a tuple as the first entry. Remember that these must be
# zero-based indices!
chd_rec = []
chd_rec.append(((0, int(N / 4), int(N / 4)), h2))
for layer in range(0, Nlay):
for row_col in range(0, N):
chd_rec.append(((layer, row_col, 0), h1))
chd_rec.append(((layer, row_col, N - 1), h1))
if row_col != 0 and row_col != N - 1:
chd_rec.append(((layer, 0, row_col), h1))
chd_rec.append(((layer, N - 1, row_col), h1))
chd = flopy.mf6.ModflowGwfchd(
gwf,
maxbound=len(chd_rec),
stress_period_data=chd_rec,
save_flows=True,
)
# The `CHD` Package stored the constant heads in a structured array,
# also called a `numpy.recarray`. We can get a pointer to the recarray
# for the first stress period (iper = 0) as follows.
iper = 0
ra = chd.stress_period_data.get_data(key=iper)
ra
# Create the output control (`OC`) Package
headfile = "{}.hds".format(name)
head_filerecord = [headfile]
budgetfile = "{}.cbb".format(name)
budget_filerecord = [budgetfile]
saverecord = [("HEAD", "ALL"), ("BUDGET", "ALL")]
printrecord = [("HEAD", "LAST")]
oc = flopy.mf6.ModflowGwfoc(
gwf,
saverecord=saverecord,
head_filerecord=head_filerecord,
budget_filerecord=budget_filerecord,
printrecord=printrecord,
)
# ## Create the MODFLOW 6 Input Files and Run the Model
#
# Once all the flopy objects are created, it is very easy to create
# all of the input files and run the model.
# ### Write the datasets
sim.write_simulation()
# ### Run the Simulation
#
# We can also run the simulation from python, but only if the MODFLOW 6
# executable is available. The executable can be made available by putting
# the executable in a folder that is listed in the system path variable.
# Another option is to just put a copy of the executable in the simulation
# folder, though this should generally be avoided. A final option is to
# provide a full path to the executable when the simulation is constructed.
# This would be done by specifying exe_name with the full path.
success, buff = sim.run_simulation()
if not success:
raise Exception("MODFLOW 6 did not terminate normally.")
# ## Post-Process Head Results
#
# First, a link to the heads file is created with `HeadFile`. The link can then be accessed with the `get_data` function, by specifying, in this case, the step number and period number for which we want to retrieve data. A three-dimensional array is returned of size `nlay, nrow, ncol`. Matplotlib contouring functions are used to make contours of the layers or a cross-section.
# Read the binary head file and plot the results. We can use the Flopy
# `HeadFile()` class because the format of the headfile for MODFLOW 6 is
# the same as for previous MODFLOW versions.
# ### Plot a Map of Layer 1
hds = flopy.utils.binaryfile.HeadFile(headfile)
h = hds.get_data(kstpkper=(0, 0))
x = y = np.linspace(0, L, N)
y = y[::-1]
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
c = ax.contour(x, y, h[0], np.arange(90, 100.1, 0.2), colors="black")
plt.clabel(c, fmt="%2.1f")
# ### Plot a Map of Layer 10
x = y = np.linspace(0, L, N)
y = y[::-1]
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
c = ax.contour(x, y, h[-1], np.arange(90, 100.1, 0.2), colors="black")
plt.clabel(c, fmt="%1.1f")
# ### Plot a Cross-section along row 51
z = np.linspace(-H / Nlay / 2, -H + H / Nlay / 2, Nlay)
fig = plt.figure(figsize=(5, 2.5))
ax = fig.add_subplot(1, 1, 1, aspect="auto")
c = ax.contour(x, z, h[:, 50, :], np.arange(90, 100.1, 0.2), colors="black")
plt.clabel(c, fmt="%1.1f")
# ### We can also use the Flopy `PlotMapView()` capabilities for MODFLOW 6
#
# Before we start we will create a MODFLOW-2005 ibound array to use to plot
# the locations of the constant heads.
ibd = np.ones((Nlay, N, N), dtype=np.int)
for k, i, j in ra["cellid"]:
ibd[k, i, j] = -1
# ### Plot a Map of Layers 1 and 10
fig, axes = plt.subplots(2, 1, figsize=(6, 12), constrained_layout=True)
# first subplot
ax = axes[0]
ax.set_title("Model Layer 1")
modelmap = flopy.plot.PlotMapView(model=gwf, ax=ax)
quadmesh = modelmap.plot_ibound(ibound=ibd)
linecollection = modelmap.plot_grid(lw=0.5, color="0.5")
contours = modelmap.contour_array(
h[0], levels=np.arange(90, 100.1, 0.2), colors="black"
)
ax.clabel(contours, fmt="%2.1f")
# second subplot
ax = axes[1]
ax.set_title("Model Layer {}".format(Nlay))
modelmap = flopy.plot.PlotMapView(model=gwf, ax=ax, layer=Nlay - 1)
quadmesh = modelmap.plot_ibound(ibound=ibd)
linecollection = modelmap.plot_grid(lw=0.5, color="0.5")
pa = modelmap.plot_array(h[0])
contours = modelmap.contour_array(
h[0], levels=np.arange(90, 100.1, 0.2), colors="black"
)
cb = plt.colorbar(pa, shrink=0.5, ax=ax)
ax.clabel(contours, fmt="%2.1f")

View File

@ -0,0 +1,196 @@
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: light
# format_version: '1.5'
# jupytext_version: 1.5.1
# kernelspec:
# display_name: Python 3
# language: python
# name: python3
# ---
# # SEAWAT Tutorial 1: Henry Saltwater Intrusion Problem
#
# In this tutorial, we will use Flopy to create, run, and post process the
# Henry saltwater intrusion problem using SEAWAT Version 4.
# ## Getting Started
import os
import numpy as np
import flopy
# ### Input variables for the Henry Problem
Lx = 2.0
Lz = 1.0
nlay = 50
nrow = 1
ncol = 100
delr = Lx / ncol
delc = 1.0
delv = Lz / nlay
henry_top = 1.0
henry_botm = np.linspace(henry_top - delv, 0.0, nlay)
qinflow = 5.702 # m3/day
dmcoef = 0.57024 # m2/day Could also try 1.62925 as another case of the Henry problem
hk = 864.0 # m/day
# ### Create the basic MODFLOW model structure
modelname = "henry"
swt = flopy.seawat.Seawat(modelname, exe_name="swtv4")
print(swt.namefile)
# save cell fluxes to unit 53
ipakcb = 53
# Add DIS package to the MODFLOW model
dis = flopy.modflow.ModflowDis(
swt,
nlay,
nrow,
ncol,
nper=1,
delr=delr,
delc=delc,
laycbd=0,
top=henry_top,
botm=henry_botm,
perlen=1.5,
nstp=15,
)
# Variables for the BAS package
ibound = np.ones((nlay, nrow, ncol), dtype=np.int32)
ibound[:, :, -1] = -1
# Add BAS package to the MODFLOW model
bas = flopy.modflow.ModflowBas(swt, ibound, 0)
# Add LPF package to the MODFLOW model
lpf = flopy.modflow.ModflowLpf(swt, hk=hk, vka=hk, ipakcb=ipakcb)
# Add PCG Package to the MODFLOW model
pcg = flopy.modflow.ModflowPcg(swt, hclose=1.0e-8)
# Add OC package to the MODFLOW model
oc = flopy.modflow.ModflowOc(
swt,
stress_period_data={(0, 0): ["save head", "save budget"]},
compact=True,
)
# Create WEL and SSM data
itype = flopy.mt3d.Mt3dSsm.itype_dict()
wel_data = {}
ssm_data = {}
wel_sp1 = []
ssm_sp1 = []
for k in range(nlay):
wel_sp1.append([k, 0, 0, qinflow / nlay])
ssm_sp1.append([k, 0, 0, 0.0, itype["WEL"]])
ssm_sp1.append([k, 0, ncol - 1, 35.0, itype["BAS6"]])
wel_data[0] = wel_sp1
ssm_data[0] = ssm_sp1
wel = flopy.modflow.ModflowWel(swt, stress_period_data=wel_data, ipakcb=ipakcb)
# ### Create the basic MT3DMS model structure
btn = flopy.mt3d.Mt3dBtn(
swt,
nprs=-5,
prsity=0.35,
sconc=35.0,
ifmtcn=0,
chkmas=False,
nprobs=10,
nprmas=10,
dt0=0.001,
)
adv = flopy.mt3d.Mt3dAdv(swt, mixelm=0)
dsp = flopy.mt3d.Mt3dDsp(swt, al=0.0, trpt=1.0, trpv=1.0, dmcoef=dmcoef)
gcg = flopy.mt3d.Mt3dGcg(swt, iter1=500, mxiter=1, isolve=1, cclose=1e-7)
ssm = flopy.mt3d.Mt3dSsm(swt, stress_period_data=ssm_data)
# ### Create the SEAWAT model structure
vdf = flopy.seawat.SeawatVdf(
swt,
iwtable=0,
densemin=0,
densemax=0,
denseref=1000.0,
denseslp=0.7143,
firstdt=1e-3,
)
# ## Write the input files
swt.write_input()
# ## Run the model
success, buff = swt.run_model(silent=True, report=True)
if not success:
raise Exception("SEAWAT did not terminate normally.")
# ## Post-process the results
import numpy as np
import flopy.utils.binaryfile as bf
# ### Load the concentration data
ucnobj = bf.UcnFile("MT3D001.UCN", model=swt)
times = ucnobj.get_times()
concentration = ucnobj.get_data(totim=times[-1])
# ### Load the cell-by-cell flow data
cbbobj = bf.CellBudgetFile("henry.cbc")
times = cbbobj.get_times()
qx = cbbobj.get_data(text="flow right face", totim=times[-1])[0]
qy = np.zeros((nlay, nrow, ncol), dtype=np.float)
qz = cbbobj.get_data(text="flow lower face", totim=times[-1])[0]
# ### Create a plot with concentrations and flow vectors
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
pmv = flopy.plot.PlotCrossSection(model=swt, ax=ax, line={"row": 0})
arr = pmv.plot_array(concentration)
pmv.plot_vector(qx, qy, -qz, color="white", kstep=3, hstep=3)
plt.colorbar(arr, shrink=0.5, ax=ax)
ax.set_title("Simulated Concentrations");
# ### Load the head data
headobj = bf.HeadFile("henry.hds")
times = headobj.get_times()
head = headobj.get_data(totim=times[-1])
# ### Create a plot with heads
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(1, 1, 1, aspect="equal")
pmv = flopy.plot.PlotCrossSection(model=swt, ax=ax, line={"row": 0})
arr = pmv.plot_array(head)
contours = pmv.contour_array(head, colors="white")
ax.clabel(contours, fmt="%2.2f")
plt.colorbar(arr, shrink=0.5, ax=ax)
ax.set_title("Simulated Heads");

View File

@ -1,7 +1,48 @@
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: light
# format_version: '1.5'
# jupytext_version: 1.5.1
# kernelspec:
# display_name: Python 3
# language: python
# name: python3
# ---
# # MODFLOW Tutorial 2: Unconfined Transient Flow Model
#
# In this example, we will convert the tutorial 1 model into an unconfined,
# transient flow model with time varying boundaries. Instead of using constant
# heads for the left and right boundaries (by setting ibound to -1), we will use
# general head boundaries. We will have the model consider the following
# conditions:
#
# * Initial conditions -- head is 10.0 everywhere
# * Period 1 (1 day) -- steady state with left and right GHB stage = 10.
# * Period 2 (100 days) -- left GHB with stage = 10., right GHB with stage set
# to 0.
# * Period 3 (100 days) -- pumping well at model center with rate = -500., left
# and right GHB = 10., and 0.
#
# We will start with selected model commands from the previous tutorial.
#
# ## Getting Started
#
# As shown in the previous MODFLOW tutorial, import flopy.
import numpy as np
import flopy
# Model domain and grid definition
# ## Creating the MODFLOW Model
#
# ### Define the Model Extent, Grid Resolution, and Characteristics
#
# Assign the model information
Lx = 1000.0
Ly = 1000.0
ztop = 10.0
@ -20,18 +61,28 @@ ss = 1.0e-4
laytyp = 1
# Variables for the BAS package
# Note that changes from the previous tutorial!
# Note that changes from the MODFLOW tutorial 1
ibound = np.ones((nlay, nrow, ncol), dtype=np.int32)
strt = 10.0 * np.ones((nlay, nrow, ncol), dtype=np.float32)
# Time step parameters
# ### Define the Stress Periods
#
# To create a model with multiple stress periods, we need to define nper,
# perlen, nstp, and steady. This is done in the following block in a manner
# that allows us to pass these variable directly to the discretization object:
nper = 3
perlen = [1, 100, 100]
nstp = [1, 100, 100]
steady = [True, False, False]
# Flopy objects
modelname = "tutorial2"
# ### Create Time-Invariant Flopy Objects
#
# With this information, we can now create the static flopy objects that do
# not change with time:
modelname = "tutorial2_mf"
mf = flopy.modflow.Modflow(modelname, exe_name="mf2005")
dis = flopy.modflow.ModflowDis(
mf,
@ -53,7 +104,18 @@ lpf = flopy.modflow.ModflowLpf(
)
pcg = flopy.modflow.ModflowPcg(mf)
# Make list for stress period 1
# ### Transient General-Head Boundary Package
#
# At this point, our model is ready to add our transient boundary packages.
# First, we will create the GHB object, which is of the following type:
# `flopy.modflow.ModflowGhb()`.
#
# The key to creating Flopy transient boundary packages is recognizing that
# the boundary data is stored in a dictionary with key values equal to the
# zero-based stress period number and values equal to the boundary conditions
# for that stress period. For a GHB the values can be a two-dimensional nested
# list of `[layer, row, column, stage, conductance]`.
stageleft = 10.0
stageright = 10.0
bound_sp1 = []
@ -85,6 +147,11 @@ stress_period_data = {0: bound_sp1, 1: bound_sp2}
# Create the flopy ghb object
ghb = flopy.modflow.ModflowGhb(mf, stress_period_data=stress_period_data)
# ### Transient Well Package
#
# Now we can create the well package object, which is of the type,
# `flopy.modflow.ModflowWel()`.
# Create the well package
# Remember to use zero-based layer, row, column indices!
pumping_rate = -500.0
@ -94,7 +161,11 @@ wel_sp3 = [[0, nrow / 2 - 1, ncol / 2 - 1, pumping_rate]]
stress_period_data = {0: wel_sp1, 1: wel_sp2, 2: wel_sp3}
wel = flopy.modflow.ModflowWel(mf, stress_period_data=stress_period_data)
# Output control
# ### Output Control
#
# Here we create the output control package object, which is of the
# type `flopy.modflow.ModflowOc()`.
stress_period_data = {}
for kper in range(nper):
for kstp in range(nstp[kper]):
@ -109,14 +180,34 @@ oc = flopy.modflow.ModflowOc(
mf, stress_period_data=stress_period_data, compact=True
)
# ## Running the Model
#
# Run the model with the run_model method, which returns a success flag and
# the stream of output. With run_model, we have some finer control, that
# allows us to suppress the output.
# Write the model input files
mf.write_input()
# Run the model
success, mfoutput = mf.run_model(silent=False, pause=False)
success, mfoutput = mf.run_model(silent=True, pause=False)
if not success:
raise Exception("MODFLOW did not terminate normally.")
# ## Post-Processing the Results
#
# Once again, we can read heads from the MODFLOW binary output file, using
# the `flopy.utils.binaryfile()` module. Included with the HeadFile object
# are several methods that we will use here:
# * `get_times()` will return a list of times contained in the binary head file
# * `get_data()` will return a three-dimensional head array for the specified
# time
# * `get_ts()` will return a time series array `[ntimes, headval]` for the
# specified cell
#
# Using these methods, we can create head plots and hydrographs from the
# model results.
# Imports
import matplotlib.pyplot as plt
@ -133,11 +224,13 @@ extent = (delr / 2.0, Lx - delr / 2.0, delc / 2.0, Ly - delc / 2.0)
print("Levels: ", levels)
print("Extent: ", extent)
# Well point
wpt = ((float(ncol / 2) - 0.5) * delr, (float(nrow / 2 - 1) + 0.5) * delc)
# Well point for plotting
wpt = (450.0, 550.0)
# Create a figure with maps for three times
# Make the plots
fig = plt.figure(figsize=(5, 15))
mytimes = [1.0, 101.0, 201.0]
for iplot, time in enumerate(mytimes):
print("*****Processing time: ", time)
@ -152,23 +245,23 @@ for iplot, time in enumerate(mytimes):
frf = cbb.get_data(text="FLOW RIGHT FACE", totim=time)[0]
fff = cbb.get_data(text="FLOW FRONT FACE", totim=time)[0]
# Create the plot
# plt.subplot(1, len(mytimes), iplot + 1, aspect='equal')
plt.subplot(1, 1, 1, aspect="equal")
plt.title("stress period " + str(iplot + 1))
# Create a map for this time
ax = fig.add_subplot(len(mytimes), 1, iplot + 1, aspect="equal")
ax.set_title("stress period " + str(iplot + 1))
modelmap = flopy.plot.ModelMap(model=mf, layer=0)
qm = modelmap.plot_ibound()
lc = modelmap.plot_grid()
qm = modelmap.plot_bc("GHB", alpha=0.5)
cs = modelmap.contour_array(head, levels=levels)
plt.clabel(cs, inline=1, fontsize=10, fmt="%1.1f")
quiver = modelmap.plot_discharge(frf, fff, head=head)
pmv = flopy.plot.PlotMapView(model=mf, layer=0, ax=ax)
qm = pmv.plot_ibound()
lc = pmv.plot_grid()
qm = pmv.plot_bc("GHB", alpha=0.5)
if head.min() != head.max():
cs = pmv.contour_array(head, levels=levels)
plt.clabel(cs, inline=1, fontsize=10, fmt="%1.1f")
quiver = pmv.plot_vector(frf, fff)
mfc = "None"
if (iplot + 1) == len(mytimes):
mfc = "black"
plt.plot(
ax.plot(
wpt[0],
wpt[1],
lw=0,
@ -179,17 +272,17 @@ for iplot, time in enumerate(mytimes):
markerfacecolor=mfc,
zorder=9,
)
plt.text(wpt[0] + 25, wpt[1] - 25, "well", size=12, zorder=12)
plt.savefig("tutorial2-{}.png".format(iplot))
ax.text(wpt[0] + 25, wpt[1] - 25, "well", size=12, zorder=12)
# Create a hydrograph
# Plot the head versus time
idx = (0, int(nrow / 2) - 1, int(ncol / 2) - 1)
ts = headobj.get_ts(idx)
plt.subplot(1, 1, 1)
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(1, 1, 1)
ttl = "Head at cell ({0},{1},{2})".format(idx[0] + 1, idx[1] + 1, idx[2] + 1)
plt.title(ttl)
plt.xlabel("time")
plt.ylabel("head")
plt.plot(ts[:, 0], ts[:, 1], "bo-")
plt.savefig("tutorial2-ts.png")
ax.set_title(ttl)
ax.set_xlabel("time")
ax.set_ylabel("head")
ax.plot(ts[:, 0], ts[:, 1], "bo-")