def plot_array(self, a, masked_values=None, head=None, **kwargs): """ Plot a three-dimensional array as a patch collection. Parameters ---------- a : numpy.ndarray Three-dimensional array to plot. masked_values : iterable of floats, ints Values to mask. head : numpy.ndarray Three-dimensional array to set top of patches to the minimum of the top of a layer or the head value. Used to create patches that conform to water-level elevations. **kwargs : dictionary keyword arguments passed to matplotlib.collections.PatchCollection Returns ------- patches : matplotlib.collections.PatchCollection """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax xedge, yedge = self.mg.xyedges vpts = [] for k in range(self.mg.nlay): vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, a[k, :, :])) if len(self.laycbd) > 0: if self.laycbd[k] > 0: ta = np.empty((self.mg.nrow, self.mg.ncol), dtype=np.float) ta[:, :] = -1e9 vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, ta)) vpts = np.array(vpts) if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if isinstance(head, np.ndarray): zpts = self.set_zpts(head) else: zpts = self.zpts if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if self.ncb > 0: vpts = np.ma.masked_equal(vpts, -1e9) pc = self.get_grid_patch_collection(zpts, vpts, **kwargs) if pc != None: ax.add_collection(pc) return pc
def plot_array(self, a, masked_values=None, head=None, **kwargs): """ Plot a three-dimensional array as a patch collection. Parameters ---------- a : numpy.ndarray Three-dimensional array to plot. masked_values : iterable of floats, ints Values to mask. head : numpy.ndarray Three-dimensional array to set top of patches to the minimum of the top of a layer or the head value. Used to create patches that conform to water-level elevations. **kwargs : dictionary keyword arguments passed to matplotlib.collections.PatchCollection Returns ------- patches : matplotlib.collections.PatchCollection """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax xedge, yedge = self.mg.xyedges vpts = [] for k in range(self.mg.nlay): vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, a[k, :, :])) if self.laycbd[k] > 0: ta = np.empty((self.mg.nrow, self.mg.ncol), dtype=np.float) ta[:, :] = -1e9 vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, ta)) vpts = np.array(vpts) if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if isinstance(head, np.ndarray): zpts = self.set_zpts(head) else: zpts = self.zpts if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if self.ncb > 0: vpts = np.ma.masked_equal(vpts, -1e9) pc = self.get_grid_patch_collection(zpts, vpts, **kwargs) if pc != None: ax.add_collection(pc) return pc
def set_zpts(self, vs): """ Get an array of z elevations based on minimum of cell elevation (self.elev) or passed vs numpy.ndarray Parameters ---------- vs : numpy.ndarray Three-dimensional array to plot. Returns ------- zpts : numpy.ndarray """ zpts = [] xedge, yedge = self.mg.xyedges for k in range(self.layer0, self.layer1): e = self.elev[k, :, :] if k < self.mg.nlay: v = vs[k, :, :] idx = v < e e[idx] = v[idx] zpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, e)) return np.array(zpts)
def plot_surface(self, a, masked_values=None, **kwargs): """ Plot a two- or three-dimensional array as line(s). Parameters ---------- a : numpy.ndarray Two- or three-dimensional array to plot. masked_values : iterable of floats, ints Values to mask. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.plot Returns ------- plot : list containing matplotlib.plot objects """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax plotarray = a vpts = [] if len(plotarray.shape) == 2: nlay = 1 plotarray = np.reshape(plotarray, (1, plotarray.shape[0], plotarray.shape[1])) elif len(plotarray.shape) == 3: nlay = plotarray.shape[0] else: raise Exception('plot_array array must be a 2D or 3D array') xedge, yedge = self.mg.xyedges for k in range(nlay): vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, plotarray[k, :, :])) vpts = np.array(vpts) if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) plot = [] # adust distance array for modelgrid offset if self.geographic_coords: d = self.geographic_xpts.T[-1] else: d = self.d for k in range(vpts.shape[0]): plot.append(ax.plot(d, vpts[k, :], **kwargs)) return plot
def set_zcentergrid(self, vs): """ Get an array of z elevations at the center of a cell that is based on minimum of cell top elevation (self.elev) or passed vs numpy.ndarray Parameters ---------- vs : numpy.ndarray Three-dimensional array to plot. Returns ------- zcentergrid : numpy.ndarray """ vpts = [] xedge, yedge = self.mg.xyedges for k in range(self.layer0, self.layer1): if k < self.mg.nlay: e = vs[k, :, :] else: e = self.elev[k, :, :] vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, e)) vpts = np.array(vpts) zcentergrid = [] nz = 0 if self.mg.nlay == 1: for k in range(0, self.zpts.shape[0]): nz += 1 nx = 0 for i in range(0, self.xpts.shape[0], 2): nx += 1 vp = vpts[k, i] zp = self.zpts[k, i] if k == 0: if vp < zp: zp = vp zcentergrid.append(zp) else: for k in range(0, self.zpts.shape[0] - 1): if not self.active[k] == 1: continue nz += 1 nx = 0 for i in range(0, self.xpts.shape[0], 2): nx += 1 vp = vpts[k, i] ep = self.zpts[k, i] if vp < ep: ep = vp zp = 0.5 * (ep + self.zpts[k + 1, i + 1]) zcentergrid.append(zp) return np.array(zcentergrid).reshape((nz, nx))
def set_zcentergrid(self, vs): """ Get an array of z elevations at the center of a cell that is based on minimum of cell top elevation (self.elev) or passed vs numpy.ndarray Parameters ---------- vs : numpy.ndarray Three-dimensional array to plot. Returns ------- zcentergrid : numpy.ndarray """ vpts = [] xedge, yedge = self.mg.xyedges for k in range(self.layer0, self.layer1): if k < self.mg.nlay: e = vs[k, :, :] else: e = self.elev[k, :, :] vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, e)) vpts = np.array(vpts) zcentergrid = [] nz = 0 if self.mg.nlay == 1: for k in range(0, self.zpts.shape[0]): nz += 1 nx = 0 for i in range(0, self.xpts.shape[0], 2): nx += 1 vp = vpts[k, i] zp = self.zpts[k, i] if k == 0: if vp < zp: zp = vp zcentergrid.append(zp) else: for k in range(0, self.zpts.shape[0] - 1): nz += 1 nx = 0 for i in range(0, self.xpts.shape[0], 2): nx += 1 vp = vpts[k, i] ep = self.zpts[k, i] if vp < ep: ep = vp zp = 0.5 * (ep + self.zpts[k + 1, i + 1]) zcentergrid.append(zp) return np.array(zcentergrid).reshape((nz, nx))
def contour_array(self, a, masked_values=None, head=None, **kwargs): """ Contour a three-dimensional array. Parameters ---------- a : numpy.ndarray Three-dimensional array to plot. masked_values : iterable of floats, ints Values to mask. head : numpy.ndarray Three-dimensional array to set top of patches to the minimum of the top of a layer or the head value. Used to create patches that conform to water-level elevations. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.contour Returns ------- contour_set : matplotlib.pyplot.contour """ plotarray = a vpts = [] xedge, yedge = self.mg.xyedges for k in range(self.mg.nlay): vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, plotarray[k, :, :])) vpts = np.array(vpts) vpts = vpts[:, ::2] if self.mg.nlay == 1: vpts = np.vstack((vpts, vpts)) if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if isinstance(head, np.ndarray): zcentergrid = self.set_zcentergrid(head) else: zcentergrid = self.zcentergrid if self.geographic_coords: xcentergrid = self.geographic_xcentergrid else: xcentergrid = self.xcentergrid contour_set = self.ax.contour(xcentergrid, zcentergrid, vpts, **kwargs) return contour_set
def plot_surface(self, a, masked_values=None, **kwargs): """ Plot a two- or three-dimensional array as line(s). Parameters ---------- a : numpy.ndarray Two- or three-dimensional array to plot. masked_values : iterable of floats, ints Values to mask. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.plot Returns ------- plot : list containing matplotlib.plot objects """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax plotarray = a vpts = [] if len(plotarray.shape) == 2: nlay = 1 plotarray = np.reshape(plotarray, (1, plotarray.shape[0], plotarray.shape[1])) elif len(plotarray.shape) == 3: nlay = plotarray.shape[0] else: raise Exception('plot_array array must be a 2D or 3D array') xedge, yedge = self.mg.xyedges for k in range(nlay): vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, plotarray[k, :, :])) vpts = np.array(vpts) if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) plot = [] for k in range(vpts.shape[0]): plot.append(ax.plot(self.d, vpts[k, :], **kwargs)) return plot
def contour_array(self, a, masked_values=None, head=None, **kwargs): """ Contour a three-dimensional array. Parameters ---------- a : numpy.ndarray Three-dimensional array to plot. masked_values : iterable of floats, ints Values to mask. head : numpy.ndarray Three-dimensional array to set top of patches to the minimum of the top of a layer or the head value. Used to create patches that conform to water-level elevations. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.contour Returns ------- contour_set : matplotlib.pyplot.contour """ plotarray = a vpts = [] xedge, yedge = self.mg.xyedges for k in range(self.mg.nlay): vpts.append(plotutil.cell_value_points(self.xpts, xedge, yedge, plotarray[k, :, :])) vpts = np.array(vpts) vpts = vpts[:, ::2] if self.mg.nlay == 1: vpts = np.vstack((vpts, vpts)) if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if isinstance(head, np.ndarray): zcentergrid = self.set_zcentergrid(head) else: zcentergrid = self.zcentergrid contour_set = self.ax.contour(self.xcentergrid, zcentergrid, vpts, **kwargs) return contour_set
def __init__(self, ax=None, model=None, modelgrid=None, line=None, extent=None, geographic_coords=False): super(_StructuredCrossSection, self).__init__(ax=ax, model=model, modelgrid=modelgrid, geographic_coords= geographic_coords) if line is None: s = 'line must be specified.' raise Exception(s) linekeys = [linekeys.lower() for linekeys in list(line.keys())] if len(linekeys) != 1: s = 'only row, column, or line can be specified in line dictionary.\n' s += 'keys specified: ' for k in linekeys: s += '{} '.format(k) raise AssertionError(s) if ax is None: self.ax = plt.gca() else: self.ax = ax onkey = list(line.keys())[0] eps = 1.e-4 xedge, yedge = self.mg.xyedges self.__geographic_xpts = None # un-translate model grid into model coordinates self.xcellcenters, self.ycellcenters = \ geometry.transform(self.mg.xcellcenters, self.mg.ycellcenters, self.mg.xoffset, self.mg.yoffset, self.mg.angrot_radians, inverse=True) if 'row' in linekeys: self.direction = 'x' ycenter = self.ycellcenters.T[0] pts = [(xedge[0] + eps, ycenter[int(line[onkey])] - eps), (xedge[-1] - eps, ycenter[int(line[onkey])] + eps)] elif 'column' in linekeys: self.direction = 'y' xcenter = self.xcellcenters[0, :] pts = [(xcenter[int(line[onkey])] + eps, yedge[0] - eps), (xcenter[int(line[onkey])] - eps, yedge[-1] + eps)] else: self.direction = 'xy' verts = line[onkey] xp = [] yp = [] for [v1, v2] in verts: xp.append(v1) yp.append(v2) xp, yp = self.mg.get_local_coords(xp, yp) pts = [(xt, yt) for xt, yt in zip(xp, yp)] # for now set offset to zero, since we do not have # information on projection from the user # convert pts list to numpy array self.pts = np.array(pts) # get points along the line self.xpts = plotutil.line_intersect_grid(self.pts, self.mg.xyedges[0], self.mg.xyedges[1]) if len(self.xpts) < 2: s = 'cross-section cannot be created\n.' s += ' less than 2 points intersect the model grid\n' s += ' {} points intersect the grid.'.format(len(self.xpts)) raise Exception(s) # set horizontal distance d = [] for v in self.xpts: d.append(v[2]) self.d = np.array(d) self.idomain = self.mg.idomain if self.mg.idomain is None: self.idomain = np.ones((self.mg.nlay, self.mg.nrow, self.mg.ncol), dtype=int) self.ncb = 0 self.laycbd = [] if self.model is not None: if self.model.laycbd is not None: self.laycbd = self.model.laycbd for l in self.laycbd: if l > 0: self.ncb += 1 self.active = np.ones((self.mg.nlay + self.ncb), dtype=np.int) kon = 0 if len(self.laycbd) > 0: for k in range(self.mg.nlay): if self.laycbd[k] > 0: kon += 1 self.active[kon] = 0 kon += 1 top = self.mg.top botm = self.mg.botm elev = [top.copy()] for k in range(self.mg.nlay + self.ncb): elev.append(botm[k, :, :]) self.elev = np.array(elev) self.layer0 = 0 self.layer1 = self.mg.nlay + self.ncb + 1 zpts = [] for k in range(self.layer0, self.layer1): zpts.append(plotutil.cell_value_points(self.xpts, self.mg.xyedges[0], self.mg.xyedges[1], self.elev[k, :, :])) self.zpts = np.array(zpts) xcentergrid, zcentergrid = self.get_centergrids(self.xpts, self.zpts) self.xcentergrid = xcentergrid self.zcentergrid = zcentergrid geo_xcentergrid, _ = self.get_centergrids(self.geographic_xpts, self.zpts) self.geographic_xcentergrid = geo_xcentergrid # Create cross-section extent if extent is None: self.extent = self.get_extent() else: self.extent = extent # Set axis limits self.ax.set_xlim(self.extent[0], self.extent[1]) self.ax.set_ylim(self.extent[2], self.extent[3]) return
def plot_fill_between(self, a, colors=('blue', 'red'), masked_values=None, head=None, **kwargs): """ Plot a three-dimensional array as lines. Parameters ---------- a : numpy.ndarray Three-dimensional array to plot. colors : list matplotlib fill colors, two required masked_values : iterable of floats, ints Values to mask. head : numpy.ndarray Three-dimensional array to set top of patches to the minimum of the top of a layer or the head value. Used to create patches that conform to water-level elevations. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.plot Returns ------- plot : list containing matplotlib.fillbetween objects """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax plotarray = a vpts = [] for k in range(self.mg.nlay): # print('k', k, self.laycbd[k]) vpts.append(plotutil.cell_value_points(self.xpts, self.mg.xyedges[0], self.mg.xyedges[1], plotarray[k, :, :])) if len(self.laycbd) > 0: if self.laycbd[k] > 0: ta = np.empty((self.mg.nrow, self.mg.ncol), dtype=np.float) ta[:, :] = self.mg.botm.array[k, :, :] vpts.append(plotutil.cell_value_points(self.xpts, self.mg.xyedges[0], self.mg.xyedges[1], ta)) vpts = np.ma.array(vpts, mask=False) if isinstance(head, np.ndarray): zpts = self.set_zpts(head) else: zpts = self.zpts if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if self.ncb > 0: vpts = np.ma.masked_equal(vpts, -1e9) idxm = np.ma.getmask(vpts) plot = [] # print(zpts.shape) for k in range(self.mg.nlay + self.ncb): if self.active[k] == 0: continue idxmk = idxm[k, :] v = vpts[k, :] y1 = zpts[k, :] y2 = zpts[k + 1, :] # make sure y1 is not below y2 idx = y1 < y2 y1[idx] = y2[idx] # make sure v is not below y2 idx = v < y2 v[idx] = y2[idx] # make sure v is not above y1 idx = v > y1 v[idx] = y1[idx] # set y2 to v y2 = v # mask cells y1[idxmk] = np.nan y2[idxmk] = np.nan # adjust distance array for modelgrid offset if self.geographic_coords: d = self.geographic_xpts.T[-1] else: d = self.d plot.append(ax.fill_between(d, y1=y1, y2=y2, color=colors[0], **kwargs)) y1 = y2 y2 = self.zpts[k + 1, :] y2[idxmk] = np.nan plot.append(ax.fill_between(d, y1=y1, y2=y2, color=colors[1], **kwargs)) return plot
def __init__(self, ax=None, model=None, modelgrid=None, line=None, extent=None): super(_StructuredCrossSection, self).__init__(ax=ax, model=model, modelgrid=modelgrid) if line is None: s = 'line must be specified.' raise Exception(s) linekeys = [linekeys.lower() for linekeys in list(line.keys())] if len(linekeys) != 1: s = 'only row, column, or line can be specified in line dictionary.\n' s += 'keys specified: ' for k in linekeys: s += '{} '.format(k) raise AssertionError(s) if ax is None: self.ax = plt.gca() else: self.ax = ax onkey = list(line.keys())[0] eps = 1.e-4 xedge, yedge = self.mg.xyedges # un-translate model grid into model coordinates self.xcellcenters, self.ycellcenters = \ geometry.transform(self.mg.xcellcenters, self.mg.ycellcenters, self.mg.xoffset, self.mg.yoffset, self.mg.angrot_radians, inverse=True) if 'row' in linekeys: self.direction = 'x' ycenter = self.ycellcenters.T[0] pts = [(xedge[0] + eps, ycenter[int(line[onkey])] - eps), (xedge[-1] - eps, ycenter[int(line[onkey])] + eps)] elif 'column' in linekeys: self.direction = 'y' xcenter = self.xcellcenters[0, :] pts = [(xcenter[int(line[onkey])] + eps, yedge[0] - eps), (xcenter[int(line[onkey])] - eps, yedge[-1] + eps)] else: self.direction = 'xy' verts = line[onkey] xp = [] yp = [] for [v1, v2] in verts: xp.append(v1) yp.append(v2) xp, yp = self.mg.get_local_coords(xp, yp) pts = [(xt, yt) for xt, yt in zip(xp, yp)] # convert pts list to numpy array self.pts = np.array(pts) # get points along the line self.xpts = plotutil.line_intersect_grid(self.pts, self.mg.xyedges[0], self.mg.xyedges[1]) if len(self.xpts) < 2: s = 'cross-section cannot be created\n.' s += ' less than 2 points intersect the model grid\n' s += ' {} points intersect the grid.'.format(len(self.xpts)) raise Exception(s) # set horizontal distance d = [] for v in self.xpts: d.append(v[2]) self.d = np.array(d) self.idomain = self.mg.idomain if self.mg.idomain is None: self.idomain = np.ones((self.mg.nlay, self.mg.nrow, self.mg.ncol), dtype=int) self.ncb = 0 self.laycbd = [] if self.model is not None: if self.model.laycbd is not None: self.laycbd = self.model.laycbd for l in self.laycbd: if l > 0: self.ncb += 1 self.active = np.ones((self.mg.nlay + self.ncb), dtype=np.int) kon = 0 if len(self.laycbd) > 0: for k in range(self.mg.nlay): if self.laycbd[k] > 0: kon += 1 self.active[kon] = 0 kon += 1 top = self.mg.top botm = self.mg.botm elev = [top.copy()] for k in range(self.mg.nlay + self.ncb): elev.append(botm[k, :, :]) self.elev = np.array(elev) self.layer0 = 0 self.layer1 = self.mg.nlay + self.ncb + 1 zpts = [] for k in range(self.layer0, self.layer1): zpts.append(plotutil.cell_value_points(self.xpts, self.mg.xyedges[0], self.mg.xyedges[1], self.elev[k, :, :])) self.zpts = np.array(zpts) xcentergrid = [] zcentergrid = [] nz = 0 if self.mg.nlay == 1: for k in range(0, self.zpts.shape[0]): nz += 1 nx = 0 for i in range(0, self.xpts.shape[0], 2): try: xp = 0.5 * (self.xpts[i][2] + self.xpts[i + 1][2]) zp = self.zpts[k, i] xcentergrid.append(xp) zcentergrid.append(zp) nx += 1 except: break else: for k in range(0, self.zpts.shape[0] - 1): nz += 1 nx = 0 for i in range(0, self.xpts.shape[0], 2): try: xp = 0.5 * (self.xpts[i][2] + self.xpts[i + 1][2]) zp = 0.5 * (self.zpts[k, i] + self.zpts[k + 1, i + 1]) xcentergrid.append(xp) zcentergrid.append(zp) nx += 1 except: break self.xcentergrid = np.array(xcentergrid).reshape((nz, nx)) self.zcentergrid = np.array(zcentergrid).reshape((nz, nx)) # Create cross-section extent if extent is None: self.extent = self.get_extent() else: self.extent = extent # Set axis limits self.ax.set_xlim(self.extent[0], self.extent[1]) self.ax.set_ylim(self.extent[2], self.extent[3]) return
def plot_fill_between(self, a, colors=('blue', 'red'), masked_values=None, head=None, **kwargs): """ Plot a three-dimensional array as lines. Parameters ---------- a : numpy.ndarray Three-dimensional array to plot. colors : list matplotlib fill colors, two required masked_values : iterable of floats, ints Values to mask. head : numpy.ndarray Three-dimensional array to set top of patches to the minimum of the top of a layer or the head value. Used to create patches that conform to water-level elevations. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.plot Returns ------- plot : list containing matplotlib.fillbetween objects """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax plotarray = a vpts = [] for k in range(self.mg.nlay): # print('k', k, self.laycbd[k]) vpts.append(plotutil.cell_value_points(self.xpts, self.mg.xyedges[0], self.mg.xyedges[1], plotarray[k, :, :])) if self.laycbd[k] > 0: ta = np.empty((self.mg.nrow, self.mg.ncol), dtype=np.float) ta[:, :] = self.mg.botm.array[k, :, :] vpts.append(plotutil.cell_value_points(self.xpts, self.mg.xyedges[0], self.mg.xyedges[1], ta)) vpts = np.ma.array(vpts, mask=False) if isinstance(head, np.ndarray): zpts = self.set_zpts(head) else: zpts = self.zpts if masked_values is not None: for mval in masked_values: vpts = np.ma.masked_equal(vpts, mval) if self.ncb > 0: vpts = np.ma.masked_equal(vpts, -1e9) idxm = np.ma.getmask(vpts) plot = [] # print(zpts.shape) for k in range(self.mg.nlay + self.ncb): if self.active[k] == 0: continue idxmk = idxm[k, :] v = vpts[k, :] y1 = zpts[k, :] y2 = zpts[k + 1, :] # make sure y1 is not below y2 idx = y1 < y2 y1[idx] = y2[idx] # make sure v is not below y2 idx = v < y2 v[idx] = y2[idx] # make sure v is not above y1 idx = v > y1 v[idx] = y1[idx] # set y2 to v y2 = v # mask cells y1[idxmk] = np.nan y2[idxmk] = np.nan plot.append(ax.fill_between(self.d, y1=y1, y2=y2, color=colors[0], **kwargs)) y1 = y2 y2 = self.zpts[k + 1, :] y2[idxmk] = np.nan plot.append(ax.fill_between(self.d, y1=y1, y2=y2, color=colors[1], **kwargs)) return plot