From c176c1ebe03d39d9c91384fcbc0fe65a716c2370 Mon Sep 17 00:00:00 2001 From: aleaf Date: Wed, 9 Sep 2020 21:57:23 -0500 Subject: [PATCH] docs: working on code reference; some minor clean-up --- ci/37-conda-flopy.yml | 2 +- ci/latest-python-flopy-develop.yml | 2 +- docs/source/api/index.rst | 2 + docs/source/api/sfrmaker.grid.rst | 7 + docs/source/api/sfrmaker.mf5to6.rst | 7 + docs/source/conf.py | 3 +- examples/tylerforks/grid.dbf | Bin 1616354 -> 1616354 bytes sfrmaker/grid.py | 81 ++++++++- sfrmaker/lines.py | 273 +++++++++++++++++++++++----- sfrmaker/observations.py | 3 +- sfrmaker/preprocessing.py | 6 +- sfrmaker/sfrdata.py | 64 ++++++- 12 files changed, 379 insertions(+), 71 deletions(-) create mode 100644 docs/source/api/sfrmaker.grid.rst create mode 100644 docs/source/api/sfrmaker.mf5to6.rst diff --git a/ci/37-conda-flopy.yml b/ci/37-conda-flopy.yml index 4ce21168..cc971f86 100644 --- a/ci/37-conda-flopy.yml +++ b/ci/37-conda-flopy.yml @@ -31,8 +31,8 @@ dependencies: - sphinx-copybutton - sphinx_rtd_theme - flopy - - gis-utils - pip - pip: + - gis-utils - pytest-timeout - modflow-export diff --git a/ci/latest-python-flopy-develop.yml b/ci/latest-python-flopy-develop.yml index b465a0f2..8e2d025f 100644 --- a/ci/latest-python-flopy-develop.yml +++ b/ci/latest-python-flopy-develop.yml @@ -30,9 +30,9 @@ dependencies: - nbsphinx # for rendering notebooks in sphinx-generated docs - sphinx-copybutton - sphinx_rtd_theme - - gis-utils - pip - pip: + - gis-utils - pytest-timeout - git+git://github.com/modflowpy/flopy@develop - modflow-export diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index 8e14ee1f..fa799556 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -3,7 +3,9 @@ Modules .. toctree:: + Grid Module Lines Module + MODFLOW-2005 to 6 Module Preprocessing Module SFRData Module Utilities Module \ No newline at end of file diff --git a/docs/source/api/sfrmaker.grid.rst b/docs/source/api/sfrmaker.grid.rst new file mode 100644 index 00000000..c7bbc8ca --- /dev/null +++ b/docs/source/api/sfrmaker.grid.rst @@ -0,0 +1,7 @@ +The Grid Module +============================= + +.. automodule:: sfrmaker.grid + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/api/sfrmaker.mf5to6.rst b/docs/source/api/sfrmaker.mf5to6.rst new file mode 100644 index 00000000..19c43fe9 --- /dev/null +++ b/docs/source/api/sfrmaker.mf5to6.rst @@ -0,0 +1,7 @@ +The MODFLOW-2005 to 6 Module +============================= + +.. automodule:: sfrmaker.mf5to6 + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 2fa6ee0a..d1d96fae 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -205,5 +205,6 @@ 'matplotlib': ('https://matplotlib.org', None), 'flopy': ('https://modflowpy.github.io/flopydoc/', None), 'rasterstats': ('https://pythonhosted.org/rasterstats/', None), - 'shapely': ('https://shapely.readthedocs.io/en/latest/', None) + 'shapely': ('https://shapely.readthedocs.io/en/latest/', None), + 'pyproj': ('http://pyproj4.github.io/pyproj/stable/', None) } diff --git a/examples/tylerforks/grid.dbf b/examples/tylerforks/grid.dbf index 2c1d50660d18f039bba2c123803edf942f56226a..bf6f4e0ebc14c04ac83a7d0ba14854fd655b9431 100644 GIT binary patch delta 87 zcmWN@xfMiE06@`$XZaY*jIBOZNGEv3C_p80fl9d1z0dVNSCTE1e@Q4 ZA1S1iN@{7Ol}^6$lV1KZ$apYa`vcf&6%YUb diff --git a/sfrmaker/grid.py b/sfrmaker/grid.py index 0a312991..f4c8cd4d 100644 --- a/sfrmaker/grid.py +++ b/sfrmaker/grid.py @@ -1,3 +1,8 @@ +""" +Module for working with model grids. For examples of how to easily set +up a StructuredGrid instance, see :ref:`Basic usage of SFRmaker in a scripting context`. +""" + import os import fiona @@ -18,22 +23,15 @@ class Grid: """Base class for model grids. Has methods and attributes - that are common to both Structured and Unstructured Grids. - - Parameters - ---------- - model_units : str ('feet' or 'meters') - Computation units units of model - crs_units : str ('feet' or 'meters') - Units of coordinate reference system for grid. - (optional; otherwise inferred from epsg, proj, or prjfile inputs. + that are common to both Structured and Unstructured Grids. Not + meant to be called directly. """ units_dict = {0: 'undefined', 'feet': 1, 'meters': 2} def __init__(self, df, model_units='undefined', crs_units=None, - bounds=None, active_area=None, + bounds=None, epsg=None, proj_str=None, prjfile=None, **kwargs): self.df = df @@ -213,6 +211,69 @@ def write_grid_shapefile(self, outshp='grid.shp'): class StructuredGrid(Grid): """Class representing a model grid that has a row/column structure. + + + Parameters + ---------- + df : DataFrame + Pandas DataFrame that is the primary container for information about the model grid. + Must have the following columns: + + ========= === ================================================== + k int model layer (zero-based) + i int model row (zero-based) + j int model column (zero-based) + isfr int flag indicating whether the cell can have SFR reaches + (0=False, 1=True) + geometry obj shapely :class:`Polygons ` of model cells + ========= === ================================================== + + xul : float, optional + Upper left corner of the grid x-coordinate. Only used for creating + the :attr:`transform` attribute, by default None + yul : [type], optional + Upper left corner of the grid y-coordinate. Only used for creating + the :attr:`transform` attribute, by default None + dx : float, optional + Uniform spacing in the x-direction (if the grid is uniform), + Only used for creating + the :attr:`transform` attribute, by default None + dy : float, optional + Uniform spacing in the x-direction (if the grid is uniform), + Only used for creating + the :attr:`transform` attribute, by default None + rotation : float, optional + Grid rotation angle in degrees, counter-clockwise + about the origin, by default 0. Only used for creating + the :attr:`transform` attribute, by default None + uniform : bool, optional + Optional flag indicating the grid is uniform, + by default None + model_units : str, optional, {'meters', 'feet', ..} + Model length units, by default 'undefined' + crs_units : str, optional, {'meters', 'feet', ..} + Coordinate reference system length. Usually these + are read from the CRS information below, + by default None + bounds : tuple, optional + (left, bottom, top, right) edges of the grid bounding box, if known. + Otherwise, the :attr:`bounds` attribute is computed from the + shapely :class:`Polygons ` in the :attr:`Grid DataFrame ` attribute. + by default None + active_area : shapely Polygon, list of Polygons, or shapefile path, optional + Polygon defining the active portion of the model grid. + Polygons must be in same CRS as linework; shapefile features will be reprojected if their crs is different. + by default None, in which case the entire grid is assumed to be active. + epsg: int, optional + EPSG code identifying Coordinate Reference System (CRS) + for features in the input shapefile. + proj_str: str, optional + proj_str string identifying CRS for features in the input shapefile. + prjfile: str, optional + File path to projection (.prj) file identifying CRS + for features in the input shapefile. By default, + the projection file included with the input shapefile + will be used. """ _structured = True diff --git a/sfrmaker/lines.py b/sfrmaker/lines.py index 70b0e8f5..5ed092ba 100644 --- a/sfrmaker/lines.py +++ b/sfrmaker/lines.py @@ -5,7 +5,7 @@ import pandas as pd from shapely.geometry import box import flopy -from gisutils import shp2df, df2shp, project +from gisutils import shp2df, df2shp, project, get_authority_crs import sfrmaker from sfrmaker.routing import pick_toids, find_path, make_graph, renumber_segments from sfrmaker.checks import routing_is_circular, is_to_one @@ -26,14 +26,17 @@ class Lines: df : DataFrame Dataframe with linestring features and attribute information. Must have the following columns: - id: sequential integers for identifying each feature - toid: integers representing routing connections - ... - geometry: shapely LineString objects for each feature - attr_length_units : 'meters' or 'feet' + + ============ ===================================================== + **id** sequential integers for identifying each feature + **toid** integers representing routing connections + **geometry** shapely :class:`LineString` objects for each feature + ============ ===================================================== + + attr_length_units : str, {'meters', 'feet', ..} Length units for feature attributes (e.g. width, arbolate sum, etc.) (default 'meters') - attr_height_units : 'meters' or 'feet' + attr_height_units : str, {'meters', 'feet', ..} Length units for elevation attributes (default 'meters') epsg: int @@ -47,6 +50,10 @@ class Lines: File path to projection (.prj) file identifying CRS for features in df.geometry (optional) + + Attributes + ---------- + crs : :class:`sfrmaker.gis.CRS` instance """ def __init__(self, df=None, @@ -69,32 +76,10 @@ def __init__(self, df=None, self._original_routing = self.routing.copy() - @property - def attr_to_m(self): - if self.attr_length_units == 'feet': - return 0.3048 - return 1.0 - - @property - def attr_to_ft(self): - if self.attr_length_units == 'feet': - return 1. - return 1 / 0.3048 - - @property - def geometry_to_m(self): - if self.geometry_length_units == 'feet': - return 0.3048 - return 1.0 - - @property - def geometry_to_ft(self): - if self.geometry_length_units == 'feet': - return 1. - return 1 / 0.3048 - @property def geometry_length_units(self): + """Length units of reach LineString geometries. + """ self._geometry_length_units = self.crs.length_units if self._geometry_length_units is None: print("Warning: No length units specified in CRS for input LineStrings; " @@ -104,6 +89,9 @@ def geometry_length_units(self): @property def routing(self): + """Dictionary of routing connections from ids (keys) + to to_ids (values). + """ if self._routing is None or self._routing_changed(): toid = self.df.toid.values # check whether or not routing is @@ -129,6 +117,10 @@ def routing(self): @property def paths(self): + """Dictionary of paths, where each value is a list + of downstream lines constituting a flow path to an outlet + for a given line (key). + """ if self._paths is None: self._set_paths() return self._paths @@ -158,9 +150,37 @@ def cull(self, feature, simplify=False, tol=None, feature : shapely Polygon, list of Polygons, or shapefile path Polygons must be in same CRS as linework; shapefile features will be reprojected if their crs is different. + simplify : bool + Option to simplify the polygon, which can speed intersection + with the lines. + tol: float + Simplification tolerance (distance), in the units of the LineStrings + (usually meters). + feature_crs : obj + A Python int, dict, str, or :py:class:`pyproj.crs.CRS` instance + passed to the :py:meth:`pyproj.crs.CRS.from_user_input` + See http://pyproj4.github.io/pyproj/stable/api/crs/crs.html#pyproj.crs.CRS.from_user_input. + Can be any of: + + - PROJ string + - Dictionary of PROJ parameters + - PROJ keyword arguments for parameters + - JSON string with PROJ parameters + - CRS WKT string + - An authority string [i.e. 'epsg:4326'] + - An EPSG integer code [i.e. 4326] + - A tuple of ("auth_name": "auth_code") [i.e ('epsg', '4326')] + - An object with a `to_wkt` method. + - A :class:`pyproj.crs.CRS` class inplace : bool If True, the attribute .df is modified; if False, a copy of .df is returned. + + Returns + ------- + df : DataFrame + Version of the :py:attr:`Lines.df` DataFrame + containing only the lines that intersect the ``feature``. """ print('\nCulling hydrography to active area...') ta = time.time() @@ -217,9 +237,9 @@ def intersect(self, grid, size_thresh=1e5): """ from .gis import intersect, intersect_rtree - # reproject the flowlines if they aren't in same CRS as grid + # to_crs the flowlines if they aren't in same CRS as grid if self.crs != grid.crs: - self.reproject(grid.crs.proj_str) + self.to_crs(grid.crs.proj_str) grid_polygons = grid.df.geometry.tolist() stream_linework = self.df.geometry.tolist() @@ -259,19 +279,49 @@ def intersect(self, grid, size_thresh=1e5): column_order.remove('j') return reach_data[column_order].copy() - def reproject(self, dest_proj_str): + def to_crs(self, dest_crs): + """Reproject the LineStrings in :py:attr:`Lines.df` to + a different Coordinate Reference System. + + Parameters + ---------- + dest_crs : obj + A Python int, dict, str, or :py:class:`pyproj.crs.CRS` instance + passed to the :py:meth:`pyproj.crs.CRS.from_user_input` + See http://pyproj4.github.io/pyproj/stable/api/crs/crs.html#pyproj.crs.CRS.from_user_input. + Can be any of: + + - PROJ string + - Dictionary of PROJ parameters + - PROJ keyword arguments for parameters + - JSON string with PROJ parameters + - CRS WKT string + - An authority string [i.e. 'epsg:4326'] + - An EPSG integer code [i.e. 4326] + - A tuple of ("auth_name": "auth_code") [i.e ('epsg', '4326')] + - An object with a `to_wkt` method. + - A :class:`pyproj.crs.CRS` class + """ assert self.crs.proj_str is not None, "No proj_str string for flowlines" - assert dest_proj_str is not None, "No destination CRS." + assert dest_crs is not None, "No destination CRS." + dest_crs = get_authority_crs(dest_crs) print('\nreprojecting hydrography from\n{}\nto\n{}\n'.format(self.crs.proj_str, - dest_proj_str)) - geoms = project(self.df.geometry, self.crs.proj_str, dest_proj_str) + dest_crs)) + geoms = project(self.df.geometry, self.crs.pyproj_crs, dest_crs) assert np.isfinite(np.max(geoms[0].xy[0])), \ "Invalid reprojection; check CRS for lines and grid." self.df['geometry'] = geoms - self.crs.proj_str = dest_proj_str + self.crs = CRS(proj_str=dest_crs.srs) def write_shapefile(self, outshp='flowlines.shp'): + """Write a shapefile of :py:attr:`Lines.df`. + + Parameters + ---------- + outshp : str, optional + Shapefile name, by default 'flowlines.shp' + """ df2shp(self.df, outshp, epsg=self.crs.epsg, prj=self.crs.prjfile) @classmethod @@ -287,13 +337,61 @@ def from_shapefile(cls, shapefile, attr_length_units='meters', attr_height_units='meters', filter=None, epsg=None, proj_str=None, prjfile=None): - """ + """Create a Lines instance from a shapefile. + Parameters ---------- + shapefile : str + Input shapefile + id_column : str, optional + Attribute field with line identifiers, + by default 'id' + routing_column : str, optional + Attribute field with downstream routing connections, + by default 'toid' + arbolate_sum_column2 : str, optional + Attribute field with arbolate sums at downstream ends of lines, + by default 'asum2' + width1_column : str, optional + Attribute field with channel widths at upstream ends of lines, + by default 'width1' + width2_column : str, optional + Attribute field with channel widths at downstream ends of lines, + by default 'width2' + up_elevation_column : str, optional + Attribute field with elevations at upstream ends of lines, + by default 'elevup' + dn_elevation_column : str, optional + Attribute field with elevations at downstream ends of lines, + by default 'elevdn' + name_column : str, optional + Attribute field with feature names, + by default 'name' + attr_length_units : str, optional + Length units for feature attributes (e.g. width, arbolate sum, etc.) + By default, meters. + attr_height_units : str, optional + Length units for elevation attributes + By default, 'meters'. + filter : tuple, optional + (xmin, ymin, xmax, ymax) bounding box to filter which records + are read from the shapefile. By default None. + epsg: int, optional + EPSG code identifying Coordinate Reference System (CRS) + for features in the input shapefile. + proj_str: str, optional + proj_str string identifying CRS for features in the input shapefile. + prjfile: str, optional + File path to projection (.prj) file identifying CRS + for features in the input shapefile. By default, + the projection file included with the input shapefile + will be used. - filter : tuple or str - Bounding box (tuple) or shapefile of model stream network area. - """ + Returns + ------- + lines : :class:`Lines` instance + """ + if prjfile is None: prjfile = shapefile.replace('.shp', '.prj') @@ -335,7 +433,61 @@ def from_dataframe(cls, df, attr_length_units='meters', attr_height_units='meters', epsg=None, proj_str=None, prjfile=None): + """[summary] + Parameters + ---------- + df : DataFrame + Pandas DataFrame with flowline information, including + shapely :class:`LineStrings ` in a `'geometry'` column. + id_column : str, optional + Attribute field with line identifiers, + by default 'id' + routing_column : str, optional + Attribute field with downstream routing connections, + by default 'toid' + arbolate_sum_column2 : str, optional + Attribute field with arbolate sums at downstream ends of lines, + by default 'asum2' + width1_column : str, optional + Attribute field with channel widths at upstream ends of lines, + by default 'width1' + width2_column : str, optional + Attribute field with channel widths at downstream ends of lines, + by default 'width2' + up_elevation_column : str, optional + Attribute field with elevations at upstream ends of lines, + by default 'elevup' + dn_elevation_column : str, optional + Attribute field with elevations at downstream ends of lines, + by default 'elevdn' + name_column : str, optional + Attribute field with feature names, + by default 'name' + attr_length_units : str, optional + Length units for feature attributes (e.g. width, arbolate sum, etc.) + By default, meters. + attr_height_units : str, optional + Length units for elevation attributes + By default, 'meters'. + filter : tuple, optional + (xmin, ymin, xmax, ymax) bounding box to filter which records + are read from the shapefile. By default None. + epsg: int, optional + EPSG code identifying Coordinate Reference System (CRS) + for features in the input shapefile. + proj_str: str, optional + proj_str string identifying CRS for features in the input shapefile. + prjfile: str, optional + File path to projection (.prj) file identifying CRS + for features in the input shapefile. By default, + the projection file included with the input shapefile + will be used. + + Returns + ------- + lines : :class:`Lines` instance + """ assert geometry_column in df.columns, \ "No feature geometries found: dataframe column '{}' doesn't exist.".format(geometry_column) assert routing_column in df.columns, \ @@ -385,6 +537,15 @@ def from_nhdplus_v2(cls, NHDPlus_paths=None, """ Parameters ========== + NHDPlus_paths : str or list of strings + List of paths to the root folders of NHDPlus drainage basins + to include, assuming the file structure is the same as + downloaded from the NHDPlus version 2 website. For example:: + + NHDPlus_paths=['/NHDPlusGL/NHDPlus04/', + '/NHDPlusMS/NHDPlus07/'] + + for the Great Lakes (04) and Upper Mississippi (07) basins. NHDFlowlines : str or list of strings. Shapefile or list of NHDFlowline shapefiles containing feature geometries (line arcs) for stream network. Must contain @@ -404,6 +565,20 @@ def from_nhdplus_v2(cls, NHDPlus_paths=None, COMID : common identifier number filter : tuple or str Bounding box (tuple) or shapefile of model stream network area. + epsg: int, optional + EPSG code identifying Coordinate Reference System (CRS) + for features in the input shapefile. + proj_str: str, optional + proj_str string identifying CRS for features in the input shapefile. + prjfile: str, optional + File path to projection (.prj) file identifying CRS + for features in the input shapefile. By default, + the projection file included with the input shapefile + will be used. + + Returns + ------- + lines : :class:`Lines` instance """ df = load_nhdplus_v2(NHDPlus_paths=NHDPlus_paths, NHDFlowlines=NHDFlowlines, PlusFlowlineVAA=PlusFlowlineVAA, @@ -490,7 +665,7 @@ def to_sfr(self, grid=None, the most downstream reach are dropped. package_name : str Base name for writing sfr output. - kwargs : keyword arguments to sfrmaker.SFRData + kwargs : keyword arguments to :class:`SFRData` Returns ------- @@ -527,9 +702,9 @@ def to_sfr(self, grid=None, mult_h = convert_length_units(self.attr_height_units, model_length_units) gis_mult = convert_length_units(self.geometry_length_units, model_length_units) - # reproject the flowlines if they aren't in same CRS as grid + # to_crs the flowlines if they aren't in same CRS as grid if self.crs != grid.crs: - self.reproject(grid.crs.proj_str) + self.to_crs(grid.crs.proj_str) # cull the flowlines to the active part of the model grid if grid.active_area is not None: self.cull(grid.active_area, inplace=True, simplify=True, tol=2000) @@ -574,11 +749,13 @@ def to_sfr(self, grid=None, raise NotImplementedError('Check length unit conversions before using this option.') asums = arbolate_sum(self.df.id, dict(zip(self.df.id, - np.array([g.length for g in self.df.geometry]) * self.geometry_to_m + np.array([g.length for g in self.df.geometry]) * convert_length_units(self.geometry_length_units, 'meters') )), self.routing) else: - asums = dict(zip(self.df.id, self.df.asum2 * self.attr_to_m)) + asums = dict(zip(self.df.id, + self.df.asum2 * convert_length_units(self.attr_length_units, + 'meters'))) # populate starting asums (asum1) routing_r = {v: k for k, v in self.routing.items() if v != 0} @@ -587,7 +764,7 @@ def to_sfr(self, grid=None, # compute arbolate sum at reach midpoints (in meters) lengths = rd[['line_id', 'ireach', 'geometry']].copy() - lengths['rchlen'] = np.array([g.length for g in lengths.geometry]) * self.geometry_to_m + lengths['rchlen'] = np.array([g.length for g in lengths.geometry]) * convert_length_units(self.geometry_length_units, 'meters') groups = lengths.groupby('line_id') # fragments grouped by parent line reach_cumsums = [] diff --git a/sfrmaker/observations.py b/sfrmaker/observations.py index 287b5258..b608697d 100644 --- a/sfrmaker/observations.py +++ b/sfrmaker/observations.py @@ -300,7 +300,7 @@ def locate_sites(site_data, else: raise TypeError('Datatype for site_data not understood: {}'.format(site_data)) - # reproject if crs are available + # to_crs if crs are available if locsproj4 is not None and sfrproj4 is not None: locs['geometry'] = project(locs.geometry.values, locsproj4, sfrproj4) @@ -410,6 +410,7 @@ def write_gage_package(location_data, with open(gage_namfile_entries_file, 'w') as output: for i, f in enumerate(df.gagefile.values): output.write('DATA {:d} {}\n'.format(gage_data.unit[i], f)) + return gag def write_mf6_sfr_obsfile(observation_locations, diff --git a/sfrmaker/preprocessing.py b/sfrmaker/preprocessing.py index 89fec5e6..bd595a3e 100644 --- a/sfrmaker/preprocessing.py +++ b/sfrmaker/preprocessing.py @@ -423,7 +423,7 @@ def preprocess_nhdplus(flowlines_file, pfvaa_file, pf = pf.loc[fl.index] elevslope = elevslope.loc[fl.index] - # reproject the flowlines if they are not in project_crs + # to_crs the flowlines if they are not in project_crs if project_crs is not None and flowline_crs is not None and project_crs != flowline_crs: fl['geometry'] = project(fl.geometry, flowline_crs.proj_str, project_crs.proj_str) @@ -735,7 +735,7 @@ def clip_flowlines_to_polygon(flowlines, polygon, if logger is None: logger = Logger() - # read in the active area and reproject to same crs as flowlines + # read in the active area and to_crs to same crs as flowlines flowlines_crs = None if flowlines_epsg is not None: flowlines_crs = CRS(epsg=flowlines_epsg) @@ -808,7 +808,7 @@ def sample_NARWidth(flowlines, narwidth_shapefile, waterbodies, "(CRS; i.e. with units of meters).") raise ValueError(msg) - # read in narwidth shapefile; reproject to flowline CRS + # read in narwidth shapefile; to_crs to flowline CRS nw = shp2df(narwidth_shapefile, filter=filter) narwidth_crs = CRS(prjfile=narwidth_shapefile[:-4] + '.prj') nw['geometry'] = project(nw.geometry, narwidth_crs.proj_str, flowline_crs.proj_str) diff --git a/sfrmaker/sfrdata.py b/sfrmaker/sfrdata.py index f5b933f0..df256d90 100644 --- a/sfrmaker/sfrdata.py +++ b/sfrmaker/sfrdata.py @@ -268,7 +268,7 @@ def rno_routing(self): @property def modflow_sfr2(self): - """Flopy modflow_sfr2 instance.""" + """A `flopy.modflow.mfsfr2.ModflowSfr2` represenation of the sfr dataset.""" if self._ModflowSfr2 is None: self.create_modflow_sfr2() return self._ModflowSfr2 @@ -905,7 +905,7 @@ def sample_reach_elevations(self, dem, dem_z_units=None, features = self.grid.df.loc[self.reach_data.node, 'geometry'].tolist() txt = method - # reproject features if they're not in the same crs + # to_crs features if they're not in the same crs if raster_crs != self.crs: features = project(features, self.crs.proj_str, @@ -1445,13 +1445,39 @@ def write_package(self, filename=None, version='mf2005', idomain=None, write_observations_input=True, external_files_path=None, gage_starting_unit_number=None, **kwargs): - """Write SFR package input. + """Write an SFR package input file. Parameters ---------- - version : str - 'mf2005' or 'mf6' - """ + filename : str, optional + File name for SFR package. By default None, in which + case the filename of the attached :py:class:`flopy.modflow.mfsfr2.ModflowSfr2` instance + (``SFRData.modflow_sfr2.fn_path``) is used. + version : str, optional, {'mf2005', 'mfnwt', 'mf6'} + MODFLOW version for the SFR package, by default 'mf2005' + idomain : ndarray, optional + 3D numpy array designating active cells (idomain==1). + SFR reaches in inactive cells will be written with 'none' in the cellid field. + by default None + options : list, optional + List of strings to write to the MODFLOW-6 SFR options block. + By default None. An appropriate unit_conversion is written by default. + See MODFLOW-6 documentation for other options. + run_diagnostics : bool, optional + Option to run the :ref:`diagnostic checks `. + by default True + write_observations_input : bool, optional + Option to write input to the the MODFLOW-2005 gage package or MODFLOW-6 Observations Process. + Requires attached observations, as added through the :py:meth:`SFRData.add_observations` method. + by default True + external_files_path : str, optional + Path for writing an external file for packagedata, relative to the location of the SFR package file. + If specified, an open/close statement referencing the file is written to the packagedata block. + By default, None (packagedata table is written to the SFR package file) + gage_starting_unit_number : int, optional + Starting unit number for gage output files, + by default None + """ print('SFRmaker v. {}'.format(sfrmaker.__version__)) # run the flopy SFR diagnostics if run_diagnostics: @@ -1509,6 +1535,15 @@ def write_package(self, filename=None, version='mf2005', idomain=None, sfr6.write_file(filename=filename, external_files_path=external_files_path) def write_tables(self, basename=None): + """Write :py:attr:`~SFRData.reach_data`, :py:attr:`~SFRData.segment_data`, + and :py:attr:`~SFRData.period_data` (if populated) to csv files. + + Parameters + ---------- + basename : str, optional + Base name for csv files, by default None, in which case + :py:attr:`SFRData.package_name` is used. + """ if basename is None: output_path = self._tables_path if not os.path.isdir(output_path): @@ -1531,6 +1566,23 @@ def write_tables(self, basename=None): def write_gage_package(self, filename=None, gage_package_unit=25, gage_starting_unit_number=None): + """Write observation input for the MODFLOW-2005 Gage Package. + + Parameters + ---------- + filename : str, optional + Gage package file, by default None, in which case + :py:attr:`SFRData.observations_file` is used. + gage_package_unit : int, optional + Unit number for Gage Package, by default 25 + gage_starting_unit_number : int, optional + Starting unit number for gage output files, + by default None + + Returns + ------- + gag : :py:class:`flopy.modflow.mfgage.ModflowGage` instance + """ if filename is None: filename = self.observations_file else: