Example: Head Controlled Well¶
This example demonstrates how pymf6
can be used to control the rate of an
extraction well so that the well water level does fall below under a given water
level.
Setting up a model with flopy
¶
First, we create a simple model with flopy
.
You may use any GUI tool such as ModelMuse to create such a model.
This is not specific to pymf6
but helps to better understand the problem.
You may skip ahead to the section with use of pymf6
.
Creating the input data¶
First, we import a few need libraries:
"""Create, run, and postprocess a MODFLOW 6 model with flopy.
"""
import flopy
from flopy.utils.postprocessing import get_specific_discharge
from matplotlib import pyplot as plt
import numpy as np
The function make_input
creates our model with help pf flopy
:
def _make_wel_stress_period(gwf, wel_q, wel_coords):
"""Create stress period data for the wel package."""
period = flopy.mf6.ModflowGwfwel.stress_period_data.empty(
gwf,
maxbound=1,
)
period[0][0] = (wel_coords, wel_q)
return period
def make_input(
wel_q,
wel_coords,
model_path,
name,
exe_name='mf6',
verbosity_level=0):
"""Create MODFLOW 6 input"""
sim = flopy.mf6.MFSimulation(
sim_name=name,
sim_ws=model_path,
exe_name=exe_name,
verbosity_level=verbosity_level,
)
times = (10.0, 120, 1.0)
tdis_rc = [(1.0, 1, 1.0), times, times, times]
flopy.mf6.ModflowTdis(
sim, pname="tdis",
time_units="DAYS",
nper=4,
perioddata=tdis_rc,
)
flopy.mf6.ModflowIms(sim)
gwf = flopy.mf6.ModflowGwf(sim, modelname=name, save_flows=True)
flopy.mf6.ModflowGwfdis(gwf, nrow=10, ncol=10)
flopy.mf6.ModflowGwfic(gwf)
flopy.mf6.ModflowGwfnpf(
gwf,
save_flows=True,
save_specific_discharge=True,
icelltype=[0],
k=[0.5],
k33=[0.1],
)
sy = flopy.mf6.ModflowGwfsto.sy.empty(
gwf,
default_value=0.2
)
ss = flopy.mf6.ModflowGwfsto.ss.empty(
gwf, default_value=0.000001
)
flopy.mf6.ModflowGwfsto(
gwf,
pname="sto",
save_flows=True,
save_specific_discharge=True,
iconvert=1,
ss=ss,
sy=sy,
transient={0: True},
)
stress_period_data = {
1: _make_wel_stress_period(gwf, wel_q / 10, wel_coords)[0],
2: _make_wel_stress_period(gwf, wel_q, wel_coords)[0],
3: _make_wel_stress_period(gwf, wel_q / 10, wel_coords)[0]
}
flopy.mf6.ModflowGwfwel(
gwf,
stress_period_data=stress_period_data,
)
flopy.mf6.ModflowGwfchd(
gwf,
stress_period_data=[
[(0, 0, 0), 1.],
[(0, 9, 9), 1.]],
)
budget_file = name + '.bud'
head_file = name + '.hds'
flopy.mf6.ModflowGwfoc(
gwf,
budget_filerecord=budget_file,
head_filerecord=head_file,
saverecord=[('HEAD', 'ALL'), ('BUDGET', 'ALL')])
sim.write_simulation()
We specify some data for the model:
model_path=r'models\mf6'
name='headconwell'
wel_coords=(0, 4, 4)
wel_q = -0.5
and create the input files.
make_input(
wel_q=wel_q,
wel_coords=wel_coords,
model_path=model_path,
name=name
)
Running the model¶
The function get_simulation
retrieves a flopy
simulation object:
def get_simulation(model_path, exe_name='mf6', verbosity_level=0):
"""Get simulation for a model."""
sim = flopy.mf6.MFSimulation.load(
sim_ws=model_path,
exe_name=exe_name,
verbosity_level=verbosity_level,
)
return sim
This function runs the model:
def run_simulation(model_path, verbosity_level=0):
"""Run a MODFLOW 6 model"""
sim = get_simulation(
model_path,
verbosity_level=verbosity_level)
sim.run_simulation()
We run the simulation:
run_simulation(model_path=model_path)
There is visible output from MODFLOW 6.
Setting the verbosity_level
to 1
or higher in run_simulation
will show
the simulations steps.
Looking at the model results¶
Let’s look at the model results. This function visualizes the head at the end of the last time step:
def show_heads(model_path, name):
"""Plot calculated heads along with flow vector."""
sim = get_simulation(model_path, name)
gwf = sim.get_model(name)
head = gwf.output.head().get_data()
bud = gwf.output.budget()
spdis = bud.get_data(text='DATA-SPDIS')[-1]
qx, qy, _ = get_specific_discharge(spdis, gwf)
pmv = flopy.plot.PlotMapView(gwf)
pmv.plot_array(head)
pmv.plot_grid(colors='white')
pmv.contour_array(
head,
levels=np.arange(0.2, 1.0, 0.02),
)
pmv.plot_vector(qx, qy, normalize=True, color="white")
We look at the heads:
show_heads(model_path=model_path, name=name)
The well is roughly in the middle of the model. The yellow cells have a constant water level and “feed” the model. The well causes a cone of depression.
This function shows the head the well location over time:
def show_well_head(model_path, name, wel_coords):
"""Plot head at well over time."""
sim = get_simulation(model_path, name)
gwf = sim.get_model(name)
heads = gwf.output.head().get_ts(wel_coords)
_, ax = plt.subplots()
ax.plot(heads[:, 0], heads[:, 1])
This is the head at the well over time:
show_well_head(model_path=model_path, name=name, wel_coords=wel_coords)
There are four stress periods. The first period is steady state. Periods two to four a transient with different extraction rate. Period three has the full rate and periods two and four have only a tenth of the rate. See generation of the input files for details.
Working with pymf6
¶
Finally, we will use pymf6
.
The objective is the avoid a drop of the water level at the well below a given
limit.
Creating new input files¶
We generate the input files again. There are no differences in the files to the model above. The only difference is the model directory:
model_path=r'models\pymf6'
Creating the input files:
make_input(
wel_q=wel_q,
wel_coords=wel_coords,
model_path=model_path,
name=name
)
Run the model with pymf6
¶
Now run the model with pymf6
:
from pymf6.mf6 import MF6
def run_model(nam_file):
mf6 = MF6(nam_file=nam_file)
head = mf6.vars['SLN_1/X']
wel_index = 44
tolerance = 0.01
head_limit = 0.5
upper_limit = head_limit + tolerance
lower_limit = head_limit - tolerance
wel = mf6.vars['HEADCONWELL/WEL_0/BOUND']
been_below = False
for step in mf6.steps():
if step < 21:
if head[wel_index] <= upper_limit:
wel[:] = wel[:] * 0.9
been_below = True
elif been_below and head[wel_index] >= upper_limit:
wel[:] = wel[:] * 1.1
The class MF6
provides the functionality of pymf6
.
The dictionary mf6.vars
provides all MODFLOW 6 variables.
The head has the name SLN_1/X
and the values of the boundary condition WEL
is HEADCONWELL/WEL_0/BOUND
.
The name will change depending on model name and the chosen name for the
boundary condition.
mf6.steps()
provides a generator that allows to iterate over all MODFLOW 6
time steps.
step
is the current time step in the unit specified in the input files, here
DAYS
.
We don’t want influence the last, which starts at day 21.
We adjust the extraction rate by reducing o increasing the rate by 10%,
if the water level at the well, found at index 44, is not within the upper
and lower limit.
Running the model:
run_model(r'models/pymf6/mfsim.nam')
Looking at the results¶
Now the water level stays just around 0.5:
show_well_head(model_path=model_path, name=name, wel_coords=wel_coords)
There is some oscillation in the water level. These can be reduced by adding more time steps in the TDIS package.