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.
Files changed (90) hide show
  1. {flopy-3.2.1 → flopy-3.2.2}/PKG-INFO +47 -11
  2. {flopy-3.2.1 → flopy-3.2.2}/flopy/mbase.py +307 -17
  3. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mf.py +8 -6
  4. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfbcf.py +2 -2
  5. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfchd.py +1 -1
  6. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfde4.py +2 -2
  7. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfdis.py +11 -94
  8. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfdrn.py +1 -1
  9. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfevt.py +3 -3
  10. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfghb.py +2 -2
  11. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfgmg.py +60 -19
  12. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfhfb.py +7 -7
  13. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mflpf.py +13 -10
  14. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmlt.py +3 -3
  15. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfnwt.py +1 -1
  16. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfoc.py +26 -20
  17. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfoc88.py +9 -9
  18. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpar.py +15 -9
  19. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfparbc.py +7 -7
  20. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpcg.py +2 -2
  21. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpcgn.py +3 -3
  22. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpks.py +1 -1
  23. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfrch.py +4 -4
  24. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfriv.py +6 -4
  25. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfsip.py +2 -2
  26. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfsor.py +3 -3
  27. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfswi2.py +34 -34
  28. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfswr1.py +4 -4
  29. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfupw.py +4 -4
  30. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfuzf1.py +5 -5
  31. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfwel.py +1 -2
  32. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfzon.py +2 -2
  33. {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/mp.py +7 -5
  34. {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/mpbas.py +1 -1
  35. {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/mpsim.py +18 -16
  36. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mt.py +1 -1
  37. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtbtn.py +3 -3
  38. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtssm.py +7 -6
  39. {flopy-3.2.1 → flopy-3.2.2}/flopy/plot/__init__.py +1 -1
  40. {flopy-3.2.1 → flopy-3.2.2}/flopy/plot/crosssection.py +73 -103
  41. flopy-3.2.2/flopy/plot/map.py +647 -0
  42. flopy-3.2.2/flopy/plot/plotutil.py +1205 -0
  43. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/__init__.py +3 -1
  44. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/binaryfile.py +91 -364
  45. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/binaryhydmodfile.py +3 -3
  46. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/binaryswrfile.py +21 -21
  47. flopy-3.2.2/flopy/utils/datafile.py +476 -0
  48. flopy-3.2.2/flopy/utils/flopy_io.py +128 -0
  49. flopy-3.2.2/flopy/utils/formattedfile.py +366 -0
  50. flopy-3.2.2/flopy/utils/modpathfile.py +421 -0
  51. flopy-3.2.2/flopy/utils/reference.py +386 -0
  52. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/util_array.py +512 -83
  53. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/util_list.py +234 -13
  54. flopy-3.2.2/flopy/version.py +4 -0
  55. {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/PKG-INFO +47 -11
  56. {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/SOURCES.txt +5 -0
  57. {flopy-3.2.1 → flopy-3.2.2}/setup.py +2 -6
  58. flopy-3.2.1/flopy/plot/map.py +0 -592
  59. flopy-3.2.1/flopy/plot/plotutil.py +0 -512
  60. flopy-3.2.1/flopy/version.py +0 -4
  61. flopy-3.2.1/requirements.txt +0 -2
  62. {flopy-3.2.1 → flopy-3.2.2}/flopy/__init__.py +0 -0
  63. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/__init__.py +0 -0
  64. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfaddoutsidefile.py +0 -0
  65. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfbas.py +0 -0
  66. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfbct.py +0 -0
  67. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mflmt.py +0 -0
  68. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmnw1.py +0 -0
  69. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmnw2.py +0 -0
  70. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfmnwi.py +0 -0
  71. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpbc.py +0 -0
  72. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfpval.py +0 -0
  73. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfsms.py +0 -0
  74. {flopy-3.2.1 → flopy-3.2.2}/flopy/modflow/mfswi.py +0 -0
  75. {flopy-3.2.1 → flopy-3.2.2}/flopy/modpath/__init__.py +0 -0
  76. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/__init__.py +0 -0
  77. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtadv.py +0 -0
  78. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtdsp.py +0 -0
  79. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtgcg.py +0 -0
  80. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtphc.py +0 -0
  81. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mtrct.py +0 -0
  82. {flopy-3.2.1 → flopy-3.2.2}/flopy/mt3d/mttob.py +0 -0
  83. {flopy-3.2.1 → flopy-3.2.2}/flopy/seawat/__init__.py +0 -0
  84. {flopy-3.2.1 → flopy-3.2.2}/flopy/seawat/swt.py +0 -0
  85. {flopy-3.2.1 → flopy-3.2.2}/flopy/seawat/swtvdf.py +0 -0
  86. {flopy-3.2.1 → flopy-3.2.2}/flopy/utils/mfreadnam.py +0 -0
  87. {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/dependency_links.txt +0 -0
  88. {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/requires.txt +0 -0
  89. {flopy-3.2.1 → flopy-3.2.2}/flopy.egg-info/top_level.txt +0 -0
  90. {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