flopy 3.2.1__zip → 3.2.2__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flopy-3.2.1 → flopy-3.2.2}/PKG-INFO +47 -11
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mbase.py +307 -17
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mf.py +8 -6
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfbcf.py +2 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfchd.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfde4.py +2 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfdis.py +11 -94
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfdrn.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfevt.py +3 -3
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfghb.py +2 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfgmg.py +60 -19
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfhfb.py +7 -7
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mflpf.py +13 -10
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmlt.py +3 -3
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfnwt.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfoc.py +26 -20
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfoc88.py +9 -9
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpar.py +15 -9
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfparbc.py +7 -7
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpcg.py +2 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpcgn.py +3 -3
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpks.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfrch.py +4 -4
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfriv.py +6 -4
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfsip.py +2 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfsor.py +3 -3
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfswi2.py +34 -34
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfswr1.py +4 -4
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfupw.py +4 -4
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfuzf1.py +5 -5
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfwel.py +1 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfzon.py +2 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/mp.py +7 -5
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/mpbas.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/mpsim.py +18 -16
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mt.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtbtn.py +3 -3
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtssm.py +7 -6
- {flopy-3.2.1 → flopy-3.2.2}/flopy/plot/__init__.py +1 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/plot/crosssection.py +73 -103
- flopy-3.2.2/flopy/plot/map.py +647 -0
- flopy-3.2.2/flopy/plot/plotutil.py +1205 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/__init__.py +3 -1
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/binaryfile.py +91 -364
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/binaryhydmodfile.py +3 -3
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/binaryswrfile.py +21 -21
- flopy-3.2.2/flopy/utils/datafile.py +476 -0
- flopy-3.2.2/flopy/utils/flopy_io.py +128 -0
- flopy-3.2.2/flopy/utils/formattedfile.py +366 -0
- flopy-3.2.2/flopy/utils/modpathfile.py +421 -0
- flopy-3.2.2/flopy/utils/reference.py +386 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/util_array.py +512 -83
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/util_list.py +234 -13
- flopy-3.2.2/flopy/version.py +4 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/PKG-INFO +47 -11
- {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/SOURCES.txt +5 -0
- {flopy-3.2.1 → flopy-3.2.2}/setup.py +2 -6
- flopy-3.2.1/flopy/plot/map.py +0 -592
- flopy-3.2.1/flopy/plot/plotutil.py +0 -512
- flopy-3.2.1/flopy/version.py +0 -4
- flopy-3.2.1/requirements.txt +0 -2
- {flopy-3.2.1 → flopy-3.2.2}/flopy/__init__.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/__init__.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfaddoutsidefile.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfbas.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfbct.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mflmt.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmnw1.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmnw2.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmnwi.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpbc.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpval.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfsms.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfswi.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/__init__.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/__init__.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtadv.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtdsp.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtgcg.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtphc.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtrct.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mttob.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/seawat/__init__.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/seawat/swt.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/seawat/swtvdf.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/mfreadnam.py +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/dependency_links.txt +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/requires.txt +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/top_level.txt +0 -0
- {flopy-3.2.1 → flopy-3.2.2}/setup.cfg +0 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import matplotlib.colors
|
|
5
|
+
from . import plotutil
|
|
6
|
+
from .plotutil import bc_color_dict
|
|
7
|
+
|
|
8
|
+
from flopy.utils import util_2d, util_3d, transient_2d
|
|
9
|
+
|
|
10
|
+
class ModelMap(object):
|
|
11
|
+
"""
|
|
12
|
+
Class to create a map of the model.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
sr : flopy.utils.reference.SpatialReference
|
|
17
|
+
The spatial reference class (Default is None)
|
|
18
|
+
ax : matplotlib.pyplot axis
|
|
19
|
+
The plot axis. If not provided it, plt.gca() will be used.
|
|
20
|
+
If there is not a current axis then a new one will be created.
|
|
21
|
+
model : flopy.modflow object
|
|
22
|
+
flopy model object. (Default is None)
|
|
23
|
+
dis : flopy.modflow.ModflowDis object
|
|
24
|
+
flopy discretization object. (Default is None)
|
|
25
|
+
layer : int
|
|
26
|
+
Layer to plot. Default is 0. Must be between 0 and nlay - 1.
|
|
27
|
+
xul : float
|
|
28
|
+
x coordinate for upper left corner
|
|
29
|
+
yul : float
|
|
30
|
+
y coordinate for upper left corner. The default is the sum of the
|
|
31
|
+
delc array.
|
|
32
|
+
rotation : float
|
|
33
|
+
Angle of grid rotation around the upper left corner. A positive value
|
|
34
|
+
indicates clockwise rotation. Angles are in degrees.
|
|
35
|
+
extent : tuple of floats
|
|
36
|
+
(xmin, xmax, ymin, ymax) will be used to specify axes limits. If None
|
|
37
|
+
then these will be calculated based on grid, coordinates, and rotation.
|
|
38
|
+
|
|
39
|
+
Notes
|
|
40
|
+
-----
|
|
41
|
+
ModelMap must know the position and rotation of the grid in order to make
|
|
42
|
+
the plot. This information is contained in the SpatialReference class
|
|
43
|
+
(sr), which can be passed. If sr is None, then it looks for sr in dis.
|
|
44
|
+
If dis is None, then it looks for sr in model.dis. If all of these
|
|
45
|
+
arguments are none, then it uses xul, yul, and rotation. If none of these
|
|
46
|
+
arguments are provided, then it puts the lower-left-hand corner of the
|
|
47
|
+
grid at (0, 0).
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
def __init__(self, sr=None, ax=None, model=None, dis=None, layer=0,
|
|
51
|
+
extent=None, xul=None, yul=None, rotation=None):
|
|
52
|
+
self.model = model
|
|
53
|
+
self.layer = layer
|
|
54
|
+
self.dis = dis
|
|
55
|
+
self.sr = None
|
|
56
|
+
if sr is not None:
|
|
57
|
+
self.sr = copy.deepcopy(sr)
|
|
58
|
+
elif dis is not None:
|
|
59
|
+
#print("warning: the dis arg to model map is deprecated")
|
|
60
|
+
self.sr = copy.deepcopy(dis.sr)
|
|
61
|
+
elif model is not None:
|
|
62
|
+
#print("warning: the model arg to model map is deprecated")
|
|
63
|
+
self.sr = copy.deepcopy(model.dis.sr)
|
|
64
|
+
|
|
65
|
+
# model map override spatial reference settings
|
|
66
|
+
if xul is not None:
|
|
67
|
+
self.sr.xul = xul
|
|
68
|
+
if yul is not None:
|
|
69
|
+
self.sr.yul = yul
|
|
70
|
+
if rotation is not None:
|
|
71
|
+
self.sr.rotation = rotation
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if ax is None:
|
|
75
|
+
try:
|
|
76
|
+
self.ax = plt.gca()
|
|
77
|
+
self.ax.set_aspect('equal')
|
|
78
|
+
except:
|
|
79
|
+
self.ax = plt.subplot(1, 1, 1, aspect='equal', axisbg="white")
|
|
80
|
+
else:
|
|
81
|
+
self.ax = ax
|
|
82
|
+
if extent is not None:
|
|
83
|
+
self._extent = extent
|
|
84
|
+
else:
|
|
85
|
+
self._extent = None
|
|
86
|
+
|
|
87
|
+
# why is this non-default color scale used??
|
|
88
|
+
# This should be passed as a kwarg by the user to the indivudual plotting method.
|
|
89
|
+
#self.cmap = plotutil.viridis
|
|
90
|
+
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def extent(self):
|
|
95
|
+
if self._extent is None:
|
|
96
|
+
self._extent = self.sr.get_extent()
|
|
97
|
+
return self._extent
|
|
98
|
+
|
|
99
|
+
def plot_array(self, a, masked_values=None, **kwargs):
|
|
100
|
+
"""
|
|
101
|
+
Plot an array. If the array is three-dimensional, then the method
|
|
102
|
+
will plot the layer tied to this class (self.layer).
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
a : numpy.ndarray
|
|
107
|
+
Array to plot.
|
|
108
|
+
masked_values : iterable of floats, ints
|
|
109
|
+
Values to mask.
|
|
110
|
+
**kwargs : dictionary
|
|
111
|
+
keyword arguments passed to matplotlib.pyplot.pcolormesh
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
quadmesh : matplotlib.collections.QuadMesh
|
|
116
|
+
"""
|
|
117
|
+
if a.ndim == 3:
|
|
118
|
+
plotarray = a[self.layer, :, :]
|
|
119
|
+
elif a.ndim == 2:
|
|
120
|
+
plotarray = a
|
|
121
|
+
else:
|
|
122
|
+
raise Exception('Array must be of dimension 2 or 3')
|
|
123
|
+
if masked_values is not None:
|
|
124
|
+
for mval in masked_values:
|
|
125
|
+
plotarray = np.ma.masked_equal(plotarray, mval)
|
|
126
|
+
if 'ax' in kwargs:
|
|
127
|
+
ax = kwargs.pop('ax')
|
|
128
|
+
else:
|
|
129
|
+
ax = self.ax
|
|
130
|
+
quadmesh = ax.pcolormesh(self.sr.xgrid, self.sr.ygrid, plotarray,
|
|
131
|
+
**kwargs)
|
|
132
|
+
ax.set_xlim(self.extent[0], self.extent[1])
|
|
133
|
+
ax.set_ylim(self.extent[2], self.extent[3])
|
|
134
|
+
return quadmesh
|
|
135
|
+
|
|
136
|
+
def contour_array(self, a, masked_values=None, **kwargs):
|
|
137
|
+
"""
|
|
138
|
+
Contour an array. If the array is three-dimensional, then the method
|
|
139
|
+
will contour the layer tied to this class (self.layer).
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
a : numpy.ndarray
|
|
144
|
+
Array to plot.
|
|
145
|
+
masked_values : iterable of floats, ints
|
|
146
|
+
Values to mask.
|
|
147
|
+
**kwargs : dictionary
|
|
148
|
+
keyword arguments passed to matplotlib.pyplot.pcolormesh
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
contour_set : matplotlib.pyplot.contour
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
if a.ndim == 3:
|
|
156
|
+
plotarray = a[self.layer, :, :]
|
|
157
|
+
elif a.ndim == 2:
|
|
158
|
+
plotarray = a
|
|
159
|
+
else:
|
|
160
|
+
raise Exception('Array must be of dimension 2 or 3')
|
|
161
|
+
if masked_values is not None:
|
|
162
|
+
for mval in masked_values:
|
|
163
|
+
plotarray = np.ma.masked_equal(plotarray, mval)
|
|
164
|
+
if 'ax' in kwargs:
|
|
165
|
+
ax = kwargs.pop('ax')
|
|
166
|
+
else:
|
|
167
|
+
ax = self.ax
|
|
168
|
+
if 'colors' in kwargs.keys():
|
|
169
|
+
if 'cmap' in kwargs.keys():
|
|
170
|
+
cmap = kwargs.pop('cmap')
|
|
171
|
+
cmap = None
|
|
172
|
+
contour_set = ax.contour(self.sr.xcentergrid, self.sr.ycentergrid,
|
|
173
|
+
plotarray, **kwargs)
|
|
174
|
+
ax.set_xlim(self.extent[0], self.extent[1])
|
|
175
|
+
ax.set_ylim(self.extent[2], self.extent[3])
|
|
176
|
+
|
|
177
|
+
return contour_set
|
|
178
|
+
|
|
179
|
+
def plot_inactive(self, ibound=None, color_noflow='black', **kwargs):
|
|
180
|
+
"""
|
|
181
|
+
Make a plot of inactive cells. If not specified, then pull ibound from the
|
|
182
|
+
self.ml
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
ibound : numpy.ndarray
|
|
187
|
+
ibound array to plot. (Default is ibound in 'BAS6' package.)
|
|
188
|
+
color_noflow : string
|
|
189
|
+
(Default is 'black')
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
quadmesh : matplotlib.collections.QuadMesh
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
if 'ax' in kwargs:
|
|
197
|
+
ax = kwargs.pop('ax')
|
|
198
|
+
else:
|
|
199
|
+
ax = self.ax
|
|
200
|
+
|
|
201
|
+
if ibound is None:
|
|
202
|
+
bas = self.model.get_package('BAS6')
|
|
203
|
+
ibound = bas.ibound
|
|
204
|
+
|
|
205
|
+
plotarray = np.zeros(ibound.shape, dtype=np.int)
|
|
206
|
+
idx1 = (ibound == 0)
|
|
207
|
+
plotarray[idx1] = 1
|
|
208
|
+
plotarray = np.ma.masked_equal(plotarray, 0)
|
|
209
|
+
cmap = matplotlib.colors.ListedColormap(['0', color_noflow])
|
|
210
|
+
bounds = [0, 1, 2]
|
|
211
|
+
norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)
|
|
212
|
+
quadmesh = self.plot_array(plotarray, cmap=cmap, norm=norm, **kwargs)
|
|
213
|
+
return quadmesh
|
|
214
|
+
|
|
215
|
+
def plot_ibound(self, ibound=None, color_noflow='black', color_ch='blue',
|
|
216
|
+
**kwargs):
|
|
217
|
+
"""
|
|
218
|
+
Make a plot of ibound. If not specified, then pull ibound from the
|
|
219
|
+
self.ml
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
ibound : numpy.ndarray
|
|
224
|
+
ibound array to plot. (Default is ibound in 'BAS6' package.)
|
|
225
|
+
color_noflow : string
|
|
226
|
+
(Default is 'black')
|
|
227
|
+
color_ch : string
|
|
228
|
+
Color for constant heads (Default is 'blue'.)
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
quadmesh : matplotlib.collections.QuadMesh
|
|
233
|
+
|
|
234
|
+
"""
|
|
235
|
+
if 'ax' in kwargs:
|
|
236
|
+
ax = kwargs.pop('ax')
|
|
237
|
+
else:
|
|
238
|
+
ax = self.ax
|
|
239
|
+
|
|
240
|
+
if ibound is None:
|
|
241
|
+
bas = self.model.get_package('BAS6')
|
|
242
|
+
ibound = bas.ibound
|
|
243
|
+
plotarray = np.zeros(ibound.shape, dtype=np.int)
|
|
244
|
+
idx1 = (ibound == 0)
|
|
245
|
+
idx2 = (ibound < 0)
|
|
246
|
+
plotarray[idx1] = 1
|
|
247
|
+
plotarray[idx2] = 2
|
|
248
|
+
plotarray = np.ma.masked_equal(plotarray, 0)
|
|
249
|
+
cmap = matplotlib.colors.ListedColormap(['0', color_noflow, color_ch])
|
|
250
|
+
bounds=[0, 1, 2, 3]
|
|
251
|
+
norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)
|
|
252
|
+
quadmesh = self.plot_array(plotarray, cmap=cmap, norm=norm, **kwargs)
|
|
253
|
+
return quadmesh
|
|
254
|
+
|
|
255
|
+
def plot_grid(self, **kwargs):
|
|
256
|
+
"""
|
|
257
|
+
Plot the grid lines.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
kwargs : ax, colors. The remaining kwargs are passed into the
|
|
262
|
+
the LineCollection constructor.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
lc : matplotlib.collections.LineCollection
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
if 'ax' in kwargs:
|
|
270
|
+
ax = kwargs.pop('ax')
|
|
271
|
+
else:
|
|
272
|
+
ax = self.ax
|
|
273
|
+
|
|
274
|
+
if 'colors' not in kwargs:
|
|
275
|
+
kwargs['colors'] = '0.5'
|
|
276
|
+
|
|
277
|
+
lc = self.get_grid_line_collection(**kwargs)
|
|
278
|
+
ax.add_collection(lc)
|
|
279
|
+
ax.set_xlim(self.extent[0], self.extent[1])
|
|
280
|
+
ax.set_ylim(self.extent[2], self.extent[3])
|
|
281
|
+
|
|
282
|
+
return lc
|
|
283
|
+
|
|
284
|
+
def plot_bc(self, ftype=None, package=None, kper=0, color=None, **kwargs):
|
|
285
|
+
"""
|
|
286
|
+
Plot boundary conditions locations for a specific boundary
|
|
287
|
+
type from a flopy model
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
ftype : string
|
|
292
|
+
Package name string ('WEL', 'GHB', etc.). (Default is None)
|
|
293
|
+
package : flopy.modflow.Modflow package class instance
|
|
294
|
+
flopy package class instance. (Default is None)
|
|
295
|
+
kper : int
|
|
296
|
+
Stress period to plot
|
|
297
|
+
color : string
|
|
298
|
+
matplotlib color string. (Default is None)
|
|
299
|
+
**kwargs : dictionary
|
|
300
|
+
keyword arguments passed to matplotlib.collections.PatchCollection
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
quadmesh : matplotlib.collections.QuadMesh
|
|
305
|
+
|
|
306
|
+
"""
|
|
307
|
+
if 'ax' in kwargs:
|
|
308
|
+
ax = kwargs.pop('ax')
|
|
309
|
+
else:
|
|
310
|
+
ax = self.ax
|
|
311
|
+
|
|
312
|
+
# Find package to plot
|
|
313
|
+
if package is not None:
|
|
314
|
+
p = package
|
|
315
|
+
elif self.model is not None:
|
|
316
|
+
if ftype is None:
|
|
317
|
+
raise Exception('ftype not specified')
|
|
318
|
+
p = self.model.get_package(ftype)
|
|
319
|
+
else:
|
|
320
|
+
raise Exception('Cannot find package to plot')
|
|
321
|
+
|
|
322
|
+
# Get the list data
|
|
323
|
+
try:
|
|
324
|
+
mflist = p.stress_period_data[kper]
|
|
325
|
+
except:
|
|
326
|
+
raise Exception('Not a list-style boundary package')
|
|
327
|
+
|
|
328
|
+
# Return if mflist is None
|
|
329
|
+
if mflist is None:
|
|
330
|
+
return None
|
|
331
|
+
nlay = self.model.nlay
|
|
332
|
+
# Plot the list locations
|
|
333
|
+
plotarray = np.zeros((nlay, self.sr.nrow, self.sr.ncol), dtype=np.int)
|
|
334
|
+
idx = [mflist['k'], mflist['i'], mflist['j']]
|
|
335
|
+
plotarray[idx] = 1
|
|
336
|
+
plotarray = np.ma.masked_equal(plotarray, 0)
|
|
337
|
+
if color is None:
|
|
338
|
+
if ftype in bc_color_dict:
|
|
339
|
+
c = bc_color_dict[ftype]
|
|
340
|
+
else:
|
|
341
|
+
c = bc_color_dict['default']
|
|
342
|
+
else:
|
|
343
|
+
c = color
|
|
344
|
+
cmap = matplotlib.colors.ListedColormap(['0', c])
|
|
345
|
+
bounds = [0, 1, 2]
|
|
346
|
+
norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)
|
|
347
|
+
quadmesh = self.plot_array(plotarray, cmap=cmap, norm=norm, **kwargs)
|
|
348
|
+
return quadmesh
|
|
349
|
+
|
|
350
|
+
def plot_shapefile(self, shp, **kwargs):
|
|
351
|
+
"""
|
|
352
|
+
Plot a shapefile. The shapefile must be in the same coordinates as
|
|
353
|
+
the rotated and offset grid.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
shp : string
|
|
358
|
+
Name of the shapefile to plot
|
|
359
|
+
|
|
360
|
+
kwargs : dictionary
|
|
361
|
+
Keyword arguments passed to plotutil.plot_shapefile()
|
|
362
|
+
|
|
363
|
+
"""
|
|
364
|
+
if 'ax' in kwargs:
|
|
365
|
+
ax = kwargs.pop('ax')
|
|
366
|
+
else:
|
|
367
|
+
ax = self.ax
|
|
368
|
+
patch_collection = plotutil.plot_shapefile(shp, ax, **kwargs)
|
|
369
|
+
return patch_collection
|
|
370
|
+
|
|
371
|
+
def plot_discharge(self, frf, fff, dis=None, flf=None, head=None, istep=1, jstep=1,
|
|
372
|
+
**kwargs):
|
|
373
|
+
"""
|
|
374
|
+
Use quiver to plot vectors.
|
|
375
|
+
|
|
376
|
+
Parameters
|
|
377
|
+
----------
|
|
378
|
+
frf : numpy.ndarray
|
|
379
|
+
MODFLOW's 'flow right face'
|
|
380
|
+
fff : numpy.ndarray
|
|
381
|
+
MODFLOW's 'flow front face'
|
|
382
|
+
flf : numpy.ndarray
|
|
383
|
+
MODFLOW's 'flow lower face' (Default is None.)
|
|
384
|
+
head : numpy.ndarray
|
|
385
|
+
MODFLOW's head array. If not provided, then will assume confined
|
|
386
|
+
conditions in order to calculated saturated thickness.
|
|
387
|
+
istep : int
|
|
388
|
+
row frequency to plot. (Default is 1.)
|
|
389
|
+
jstep : int
|
|
390
|
+
column frequency to plot. (Default is 1.)
|
|
391
|
+
kwargs : dictionary
|
|
392
|
+
Keyword arguments passed to plt.quiver()
|
|
393
|
+
|
|
394
|
+
Returns
|
|
395
|
+
-------
|
|
396
|
+
quiver : matplotlib.pyplot.quiver
|
|
397
|
+
Vectors of specific discharge.
|
|
398
|
+
|
|
399
|
+
"""
|
|
400
|
+
# Calculate specific discharge
|
|
401
|
+
# make sure dis is defined
|
|
402
|
+
if dis is None:
|
|
403
|
+
if self.model is not None:
|
|
404
|
+
dis = self.model.dis
|
|
405
|
+
else:
|
|
406
|
+
print("ModelMap.plot_quiver() error: self.dis is None and dis arg is None ")
|
|
407
|
+
return
|
|
408
|
+
delr = dis.delr.array
|
|
409
|
+
delc = dis.delc.array
|
|
410
|
+
top = dis.top.array
|
|
411
|
+
botm = dis.botm.array
|
|
412
|
+
nlay, nrow, ncol = botm.shape
|
|
413
|
+
laytyp = None
|
|
414
|
+
hnoflo = 999.
|
|
415
|
+
hdry = 999.
|
|
416
|
+
if self.model is not None:
|
|
417
|
+
lpf = self.model.get_package('LPF')
|
|
418
|
+
if lpf is not None:
|
|
419
|
+
laytyp = lpf.laytyp.array
|
|
420
|
+
hdry = lpf.hdry
|
|
421
|
+
bas = self.model.get_package('BAS6')
|
|
422
|
+
if bas is not None:
|
|
423
|
+
hnoflo = bas.hnoflo
|
|
424
|
+
|
|
425
|
+
# If no access to head or laytyp, then calculate confined saturated
|
|
426
|
+
# thickness by setting laytyp to zeros
|
|
427
|
+
if head is None or laytyp is None:
|
|
428
|
+
head = np.zeros(botm.shape, np.float32)
|
|
429
|
+
laytyp = np.zeros((nlay), dtype=np.int)
|
|
430
|
+
sat_thk = plotutil.saturated_thickness(head, top, botm, laytyp,
|
|
431
|
+
[hnoflo, hdry])
|
|
432
|
+
|
|
433
|
+
# Calculate specific discharge
|
|
434
|
+
qx, qy, qz = plotutil.centered_specific_discharge(frf, fff, flf, delr,
|
|
435
|
+
delc, sat_thk)
|
|
436
|
+
|
|
437
|
+
# Select correct slice and step
|
|
438
|
+
x = self.sr.xcentergrid[::istep, ::jstep]
|
|
439
|
+
y = self.sr.ycentergrid[::istep, ::jstep]
|
|
440
|
+
u = qx[self.layer, :, :]
|
|
441
|
+
v = qy[self.layer, :, :]
|
|
442
|
+
u = u[::istep, ::jstep]
|
|
443
|
+
v = v[::istep, ::jstep]
|
|
444
|
+
|
|
445
|
+
if 'ax' in kwargs:
|
|
446
|
+
ax = kwargs.pop('ax')
|
|
447
|
+
else:
|
|
448
|
+
ax = self.ax
|
|
449
|
+
|
|
450
|
+
# Rotate and plot
|
|
451
|
+
urot, vrot = self.sr.rotate(u, v, self.sr.rotation)
|
|
452
|
+
quiver = ax.quiver(x, y, urot, vrot, **kwargs)
|
|
453
|
+
|
|
454
|
+
return quiver
|
|
455
|
+
|
|
456
|
+
def plot_pathline(self, pl, **kwargs):
|
|
457
|
+
"""
|
|
458
|
+
Plot the MODPATH pathlines.
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
pl : list of rec arrays or a single rec array
|
|
463
|
+
rec array or list of rec arrays is data returned from
|
|
464
|
+
modpathfile PathlineFile get_data() or get_alldata()
|
|
465
|
+
methods. Data in rec array is 'x', 'y', 'z', 'time',
|
|
466
|
+
'k', and 'particleid'.
|
|
467
|
+
|
|
468
|
+
kwargs : layer, ax, colors. The remaining kwargs are passed
|
|
469
|
+
into the LineCollection constructor. If layer='all',
|
|
470
|
+
pathlines are output for all layers
|
|
471
|
+
|
|
472
|
+
Returns
|
|
473
|
+
-------
|
|
474
|
+
lc : matplotlib.collections.LineCollection
|
|
475
|
+
|
|
476
|
+
"""
|
|
477
|
+
from matplotlib.collections import LineCollection
|
|
478
|
+
#make sure pathlines is a list
|
|
479
|
+
if isinstance(pl, np.ndarray):
|
|
480
|
+
pl = [pl]
|
|
481
|
+
|
|
482
|
+
if 'layer' in kwargs:
|
|
483
|
+
kon = kwargs.pop('layer')
|
|
484
|
+
if isinstance(kon, str):
|
|
485
|
+
if kon.lower() == 'all':
|
|
486
|
+
kon = -1
|
|
487
|
+
else:
|
|
488
|
+
kon = self.layer
|
|
489
|
+
else:
|
|
490
|
+
kon = self.layer
|
|
491
|
+
|
|
492
|
+
if 'ax' in kwargs:
|
|
493
|
+
ax = kwargs.pop('ax')
|
|
494
|
+
else:
|
|
495
|
+
ax = self.ax
|
|
496
|
+
|
|
497
|
+
if 'colors' not in kwargs:
|
|
498
|
+
kwargs['colors'] = '0.5'
|
|
499
|
+
|
|
500
|
+
linecol = []
|
|
501
|
+
for p in pl:
|
|
502
|
+
vlc = []
|
|
503
|
+
#rotate data
|
|
504
|
+
x0r, y0r = self.sr.rotate(p['x'], p['y'], self.sr.rotation, 0., self.sr.yedge[0])
|
|
505
|
+
x0r += self.sr.xul
|
|
506
|
+
y0r += self.sr.yul - self.sr.yedge[0]
|
|
507
|
+
#build polyline array
|
|
508
|
+
arr = np.vstack((x0r, y0r)).T
|
|
509
|
+
#select based on layer
|
|
510
|
+
if kon >= 0:
|
|
511
|
+
arr = np.ma.masked_where((p['k'] != kon), arr)
|
|
512
|
+
#append line to linecol if there is some unmasked segment
|
|
513
|
+
if not arr.mask.all():
|
|
514
|
+
linecol.append(arr)
|
|
515
|
+
#create line collection
|
|
516
|
+
lc = None
|
|
517
|
+
if len(linecol) > 0:
|
|
518
|
+
lc = LineCollection(linecol, **kwargs)
|
|
519
|
+
ax.add_collection(lc)
|
|
520
|
+
return lc
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def plot_endpoint(self, ep, **kwargs):
|
|
524
|
+
"""
|
|
525
|
+
Plot the MODPATH endpoints.
|
|
526
|
+
|
|
527
|
+
Parameters
|
|
528
|
+
----------
|
|
529
|
+
ep : rec array
|
|
530
|
+
rec array is data returned from modpathfile EndpointFile
|
|
531
|
+
get_data() or get_alldata() methods. Data in rec array
|
|
532
|
+
is 'x', 'y', 'z', 'time', 'k', and 'particleid'.
|
|
533
|
+
|
|
534
|
+
kwargs : layer, ax, c, s, colorbar, colorbar_label, shrink. The
|
|
535
|
+
remaining kwargs are passed into the matplotlib scatter
|
|
536
|
+
method. If layer='all', endpoints are output for all layers.
|
|
537
|
+
If colorbar is True a colorbar will be added to the plot.
|
|
538
|
+
If colorbar_label is passed in and colorbar is True then
|
|
539
|
+
colorbar_label will be passed to the colorbar set_label()
|
|
540
|
+
method. If shrink is passed in and colorbar is True then
|
|
541
|
+
the colorbar size will be set using shrink.
|
|
542
|
+
|
|
543
|
+
Returns
|
|
544
|
+
-------
|
|
545
|
+
sp : matplotlib.pyplot.scatter
|
|
546
|
+
|
|
547
|
+
"""
|
|
548
|
+
|
|
549
|
+
if 'layer' in kwargs:
|
|
550
|
+
kon = kwargs.pop('layer')
|
|
551
|
+
if isinstance(kon, str):
|
|
552
|
+
if kon.lower() == 'all':
|
|
553
|
+
kon = -1
|
|
554
|
+
else:
|
|
555
|
+
kon = self.layer
|
|
556
|
+
else:
|
|
557
|
+
kon = self.layer
|
|
558
|
+
|
|
559
|
+
if 'ax' in kwargs:
|
|
560
|
+
ax = kwargs.pop('ax')
|
|
561
|
+
else:
|
|
562
|
+
ax = self.ax
|
|
563
|
+
|
|
564
|
+
#scatter kwargs that users may redefine
|
|
565
|
+
if 'c' not in kwargs:
|
|
566
|
+
c = ep['time']
|
|
567
|
+
else:
|
|
568
|
+
c = np.empty((ep.shape[0]), dtype="S30")
|
|
569
|
+
c.fill(kwargs.pop('c'))
|
|
570
|
+
|
|
571
|
+
if 's' not in kwargs:
|
|
572
|
+
s = 50.
|
|
573
|
+
else:
|
|
574
|
+
s = float(kwargs.pop('s'))**2.
|
|
575
|
+
|
|
576
|
+
#colorbar kwargs
|
|
577
|
+
createcb = False
|
|
578
|
+
if 'colorbar' in kwargs:
|
|
579
|
+
createcb = kwargs.pop('colorbar')
|
|
580
|
+
|
|
581
|
+
colorbar_label = 'Endpoint Time'
|
|
582
|
+
if 'colorbar_label' in kwargs:
|
|
583
|
+
colorbar_label = kwargs.pop('colorbar_label')
|
|
584
|
+
|
|
585
|
+
shrink = 1.
|
|
586
|
+
if 'shrink' in kwargs:
|
|
587
|
+
shrink = float(kwargs.pop('shrink'))
|
|
588
|
+
|
|
589
|
+
#rotate data
|
|
590
|
+
x0r, y0r = self.sr.rotate(ep['x'], ep['y'], self.sr.rotation, 0., self.sr.yedge[0])
|
|
591
|
+
x0r += self.sr.xul
|
|
592
|
+
y0r += self.sr.yul - self.sr.yedge[0]
|
|
593
|
+
#build array to plot
|
|
594
|
+
arr = np.vstack((x0r, y0r)).T
|
|
595
|
+
#select based on layer
|
|
596
|
+
if kon >= 0:
|
|
597
|
+
c = np.ma.masked_where((ep['k'] != kon), c)
|
|
598
|
+
#plot the end point data
|
|
599
|
+
sp = plt.scatter(arr[:, 0], arr[:, 1], c=c, s=s, **kwargs)
|
|
600
|
+
#add a colorbar for endpoint times
|
|
601
|
+
if createcb:
|
|
602
|
+
cb = plt.colorbar(sp, shrink=shrink)
|
|
603
|
+
cb.set_label(colorbar_label)
|
|
604
|
+
return sp
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def get_grid_line_collection(self, **kwargs):
|
|
608
|
+
"""
|
|
609
|
+
Get a LineCollection of the grid
|
|
610
|
+
|
|
611
|
+
"""
|
|
612
|
+
from matplotlib.collections import LineCollection
|
|
613
|
+
xmin = self.sr.xedge[0]
|
|
614
|
+
xmax = self.sr.xedge[-1]
|
|
615
|
+
ymin = self.sr.yedge[-1]
|
|
616
|
+
ymax = self.sr.yedge[0]
|
|
617
|
+
linecol = []
|
|
618
|
+
# Vertical lines
|
|
619
|
+
for j in range(self.sr.ncol + 1):
|
|
620
|
+
x0 = self.sr.xedge[j]
|
|
621
|
+
x1 = x0
|
|
622
|
+
y0 = ymin
|
|
623
|
+
y1 = ymax
|
|
624
|
+
x0r, y0r = self.sr.rotate(x0, y0, self.sr.rotation, 0, self.sr.yedge[0])
|
|
625
|
+
x0r += self.sr.xul
|
|
626
|
+
y0r += self.sr.yul - self.sr.yedge[0]
|
|
627
|
+
x1r, y1r = self.sr.rotate(x1, y1, self.sr.rotation, 0, self.sr.yedge[0])
|
|
628
|
+
x1r += self.sr.xul
|
|
629
|
+
y1r += self.sr.yul - self.sr.yedge[0]
|
|
630
|
+
linecol.append(((x0r, y0r), (x1r, y1r)))
|
|
631
|
+
|
|
632
|
+
#horizontal lines
|
|
633
|
+
for i in range(self.sr.nrow + 1):
|
|
634
|
+
x0 = xmin
|
|
635
|
+
x1 = xmax
|
|
636
|
+
y0 = self.sr.yedge[i]
|
|
637
|
+
y1 = y0
|
|
638
|
+
x0r, y0r = self.sr.rotate(x0, y0, self.sr.rotation, 0, self.sr.yedge[0])
|
|
639
|
+
x0r += self.sr.xul
|
|
640
|
+
y0r += self.sr.yul - self.sr.yedge[0]
|
|
641
|
+
x1r, y1r = self.sr.rotate(x1, y1, self.sr.rotation, 0, self.sr.yedge[0])
|
|
642
|
+
x1r += self.sr.xul
|
|
643
|
+
y1r += self.sr.yul - self.sr.yedge[0]
|
|
644
|
+
linecol.append(((x0r, y0r), (x1r, y1r)))
|
|
645
|
+
|
|
646
|
+
lc = LineCollection(linecol, **kwargs)
|
|
647
|
+
return lc
|