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
Pritunl is a distributed enterprise vpn server built using the OpenVPN protocol.

Pritunl is a distributed enterprise vpn server built using the OpenVPN protocol.

Pritunl 3.8k Jan 03, 2023
Fast and configurable script to get and check free HTTP, SOCKS4 and SOCKS5 proxy lists from different sources and save them to files

Fast and configurable script to get and check free HTTP, SOCKS4 and SOCKS5 proxy lists from different sources and save them to files. It can also get geolocation for each proxy and check if proxies a

Almaz 385 Dec 31, 2022
The sequel to SquidNet. It has many of the previous features that were in the original script, however a lot of the functions that do not serve much functionality have been removed.

SquidNet2 The sequel to SquidNet. It has many of the previous features that were in the original script, however a lot of the functions that do not se

DrSquidX 5 Mar 25, 2022
Mass querying whois records using whois tool

Mass querying whois records using whois tool

Mohamed Elbadry 24 Nov 10, 2022
The Delegate Network: An Interactive Voice Response Delegative Democracy Implementation of Liquid Democracy

The Delegate Network Overview The delegate network is a completely transparent, easy-to-use and understand version of what is sometimes called liquid

James Bowery 2 Feb 25, 2022
Simple HTTP Server for CircuitPython

Introduction Simple HTTP Server for CircuitPython Dependencies This driver depen

Adafruit Industries 22 Jan 06, 2023
Script and library to wait for a DNS authority server to get its configuration.

DNSWait dnswait is a small script to wait for the "propagation" of a namserver configuration. Installing It's as easy as: python -m pip install dnswai

Julien Palard 14 Jan 17, 2022
An API for controlling Wi-Fi connections on Balena devices.

Description An API for controlling Wi-Fi connections on Balena devices. It does not contain an interface, instead it provides API endpoints to send re

8 Dec 25, 2022
A simple tool to utilize the basic functionality of the Private API From Virus Total

Welcome To VT-SCAN (viurs total api) Information This is a simple tool to utilize the basic functionality of the Private API From Virus Total. with th

0X0ŽĒR∅⁰ 1 Sep 21, 2022
This is a small python code that I use with my NAS server connected to Plex

Spotifarr This is a small python code that I use with my NAS server connected to Plex I didn't appreciate how Lidarr works because it downloads a full

Automator 35 Oct 04, 2022
Terminal based chat - networking project with sockets in python

Terminal based chat - networking project with sockets in python

2 Jan 24, 2022
Truetool - A TrueCharts automatic and bulk update utility

truetool A easy tool for frequently used TrueNAS SCALE CLI utilities. Previously

TrueCharts 125 Jan 04, 2023
A p2p chat app for zephyr

A p2p chat app for zephyr

L3gacy B3ta 4 Jun 02, 2021
Openconnect VPN RPi Gateway

Openconnect-VPN-RPi-Gateway See the blog (Chinese) for how to build an Openconne

Zhongze Tang 2 Jan 30, 2022
Desktop application for checking sites connection in a background mode

Site connectivity checker Desktop application for checking site connection in a background mode by sending ICMP messages. Problem and solution Usually

Karina Singatullina 26 Dec 19, 2022
Juniper SNMP Migrations For Python

Juniper SNMP Migrations This example will show how to use the PyEZ plugin for Nornir to build a NETCONF connection to a remote device validate that SN

Calvin Remsburg 1 Jan 07, 2022
QUIC and HTTP/3 implementation in Python

aioquic What is aioquic? aioquic is a library for the QUIC network protocol in Python. It features a minimal TLS 1.3 implementation, a QUIC stack and

1.2k Dec 29, 2022
Wifi-jammer - Continuously perform deauthentication attacks on all detectable stations

wifi-jammer Continuously perform deauthentication attacks on all detectable stat

Leonardo de Araujo 14 Nov 03, 2022
Python script to stop qBittorrent from torrenting without VPN for users with static IP.

Python script to stop qBittorrent from torrenting without VPN for users with static IP.

voidoak_ 1 Oct 25, 2021
This Python script can be used to bypass IP source restrictions using HTTP headers.

ipsourcebypass This Python script can be used to bypass IP source restrictions using HTTP headers. Features 17 HTTP headers. Multithreading. JSON expo

Podalirius 322 Dec 28, 2022