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
This is the code repository for Mastering Python for Networking and Security – Second Edition

Mastering Python for Networking and Security – Second Edition This is the code repository for Mastering Python for Networking and Security – Second Ed

Frank Gottinger 1 Feb 09, 2022
NanoChat - nano chat server and client

NanoChat This is a work in progress! NanoChat is an application for connecting with your friends using Python that uses ONLY default Python libraries.

Miss Bliss 1 Nov 13, 2021
Network Dynaimcs Simulation

A Final Year Project in CUHK, Autumn 2021 Network Dynaimcs Simulation Files param.h edit all the variables & settings here simulate.c the main program

Likchun 0 Mar 28, 2022
Tiny JSON RPC via HTTP library.

jrpc Simplest ever possible Asynchronous JSON RPC via HTTP library for Python, backed by httpx. Installation pip install async-jrpc Usage Import JRPC

Onigiri Team 2 Jan 31, 2022
Linkedin Connection Automation

Why spend an hour+ a week, connecting with the correct people on LinkedIn when you can go for lunch and let your computer do the hard work?

1 Nov 29, 2021
Build custom OSINT tools and APIs (Ping, Traceroute, Scans, Archives, DNS, Scrape, Whois, Metadata & built-in database for more info) with this python package

Build custom OSINT tools and APIs with this python package - It includes different OSINT modules (Ping, Traceroute, Scans, Archives, DNS, Scrape, Whoi

QeeqBox 52 Jan 06, 2023
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
gRPC typing stubs for Python

gRPC Typing Stubs for Python This is a PEP-561-compliant stub-only package which provides type information of gRPC. Install using pip: pip install grp

Blake Williams 27 Dec 20, 2022
IoT owl is light face detection and recognition system made for small IoT devices like raspberry pi.

IoT Owl IoT owl is light face detection and recognition system made for small IoT devices like raspberry pi. Versions Heavy with mask detection withou

Ret2Me 6 Jun 06, 2022
Network monitoring tool

netmeter If you are looking for a tool to monitor your network interfaces, here you are. See netmeter-exporter to export Prometheus metrics. Installat

Saeid Bostandoust 97 Dec 03, 2022
A python socket.io client for Roboteur

Roboteur Client Example TODO Basic setup Install the requirements: $ pip install -r requirements.txt Run the application: $ python -m roboteur_client

Barry Buck 1 Oct 13, 2021
stellar-add-guest is a small tool to generate a new guest for Stellar Wireless (Enterprise mode) in OmniVista 2500 hosted on OmniSwitch with AOS Release 8

stellar-add-guest is a small tool to generate a new guest for Stellar Wireless (Enterprise mode) in OmniVista 2500 hosted on OmniSwitch with AOS Release 8.

BennyE 3 Jan 24, 2022
A python 3 library which helps in using nmap port scanner.

A python 3 library which helps in using nmap port scanner. This is done by converting each nmap command into a callable python3 method or function. System administrators can now automatic nmap scans

Nmmapper 179 Dec 19, 2022
This is a Client-Server-System which can send audio from a microphone from the server to client and in the other direction.

Audio-Streaming-Python This is a Client-Server-System which can send audio from a microphone from the server to client and in the other direction. You

VFX / Videoeffects Creator 0 Jan 05, 2023
FIRM-AFL is the first high-throughput greybox fuzzer for IoT firmware.

FIRM-AFL is the first high-throughput greybox fuzzer for IoT firmware. FIRM-AFL addresses two fundamental problems in IoT fuzzing

356 Dec 23, 2022
Wallc - Calculate the layout on the wall to hang up pictures

wallc Calculate the layout on the wall to hang up pictures. Installation pip install git+https://github.com/trbznk/wallc.git Getting Started Currently

Alex Trbznk 68 Sep 09, 2022
Automated network configuration backups using Github actions and git-scraping

Network Config Scraper This repository demonstrates the use of Github Actions and git-scraping to build an automated backup solution for network confi

WWT 19 Dec 14, 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
BLE parser for passive BLE advertisements

This pypi package is parsing BLE advertisements to readable data for several sensors and can be used for device tracking, as long as the MAC address is static. The parser was originally developed as

Ernst Klamer 19 Dec 26, 2022
Arp Spoofer using Python 3.

ARP Spoofer / Wifi Killer By Auax Run: Run the application with the following command: python3 spoof.py -t target_ip_address -lh host_ip_address I

Auax 6 Sep 15, 2022