def test_export(): fm = flopy.modflow m = fm.Modflow() dis = fm.ModflowDis(m, 1, 10, 10, lenuni=2, itmuni=4) m.sr = SpatialReference(delr=m.dis.delr.array, delc=m.dis.delc.array) m.sr.write_shapefile(os.path.join(outpath, 'grid.shp')) r, d = create_sfr_data() sfr = flopy.modflow.ModflowSfr2(m, reach_data=r, segment_data={0: d}) sfr.segment_data[0]['flow'][-1] = 1e4 sfr.stress_period_data.export(os.path.join(outpath, 'sfr.shp'), sparse=True) sfr.export_linkages(os.path.join(outpath, 'linkages.shp')) sfr.export_outlets(os.path.join(outpath, 'outlets.shp')) sfr.export_transient_variable(os.path.join(outpath, 'inlets.shp'), 'flow') from flopy.export.shapefile_utils import shp2recarray ra = shp2recarray(os.path.join(outpath, 'inlets.shp')) assert ra.flow0[0] == 1e4 ra = shp2recarray(os.path.join(outpath, 'outlets.shp')) assert ra.iseg[0] + ra.ireach[0] == 5 ra = shp2recarray(os.path.join(outpath, 'linkages.shp')) crds = np.array(list(ra.geometry[2].coords)) assert np.array_equal(crds, np.array([[2.5, 4.5], [3.5, 5.5]])) ra = shp2recarray(os.path.join(outpath, 'sfr.shp')) assert ra.iseg0.sum() == sfr.reach_data.iseg.sum() assert ra.ireach0.sum() == sfr.reach_data.ireach.sum() y = np.concatenate([np.array(g.exterior)[:, 1] for g in ra.geometry]) x = np.concatenate([np.array(g.exterior)[:, 0] for g in ra.geometry]) assert (x.min(), y.min(), x.max(), y.max()) == m.sr.bounds assert ra[(ra.iseg0 == 2) & (ra.ireach0 == 1)]['geometry'][0].bounds \ == (6.0, 2.0, 7.0, 3.0)
def test_get_vertices(): from flopy.utils.reference import SpatialReference from flopy.discretization import StructuredGrid m = flopy.modflow.Modflow(rotation=20.) nrow, ncol = 40, 20 dis = flopy.modflow.ModflowDis(m, nlay=1, nrow=nrow, ncol=ncol, delr=250., delc=250., top=10, botm=0) xul, yul = 500000, 2934000 sr = SpatialReference(delc=m.dis.delc.array, xul=xul, yul=yul, rotation=45.) mg = StructuredGrid(delc=m.dis.delc.array, delr=m.dis.delr.array, xoff=sr.xll, yoff=sr.yll, angrot=sr.rotation) xgrid = mg.xvertices ygrid = mg.yvertices # a1 = np.array(mg.xyvertices) a1 = np.array([[xgrid[0, 0], ygrid[0, 0]], [xgrid[0, 1], ygrid[0, 1]], [xgrid[1, 1], ygrid[1, 1]], [xgrid[1, 0], ygrid[1, 0]]]) a2 = np.array(mg.get_cell_vertices(0, 0)) assert np.array_equal(a1, a2)
def test_read_usgs_model_reference(): nlay, nrow, ncol = 1, 30, 5 delr, delc = 250, 500 #xll, yll = 272300, 5086000 model_ws = os.path.join('temp', 't007') shutil.copy('../examples/data/usgs.model.reference', model_ws) fm = flopy.modflow m = fm.Modflow(modelname='junk', model_ws=model_ws) # feet and days dis = fm.ModflowDis(m, nlay=nlay, nrow=nrow, ncol=ncol, delr=delr, delc=delc, lenuni=1, itmuni=4) m.write_input() # test reading of SR information from usgs.model.reference m2 = fm.Modflow.load('junk.nam', model_ws=os.path.join('temp', 't007')) from flopy.utils.reference import SpatialReference d = SpatialReference.read_usgs_model_reference_file(os.path.join('temp', 't007', 'usgs.model.reference')) assert m2.sr.xul == d['xul'] assert m2.sr.yul == d['yul'] assert m2.sr.rotation == d['rotation'] assert m2.sr.lenuni == d['lenuni'] assert m2.sr.epsg == d['epsg'] # have to delete this, otherwise it will mess up other tests if os.path.exists(os.path.join(tpth, 'usgs.model.reference')): os.remove(os.path.join(tpth, 'usgs.model.reference'))
def _set_spatialreference(self): """ Define structured or unstructured spatial reference based on MODFLOW 6 discretization type. Returns ------- sr : SpatialReference """ sr = None try: if self._grid == 'DISV' or self._grid == 'DISU': try: iverts, verts = self.get_verts() vertc = self.get_centroids() xc = vertc[:, 0] yc = vertc[:, 1] sr = SpatialReferenceUnstructured(xc, yc, verts, iverts, [xc.shape[0]]) except: msg = 'could not set spatial reference for ' + \ '{} discretization '.format(self._grid) + \ 'defined in {}'.format(self.file.name) print(msg) elif self._grid == 'DIS': delr, delc = self._datadict['DELR'], self._datadict['DELC'] xorigin, yorigin, rot = self._datadict['XORIGIN'], \ self._datadict['YORIGIN'], \ self._datadict['ANGROT'] sr = SpatialReference(delr=delr, delc=delc, xll=xorigin, yll=yorigin, rotation=rot) except: print('could not set spatial reference for {}'.format( self.file.name)) return sr
def write_shapefile(self, endpoint_data=None, shpname='endpoings.shp', direction='ending', sr=None, epsg=None, **kwargs): """Write particle starting / ending locations to shapefile. endpoint_data : np.recarry Record array of same form as that returned by EndpointFile.get_alldata. (if none, EndpointFile.get_alldata() is exported). shpname : str File path for shapefile direction : str String defining if starting or ending particle locations should be considered. (default is 'ending') sr : flopy.utils.reference.SpatialReference instance Used to scale and rotate Global x,y,z values in MODPATH Endpoint file epsg : int EPSG code for writing projection (.prj) file. If this is not supplied, the proj4 string or epgs code associated with sr will be used. kwargs : keyword arguments to flopy.export.shapefile_utils.recarray2shp """ from flopy.utils.reference import SpatialReference from flopy.utils.geometry import Point from flopy.export.shapefile_utils import recarray2shp epd = endpoint_data.copy() if epd is None: epd = self.get_alldata() if direction.lower() == 'ending': xcol, ycol, zcol = 'x', 'y', 'z' elif direction.lower() == 'starting': xcol, ycol, zcol = 'x0', 'y0', 'z0' else: errmsg = 'flopy.map.plot_endpoint direction must be "ending" ' + \ 'or "starting".' raise Exception(errmsg) if sr is None: sr = SpatialReference() x, y = sr.transform(epd[xcol], epd[ycol]) z = epd[zcol] geoms = [Point(x[i], y[i], z[i]) for i in range(len(epd))] # convert back to one-based for n in self.kijnames: epd[n] += 1 recarray2shp(epd, geoms, shpname=shpname, epsg=epsg, **kwargs)
def test_polygon_from_ij(): """test creation of a polygon from an i, j location using get_vertices().""" m = flopy.modflow.Modflow('toy_model', model_ws=mpth) botm = np.zeros((2, 10, 10)) botm[0, :, :] = 1.5 botm[1, 5, 5] = 4 # negative layer thickness! botm[1, 6, 6] = 4 dis = flopy.modflow.ModflowDis(nrow=10, ncol=10, nlay=2, delr=100, delc=100, top=3, botm=botm, model=m) m.sr = SpatialReference(delr=m.dis.delr * .3048, delc=m.dis.delc * .3048, xul=600000, yul=5170000, proj4_str='EPSG:26715', rotation=-45) recarray = np.array([(0, 5, 5, .1, True, 's0'), (1, 4, 5, .2, False, 's1'), (0, 7, 8, .3, True, 's2')], dtype=[('k', '<i8'), ('i', '<i8'), ('j', '<i8'), ('stuff', '<f4'), ('stuf', '|b1'), ('stf', np.object)]).view(np.recarray) get_vertices = m.sr.get_vertices # function to get the referenced vertices for a model cell geoms = [ Polygon(get_vertices(i, j)) for i, j in zip(recarray.i, recarray.j) ] assert geoms[0].type == 'Polygon' assert np.abs(geoms[0].bounds[-1] - 5169784.473861726) < 1e-4 fpth = os.path.join(mpth, 'test.shp') recarray2shp(recarray, geoms, fpth, epsg=26715) import epsgref reload(epsgref) from epsgref import prj assert 26715 in prj fpth = os.path.join(mpth, 'test.prj') fpth2 = os.path.join(mpth, '26715.prj') shutil.copy(fpth, fpth2) fpth = os.path.join(mpth, 'test.shp') recarray2shp(recarray, geoms, fpth, prj=fpth2) # test_dtypes fpth = os.path.join(mpth, 'test.shp') ra = shp2recarray(fpth) assert "int" in ra.dtype['k'].name assert "float" in ra.dtype['stuff'].name assert "bool" in ra.dtype['stuf'].name assert "object" in ra.dtype['stf'].name assert True
def test_read_usgs_model_reference(): nlay, nrow, ncol = 1, 30, 5 delr, delc = 250, 500 #xll, yll = 272300, 5086000 model_ws = os.path.join('temp', 't007') mrf = os.path.join(model_ws, 'usgs.model.reference') shutil.copy('../examples/data/usgs.model.reference', mrf) fm = flopy.modflow m = fm.Modflow(modelname='junk', model_ws=model_ws) # feet and days dis = fm.ModflowDis(m, nlay=nlay, nrow=nrow, ncol=ncol, delr=delr, delc=delc, lenuni=1, itmuni=4) m.write_input() # test reading of SR information from usgs.model.reference m2 = fm.Modflow.load('junk.nam', model_ws=os.path.join('temp', 't007')) from flopy.utils.reference import SpatialReference d = SpatialReference.read_usgs_model_reference_file(mrf) assert m2.sr.xul == d['xul'] assert m2.sr.yul == d['yul'] assert m2.sr.rotation == d['rotation'] assert m2.sr.lenuni == d['lenuni'] assert m2.sr.epsg == d['epsg'] # test reading non-default units from usgs.model.reference shutil.copy(mrf, mrf + '_copy') with open(mrf + '_copy') as src: with open(mrf, 'w') as dst: for line in src: if 'time_unit' in line: line = line.replace('days', 'seconds') elif 'length_units' in line: line = line.replace('feet', 'meters') dst.write(line) m2 = fm.Modflow.load('junk.nam', model_ws=os.path.join('temp', 't007')) assert m2.tr.itmuni == 1 assert m2.sr.lenuni == 2 # have to delete this, otherwise it will mess up other tests to_del = glob.glob(mrf + '*') for f in to_del: if os.path.exists(f): os.remove(os.path.join(f)) assert True
def init_model(self, bron_data): """ Initiziation of the model starts with the Modflow object and spatial and temporal discretization parameters of the DIS package Parameters ---------- bron_data : dict dictionary containing data to build up the model """ mf = flopy.modflow.Modflow( bron_data["modelname"], exe_name=bron_data["exe_name"], model_ws=bron_data["model_ws"], ) # --> needed when using UPW insted of LPF, version='mfnwt') # apply the spatial and temporal discretization parameters to the DIS package mf_dis = flopy.modflow.ModflowDis( mf, nlay=self.dis["nlays"], nrow=self.dis["nrow"], ncol=self.dis["ncol"], delr=self.dis["delr"], delc=self.dis["delc"], top=self.dis["top"], botm=self.dis["botm"], nper=self.dis["nper"], perlen=self.dis["perlen"], nstp=self.dis["nstp"], steady=self.dis["steady"], ) mf.dis.sr = SpatialReference( delr=self.dis["delr"], delc=self.dis["delc"], xul=self.dis["bbox_large"][0], yul=self.dis["bbox_large"][3], epsg=28992, ) mf.modelgrid.set_coord_info( xoff=self.dis["bbox_large"][0], yoff=self.dis["bbox_large"][3] - self.dis["delc"].sum(), epsg=28992, ) self.mf = mf
def __init__(self, sr=None, ax=None, model=None, dis=None, layer=0, extent=None, xul=None, yul=None, xll=None, yll=None, rotation=0., length_multiplier=1.): self.model = model self.layer = layer self.dis = dis self.sr = None if sr is not None: self.sr = copy.deepcopy(sr) elif dis is not None: # print("warning: the dis arg to model map is deprecated") self.sr = copy.deepcopy(dis.parent.sr) elif model is not None: # print("warning: the model arg to model map is deprecated") self.sr = copy.deepcopy(model.sr) else: self.sr = SpatialReference(xll, yll, xul, yul, rotation, length_multiplier) # model map override spatial reference settings if any(elem is not None for elem in (xul, yul, xll, yll)) or rotation != 0 or length_multiplier != 1.: self.sr.set_spatialreference(xul, yul, xll, yll, rotation, length_multiplier) ''' if xul is not None and yul is not None: self.sr.xul = xul if yul is not None: self.sr.yul = yul if rotation is not None: self.sr.rotation = rotation ''' if ax is None: try: self.ax = plt.gca() self.ax.set_aspect('equal') except: self.ax = plt.subplot(1, 1, 1, aspect='equal', axisbg="white") else: self.ax = ax if extent is not None: self._extent = extent else: self._extent = None # why is this non-default color scale used?? # This should be passed as a kwarg by the user to the indivudual plotting method. # self.cmap = plotutil.viridis return
def _set_spatialreference(self): """ Define structured or unstructured spatial reference based on MODFLOW 6 discretization type. Returns ------- sr : SpatialReference """ sr = None try: if self._grid == "DISV" or self._grid == "DISU": try: iverts, verts = self.get_verts() vertc = self.get_centroids() xc = vertc[:, 0] yc = vertc[:, 1] sr = SpatialReferenceUnstructured( xc, yc, verts, iverts, [xc.shape[0]] ) except: msg = ( "could not set spatial reference for " + "{} discretization ".format(self._grid) + "defined in {}".format(self.file.name) ) print(msg) elif self._grid == "DIS": delr, delc = self._datadict["DELR"], self._datadict["DELC"] xorigin, yorigin, rot = ( self._datadict["XORIGIN"], self._datadict["YORIGIN"], self._datadict["ANGROT"], ) sr = SpatialReference( delr=delr, delc=delc, xll=xorigin, yll=yorigin, rotation=rot, ) except: print( "could not set spatial reference for {}".format(self.file.name) ) return sr
def test_write_shapefile(): from flopy.utils.reference import SpatialReference from flopy.export.shapefile_utils import shp2recarray from flopy.export.shapefile_utils import write_grid_shapefile, write_grid_shapefile2 sr = SpatialReference( delr=np.ones(10) * 1.1, # cell spacing along model rows delc=np.ones(10) * 1.1, # cell spacing along model columns epsg=26715, lenuni=1 # MODFLOW length units ) vrts = copy.deepcopy(sr.vertices) outshp1 = os.path.join(tpth, 'junk.shp') outshp2 = os.path.join(tpth, 'junk2.shp') write_grid_shapefile(outshp1, sr, array_dict={}) write_grid_shapefile2(outshp2, sr, array_dict={}) # test that vertices aren't getting altered by writing shapefile assert np.array_equal(vrts, sr.vertices) for outshp in [outshp1, outshp2]: # check that pyshp reads integers # this only check that row/column were recorded as "N" # not how they will be cast by python or numpy import shapefile as sf sfobj = sf.Reader(outshp) for f in sfobj.fields: if f[0] == 'row' or f[0] == 'column': assert f[1] == 'N' recs = list(sfobj.records()) for r in recs[0]: assert isinstance(r, int) # check that row and column appear as integers in recarray ra = shp2recarray(outshp) assert np.issubdtype(ra.dtype['row'], np.integer) assert np.issubdtype(ra.dtype['column'], np.integer) try: # check that fiona reads integers import fiona with fiona.open(outshp) as src: meta = src.meta assert 'int' in meta['schema']['properties']['row'] assert 'int' in meta['schema']['properties']['column'] except: pass
def test_const(): fm = flopy.modflow m = fm.Modflow() dis = fm.ModflowDis(m, 1, 10, 10, lenuni=2, itmuni=4) m.sr = SpatialReference() r, d = create_sfr_data() sfr = flopy.modflow.ModflowSfr2(m, reach_data=r, segment_data={0: d}) assert sfr.const == 86400. m.dis.itmuni = 1. m.sfr.const = None assert sfr.const == 1. m.dis.lenuni = 1. m.sfr.const = None assert sfr.const == 1.486 m.dis.itmuni = 4. m.sfr.const = None assert sfr.const == 1.486 * 86400. assert True
def test_read_usgs_model_reference(): nlay, nrow, ncol = 1, 30, 5 delr, delc = 250, 500 #xll, yll = 272300, 5086000 model_ws = os.path.join('temp', 't007') mrf = os.path.join(model_ws, 'usgs.model.reference') shutil.copy('../examples/data/usgs.model.reference', mrf) fm = flopy.modflow m = fm.Modflow(modelname='junk', model_ws=model_ws) # feet and days dis = fm.ModflowDis(m, nlay=nlay, nrow=nrow, ncol=ncol, delr=delr, delc=delc, lenuni=1, itmuni=4) m.write_input() # test reading of SR information from usgs.model.reference m2 = fm.Modflow.load('junk.nam', model_ws=os.path.join('temp', 't007')) from flopy.utils.reference import SpatialReference d = SpatialReference.read_usgs_model_reference_file(mrf) assert m2.sr.xul == d['xul'] assert m2.sr.yul == d['yul'] assert m2.sr.rotation == d['rotation'] assert m2.sr.lenuni == d['lenuni'] assert m2.sr.epsg == d['epsg'] # test reading non-default units from usgs.model.reference shutil.copy(mrf, mrf+'_copy') with open(mrf+'_copy') as src: with open(mrf, 'w') as dst: for line in src: if 'time_unit' in line: line = line.replace('days', 'seconds') elif 'length_units' in line: line = line.replace('feet', 'meters') dst.write(line) m2 = fm.Modflow.load('junk.nam', model_ws=os.path.join('temp', 't007')) assert m2.tr.itmuni == 1 assert m2.sr.lenuni == 2 # have to delete this, otherwise it will mess up other tests to_del = glob.glob(mrf + '*') for f in to_del: if os.path.exists(f): os.remove(os.path.join(f)) assert True
nlay = 1 nrow, ncol = 51, 51 delr, delc = int(Lx / ncol), int(Ly / nrow) delv = (ztop - zbot) / nlay botm = np.linspace(ztop, zbot, nlay + 1) nper = 10 # annual for 10 years, find a way to do a steady-state period and then pipe in the values perlen = 365.2 # make a circle! offset = 160 / 2 proj4 = '+proj=aea +lat_1=27.5 +lat_2=35 +lat_0=31.25 +lon_0=-100 +x_0=1500000 +y_0=6000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs' xul, yul = 5661342.80316535942256451 - offset, 19628009.74438977241516113 + offset # get the row/column! delcl = np.ones(nrow) * (int(Lx / ncol)) delrl = delcl sr = SpatialReference(delr=delrl, delc=delcl, xul=xul, yul=yul) mf.sr = sr yll = yul - 8000. # measure from the bottom up well3x = 3840. + offset well3y = 8000. - 4640. - offset # measure from the bottom up def PointsInCircum(xul, yll, r, n=100): a = [[np.cos(2 * np.pi / n * i) * r, np.sin(2 * np.pi / n * i) * r] for i in range(0, n)] x0 = np.array([i[0] for i in a]) y0 = np.array([i[1] for i in a]) return [x0 + xul + well3x, y0 + yll + well3y] test = PointsInCircum(xul, yll, 50, 100)
fig.subplots_adjust(right=0.8) cbar_ax = fig.add_axes([0.85, 0.2, 0.02, 0.6]) #l, b, w, h fig.colorbar(im,cax=cbar_ax) #%% ## 4. Export to model top # Spatial Reference Module xll, yll = 2247000.00, 1330950.00 # origin of the model [m] (lower left corner) dxdy = 1609.344 # grid spacing (in model units) delc = np.ones(nrow, dtype=float) * dxdy delr = np.ones(ncol, dtype=float) * dxdy nrow , ncol = npDEM.shape rot = -2 # rotation (positive ccw) # Specify coordinate system with custom Proj4 string for IDTM83 model_proj4 = '+proj=tmerc +lat_0=42 +lon_0=-114 +k=0.9996 +x_0=2500000 +y_0=1200000 +ellps=GRS80 +units=m +no_defs' sr = SpatialReference(delr=delr, delc=delc, xll=xll, yll=yll, rotation=rot, proj4_str = model_proj4) # Modflow discretization # row and column spacings # (note that delc is column spacings along a row; delr the row spacings along a column) nlay = 1 delr = dxdy delc = dxdy # Top of model is the DEM we just created ztop = npDEM botm = np.stack( (DEMbottom1,DEMbottom2)) botm = DEMbottom2 # Initialize model objext ml = flopy.modflow.Modflow(modelname = 'TV', exe_name = 'mf2005', model_ws = root ) dis = flopy.modflow.ModflowDis(ml, nlay ,nrow, ncol , delr=delr, delc=delc ,top=ztop, botm=botm)
def write_shapefile(self, pathline_data=None, one_per_particle=True, direction='ending', shpname='endpoings.shp', sr=None, epsg=None, **kwargs): """Write pathlines to shapefile. pathline_data : np.recarry Record array of same form as that returned by EndpointFile.get_alldata. (if none, EndpointFile.get_alldata() is exported). one_per_particle : boolean (default True) True writes a single LineString with a single set of attribute data for each particle. False writes a record/geometry for each pathline segment (each row in the PathLine file). This option can be used to visualize attribute information (time, model layer, etc.) across a pathline in a GIS. direction : str String defining if starting or ending particle locations should be included in shapefile attribute information. Only used if one_per_particle=False. (default is 'ending') shpname : str File path for shapefile sr : flopy.utils.reference.SpatialReference instance Used to scale and rotate Global x,y,z values in MODPATH Endpoint file epsg : int EPSG code for writing projection (.prj) file. If this is not supplied, the proj4 string or epgs code associated with sr will be used. kwargs : keyword arguments to flopy.export.shapefile_utils.recarray2shp """ from flopy.utils.reference import SpatialReference from flopy.utils.geometry import LineString from flopy.export.shapefile_utils import recarray2shp pth = pathline_data if pth is None: pth = self._data.view(np.recarray) pth = pth.copy() pth.sort(order=['particleid', 'time']) if sr is None: sr = SpatialReference() particles = np.unique(pth.particleid) geoms = [] # 1 geometry for each path if one_per_particle: loc_inds = 0 if direction == 'ending': loc_inds = -1 pthdata = [] for pid in particles: ra = pth[pth.particleid == pid] x, y = sr.transform(ra.x, ra.y) z = ra.z geoms.append(LineString(list(zip(x, y, z)))) pthdata.append((pid, ra.particlegroup[0], ra.time.max(), ra.k[loc_inds], ra.i[loc_inds], ra.j[loc_inds])) pthdata = np.array(pthdata, dtype=[('particleid', np.int), ('particlegroup', np.int), ('time', np.float), ('k', np.int), ('i', np.int), ('j', np.int) ]).view(np.recarray) # geometry for each row in PathLine file else: dtype = pth.dtype #pthdata = np.empty((0, len(dtype)), dtype=dtype).view(np.recarray) pthdata = [] for pid in particles: ra = pth[pth.particleid == pid] x, y = sr.transform(ra.x, ra.y) z = ra.z geoms += [LineString([(x[i-1], y[i-1], z[i-1]), (x[i], y[i], z[i])]) for i in np.arange(1, (len(ra)))] #pthdata = np.append(pthdata, ra[1:]).view(np.recarray) pthdata += ra[1:].tolist() pthdata = np.array(pthdata, dtype=dtype).view(np.recarray) # convert back to one-based for n in set(self.kijnames).intersection(set(pthdata.dtype.names)): pthdata[n] += 1 recarray2shp(pthdata, geoms, shpname=shpname, epsg=sr.epsg, **kwargs)
def test_get_destination_data(): m = flopy.modflow.Modflow.load('EXAMPLE.nam', model_ws=path) m.sr = SpatialReference(delr=m.dis.delr, delc=m.dis.delc, xul=0, yul=0, rotation=30) sr = SpatialReference(delr=list(m.dis.delr), delc=list(m.dis.delc), xul=1000, yul=1000, rotation=30) sr2 = SpatialReference(xll=sr.xll, yll=sr.yll, rotation=-30) m.dis.export(path + '/dis.shp') pthld = PathlineFile(os.path.join(path, 'EXAMPLE-3.pathline')) epd = EndpointFile(os.path.join(path, 'EXAMPLE-3.endpoint')) well_epd = epd.get_destination_endpoint_data(dest_cells=[(4, 12, 12)]) well_pthld = pthld.get_destination_pathline_data(dest_cells=[(4, 12, 12)], to_recarray=True) # same particle IDs should be in both endpoint data and pathline data tval = len(set(well_epd.particleid).difference(set(well_pthld.particleid))) msg = 'same particle IDs should be in both endpoint data and pathline data' assert tval == 0, msg # check that all starting locations are included in the pathline data # (pathline data slice not just endpoints) starting_locs = ra_slice(well_epd, ['k0', 'i0', 'j0']) pathline_locs = np.array(np.array(well_pthld)[['k', 'i', 'j']].tolist(), dtype=starting_locs.dtype) assert np.all(np.in1d(starting_locs, pathline_locs)) # test writing a shapefile of endpoints epd.write_shapefile(well_epd, direction='starting', shpname=os.path.join(path, 'starting_locs.shp'), sr=m.sr) # test writing shapefile of pathlines fpth = os.path.join(path, 'pathlines_1per.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='starting', sr=m.sr, shpname=fpth) fpth = os.path.join(path, 'pathlines_1per_end.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='ending', sr=m.sr, shpname=fpth) # test writing shapefile of pathlines fpth = os.path.join(path, 'pathlines_1per2.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='starting', sr=sr, shpname=fpth) # test writing shapefile of pathlines fpth = os.path.join(path, 'pathlines_1per2_ll.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='starting', sr=sr2, shpname=fpth) fpth = os.path.join(path, 'pathlines.shp') pthld.write_shapefile(well_pthld, one_per_particle=False, sr=m.sr, shpname=fpth) # test that endpoints were rotated and written correctly from flopy.export.shapefile_utils import shp2recarray ra = shp2recarray(os.path.join(path, 'starting_locs.shp')) p3 = ra.geometry[ra.particleid == 4][0] xorig, yorig = m.sr.transform(well_epd.x0[0], well_epd.y0[0]) assert p3.x - xorig + p3.y - yorig < 1e-4 xorig, yorig = m.sr.xcentergrid[3, 4], m.sr.ycentergrid[3, 4] assert np.abs(p3.x - xorig + p3.y - yorig) < 1e-4 # this also checks for 1-based # test that particle attribute information is consistent with pathline file ra = shp2recarray(os.path.join(path, 'pathlines.shp')) inds = (ra.particleid == 8) & (ra.i == 12) & (ra.j == 12) assert ra.time[inds][0] - 20181.7 < .1 assert ra.xloc[inds][0] - 0.933 < .01 # test that k, i, j are correct for single geometry pathlines, forwards # and backwards ra = shp2recarray(os.path.join(path, 'pathlines_1per.shp')) assert ra.i[0] == 4, ra.j[0] == 5 ra = shp2recarray(os.path.join(path, 'pathlines_1per_end.shp')) assert ra.i[0] == 13, ra.j[0] == 13 # test use of arbitrary spatial reference and offset ra = shp2recarray(os.path.join(path, 'pathlines_1per2.shp')) p3_2 = ra.geometry[ra.particleid == 4][0] assert np.abs(p3_2.x[0] - sr.xcentergrid[3, 4] + p3_2.y[0] - sr.ycentergrid[3, 4]) < 1e-4 # arbitrary spatial reference with ll specified instead of ul ra = shp2recarray(os.path.join(path, 'pathlines_1per2_ll.shp')) p3_2 = ra.geometry[ra.particleid == 4][0] sr3 = SpatialReference(xll=sr.xll, yll=sr.yll, rotation=-30, delr=list(m.dis.delr), delc=list(m.dis.delc)) assert np.abs(p3_2.x[0] - sr3.xcentergrid[3, 4] + p3_2.y[0] - sr3.ycentergrid[3, 4]) < 1e-4 xul = 3628793 yul = 21940389 m = flopy.modflow.Modflow.load('EXAMPLE.nam', model_ws=path) m.sr = flopy.utils.reference.SpatialReference(delr=m.dis.delr, delc=m.dis.delc, lenuni=1, xul=xul, yul=yul, rotation=0.0) fpth = os.path.join(path, 'dis2.shp') m.dis.export(fpth) pthobj = flopy.utils.PathlineFile(os.path.join(path, 'EXAMPLE-3.pathline')) fpth = os.path.join(path, 'pathlines_1per3.shp') pthobj.write_shapefile(shpname=fpth, direction='ending', sr=m.sr)
name = 'map_test' epsg = 5070 xul, yul = 520487.3, 1194668.3 nrow, ncol = 20, 20 dxy = 5280 * .3048 buf = 1e4 bounds = xul - buf, \ yul - dxy * nrow - buf, \ xul + dxy * ncol + buf, \ yul + buf # make version of preprocessed flowlines filtered to bounding box df = shp2df('/Users/aleaf/Documents/MAP/repos/sfr_output/preprocessed/flowlines_gt20km/flowlines_gt20km_edited.shp', filter=bounds) df2shp(df, 'data/{}_flowlines.shp'.format(name), epsg=epsg) # make a spatial reference object defining the grid sr = SpatialReference(delr=np.ones(ncol, dtype=float) * dxy, delc=np.ones(nrow, dtype=float) * dxy, xul=xul, yul=yul, epsg=epsg) # export sr info to json file model_info = sr.attribute_dict model_info['nrow'] = sr.nrow model_info['ncol'] = sr.ncol model_info['delr'] = sr.delr[0] model_info['delc'] = sr.delc[0] model_info['epsg'] = sr.epsg with open('data/{}_grid.json'.format(name), 'w') as output: json.dump(model_info, output, indent=4, sort_keys=True)
def test_freyberg_export(): from flopy.utils.reference import SpatialReference namfile = 'freyberg.nam' # steady state model_ws = '../examples/data/freyberg' m = flopy.modflow.Modflow.load(namfile, model_ws=model_ws, check=False, verbose=False) # test export at model, package and object levels m.export('{}/model.shp'.format(spth)) m.wel.export('{}/wel.shp'.format(spth)) m.lpf.hk.export('{}/hk.shp'.format(spth)) m.riv.stress_period_data.export('{}/riv_spd.shp'.format(spth)) # transient # (doesn't work at model level because the total size of # the attribute fields exceeds the shapefile limit) model_ws = '../examples/data/freyberg_multilayer_transient/' m = flopy.modflow.Modflow.load( namfile, model_ws=model_ws, verbose=False, load_only=['DIS', 'BAS6', 'NWT', 'OC', 'RCH', 'WEL', 'DRN', 'UPW']) # test export without instantiating an sr outshp = os.path.join(spth, namfile[:-4] + '_drn_sparse.shp') m.drn.stress_period_data.export(outshp, sparse=True) assert os.path.exists(outshp) remove_shp(outshp) m.sr = SpatialReference(delr=m.dis.delr.array, delc=m.dis.delc.array, epsg=5070) # test export with an sr, regardless of whether or not wkt was found m.drn.stress_period_data.export(outshp, sparse=True) assert os.path.exists(outshp) remove_shp(outshp) m.sr = SpatialReference(delr=m.dis.delr.array, delc=m.dis.delc.array, epsg=3070) # if wkt text was fetched from spatialreference.org if m.sr.wkt is not None: # test default package export outshp = os.path.join(spth, namfile[:-4] + '_dis.shp') m.dis.export(outshp) prjfile = outshp.replace('.shp', '.prj') with open(prjfile) as src: prjtxt = src.read() assert prjtxt == m.sr.wkt remove_shp(outshp) # test default package export to higher level dir outshp = os.path.join('..', namfile[:-4] + '_dis.shp') m.dis.export(outshp) prjfile = outshp.replace('.shp', '.prj') with open(prjfile) as src: prjtxt = src.read() assert prjtxt == m.sr.wkt remove_shp(outshp) # test sparse package export outshp = os.path.join(spth, namfile[:-4] + '_drn_sparse.shp') m.drn.stress_period_data.export(outshp, sparse=True) prjfile = outshp.replace('.shp', '.prj') with open(prjfile) as src: prjtxt = src.read() assert prjtxt == m.sr.wkt remove_shp(outshp)
# ### Define the Model Extent, Grid Resolution, and Characteristics # It is normally good practice to group things that you might want to change into a single code block. This makes it easier to make changes and rerun the code. # In[5]: fb = flopy.modflow.Modflow.load(os.path.join(modelname + '.nam'), version='mf2005', model_ws=modelpath, verbose=True) lowerleft = [6436682.01941381, 1791562.92451396] fb.sr = SpatialReference(delr=fb.dis.delr, delc=fb.dis.delc, xll=lowerleft[0], yll=lowerleft[1], units='feet', proj4_str='EPSG:2871', rotation=23) fb.sr # ## Flopy Tutorial 1: Running the Model # # Flopy has several methods attached to the model object that can be used to run the model. They are run_model, run_model2, and run_model3. Here we use run_model3, which will write output to the notebook. # In[6]: # Imports for plotting and reading the MODFLOW binary output file import flopy.utils.binaryfile as bf
class ModelMap(object): """ Class to create a map of the model. Parameters ---------- sr : flopy.utils.reference.SpatialReference The spatial reference class (Default is None) ax : matplotlib.pyplot axis The plot axis. If not provided it, plt.gca() will be used. If there is not a current axis then a new one will be created. model : flopy.modflow object flopy model object. (Default is None) dis : flopy.modflow.ModflowDis object flopy discretization object. (Default is None) layer : int Layer to plot. Default is 0. Must be between 0 and nlay - 1. xul : float x coordinate for upper left corner yul : float y coordinate for upper left corner. The default is the sum of the delc array. rotation : float Angle of grid rotation around the upper left corner. A positive value indicates clockwise rotation. Angles are in degrees. extent : tuple of floats (xmin, xmax, ymin, ymax) will be used to specify axes limits. If None then these will be calculated based on grid, coordinates, and rotation. Notes ----- ModelMap must know the position and rotation of the grid in order to make the plot. This information is contained in the SpatialReference class (sr), which can be passed. If sr is None, then it looks for sr in dis. If dis is None, then it looks for sr in model.dis. If all of these arguments are none, then it uses xul, yul, and rotation. If none of these arguments are provided, then it puts the lower-left-hand corner of the grid at (0, 0). """ def __init__(self, sr=None, ax=None, model=None, dis=None, layer=0, extent=None, xul=None, yul=None, xll=None, yll=None, rotation=0., length_multiplier=1.): self.model = model self.layer = layer self.dis = dis self.sr = None if sr is not None: self.sr = copy.deepcopy(sr) elif dis is not None: # print("warning: the dis arg to model map is deprecated") self.sr = copy.deepcopy(dis.parent.sr) elif model is not None: # print("warning: the model arg to model map is deprecated") self.sr = copy.deepcopy(model.sr) else: self.sr = SpatialReference(xll, yll, xul, yul, rotation, length_multiplier) # model map override spatial reference settings if any(elem is not None for elem in (xul, yul, xll, yll)) or rotation != 0 or length_multiplier != 1.: self.sr.set_spatialreference(xul, yul, xll, yll, rotation, length_multiplier) ''' if xul is not None and yul is not None: self.sr.xul = xul if yul is not None: self.sr.yul = yul if rotation is not None: self.sr.rotation = rotation ''' if ax is None: try: self.ax = plt.gca() self.ax.set_aspect('equal') except: self.ax = plt.subplot(1, 1, 1, aspect='equal', axisbg="white") else: self.ax = ax if extent is not None: self._extent = extent else: self._extent = None # why is this non-default color scale used?? # This should be passed as a kwarg by the user to the indivudual plotting method. # self.cmap = plotutil.viridis return @property def extent(self): if self._extent is None: self._extent = self.sr.get_extent() return self._extent def plot_array(self, a, masked_values=None, **kwargs): """ Plot an array. If the array is three-dimensional, then the method will plot the layer tied to this class (self.layer). Parameters ---------- a : numpy.ndarray Array to plot. masked_values : iterable of floats, ints Values to mask. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.pcolormesh Returns ------- quadmesh : matplotlib.collections.QuadMesh """ if a.ndim == 3: plotarray = a[self.layer, :, :] elif a.ndim == 2: plotarray = a else: raise Exception('Array must be of dimension 2 or 3') if masked_values is not None: for mval in masked_values: plotarray = np.ma.masked_equal(plotarray, mval) if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax quadmesh = ax.pcolormesh(self.sr.xgrid, self.sr.ygrid, plotarray, **kwargs) ax.set_xlim(self.extent[0], self.extent[1]) ax.set_ylim(self.extent[2], self.extent[3]) return quadmesh def contour_array(self, a, masked_values=None, **kwargs): """ Contour an array. If the array is three-dimensional, then the method will contour the layer tied to this class (self.layer). Parameters ---------- a : numpy.ndarray Array to plot. masked_values : iterable of floats, ints Values to mask. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.pcolormesh Returns ------- contour_set : matplotlib.pyplot.contour """ if a.ndim == 3: plotarray = a[self.layer, :, :] elif a.ndim == 2: plotarray = a else: raise Exception('Array must be of dimension 2 or 3') if masked_values is not None: for mval in masked_values: plotarray = np.ma.masked_equal(plotarray, mval) if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax if 'colors' in kwargs.keys(): if 'cmap' in kwargs.keys(): cmap = kwargs.pop('cmap') cmap = None contour_set = ax.contour(self.sr.xcentergrid, self.sr.ycentergrid, plotarray, **kwargs) ax.set_xlim(self.extent[0], self.extent[1]) ax.set_ylim(self.extent[2], self.extent[3]) return contour_set def plot_inactive(self, ibound=None, color_noflow='black', **kwargs): """ Make a plot of inactive cells. If not specified, then pull ibound from the self.ml Parameters ---------- ibound : numpy.ndarray ibound array to plot. (Default is ibound in 'BAS6' package.) color_noflow : string (Default is 'black') Returns ------- quadmesh : matplotlib.collections.QuadMesh """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax if ibound is None: bas = self.model.get_package('BAS6') ibound = bas.ibound.array plotarray = np.zeros(ibound.shape, dtype=np.int) idx1 = (ibound == 0) plotarray[idx1] = 1 plotarray = np.ma.masked_equal(plotarray, 0) cmap = matplotlib.colors.ListedColormap(['0', color_noflow]) bounds = [0, 1, 2] norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N) quadmesh = self.plot_array(plotarray, cmap=cmap, norm=norm, **kwargs) return quadmesh def plot_ibound(self, ibound=None, color_noflow='black', color_ch='blue', **kwargs): """ Make a plot of ibound. If not specified, then pull ibound from the self.ml Parameters ---------- ibound : numpy.ndarray ibound array to plot. (Default is ibound in 'BAS6' package.) color_noflow : string (Default is 'black') color_ch : string Color for constant heads (Default is 'blue'.) Returns ------- quadmesh : matplotlib.collections.QuadMesh """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax if ibound is None: bas = self.model.get_package('BAS6') ibound = bas.ibound.array plotarray = np.zeros(ibound.shape, dtype=np.int) idx1 = (ibound == 0) idx2 = (ibound < 0) plotarray[idx1] = 1 plotarray[idx2] = 2 plotarray = np.ma.masked_equal(plotarray, 0) cmap = matplotlib.colors.ListedColormap(['0', color_noflow, color_ch]) bounds = [0, 1, 2, 3] norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N) quadmesh = self.plot_array(plotarray, cmap=cmap, norm=norm, **kwargs) return quadmesh def plot_grid(self, **kwargs): """ Plot the grid lines. Parameters ---------- kwargs : ax, colors. The remaining kwargs are passed into the the LineCollection constructor. Returns ------- lc : matplotlib.collections.LineCollection """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax if 'colors' not in kwargs: kwargs['colors'] = '0.5' lc = self.get_grid_line_collection(**kwargs) ax.add_collection(lc) ax.set_xlim(self.extent[0], self.extent[1]) ax.set_ylim(self.extent[2], self.extent[3]) return lc def plot_bc(self, ftype=None, package=None, kper=0, color=None, plotAll=False, **kwargs): """ Plot boundary conditions locations for a specific boundary type from a flopy model Parameters ---------- ftype : string Package name string ('WEL', 'GHB', etc.). (Default is None) package : flopy.modflow.Modflow package class instance flopy package class instance. (Default is None) kper : int Stress period to plot color : string matplotlib color string. (Default is None) plotAll : bool Boolean used to specify that boundary condition locations for all layers will be plotted on the current ModelMap layer. (Default is False) **kwargs : dictionary keyword arguments passed to matplotlib.collections.PatchCollection Returns ------- quadmesh : matplotlib.collections.QuadMesh """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax # Find package to plot if package is not None: p = package ftype = p.name[0] elif self.model is not None: if ftype is None: raise Exception('ftype not specified') ftype = ftype.upper() p = self.model.get_package(ftype) else: raise Exception('Cannot find package to plot') # Get the list data try: mflist = p.stress_period_data[kper] except Exception as e: raise Exception('Not a list-style boundary package:' + str(e)) # Return if MfList is None if mflist is None: return None nlay = self.model.nlay # Plot the list locations plotarray = np.zeros((nlay, self.sr.nrow, self.sr.ncol), dtype=np.int) if plotAll: idx = [mflist['i'], mflist['j']] # plotarray[:, idx] = 1 pa = np.zeros((self.sr.nrow, self.sr.ncol), dtype=np.int) pa[idx] = 1 for k in range(nlay): plotarray[k, :, :] = pa.copy() else: idx = [mflist['k'], mflist['i'], mflist['j']] plotarray[idx] = 1 plotarray = np.ma.masked_equal(plotarray, 0) if color is None: if ftype in bc_color_dict: c = bc_color_dict[ftype] else: c = bc_color_dict['default'] else: c = color cmap = matplotlib.colors.ListedColormap(['0', c]) bounds = [0, 1, 2] norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N) quadmesh = self.plot_array(plotarray, cmap=cmap, norm=norm, **kwargs) return quadmesh def plot_shapefile(self, shp, **kwargs): """ Plot a shapefile. The shapefile must be in the same coordinates as the rotated and offset grid. Parameters ---------- shp : string Name of the shapefile to plot kwargs : dictionary Keyword arguments passed to plotutil.plot_shapefile() """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax patch_collection = plotutil.plot_shapefile(shp, ax, **kwargs) return patch_collection def plot_cvfd(self, verts, iverts, **kwargs): """ Plot a cvfd grid. The vertices must be in the same coordinates as the rotated and offset grid. Parameters ---------- verts : ndarray 2d array of x and y points. iverts : list of lists should be of len(ncells) with a list of vertex number for each cell kwargs : dictionary Keyword arguments passed to plotutil.plot_cvfd() """ if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax patch_collection = plotutil.plot_cvfd(verts, iverts, ax, self.layer, **kwargs) return patch_collection def contour_array_cvfd(self, vertc, a, masked_values=None, **kwargs): """ Contour an array. If the array is three-dimensional, then the method will contour the layer tied to this class (self.layer). Parameters ---------- vertc : np.ndarray Array with centroid location of cvfd a : numpy.ndarray Array to plot. masked_values : iterable of floats, ints Values to mask. **kwargs : dictionary keyword arguments passed to matplotlib.pyplot.pcolormesh Returns ------- contour_set : matplotlib.pyplot.contour """ if 'ncpl' in kwargs: nlay = self.layer + 1 ncpl = kwargs.pop('ncpl') if isinstance(ncpl, int): i = int(ncpl) ncpl = np.ones((nlay), dtype=np.int) * i elif isinstance(ncpl, list) or isinstance(ncpl, tuple): ncpl = np.array(ncpl) i0 = 0 i1 = 0 for k in range(nlay): i0 = i1 i1 = i0 + ncpl[k] # retain vertc in selected layer vertc = vertc[i0:i1, :] else: i0 = 0 i1 = vertc.shape[0] plotarray = a[i0:i1] if masked_values is not None: for mval in masked_values: plotarray = np.ma.masked_equal(plotarray, mval) if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax if 'colors' in kwargs.keys(): if 'cmap' in kwargs.keys(): cmap = kwargs.pop('cmap') cmap = None contour_set = ax.tricontour(vertc[:, 0], vertc[:, 1], plotarray, **kwargs) return contour_set def plot_discharge(self, frf, fff, dis=None, flf=None, head=None, istep=1, jstep=1, normalize=False, **kwargs): """ Use quiver to plot vectors. Parameters ---------- frf : numpy.ndarray MODFLOW's 'flow right face' fff : numpy.ndarray MODFLOW's 'flow front face' flf : numpy.ndarray MODFLOW's 'flow lower face' (Default is None.) head : numpy.ndarray MODFLOW's head array. If not provided, then will assume confined conditions in order to calculated saturated thickness. istep : int row frequency to plot. (Default is 1.) jstep : int column frequency to plot. (Default is 1.) normalize : bool boolean flag used to determine if discharge vectors should be normalized using the magnitude of the specific discharge in each cell. (default is False) kwargs : dictionary Keyword arguments passed to plt.quiver() Returns ------- quiver : matplotlib.pyplot.quiver Vectors of specific discharge. """ # remove 'pivot' keyword argument # by default the center of the arrow is plotted in the center of a cell if 'pivot' in kwargs: pivot = kwargs.pop('pivot') else: pivot = 'middle' # Calculate specific discharge # make sure dis is defined if dis is None: if self.model is not None: dis = self.model.dis else: print( "ModelMap.plot_quiver() error: self.dis is None and dis arg is None ") return ib = self.model.bas6.ibound.array delr = dis.delr.array delc = dis.delc.array top = dis.top.array botm = dis.botm.array nlay, nrow, ncol = botm.shape laytyp = None hnoflo = 999. hdry = 999. if self.model is not None: lpf = self.model.get_package('LPF') if lpf is not None: laytyp = lpf.laytyp.array hdry = lpf.hdry bas = self.model.get_package('BAS6') if bas is not None: hnoflo = bas.hnoflo # If no access to head or laytyp, then calculate confined saturated # thickness by setting laytyp to zeros if head is None or laytyp is None: head = np.zeros(botm.shape, np.float32) laytyp = np.zeros((nlay), dtype=np.int) sat_thk = plotutil.saturated_thickness(head, top, botm, laytyp, [hnoflo, hdry]) # Calculate specific discharge qx, qy, qz = plotutil.centered_specific_discharge(frf, fff, flf, delr, delc, sat_thk) # Select correct slice u = qx[self.layer, :, :] v = qy[self.layer, :, :] # apply step x = self.sr.xcentergrid[::istep, ::jstep] y = self.sr.ycentergrid[::istep, ::jstep] u = u[::istep, ::jstep] v = v[::istep, ::jstep] # normalize if normalize: vmag = np.sqrt(u ** 2. + v ** 2.) idx = vmag > 0. u[idx] /= vmag[idx] v[idx] /= vmag[idx] if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax # mask discharge in inactive cells idx = (ib[self.layer, ::istep, ::jstep] == 0) u[idx] = np.nan v[idx] = np.nan # Rotate and plot urot, vrot = self.sr.rotate(u, v, self.sr.rotation) quiver = ax.quiver(x, y, urot, vrot, pivot=pivot, **kwargs) return quiver def plot_pathline(self, pl, travel_time=None, **kwargs): """ Plot the MODPATH pathlines. Parameters ---------- pl : list of rec arrays or a single rec array rec array or list of rec arrays is data returned from modpathfile PathlineFile get_data() or get_alldata() methods. Data in rec array is 'x', 'y', 'z', 'time', 'k', and 'particleid'. travel_time: float or str travel_time is a travel time selection for the displayed pathlines. If a float is passed then pathlines with times less than or equal to the passed time are plotted. If a string is passed a variety logical constraints can be added in front of a time value to select pathlines for a select period of time. Valid logical constraints are <=, <, >=, and >. For example, to select all pathlines less than 10000 days travel_time='< 10000' would be passed to plot_pathline. (default is None) kwargs : layer, ax, colors. The remaining kwargs are passed into the LineCollection constructor. If layer='all', pathlines are output for all layers Returns ------- lc : matplotlib.collections.LineCollection """ from matplotlib.collections import LineCollection # make sure pathlines is a list if not isinstance(pl, list): pl = [pl] if 'layer' in kwargs: kon = kwargs.pop('layer') if isinstance(kon, bytes): kon = kon.decode() if isinstance(kon, str): if kon.lower() == 'all': kon = -1 else: kon = self.layer else: kon = self.layer if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax if 'colors' not in kwargs: kwargs['colors'] = '0.5' linecol = [] for p in pl: if travel_time is None: tp = p.copy() else: if isinstance(travel_time, str): if '<=' in travel_time: time = float(travel_time.replace('<=', '')) idx = (p['time'] <= time) elif '<' in travel_time: time = float(travel_time.replace('<', '')) idx = (p['time'] < time) elif '>=' in travel_time: time = float(travel_time.replace('>=', '')) idx = (p['time'] >= time) elif '<' in travel_time: time = float(travel_time.replace('>', '')) idx = (p['time'] > time) else: try: time = float(travel_time) idx = (p['time'] <= time) except: errmsg = 'flopy.map.plot_pathline travel_time ' + \ 'variable cannot be parsed. ' + \ 'Acceptable logical variables are , ' + \ '<=, <, >=, and >. ' + \ 'You passed {}'.format(travel_time) raise Exception(errmsg) else: time = float(travel_time) idx = (p['time'] <= time) tp = p[idx] vlc = [] # rotate data x0r, y0r = self.sr.rotate(tp['x'], tp['y'], self.sr.rotation, 0., self.sr.yedge[0]) x0r += self.sr.xul y0r += self.sr.yul - self.sr.yedge[0] # build polyline array arr = np.vstack((x0r, y0r)).T # select based on layer if kon >= 0: kk = p['k'].copy().reshape(p.shape[0], 1) kk = np.repeat(kk, 2, axis=1) arr = np.ma.masked_where((kk != kon), arr) else: arr = np.ma.asarray(arr) # append line to linecol if there is some unmasked segment if not arr.mask.all(): linecol.append(arr) # create line collection lc = None if len(linecol) > 0: lc = LineCollection(linecol, **kwargs) ax.add_collection(lc) return lc def plot_endpoint(self, ep, direction='ending', selection=None, selection_direction=None, **kwargs): """ Plot the MODPATH endpoints. Parameters ---------- ep : rec array A numpy recarray with the endpoint particle data from the MODPATH 6 endpoint file direction : str String defining if starting or ending particle locations should be considered. (default is 'ending') selection : tuple tuple that defines the zero-base layer, row, column location (l, r, c) to use to make a selection of particle endpoints. The selection could be a well location to determine capture zone for the well. If selection is None, all particle endpoints for the user-sepcified direction will be plotted. (default is None) selection_direction : str String defining is a selection should be made on starting or ending particle locations. If selection is not None and selection_direction is None, the selection direction will be set to the opposite of direction. (default is None) kwargs : ax, c, s or size, colorbar, colorbar_label, shrink. The remaining kwargs are passed into the matplotlib scatter method. If colorbar is True a colorbar will be added to the plot. If colorbar_label is passed in and colorbar is True then colorbar_label will be passed to the colorbar set_label() method. If shrink is passed in and colorbar is True then the colorbar size will be set using shrink. Returns ------- sp : matplotlib.pyplot.scatter """ if direction.lower() == 'ending': direction = 'ending' elif direction.lower() == 'starting': direction = 'starting' else: errmsg = 'flopy.map.plot_endpoint direction must be "ending" ' + \ 'or "starting".' raise Exception(errmsg) if direction == 'starting': xp, yp = 'x0', 'y0' elif direction == 'ending': xp, yp = 'x', 'y' if selection_direction is not None: if selection_direction.lower() != 'starting' and \ selection_direction.lower() != 'ending': errmsg = 'flopy.map.plot_endpoint selection_direction ' + \ 'must be "ending" or "starting".' raise Exception(errmsg) else: if direction.lower() == 'starting': selection_direction = 'ending' elif direction.lower() == 'ending': selection_direction = 'starting' if selection is not None: try: k, i, j = selection[0], selection[1], selection[2] if selection_direction.lower() == 'starting': ksel, isel, jsel = 'k0', 'i0', 'j0' elif selection_direction.lower() == 'ending': ksel, isel, jsel = 'k', 'i', 'j' except: errmsg = 'flopy.map.plot_endpoint selection must be a ' + \ 'zero-based layer, row, column tuple (l, r, c) ' + \ 'of the location to evaluate (i.e., well location).' raise Exception(errmsg) if selection is not None: idx = (ep[ksel] == k) & (ep[isel] == i) & (ep[jsel] == j) tep = ep[idx] else: tep = ep.copy() if 'ax' in kwargs: ax = kwargs.pop('ax') else: ax = self.ax # scatter kwargs that users may redefine if 'c' not in kwargs: c = tep['finaltime'] - tep['initialtime'] else: c = np.empty((tep.shape[0]), dtype="S30") c.fill(kwargs.pop('c')) s = 50 if 's' in kwargs: s = float(kwargs.pop('s')) ** 2. elif 'size' in kwargs: s = float(kwargs.pop('size')) ** 2. # colorbar kwargs createcb = False if 'colorbar' in kwargs: createcb = kwargs.pop('colorbar') colorbar_label = 'Endpoint Time' if 'colorbar_label' in kwargs: colorbar_label = kwargs.pop('colorbar_label') shrink = 1. if 'shrink' in kwargs: shrink = float(kwargs.pop('shrink')) # rotate data x0r, y0r = self.sr.rotate(tep[xp], tep[yp], self.sr.rotation, 0., self.sr.yedge[0]) x0r += self.sr.xul y0r += self.sr.yul - self.sr.yedge[0] # build array to plot arr = np.vstack((x0r, y0r)).T # plot the end point data sp = plt.scatter(arr[:, 0], arr[:, 1], c=c, s=s, **kwargs) # add a colorbar for travel times if createcb: cb = plt.colorbar(sp, shrink=shrink) cb.set_label(colorbar_label) return sp def get_grid_line_collection(self, **kwargs): """ Get a LineCollection of the grid """ from matplotlib.collections import LineCollection lc = LineCollection(self.sr.get_grid_lines(), **kwargs) return lc
def test_get_destination_data(): m = flopy.modflow.Modflow.load('EXAMPLE.nam', model_ws=path) mg1 = m.modelgrid mg1.set_coord_info(xoff=mg1._xul_to_xll(0.0, 30.0), yoff=mg1._yul_to_yll(0.0, 30.0), angrot=30.0) mg = StructuredGrid(delc=m.dis.delc.array, delr=m.dis.delr.array) mg.set_coord_info(xoff=mg._xul_to_xll(1000.0, 30.0), yoff=mg._yul_to_yll(1000.0, 30.0), angrot=30.0) # test deprecation sr2 = SpatialReference(xll=mg.xoffset, yll=mg.yoffset, rotation=-30) if shapefile: m.dis.export(path + '/dis.shp') pthld = PathlineFile(os.path.join(path, 'EXAMPLE-3.pathline')) epd = EndpointFile(os.path.join(path, 'EXAMPLE-3.endpoint')) well_epd = epd.get_destination_endpoint_data(dest_cells=[(4, 12, 12)]) well_pthld = pthld.get_destination_pathline_data(dest_cells=[(4, 12, 12)], to_recarray=True) # same particle IDs should be in both endpoint data and pathline data tval = len(set(well_epd.particleid).difference(set(well_pthld.particleid))) msg = 'same particle IDs should be in both endpoint data and pathline data' assert tval == 0, msg # check that all starting locations are included in the pathline data # (pathline data slice not just endpoints) starting_locs = ra_slice(well_epd, ['k0', 'i0', 'j0']) pathline_locs = np.array(np.array(well_pthld)[['k', 'i', 'j']].tolist(), dtype=starting_locs.dtype) assert np.all(np.in1d(starting_locs, pathline_locs)) if shapefile is None: return # skip remainder # test writing a shapefile of endpoints epd.write_shapefile(well_epd, direction='starting', shpname=os.path.join(path, 'starting_locs.shp'), mg=m.modelgrid) # test writing shapefile of pathlines fpth = os.path.join(path, 'pathlines_1per.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='starting', mg=m.modelgrid, shpname=fpth) fpth = os.path.join(path, 'pathlines_1per_end.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='ending', mg=m.modelgrid, shpname=fpth) # test writing shapefile of pathlines fpth = os.path.join(path, 'pathlines_1per2.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='starting', mg=mg, shpname=fpth) # test writing shapefile of pathlines fpth = os.path.join(path, 'pathlines_1per2_ll.shp') pthld.write_shapefile(well_pthld, one_per_particle=True, direction='starting', mg=sr2, shpname=fpth) fpth = os.path.join(path, 'pathlines.shp') pthld.write_shapefile(well_pthld, one_per_particle=False, mg=m.modelgrid, shpname=fpth) # test that endpoints were rotated and written correctly from flopy.export.shapefile_utils import shp2recarray ra = shp2recarray(os.path.join(path, 'starting_locs.shp')) p3 = ra.geometry[ra.particleid == 4][0] xorig, yorig = m.modelgrid.get_coords(well_epd.x0[0], well_epd.y0[0]) assert p3.x - xorig + p3.y - yorig < 1e-4 xorig, yorig = mg1.xcellcenters[3, 4], mg1.ycellcenters[3, 4] assert np.abs(p3.x - xorig + p3.y - yorig) < 1e-4 # this also checks for 1-based # test that particle attribute information is consistent with pathline file ra = shp2recarray(os.path.join(path, 'pathlines.shp')) inds = (ra.particleid == 8) & (ra.i == 12) & (ra.j == 12) assert ra.time[inds][0] - 20181.7 < .1 assert ra.xloc[inds][0] - 0.933 < .01 # test that k, i, j are correct for single geometry pathlines, forwards # and backwards ra = shp2recarray(os.path.join(path, 'pathlines_1per.shp')) assert ra.i[0] == 4, ra.j[0] == 5 ra = shp2recarray(os.path.join(path, 'pathlines_1per_end.shp')) assert ra.i[0] == 13, ra.j[0] == 13 # test use of arbitrary spatial reference and offset mg1.set_coord_info(xoff=mg.xoffset, yoff=mg.yoffset, angrot=mg.angrot, epsg=mg.epsg, proj4=mg.proj4) ra = shp2recarray(os.path.join(path, 'pathlines_1per2.shp')) p3_2 = ra.geometry[ra.particleid == 4][0] test1 = mg1.xcellcenters[3, 4] test2 = mg1.ycellcenters[3, 4] assert np.abs(p3_2.x[0] - mg1.xcellcenters[3, 4] + p3_2.y[0] - mg1.ycellcenters[3, 4]) < 1e-4 # arbitrary spatial reference with ll specified instead of ul ra = shp2recarray(os.path.join(path, 'pathlines_1per2_ll.shp')) p3_2 = ra.geometry[ra.particleid == 4][0] #sr3 = SpatialReference(xll=sr.xll, yll=sr.yll, rotation=-30, # delc=list(m.dis.delc)) mg.set_coord_info(xoff=mg.xoffset, yoff=mg.yoffset, angrot=-30.0) assert np.abs(p3_2.x[0] - mg.xcellcenters[3, 4] + p3_2.y[0] - mg.ycellcenters[3, 4]) < 1e-4 xul = 3628793 yul = 21940389 m = flopy.modflow.Modflow.load('EXAMPLE.nam', model_ws=path) mg4 = m.modelgrid mg4.set_coord_info(xoff=mg4._xul_to_xll(xul, 0.0), yoff=mg4._yul_to_yll(yul, 0.0), angrot=0.0, epsg=mg4.epsg, proj4=mg4.proj4) fpth = os.path.join(path, 'dis2.shp') m.dis.export(fpth) pthobj = flopy.utils.PathlineFile(os.path.join(path, 'EXAMPLE-3.pathline')) fpth = os.path.join(path, 'pathlines_1per3.shp') pthobj.write_shapefile(shpname=fpth, direction='ending', mg=mg4)
# make a shapefile! circle = pd.DataFrame({'x':xcirc, 'y':ycirc}) # print(circle) circle['cirque'] = circle.apply(lambda x: Point((float(x.x), float(x.y))), axis=1) circle = geopandas.GeoDataFrame(circle, geometry = 'cirque') circle.to_file('starting_circle.shp', driver='ESRI Shapefile') # get the row/column! delcl = np.ones(nrow)*(int(Lx/ncol)) delrl = delcl sr = SpatialReference(delr=delrl, delc=delcl, xul=xul, yul=yul) # the shapefile grid is wrong i think, because it's -1 on both sides row_column=sr.get_rc(xcirc, ycirc) row=row_column[0].tolist() column=row_column[1].tolist() # get the local x! for i in column: for j in xcirc: print(j) # s1 = column[i]*delc # s2 = xul + s1 # s3 = xcirc[i] - s2 # s4 = delc - s3 # s5 = 1 - (s4/delc)