MODFLOW 6 Tutorial 2: Working with Package Variables¶
This tutorial shows how to view access and change the underlying package variables for MODFLOW 6 objects in flopy. Interaction with a FloPy MODFLOW 6 model is different from other models, such as MODFLOW-2005, MT3D, and SEAWAT, for example.
Package Import¶
[1]:
import os
import numpy as np
import matplotlib.pyplot as plt
import flopy
Create Simple Demonstration Model¶
This tutorial uses a simple demonstration simulation with one GWF Model. The model has 3 layers, 4 rows, and 5 columns. The model is set up to use multiple model layers in order to demonstrate some of the layered functionality in FloPy.
[2]:
name = "tutorial02_mf6"
sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=".")
flopy.mf6.ModflowTdis(
sim, nper=10, perioddata=[[365.0, 1, 1.0] for _ in range(10)]
)
flopy.mf6.ModflowIms(sim)
gwf = flopy.mf6.ModflowGwf(sim, modelname=name, save_flows=True)
flopy.mf6.ModflowGwfdis(gwf, nlay=3, nrow=4, ncol=5)
flopy.mf6.ModflowGwfic(gwf)
flopy.mf6.ModflowGwfnpf(gwf, save_specific_discharge=True)
flopy.mf6.ModflowGwfchd(
gwf, stress_period_data=[[(0, 0, 0), 1.0], [(2, 3, 4), 0.0]]
)
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")],
)
print("Done creating simulation.")
Done creating simulation.
Accessing Simulation-Level Packages, Models, and Model Packages¶
At this point a simulation is available in memory. In this particular case the simulation was created directly using Python code; however, the simulation might also have been loaded from existing model files using the flopy.mf6.MFSimulation.load()
function.
Once a MODFLOW 6 simulation is available in memory, the contents of the simulation object can be listed using a simple print command
[3]:
print(sim)
sim_name = tutorial02_mf6
sim_path = /home/runner/work/flopy/flopy/.docs/.working/.
exe_name = mf6.exe
###################
Package mfsim.nam
###################
package_name = mfsim.nam
filename = mfsim.nam
package_type = nam
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
###################
Package tutorial02_mf6.tdis
###################
package_name = tutorial02_mf6.tdis
filename = tutorial02_mf6.tdis
package_type = tdis
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
###################
Package ims_-1
###################
package_name = ims_-1
filename = tutorial02_mf6.ims
package_type = ims
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
@@@@@@@@@@@@@@@@@@@@
Model tutorial02_mf6
@@@@@@@@@@@@@@@@@@@@
name = tutorial02_mf6
model_type = gwf6
version = mf6
model_relative_path = .
###################
Package dis
###################
package_name = dis
filename = tutorial02_mf6.dis
package_type = dis
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package ic
###################
package_name = ic
filename = tutorial02_mf6.ic
package_type = ic
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package npf
###################
package_name = npf
filename = tutorial02_mf6.npf
package_type = npf
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package chd_0
###################
package_name = chd_0
filename = tutorial02_mf6.chd
package_type = chd
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package oc
###################
package_name = oc
filename = tutorial02_mf6.oc
package_type = oc
model_or_simulation_package = model
model_name = tutorial02_mf6
Simulation-level packages, models, and model packages are shown from the when printing the simulation object. In this case, you should see the all of the contents of sim and some information about each FloPy object that is part of sim.
To get the tdis package and print the contents, we can do the following
[4]:
tdis = sim.tdis
print(tdis)
package_name = tutorial02_mf6.tdis
filename = tutorial02_mf6.tdis
package_type = tdis
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
Block dimensions
--------------------
nper
{internal}
(10)
Block perioddata
--------------------
perioddata
{internal}
([(365., 1, 1.) (365., 1, 1.) (365., 1, 1.) (365., 1, 1.) (365., 1, 1.)
(365., 1, 1.) (365., 1, 1.) (365., 1, 1.) (365., 1, 1.) (365., 1, 1.)])
To get the Iterative Model Solution (IMS) object, we use the following syntax
[5]:
ims = sim.get_package("ims_-1")
print(ims)
package_name = ims_-1
filename = tutorial02_mf6.ims
package_type = ims
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
Or because there is only one IMS object for this simulation, we can access it as
[6]:
ims = sim.get_package("ims")
print(ims)
package_name = ims_-1
filename = tutorial02_mf6.ims
package_type = ims
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
When printing the sim object, there is also a simulation package called nam. This package contains the information that is written to the mfsim.nam file, which is the primary file that MODFLOW 6 reads when it first starts
[7]:
nam = sim.get_package("nam")
print(nam)
package_name = mfsim.nam
filename = mfsim.nam
package_type = nam
model_or_simulation_package = simulation
simulation_name = tutorial02_mf6
Block timing
--------------------
tdis6
{internal}
(tutorial02_mf6.tdis)
Block models
--------------------
models
{internal}
([('gwf6', 'tutorial02_mf6.nam', 'tutorial02_mf6')])
Block solutiongroup
--------------------
solutiongroup
{internal}
([('ims6', 'tutorial02_mf6.ims', 'tutorial02_mf6')])
To see the models that are contained within the simulation, we can get a list of their names as follows
[8]:
print(sim.model_names)
odict_keys(['tutorial02_mf6'])
sim.model_names returns the keys of an ordered dictionary, which isn’t very useful to us, but we can convert that to a list and then go through that list and print information about each model in the simulation. In this case there is only one model, but had there been more models, we would see them listed here
[9]:
model_names = list(sim.model_names)
for mname in model_names:
print(mname)
tutorial02_mf6
If we want to get a model from a simulation, then we use the get_model() method of the sim object. Here we go through all the models in the simulation and print the model name and the model type.
[10]:
model_names = list(sim.model_names)
for mname in model_names:
m = sim.get_model(mname)
print(m.name, m.model_type)
tutorial02_mf6 gwf6
For this simple case here with only one GWF model, we can very easily get the FloPy representation of the GWF model as
[11]:
gwf = sim.get_model("tutorial02_mf6")
Now that we have the gwf object, we can print it, and see what’s it contains.
[12]:
print(gwf)
name = tutorial02_mf6
model_type = gwf6
version = mf6
model_relative_path = .
###################
Package dis
###################
package_name = dis
filename = tutorial02_mf6.dis
package_type = dis
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package ic
###################
package_name = ic
filename = tutorial02_mf6.ic
package_type = ic
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package npf
###################
package_name = npf
filename = tutorial02_mf6.npf
package_type = npf
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package chd_0
###################
package_name = chd_0
filename = tutorial02_mf6.chd
package_type = chd
model_or_simulation_package = model
model_name = tutorial02_mf6
###################
Package oc
###################
package_name = oc
filename = tutorial02_mf6.oc
package_type = oc
model_or_simulation_package = model
model_name = tutorial02_mf6
What we see here is the information that we saw when we printed the sim object.
One of the most common operations on a model is to see what packages are in in and then get packages of interest. A list of packages in a model can obtained as
[13]:
package_list = gwf.get_package_list()
print(package_list)
['DIS', 'IC', 'NPF', 'CHD_0', 'OC']
As you might expect we can access each package in this list with gwf.get_package(). Thus, the following syntax can be used to obtain and print the contents of the Discretization Package
[14]:
dis = gwf.get_package("dis")
print(dis)
package_name = dis
filename = tutorial02_mf6.dis
package_type = dis
model_or_simulation_package = model
model_name = tutorial02_mf6
Block dimensions
--------------------
nlay
{internal}
(3)
nrow
{internal}
(4)
ncol
{internal}
(5)
Block griddata
--------------------
delr
{constant 1.0}
delc
{constant 1.0}
top
{constant 1.0}
botm
{constant 0.0}
The Python type for this dis package is simply
[15]:
print(type(dis))
<class 'flopy.mf6.modflow.mfgwfdis.ModflowGwfdis'>
FloPy MODFLOW 6 Scalar Variables (MFScalar)¶
Once we are able to get any package from a FloPy simulation object, a next step is to be able to access individual package variables. We could see the variables in the dis object when we used the print(dis) command. If we wanted to see information about ncol, for example, we print it as
[16]:
print(dis.ncol)
{internal}
(5)
By doing so, we get information about ncol, but we do not get an integer value. Instead we get a representation of ncol and how it is stored in FloPy. The type of ncol is
[17]:
print(type(dis.ncol))
<class 'flopy.mf6.data.mfdatascalar.MFScalar'>
When we print the of ncol, we see that it is an MFScalar type. This is a specific type of information used by FloPy to represent variables that are stored in in MODFLOW 6 objects.
If we want to get a more useful representation of ncol, then we use the get_data() method, which is available on all of the underlying flopy package variables. Thus, for ncol, we can get the integer value and the type that is returned as
[18]:
ncol = dis.ncol.get_data()
print(ncol)
print(type(ncol))
5
<class 'int'>
FloPy MODFLOW 6 Arrays (MFArray)¶
The dis object also has several arrays stored with it. For example the discretization has botm, which stores the bottom elevation for every model cell in the grid. We can see the type of botm as
[19]:
print(type(dis.botm))
<class 'flopy.mf6.data.mfdataarray.MFArray'>
The MFArray class in flopy is designed to efficiently store array information for MODFLOW 6 variables. In this case, dis.botm has a constant value of zero, which we can see with
[20]:
print(dis.botm)
{constant 0.0}
If we want to get an array representation of dis.botm, we can use
[21]:
print(dis.botm.get_data())
[[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]]
or
[22]:
print(dis.botm.array)
[[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]]
In both of these cases, a full three-dimensional array is created on the fly and provided back to the user. In this particular case, the returned arrays are the same; however, if a factor is assigned to this array, as will be seen later, dis.botm.array will have values with the factor applied whereas dis.botm.get_data() will be the data values stored in the array without the factor applied.
We can see here that the shape of the returned array is correct and is (nlay, nrow, ncol)
[23]:
print(dis.botm.array.shape)
(3, 4, 5)
and that it is a numpy array
[24]:
print(type(dis.botm.array))
<class 'numpy.ndarray'>
We can easily change the value of this array as a constant using the set_data() method
[25]:
dis.botm.set_data(-10)
print(dis.botm)
{constant -10}
Or alternatively, we could call set_data() with an array as long as it is the correct shape
[26]:
shp = dis.botm.array.shape
a = np.arange(shp[0] * shp[1] * shp[2]).reshape(shp)
dis.botm.set_data(a)
print(dis.botm.array)
[[[ 0. 1. 2. 3. 4.]
[ 5. 6. 7. 8. 9.]
[10. 11. 12. 13. 14.]
[15. 16. 17. 18. 19.]]
[[20. 21. 22. 23. 24.]
[25. 26. 27. 28. 29.]
[30. 31. 32. 33. 34.]
[35. 36. 37. 38. 39.]]
[[40. 41. 42. 43. 44.]
[45. 46. 47. 48. 49.]
[50. 51. 52. 53. 54.]
[55. 56. 57. 58. 59.]]]
We even have an option to see how dis.botm will be written to the MODFLOW 6 Discretization input file
[27]:
print(dis.botm.get_file_entry())
botm
INTERNAL FACTOR 1.0
0.00000000 1.00000000 2.00000000 3.00000000 4.00000000
5.00000000 6.00000000 7.00000000 8.00000000 9.00000000
10.00000000 11.00000000 12.00000000 13.00000000 14.00000000
15.00000000 16.00000000 17.00000000 18.00000000 19.00000000
20.00000000 21.00000000 22.00000000 23.00000000 24.00000000
25.00000000 26.00000000 27.00000000 28.00000000 29.00000000
30.00000000 31.00000000 32.00000000 33.00000000 34.00000000
35.00000000 36.00000000 37.00000000 38.00000000 39.00000000
40.00000000 41.00000000 42.00000000 43.00000000 44.00000000
45.00000000 46.00000000 47.00000000 48.00000000 49.00000000
50.00000000 51.00000000 52.00000000 53.00000000 54.00000000
55.00000000 56.00000000 57.00000000 58.00000000 59.00000000
Layered Data¶
When we look at what will be written to the discretization input file, we see that the entire dis.botm array is written as one long array with the number of values equal to nlay * nrow * ncol. And this whole-array specification may be of use in some cases. Often times, however, it is easier to work with each layer separately. An MFArray object, such as dis.botm can be set to be a layered array as follows
[28]:
dis.botm.make_layered()
By changing dis.botm to layered, we are then able to manage each layer separately. Before doing so, however, we need to pass in data that can be separated into three layers. An array of the correct size is one option
[29]:
a = np.arange(shp[0] * shp[1] * shp[2]).reshape(shp)
dis.botm.set_data(a)
Now that dis.botm has been set to be layered, if we print information about it, we see that each layer is stored separately, however, dis.botm.array will still return a full three-dimensional array.
[30]:
print(type(dis.botm))
print(dis.botm)
<class 'flopy.mf6.data.mfdataarray.MFArray'>
Layer_1{internal}
([[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]])
Layer_2{internal}
([[20 21 22 23 24]
[25 26 27 28 29]
[30 31 32 33 34]
[35 36 37 38 39]])
Layer_3{internal}
([[40 41 42 43 44]
[45 46 47 48 49]
[50 51 52 53 54]
[55 56 57 58 59]])
We also see that each layer is printed separately to the Discretization Package input file, and that the LAYERED keyword is activated:
[31]:
print(dis.botm.get_file_entry())
botm LAYERED
INTERNAL FACTOR 1.0
0.00000000 1.00000000 2.00000000 3.00000000 4.00000000
5.00000000 6.00000000 7.00000000 8.00000000 9.00000000
10.00000000 11.00000000 12.00000000 13.00000000 14.00000000
15.00000000 16.00000000 17.00000000 18.00000000 19.00000000
INTERNAL FACTOR 1.0
20.00000000 21.00000000 22.00000000 23.00000000 24.00000000
25.00000000 26.00000000 27.00000000 28.00000000 29.00000000
30.00000000 31.00000000 32.00000000 33.00000000 34.00000000
35.00000000 36.00000000 37.00000000 38.00000000 39.00000000
INTERNAL FACTOR 1.0
40.00000000 41.00000000 42.00000000 43.00000000 44.00000000
45.00000000 46.00000000 47.00000000 48.00000000 49.00000000
50.00000000 51.00000000 52.00000000 53.00000000 54.00000000
55.00000000 56.00000000 57.00000000 58.00000000 59.00000000
Working with a layered array provides lots of flexibility. For example, constants can be set for some layers, but arrays for others:
[32]:
dis.botm.set_data([-1, -a[2], -200])
print(dis.botm.get_file_entry())
botm LAYERED
CONSTANT -1.00000000
INTERNAL FACTOR 1.0
-40.00000000 -41.00000000 -42.00000000 -43.00000000 -44.00000000
-45.00000000 -46.00000000 -47.00000000 -48.00000000 -49.00000000
-50.00000000 -51.00000000 -52.00000000 -53.00000000 -54.00000000
-55.00000000 -56.00000000 -57.00000000 -58.00000000 -59.00000000
CONSTANT -200.00000000
To gain full control over an individual layers, layer information can be provided as a dictionary:
[33]:
a0 = {"factor": 0.5, "iprn": 1, "data": np.ones((4, 5))}
a1 = -100
a2 = {"factor": 1.0, "iprn": 14, "data": -100 * np.ones((4, 5))}
dis.botm.set_data([a0, a1, a2])
print(dis.botm.get_file_entry())
botm LAYERED
INTERNAL FACTOR 0.5 IPRN 1
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
CONSTANT -100.00000000
INTERNAL FACTOR 1.0 IPRN 14
-100.00000000 -100.00000000 -100.00000000 -100.00000000 -100.00000000
-100.00000000 -100.00000000 -100.00000000 -100.00000000 -100.00000000
-100.00000000 -100.00000000 -100.00000000 -100.00000000 -100.00000000
-100.00000000 -100.00000000 -100.00000000 -100.00000000 -100.00000000
Here we say that the FACTOR has been set to 0.5 for the first layer and an alternative print flag is set for the last layer.
Because we are specifying a factor for the top layer, we can also see that the get_data() method returns the array without the factor applied
[34]:
print(dis.botm.get_data())
[[[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]]
[[-100. -100. -100. -100. -100.]
[-100. -100. -100. -100. -100.]
[-100. -100. -100. -100. -100.]
[-100. -100. -100. -100. -100.]]
[[-100. -100. -100. -100. -100.]
[-100. -100. -100. -100. -100.]
[-100. -100. -100. -100. -100.]
[-100. -100. -100. -100. -100.]]]
whereas .array returns the array with the factor applied
[35]:
print(dis.botm.array)
[[[ 0.5 0.5 0.5 0.5 0.5]
[ 0.5 0.5 0.5 0.5 0.5]
[ 0.5 0.5 0.5 0.5 0.5]
[ 0.5 0.5 0.5 0.5 0.5]]
[[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]]
[[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]]]
External Files¶
If we want to store the bottom elevations for the bottom layer to an external file, then the dictionary that we pass in for the third layer can be given a filename keyword
[36]:
a0 = {"factor": 0.5, "iprn": 1, "data": np.ones((4, 5))}
a1 = -100
a2 = {
"filename": "dis.botm.3.txt",
"factor": 2.0,
"iprn": 1,
"data": -100 * np.ones((4, 5)),
}
dis.botm.set_data([a0, a1, a2])
print(dis.botm.get_file_entry())
botm LAYERED
INTERNAL FACTOR 0.5 IPRN 1
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
CONSTANT -100.00000000
OPEN/CLOSE 'dis.botm.3.txt' FACTOR 2.0 IPRN 1
And we can even have our data be stored in binary format by adding a ‘binary’ key to the layer dictionary and setting it’s value to True.
[37]:
a0 = {"factor": 0.5, "iprn": 1, "data": np.ones((4, 5))}
a1 = -100
a2 = {
"filename": "dis.botm.3.bin",
"factor": 2.0,
"iprn": 1,
"data": -100 * np.ones((4, 5)),
"binary": True,
}
dis.botm.set_data([a0, a1, a2])
print(dis.botm.get_file_entry())
print(dis.botm.array)
botm LAYERED
INTERNAL FACTOR 0.5 IPRN 1
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
1.00000000 1.00000000 1.00000000 1.00000000 1.00000000
CONSTANT -100.00000000
OPEN/CLOSE 'dis.botm.3.bin' FACTOR 2.0 (BINARY) IPRN 1
[[[ 0.5 0.5 0.5 0.5 0.5]
[ 0.5 0.5 0.5 0.5 0.5]
[ 0.5 0.5 0.5 0.5 0.5]
[ 0.5 0.5 0.5 0.5 0.5]]
[[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]
[-100. -100. -100. -100. -100. ]]
[[-200. -200. -200. -200. -200. ]
[-200. -200. -200. -200. -200. ]
[-200. -200. -200. -200. -200. ]
[-200. -200. -200. -200. -200. ]]]
Note that we could have also specified botm this way as part of the original flopy.mf6.ModflowGwfdis constructor:
[38]:
a0 = {"factor": 0.5, "iprn": 1, "data": np.ones((4, 5))}
a1 = -100
a2 = {
"filename": "dis.botm.3.bin",
"factor": 2.0,
"iprn": 1,
"data": -100 * np.ones((4, 5)),
"binary": True,
}
botm = [a0, a1, a2]
flopy.mf6.ModflowGwfdis(gwf, nlay=3, nrow=4, ncol=5, botm=botm)
WARNING: Package with type dis already exists. Replacing existing package.
[38]:
package_name = dis
filename = tutorial02_mf6.dis
package_type = dis
model_or_simulation_package = model
model_name = tutorial02_mf6
Block dimensions
--------------------
nlay
{internal}
(3)
nrow
{internal}
(4)
ncol
{internal}
(5)
Block griddata
--------------------
delr
{constant 1.0}
delc
{constant 1.0}
top
{constant 1.0}
botm
Layer_1{internal, factor 0.5, iprn 1}
(array([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]]))
Layer_2{constant -100}
Stress Period Data (MFTransientList)¶
Data that varies during a simulation is often stored in flopy in a special data structured call an MFTransientList. We can see how one of these behaves by looking at the stress period data for the constant head package. We can access the constant head package by getting it from the GWF model using the package name:
[39]:
chd = gwf.get_package("chd_0")
print(chd)
package_name = chd_0
filename = tutorial02_mf6.chd
package_type = chd
model_or_simulation_package = model
model_name = tutorial02_mf6
Block period
--------------------
stress_period_data
{internal}
([((0, 0, 0), 1.) ((2, 3, 4), 0.)])
[40]:
# We can now look at the type and contents of the stress period data
print(type(chd.stress_period_data))
print(chd.stress_period_data)
<class 'flopy.mf6.data.mfdatalist.MFTransientList'>
{internal}
([((0, 0, 0), 1.) ((2, 3, 4), 0.)])
We can get a dictionary of the stress period data using the get_data() method:
[41]:
spd = chd.stress_period_data.get_data()
print(spd)
{0: rec.array([((0, 0, 0), 1.), ((2, 3, 4), 0.)],
dtype=[('cellid', 'O'), ('head', '<f8')])}
Here we see that they key in the dictionary is the stress period number, which is zero based and the value in the dictionary is a numpy recarray, which is a numpy array that is optimized to store columns of infomration. The first column contains a tuple, which is the layer, row, and column number. The second column contains the head value.
more to come…
Time Series¶
more to come…
Observations¶
more to come…
Activating External Output¶
more to come…
Plotting¶
more to come…