Build surface water network for MODFLOW's SFR Package

Overview

Surface water network

Codacy Badge CI

Creates surface water network, which can be used to create MODFLOW's SFR.

Python packages

Python 3.6+ is required.

Required

  • geopandas - process spatial data similar to pandas
  • pyproj>=2.2 - spatial projection support
  • rtree - spatial index support

Optional

  • flopy - read/write MODFLOW models
  • netCDF4 - used to read TopNet files

Testing

Run pytest -v or python3 -m pytest -v

For faster multi-core pytest -v -n 2 (with pytest-xdist)

Examples

import geopandas
import pandas as pd
import swn
import swn.file

Read from Shapefile:

shp_srs = 'tests/data/DN2_Coastal_strahler1z_stream_vf.shp'
lines = geopandas.read_file(shp_srs)
lines.set_index('nzsegment', inplace=True, verify_integrity=True)  # optional

Or, read from PostGIS:

from sqlalchemy import create_engine, engine

con_url = engine.url.URL(drivername='postgresql', database='scigen')
con = create_engine(con_url)
sql = 'SELECT * FROM wrc.rec2_riverlines_coastal'
lines = geopandas.read_postgis(sql, con)
lines.set_index('nzsegment', inplace=True, verify_integrity=True)  # optional

Initialise and create network:

n = swn.SurfaceWaterNetwork.from_lines(lines.geometry)
print(n)
# 
   

    #   304 segments: [3046409, 3046455, ..., 3050338, 3050418]

    #   154 headwater: [3046409, 3046542, ..., 3050338, 3050418]

    #   3 outlets: [3046700, 3046737, 3046736]

    #   no diversions />
   

Plot the network, write a Shapefile, write and read a SurfaceWaterNetwork file:

n.plot()

swn.file.gdf_to_shapefile(n.segments, 'segments.shp')

n.to_pickle('network.pkl')
n = swn.SurfaceWaterNetwork.from_pickle('network.pkl')

Remove segments that meet a condition (stream order), or that are upstream/downstream from certain locations:

n.remove(n.segments.stream_order == 1, segnums=n.query(upstream=3047927))

Read flow data from a TopNet netCDF file, convert from m3/s to m3/day:

nc_path = 'tests/data/streamq_20170115_20170128_topnet_03046727_strahler1.nc'
flow = swn.file.topnet2ts(nc_path, 'mod_flow', 86400)
# remove time and truncate to closest day
flow.index = flow.index.floor('d')

# 7-day mean
flow7d = flow.resample('7D').mean()

# full mean
flow_m = pd.DataFrame(flow.mean(0)).T

Process a MODFLOW/flopy model:

import flopy

m = flopy.modflow.Modflow.load('h.nam', model_ws='tests/data', check=False)
nm = swn.SwnModflow.from_swn_flopy(n, m)
nm.default_segment_data()
nm.set_segment_data_inflow(flow_m)
nm.plot()
nm.to_pickle('sfr_network.pkl')
nm = swn.SwnModflow.from_pickle('sfr_network.pkl', n, m)
nm.set_sfr_obj()
m.sfr.write_file('file.sfr')
nm.grid_cells.to_file('grid_cells.shp')
nm.reaches.to_file('reaches.shp')

Citation

Toews, M. W.; Hemmings, B. 2019. A surface water network method for generalising streams and rapid groundwater model development. In: New Zealand Hydrological Society Conference, Rotorua, 3-6 December, 2019. p. 166-169.

Comments
  • sort index before comparing

    sort index before comparing

    Lines and polygons dfs needed to be sorted before comparing index. (I suck at git so hopefully this works. Let me know if I need to do something different.)

    opened by wkitlasten 4
  • SwnMf6 obj has no attribute 'grid_cells'

    SwnMf6 obj has no attribute 'grid_cells'

    Generating my SwnMf6 object and pickling it like this:

    ngwf = swn.SwnMf6.from_swn_flopy(n, gwf,reach_include_fraction=0)
    ngwf.to_pickle('test_pickle.pkl')
    

    Then reading it back in like this:

    ngwf = swn.SwnMf6.from_pickle('test_pickle.pkl',gwf)

    But some of the object attributes are note being set. I can manually set ngwf._swn=n, but the grid_cells seems a bit more difficult. Results in this error:

    AttributeError: 'SwnMf6' object has no attribute 'grid_cells'

    AttributeError Traceback (most recent call last) in ----> 1 ngwf.plot()

    d:\modelling\surface-water-network\swn\modflow_base.py in plot(self, column, cmap, colorbar) 996 column=column, label="reaches", legend=colorbar, ax=ax, cmap=cmap) 997 --> 998 self.grid_cells.plot(ax=ax, color="whitesmoke", edgecolor="gainsboro") 999 1000 def getpt(g, idx):

    AttributeError: 'SwnMf6' object has no attribute 'grid_cells'

    opened by wkitlasten 2
  • mf6 support

    mf6 support

    Add support for building sfr datasets for MODFLOW6. Have started plumbing in the raw datasets on branch feat_mf6sfr. Perhaps don't need to worry too much about wrangling into Flopy framework, as long as we can build a write the external files as mf6 needs them.

    opened by briochh 2
  • Fixed to_rno_elev using pandas

    Fixed to_rno_elev using pandas

    This should ensure downstream rtp < upstream rtp, plus ensure reach falls within top and bottom of upper layer.

    Also changed keep_geom_type to False to get rid of heaps of deprication warnings. Not sure if that has other ramifications.

    opened by wkitlasten 1
  • swn can only be assigned once?

    swn can only be assigned once?

    I just pulled from main to make sure I was updated and started getting the following error after trying to load the pickle. Thoughts?

    ngwf = swn.SwnMf6.from_pickle(ppth,gwf)
    
    File "d:\modelling\surface-water-network\swn\modflow\_base.py", line 126, in from_pickle
        obj.swn = swn
    
      File "d:\modelling\surface-water-network\swn\modflow\_base.py", line 152, in swn
        raise AttributeError("swn property can only be set once")
    
    AttributeError: swn property can only be set once
    
    opened by wkitlasten 1
  • Need to be able to update model arrays

    Need to be able to update model arrays

    Trying to directly assign model arrays. For example after adjusting layer bottoms in _reachbyreach_elevs or _to_rno_elevs this results in Error: can't set attribute:

    self.model.dis.botm.array = botm

    But self.model.dis.botm = botm obliterates other information typically available through ngwf.model.dis.top.get_file_entry()

    Any suggestions on how to set just the array, but leave the other information intact?

    opened by wkitlasten 1
  • flopy SFR package interaction

    flopy SFR package interaction

    There is some behaviour to address related to modifying the flopy segment and reach data frames after undertaking the processing:

    When initiallising the flopy SFR object any missing column entries are added with default values - totally cool.

    However, when updating (e.g. m.sfr.segment_data = dict) missing data columns are never constructed - this then falls apart when writing the sfr (see for e.g. around line 1637 of mfsfr2.py). for flexibility at that writing step flopy expects a number of data columns to exist (even if they are infact never used).

    Worth noting that m.sfr.segment_data = dict approaches will (I think) happily deal with additional data columns in the passed dict (or record arrays) - but the initial constructor will not.

    opened by briochh 1
  • Feat: write_formatted_frame and read_formatted_frame methods

    Feat: write_formatted_frame and read_formatted_frame methods

    Closes #59 for packagedata and diversions files, but does not change connectiondata (for now, at least)

    Also, add read_formatted_frame as a convenience to read these formats back into a pandas DataFrame.

    enhancement 
    opened by mwtoews 0
  • Use items instead of iteritems

    Use items instead of iteritems

    iteritems() is deprecated since version 1.5.0, so use items() instead.

    Also pd.Series(data, dtype=dtype) does not properly cast a dict to dtype, so use pd.Series(data).astype(dtype) instead.

    opened by mwtoews 0
  • space before # in write_packagedata (maybe write_connectiondata)?

    space before # in write_packagedata (maybe write_connectiondata)?

    I am getting a space before # in the header of my packagedata file which causes issues in pandas unless I explicitly skip that line (i.e. pandas seems to read the line as a data followed by a comment, rather than a comment line). I can't quite figure out the formatting syntax to fix it in SWN and the fix on my end is a bit annoying (check for a header in each file, parse, etc).

    opened by wkitlasten 0
  • Mark deprecation for swn.spatial.get_sindex; require geopandas >=0.9

    Mark deprecation for swn.spatial.get_sindex; require geopandas >=0.9

    This approach might have been useful for older geopandas versions, but not anymore. The performance of gdf.sindex to get/generate a spatial index is good.

    Also, require geopandas >=0.9

    opened by mwtoews 0
  • to_rno_elev method now using pandas

    to_rno_elev method now using pandas

    This PR should take care of some bugs and speed things up. I also added a small work around for the rare case when a stream vertex falls on a grid line, resulting in the stream reach being a POINT. Probably a better way to handle it, but it got me past my issue.

    opened by wkitlasten 0
  • recurse_upstream issue

    recurse_upstream issue

    I am running into this issue with the DN3 network in Otago. Presumably it is an issue in the network. Is it a network connection/routing issue? Shapefile topology issue? Something else?

    File "D:\modelling\nzmf6\nzmf6\utils.py", line 315, in add_sfr n = swn.SurfaceWaterNetwork.from_lines(lines.geometry) File "d:\modelling\surface-water-network\swn\core.py", line 336, in from_lines recurse_upstream(segnum, segnum, 0, 0.0) File "d:\modelling\surface-water-network\swn\core.py", line 333, in recurse_upstream recurse_upstream(from_segnum, cat_group, num, dist) File "d:\modelling\surface-water-network\swn\core.py", line 333, in recurse_upstream recurse_upstream(from_segnum, cat_group, num, dist) File "d:\modelling\surface-water-network\swn\core.py", line 333, in recurse_upstream recurse_upstream(from_segnum, cat_group, num, dist) [Previous line repeated 976 more times] File "d:\modelling\surface-water-network\swn\core.py", line 329, in recurse_upstream dist += obj.segments.geometry[segnum].length File "C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\geopandas\geoseries.py", line 558, in getitem return self._wrapped_pandas_method("getitem", key) File "C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\geopandas\geoseries.py", line 551, in _wrapped_pandas_method val = getattr(super(GeoSeries, self), mtd)(*args, **kwargs) File "C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\pandas\core\series.py", line 942, in getitem return self._get_value(key) File "C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\pandas\core\series.py", line 1052, in _get_value return self.index._get_values_for_loc(self, loc, label) File "C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\pandas\core\indexes\base.py", line 5184, in _get_values_for_loc return series._values[loc] File "C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\geopandas\array.py", line 376, in getitem if isinstance(idx, numbers.Integral): File "C:\ProgramData\Anaconda3\envs\py37\lib\abc.py", line 139, in instancecheck return _abc_instancecheck(cls, instance) File "C:\ProgramData\Anaconda3\envs\py37\lib\abc.py", line 143, in subclasscheck return _abc_subclasscheck(cls, subclass) RecursionError: maximum recursion depth exceeded in comparison

    opened by wkitlasten 0
  • Massive geopandas deprication warnings

    Massive geopandas deprication warnings

    Not sure how to address this, but the repeated warnings from geopandas (0.9.0) are a bit overwhelming.

    My code: ngwf = swn.SwnMf6.from_swn_flopy(n, model=gwf)

    The warning (x heaps): C:\ProgramData\Anaconda3\envs\py37\lib\site-packages\geopandas\geoseries.py:207: DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.

    Not sure where the empty series is being being created. Any suggestions?

    opened by wkitlasten 1
  • SwnModflowBase methods using full swn rather than limited to the model?

    SwnModflowBase methods using full swn rather than limited to the model?

    More of an enhancement perhaps: Is the SwnModflowBase class using the full swn object for calculations? For example, is "set_reach_data_from_segments" doing calcs on all of the swn.segments (e.g. line 1050, "segdat = self._swn.pair_segments_frame")? There are about 5,000 reaches in my MF6 model, but it appears calcs are being done on all 300,000 segments in the swn object, unless I am missing something (which is usually the case!).

    opened by wkitlasten 0
  • Another shapely deprecation warning trigger

    Another shapely deprecation warning trigger

    @mwtoews another spot where we a triggering a shapely deprecation warning.

    Are we happy to just be swallowing these for now?

    https://github.com/mwtoews/surface-water-network/blob/63cc21ca72d95e150dbeb7789a4364b1339ae9a8/swn/modflow/_base.py#L559

    opened by briochh 0
Releases(0.5)
Owner
Mike Taves
Hydrogeologist, numerical modeller, GIS guru. Last name is also Toews.
Mike Taves
A Python framework for interacting with Solana's Pyth network.

Pyth Network A basic Python framework for reading and decoding data regarding the Pyth network

1 Nov 29, 2021
A library of functions that can be used to manage the download of claims from the LBRY network.

lbrytools A library of functions that can be used to manage the download of claims from the LBRY network. It includes methods to download claims by UR

13 Dec 03, 2022
A Python based command line ARP Spoofer utility, which takes input as arguments for the exact target IP and gateway IP for which you wish to Spoof ARP request

A Python based command line ARP Spoofer utility, which takes input as arguments for the exact target IP and gateway IP for which you wish to Spoof ARP request

Abhinandan Khurana 1 Feb 10, 2022
Program can control your server via discord bot

GTPS Controller Program can control your server via discord bot Require Python How To Use Download This Source Extract The Zip File Paste gtps.py to y

Lamp 2 Mar 15, 2022
Equibles Stocks API for Python

Equibles Stocks API for Python Requirements. Python 2.7 and 3.4+ Installation & Usage pip install If the python package is hosted on Github, you can i

Equibles 3 Apr 15, 2022
Module for convenient work with TCP sockets.

m_socket-py Module for convenient work with TCP sockets. Contributing Pool Request is supported! Ask questions in the Issues section. License Copyrigh

Egor Arskiy 5 Mar 09, 2022
A simple Tor switcher script switches tor nodes in interval of time

Tor_Switcher A simple Tor switcher script switches tor nodes in interval of time This script will switch tor nodes in every interval of time that you

d4rk sh4d0w 2 Nov 15, 2021
The World Most Fastest Proxy Checker In Python, Maybe?!

The World's Most Fastest Proxy Checker In Python, Maybe?! Features Based on Python 3.7+ Save Valid Porixes into the custom file Multi-Thread Fully Asy

Cyber 4 Feb 10, 2022
DEMO SOCKET AF INET SSL PYTHON

DEMO_SOCKET_AF_INET_SSL_PYTHON Python demo of socket family as AF_INET using TCP with SSL. Compatibility : macOS & GNU/Linux Network Topology style :

Enola 1 Jan 24, 2022
NetworkX is a Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks.

NetworkX is a Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks.

NetworkX 12k Jan 02, 2023
A Simple Web Server made by Python3.

A Simple Web Server made by Python3.

GGN_2015 2 Nov 27, 2021
Timeouts for popular Python packages

Python Timeouts An unresponsive service can be worse than a down one. It can tie up your entire system if not handled properly. All network requests s

Andrew Kane 11 Nov 22, 2022
Base on browser-time to get har from network, and use python to analyze the data .

base on browser-time to get har from network, and use python to analyze the data

1 Dec 20, 2021
Building a Robust IOT device which is customizable, encrypted, secure and user friendly

Building a Robust IOT device which is customizable, encrypted, secure and user friendly, which uses a single GPIO pin to extract multiple sensor values

1 Jan 03, 2022
Netwalk is a Python library to discover, parse, analyze and change Cisco switched networks

Netwalk is a Python library born out of a large remadiation project aimed at making network device discovery and management as fast and painless as possible.

38 Nov 07, 2022
JF⚡can - Super fast port scanning & service discovery using Masscan and Nmap. Scan large networks with Masscan and use Nmap's scripting abilities to discover information about services. Generate report.

Description Killing features Perform a large-scale scans using Nmap! Allows you to use Masscan to scan targets and execute Nmap on detected ports with

377 Jan 03, 2023
订阅转换,添加免流host

普通订阅转免流订阅 原理 将原来的订阅解析后添加免流host 使用方法 服务器域名/&&订阅链接&&免流host&&转换后服务器前缀 我这里已经在服务器上搭建好了

163 Apr 01, 2022
This application aims to read all wifi passwords and visualizes the complexity in graph formation by taking into account several criteria and help you generate new random passwords.

This application aims to read all wifi passwords and visualizes the complexity in graph formation by taking into account several criteria and help you generate new random passwords.

Njomza Rexhepi 0 May 29, 2022
Python Scrcpy Client - allows you to view and control android device in realtime

Python Scrcpy Client This package allows you to view and control android device in realtime. Note: This gif is compressed and experience lower quality

LengYue 126 Jan 02, 2023
Simplest dashboard for WireGuard VPN written in Python w/ Flask

Hi! I'm planning the next major update for this project, please let me know if you have any suggestions or feature requests ;) You can create an issue

Donald Zou 763 Jan 02, 2023