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
HTTP proxy pool server primarily meant for evading IP whitelists

proxy-forwarder HTTP proxy pool server primarily meant for evading IP whitelists. Setup Create a file named proxies.txt and fill it with your HTTP pro

h0nda 2 Feb 19, 2022
ASC - Api Server Controller

ASC - Api Server Controller

Uriel Alves 1 Jan 03, 2022
Passive TCP/IP Fingerprinting Tool. Run this on your server and find out what Operating Systems your clients are *really* using.

Passive TCP/IP Fingerprinting This is a passive TCP/IP fingerprinting tool. Run this on your server and find out what operating systems your clients a

Nikolai Tschacher 158 Dec 20, 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
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
Simple self-hosted server to receive files from remote systems

Badtray This is a very simple self-hosted server to receive files from remote systems. This works similar to Bintray (RIP) and primarily designed to d

Alex Taradov 1 Nov 22, 2021
A Simple Web Server made by Python3.

A Simple Web Server made by Python3.

GGN_2015 2 Nov 27, 2021
nettrace is a powerful tool to trace network packet and diagnose network problem inside kernel.

nettrace nettrace is is a powerful tool to trace network packet and diagnose network problem inside kernel on TencentOS. It make use of eBPF and BCC.

84 Jan 01, 2023
A simple tcpdump sidecar injector to demonstrate Kubernetes's Mutating Webhook

k8s-tcpdump-webhook A simple tcpdump sidecar injector to demonstrate Kubernetes's Mutating Webhook Build and Deploy Build docker image; docker build -

Bilal Ünal 2 Sep 01, 2022
A python tool auto change proxy or ip after dealy time set by user

Auto proxy Ghost This tool auto change proxy or ip after dealy time set by user how to run 1. Install required file ./requirements.sh 2.Enter command

Harsh Tagra 0 Feb 23, 2022
A simple software which can use to make a server in local network

home-nas it is simple software which can use to make a server in local network, it has a web site on it which can use by multipale system, i use nginx

R ansh joseph 1 Nov 10, 2021
Simple Python Script to Parse Apache Log, Get all Unique IPs and Urls visited by that IP

Parse_Apache_Log Simple Python Script to Parse Apache Log, Get all Unique IPs and Urls visited by that IP. It will create 3 different files. allIP.txt

Kathan Patel 2 Mar 29, 2022
Compare the contents of your hosted and proxy repositories for coordinate collisions

Nexus Repository Manager dependency/namespace confusion checker This repository contains a script to check if you have artifacts containing the same n

Sonatype Community 59 Mar 31, 2022
Light, simple RPC framework for Python

Agileutil是一个Python3 RPC框架。基于微服务架构,封装了rpc/http/orm/log等常用组件,提供了简洁的API,开发者可以很快上手,快速进行业务开发。

16 Nov 22, 2022
Simple python script for automated network scans with random name generator(useful for CTF boxes).

📄 Automated NMAP script Description Simple python script for automated network scans with random name generator(useful for CTF boxes). Requirements 1

Dhmos Funk 2 Oct 29, 2021
A Powerful, Smart And Simple Userbot In Telethon

Owner: KeinShin 🇮🇳 BLACK LIGHTNING A Powerful, Smart And Simple Userbot In Telethon. Credits This is A Remix Bot Of Many UserBot. DARKCOBRA FridayUs

Akki ThePro 1 Nov 29, 2021
A library for interacting with APNs and VoIP using HTTP/2.

kalyke A library for interacting with APNs and VoIP using HTTP/2. Installation kalyke requires python 3.6 or later. $ pip install kalyke-apns Usage AP

Yuya Oka 11 Dec 08, 2022
sshuttle: where transparent proxy meets VPN meets ssh

Transparent proxy server that works as a poor man's VPN. Forwards over ssh. Doesn't require admin. Works with Linux and MacOS. Supports DNS tunneling.

9.4k Jan 09, 2023
ProtOSINT is a Python script that helps you investigate Protonmail accounts and ProtonVPN IP addresses

ProtOSINT ProtOSINT is a Python script that helps you investigate ProtonMail accounts and ProtonVPN IP addresses. Description This tool can help you i

pixelbubble 249 Dec 23, 2022
Roadster - Distance to Closest Road Feature Server

Roadster: Distance to Closest Road Feature Server Milliarium Aerum, the zero of

Textualization Software Ltd. 4 May 23, 2022